Skip to content

Commit

Permalink
Add 067772 and various Legrand fixes (#3832)
Browse files Browse the repository at this point in the history
* add Legrand 067772

* fixes

* Remove unnecessary spaces and comments
* Refactoring `legrand_device_mode`
* Change `exposes.enum('device_mode')` to `exposes.binary('device_mode')` for Legrand 064882
* Removal of `legrand_settingEnableDimmer`: duplication of `legrand_deviceMode`
* Fix `led_when_on`, `permanent_led`: did not show their status

* update conditional operator
  • Loading branch information
EXP-Carthage authored Feb 6, 2022
1 parent 2eaf758 commit 53f0a8a
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 46 deletions.
33 changes: 17 additions & 16 deletions converters/fromZigbee.js
Original file line number Diff line number Diff line change
Expand Up @@ -5059,27 +5059,28 @@ const converters = {
}
},
},
legrand_device_mode: {
legrand_cluster_fc01: {
cluster: 'manuSpecificLegrandDevices',
type: ['readResponse'],
convert: (model, msg, publish, options, meta) => {
const payload = {};
const option0 = msg.data['0'];
// Beware that mode depends on device type
// contactor
if (option0 === 0x0003) payload.device_mode = 'switch';
else if (option0 === 0x0004) payload.device_mode = 'auto';
// dimmer
else if (option0 === 0x0101) payload.device_mode = 'dimmer_on';
else if (option0 === 0x0100) payload.device_mode = 'dimmer_off';
// pilot wire
else if (option0 === 0x0002) payload.device_mode = 'pilot_on';
else if (option0 === 0x0001) payload.device_mode = 'pilot_off';
// unknown case
else {
meta.logger.warn(`device_mode ${option0} not recognized, please fix me`);
payload.device_mode = 'unknown';

if (msg.data.hasOwnProperty('0')) {
const option0 = msg.data['0'];

if (option0 === 0x0001) payload.device_mode = 'pilot_off';
else if (option0 === 0x0002) payload.device_mode = 'pilot_on';
else if (option0 === 0x0003) payload.device_mode = 'switch';
else if (option0 === 0x0004) payload.device_mode = 'auto';
else if (option0 === 0x0100) payload.device_mode = 'dimmer_off';
else if (option0 === 0x0101) payload.device_mode = 'dimmer_on';
else {
meta.logger.warn(`device_mode ${option0} not recognized, please fix me`);
payload.device_mode = 'unknown';
}
}
if (msg.data.hasOwnProperty('1')) payload.permanent_led = msg.data['1'] === 0x00 ? 'OFF' : 'ON';
if (msg.data.hasOwnProperty('2')) payload.led_when_on = msg.data['2'] === 0x00 ? 'OFF' : 'ON';
return payload;
},
},
Expand Down
15 changes: 7 additions & 8 deletions converters/toZigbee.js
Original file line number Diff line number Diff line change
Expand Up @@ -4523,6 +4523,10 @@ const converters = {
const enableLedIfOn = value === 'ON' || (value === 'OFF' ? false : !!value);
const payload = {1: {value: enableLedIfOn, type: 16}};
await entity.write('manuSpecificLegrandDevices', payload, manufacturerOptions.legrand);
return {state: {'permanent_led': value}};
},
convertGet: async (entity, key, meta) => {
await entity.read('manuSpecificLegrandDevices', [0x0001], manufacturerOptions.legrand);
},
},
legrand_settingEnableLedIfOn: {
Expand All @@ -4534,15 +4538,10 @@ const converters = {
const enableLedIfOn = value === 'ON' || (value === 'OFF' ? false : !!value);
const payload = {2: {value: enableLedIfOn, type: 16}};
await entity.write('manuSpecificLegrandDevices', payload, manufacturerOptions.legrand);
return {state: {'led_when_on': value}};
},
},
legrand_settingEnableDimmer: {
key: ['dimmer_enabled'],
convertSet: async (entity, key, value, meta) => {
// enable the dimmer, requires a recent firmware on the device
const enableDimmer = value === 'ON' || (value === 'OFF' ? false : !!value);
const payload = {0: {value: enableDimmer ? 0x0101 : 0x0100, type: 9}};
await entity.write('manuSpecificLegrandDevices', payload, manufacturerOptions.legrand);
convertGet: async (entity, key, meta) => {
await entity.read('manuSpecificLegrandDevices', [0x0002], manufacturerOptions.legrand);
},
},
legrand_deviceMode: {
Expand Down
20 changes: 10 additions & 10 deletions devices/bticino.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ module.exports = [
model: 'K4003C/L4003C/N4003C/NT4003C',
vendor: 'BTicino',
description: 'Light switch with neutral',
fromZigbee: [fz.identify, fz.on_off, fz.K4003C_binary_input],
fromZigbee: [fz.identify, fz.on_off, fz.K4003C_binary_input, fz.legrand_cluster_fc01],
toZigbee: [tz.on_off, tz.legrand_settingAlwaysEnableLed, tz.legrand_settingEnableLedIfOn, tz.legrand_identify],
exposes: [
e.switch(), e.action(['identify', 'on', 'off']),
exposes.binary('permanent_led', ea.STATE_SET, 'ON', 'OFF').withDescription('Enable or disable the permanent blue LED'),
exposes.binary('led_when_on', ea.STATE_SET, 'ON', 'OFF').withDescription('Enables the LED when the light is on'),
exposes.binary('permanent_led', ea.ALL, 'ON', 'OFF').withDescription('Enable or disable the permanent blue LED'),
exposes.binary('led_when_on', ea.ALL, 'ON', 'OFF').withDescription('Enables the LED when the light is on'),
],
configure: async (device, coordinatorEndpoint, logger) => {
const endpoint = device.getEndpoint(1);
Expand All @@ -30,17 +30,17 @@ module.exports = [
vendor: 'BTicino',
description: 'Dimmer switch with neutral',
extend: extend.light_onoff_brightness({noConfigure: true}),
fromZigbee: [fz.brightness, fz.identify, fz.on_off, fz.lighting_ballast_configuration],
fromZigbee: [fz.brightness, fz.identify, fz.on_off, fz.lighting_ballast_configuration, fz.legrand_cluster_fc01],
toZigbee: [tz.light_onoff_brightness, tz.legrand_settingAlwaysEnableLed, tz.legrand_settingEnableLedIfOn,
tz.legrand_settingEnableDimmer, tz.legrand_identify, tz.ballast_config],
tz.legrand_deviceMode, tz.legrand_identify, tz.ballast_config],
exposes: [e.light_brightness(),
exposes.numeric('ballast_minimum_level', ea.ALL).withValueMin(1).withValueMax(254)
.withDescription('Specifies the minimum brightness value'),
exposes.numeric('ballast_maximum_level', ea.ALL).withValueMin(1).withValueMax(254)
.withDescription('Specifies the maximum brightness value'),
exposes.binary('dimmer_enabled', ea.STATE_SET, 'ON', 'OFF').withDescription('Allow the device to change brightness'),
exposes.binary('permanent_led', ea.STATE_SET, 'ON', 'OFF').withDescription('Enable or disable the permanent blue LED'),
exposes.binary('led_when_on', ea.STATE_SET, 'ON', 'OFF').withDescription('Enables the LED when the light is on')],
exposes.binary('device_mode', ea.ALL, 'dimmer_on', 'dimmer_off').withDescription('Allow the device to change brightness'),
exposes.binary('permanent_led', ea.ALL, 'ON', 'OFF').withDescription('Enable or disable the permanent blue LED'),
exposes.binary('led_when_on', ea.ALL, 'ON', 'OFF').withDescription('Enables the LED when the light is on')],
configure: async (device, coordinatorEndpoint, logger) => {
await extend.light_onoff_brightness().configure(device, coordinatorEndpoint, logger);
const endpoint = device.getEndpoint(1);
Expand Down Expand Up @@ -76,10 +76,10 @@ module.exports = [
description: 'DIN power consumption module (same as Legrand 412015)',
vendor: 'BTicino',
extend: extend.switch(),
fromZigbee: [fz.identify, fz.on_off, fz.electrical_measurement, fz.legrand_device_mode, fz.ignore_basic_report, fz.ignore_genOta],
fromZigbee: [fz.identify, fz.on_off, fz.electrical_measurement, fz.legrand_cluster_fc01, fz.ignore_basic_report, fz.ignore_genOta],
toZigbee: [tz.legrand_deviceMode, tz.on_off, tz.legrand_identify, tz.electrical_measurement_power],
exposes: [exposes.switch().withState('state', true, 'On/off (works only if device is in "switch" mode)'),
e.power().withAccess(ea.STATE_GET), exposes.enum( 'device_mode', ea.ALL, ['switch', 'auto'])
e.power().withAccess(ea.STATE_GET), exposes.enum('device_mode', ea.ALL, ['switch', 'auto'])
.withDescription('switch: allow on/off, auto will use wired action via C1/C2 on contactor for example with HC/HP')],
configure: async (device, coordinatorEndpoint, logger) => {
const endpoint = device.getEndpoint(1);
Expand Down
45 changes: 33 additions & 12 deletions devices/legrand.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ module.exports = [
description: 'Legrand (or Bticino) DIN contactor module',
vendor: 'Legrand',
extend: extend.switch(),
fromZigbee: [fz.identify, fz.on_off, fz.electrical_measurement, fz.legrand_device_mode, fz.ignore_basic_report, fz.ignore_genOta],
fromZigbee: [fz.identify, fz.on_off, fz.electrical_measurement, fz.legrand_cluster_fc01, fz.ignore_basic_report, fz.ignore_genOta],
toZigbee: [tz.legrand_deviceMode, tz.on_off, tz.legrand_identify, tz.electrical_measurement_power],
exposes: [exposes.switch().withState('state', true, 'On/off (works only if device is in "switch" mode)'),
e.power().withAccess(ea.STATE_GET), exposes.enum( 'device_mode', ea.ALL, ['switch', 'auto'])
e.power().withAccess(ea.STATE_GET), exposes.enum('device_mode', ea.ALL, ['switch', 'auto'])
.withDescription('switch: allow on/off, auto will use wired action via C1/C2 on contactor for example with HC/HP')],
configure: async (device, coordinatorEndpoint, logger) => {
const endpoint = device.getEndpoint(1);
Expand All @@ -56,10 +56,10 @@ module.exports = [
description: 'Legrand (or Bticino) DIN smart relay for light control (note: Legrand 412170 may be similar to Bticino FC80RC)',
vendor: 'Legrand',
extend: extend.switch(),
fromZigbee: [fz.identify, fz.on_off, fz.electrical_measurement, fz.legrand_device_mode, fz.ignore_basic_report, fz.ignore_genOta],
fromZigbee: [fz.identify, fz.on_off, fz.electrical_measurement, fz.legrand_cluster_fc01, fz.ignore_basic_report, fz.ignore_genOta],
toZigbee: [tz.legrand_deviceMode, tz.on_off, tz.legrand_identify, tz.electrical_measurement_power],
exposes: [exposes.switch().withState('state', true, 'On/off (works only if device is in "switch" mode)'),
e.power().withAccess(ea.STATE_GET), exposes.enum( 'device_mode', ea.ALL, ['switch', 'auto'])
e.power().withAccess(ea.STATE_GET), exposes.enum('device_mode', ea.ALL, ['switch', 'auto'])
.withDescription('switch: allow on/off, auto will use wired action via C1/C2 on teleruptor with buttons')],
configure: async (device, coordinatorEndpoint, logger) => {
const endpoint = device.getEndpoint(1);
Expand Down Expand Up @@ -171,20 +171,19 @@ module.exports = [
zigbeeModel: [' Dimmer switch w/o neutral\u0000\u0000\u0000\u0000\u0000'],
model: '067771',
vendor: 'Legrand',
// led blink RED when battery is low
description: 'Wired switch without neutral',
extend: extend.light_onoff_brightness({noConfigure: true}),
fromZigbee: [fz.brightness, fz.identify, fz.on_off, fz.lighting_ballast_configuration],
fromZigbee: [fz.brightness, fz.identify, fz.on_off, fz.lighting_ballast_configuration, fz.legrand_cluster_fc01],
toZigbee: [tz.light_onoff_brightness, tz.legrand_settingAlwaysEnableLed, tz.legrand_settingEnableLedIfOn,
tz.legrand_settingEnableDimmer, tz.legrand_identify, tz.ballast_config],
tz.legrand_deviceMode, tz.legrand_identify, tz.ballast_config],
exposes: [e.light_brightness(),
exposes.numeric('ballast_minimum_level', ea.ALL).withValueMin(1).withValueMax(254)
.withDescription('Specifies the minimum brightness value'),
exposes.numeric('ballast_maximum_level', ea.ALL).withValueMin(1).withValueMax(254)
.withDescription('Specifies the maximum brightness value'),
exposes.binary('dimmer_enabled', ea.STATE_SET, 'ON', 'OFF').withDescription('Allow the device to change brightness'),
exposes.binary('permanent_led', ea.STATE_SET, 'ON', 'OFF').withDescription('Enable or disable the permanent blue LED'),
exposes.binary('led_when_on', ea.STATE_SET, 'ON', 'OFF').withDescription('Enables the LED when the light is on')],
exposes.binary('device_mode', ea.ALL, 'dimmer_on', 'dimmer_off').withDescription('Allow the device to change brightness'),
exposes.binary('permanent_led', ea.ALL, 'ON', 'OFF').withDescription('Enable or disable the permanent blue LED'),
exposes.binary('led_when_on', ea.ALL, 'ON', 'OFF').withDescription('Enables the LED when the light is on')],
configure: async (device, coordinatorEndpoint, logger) => {
await extend.light_onoff_brightness().configure(device, coordinatorEndpoint, logger);
const endpoint = device.getEndpoint(1);
Expand Down Expand Up @@ -306,9 +305,9 @@ module.exports = [
model: '064882',
vendor: 'Legrand',
description: 'Cable outlet with pilot wire and consumption measurement',
fromZigbee: [fz.legrand_device_mode, fz.legrand_cable_outlet_mode, fz.on_off, fz.electrical_measurement],
fromZigbee: [fz.legrand_cluster_fc01, fz.legrand_cable_outlet_mode, fz.on_off, fz.electrical_measurement],
toZigbee: [tz.legrand_deviceMode, tz.legrand_cableOutletMode, tz.on_off, tz.electrical_measurement_power],
exposes: [exposes.enum('device_mode', ea.ALL, ['pilot_off', 'pilot_on']),
exposes: [exposes.binary('device_mode', ea.ALL, 'pilot_on', 'pilot_off'),
exposes.enum('cable_outlet_mode', ea.ALL, ['comfort', 'comfort-1', 'comfort-2', 'eco', 'frost_protection', 'off']),
exposes.switch().withState('state', true, 'Works only when the pilot wire is deactivated'),
e.power().withAccess(ea.STATE_GET)],
Expand All @@ -320,4 +319,26 @@ module.exports = [
await reporting.activePower(endpoint);
},
},
{
zigbeeModel: [' NLIS - Double light switch\u0000\u0000\u0000\u0000'],
model: '067772',
vendor: 'Legrand',
description: 'Double wired switch with neutral',
fromZigbee: [fz.on_off, fz.legrand_cluster_fc01],
toZigbee: [tz.on_off, tz.legrand_settingAlwaysEnableLed, tz.legrand_settingEnableLedIfOn],
exposes: [e.switch().withEndpoint('left'),
e.switch().withEndpoint('right'),
exposes.binary('permanent_led', ea.ALL, 'ON', 'OFF').withDescription('Enable or disable the permanent blue LED'),
exposes.binary('led_when_on', ea.ALL, 'ON', 'OFF').withDescription('Enables the LED when the light is on')],
meta: {multiEndpoint: true},
configure: async (device, coordinatorEndpoint, logger) => {
const endpointLeft = device.getEndpoint(1);
await reporting.bind(endpointLeft, coordinatorEndpoint, ['genOnOff']);
const endpointRight = device.getEndpoint(2);
await reporting.bind(endpointRight, coordinatorEndpoint, ['genOnOff']);
},
endpoint: (device) => {
return {left: 1, right: 2};
},
},
];

0 comments on commit 53f0a8a

Please sign in to comment.