zigbee-herdsman-converters 14.0.265 → 14.0.269
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/converters/fromZigbee.js +10 -0
- package/converters/toZigbee.js +40 -38
- package/devices/philips.js +9 -0
- package/devices/saswell.js +1 -0
- package/devices/tuya.js +34 -0
- package/lib/exposes.js +1 -1
- package/lib/utils.js +27 -2
- package/npm-shrinkwrap.json +1 -1
- package/package.json +1 -1
package/converters/fromZigbee.js
CHANGED
|
@@ -3769,6 +3769,16 @@ const converters = {
|
|
|
3769
3769
|
});
|
|
3770
3770
|
},
|
|
3771
3771
|
},
|
|
3772
|
+
tuya_switch_type: {
|
|
3773
|
+
cluster: 'manuSpecificTuya_3',
|
|
3774
|
+
type: ['attributeReport', 'readResponse'],
|
|
3775
|
+
convert: (model, msg, publish, options, meta) => {
|
|
3776
|
+
const lookup = {0: 'toggle', 1: 'state', 2: 'momentary'};
|
|
3777
|
+
if (msg.data.hasOwnProperty('switchType')) {
|
|
3778
|
+
return {switch_type: lookup[msg.data['switchType']]};
|
|
3779
|
+
}
|
|
3780
|
+
},
|
|
3781
|
+
},
|
|
3772
3782
|
restorable_brightness: {
|
|
3773
3783
|
cluster: 'genLevelCtrl',
|
|
3774
3784
|
type: ['attributeReport', 'readResponse'],
|
package/converters/toZigbee.js
CHANGED
|
@@ -2869,6 +2869,19 @@ const converters = {
|
|
|
2869
2869
|
return {state: {state: value.toUpperCase()}};
|
|
2870
2870
|
},
|
|
2871
2871
|
},
|
|
2872
|
+
tuya_switch_type: {
|
|
2873
|
+
key: ['switch_type'],
|
|
2874
|
+
convertSet: async (entity, key, value, meta) => {
|
|
2875
|
+
value = value.toLowerCase();
|
|
2876
|
+
const lookup = {'toggle': 0, 'state': 1, 'momentary': 2};
|
|
2877
|
+
utils.validateValue(value, Object.keys(lookup));
|
|
2878
|
+
await entity.write('manuSpecificTuya_3', {'switchType': lookup[value]}, {disableDefaultResponse: true});
|
|
2879
|
+
return {state: {switch_type: value}};
|
|
2880
|
+
},
|
|
2881
|
+
convertGet: async (entity, key, meta) => {
|
|
2882
|
+
await entity.read('manuSpecificTuya_3', ['switchType']);
|
|
2883
|
+
},
|
|
2884
|
+
},
|
|
2872
2885
|
frankever_threshold: {
|
|
2873
2886
|
key: ['threshold'],
|
|
2874
2887
|
convertSet: async (entity, key, value, meta) => {
|
|
@@ -4612,21 +4625,27 @@ const converters = {
|
|
|
4612
4625
|
convertSet: async (entity, key, value, meta) => {
|
|
4613
4626
|
const isGroup = entity.constructor.name === 'Group';
|
|
4614
4627
|
const groupid = isGroup ? entity.groupID : 0;
|
|
4615
|
-
|
|
4628
|
+
let sceneid = value;
|
|
4629
|
+
let scenename = null;
|
|
4630
|
+
if (typeof value === 'object') {
|
|
4631
|
+
sceneid = value.ID;
|
|
4632
|
+
scenename = value.name;
|
|
4633
|
+
}
|
|
4634
|
+
|
|
4616
4635
|
const response = await entity.command('genScenes', 'store', {groupid, sceneid}, utils.getOptions(meta.mapped));
|
|
4617
4636
|
|
|
4618
4637
|
if (isGroup) {
|
|
4619
4638
|
if (meta.membersState) {
|
|
4620
4639
|
for (const member of entity.members) {
|
|
4621
|
-
utils.saveSceneState(member, sceneid, groupid, meta.membersState[member.getDevice().ieeeAddr]);
|
|
4640
|
+
utils.saveSceneState(member, sceneid, groupid, meta.membersState[member.getDevice().ieeeAddr], scenename);
|
|
4622
4641
|
}
|
|
4623
4642
|
}
|
|
4624
4643
|
} else if (response.status === 0) {
|
|
4625
|
-
utils.saveSceneState(entity, sceneid, groupid, meta.state);
|
|
4644
|
+
utils.saveSceneState(entity, sceneid, groupid, meta.state, scenename);
|
|
4626
4645
|
} else {
|
|
4627
4646
|
throw new Error(`Scene add not succesfull ('${herdsman.Zcl.Status[response.status]}')`);
|
|
4628
4647
|
}
|
|
4629
|
-
|
|
4648
|
+
meta.logger.info('Successfully stored scene');
|
|
4630
4649
|
return {state: {}};
|
|
4631
4650
|
},
|
|
4632
4651
|
},
|
|
@@ -4652,15 +4671,11 @@ const converters = {
|
|
|
4652
4671
|
};
|
|
4653
4672
|
|
|
4654
4673
|
const isGroup = entity.constructor.name === 'Group';
|
|
4655
|
-
const metaKey = `${sceneid}_${groupid}`;
|
|
4656
4674
|
if (isGroup) {
|
|
4657
4675
|
const membersState = {};
|
|
4658
4676
|
for (const member of entity.members) {
|
|
4659
|
-
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
let recalledState = member.meta.scenes[metaKey].state;
|
|
4663
|
-
|
|
4677
|
+
let recalledState = utils.getSceneState(member, sceneid, groupid);
|
|
4678
|
+
if (recalledState) {
|
|
4664
4679
|
// add color_mode if saved state does not contain it
|
|
4665
4680
|
if (!recalledState.hasOwnProperty('color_mode')) {
|
|
4666
4681
|
recalledState = addColorMode(recalledState);
|
|
@@ -4673,18 +4688,18 @@ const converters = {
|
|
|
4673
4688
|
membersState[member.getDevice().ieeeAddr] = {};
|
|
4674
4689
|
}
|
|
4675
4690
|
}
|
|
4691
|
+
meta.logger.info('Successfully recalled group scene');
|
|
4676
4692
|
return {membersState};
|
|
4677
4693
|
} else {
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
4694
|
+
let recalledState = utils.getSceneState(entity, sceneid, groupid);
|
|
4695
|
+
if (recalledState) {
|
|
4681
4696
|
// add color_mode if saved state does not contain it
|
|
4682
4697
|
if (!recalledState.hasOwnProperty('color_mode')) {
|
|
4683
4698
|
recalledState = addColorMode(recalledState);
|
|
4684
4699
|
}
|
|
4685
4700
|
|
|
4686
4701
|
Object.assign(recalledState, libColor.syncColorState(recalledState, meta.state, meta.options));
|
|
4687
|
-
|
|
4702
|
+
meta.logger.info('Successfully recalled scene');
|
|
4688
4703
|
return {state: recalledState};
|
|
4689
4704
|
} else {
|
|
4690
4705
|
meta.logger.warn(`Unknown scene was recalled for ${entity.deviceIeeeAddress}, can't restore state.`);
|
|
@@ -4711,7 +4726,7 @@ const converters = {
|
|
|
4711
4726
|
const isGroup = entity.constructor.name === 'Group';
|
|
4712
4727
|
const groupid = isGroup ? entity.groupID : 0;
|
|
4713
4728
|
const sceneid = value.ID;
|
|
4714
|
-
const scenename =
|
|
4729
|
+
const scenename = value.name;
|
|
4715
4730
|
const transtime = value.hasOwnProperty('transition') ? value.transition : 0;
|
|
4716
4731
|
|
|
4717
4732
|
const state = {};
|
|
@@ -4813,24 +4828,24 @@ const converters = {
|
|
|
4813
4828
|
|
|
4814
4829
|
if (isGroup || (removeresp.status === 0 || removeresp.status == 133 || removeresp.status == 139)) {
|
|
4815
4830
|
const response = await entity.command(
|
|
4816
|
-
'genScenes', 'add', {groupid, sceneid, scenename, transtime, extensionfieldsets}, utils.getOptions(meta.mapped),
|
|
4831
|
+
'genScenes', 'add', {groupid, sceneid, scenename: '', transtime, extensionfieldsets}, utils.getOptions(meta.mapped),
|
|
4817
4832
|
);
|
|
4818
4833
|
|
|
4819
4834
|
if (isGroup) {
|
|
4820
4835
|
if (meta.membersState) {
|
|
4821
4836
|
for (const member of entity.members) {
|
|
4822
|
-
utils.saveSceneState(member, sceneid, groupid, state);
|
|
4837
|
+
utils.saveSceneState(member, sceneid, groupid, state, scenename);
|
|
4823
4838
|
}
|
|
4824
4839
|
}
|
|
4825
4840
|
} else if (response.status === 0) {
|
|
4826
|
-
utils.saveSceneState(entity, sceneid, groupid, state);
|
|
4841
|
+
utils.saveSceneState(entity, sceneid, groupid, state, scenename);
|
|
4827
4842
|
} else {
|
|
4828
4843
|
throw new Error(`Scene add not succesfull ('${herdsman.Zcl.Status[response.status]}')`);
|
|
4829
4844
|
}
|
|
4830
4845
|
} else {
|
|
4831
4846
|
throw new Error(`Scene add unable to remove existing scene ('${herdsman.Zcl.Status[removeresp.status]}')`);
|
|
4832
4847
|
}
|
|
4833
|
-
|
|
4848
|
+
meta.logger.info('Successfully added scene');
|
|
4834
4849
|
return {state: {}};
|
|
4835
4850
|
},
|
|
4836
4851
|
},
|
|
@@ -4842,26 +4857,19 @@ const converters = {
|
|
|
4842
4857
|
const response = await entity.command(
|
|
4843
4858
|
'genScenes', 'remove', {groupid, sceneid}, utils.getOptions(meta.mapped),
|
|
4844
4859
|
);
|
|
4845
|
-
|
|
4846
4860
|
const isGroup = entity.constructor.name === 'Group';
|
|
4847
|
-
const metaKey = `${sceneid}_${groupid}`;
|
|
4848
4861
|
if (isGroup) {
|
|
4849
4862
|
if (meta.membersState) {
|
|
4850
4863
|
for (const member of entity.members) {
|
|
4851
|
-
|
|
4852
|
-
delete member.meta.scenes[metaKey];
|
|
4853
|
-
member.save();
|
|
4854
|
-
}
|
|
4864
|
+
utils.deleteSceneState(member, sceneid, groupid);
|
|
4855
4865
|
}
|
|
4856
4866
|
}
|
|
4857
4867
|
} else if (response.status === 0) {
|
|
4858
|
-
|
|
4859
|
-
delete entity.meta.scenes[metaKey];
|
|
4860
|
-
entity.save();
|
|
4861
|
-
}
|
|
4868
|
+
utils.deleteSceneState(entity, sceneid, groupid);
|
|
4862
4869
|
} else {
|
|
4863
4870
|
throw new Error(`Scene remove not succesfull ('${herdsman.Zcl.Status[response.status]}')`);
|
|
4864
4871
|
}
|
|
4872
|
+
meta.logger.info('Successfully removed scene');
|
|
4865
4873
|
},
|
|
4866
4874
|
},
|
|
4867
4875
|
scene_remove_all: {
|
|
@@ -4871,25 +4879,19 @@ const converters = {
|
|
|
4871
4879
|
const response = await entity.command(
|
|
4872
4880
|
'genScenes', 'removeAll', {groupid}, utils.getOptions(meta.mapped),
|
|
4873
4881
|
);
|
|
4874
|
-
|
|
4875
4882
|
const isGroup = entity.constructor.name === 'Group';
|
|
4876
4883
|
if (isGroup) {
|
|
4877
4884
|
if (meta.membersState) {
|
|
4878
4885
|
for (const member of entity.members) {
|
|
4879
|
-
|
|
4880
|
-
member.meta.scenes = {};
|
|
4881
|
-
member.save();
|
|
4882
|
-
}
|
|
4886
|
+
utils.deleteSceneState(member);
|
|
4883
4887
|
}
|
|
4884
4888
|
}
|
|
4885
4889
|
} else if (response.status === 0) {
|
|
4886
|
-
|
|
4887
|
-
entity.meta.scenes = {};
|
|
4888
|
-
entity.save();
|
|
4889
|
-
}
|
|
4890
|
+
utils.deleteSceneState(entity);
|
|
4890
4891
|
} else {
|
|
4891
4892
|
throw new Error(`Scene remove all not succesfull ('${herdsman.Zcl.Status[response.status]}')`);
|
|
4892
4893
|
}
|
|
4894
|
+
meta.logger.info('Successfully removed all scenes');
|
|
4893
4895
|
},
|
|
4894
4896
|
},
|
|
4895
4897
|
TS0003_curtain_switch: {
|
package/devices/philips.js
CHANGED
|
@@ -501,6 +501,15 @@ module.exports = [
|
|
|
501
501
|
extend: hueExtend.light_onoff_brightness(),
|
|
502
502
|
ota: ota.zigbeeOTA,
|
|
503
503
|
},
|
|
504
|
+
{
|
|
505
|
+
zigbeeModel: ['LWA017'],
|
|
506
|
+
model: '929002469202',
|
|
507
|
+
vendor: 'Philips',
|
|
508
|
+
description: 'Hue white A60 bulb E27 1050lm with Bluetooth',
|
|
509
|
+
meta: {turnsOffAtBrightness1: true},
|
|
510
|
+
extend: extend.light_onoff_brightness(),
|
|
511
|
+
ota: ota.zigbeeOTA,
|
|
512
|
+
},
|
|
504
513
|
{
|
|
505
514
|
zigbeeModel: ['LWA002'],
|
|
506
515
|
model: '9290018215',
|
package/devices/saswell.js
CHANGED
|
@@ -17,6 +17,7 @@ module.exports = [
|
|
|
17
17
|
{modelID: 'TS0601', manufacturerName: '_TZE200_yw7cahqs'},
|
|
18
18
|
{modelID: 'TS0601', manufacturerName: '_TZE200_azqp6ssj'},
|
|
19
19
|
{modelID: 'TS0601', manufacturerName: '_TZE200_zuhszj9s'},
|
|
20
|
+
{modelID: 'TS0601', manufacturerName: '_TZE200_9gvruqf5'},
|
|
20
21
|
],
|
|
21
22
|
model: 'SEA801-Zigbee/SEA802-Zigbee',
|
|
22
23
|
vendor: 'Saswell',
|
package/devices/tuya.js
CHANGED
|
@@ -585,6 +585,40 @@ module.exports = [
|
|
|
585
585
|
await reporting.bind(device.getEndpoint(2), coordinatorEndpoint, ['genOnOff']);
|
|
586
586
|
},
|
|
587
587
|
},
|
|
588
|
+
{
|
|
589
|
+
fingerprint: [{modelID: 'TS0001', manufacturerName: '_TZ3000_tqlv4ug4'}],
|
|
590
|
+
model: 'TS0001_switch_module',
|
|
591
|
+
vendor: 'TuYa',
|
|
592
|
+
description: '1 gang switch module',
|
|
593
|
+
whiteLabel: [{vendor: 'OXT', model: 'SWTZ21'}],
|
|
594
|
+
toZigbee: extend.switch().toZigbee.concat([tz.moes_power_on_behavior, tz.tuya_switch_type]),
|
|
595
|
+
fromZigbee: extend.switch().fromZigbee.concat([fz.moes_power_on_behavior, fz.tuya_switch_type]),
|
|
596
|
+
exposes: extend.switch().exposes.concat([exposes.presets.power_on_behavior(),
|
|
597
|
+
exposes.enum('switch_type', ea.ALL, ['toggle', 'state', 'momentary'])
|
|
598
|
+
.withDescription('Switch type settings')]),
|
|
599
|
+
configure: async (device, coordinatorEndpoint, logger) => {
|
|
600
|
+
await reporting.bind(device.getEndpoint(1), coordinatorEndpoint, ['genOnOff']);
|
|
601
|
+
},
|
|
602
|
+
},
|
|
603
|
+
{
|
|
604
|
+
fingerprint: [{modelID: 'TS0002', manufacturerName: '_TZ3000_01gpyda5'}],
|
|
605
|
+
model: 'TS0002_switch_module',
|
|
606
|
+
vendor: 'TuYa',
|
|
607
|
+
description: '2 gang switch module',
|
|
608
|
+
whiteLabel: [{vendor: 'OXT', model: 'SWTZ22'}],
|
|
609
|
+
toZigbee: extend.switch().toZigbee.concat([tz.moes_power_on_behavior, tz.tuya_switch_type]),
|
|
610
|
+
fromZigbee: extend.switch().fromZigbee.concat([fz.moes_power_on_behavior, fz.tuya_switch_type]),
|
|
611
|
+
exposes: [e.switch().withEndpoint('l1'), e.switch().withEndpoint('l2'), exposes.presets.power_on_behavior(),
|
|
612
|
+
exposes.enum('switch_type', ea.ALL, ['toggle', 'state', 'momentary']).withDescription('Switch type settings')],
|
|
613
|
+
endpoint: (device) => {
|
|
614
|
+
return {'l1': 1, 'l2': 2};
|
|
615
|
+
},
|
|
616
|
+
meta: {multiEndpoint: true},
|
|
617
|
+
configure: async (device, coordinatorEndpoint, logger) => {
|
|
618
|
+
await reporting.bind(device.getEndpoint(1), coordinatorEndpoint, ['genOnOff']);
|
|
619
|
+
await reporting.bind(device.getEndpoint(2), coordinatorEndpoint, ['genOnOff']);
|
|
620
|
+
},
|
|
621
|
+
},
|
|
588
622
|
{
|
|
589
623
|
zigbeeModel: [
|
|
590
624
|
'owvfni3\u0000', 'owvfni3', 'u1rkty3', 'aabybja', // Curtain motors
|
package/lib/exposes.js
CHANGED
|
@@ -469,7 +469,7 @@ module.exports = {
|
|
|
469
469
|
child_lock: () => new Lock().withState('child_lock', 'LOCK', 'UNLOCK', 'Enables/disables physical input on the device', access.STATE_SET),
|
|
470
470
|
co2: () => new Numeric('co2', access.STATE).withUnit('ppm').withDescription('The measured CO2 (carbon dioxide) value'),
|
|
471
471
|
comfort_temperature: () => new Numeric('comfort_temperature', access.STATE_SET).withUnit('°C').withDescription('Comfort temperature'),
|
|
472
|
-
consumer_connected: () => new Binary('consumer_connected', access.STATE, true, false).withDescription('Indicates whether
|
|
472
|
+
consumer_connected: () => new Binary('consumer_connected', access.STATE, true, false).withDescription('Indicates whether a plug is physically attached. Device does not have to pull power or even be connected electrically (state of this binary switch can be ON even if main power switch is OFF)'),
|
|
473
473
|
contact: () => new Binary('contact', access.STATE, false, true).withDescription('Indicates if the contact is closed (= true) or open (= false)'),
|
|
474
474
|
cover_position: () => new Cover().withPosition(),
|
|
475
475
|
cover_position_tilt: () => new Cover().withPosition().withTilt(),
|
package/lib/utils.js
CHANGED
|
@@ -271,14 +271,37 @@ function toCamelCase(value) {
|
|
|
271
271
|
}
|
|
272
272
|
}
|
|
273
273
|
|
|
274
|
-
function saveSceneState(entity, sceneID, groupID, state) {
|
|
274
|
+
function saveSceneState(entity, sceneID, groupID, state, name) {
|
|
275
275
|
const attributes = ['state', 'brightness', 'color', 'color_temp', 'color_mode'];
|
|
276
276
|
if (!entity.meta.hasOwnProperty('scenes')) entity.meta.scenes = {};
|
|
277
277
|
const metaKey = `${sceneID}_${groupID}`;
|
|
278
|
-
entity.meta.scenes[metaKey] = {state: filterObject(state, attributes)};
|
|
278
|
+
entity.meta.scenes[metaKey] = {name, state: filterObject(state, attributes)};
|
|
279
279
|
entity.save();
|
|
280
280
|
}
|
|
281
281
|
|
|
282
|
+
function deleteSceneState(entity, sceneID=null, groupID=null) {
|
|
283
|
+
if (entity.meta.scenes) {
|
|
284
|
+
if (sceneID == null && groupID == null) {
|
|
285
|
+
entity.meta.scenes = {};
|
|
286
|
+
} else {
|
|
287
|
+
const metaKey = `${sceneID}_${groupID}`;
|
|
288
|
+
if (entity.meta.scenes.hasOwnProperty(metaKey)) {
|
|
289
|
+
delete entity.meta.scenes[metaKey];
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
entity.save();
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function getSceneState(entity, sceneID, groupID) {
|
|
297
|
+
const metaKey = `${sceneID}_${groupID}`;
|
|
298
|
+
if (entity.meta.hasOwnProperty('scenes') && entity.meta.scenes.hasOwnProperty(metaKey)) {
|
|
299
|
+
return entity.meta.scenes[metaKey].state;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
304
|
+
|
|
282
305
|
function getEntityOrFirstGroupMember(entity) {
|
|
283
306
|
if (entity.constructor.name === 'Group') {
|
|
284
307
|
return entity.members.length > 0 ? entity.members[0] : null;
|
|
@@ -381,4 +404,6 @@ module.exports = {
|
|
|
381
404
|
toSnakeCase,
|
|
382
405
|
toCamelCase,
|
|
383
406
|
normalizeCelsiusVersionOfFahrenheit,
|
|
407
|
+
deleteSceneState,
|
|
408
|
+
getSceneState,
|
|
384
409
|
};
|
package/npm-shrinkwrap.json
CHANGED