aholstenson / miio

Control Mi Home devices, such as Mi Robot Vacuums, Mi Air Purifiers, Mi Smart Home Gateway (Aqara) and more
MIT License
1.86k stars 354 forks source link

set_power function doesn't work in zhimi.airpurifier.v1 #35

Open syu-lk4b opened 7 years ago

syu-lk4b commented 7 years ago

tried the power plug ,works fine, but the same function doesn't work in zhimi,airpurifier.v1 from the document ,it says have't tested, trying hookup it with homebridge-miio, but if miio doesn't support it ,then homebridge-miio wont' work for sure.

any idea?

here is my device

Device ID: 45531555 Model info: zimi.powerstrip.v2 (power-strip) Address: 192.168.88.55 (zimi-powerstrip-v2_miio45531555) Token: 4407cd577d6f7787fd63a759fc9fdf95 via auto-token Support: At least basic

Device ID: 937021 Model info: chuangmi.plug.v1 (power-plug) Address: 192.168.88.18 (chuangmi-plug-v1_miio937021) Token: a6ab080d5223bdbc73fbf572ef8388dc via auto-token Support: At least basic

Device ID: 52786 Model info: zhimi.airpurifier.v1 (air-purifier) Address: 192.168.88.23 (zhimi-airpurifier-v1_miio52786) Token: 0cc749b4c2124fc67f435e93c0045378 via auto-token Support: At least basic

the script output

pi@stevenhomesmart:~/Git/temp $ node set.js 192.168.88.23 power on { Error: Method set_power is not supported at Object.reject (/usr/lib/node_modules/miio/lib/device.js:213:13) at AirPurifier._onMessage (/usr/lib/node_modules/miio/lib/device.js:104:8) at emitTwo (events.js:106:13) at Socket.emit (events.js:191:7) at UDP.onMessage (dgram.js:549:8) code: -10000 } pi@stevenhomesmart:~/Git/temp $ ls^C pi@stevenhomesmart:~/Git/temp $ node set.js 192.168.88.55 power on [ 'ok' ] pi@stevenhomesmart:~/Git/temp $ node set.js 192.168.88.18 power on [ 'ok' ] pi@stevenhomesmart:~/Git/temp $ node set.js 192.168.88.18 power off [ 'ok' ] pi@stevenhomesmart:~/Git/temp $

here is the script that i use

!/usr/bin/node

/ eslint-disable /

// Location of miio node lib const miio = require('miio');

// No need to change any lines in this section var deviceip = process.argv[2]; var secondarg = process.argv[3]; var thirdarg = process.argv[4]; function exit() { process.exit(-1); }

// Power On (on / off specified as on or off) if ( secondarg === "power" ) { setTimeout(exit, 7000); miio.device({ address: deviceip }).then(device => { return device.call('set_power', [thirdarg]) .then(console.log) .catch(console.error); })}

// Status if ( secondarg === "status" ) { setTimeout(exit, 7000); miio.device({ address: deviceip }).then(device => { return device.call('get_prop', ["humidity","temp_dec","power","mode","led_b","buzzer","child_lock","limit_hum","trans_level"]) .then(console.log) .catch(console.error); })}

// Status temperature and humidity only if ( secondarg === "status2" ) { setTimeout(exit, 7000); miio.device({ address: deviceip }).then(device => { return device.call('get_prop', ["temp_dec","humidity"]) .then(console.log) .catch(console.error); })}

// Set mode (silent, medium or high) if ( secondarg === "mode" ) { setTimeout(exit, 7000); miio.device({ address: deviceip }).then(device => { return device.call('set_mode', [thirdarg]) .then(console.log) .catch(console.error); })}

// Set buzzer (on or off) if ( secondarg === "buzzer" ) { setTimeout(exit, 7000); miio.device({ address: deviceip }).then(device => { return device.call('set_buzzer', [thirdarg]) .then(console.log) .catch(console.error); })}

// led if ( secondarg === "led" ) { setTimeout(exit, 7000); miio.device({ address: deviceip }).then(device => { return device.call('set_led_b', [JSON.parse(thirdarg)]) .then(console.log) .catch(console.error); })}

// Humidity limit percent (specified as 40, 50, 60, 70 or 80) if ( secondarg === "humiditylimit" ) { setTimeout(exit, 7000); miio.device({ address: deviceip }).then(device => { return device.call('set_limit_hum', [JSON.parse(thirdarg)]) .then(console.log) .catch(console.error); })}

syu-lk4b commented 7 years ago

my air zhimi,airpurifier.v1 firmware version is 1.4.0

pi@stevenhomesmart:~/Git/temp $ node air.js AirPurifier { domain: null, _events: {}, _eventsCount: 0, _maxListeners: undefined, id: undefined, type: 'air-purifier', model: 'zhimi.airpurifier.v1', capabilities: [ 'power', 'mode', 'sensor', 'temperature', 'humidity', 'aqi' ], address: '192.168.88.23', port: 54321, writeOnly: false, packet: Packet { header: <Buffer 21 31 00 80 00 00 00 00 00 00 ce 32 00 00 d8 68 3e 3e f1 97 3c 76 37 f3 02 aa b0 b6 e1 c1 23 7b>, _serverStampTime: 1495860174359, _token: <Buffer 0c c7 49 b4 c2 12 4f c6 7f 43 5e 93 c0 04 53 78>, _tokenKey: <Buffer 26 50 49 f0 bf 8d ec 89 96 fe fb da 46 4d 3b 93>, _tokenIV: <Buffer 85 e2 5b de f3 92 ba d2 d1 33 74 e1 fe 38 a9 4b>, data: <Buffer 7b 22 72 65 73 75 6c 74 22 3a 5b 6e 75 6c 6c 2c 22 69 64 6c 65 22 2c 6e 75 6c 6c 2c 6e 75 6c 6c 2c 6e 75 6c 6c 2c 34 31 2c 31 36 37 2c 36 30 2c 6e 75 ... >, _serverStamp: 55400 }, socket: Socket { domain: null, _events: { message: [Function: bound _onMessage] }, _eventsCount: 1, _maxListeners: undefined, _handle: UDP { fd: 12, lookup: [Function: lookup4], owner: [Circular], onmessage: [Function: onMessage] }, _receiving: true, _bindState: 2, type: 'udp4', fd: -42, _reuseAddr: undefined, _queue: undefined }, _id: 1, _promises: {}, _hasFailedToken: false, _properties: { power: false, mode: 'idle', favoriteLevel: null, temperature: 0, humidity: null, aqi: 41, bright: 167, filterLifeRemaining: 60, filterHoursUsed: null, useTime: null, led: false, ledBrightness: 'unknown', buzzer: true }, _propertiesToMonitor: [ 'power', 'mode', 'favorite_level', 'temp_dec', 'humidity', 'aqi', 'bright', 'filter1_life', 'f1_hour_used', 'use_time', 'led', 'led_b', 'buzzer' ], _propertyDefinitions: { power: { mapper: [Function] }, mode: { mapper: [Function: IDENTITY_MAPPER] }, favorite_level: { name: 'favoriteLevel', mapper: [Function: IDENTITY_MAPPER] }, temp_dec: { name: 'temperature', mapper: [Function: mapper] }, humidity: { mapper: [Function: IDENTITY_MAPPER] }, aqi: { mapper: [Function: IDENTITY_MAPPER] }, bright: { mapper: [Function: IDENTITY_MAPPER] }, filter1_life: { name: 'filterLifeRemaining', mapper: [Function: IDENTITY_MAPPER] }, f1_hour_used: { name: 'filterHoursUsed', mapper: [Function: IDENTITY_MAPPER] }, use_time: { name: 'useTime', mapper: [Function: IDENTITY_MAPPER] }, led: { mapper: [Function: mapper] }, led_b: { name: 'ledBrightness', mapper: [Function: mapper] }, buzzer: { mapper: [Function: mapper] } }, _reversePropertyDefinitions: { favoriteLevel: 'favorite_level', temperature: 'temp_dec', filterLifeRemaining: 'filter1_life', filterHoursUsed: 'f1_hour_used', useTime: 'use_time', ledBrightness: 'led_b' }, _loadProperties: [Function: bound _loadProperties], management: DeviceManagement { device: [Circular] }, debug: { [Function: debug] namespace: 'miio.device.[192.168.88.23]', enabled: false, useColors: true, color: 5, inspectOpts: {} }, setPower: [Function], _propertyMonitor: Timeout { _called: false, _idleTimeout: 30000, _idlePrev: TimersList { _idleNext: [Circular], _idlePrev: [Circular], _timer: [Object], _unrefed: false, msecs: 30000, nextTick: false }, _idleNext: TimersList { _idleNext: [Circular], _idlePrev: [Circular], _timer: [Object], _unrefed: false, msecs: 30000, nextTick: false }, _idleStart: 645, _onTimeout: [Function: bound _loadProperties], _timerArgs: undefined, _repeat: 30000 }, _lastToken: 1495860172177 }

aholstenson commented 7 years ago

Thanks for the report! It looks to me like zhimi.airpurifier.v1 doesn't share the same API as newer models. I based the initial air purifier code on the methods I could see coming from my own air purifier which is zhimi.airpurifier.m1.

Does calling set_mode work for turning it on and off? idle should turn it off and auto should turn it on.

If you feel up for it you can read Protocol and commands to see how you can create a packet capture and find the correct methods to use.

If set_mode works I can easily add a workaround that uses that to your this model of airpurifier on and off. And if you figure out more about how what methods work on the device I'll be more than happy to add support for those too.

syu-lk4b commented 7 years ago

Thanks man,I'll try to testing it tonight, and let you know if that's the case, again, thanks the reply

syu-lk4b commented 7 years ago

got 404 for the doc of how to get the tokens https://github.com/aholstenson/miio/blob/master/docs/tokens.md

syu-lk4b commented 7 years ago

btw, tested the set_mode function ,it's also not supported.

1): Error: Method set_mode is not supported

aholstenson commented 7 years ago

Thank you for the details, it seems like the zhimi.airpurifier.v1 has a completely different API. For now I'll disable the support in the next release.

I'm more than happy to readd support if you or anyone else can figure out what methods and properties it uses. Doing so is a bit tricky and involves packet capturing via a local Android emulator, Protocol and commands contains an intro and links to further docs.

syu-lk4b commented 7 years ago

this is the wireshark dump, I am pretty sure I have capture all the command UDP package. but When I tested as Protocol and commands shows from PackageSender , it doesnt' give any feedback. is there anything that I done wrong ? any suggestion or advise?

zhimi.pcap.zip

syu-lk4b commented 7 years ago

syu-mbp-15 :: ~/git_repo/miio ‹master› » miio --token "0cc749b4c2124fc67f435e93c0045378" --json-dump /tmp/test.json undefined:1

SyntaxError: Unexpected end of JSON input at Object.parse (native) at Object. (/usr/local/lib/node_modules/miio/cli/index.js:424:23) at Module._compile (module.js:570:32) at Object.Module._extensions..js (module.js:579:10) at Module.load (module.js:487:32) at tryModuleLoad (module.js:446:12) at Function.Module._load (module.js:438:3) at Module.runMain (module.js:604:10) at run (bootstrap_node.js:390:7) at startup (bootstrap_node.js:150:9) syu-mbp-15 :: ~/git_repo/miio ‹master› »

syu-mbp-15 :: ~ » miio --discover 1 ↵ INFO Discovering devices. Press Ctrl+C to stop.

Device ID: 52038474 Model info: lumi.gateway.v3 (gateway) Address: 192.168.88.111 (lumi-gateway-v3_miio52038474) Token: ??? Support: At least basic

Device ID: 46778211 Model info: Unknown Address: 192.168.88.228 (esp_c28b4e) Token: ??? Support: Unknown

Device ID: 52786 Model info: zhimi.airpurifier.v1 (air-purifier) Address: 192.168.88.23 (zhimi-airpurifier-v1_miio52786) Token: 0cc749b4c2124fc67f435e93c0045378 via auto-token Support: At least basic

aholstenson commented 7 years ago

Thanks. Sorry for the late reply. Did you export the packet capture as a JSON-file? The error looks like it might not be a JSON.

After I exported your provided capture I get this:

 ->  192.168.88.16 data= N/A
 <-  192.168.88.23 data= N/A
 ->  192.168.88.16 data= {"id":10,"method":"set_mode","params":["auto"]}
 <-  192.168.88.23 data= {"result":["ok"],"id":10}
 ->  192.168.88.16 data= {"id":11,"method":"get_prop","params":["aqi","filter1_life","led","mode","act_det","buzzer"]}
 <-  192.168.88.23 data= {"result":[42,60,"on","auto","off","on"],"id":11}
 ->  192.168.88.16 data= {"id":12,"method":"set_mode","params":["silent"]}
 <-  192.168.88.23 data= {"result":["ok"],"id":12}
 ->  192.168.88.16 data= {"id":13,"method":"set_mode","params":["auto"]}
 <-  192.168.88.23 data= {"result":["ok"],"id":13}
 ->  192.168.88.16 data= {"id":14,"method":"get_prop","params":["aqi","filter1_life","led","mode","act_det","buzzer"]}
 <-  192.168.88.23 data= {"result":[47,60,"on","auto","off","on"],"id":14}
 ->  192.168.88.16 data= {"id":15,"method":"set_mode","params":["silent"]}
 <-  192.168.88.23 data= {"result":["ok"],"id":15}
 ->  192.168.88.16 data= {"id":16,"method":"set_mode","params":["strong"]}
 <-  192.168.88.23 data= {"result":["ok"],"id":16}
 ->  192.168.88.16 data= {"id":17,"method":"get_prop","params":["aqi","filter1_life","led","mode","act_det","buzzer"]}
 <-  192.168.88.23 data= {"result":[44,60,"on","strong","off","on"],"id":17}
 ->  192.168.88.16 data= {"id":18,"method":"set_mode","params":["auto"]}
 <-  192.168.88.23 data= {"result":["ok"],"id":18}
 ->  192.168.88.16 data= {"id":19,"method":"set_mode","params":["idle"]}
 <-  192.168.88.23 data= {"result":["ok"],"id":19}
 ->  192.168.88.16 data= {"id":20,"method":"get_prop","params":["aqi","filter1_life","led","mode","act_det","buzzer"]}
 <-  192.168.88.23 data= {"result":[44,60,"off","idle","off","on"],"id":20}
 ->  192.168.88.16 data= {"id":21,"method":"set_mode","params":["auto"]}
 <-  192.168.88.23 data= {"result":["ok"],"id":21}
 ->  192.168.88.16 data= {"id":22,"method":"get_prop","params":["aqi","filter1_life","led","mode","act_det","buzzer"]}
 <-  192.168.88.23 data= {"result":[43,60,"on","auto","off","on"],"id":22}
 ->  192.168.88.16 data= {"id":23,"method":"set_mode","params":["idle"]}
 <-  192.168.88.23 data= {"result":["ok"],"id":23}
 ->  192.168.88.16 data= {"id":24,"method":"set_mode","params":["auto"]}
 <-  192.168.88.23 data= {"result":["ok"],"id":24}
 ->  192.168.88.16 data= {"id":25,"method":"set_mode","params":["idle"]}
 <-  192.168.88.23 data= {"result":["ok"],"id":25}
 ->  192.168.88.16 data= {"id":26,"method":"get_prop","params":["aqi","filter1_life","led","mode","act_det","buzzer"]}
 <-  192.168.88.23 data= {"result":[43,60,"off","idle","off","on"],"id":26}

It looks like set_mode is being used with values of idle, auto, silent' andstrong`. That should be enough for me to add in some initial support.

syu-lk4b commented 7 years ago

Thanks man, i am not sure what you mean capture as a Json file ,will take a look that. is there anything else that I can help provide?

syu-lk4b commented 7 years ago

change the code a little bit ,yes, set_mode works

syu-mbp-15 :: ~/Desktop/js » node set.js 192.168.88.23 power auto [ 'ok' ] syu-mbp-15 :: ~/Desktop/js » 255 ↵ syu-mbp-15 :: ~/Desktop/js » 255 ↵ syu-mbp-15 :: ~/Desktop/js » node set.js 192.168.88.23 power idle 255 ↵ [ 'ok' ] syu-mbp-15 :: ~/Desktop/js »

yestop commented 7 years ago

@Stevenyu1982 how to set? thx

sibero80 commented 7 years ago

Having same issue generating capture as JSON

JSON file contained: {}

sudo miio --token 661bc1e355a15ed4197398e229a3ff20 --json-dump file.json
/usr/lib/node_modules/miio/cli/index.js:427
        packets.forEach(p => {
                ^

TypeError: packets.forEach is not a function
    at Object.<anonymous> (/usr/lib/node_modules/miio/cli/index.js:427:10)
    at Module._compile (module.js:571:32)
    at Object.Module._extensions..js (module.js:580:10)
    at Module.load (module.js:488:32)
    at tryModuleLoad (module.js:447:12)
    at Function.Module._load (module.js:439:3)
    at Module.runMain (module.js:605:10)
    at run (bootstrap_node.js:427:7)
    at startup (bootstrap_node.js:151:9)
    at bootstrap_node.js:542:3

if JSON file does not exist, it does not create the file and prompts error:

sudo miio --token 661bc1e355a15ed4197398e229a3ff20 --json-dump file fs.js:584 return binding.open(pathModule._makeLong(path), stringToFlags(flags), mode); ^

Error: ENOENT: no such file or directory, open 'file'
    at Object.fs.openSync (fs.js:584:18)
    at Object.fs.readFileSync (fs.js:491:33)
    at Object.<anonymous> (/usr/lib/node_modules/miio/cli/index.js:421:18)
    at Module._compile (module.js:571:32)
    at Object.Module._extensions..js (module.js:580:10)
    at Module.load (module.js:488:32)
    at tryModuleLoad (module.js:447:12)
    at Function.Module._load (module.js:439:3)
    at Module.runMain (module.js:605:10)
    at run (bootstrap_node.js:427:7)
aholstenson commented 7 years ago

@sibero80 The miio tool doesn't generate any JSON dumps, those are created from Wireshark. Have a look at the protocol documentation for more details.