Xiaomi Mi Air Purifier - solution

@marcel_verpaalen developing the binding, what is working fine! So it is recommended:

How to add Xiaomi Mi Air Purifier to openhab2

A long time ago I waiting a solution to control my Mi Air Purifier 2 without Xiaomi cloud. A few days ago, I just found a new npm (NodeJS) package, name is miio:

And it is the GitHub link:

Unfortunately this is not in the mihome binding yet. But I hope soon will be support too.

https://github.com/openhab/openhab2-addons/pull/1660

Until this will be support, I made a solution using exec bindings.

Created 3 file to control the purifier.

root@raspberrypi:/usr/share/openhab2/miairpurifier# cat control_power_miairpurifier.js
'use strict';

const miio = require('miio');
var args = process.argv.slice(2);
var exec = require('child_process').exec;


// Create a new device over the given address
const device = miio.createDevice({
        model: 'zhimi-airpurifier-m1',
        address: 'PURIFIER-IP-ADDRESS'
});

function exit() {
process.exit(-1);
}

function openhab2_rest() {
exec('curl -s -X POST --header "Content-Type: text/plain" --header "Accept: application/json" -d "status" "http://localhost:8080/rest/items/MiAirPurifierPower_Input" > /dev/null');
}

if (args == "" || args == "status" || args == "Status") {
device.getProperties([ 'power' ])
.then(console.log)
setTimeout(openhab2_rest, 7000);
setTimeout(exit, 7000);
}

if (args == "on" || args == "On" || args == "ON") {

device.setPower(true);
setTimeout(openhab2_rest, 7000);
setTimeout(exit, 7000);
}

if (args == "off" || args == "Off" || args == "OFF") {

device.setPower(false);
setTimeout(openhab2_rest, 7000);
setTimeout(exit, 7000);
}
root@raspberrypi:/usr/share/openhab2/miairpurifier#
root@raspberrypi:/usr/share/openhab2/miairpurifier# cat control_mode_miairpurifier.js
'use strict';

const miio = require('miio');
var args = process.argv.slice(2);
var exec = require('child_process').exec;


// Create a new device over the given address
const device = miio.createDevice({
        model: 'zhimi-airpurifier-m1',
        address: 'PURIFIER-IP-ADDRESS'
});

function exit() {
process.exit(-1);
}

function openhab2_rest() {
exec('curl -s -X POST --header "Content-Type: text/plain" --header "Accept: application/json" -d "status" "http://localhost:8080/rest/items/MiAirPurifierMode_Input" > /dev/null');
}

if (args == "" || args == "status" || args == "Status") {
device.getProperties([ 'mode' ])
    .then(console.log)
setTimeout(openhab2_rest, 7000);
setTimeout(exit, 7000);
}

if (args == "auto" || args == "Auto") {

device.call('set_mode', [ 'auto' ])
    .then(console.log)
setTimeout(openhab2_rest, 7000);
setTimeout(exit, 7000);
}

if (args == "sleep" || args == "Sleep") {

device.call('set_mode', [ 'silent' ])
    .then(console.log)
setTimeout(openhab2_rest, 7000);
setTimeout(exit, 7000);
}

if (args == "favorite" || args == "Favorite") {

device.call('set_mode', [ 'favorite' ])
    .then(console.log)
setTimeout(openhab2_rest, 7000);
setTimeout(exit, 7000);
}
root@raspberrypi:/usr/share/openhab2/miairpurifier#
root@raspberrypi:/usr/share/openhab2/miairpurifier# cat monitoring_miairpurifier.js
'use strict';

const miio = require('miio');

// Create a new device over the given address
const device = miio.createDevice({
        model: 'zhimi-airpurifier-m1',
        address: 'PURIFIER-IP-ADDRESS'
});


function exit() {
process.exit(-1);
}

const value = device.getProperties([ 'aqi', 'temp_dec', 'humidity' ])
        .then(console.log)

setTimeout(exit, 3000);
root@raspberrypi:/usr/share/openhab2/miairpurifier#

Example rules:

rule "Aveo Ignition OFF && Mi Air Purifier ON && E-Plug ON (work/living) When MailBox button long clicked"
when
        Channel "mihome:sensor_switch:158d00010a9812:button" triggered LONG_CLICK_PRESS
then

        if (MiAirPurifierMode_Output.state == "{ power: false }") {
        sendCommand(MiAirPurifierPower_Input, "on")
        }

        if (DSPW215WorkRoom_Output.state == "OFF") {
        logInfo("RULES", "Work Room E-Plug ON")
        sendCommand(DSPW215WorkRoom_Input, "on")
        }

        if (DSPW215LivingRoom_Output.state == "OFF") {
        logInfo("RULES", "Living Room E-Plug ON")
        sendCommand(DSPW215LivingRoom_Input, "on")
        }

        if (AveoStatus_Output.state == "Ignition On") {
        logInfo("RULES", "Aveo Ignition OFF When MailBox button long clicked")
        sendCommand(AveoStatus_Input, "Stop")
        }
end

rule "Change Mi Air Purifier mode to sleep"
when
        Time cron "0 0 23 ? * MON-SUN" //Everyday at 11pm
then
        if (MiAirPurifierMode_Output.state == "{ mode: auto }" && MiAirPurifierPower_Output.state == "{ power: true }") {
        sendCommand(MiAirPurifierMode_Input, "sleep")
        }
end


rule "Change Mi Air Purifier mode to auto"
when
        Time cron "0 0 7 ? * MON-SUN" //Every weekday at 7am
then
        if (MiAirPurifierMode_Output.state == "{ mode: silent }" && MiAirPurifierPower_Output.state == "{ power: true }") {
        sendCommand(MiAirPurifierMode_Input, "auto")
        }
end

rule "Leave Home"

when
    Channel "mihome:sensor_switch:158d00010d19de:button" triggered LONG_CLICK_PRESS
then
        var Number hour = now.getHourOfDay
        if ((hour > 7) || (hour < 9)){

        if (DSPW215LivingRoom_Output.state == "ON") {
        logInfo("RULES", "Living Room E-Plug OFF")
        sendCommand(DSPW215LivingRoom_Input, "off")
        }
        if (DSPW215WorkRoom_Output.state == "ON") {
        logInfo("RULES", "Work Room E-Plug OFF")
        sendCommand(DSPW215WorkRoom_Input, "off")
        }
        }

        if (AveoStatus_Output.state == "Ignition Off") {
        logInfo("RULES", "Aveo Ignition ON When Leave Home")
        sendCommand(AveoStatus_Input, "Start")
        }

        if (MiAirPurifierPower_Output.state == "{ power: true }") {
        sendCommand(MiAirPurifierPower_Input, "off")
        }
end

All credits to aholstenson

Note: This npm package is tested on node 5.12.0 (on 6 and 7 not worked well)

3 Likes

hi, can u post your item definition ? please

PS: miio got some updates, now it works for me like this:

const miio = require('miio');

// Create a new device over the given address
const device = miio.createDevice({
        address: 'IP',
        model : 'zhimi.airpurifier.m1',
        token: 'HEX GOT WITH MIIO --discover'
});

device.init();
function exit() {
process.exit(-1);
}
setTimeout(exit, 7000);

device.call('get_prop', ["humidity","temp_dec","power","mode","led_b","buzzer","child_lock","limit_hum","trans_level"])
.then(console.log)
.catch(console.error);

Would you mind trying the mi vacuum binding.

If things go well, it should discover your device.
Once you provided the tolken, you have a command channel to which you can send the xiaomi commands (any parameters can be provided in between square brackets) eg. set_mode [ favorite ]

Let me know if it works, and provide the necessary commands & channels (pls provide the log, so I can see the exact format) than I can easy add it to the binding

2 Likes

Something goes wrong :slight_smile:

Things:

xiaomivacuum:vacuum:xiaomiair [ ip="192.168.1.122", token="XXXX"]

Items:

String   Testaaa   "Testaaa"  <none>   (gMiHome)  {channel="xiaomivacuum:vacuum:xiaomiair:actions#commands"}

sitemap:

Switch item=Testaaa label="Test" mappings=["set_mode [ favorite ]"="AN", "set_mode [ off ]"="AUS"]

Shouldn’t it be like

Switch item=Testaaa label="Test" mappings=[ON="set_mode [ favorite ]", OFF="set_mode [ off ]"]

See the docs on Sitemap Mappings

Yes, pls also check with or without the quotes inside the brackets.
I think possible commands are:
“set_power[‘on’]”, “set_power[‘off’]”,
“get_prop[‘power’, ‘mode’, ‘temperature’, ‘humidity’, ‘aqi’]”, “set_mode[‘auto’]”, “led”,
“favoriteLevel”, “ledBrightness”

btw, please take the latest version of the marketplace.
There was an issue with the yesterday’s build for the non-vacuum devices.
Also there is now a testcommand switch which will execute these commands

1 Like

After Update i added as generic device

But i have problem to send commands:
my sitemap:

Switch item=Testaaa label="Test" mappings=[ set_power\[on\]="AN", set_power\[off\]="AUS" ]

my item:
String Testaaa “Testaaa” (gMiHome) {channel=“xiaomivacuum:generic:03D8E369:actions#testcommands”}

The marketplace is updated with new version that takes device time instead of server time. Maybe that fixes the communication issue
Hopefully that fixes the communication with the purifier.

Best to delete your thing and have it discovered again once the new version is installed

let me know the result

1 Like

Now that the communication to the purifier is working, I build a very experimental version for it.

pls find it here
http://www.verpaalen.com/openhab2/org.openhab.binding.miio_2.2.0.SNAPSHOT.jar

So if it works as planned, it will discover your purifier. If it gets the tolken & communicates, it will change itself to a basic device.
Once it is a basic device, it should be able to read the database and dynamically add all relevant channels.
From than it should update the channels.

notes:

  • I found that sometimes paperUI does not update correctly, doing a browser refresh may help than.
  • Sometimes the initiation goes off and the channels don’t get updated. Than pls edit the thing, change something (e.g. location) and save again.
  • If no response is received, there is a hardcoded response (for testing purposes, so don’t be surpised).

Let me know if this is working for you as well. If not, please send me a debug log (not only the errors, but from the start of the binding until the error)

If test is successful I’ll replace the marketplace binding with this one

1 Like

Hi @marcel_verpaalen,

I tried to activate your binding in the Karaf console, but turns out there’s an error:

235 | Installed |  80 | 2.2.0.SNAPSHOT         | Xiaomi Mi IO Binding
openhab> bundle:start 235
Error executing command: Error executing command on bundles:
        Error starting bundle 235: Could not resolve module: org.openhab.binding.miio [235]
  Unresolved requirement: Import-Package: com.google.gson.annotations; version="2.5.0"

openhab.log:

==> /var/log/openhab2/openhab.log <==
2017-08-13 09:34:11.713 [ERROR] [apache.karaf.shell.support.ShellUtil] - Exception caught while executing command
org.apache.karaf.shell.support.MultiException: Error executing command on bundles:
	Error starting bundle 235: Could not resolve module: org.openhab.binding.miio [235]
  Unresolved requirement: Import-Package: com.google.gson.annotations; version="2.5.0"
	at org.apache.karaf.shell.support.MultiException.throwIf(MultiException.java:61)
	at org.apache.karaf.bundle.command.BundlesCommand.doExecute(BundlesCommand.java:69)[42:org.apache.karaf.bundle.core:4.0.8]
	at org.apache.karaf.bundle.command.BundlesCommand.execute(BundlesCommand.java:54)[42:org.apache.karaf.bundle.core:4.0.8]
	at org.apache.karaf.shell.impl.action.command.ActionCommand.execute(ActionCommand.java:83)[59:org.apache.karaf.shell.core:4.0.8]
	at org.apache.karaf.shell.impl.console.osgi.secured.SecuredCommand.execute(SecuredCommand.java:67)[59:org.apache.karaf.shell.core:4.0.8]
	at org.apache.karaf.shell.impl.console.osgi.secured.SecuredCommand.execute(SecuredCommand.java:87)[59:org.apache.karaf.shell.core:4.0.8]
	at org.apache.felix.gogo.runtime.Closure.executeCmd(Closure.java:480)[59:org.apache.karaf.shell.core:4.0.8]
	at org.apache.felix.gogo.runtime.Closure.executeStatement(Closure.java:406)[59:org.apache.karaf.shell.core:4.0.8]
	at org.apache.felix.gogo.runtime.Pipe.run(Pipe.java:108)[59:org.apache.karaf.shell.core:4.0.8]
	at org.apache.felix.gogo.runtime.Closure.execute(Closure.java:182)[59:org.apache.karaf.shell.core:4.0.8]
	at org.apache.felix.gogo.runtime.Closure.execute(Closure.java:119)[59:org.apache.karaf.shell.core:4.0.8]
	at org.apache.felix.gogo.runtime.CommandSessionImpl.execute(CommandSessionImpl.java:94)[59:org.apache.karaf.shell.core:4.0.8]
	at org.apache.karaf.shell.impl.console.ConsoleSessionImpl.run(ConsoleSessionImpl.java:274)[59:org.apache.karaf.shell.core:4.0.8]
	at java.lang.Thread.run(Thread.java:745)[:1.8.0_121]
Caused by: java.lang.Exception: Error starting bundle 235: Could not resolve module: org.openhab.binding.miio [235]
  Unresolved requirement: Import-Package: com.google.gson.annotations; version="2.5.0"
	at org.apache.karaf.bundle.command.BundlesCommand.doExecute(BundlesCommand.java:66)[42:org.apache.karaf.bundle.core:4.0.8]
	... 12 more
Caused by: org.osgi.framework.BundleException: Could not resolve module: org.openhab.binding.miio [235]
  Unresolved requirement: Import-Package: com.google.gson.annotations; version="2.5.0"
	at org.eclipse.osgi.container.Module.start(Module.java:434)[org.eclipse.osgi-3.10.101.v20150820-1432.jar:]
	at org.eclipse.osgi.internal.framework.EquinoxBundle.start(EquinoxBundle.java:392)[org.eclipse.osgi-3.10.101.v20150820-1432.jar:]
	at org.apache.karaf.bundle.command.Start.executeOnBundle(Start.java:38)[42:org.apache.karaf.bundle.core:4.0.8]
	at org.apache.karaf.bundle.command.BundlesCommand.doExecute(BundlesCommand.java:64)[42:org.apache.karaf.bundle.core:4.0.8]
	... 12 more

I’m running openHAB 2.2.0~20170805035616-1 (Build #1003)

I@kubawolanin I see it had some constraints for a specific version of gson.
I removed now the version constraints and it loads in my version now (2.2.0-SNAPSHOT Build #975)
(marketplace is updated)

Thanks @marcel_verpaalen for fixing it.

Is this still the marketplace binding? Can’t find miio there so I assume it is.

Openhab Xiaomi Vacuum Binding
market:binding-3499123 - 1.0

Oops, i mean link is updated

1 Like

Just tried “org.openhab.binding.miio_2.2.0.SNAPSHOT.jar”.
The commands are now working but i don’t receive any data like temperature/humidity.

Pls PM me / post a full log in case of issues.
I don’t own the device, so unless I can see the log it is very difficult to address anything. Hence it is still quite experimental.

This evening I made an update that is hopefully fixing the mode command sending and some of the initiation issues that can prevent the proper sending of refresh commands.

The binding is now posted on the market place, so updating can be done from there as well. (pls ensure you don’t have it twice if you added it as jar in addons before)
As channel & thing definition changed, you will need to delete your thing and add it back again when the new binding is installed.

Hi Marcel

Thanks for your great works.

my case is same, I can command airpurifier but not getting any data,
log only keep have vacuum, if I remove vacuum from thing, than no update for 15 mins
I have set DEBUG

03:14:37.217 [DEBUG] [inding.miio.handler.MiIoBasicHandler] - Locating action for channel power:OFF
03:14:37.219 [DEBUG] [inding.miio.handler.MiIoBasicHandler] -  sending command set_power["off"]
03:14:37.225 [DEBUG] [ding.miio.internal.MiIoCommunication] - Send command: {"id":560,"method":"set_power","params":["off"]} -> 192.xxx.xxx.xxx (Device: 02EB9B64 token: 0203CF4904C5EC7E7645A1415D26B565)
03:14:37.263 [DEBUG] [ding.miio.internal.MiIoCommunication] - Received response from 192.xxx.xxx.xxx: {"result":["ok"],"id":560}
03:14:38.893 [DEBUG] [inding.miio.handler.MiIoBasicHandler] - Locating action for channel power:ON
03:14:38.895 [DEBUG] [inding.miio.handler.MiIoBasicHandler] -  sending command set_power["on"]
03:14:38.897 [DEBUG] [ding.miio.internal.MiIoCommunication] - Send command: {"id":561,"method":"set_power","params":["on"]} -> 192.xxx.xxx.xxx (Device: 02EB9B64 token: 0203CF4904C5EC7E7645A1415D26B565)
03:14:38.917 [DEBUG] [ding.miio.internal.MiIoCommunication] - Received response from 192.xxx.xxx.xxx: {"result":["ok"],"id":561}

it also happen on XiaoMi PM2.5, zhimi.airmonitor.v1
I create zhimi.airmonitor.v1.json at database folder
no status from device (no error as well)

my personally think XiaoMi PM2.5 is great device, it can immediately detect I am smoking, so once detected I am smoking, I set OpenHab to turn on Exhaust fan, as I am not able to getting data directly from PM2.5, so I set rules in Xaomi Gateway to turn on a plug (I waste a plug) and once openhab detect plug is on, than turn on Fan.

{
  "deviceMapping": {
    "id": [
      "zhimi.airmonitor.v1"
    ],
    "channels": [
      {
        "property": "aqi",
        "friendlyName": "Air Quality Index",
        "channel": "aqi",
        "type": "Number",
        "refresh": true,
        "ChannelGroup": "Status",
        "actions": []
      },
      {
        "property": "battery",
        "friendlyName": "Battery",
        "channel": "battery",
        "type": "Number",
        "refresh": true,
        "ChannelGroup": "Status",
        "actions": []
      },
      {
        "property": "usb_state",
        "friendlyName": "USB State",
        "channel": "usb_state",
        "type": "Number",
        "refresh": true,
        "ChannelGroup": "Status",
        "actions": []
      }
    ]
  }
}

Thanks very much for the mapping file. I’m not 100% sure what is the version that I published wrt to the miio:basic
I’m seeing some fundamental issue with the binding, I’m rewriting the communications piece to work asynchronous.

Made many small improvements including the channels/refresh piece, I see you’re mapping file I think you already have it according to the last definition. I’ll include it

I’ll try to push a more updated version somewhere this week

HI,
Thanks, I am so excite and wait for your coming update, Thank you for your great works
in fact I dig some more, I found with the most update miio script, use below command

miio --control 192.168.101.245 --method get_prop --params '["aqi","power","battery","usb_state"]'

will have return as

 INFO  Attempting to control 192.168.xxx.xxx

 INFO  Got result:
[
  149,
  "on",
  100,
  "on"
]

at this moment, I don’t found further command from PM2.5, hope above info will help you out, also the mapping file I create earlier made a mistake,usb_state data type should be string or switch

Hi I am new to openhanded and programming as well. I have couple of Xiaomi devices Including Air purifier 2. I added org.openhab.binding.miio_2.2.0.SNAPSHOT.jar in addon folder and was able to add Xiaomi Air purifier manually as things. Thanks a lot.
I have one issue here, As I have not much knowledge in programming I use paper UI all the time. I am trying to Add Swtich to power on and off Air purifier, but power channel is ONOFF type not switch and even when try to link that I get error. Though I can link buzzer, led aqi etc. But not power. Below is the screen shot