Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
do a git commit where it adds all the files
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
0 parents
commit f0deaea
Showing
16 changed files
with
928 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/node_modules/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} |
Oops, something went wrong.