zigbee-herdsman-converters 14.0.519 → 14.0.522
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 +0 -53
- package/converters/toZigbee.js +11 -422
- package/devices/kmpcil.js +97 -0
- package/devices/legrand.js +4 -4
- package/devices/lidl.js +11 -0
- package/devices/lifecontrol.js +4 -2
- package/devices/schneider_electric.js +4 -3
- package/devices/sonoff.js +2 -0
- package/devices/the_light_group.js +25 -1
- package/devices/third_reality.js +16 -0
- package/devices/tuya.js +3 -2
- package/devices/ubisys.js +497 -9
- package/devices/xiaomi.js +20 -15
- package/lib/legacy.js +1 -1
- package/lib/xiaomi.js +17 -2
- package/package.json +2 -2
package/converters/fromZigbee.js
CHANGED
|
@@ -7270,59 +7270,6 @@ const converters = {
|
|
|
7270
7270
|
};
|
|
7271
7271
|
},
|
|
7272
7272
|
},
|
|
7273
|
-
ubisys_dimmer_setup: {
|
|
7274
|
-
cluster: 'manuSpecificUbisysDimmerSetup',
|
|
7275
|
-
type: ['attributeReport', 'readResponse'],
|
|
7276
|
-
convert: (model, msg, publish, options, meta) => {
|
|
7277
|
-
if (msg.data.hasOwnProperty('capabilities')) {
|
|
7278
|
-
const capabilities = msg.data.capabilities;
|
|
7279
|
-
const forwardPhaseControl = capabilities & 1;
|
|
7280
|
-
const reversePhaseControl = (capabilities & 2) >>> 1;
|
|
7281
|
-
const reactanceDiscriminator = (capabilities & 0x20) >>> 5;
|
|
7282
|
-
const configurableCurve = (capabilities & 0x40) >>> 6;
|
|
7283
|
-
const overloadDetection = (capabilities & 0x80) >>> 7;
|
|
7284
|
-
return {
|
|
7285
|
-
capabilities_forward_phase_control: forwardPhaseControl ? true : false,
|
|
7286
|
-
capabilities_reverse_phase_control: reversePhaseControl ? true : false,
|
|
7287
|
-
capabilities_reactance_discriminator: reactanceDiscriminator ? true : false,
|
|
7288
|
-
capabilities_configurable_curve: configurableCurve ? true : false,
|
|
7289
|
-
capabilities_overload_detection: overloadDetection ? true : false,
|
|
7290
|
-
};
|
|
7291
|
-
}
|
|
7292
|
-
if (msg.data.hasOwnProperty('status')) {
|
|
7293
|
-
const status = msg.data.status;
|
|
7294
|
-
const forwardPhaseControl = status & 1;
|
|
7295
|
-
const reversePhaseControl = (status & 2) >>> 1;
|
|
7296
|
-
const overload = (status & 8) >>> 3;
|
|
7297
|
-
const capacitiveLoad = (status & 0x40) >>> 6;
|
|
7298
|
-
const inductiveLoad = (status & 0x80) >>> 7;
|
|
7299
|
-
return {
|
|
7300
|
-
status_forward_phase_control: forwardPhaseControl ? true : false,
|
|
7301
|
-
status_reverse_phase_control: reversePhaseControl ? true : false,
|
|
7302
|
-
status_overload: overload ? true : false,
|
|
7303
|
-
status_capacitive_load: capacitiveLoad ? true : false,
|
|
7304
|
-
status_inductive_load: inductiveLoad ? true : false,
|
|
7305
|
-
};
|
|
7306
|
-
}
|
|
7307
|
-
if (msg.data.hasOwnProperty('mode')) {
|
|
7308
|
-
const mode = msg.data.mode;
|
|
7309
|
-
const phaseControl = mode & 3;
|
|
7310
|
-
const phaseControlValues = {0: 'automatic', 1: 'forward', 2: 'reverse'};
|
|
7311
|
-
return {
|
|
7312
|
-
mode_phase_control: phaseControlValues[phaseControl],
|
|
7313
|
-
};
|
|
7314
|
-
}
|
|
7315
|
-
},
|
|
7316
|
-
},
|
|
7317
|
-
ubisys_dimmer_setup_genLevelCtrl: {
|
|
7318
|
-
cluster: 'genLevelCtrl',
|
|
7319
|
-
type: ['attributeReport', 'readResponse'],
|
|
7320
|
-
convert: (model, msg, publish, options, meta) => {
|
|
7321
|
-
if (msg.data.hasOwnProperty('ubisysMinimumOnLevel')) {
|
|
7322
|
-
return {minimum_on_level: msg.data.ubisysMinimumOnLevel};
|
|
7323
|
-
}
|
|
7324
|
-
},
|
|
7325
|
-
},
|
|
7326
7273
|
itcmdr_clicks: {
|
|
7327
7274
|
cluster: 'genMultistateInput',
|
|
7328
7275
|
type: ['readResponse', 'attributeReport'],
|
package/converters/toZigbee.js
CHANGED
|
@@ -20,13 +20,6 @@ const manufacturerOptions = {
|
|
|
20
20
|
hue: {manufacturerCode: herdsman.Zcl.ManufacturerCode.PHILIPS},
|
|
21
21
|
ikea: {manufacturerCode: herdsman.Zcl.ManufacturerCode.IKEA_OF_SWEDEN},
|
|
22
22
|
sinope: {manufacturerCode: herdsman.Zcl.ManufacturerCode.SINOPE_TECH},
|
|
23
|
-
/*
|
|
24
|
-
* Ubisys doesn't accept a manufacturerCode on some commands
|
|
25
|
-
* This bug has been reported, but it has not been fixed:
|
|
26
|
-
* https://github.com/Koenkk/zigbee-herdsman/issues/52
|
|
27
|
-
*/
|
|
28
|
-
ubisys: {manufacturerCode: herdsman.Zcl.ManufacturerCode.UBISYS},
|
|
29
|
-
ubisysNull: {manufacturerCode: null},
|
|
30
23
|
tint: {manufacturerCode: herdsman.Zcl.ManufacturerCode.MUELLER_LICHT_INT},
|
|
31
24
|
legrand: {manufacturerCode: herdsman.Zcl.ManufacturerCode.VANTAGE, disableDefaultResponse: true},
|
|
32
25
|
viessmann: {manufacturerCode: herdsman.Zcl.ManufacturerCode.VIESSMAN_ELEKTRO},
|
|
@@ -2734,6 +2727,17 @@ const converters = {
|
|
|
2734
2727
|
await entity.read('aqaraOpple', [0x0002], manufacturerOptions.xiaomi);
|
|
2735
2728
|
},
|
|
2736
2729
|
},
|
|
2730
|
+
RTCGQ14LM_trigger_indicator: {
|
|
2731
|
+
key: ['trigger_indicator'],
|
|
2732
|
+
convertSet: async (entity, key, value, meta) => {
|
|
2733
|
+
const lookup = {true: 1, false: 0};
|
|
2734
|
+
await entity.write('aqaraOpple', {0x0152: {value: lookup[value], type: 0x20}}, manufacturerOptions.xiaomi);
|
|
2735
|
+
return {state: {trigger_indicator: value}};
|
|
2736
|
+
},
|
|
2737
|
+
convertGet: async (entity, key, meta) => {
|
|
2738
|
+
await entity.read('aqaraOpple', [0x0152], manufacturerOptions.xiaomi);
|
|
2739
|
+
},
|
|
2740
|
+
},
|
|
2737
2741
|
LLKZMK11LM_interlock: {
|
|
2738
2742
|
key: ['interlock'],
|
|
2739
2743
|
convertSet: async (entity, key, value, meta) => {
|
|
@@ -4395,421 +4399,6 @@ const converters = {
|
|
|
4395
4399
|
}
|
|
4396
4400
|
},
|
|
4397
4401
|
},
|
|
4398
|
-
ubisys_configure_j1: {
|
|
4399
|
-
key: ['configure_j1'],
|
|
4400
|
-
convertSet: async (entity, key, value, meta) => {
|
|
4401
|
-
const log = (message) => {
|
|
4402
|
-
meta.logger.warn(`ubisys: ${message}`);
|
|
4403
|
-
};
|
|
4404
|
-
const sleepSeconds = async (s) => {
|
|
4405
|
-
return new Promise((resolve) => setTimeout(resolve, s * 1000));
|
|
4406
|
-
};
|
|
4407
|
-
const waitUntilStopped = async () => {
|
|
4408
|
-
let operationalStatus = 0;
|
|
4409
|
-
do {
|
|
4410
|
-
await sleepSeconds(2);
|
|
4411
|
-
operationalStatus = (await entity.read('closuresWindowCovering',
|
|
4412
|
-
['operationalStatus'])).operationalStatus;
|
|
4413
|
-
} while (operationalStatus != 0);
|
|
4414
|
-
await sleepSeconds(2);
|
|
4415
|
-
};
|
|
4416
|
-
const writeAttrFromJson = async (attr, jsonAttr = attr, converterFunc, delaySecondsAfter) => {
|
|
4417
|
-
if (jsonAttr.startsWith('ubisys')) {
|
|
4418
|
-
jsonAttr = jsonAttr.substring(6, 1).toLowerCase + jsonAttr.substring(7);
|
|
4419
|
-
}
|
|
4420
|
-
if (value.hasOwnProperty(jsonAttr)) {
|
|
4421
|
-
let attrValue = value[jsonAttr];
|
|
4422
|
-
if (converterFunc) {
|
|
4423
|
-
attrValue = converterFunc(attrValue);
|
|
4424
|
-
}
|
|
4425
|
-
const attributes = {};
|
|
4426
|
-
attributes[attr] = attrValue;
|
|
4427
|
-
await entity.write('closuresWindowCovering', attributes, manufacturerOptions.ubisys);
|
|
4428
|
-
if (delaySecondsAfter) {
|
|
4429
|
-
await sleepSeconds(delaySecondsAfter);
|
|
4430
|
-
}
|
|
4431
|
-
}
|
|
4432
|
-
};
|
|
4433
|
-
const stepsPerSecond = value.steps_per_second || 50;
|
|
4434
|
-
const hasCalibrate = value.hasOwnProperty('calibrate');
|
|
4435
|
-
// cancel any running calibration
|
|
4436
|
-
let mode = (await entity.read('closuresWindowCovering', ['windowCoveringMode'])).windowCoveringMode;
|
|
4437
|
-
const modeCalibrationBitMask = 0x02;
|
|
4438
|
-
if (mode & modeCalibrationBitMask) {
|
|
4439
|
-
await entity.write('closuresWindowCovering', {windowCoveringMode: mode & ~modeCalibrationBitMask});
|
|
4440
|
-
await sleepSeconds(2);
|
|
4441
|
-
}
|
|
4442
|
-
// delay a bit if reconfiguring basic configuration attributes
|
|
4443
|
-
await writeAttrFromJson('windowCoveringType', undefined, undefined, 2);
|
|
4444
|
-
await writeAttrFromJson('configStatus', undefined, undefined, 2);
|
|
4445
|
-
if (await writeAttrFromJson('windowCoveringMode', undefined, undefined, 2)) {
|
|
4446
|
-
mode = value['windowCoveringMode'];
|
|
4447
|
-
}
|
|
4448
|
-
if (hasCalibrate) {
|
|
4449
|
-
log('Cover calibration starting...');
|
|
4450
|
-
// first of all, move to top position to not confuse calibration later
|
|
4451
|
-
log(' Moving cover to top position to get a good starting point...');
|
|
4452
|
-
await entity.command('closuresWindowCovering', 'upOpen');
|
|
4453
|
-
await waitUntilStopped();
|
|
4454
|
-
log(' Settings some attributes...');
|
|
4455
|
-
// reset attributes
|
|
4456
|
-
await entity.write('closuresWindowCovering', {
|
|
4457
|
-
installedOpenLimitLiftCm: 0,
|
|
4458
|
-
installedClosedLimitLiftCm: 240,
|
|
4459
|
-
installedOpenLimitTiltDdegree: 0,
|
|
4460
|
-
installedClosedLimitTiltDdegree: 900,
|
|
4461
|
-
ubisysLiftToTiltTransitionSteps: 0xffff,
|
|
4462
|
-
ubisysTotalSteps: 0xffff,
|
|
4463
|
-
ubisysLiftToTiltTransitionSteps2: 0xffff,
|
|
4464
|
-
ubisysTotalSteps2: 0xffff,
|
|
4465
|
-
}, manufacturerOptions.ubisys);
|
|
4466
|
-
// enable calibration mode
|
|
4467
|
-
await sleepSeconds(2);
|
|
4468
|
-
await entity.write('closuresWindowCovering', {windowCoveringMode: mode | modeCalibrationBitMask});
|
|
4469
|
-
await sleepSeconds(2);
|
|
4470
|
-
// move down a bit and back up to detect upper limit
|
|
4471
|
-
log(' Moving cover down a bit...');
|
|
4472
|
-
await entity.command('closuresWindowCovering', 'downClose');
|
|
4473
|
-
await sleepSeconds(5);
|
|
4474
|
-
await entity.command('closuresWindowCovering', 'stop');
|
|
4475
|
-
await sleepSeconds(2);
|
|
4476
|
-
log(' Moving up again to detect upper limit...');
|
|
4477
|
-
await entity.command('closuresWindowCovering', 'upOpen');
|
|
4478
|
-
await waitUntilStopped();
|
|
4479
|
-
log(' Moving down to count steps from open to closed...');
|
|
4480
|
-
await entity.command('closuresWindowCovering', 'downClose');
|
|
4481
|
-
await waitUntilStopped();
|
|
4482
|
-
log(' Moving up to count steps from closed to open...');
|
|
4483
|
-
await entity.command('closuresWindowCovering', 'upOpen');
|
|
4484
|
-
await waitUntilStopped();
|
|
4485
|
-
}
|
|
4486
|
-
// now write any attribute values present in JSON
|
|
4487
|
-
await writeAttrFromJson('installedOpenLimitLiftCm');
|
|
4488
|
-
await writeAttrFromJson('installedClosedLimitLiftCm');
|
|
4489
|
-
await writeAttrFromJson('installedOpenLimitTiltDdegree');
|
|
4490
|
-
await writeAttrFromJson('installedClosedLimitTiltDdegree');
|
|
4491
|
-
await writeAttrFromJson('ubisysTurnaroundGuardTime');
|
|
4492
|
-
await writeAttrFromJson('ubisysLiftToTiltTransitionSteps');
|
|
4493
|
-
await writeAttrFromJson('ubisysTotalSteps');
|
|
4494
|
-
await writeAttrFromJson('ubisysLiftToTiltTransitionSteps2');
|
|
4495
|
-
await writeAttrFromJson('ubisysTotalSteps2');
|
|
4496
|
-
await writeAttrFromJson('ubisysAdditionalSteps');
|
|
4497
|
-
await writeAttrFromJson('ubisysInactivePowerThreshold');
|
|
4498
|
-
await writeAttrFromJson('ubisysStartupSteps');
|
|
4499
|
-
// some convenience functions to not have to calculate
|
|
4500
|
-
await writeAttrFromJson('ubisysTotalSteps', 'open_to_closed_s', (s) => s * stepsPerSecond);
|
|
4501
|
-
await writeAttrFromJson('ubisysTotalSteps2', 'closed_to_open_s', (s) => s * stepsPerSecond);
|
|
4502
|
-
await writeAttrFromJson('ubisysLiftToTiltTransitionSteps', 'lift_to_tilt_transition_ms',
|
|
4503
|
-
(s) => s * stepsPerSecond / 1000);
|
|
4504
|
-
await writeAttrFromJson('ubisysLiftToTiltTransitionSteps2', 'lift_to_tilt_transition_ms',
|
|
4505
|
-
(s) => s * stepsPerSecond / 1000);
|
|
4506
|
-
if (hasCalibrate) {
|
|
4507
|
-
log(' Finalizing calibration...');
|
|
4508
|
-
// disable calibration mode again
|
|
4509
|
-
await sleepSeconds(2);
|
|
4510
|
-
await entity.write('closuresWindowCovering', {windowCoveringMode: mode & ~modeCalibrationBitMask});
|
|
4511
|
-
await sleepSeconds(2);
|
|
4512
|
-
// re-read and dump all relevant attributes
|
|
4513
|
-
log(' Done - will now read back the results.');
|
|
4514
|
-
converters.ubisys_configure_j1.convertGet(entity, key, meta);
|
|
4515
|
-
}
|
|
4516
|
-
},
|
|
4517
|
-
convertGet: async (entity, key, meta) => {
|
|
4518
|
-
const log = (json) => {
|
|
4519
|
-
meta.logger.warn(`ubisys: Cover configuration read: ${JSON.stringify(json)}`);
|
|
4520
|
-
};
|
|
4521
|
-
log(await entity.read('closuresWindowCovering', [
|
|
4522
|
-
'windowCoveringType',
|
|
4523
|
-
'physicalClosedLimitLiftCm',
|
|
4524
|
-
'physicalClosedLimitTiltDdegree',
|
|
4525
|
-
'installedOpenLimitLiftCm',
|
|
4526
|
-
'installedClosedLimitLiftCm',
|
|
4527
|
-
'installedOpenLimitTiltDdegree',
|
|
4528
|
-
'installedClosedLimitTiltDdegree',
|
|
4529
|
-
]));
|
|
4530
|
-
log(await entity.read('closuresWindowCovering', [
|
|
4531
|
-
'configStatus',
|
|
4532
|
-
'windowCoveringMode',
|
|
4533
|
-
'currentPositionLiftPercentage',
|
|
4534
|
-
'currentPositionLiftCm',
|
|
4535
|
-
'currentPositionTiltPercentage',
|
|
4536
|
-
'currentPositionTiltDdegree',
|
|
4537
|
-
'operationalStatus',
|
|
4538
|
-
]));
|
|
4539
|
-
log(await entity.read('closuresWindowCovering', [
|
|
4540
|
-
'ubisysTurnaroundGuardTime',
|
|
4541
|
-
'ubisysLiftToTiltTransitionSteps',
|
|
4542
|
-
'ubisysTotalSteps',
|
|
4543
|
-
'ubisysLiftToTiltTransitionSteps2',
|
|
4544
|
-
'ubisysTotalSteps2',
|
|
4545
|
-
'ubisysAdditionalSteps',
|
|
4546
|
-
'ubisysInactivePowerThreshold',
|
|
4547
|
-
'ubisysStartupSteps',
|
|
4548
|
-
], manufacturerOptions.ubisys));
|
|
4549
|
-
},
|
|
4550
|
-
},
|
|
4551
|
-
ubisys_dimmer_setup: {
|
|
4552
|
-
key: ['capabilities_forward_phase_control',
|
|
4553
|
-
'capabilities_reverse_phase_control',
|
|
4554
|
-
'capabilities_reactance_discriminator',
|
|
4555
|
-
'capabilities_configurable_curve',
|
|
4556
|
-
'capabilities_overload_detection',
|
|
4557
|
-
'status_forward_phase_control',
|
|
4558
|
-
'status_reverse_phase_control',
|
|
4559
|
-
'status_overload',
|
|
4560
|
-
'status_capacitive_load',
|
|
4561
|
-
'status_inductive_load',
|
|
4562
|
-
'mode_phase_control'],
|
|
4563
|
-
convertSet: async (entity, key, value, meta) => {
|
|
4564
|
-
if (key === 'mode_phase_control') {
|
|
4565
|
-
const phaseControl = value.toLowerCase();
|
|
4566
|
-
const phaseControlValues = {'automatic': 0, 'forward': 1, 'reverse': 2};
|
|
4567
|
-
utils.validateValue(phaseControl, Object.keys(phaseControlValues));
|
|
4568
|
-
await entity.write('manuSpecificUbisysDimmerSetup',
|
|
4569
|
-
{'mode': phaseControlValues[phaseControl]}, manufacturerOptions.ubisysNull);
|
|
4570
|
-
}
|
|
4571
|
-
converters.ubisys_dimmer_setup.convertGet(entity, key, meta);
|
|
4572
|
-
},
|
|
4573
|
-
convertGet: async (entity, key, meta) => {
|
|
4574
|
-
await entity.read('manuSpecificUbisysDimmerSetup', ['capabilities'], manufacturerOptions.ubisysNull);
|
|
4575
|
-
await entity.read('manuSpecificUbisysDimmerSetup', ['status'], manufacturerOptions.ubisysNull);
|
|
4576
|
-
await entity.read('manuSpecificUbisysDimmerSetup', ['mode'], manufacturerOptions.ubisysNull);
|
|
4577
|
-
},
|
|
4578
|
-
},
|
|
4579
|
-
ubisys_dimmer_setup_genLevelCtrl: {
|
|
4580
|
-
key: ['minimum_on_level'],
|
|
4581
|
-
convertSet: async (entity, key, value, meta) => {
|
|
4582
|
-
if (key === 'minimum_on_level') {
|
|
4583
|
-
await entity.write('genLevelCtrl', {'ubisysMinimumOnLevel': value}, manufacturerOptions.ubisys);
|
|
4584
|
-
}
|
|
4585
|
-
converters.ubisys_dimmer_setup_genLevelCtrl.convertGet(entity, key, meta);
|
|
4586
|
-
},
|
|
4587
|
-
convertGet: async (entity, key, meta) => {
|
|
4588
|
-
await entity.read('genLevelCtrl', ['ubisysMinimumOnLevel'], manufacturerOptions.ubisys);
|
|
4589
|
-
},
|
|
4590
|
-
},
|
|
4591
|
-
ubisys_device_setup: {
|
|
4592
|
-
key: ['configure_device_setup'],
|
|
4593
|
-
convertSet: async (entity, key, value, meta) => {
|
|
4594
|
-
const devMgmtEp = meta.device.getEndpoint(232);
|
|
4595
|
-
|
|
4596
|
-
if (value.hasOwnProperty('input_configurations')) {
|
|
4597
|
-
// example: [0, 0, 0, 0]
|
|
4598
|
-
await devMgmtEp.write(
|
|
4599
|
-
'manuSpecificUbisysDeviceSetup',
|
|
4600
|
-
{'inputConfigurations': {elementType: 'data8', elements: value.input_configurations}},
|
|
4601
|
-
manufacturerOptions.ubisysNull,
|
|
4602
|
-
);
|
|
4603
|
-
}
|
|
4604
|
-
|
|
4605
|
-
if (value.hasOwnProperty('input_actions')) {
|
|
4606
|
-
// example (default for C4): [[0,13,1,6,0,2], [1,13,2,6,0,2], [2,13,3,6,0,2], [3,13,4,6,0,2]]
|
|
4607
|
-
await devMgmtEp.write(
|
|
4608
|
-
'manuSpecificUbisysDeviceSetup',
|
|
4609
|
-
{'inputActions': {elementType: 'octetStr', elements: value.input_actions}},
|
|
4610
|
-
manufacturerOptions.ubisysNull,
|
|
4611
|
-
);
|
|
4612
|
-
}
|
|
4613
|
-
|
|
4614
|
-
if (value.hasOwnProperty('input_action_templates')) {
|
|
4615
|
-
const templateTypes = {
|
|
4616
|
-
// source: "ZigBee Device Physical Input Configurations Integrator’s Guide"
|
|
4617
|
-
// (can be obtained directly from ubisys upon request)
|
|
4618
|
-
'toggle': {
|
|
4619
|
-
getInputActions: (input, endpoint) => [
|
|
4620
|
-
[input, 0x0D, endpoint, 0x06, 0x00, 0x02],
|
|
4621
|
-
],
|
|
4622
|
-
},
|
|
4623
|
-
'toggle_switch': {
|
|
4624
|
-
getInputActions: (input, endpoint) => [
|
|
4625
|
-
[input, 0x0D, endpoint, 0x06, 0x00, 0x02],
|
|
4626
|
-
[input, 0x03, endpoint, 0x06, 0x00, 0x02],
|
|
4627
|
-
],
|
|
4628
|
-
},
|
|
4629
|
-
'on_off_switch': {
|
|
4630
|
-
getInputActions: (input, endpoint) => [
|
|
4631
|
-
[input, 0x0D, endpoint, 0x06, 0x00, 0x01],
|
|
4632
|
-
[input, 0x03, endpoint, 0x06, 0x00, 0x00],
|
|
4633
|
-
],
|
|
4634
|
-
},
|
|
4635
|
-
'on': {
|
|
4636
|
-
getInputActions: (input, endpoint) => [
|
|
4637
|
-
[input, 0x0D, endpoint, 0x06, 0x00, 0x01],
|
|
4638
|
-
],
|
|
4639
|
-
},
|
|
4640
|
-
'off': {
|
|
4641
|
-
getInputActions: (input, endpoint) => [
|
|
4642
|
-
[input, 0x0D, endpoint, 0x06, 0x00, 0x00],
|
|
4643
|
-
],
|
|
4644
|
-
},
|
|
4645
|
-
'dimmer_single': {
|
|
4646
|
-
getInputActions: (input, endpoint, template) => {
|
|
4647
|
-
const moveUpCmd = template.no_onoff || template.no_onoff_up ? 0x01 : 0x05;
|
|
4648
|
-
const moveDownCmd = template.no_onoff || template.no_onoff_down ? 0x01 : 0x05;
|
|
4649
|
-
const moveRate = template.rate || 50;
|
|
4650
|
-
return [
|
|
4651
|
-
[input, 0x07, endpoint, 0x06, 0x00, 0x02],
|
|
4652
|
-
[input, 0x86, endpoint, 0x08, 0x00, moveUpCmd, 0x00, moveRate],
|
|
4653
|
-
[input, 0xC6, endpoint, 0x08, 0x00, moveDownCmd, 0x01, moveRate],
|
|
4654
|
-
[input, 0x0B, endpoint, 0x08, 0x00, 0x03],
|
|
4655
|
-
];
|
|
4656
|
-
},
|
|
4657
|
-
},
|
|
4658
|
-
'dimmer_double': {
|
|
4659
|
-
doubleInputs: true,
|
|
4660
|
-
getInputActions: (inputs, endpoint, template) => {
|
|
4661
|
-
const moveUpCmd = template.no_onoff || template.no_onoff_up ? 0x01 : 0x05;
|
|
4662
|
-
const moveDownCmd = template.no_onoff || template.no_onoff_down ? 0x01 : 0x05;
|
|
4663
|
-
const moveRate = template.rate || 50;
|
|
4664
|
-
return [
|
|
4665
|
-
[inputs[0], 0x07, endpoint, 0x06, 0x00, 0x01],
|
|
4666
|
-
[inputs[0], 0x06, endpoint, 0x08, 0x00, moveUpCmd, 0x00, moveRate],
|
|
4667
|
-
[inputs[0], 0x0B, endpoint, 0x08, 0x00, 0x03],
|
|
4668
|
-
[inputs[1], 0x07, endpoint, 0x06, 0x00, 0x00],
|
|
4669
|
-
[inputs[1], 0x06, endpoint, 0x08, 0x00, moveDownCmd, 0x01, moveRate],
|
|
4670
|
-
[inputs[1], 0x0B, endpoint, 0x08, 0x00, 0x03],
|
|
4671
|
-
];
|
|
4672
|
-
},
|
|
4673
|
-
},
|
|
4674
|
-
'cover': {
|
|
4675
|
-
cover: true,
|
|
4676
|
-
doubleInputs: true,
|
|
4677
|
-
getInputActions: (inputs, endpoint) => [
|
|
4678
|
-
[inputs[0], 0x0D, endpoint, 0x02, 0x01, 0x00],
|
|
4679
|
-
[inputs[0], 0x07, endpoint, 0x02, 0x01, 0x02],
|
|
4680
|
-
[inputs[1], 0x0D, endpoint, 0x02, 0x01, 0x01],
|
|
4681
|
-
[inputs[1], 0x07, endpoint, 0x02, 0x01, 0x02],
|
|
4682
|
-
],
|
|
4683
|
-
},
|
|
4684
|
-
'cover_switch': {
|
|
4685
|
-
cover: true,
|
|
4686
|
-
doubleInputs: true,
|
|
4687
|
-
getInputActions: (inputs, endpoint) => [
|
|
4688
|
-
[inputs[0], 0x0D, endpoint, 0x02, 0x01, 0x00],
|
|
4689
|
-
[inputs[0], 0x03, endpoint, 0x02, 0x01, 0x02],
|
|
4690
|
-
[inputs[1], 0x0D, endpoint, 0x02, 0x01, 0x01],
|
|
4691
|
-
[inputs[1], 0x03, endpoint, 0x02, 0x01, 0x02],
|
|
4692
|
-
],
|
|
4693
|
-
},
|
|
4694
|
-
'cover_up': {
|
|
4695
|
-
cover: true,
|
|
4696
|
-
getInputActions: (input, endpoint) => [
|
|
4697
|
-
[input, 0x0D, endpoint, 0x02, 0x01, 0x00],
|
|
4698
|
-
],
|
|
4699
|
-
},
|
|
4700
|
-
'cover_down': {
|
|
4701
|
-
cover: true,
|
|
4702
|
-
getInputActions: (input, endpoint) => [
|
|
4703
|
-
[input, 0x0D, endpoint, 0x02, 0x01, 0x01],
|
|
4704
|
-
],
|
|
4705
|
-
},
|
|
4706
|
-
'scene': {
|
|
4707
|
-
scene: true,
|
|
4708
|
-
getInputActions: (input, endpoint, groupId, sceneId) => [
|
|
4709
|
-
[input, 0x07, endpoint, 0x05, 0x00, 0x05, groupId & 0xff, groupId >> 8, sceneId],
|
|
4710
|
-
],
|
|
4711
|
-
getInputActions2: (input, endpoint, groupId, sceneId) => [
|
|
4712
|
-
[input, 0x06, endpoint, 0x05, 0x00, 0x05, groupId & 0xff, groupId >> 8, sceneId],
|
|
4713
|
-
],
|
|
4714
|
-
},
|
|
4715
|
-
'scene_switch': {
|
|
4716
|
-
scene: true,
|
|
4717
|
-
getInputActions: (input, endpoint, groupId, sceneId) => [
|
|
4718
|
-
[input, 0x0D, endpoint, 0x05, 0x00, 0x05, groupId & 0xff, groupId >> 8, sceneId],
|
|
4719
|
-
],
|
|
4720
|
-
getInputActions2: (input, endpoint, groupId, sceneId) => [
|
|
4721
|
-
[input, 0x03, endpoint, 0x05, 0x00, 0x05, groupId & 0xff, groupId >> 8, sceneId],
|
|
4722
|
-
],
|
|
4723
|
-
},
|
|
4724
|
-
};
|
|
4725
|
-
|
|
4726
|
-
// first input
|
|
4727
|
-
let input = 0;
|
|
4728
|
-
// first client endpoint - depends on actual device
|
|
4729
|
-
let endpoint = {'S1': 2, 'S2': 3, 'D1': 2, 'J1': 2, 'C4': 1}[meta.mapped.model];
|
|
4730
|
-
// default group id
|
|
4731
|
-
let groupId = 0;
|
|
4732
|
-
|
|
4733
|
-
const templates = Array.isArray(value.input_action_templates) ? value.input_action_templates :
|
|
4734
|
-
[value.input_action_templates];
|
|
4735
|
-
let resultingInputActions = [];
|
|
4736
|
-
for (const template of templates) {
|
|
4737
|
-
const templateType = templateTypes[template.type];
|
|
4738
|
-
if (!templateType) {
|
|
4739
|
-
throw new Error(`input_action_templates: Template type '${template.type}' is not valid ` +
|
|
4740
|
-
`(valid types: ${Object.keys(templateTypes)})`);
|
|
4741
|
-
}
|
|
4742
|
-
|
|
4743
|
-
if (template.hasOwnProperty('input')) {
|
|
4744
|
-
input = template.input;
|
|
4745
|
-
}
|
|
4746
|
-
if (template.hasOwnProperty('endpoint')) {
|
|
4747
|
-
endpoint = template.endpoint;
|
|
4748
|
-
}
|
|
4749
|
-
// C4 cover endpoints only start at 5
|
|
4750
|
-
if (templateType.cover && meta.mapped.model === 'C4' && endpoint < 5) {
|
|
4751
|
-
endpoint += 4;
|
|
4752
|
-
}
|
|
4753
|
-
|
|
4754
|
-
let inputActions;
|
|
4755
|
-
if (!templateType.doubleInputs) {
|
|
4756
|
-
if (!templateType.scene) {
|
|
4757
|
-
// single input, no scene(s)
|
|
4758
|
-
inputActions = templateType.getInputActions(input, endpoint, template);
|
|
4759
|
-
} else {
|
|
4760
|
-
// scene(s) (always single input)
|
|
4761
|
-
if (!template.hasOwnProperty('scene_id')) {
|
|
4762
|
-
throw new Error(`input_action_templates: Need an attribute 'scene_id' for '${template.type}'`);
|
|
4763
|
-
}
|
|
4764
|
-
if (template.hasOwnProperty('group_id')) {
|
|
4765
|
-
groupId = template.group_id;
|
|
4766
|
-
}
|
|
4767
|
-
inputActions = templateType.getInputActions(input, endpoint, groupId, template.scene_id);
|
|
4768
|
-
|
|
4769
|
-
if (template.hasOwnProperty('scene_id_2')) {
|
|
4770
|
-
if (template.hasOwnProperty('group_id_2')) {
|
|
4771
|
-
groupId = template.group_id_2;
|
|
4772
|
-
}
|
|
4773
|
-
inputActions = inputActions.concat(templateType.getInputActions2(input, endpoint, groupId,
|
|
4774
|
-
template.scene_id_2));
|
|
4775
|
-
}
|
|
4776
|
-
}
|
|
4777
|
-
} else {
|
|
4778
|
-
// double inputs
|
|
4779
|
-
input = template.hasOwnProperty('inputs') ? template.inputs : [input, input + 1];
|
|
4780
|
-
inputActions = templateType.getInputActions(input, endpoint, template);
|
|
4781
|
-
}
|
|
4782
|
-
resultingInputActions = resultingInputActions.concat(inputActions);
|
|
4783
|
-
|
|
4784
|
-
meta.logger.warn(`ubisys: Using input(s) ${input} and endpoint ${endpoint} for '${template.type}'.`);
|
|
4785
|
-
// input might by now be an array (in case of double inputs)
|
|
4786
|
-
input = (Array.isArray(input) ? Math.max(...input) : input) + 1;
|
|
4787
|
-
endpoint += 1;
|
|
4788
|
-
}
|
|
4789
|
-
|
|
4790
|
-
meta.logger.debug(`ubisys: input_actions to be sent to '${meta.options.friendlyName}': ` +
|
|
4791
|
-
JSON.stringify(resultingInputActions));
|
|
4792
|
-
await devMgmtEp.write(
|
|
4793
|
-
'manuSpecificUbisysDeviceSetup',
|
|
4794
|
-
{'inputActions': {elementType: 'octetStr', elements: resultingInputActions}},
|
|
4795
|
-
manufacturerOptions.ubisysNull,
|
|
4796
|
-
);
|
|
4797
|
-
}
|
|
4798
|
-
|
|
4799
|
-
// re-read effective settings and dump them to the log
|
|
4800
|
-
converters.ubisys_device_setup.convertGet(entity, key, meta);
|
|
4801
|
-
},
|
|
4802
|
-
|
|
4803
|
-
convertGet: async (entity, key, meta) => {
|
|
4804
|
-
const log = (dataRead) => {
|
|
4805
|
-
meta.logger.warn(
|
|
4806
|
-
`ubisys: Device setup read for '${meta.options.friendly_name}': ${JSON.stringify(utils.toSnakeCase(dataRead))}`);
|
|
4807
|
-
};
|
|
4808
|
-
const devMgmtEp = meta.device.getEndpoint(232);
|
|
4809
|
-
log(await devMgmtEp.read('manuSpecificUbisysDeviceSetup', ['inputConfigurations'], manufacturerOptions.ubisysNull));
|
|
4810
|
-
log(await devMgmtEp.read('manuSpecificUbisysDeviceSetup', ['inputActions'], manufacturerOptions.ubisysNull));
|
|
4811
|
-
},
|
|
4812
|
-
},
|
|
4813
4402
|
tint_scene: {
|
|
4814
4403
|
key: ['tint_scene'],
|
|
4815
4404
|
convertSet: async (entity, key, value, meta) => {
|
package/devices/kmpcil.js
CHANGED
|
@@ -3,8 +3,71 @@ const fz = {...require('../converters/fromZigbee'), legacy: require('../lib/lega
|
|
|
3
3
|
const tz = require('../converters/toZigbee');
|
|
4
4
|
const constants = require('../lib/constants');
|
|
5
5
|
const reporting = require('../lib/reporting');
|
|
6
|
+
const globalStore = require('../lib/store');
|
|
7
|
+
const utils = require('../lib/utils');
|
|
6
8
|
const e = exposes.presets;
|
|
7
9
|
|
|
10
|
+
const kmpcilOptions={
|
|
11
|
+
presence_timeout_dc: () => {
|
|
12
|
+
return exposes.numeric('presence_timeout_dc').withValueMin(60).withDescription(
|
|
13
|
+
'Time in seconds after which presence is cleared after detecting it (default 60 seconds) while in DC.');
|
|
14
|
+
},
|
|
15
|
+
presence_timeout_battery: () => {
|
|
16
|
+
return exposes.numeric('presence_timeout_battery').withValueMin(120).withDescription(
|
|
17
|
+
'Time in seconds after which presence is cleared after detecting it (default 420 seconds) while in Battery.');
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
function handleKmpcilPresence(model, msg, publish, options, meta) {
|
|
22
|
+
const useOptionsTimeoutBattery = options && options.hasOwnProperty('presence_timeout_battery');
|
|
23
|
+
const timeoutBattery = useOptionsTimeoutBattery ? options.presence_timeout_battery : 420; // 100 seconds by default
|
|
24
|
+
|
|
25
|
+
const useOptionsTimeoutDc = options && options.hasOwnProperty('presence_timeout_dc');
|
|
26
|
+
const timeoutDc = useOptionsTimeoutDc ? options.presence_timeout_dc : 60;
|
|
27
|
+
|
|
28
|
+
const mode = meta.state? meta.state['power_state'] : false;
|
|
29
|
+
|
|
30
|
+
const timeout = mode ? timeoutDc : timeoutBattery;
|
|
31
|
+
// Stop existing timer because motion is detected and set a new one.
|
|
32
|
+
clearTimeout(globalStore.getValue(msg.endpoint, 'timer'));
|
|
33
|
+
const timer = setTimeout(() => publish({presence: false}), timeout * 1000);
|
|
34
|
+
globalStore.putValue(msg.endpoint, 'timer', timer);
|
|
35
|
+
|
|
36
|
+
return {presence: true};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const kmpcilConverters = {
|
|
40
|
+
presence_binary_input: {
|
|
41
|
+
cluster: 'genBinaryInput',
|
|
42
|
+
type: ['attributeReport', 'readResponse'],
|
|
43
|
+
convert: (model, msg, publish, options, meta) => {
|
|
44
|
+
const payload = handleKmpcilPresence(model, msg, publish, options, meta);
|
|
45
|
+
if (msg.data.hasOwnProperty('presentValue')) {
|
|
46
|
+
const presentValue = msg.data['presentValue'];
|
|
47
|
+
payload.power_state = (presentValue & 0x01)> 0;
|
|
48
|
+
payload.occupancy = (presentValue & 0x04) > 0;
|
|
49
|
+
payload.vibration = (presentValue & 0x02) > 0;
|
|
50
|
+
}
|
|
51
|
+
return payload;
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
presence_power: {
|
|
55
|
+
cluster: 'genPowerCfg',
|
|
56
|
+
type: ['attributeReport', 'readResponse'],
|
|
57
|
+
options: [kmpcilOptions.presence_timeout_dc(), kmpcilOptions.presence_timeout_battery()],
|
|
58
|
+
convert: (model, msg, publish, options, meta) => {
|
|
59
|
+
const payload = handleKmpcilPresence(model, msg, publish, options, meta);
|
|
60
|
+
if (msg.data.hasOwnProperty('batteryVoltage')) {
|
|
61
|
+
payload.voltage = msg.data['batteryVoltage'] * 100;
|
|
62
|
+
if (model.meta && model.meta.battery && model.meta.battery.voltageToPercentage) {
|
|
63
|
+
payload.battery = utils.batteryVoltageToPercentage(payload.voltage, model.meta.battery.voltageToPercentage);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return payload;
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
|
|
8
71
|
module.exports = [
|
|
9
72
|
{
|
|
10
73
|
zigbeeModel: ['RES005'],
|
|
@@ -47,4 +110,38 @@ module.exports = [
|
|
|
47
110
|
await endpoint.configureReporting('genBinaryOutput', payloadBinaryOutput);
|
|
48
111
|
},
|
|
49
112
|
},
|
|
113
|
+
{
|
|
114
|
+
zigbeeModel: ['tagv1'],
|
|
115
|
+
model: 'KMPCIL-tag-001',
|
|
116
|
+
vendor: 'KMPCIL',
|
|
117
|
+
description: 'Arrival sensor',
|
|
118
|
+
fromZigbee: [kmpcilConverters.presence_binary_input, kmpcilConverters.presence_power, fz.temperature],
|
|
119
|
+
exposes: [e.battery(), e.presence(), exposes.binary('power_state', exposes.access.STATE, true, false),
|
|
120
|
+
e.occupancy(), e.vibration(), e.temperature()],
|
|
121
|
+
toZigbee: [],
|
|
122
|
+
meta: {battery: {voltageToPercentage: '3V_1500_2800'}},
|
|
123
|
+
configure: async (device, coordinatorEndpoint, logger) => {
|
|
124
|
+
const endpoint = device.getEndpoint(1);
|
|
125
|
+
for (const cluster of ['msTemperatureMeasurement', 'genPowerCfg', 'genBinaryInput']) {
|
|
126
|
+
// This sleep here(and the sleep) after is to allow the command to be
|
|
127
|
+
// fully sent to coordinator. In case repeater involved and the repeater
|
|
128
|
+
// is litted in resources, we may want to give some time so that the sequence of
|
|
129
|
+
// commands does not overwhelm the repeater.
|
|
130
|
+
await utils.sleep(2000);
|
|
131
|
+
await endpoint.bind(cluster, coordinatorEndpoint);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
await utils.sleep(1000);
|
|
135
|
+
const p = reporting.payload('batteryVoltage', 0, 10, 1);
|
|
136
|
+
await endpoint.configureReporting('genPowerCfg', p);
|
|
137
|
+
|
|
138
|
+
await utils.sleep(1000);
|
|
139
|
+
const p2 = reporting.payload('presentValue', 0, 300, 1);
|
|
140
|
+
await endpoint.configureReporting('genBinaryInput', p2);
|
|
141
|
+
|
|
142
|
+
await utils.sleep(1000);
|
|
143
|
+
await reporting.temperature(endpoint);
|
|
144
|
+
await endpoint.read('genBinaryInput', ['presentValue']);
|
|
145
|
+
},
|
|
146
|
+
},
|
|
50
147
|
];
|
package/devices/legrand.js
CHANGED
|
@@ -32,8 +32,8 @@ module.exports = [
|
|
|
32
32
|
{
|
|
33
33
|
zigbeeModel: [' Contactor\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000'+
|
|
34
34
|
'\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000'],
|
|
35
|
-
model: '
|
|
36
|
-
description: '
|
|
35
|
+
model: '412171',
|
|
36
|
+
description: 'DIN contactor module ( Bticino FC80CC )',
|
|
37
37
|
vendor: 'Legrand',
|
|
38
38
|
extend: extend.switch(),
|
|
39
39
|
fromZigbee: [fz.identify, fz.on_off, fz.electrical_measurement, fz.legrand_cluster_fc01, fz.ignore_basic_report, fz.ignore_genOta],
|
|
@@ -52,8 +52,8 @@ module.exports = [
|
|
|
52
52
|
{
|
|
53
53
|
zigbeeModel: [' Teleruptor\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000'+
|
|
54
54
|
'\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000'],
|
|
55
|
-
model: '
|
|
56
|
-
description: '
|
|
55
|
+
model: '412170',
|
|
56
|
+
description: 'DIN smart relay for light control ( Bticino FC80RC ) ',
|
|
57
57
|
vendor: 'Legrand',
|
|
58
58
|
extend: extend.switch(),
|
|
59
59
|
fromZigbee: [fz.identify, fz.on_off, fz.electrical_measurement, fz.legrand_cluster_fc01, fz.ignore_basic_report, fz.ignore_genOta],
|
package/devices/lidl.js
CHANGED
|
@@ -528,6 +528,17 @@ module.exports = [
|
|
|
528
528
|
device.getEndpoint(1).saveClusterAttributeKeyValue('lightingColorCtrl', {colorCapabilities: 29});
|
|
529
529
|
},
|
|
530
530
|
},
|
|
531
|
+
{
|
|
532
|
+
fingerprint: [{modelID: 'TS0502B', manufacturerName: '_TZ3210_c2iwpxf1'}],
|
|
533
|
+
model: '14156408L',
|
|
534
|
+
vendor: 'Lidl',
|
|
535
|
+
description: 'Livarno Lux smart LED ceiling light',
|
|
536
|
+
...extend.light_onoff_brightness_colortemp_color({disableColorTempStartup: true, colorTempRange: [153, 500]}),
|
|
537
|
+
meta: {applyRedFix: true, enhancedHue: false},
|
|
538
|
+
configure: async (device, coordinatorEndpoint, logger) => {
|
|
539
|
+
device.getEndpoint(1).saveClusterAttributeKeyValue('lightingColorCtrl', {colorCapabilities: 16});
|
|
540
|
+
},
|
|
541
|
+
},
|
|
531
542
|
{
|
|
532
543
|
fingerprint: [{modelID: 'TS0505B', manufacturerName: '_TZ3210_p9ao60da'}],
|
|
533
544
|
model: 'HG08008',
|