Skip to content

Commit

Permalink
Merge pull request #4 from salamwaddah/purifier-3
Browse files Browse the repository at this point in the history
Support Purifier 3
  • Loading branch information
salamwaddah authored Jan 9, 2021
2 parents 8df2bad + 06a5261 commit 96014f3
Show file tree
Hide file tree
Showing 3 changed files with 269 additions and 3 deletions.
14 changes: 12 additions & 2 deletions docs/devices/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,25 +90,35 @@ __Note:__ This table does not include Aqara (Smart Home Gateway) devices as thei
Id | Type | Auto-token | Support | Note
--------------------------|-------------------|------------|--------------|------
`zhimi.airpurifier.m1` | Air Purifier | Yes | ✅ Good |
`zhimi.airpurifier.v1` | Air Purifier` | Yes | ✅ Good |
`zhimi.airpurifier.v1` | Air Purifier | Yes | ✅ Good |
`zhimi.airpurifier.v2` | Air Purifier | Yes | ✅ Good |
`zhimi.airpurifier.v3` | Air Purifier | Unknown | ⚠️ Untested |
`zhimi.airpurifier.v4` | - | Unknown | ⚠️ Generic | Testing needed to check compatibility.
`zhimi.airpurifier.v5` | - | Unknown | ⚠️ Generic | Testing needed to check compatibility.
`zhimi.airpurifier.v6` | Air Purifier | Yes | ✅ Basic |
`zhimi.humidifier.v1` | Humidifier | Unknown | ⚠️ Untested |
`zhimi.humidifier.ca1` | Humidifier | Unknown | ⚠️ Untested |
`chuangmi.plug.m1` | Power plug | Yes | ✅ Good |
`chuangmi.plug.v1` | Power plug | Yes | ✅ Good |
`chuangmi.plug.v2` | Power plug | Yes | ✅ Good |
`chuangmi.plug.v3` | Power plug | Unknown | ⚠️ Untested |
`qmi.powerstrip.v1` | Power strip | Yes | ⚠️ Untested |
`zimi.powerstrip.v2` | Power strip | Yes | ⚠️ Untested |
`rockrobo.vaccum.v1` | Vacuum | No | ✅ Basic | DND, timers and mapping features are not supported.
`rockrobo.vaccum.s5` | Vacuum | No | ✅ Basic | DND, timers and mapping features are not supported.
`roborock.vacuum.s5e` | Vacuum | No | ⚠️ Untested |
`roborock.vacuum.a10` | Vacuum | No | ⚠️ Untested |
`roborock.vacuum.m1s` | Vacuum | No | ⚠️ Untested |
`lumi.gateway.v1` | Generic | Yes | ⚠️ Generic | API used to access sub devices not supported.
`lumi.gateway.v2` | Gateway | Yes | ✅ Basic |
`lumi.gateway.v3` | Gateway | Yes | ✅ Basic |
`yeelink.light.lamp1` | Light | No | ✅ Good |
`yeelink.light.lamp2` | Light | No | ⚠️ Untested |
`yeelink.light.mono1` | Light | No | ✅ Good |
`yeelink.light.color1` | Light | No | ✅ Good |
`yeelink.light.color2` | Light | No | ⚠️ Untested |
`yeelink.light.color3` | Light | No | ⚠️ Untested |
`yeelink.light.color4` | Light | No | ⚠️ Untested |
`yeelink.light.color5` | Light | No | ✅ Good | Verified with real device.
`yeelink.light.strip1` | Light | No | ⚠️ Untested | Support added, verification with real device needed.
`yeelink.light.strip1` | Light | No | ⚠️ Untested |
`yeelink.light.strip2` | Light | No | ⚠️ Untested |
240 changes: 240 additions & 0 deletions lib/devices/air-purifier3.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
'use strict';

const { AirPurifier } = require('abstract-things/climate');
const MiioApi = require('../device');

const Power = require('./capabilities/power');
const Mode = require('./capabilities/mode');
const SwitchableLED = require('./capabilities/switchable-led');
const LEDBrightness = require('./capabilities/changeable-led-brightness');
const Buzzer = require('./capabilities/buzzer');
const { Temperature, Humidity, AQI } = require('./capabilities/sensor');

/**
* Abstraction over a Mi Air Purifier.
*
* Air Purifiers have a mode that indicates if is on or not. Changing the mode
* to `idle` will power off the device, all other modes will power on the
* device.
*/
module.exports = class extends AirPurifier
.with(MiioApi, Power, Mode, Temperature, Humidity, AQI,
SwitchableLED, LEDBrightness, Buzzer)
{

get serviceMapping() {
return {
power: { siid: 2, piid: 2 },
mode: {
siid: 2,
piid: 5,
mapping: (mode) => {
switch (mode) {
case 'auto': return 0;
case 'sleep': return 1;
case 'favorite': return 2;
case 'idle:': return 3;
default:
return 0;

}
}
},
temp_dec: { siid: 3, piid: 8 },
humidity: { siid: 3, piid: 7 },
aqi: { siid: 3, piid: 6 },
favorite_level: {
siid: 10,
piid: 10,
mapping: (level) => {
return Math.round(level/16*14);
}
},
filter1_life: { siid: 4, piid: 3 },
f1_hour_used: { siid: 4, piid: 5 },
use_time: { siid: 12, piid: 1 },
led: { siid: 6, piid: 6},
led_b: { siid: 6, piid: 1 },
buzzer: { siid: 5, piid: 1 }
};
}

getServiceProperty(prop) {
return {
did: String(this.handle.api.id),
siid: this.serviceMapping[prop].siid,
piid: this.serviceMapping[prop].piid
};
}

static get type() {
return 'miio:air-purifier';
}

loadProperties(props) {
// Rewrite property names to device internal ones
props = props.map(key => this._reversePropertyDefinitions[key] || key);

const propObjects = props.filter(prop => this.serviceMapping[prop]).map(this.getServiceProperty.bind(this));

return this.call('get_properties', propObjects).then(result => {
const obj = {};
for(let i=0; i<result.length; i++) {
this._pushProperty(obj, props[i], result[i].value);
}
return obj;
});
}

constructor(options) {
super(options);

// Define the power property
this.defineProperty('power');

// Set the mode property and supported modes
this.defineProperty('mode', {
mapper: v => {
switch(v) {
case 0: return 'auto';
case 1: return 'silent';
case 2: return 'favorite';
case 3: return 'idle';
}
}
});
this.updateModes([
'idle',
'auto',
'silent',
'favorite'
]);

// Sensor value for Temperature capability
this.defineProperty('temp_dec', {
name: 'temperature'
});

// Sensor value for RelativeHumidity capability
this.defineProperty('humidity');

// Sensor value used for AQI (PM2.5) capability
this.defineProperty('aqi');

// The favorite level
this.defineProperty('favorite_level', {
name: 'favoriteLevel',
mapper: v => Math.round(v/14*16)
});

// Info about usage
this.defineProperty('filter1_life', {
name: 'filterLifeRemaining'
});
this.defineProperty('f1_hour_used', {
name: 'filterHoursUsed'
});
this.defineProperty('use_time', {
name: 'useTime'
});

// State for SwitchableLED capability
this.defineProperty('led');

this.defineProperty('led_b', {
name: 'ledBrightness',
mapper: v => {
switch(v) {
case 0:
return 'bright';
case 1:
return 'dim';
case 2:
return 'off';
default:
return 'unknown';
}
}
});

// Buzzer and beeping
this.defineProperty('buzzer');
}

changePower(power) {
const attributes = [];

if (!power) {
// change mode to idle when turning off
attributes.push(Object.assign(this.getServiceProperty('mode'), { value: 3 }));
}

attributes.push(Object.assign({ value: power }, this.getServiceProperty('power')));

return this.call('set_properties', attributes, {
refresh: [ 'power', 'mode' ],
refreshDelay: 200
});
}

/**
* Perform a mode change as requested by `mode(string)` or
* `setMode(string)`.
*/
changeMode(mode) {
const realMode = this.serviceMapping['mode'].mapping(mode);

return this.call('set_properties', [ Object.assign({ value: realMode }, this.getServiceProperty('mode')) ], {
refresh: [ 'power', 'mode' ],
refreshDelay: 200
})
.then(MiioApi.checkOk)
.catch(err => {
throw err.code === -5001 ? new Error('Mode `' + mode + '` not supported') : err;
});
}

/**
* Get the favorite level used when the mode is `favorite`. Between 0 and 16.
*/
favoriteLevel(level=undefined) {
if(typeof level === 'undefined') {
return Promise.resolve(this.property('favoriteLevel'));
}

return this.setFavoriteLevel(level);
}

/**
* Set the favorite level used when the mode is `favorite`, should be
* between 0 and 16.
*/
setFavoriteLevel(level) {
const realFavoriteLevel = this.serviceMapping['favorite_level'].mapping(level);

return this.call('set_properties', [ Object.assign({ value: realFavoriteLevel }, this.getServiceProperty('favorite_level')) ])
.then(() => null);
}

/**
* Set the LED brightness to either `bright`, `dim` or `off`.
*/
changeLEDBrightness(level) {
switch(level) {
case 'bright':
level = 0;
break;
case 'dim':
level = 1;
break;
case 'off':
level = 2;
break;
default:
return Promise.reject(new Error('Invalid LED brigthness: ' + level));
}

return this.call('set_properties', [ Object.assign({ value: level }, this.getServiceProperty('led_b')) ])
.then(() => null);
}
};
18 changes: 17 additions & 1 deletion lib/models.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/
const AirMonitor = require('./devices/air-monitor');
const AirPurifier = require('./devices/air-purifier');
const AirPurifier3 = require('./devices/air-purifier3');
const Gateway = require('./devices/gateway');

const Vacuum = require('./devices/vacuum');
Expand Down Expand Up @@ -33,23 +34,33 @@ module.exports = {
// Air Purifier 2S
'zhimi.airpurifier.ma2': AirPurifier,

// Air Purifier 2H
'zhimi.airpurifier.mc2': AirPurifier,

// Air Purifier 3
'zhimi.airpurifier.mb3': AirPurifier,
'zhimi.airpurifier.mb3': AirPurifier3,

'zhimi.humidifier.v1': Humidifier,
'zhimi.humidifier.ca1': Humidifier,

// Deerma Antibacterial Humidifier
'deerma.humidifier.jsq': Humidifier,

'chuangmi.plug.m1': PowerPlug,
'chuangmi.plug.v1': require('./devices/chuangmi.plug.v1'),
'chuangmi.plug.v2': PowerPlug,
'chuangmi.plug.m3': PowerPlug,
'chuangmi.plug.hmi206': PowerPlug,

'rockrobo.vacuum.v1': Vacuum,
'roborock.vacuum.s5': Vacuum,
'roborock.vacuum.s5e': Vacuum,
'roborock.vacuum.a10': Vacuum,
'roborock.vacuum.m1s': Vacuum,

'lumi.gateway.v2': Gateway.WithLightAndSensor,
'lumi.gateway.v3': Gateway.WithLightAndSensor,
'lumi.gateway.mgl03': Gateway.WithLightAndSensor,
'lumi.acpartner.v1': Gateway.Basic,
'lumi.acpartner.v2': Gateway.Basic,
'lumi.acpartner.v3': Gateway.Basic,
Expand All @@ -58,10 +69,15 @@ module.exports = {
'zimi.powerstrip.v2': PowerStrip,

'yeelink.light.lamp1': YeelightMono,
'yeelink.light.lamp2': YeelightMono,
'yeelink.light.mono1': YeelightMono,
'yeelink.light.color1': YeelightColor,
'yeelink.light.color2': YeelightColor,
'yeelink.light.color3': YeelightColor,
'yeelink.light.color4': YeelightColor,
'yeelink.light.color5': YeelightColor,
'yeelink.light.strip1': YeelightColor,
'yeelink.light.strip2': YeelightColor,

'philips.light.sread1': require('./devices/eyecare-lamp2'),
'philips.light.bulb': require('./devices/philips-light-bulb')
Expand Down

0 comments on commit 96014f3

Please sign in to comment.