Skip to content

Commit

Permalink
Add auto_off, led_disable_night, consumer_overload, consumer_connecte…
Browse files Browse the repository at this point in the history
…d, power_outage_memory for Xiaomi ZNCZ04LM plug (#1874)

* On branch lumi.plug.mmeu01.part-two there is a next step of extending functionality
of lumi.plug.mmeu01, i.e. ZNCZ04LM:
New states:
  auto_off - auto off state, to swith the plug off in case of low level of consumption;
  consumer_connected - show the status of connection of the customer to the plug;
  led_disabled - show the state of the led disable feature;
  power_outage_memory - show the state of power outage memory feature;
New switches:
  auto_off - auto off feature, to swith the plug off in case of low level of consumption;
  led_disabled - feature to set led disabled;
  power_outage_memory - feature to set power outage memory enabled or disabled;
Changes to be committed:
	modified:   converters/fromZigbee.js
	modified:   converters/toZigbee.js
	modified:   devices.js
	modified:   lib/exposes.js

* On branch lumi.plug.mmeu01.part-two
Some description of a states tunig
 Changes to be committed:
	modified:   lib/exposes.js

* On branch lumi.plug.mmeu01.part-two applying Koenkk recommendations:
- removed extra logging;
- led_disabled renamed to led-disabled_night;
- added previously empty ppayload - consumer_overload

Changes to be committed:
	modified:   converters/fromZigbee.js
	modified:   converters/toZigbee.js
	modified:   devices.js
	modified:   lib/exposes.js

* Fix lint typo

* On branch lumi.plug.mmeu01.part-two applying Koenkk's recommendation
Changed access.STATE to access.STATE_SET for changable states:
- auto_off
- power_outage_memory
- led_disabled_night

 Changes to be committed:
	modified:   lib/exposes.js

* replace case constuction by if ... if else

* fix typo in debug log

* On branch lumi.plug.mmeu01.part-two there is a fix for consumer_overload
In fact it is a value of W, not a boolean one

Changes to be committed:
	modified:   converters/fromZigbee.js
	modified:   lib/exposes.js

* Update exposes.js

* Update devices.js

* Update toZigbee.js

* Update devices.js

Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
  • Loading branch information
PeterVoronov and Koenkk authored Dec 8, 2020
1 parent 77d778a commit e258710
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 17 deletions.
31 changes: 16 additions & 15 deletions converters/fromZigbee.js
Original file line number Diff line number Diff line change
Expand Up @@ -1603,9 +1603,9 @@ const converters = {
cluster: 'aqaraOpple',
type: ['attributeReport', 'readResponse'],
convert: (model, msg, publish, options, meta) => {
if (msg.data['247']) {
const payload = {};
if (msg.data.hasOwnProperty('247')) {
const data = msg.data['247'];
const payload = {};
// Xiaomi struct parsing
const length = data.length;
// if (meta.logger) meta.logger.debug(`plug.mmeu01: Xiaomi struct: length ${length}`);
Expand Down Expand Up @@ -1644,22 +1644,23 @@ const converters = {
i += 5;
break;
default:
// if (meta.logger) meta.logger.debug(`plug.mmeu01: unknown vtype=${data[i+1]}, pos=${i+1}`);
if (meta.logger) meta.logger.debug(`plug.mmeu01: unknown vtype=${data[i+1]}, pos=${i+1}`);
}
payload[index] = value;
// if (meta.logger) meta.logger.debug(`plug.mmeu01: recorded index ${index} with value ${value}`);
if (index === 3) payload.temperature = calibrateAndPrecisionRoundOptions(value, options, 'temperature'); // 0x03
else if (index === 100) payload.state = value === 1 ? 'ON' : 'OFF'; // 0x64
else if (index === 149) payload.consumption = precisionRound(value, 2); // 0x95
else if (index === 150) payload.voltage = precisionRound(value * 0.1, 1); // 0x96
else if (index === 151) payload.current = precisionRound(value * 0.001, 4); // 0x97
else if (index === 152) payload.power = precisionRound(value, 2); // 0x98
else if (meta.logger) meta.logger.debug(`plug.mmeu01: unknown index ${index} with value ${value}`);
}
return {
state: payload['100'] === 1 ? 'ON' : 'OFF',
power: precisionRound(payload['152'], 2),
voltage: precisionRound(payload['150'] * 0.1, 1),
current: precisionRound((payload['151'] * 0.001), 4),
// Consumption is deprecated
consumption: precisionRound(payload['149'], 2),
energy: precisionRound(payload['149'], 2),
temperature: calibrateAndPrecisionRoundOptions(payload['3'], options, 'temperature'),
};
}
if (msg.data.hasOwnProperty('513')) payload.power_outage_memory = msg.data['513'] === 1; // 0x0201
if (msg.data.hasOwnProperty('514')) payload.auto_off = msg.data['514'] === 1; // 0x0202
if (msg.data.hasOwnProperty('515')) payload.led_disabled_night = msg.data['515'] === 1; // 0x0203
if (msg.data.hasOwnProperty('519')) payload.consumer_connected = msg.data['519'] === 1; // 0x0207
if (msg.data.hasOwnProperty('523')) payload.consumer_overload = precisionRound(msg.data['523'], 2); // 0x020B
return payload;
},
},
xiaomi_battery: {
Expand Down
22 changes: 22 additions & 0 deletions converters/toZigbee.js
Original file line number Diff line number Diff line change
Expand Up @@ -1377,6 +1377,28 @@ const converters = {
await endpoint.read('genAnalogInput', ['presentValue']);
},
},
xiaomi_auto_off: {
key: ['auto_off'],
convertSet: async (entity, key, value, meta) => {
if (['ZNCZ04LM'].includes(meta.mapped.model)) {
await entity.write('aqaraOpple', {0x0202: {value: value ? 1 : 0, type: 0x10}}, options.xiaomi);
} else {
throw new Error('Not supported');
}
return {state: {auto_off: value}};
},
},
xiaomi_led_disabled_night: {
key: ['led_disabled_night'],
convertSet: async (entity, key, value, meta) => {
if (['ZNCZ04LM'].includes(meta.mapped.model)) {
await entity.write('aqaraOpple', {0x0203: {value: value ? 1 : 0, type: 0x10}}, options.xiaomi);
} else {
throw new Error('Not supported');
}
return {state: {led_disabled_night: value}};
},
},
xiaomi_switch_operation_mode: {
key: ['operation_mode'],
convertSet: async (entity, key, value, meta) => {
Expand Down
7 changes: 5 additions & 2 deletions devices.js
Original file line number Diff line number Diff line change
Expand Up @@ -1041,8 +1041,11 @@ const devices = [
fz.ignore_occupancy_report,
fz.ignore_illuminance_report, fz.ignore_time_read,
],
toZigbee: [tz.on_off, tz.xiaomi_power, tz.xiaomi_switch_power_outage_memory],
exposes: [e.switch(), e.power(), e.energy(), e.temperature(), e.voltage(), e.current()],
toZigbee: [tz.on_off, tz.xiaomi_power, tz.xiaomi_switch_power_outage_memory, tz.xiaomi_auto_off, tz.xiaomi_led_disabled_night],
exposes: [
e.switch(), e.power(), e.energy(), e.temperature(), e.voltage(), e.current(), e.auto_off(),
e.consumer_connected(), e.consumer_overload(), e.led_disabled_night(), e.power_outage_memory(),
],
},
{
zigbeeModel: ['lumi.plug.maus01'],
Expand Down
5 changes: 5 additions & 0 deletions lib/exposes.js
Original file line number Diff line number Diff line change
Expand Up @@ -303,12 +303,15 @@ module.exports = {
presets: {
action: (values) => new Enum('action', access.STATE, values).withDescription('Triggered action (e.g. a button click)'),
aqi: () => new Numeric('aqi', access.STATE).withDescription('Air quality index'),
auto_off: () => new Binary('auto_off', access.STATE_SET, true, false).withDescription('Turn the device automatically off when attached device consumes little power'),
battery: () => new Numeric('battery', access.STATE).withUnit('%').withDescription('Remaining battery in %').withValueMin(0).withValueMax(100),
battery_low: () => new Binary('battery_low', access.STATE, true, false).withDescription('Indicates if the battery of this device is almost empty'),
carbon_monoxide: () => new Binary('carbon_monoxide', access.STATE, true, false).withDescription('Indicates if CO2 (carbon monoxide) is detected'),
child_lock: () => new Lock().withState('child_lock', 'LOCK', 'UNLOCK', 'Enables/disables physical input on the device'),
co2: () => new Numeric('co2', access.STATE).withUnit('ppm').withDescription('The measured CO2 (carbon monoxide) value'),
contact: () => new Binary('contact', access.STATE, false, true).withDescription('Indicates if the contact is closed (= true) or open (= false)'),
consumer_connected: () => new Binary('consumer_connected', access.STATE, true, false).withDescription('Indicates whether attached device consumes power'),
consumer_overload: () => new Numeric('consumer_overload', access.STATE, true, false).withUnit('W').withDescription('Indicates with how many Watts the maximum possible power consumption is exceeded'),
cover_position: () => new Cover().withPosition(),
cover_position_tilt: () => new Cover().withPosition().withTilt(),
cpu_temperature: () => new Numeric('cpu_temperature', access.STATE).withUnit('°C').withDescription('Temperature of the CPU'),
Expand All @@ -325,6 +328,7 @@ module.exports = {
illuminance: () => new Numeric('illuminance', access.STATE).withDescription('Raw measured illuminance'),
illuminance_lux: () => new Numeric('illuminance_lux', access.STATE).withUnit('lx').withDescription('Measured illuminance in lux'),
keypad_lockout: () => new Lock().withState('keypad_lockout', '1', '0', 'Enables/disables physical input on the device'),
led_disabled_night: () => new Binary('led_disabled_night', access.STATE_SET, true, false).withDescription('Enable/disable the LED at night'),
light_brightness: () => new Light().withBrightness(),
light_brightness_colorhs: () => new Light().withBrightness().withColor(['hs']),
light_brightness_colortemp: () => new Light().withBrightness().withColorTemp(),
Expand All @@ -342,6 +346,7 @@ module.exports = {
pm25: () => new Numeric('pm25', access.STATE).withUnit('µg/m³').withDescription('Measured PM2.5 (particulate matter) concentration'),
position: () => new Numeric('position', access.STATE).withUnit('%').withDescription('Position'),
power: () => new Numeric('power', access.STATE_GET).withUnit('W').withDescription('Instantaneous measured power'),
power_outage_memory: () => new Binary('power_outage_memory', access.STATE_SET, true, false).withDescription('Enable/disable the power outage memory, this recovers the on/off mode after power failure'),
presence: () => new Binary('presence', access.STATE, true, false).withDescription('Indicates whether the device detected presence'),
pressure: () => new Numeric('pressure', access.STATE).withUnit('hPa').withDescription('The measured atmospheric pressure'),
smoke: () => new Binary('smoke', access.STATE, true, false).withDescription('Indicates whether the device detected smoke'),
Expand Down

0 comments on commit e258710

Please sign in to comment.