Skip to content

Commit

Permalink
do a git commit where it adds all the files
Browse files Browse the repository at this point in the history
because up until now thhe files were just on my computer, but i would
liek it if i could store the files in decentralised repositories,
perhaps one on the proprietary but apparently universally trusted
software web site of the privately owned company Github, Inc. and
perhaps one on my own internet computer and maybe another on another
computer

look i'm not sure you're getting it, like it'll be the same files but
then they'll be in different places and people can use and download them
and make changes to them and then share those changes but then i've
licensed this MIT by accident so they don't have to share their changes
because freedom apparently means letting people restrict your freedom
because you have to let them be free to do that otherwise you are
restricting their freedom or something

anyway i'll download the cpmputer and put all the other computers and
the internet and baskets waeving baskets out of hair and leather and
little piles of leather and flowers and butter and happy dogs and happy
go lucky farming folk and what's the story and help and handshakes and
i'm sorry and i can't help and i'm sorry that i can't help and i'm not
ok

i'm super fine thanks i'm doing really well, i'm doing super well and
i'm fine , thankyou

yes
you too
thanksb

bye
  • Loading branch information
chee committed Jul 13, 2017
0 parents commit f0deaea
Show file tree
Hide file tree
Showing 16 changed files with 928 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
@@ -0,0 +1 @@
/node_modules/
74 changes: 74 additions & 0 deletions README.md
@@ -0,0 +1,74 @@
## smal lib for kasa veho (and tiktek?) smart bluetooth lightblibs

small library for kasa veho lightbulbs and others like it.

could not have finished this if it were not for
[https://mjg59.dreamwidth.org/43722.html](this blog post) and
[https://github.com/mjg59/python-tikteck](this python library) by
[https://twitter.com/mjg59](@mjg59). so thank you a billion times for writing
that and digging into that disassembled .so while i dug into nothing but shallow
graves

you'll note that this is nearly a carbon copy of their library

i couldn't run that because i wasn't on a loonix machine, so now this has
happened and should work on lanux and macos maybe even windows if the stars
align. relies on the wnoderful [noble](https://github.com/sandeepmistry/noble)

## use:

* with a bulb you know

```js
const {pair, setColor} = require('kasa')
pair({
name: 'Smart Light',
password: 'password obtained from adb lolcat',
address: 'ca:fe:0f:be:ef' // address of light
}, dispatch => {
// red, green, blue, brightness
dispatch(setColor(0xff, 0x2a, 0x50, 0xff))
})
```

* with a bulb you've never met:

```js
const {discover, pair, setColor} = require('kasa')
discover({
name: 'Smart Light', // defaults to Smart Light
password: 'get this from adb lolcat'
}, blub => {
console.log('a real lootblub!', blub.address)
pair(blub, dispatch => {
dispatch(setColor(0x33, 0xcc, 0xff, 0xff))
})
})
```

* there is also a promise api (note that you will only get the first bulb
discovered this way, but the callback will keep calling you up with every
light it finds)

```js
const {discover, pair, setColor} = require('kasa')

discover()
.then(pair)
.then(dispatch => dispatch(setColor(0xff, 0xff, 0xff, 0xff)))
```

there are some silly examples in the directory called `play/`

## todo:

* clean up the code (vague!)
* fix vague todo items (which?)
* find out why my tongue has lumps on it
* revise the api when i have had literally any sleep in the past 3 years
* get rich, die old
* get to the top of Tom's top 8
* wear sunscreen
* learn the xaphoon
* see how lb is doin
* switch to crypto-js and pull parts out and see if this can run in web browsers
173 changes: 173 additions & 0 deletions index.js
@@ -0,0 +1,173 @@
const crypto = require('crypto')
const noble = require('noble')

const nobleReady = new Promise(resolve =>
noble.on('stateChange', state => {
state === 'poweredOn' && resolve()
})
)

const range = to => Array(to).fill().map((_, i) => i)

function encrypt (key, data) {
key = Buffer.from(key)
key.reverse()
data = Buffer.from(data)
data.reverse()
const cipher = crypto.createCipheriv('aes-128-ecb', key, Buffer.from([]))
const encryptedData = cipher.update(data).reverse()
return encryptedData
}

function generateSk (name, password, data1, data2) {
name = Buffer.from(name.padEnd(16, '\u0000'))
password = Buffer.from(password.padEnd(16, '\u0000'))
const key = []
name.forEach((byte, index) => {
key.push(byte ^ password[index])
})
const data = [...data1.slice(0, 8), ...data2.slice(0, 8)]
return encrypt(key, data)
}

function encryptKey (name, password, data) {
name = Buffer.from(name.padEnd(16, '\u0000'))
password = Buffer.from(password.padEnd(16, '\u0000'))
const key = []
key.forEach.call(name, (byte, index) => {
key.push(byte ^ password[index])
})
return encrypt(data, key)
}

// mutate me mor
function encryptPacket (sk, mac, packet) {
let tmp = [...mac.slice(0, 4), 0x01, ...packet.slice(0, 3), 15, 0, 0, 0, 0, 0, 0, 0]
tmp = encrypt(sk, tmp)

range(15).forEach(i => {
tmp[i] = tmp[i] ^ packet[i + 5]
})

tmp = encrypt(sk, tmp)

range(2).forEach(i => {
packet[i + 3] = tmp[i]
})

tmp = [0, ...mac.slice(0, 4), 0x01, ...packet.slice(0, 3), 0, 0, 0, 0, 0, 0, 0]

tmp2 = []

range(15).forEach(i => {
if (i === 0) {
tmp2 = encrypt(sk, tmp)
tmp[0] = tmp[0] + 1
}
packet[i + 5] ^= tmp2[i]
})

return Buffer.from(packet)
}

function connect (light, callback) {
return light.connect(() => callback(light))
}

function discover (options = {}, callback) {
if (arguments.length === 1) {
if (typeof arguments[0] === 'function') {
callback = arguments[0]
options = {}
}
}
const {name = 'Smart Light', password = '234', address} = options
const discovery = new Promise(resolve => {
noble.on('discover', thing => {
if (thing.advertisement.localName !== name) return
thing.password = password
if (address) {
address === thing.address && connect(thing, (...args) => {
callback && callback(...args)
resolve(...args)
})
} else {
connect(thing, (...args) => {
callback && callback(...args)
resolve(...args)
})
}
})
})
noble.startScanning()
return discovery
}

function pair (light, callback) {
let packetCount = Math.random() * 0xffff | 0
const name = light.name || light.advertisement.localName
const password = light.password
const mac = light.address
return new Promise(resolve => {
light.discoverAllServicesAndCharacteristics(() => {
const commandChar = light.services[1].characteristics[1]
const pairChar = light.services[1].characteristics[3]
const data = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0, 0, 0, 0, 0, 0, 0, 0]
const encryptedKey = encryptKey(name, password, data)
const packet = [0x0c]
.concat(data.slice(0, 8))
.concat([...encryptedKey].slice(0, 8))

pairChar.write(new Buffer(packet), true, () => {
pairChar.read((error, received) => {
const sk = generateSk(name, password, data.slice(0, 8), received.slice(1, 9))
function dispatch ([id, command, data], callback) {
const packet = Array(20).fill(0)
packet[0] = packetCount & 0xff
packet[1] = packetCount >> 8 & 0xff
packet[5] = id & 0xff
packet[6] = id & 0xff | 0x80
packet[7] = command
packet[8] = 0x69
packet[9] = 0x69
packet[10] = data[0]
packet[11] = data[1]
packet[12] = data[2]
packet[13] = data[3]
const macKey = Buffer.from(mac.split(':').slice(0, 6).reverse().map(n => parseInt(n, 16)))
const encryptedPacket = encryptPacket(sk, macKey, [...packet])
packetCount = packetCount > 0xffff ? 1 : packetCount + 1
return new Promise(resolve => {
commandChar.write(encryptedPacket, false, (...args) => {
callback && callback(...args)
resolve(...args)
})
})
}
callback && callback(dispatch)
resolve(dispatch)
})
})
})
})
}

function sendCommand (command, ...args) {
return [0xffff, command, args]
}

// red, green, blue, brightness
function setColor (...values) {
return sendCommand(0xc1, ...values)
}

function setDefaultColor (...values) {
return sendCommand(0xc4, ...values)
}

module.exports = {
discover: (...args) => nobleReady.then(() => discover(...args)),
pair,
setColor,
setDefaultColor
}

0 comments on commit f0deaea

Please sign in to comment.