Skip to content

Instantly share code, notes, and snippets.

@3lbios
Created January 12, 2020 12:09
Show Gist options
  • Save 3lbios/81ac3989071a13a7fb43d51279d4cd9b to your computer and use it in GitHub Desktop.
Save 3lbios/81ac3989071a13a7fb43d51279d4cd9b to your computer and use it in GitHub Desktop.
Xiaomi Mi Purifier automation daemon in Perl
#!/usr/bin/perl
use strict;
use warnings;
# how to install mirobo and discover Mi devices:
# https://python-miio.readthedocs.io/en/latest/index.html
# substitute device IP and token from 'mirobo discover'
my $common_cmd = 'mirobo --ip <PURIFIER_IP> --token <PURIFIER_TOKEN>';
my $aqi_cmd = $common_cmd . ' raw-command get_prop \'["aqi"]\' 2>&1';
my $mode_cmd = $common_cmd . ' raw-command get_prop \'["mode"]\' 2>&1';
my $setfav_cmd = $common_cmd . ' raw-command set_mode \'["favorite"]\' 2>&1';
my $setauto_cmd = $common_cmd . ' raw-command set_mode \'["auto"]\' 2>&1';
# check every 90s
use constant LOOP_DELAY => 90;
use constant DELAY_AFTER_CMD => 2;
# count how many times in a row AQI was below or above threshold
# this reduces oscillation - only switch from favorite to auto after LOWAQI_CNTMAX samples below threshold
# note: auto assumed to be much lower power than favorite
use constant LOWAQI_CNTMAX => 8; # prefer to stay in favorite mode longer
use constant HIGHAQI_CNTMAX => 2; # two high aqi samples in a row -> switch to favorite
use constant AQI_THRESHOLD_HIGH => 24;
use constant AQI_THRESHOLD_LOW => 11;
my $lowaqi_cnt = 0;
my $highaqi_cnt = 0;
my $cur_mode = "";
while(1) {
# get current mode
$cur_mode = "";
my $out = `$mode_cmd`;
sleep(DELAY_AFTER_CMD);
if ($? == 0 and $out !~ /error/i) {
my @words = split /\n/, $out;
if (@words >= 2) {
$words[1] =~ /\['(\w+)'\]/;
$cur_mode = $1;
}
}
# get AQI
$out = `$aqi_cmd`;
sleep(DELAY_AFTER_CMD);
if ($? == 0 and $out !~ /error/i) {
my $aqi = 0;
my @words = split /\n/, $out;
if (@words >= 2) {
$words[1] =~ /\[(\d+)\]/;
$aqi = $1;
}
# set fav mode on high AQI, auto mode on low AQI
if ($aqi >= AQI_THRESHOLD_HIGH) {
# reset lowaqi counter on any high AQI event
$lowaqi_cnt = 0;
$highaqi_cnt += 1;
if ($highaqi_cnt >= HIGHAQI_CNTMAX) {
if ($cur_mode !~ /favorite/) {
`$setfav_cmd`;
sleep(DELAY_AFTER_CMD);
}
$highaqi_cnt = 0;
}
}
if ($aqi <= AQI_THRESHOLD_LOW) {
$highaqi_cnt = 0;
$lowaqi_cnt += 1;
if ($lowaqi_cnt >= LOWAQI_CNTMAX) {
if ($cur_mode !~ /auto/) {
`$setauto_cmd`;
sleep(DELAY_AFTER_CMD);
}
$lowaqi_cnt = 0;
}
}
} else {
# got error response from xiaomi
# calling set_mode seems to unblock xiaomi (weird)
$lowaqi_cnt = 0;
$highaqi_cnt = 0;
if ($cur_mode =~ /favorite/) {
`$setfav_cmd`;
} else {
`$setauto_cmd`;
}
sleep(DELAY_AFTER_CMD);
}
sleep(LOOP_DELAY);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment