zigbee-herdsman-converters 14.0.519 → 14.0.520
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 +0 -422
- package/devices/lidl.js +11 -0
- package/devices/lifecontrol.js +2 -2
- package/devices/schneider_electric.js +4 -3
- package/devices/ubisys.js +497 -9
- package/devices/xiaomi.js +2 -2
- package/package.json +1 -1
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},
|
|
@@ -4395,421 +4388,6 @@ const converters = {
|
|
|
4395
4388
|
}
|
|
4396
4389
|
},
|
|
4397
4390
|
},
|
|
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
4391
|
tint_scene: {
|
|
4814
4392
|
key: ['tint_scene'],
|
|
4815
4393
|
convertSet: async (entity, key, value, meta) => {
|
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',
|
package/devices/lifecontrol.js
CHANGED
|
@@ -80,8 +80,8 @@ module.exports = [
|
|
|
80
80
|
model: 'MCLH-08',
|
|
81
81
|
vendor: 'LifeControl',
|
|
82
82
|
description: 'Air sensor',
|
|
83
|
-
fromZigbee: [fz.lifecontrolVoc],
|
|
83
|
+
fromZigbee: [fz.lifecontrolVoc, fz.battery],
|
|
84
84
|
toZigbee: [],
|
|
85
|
-
exposes: [e.temperature(), e.humidity(), e.voc(), e.eco2()],
|
|
85
|
+
exposes: [e.temperature(), e.humidity(), e.voc(), e.eco2(), e.battery()],
|
|
86
86
|
},
|
|
87
87
|
];
|
|
@@ -439,10 +439,11 @@ module.exports = [
|
|
|
439
439
|
zigbeeModel: ['1GANG/SHUTTER/1'],
|
|
440
440
|
model: 'MEG5113-0300/MEG5165-0000',
|
|
441
441
|
vendor: 'Schneider Electric',
|
|
442
|
-
description: 'Merten PlusLink Shutter insert with Merten Wiser System M Push Button',
|
|
442
|
+
description: 'Merten MEG5165 PlusLink Shutter insert with Merten Wiser System M Push Button (1fold)',
|
|
443
443
|
fromZigbee: [fz.cover_position_tilt, fz.command_cover_close, fz.command_cover_open, fz.command_cover_stop],
|
|
444
|
-
toZigbee: [tz.cover_position_tilt, tz.cover_state],
|
|
445
|
-
exposes: [e.cover_position()
|
|
444
|
+
toZigbee: [tz.cover_position_tilt, tz.cover_state, tzLocal.lift_duration],
|
|
445
|
+
exposes: [e.cover_position(), exposes.numeric('lift_duration', ea.STATE_SET).withUnit('seconds')
|
|
446
|
+
.withValueMin(0).withValueMax(300).withDescription('Duration of lift')],
|
|
446
447
|
meta: {coverInverted: true},
|
|
447
448
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
448
449
|
const endpoint = device.getEndpoint(1) || device.getEndpoint(5);
|
package/devices/ubisys.js
CHANGED
|
@@ -2,10 +2,498 @@ const exposes = require('../lib/exposes');
|
|
|
2
2
|
const fz = {...require('../converters/fromZigbee'), legacy: require('../lib/legacy').fromZigbee};
|
|
3
3
|
const tz = require('../converters/toZigbee');
|
|
4
4
|
const ota = require('../lib/ota');
|
|
5
|
+
const utils = require('../lib/utils');
|
|
5
6
|
const reporting = require('../lib/reporting');
|
|
7
|
+
const herdsman = require('zigbee-herdsman');
|
|
6
8
|
const e = exposes.presets;
|
|
7
9
|
const ea = exposes.access;
|
|
8
10
|
|
|
11
|
+
const manufacturerOptions = {
|
|
12
|
+
/*
|
|
13
|
+
* Ubisys doesn't accept a manufacturerCode on some commands
|
|
14
|
+
* This bug has been reported, but it has not been fixed:
|
|
15
|
+
* https://github.com/Koenkk/zigbee-herdsman/issues/52
|
|
16
|
+
*/
|
|
17
|
+
ubisys: {manufacturerCode: herdsman.Zcl.ManufacturerCode.UBISYS},
|
|
18
|
+
ubisysNull: {manufacturerCode: null},
|
|
19
|
+
tint: {manufacturerCode: herdsman.Zcl.ManufacturerCode.MUELLER_LICHT_INT},
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const ubisys = {
|
|
23
|
+
fz: {
|
|
24
|
+
dimmer_setup: {
|
|
25
|
+
cluster: 'manuSpecificUbisysDimmerSetup',
|
|
26
|
+
type: ['attributeReport', 'readResponse'],
|
|
27
|
+
convert: (model, msg, publish, options, meta) => {
|
|
28
|
+
if (msg.data.hasOwnProperty('capabilities')) {
|
|
29
|
+
const capabilities = msg.data.capabilities;
|
|
30
|
+
const forwardPhaseControl = capabilities & 1;
|
|
31
|
+
const reversePhaseControl = (capabilities & 2) >>> 1;
|
|
32
|
+
const reactanceDiscriminator = (capabilities & 0x20) >>> 5;
|
|
33
|
+
const configurableCurve = (capabilities & 0x40) >>> 6;
|
|
34
|
+
const overloadDetection = (capabilities & 0x80) >>> 7;
|
|
35
|
+
return {
|
|
36
|
+
capabilities_forward_phase_control: forwardPhaseControl ? true : false,
|
|
37
|
+
capabilities_reverse_phase_control: reversePhaseControl ? true : false,
|
|
38
|
+
capabilities_reactance_discriminator: reactanceDiscriminator ? true : false,
|
|
39
|
+
capabilities_configurable_curve: configurableCurve ? true : false,
|
|
40
|
+
capabilities_overload_detection: overloadDetection ? true : false,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
if (msg.data.hasOwnProperty('status')) {
|
|
44
|
+
const status = msg.data.status;
|
|
45
|
+
const forwardPhaseControl = status & 1;
|
|
46
|
+
const reversePhaseControl = (status & 2) >>> 1;
|
|
47
|
+
const overload = (status & 8) >>> 3;
|
|
48
|
+
const capacitiveLoad = (status & 0x40) >>> 6;
|
|
49
|
+
const inductiveLoad = (status & 0x80) >>> 7;
|
|
50
|
+
return {
|
|
51
|
+
status_forward_phase_control: forwardPhaseControl ? true : false,
|
|
52
|
+
status_reverse_phase_control: reversePhaseControl ? true : false,
|
|
53
|
+
status_overload: overload ? true : false,
|
|
54
|
+
status_capacitive_load: capacitiveLoad ? true : false,
|
|
55
|
+
status_inductive_load: inductiveLoad ? true : false,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
if (msg.data.hasOwnProperty('mode')) {
|
|
59
|
+
const mode = msg.data.mode;
|
|
60
|
+
const phaseControl = mode & 3;
|
|
61
|
+
const phaseControlValues = {0: 'automatic', 1: 'forward', 2: 'reverse'};
|
|
62
|
+
return {
|
|
63
|
+
mode_phase_control: phaseControlValues[phaseControl],
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
dimmer_setup_genLevelCtrl: {
|
|
69
|
+
cluster: 'genLevelCtrl',
|
|
70
|
+
type: ['attributeReport', 'readResponse'],
|
|
71
|
+
convert: (model, msg, publish, options, meta) => {
|
|
72
|
+
if (msg.data.hasOwnProperty('ubisysMinimumOnLevel')) {
|
|
73
|
+
return {minimum_on_level: msg.data.ubisysMinimumOnLevel};
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
tz: {
|
|
79
|
+
configure_j1: {
|
|
80
|
+
key: ['configure_j1'],
|
|
81
|
+
convertSet: async (entity, key, value, meta) => {
|
|
82
|
+
const log = (message) => {
|
|
83
|
+
meta.logger.warn(`ubisys: ${message}`);
|
|
84
|
+
};
|
|
85
|
+
const sleepSeconds = async (s) => {
|
|
86
|
+
return new Promise((resolve) => setTimeout(resolve, s * 1000));
|
|
87
|
+
};
|
|
88
|
+
const waitUntilStopped = async () => {
|
|
89
|
+
let operationalStatus = 0;
|
|
90
|
+
do {
|
|
91
|
+
await sleepSeconds(2);
|
|
92
|
+
operationalStatus = (await entity.read('closuresWindowCovering',
|
|
93
|
+
['operationalStatus'])).operationalStatus;
|
|
94
|
+
} while (operationalStatus != 0);
|
|
95
|
+
await sleepSeconds(2);
|
|
96
|
+
};
|
|
97
|
+
const writeAttrFromJson = async (attr, jsonAttr = attr, converterFunc, delaySecondsAfter) => {
|
|
98
|
+
if (jsonAttr.startsWith('ubisys')) {
|
|
99
|
+
jsonAttr = jsonAttr.substring(6, 1).toLowerCase + jsonAttr.substring(7);
|
|
100
|
+
}
|
|
101
|
+
if (value.hasOwnProperty(jsonAttr)) {
|
|
102
|
+
let attrValue = value[jsonAttr];
|
|
103
|
+
if (converterFunc) {
|
|
104
|
+
attrValue = converterFunc(attrValue);
|
|
105
|
+
}
|
|
106
|
+
const attributes = {};
|
|
107
|
+
attributes[attr] = attrValue;
|
|
108
|
+
await entity.write('closuresWindowCovering', attributes, manufacturerOptions.ubisys);
|
|
109
|
+
if (delaySecondsAfter) {
|
|
110
|
+
await sleepSeconds(delaySecondsAfter);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
const stepsPerSecond = value.steps_per_second || 50;
|
|
115
|
+
const hasCalibrate = value.hasOwnProperty('calibrate');
|
|
116
|
+
// cancel any running calibration
|
|
117
|
+
let mode = (await entity.read('closuresWindowCovering', ['windowCoveringMode'])).windowCoveringMode;
|
|
118
|
+
const modeCalibrationBitMask = 0x02;
|
|
119
|
+
if (mode & modeCalibrationBitMask) {
|
|
120
|
+
await entity.write('closuresWindowCovering', {windowCoveringMode: mode & ~modeCalibrationBitMask});
|
|
121
|
+
await sleepSeconds(2);
|
|
122
|
+
}
|
|
123
|
+
// delay a bit if reconfiguring basic configuration attributes
|
|
124
|
+
await writeAttrFromJson('windowCoveringType', undefined, undefined, 2);
|
|
125
|
+
await writeAttrFromJson('configStatus', undefined, undefined, 2);
|
|
126
|
+
if (await writeAttrFromJson('windowCoveringMode', undefined, undefined, 2)) {
|
|
127
|
+
mode = value['windowCoveringMode'];
|
|
128
|
+
}
|
|
129
|
+
if (hasCalibrate) {
|
|
130
|
+
log('Cover calibration starting...');
|
|
131
|
+
// first of all, move to top position to not confuse calibration later
|
|
132
|
+
log(' Moving cover to top position to get a good starting point...');
|
|
133
|
+
await entity.command('closuresWindowCovering', 'upOpen');
|
|
134
|
+
await waitUntilStopped();
|
|
135
|
+
log(' Settings some attributes...');
|
|
136
|
+
// reset attributes
|
|
137
|
+
await entity.write('closuresWindowCovering', {
|
|
138
|
+
installedOpenLimitLiftCm: 0,
|
|
139
|
+
installedClosedLimitLiftCm: 240,
|
|
140
|
+
installedOpenLimitTiltDdegree: 0,
|
|
141
|
+
installedClosedLimitTiltDdegree: 900,
|
|
142
|
+
ubisysLiftToTiltTransitionSteps: 0xffff,
|
|
143
|
+
ubisysTotalSteps: 0xffff,
|
|
144
|
+
ubisysLiftToTiltTransitionSteps2: 0xffff,
|
|
145
|
+
ubisysTotalSteps2: 0xffff,
|
|
146
|
+
}, manufacturerOptions.ubisys);
|
|
147
|
+
// enable calibration mode
|
|
148
|
+
await sleepSeconds(2);
|
|
149
|
+
await entity.write('closuresWindowCovering', {windowCoveringMode: mode | modeCalibrationBitMask});
|
|
150
|
+
await sleepSeconds(2);
|
|
151
|
+
// move down a bit and back up to detect upper limit
|
|
152
|
+
log(' Moving cover down a bit...');
|
|
153
|
+
await entity.command('closuresWindowCovering', 'downClose');
|
|
154
|
+
await sleepSeconds(5);
|
|
155
|
+
await entity.command('closuresWindowCovering', 'stop');
|
|
156
|
+
await sleepSeconds(2);
|
|
157
|
+
log(' Moving up again to detect upper limit...');
|
|
158
|
+
await entity.command('closuresWindowCovering', 'upOpen');
|
|
159
|
+
await waitUntilStopped();
|
|
160
|
+
log(' Moving down to count steps from open to closed...');
|
|
161
|
+
await entity.command('closuresWindowCovering', 'downClose');
|
|
162
|
+
await waitUntilStopped();
|
|
163
|
+
log(' Moving up to count steps from closed to open...');
|
|
164
|
+
await entity.command('closuresWindowCovering', 'upOpen');
|
|
165
|
+
await waitUntilStopped();
|
|
166
|
+
}
|
|
167
|
+
// now write any attribute values present in JSON
|
|
168
|
+
await writeAttrFromJson('installedOpenLimitLiftCm');
|
|
169
|
+
await writeAttrFromJson('installedClosedLimitLiftCm');
|
|
170
|
+
await writeAttrFromJson('installedOpenLimitTiltDdegree');
|
|
171
|
+
await writeAttrFromJson('installedClosedLimitTiltDdegree');
|
|
172
|
+
await writeAttrFromJson('ubisysTurnaroundGuardTime');
|
|
173
|
+
await writeAttrFromJson('ubisysLiftToTiltTransitionSteps');
|
|
174
|
+
await writeAttrFromJson('ubisysTotalSteps');
|
|
175
|
+
await writeAttrFromJson('ubisysLiftToTiltTransitionSteps2');
|
|
176
|
+
await writeAttrFromJson('ubisysTotalSteps2');
|
|
177
|
+
await writeAttrFromJson('ubisysAdditionalSteps');
|
|
178
|
+
await writeAttrFromJson('ubisysInactivePowerThreshold');
|
|
179
|
+
await writeAttrFromJson('ubisysStartupSteps');
|
|
180
|
+
// some convenience functions to not have to calculate
|
|
181
|
+
await writeAttrFromJson('ubisysTotalSteps', 'open_to_closed_s', (s) => s * stepsPerSecond);
|
|
182
|
+
await writeAttrFromJson('ubisysTotalSteps2', 'closed_to_open_s', (s) => s * stepsPerSecond);
|
|
183
|
+
await writeAttrFromJson('ubisysLiftToTiltTransitionSteps', 'lift_to_tilt_transition_ms',
|
|
184
|
+
(s) => s * stepsPerSecond / 1000);
|
|
185
|
+
await writeAttrFromJson('ubisysLiftToTiltTransitionSteps2', 'lift_to_tilt_transition_ms',
|
|
186
|
+
(s) => s * stepsPerSecond / 1000);
|
|
187
|
+
if (hasCalibrate) {
|
|
188
|
+
log(' Finalizing calibration...');
|
|
189
|
+
// disable calibration mode again
|
|
190
|
+
await sleepSeconds(2);
|
|
191
|
+
await entity.write('closuresWindowCovering', {windowCoveringMode: mode & ~modeCalibrationBitMask});
|
|
192
|
+
await sleepSeconds(2);
|
|
193
|
+
// re-read and dump all relevant attributes
|
|
194
|
+
log(' Done - will now read back the results.');
|
|
195
|
+
ubisys.tz.configure_j1.convertGet(entity, key, meta);
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
convertGet: async (entity, key, meta) => {
|
|
199
|
+
const log = (json) => {
|
|
200
|
+
meta.logger.warn(`ubisys: Cover configuration read: ${JSON.stringify(json)}`);
|
|
201
|
+
};
|
|
202
|
+
log(await entity.read('closuresWindowCovering', [
|
|
203
|
+
'windowCoveringType',
|
|
204
|
+
'physicalClosedLimitLiftCm',
|
|
205
|
+
'physicalClosedLimitTiltDdegree',
|
|
206
|
+
'installedOpenLimitLiftCm',
|
|
207
|
+
'installedClosedLimitLiftCm',
|
|
208
|
+
'installedOpenLimitTiltDdegree',
|
|
209
|
+
'installedClosedLimitTiltDdegree',
|
|
210
|
+
]));
|
|
211
|
+
log(await entity.read('closuresWindowCovering', [
|
|
212
|
+
'configStatus',
|
|
213
|
+
'windowCoveringMode',
|
|
214
|
+
'currentPositionLiftPercentage',
|
|
215
|
+
'currentPositionLiftCm',
|
|
216
|
+
'currentPositionTiltPercentage',
|
|
217
|
+
'currentPositionTiltDdegree',
|
|
218
|
+
'operationalStatus',
|
|
219
|
+
]));
|
|
220
|
+
log(await entity.read('closuresWindowCovering', [
|
|
221
|
+
'ubisysTurnaroundGuardTime',
|
|
222
|
+
'ubisysLiftToTiltTransitionSteps',
|
|
223
|
+
'ubisysTotalSteps',
|
|
224
|
+
'ubisysLiftToTiltTransitionSteps2',
|
|
225
|
+
'ubisysTotalSteps2',
|
|
226
|
+
'ubisysAdditionalSteps',
|
|
227
|
+
'ubisysInactivePowerThreshold',
|
|
228
|
+
'ubisysStartupSteps',
|
|
229
|
+
], manufacturerOptions.ubisys));
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
dimmer_setup: {
|
|
233
|
+
key: ['capabilities_forward_phase_control',
|
|
234
|
+
'capabilities_reverse_phase_control',
|
|
235
|
+
'capabilities_reactance_discriminator',
|
|
236
|
+
'capabilities_configurable_curve',
|
|
237
|
+
'capabilities_overload_detection',
|
|
238
|
+
'status_forward_phase_control',
|
|
239
|
+
'status_reverse_phase_control',
|
|
240
|
+
'status_overload',
|
|
241
|
+
'status_capacitive_load',
|
|
242
|
+
'status_inductive_load',
|
|
243
|
+
'mode_phase_control'],
|
|
244
|
+
convertSet: async (entity, key, value, meta) => {
|
|
245
|
+
if (key === 'mode_phase_control') {
|
|
246
|
+
const phaseControl = value.toLowerCase();
|
|
247
|
+
const phaseControlValues = {'automatic': 0, 'forward': 1, 'reverse': 2};
|
|
248
|
+
utils.validateValue(phaseControl, Object.keys(phaseControlValues));
|
|
249
|
+
await entity.write('manuSpecificUbisysDimmerSetup',
|
|
250
|
+
{'mode': phaseControlValues[phaseControl]}, manufacturerOptions.ubisysNull);
|
|
251
|
+
}
|
|
252
|
+
ubisys.tz.dimmer_setup.convertGet(entity, key, meta);
|
|
253
|
+
},
|
|
254
|
+
convertGet: async (entity, key, meta) => {
|
|
255
|
+
await entity.read('manuSpecificUbisysDimmerSetup', ['capabilities'], manufacturerOptions.ubisysNull);
|
|
256
|
+
await entity.read('manuSpecificUbisysDimmerSetup', ['status'], manufacturerOptions.ubisysNull);
|
|
257
|
+
await entity.read('manuSpecificUbisysDimmerSetup', ['mode'], manufacturerOptions.ubisysNull);
|
|
258
|
+
},
|
|
259
|
+
},
|
|
260
|
+
dimmer_setup_genLevelCtrl: {
|
|
261
|
+
key: ['minimum_on_level'],
|
|
262
|
+
convertSet: async (entity, key, value, meta) => {
|
|
263
|
+
if (key === 'minimum_on_level') {
|
|
264
|
+
await entity.write('genLevelCtrl', {'ubisysMinimumOnLevel': value}, manufacturerOptions.ubisys);
|
|
265
|
+
}
|
|
266
|
+
ubisys.tz.dimmer_setup_genLevelCtrl.convertGet(entity, key, meta);
|
|
267
|
+
},
|
|
268
|
+
convertGet: async (entity, key, meta) => {
|
|
269
|
+
await entity.read('genLevelCtrl', ['ubisysMinimumOnLevel'], manufacturerOptions.ubisys);
|
|
270
|
+
},
|
|
271
|
+
},
|
|
272
|
+
configure_device_setup: {
|
|
273
|
+
key: ['configure_device_setup'],
|
|
274
|
+
convertSet: async (entity, key, value, meta) => {
|
|
275
|
+
const devMgmtEp = meta.device.getEndpoint(232);
|
|
276
|
+
|
|
277
|
+
if (value.hasOwnProperty('input_configurations')) {
|
|
278
|
+
// example: [0, 0, 0, 0]
|
|
279
|
+
await devMgmtEp.write(
|
|
280
|
+
'manuSpecificUbisysDeviceSetup',
|
|
281
|
+
{'inputConfigurations': {elementType: 'data8', elements: value.input_configurations}},
|
|
282
|
+
manufacturerOptions.ubisysNull,
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (value.hasOwnProperty('input_actions')) {
|
|
287
|
+
// 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]]
|
|
288
|
+
await devMgmtEp.write(
|
|
289
|
+
'manuSpecificUbisysDeviceSetup',
|
|
290
|
+
{'inputActions': {elementType: 'octetStr', elements: value.input_actions}},
|
|
291
|
+
manufacturerOptions.ubisysNull,
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (value.hasOwnProperty('input_action_templates')) {
|
|
296
|
+
const templateTypes = {
|
|
297
|
+
// source: "ZigBee Device Physical Input Configurations Integrator’s Guide"
|
|
298
|
+
// (can be obtained directly from ubisys upon request)
|
|
299
|
+
'toggle': {
|
|
300
|
+
getInputActions: (input, endpoint) => [
|
|
301
|
+
[input, 0x0D, endpoint, 0x06, 0x00, 0x02],
|
|
302
|
+
],
|
|
303
|
+
},
|
|
304
|
+
'toggle_switch': {
|
|
305
|
+
getInputActions: (input, endpoint) => [
|
|
306
|
+
[input, 0x0D, endpoint, 0x06, 0x00, 0x02],
|
|
307
|
+
[input, 0x03, endpoint, 0x06, 0x00, 0x02],
|
|
308
|
+
],
|
|
309
|
+
},
|
|
310
|
+
'on_off_switch': {
|
|
311
|
+
getInputActions: (input, endpoint) => [
|
|
312
|
+
[input, 0x0D, endpoint, 0x06, 0x00, 0x01],
|
|
313
|
+
[input, 0x03, endpoint, 0x06, 0x00, 0x00],
|
|
314
|
+
],
|
|
315
|
+
},
|
|
316
|
+
'on': {
|
|
317
|
+
getInputActions: (input, endpoint) => [
|
|
318
|
+
[input, 0x0D, endpoint, 0x06, 0x00, 0x01],
|
|
319
|
+
],
|
|
320
|
+
},
|
|
321
|
+
'off': {
|
|
322
|
+
getInputActions: (input, endpoint) => [
|
|
323
|
+
[input, 0x0D, endpoint, 0x06, 0x00, 0x00],
|
|
324
|
+
],
|
|
325
|
+
},
|
|
326
|
+
'dimmer_single': {
|
|
327
|
+
getInputActions: (input, endpoint, template) => {
|
|
328
|
+
const moveUpCmd = template.no_onoff || template.no_onoff_up ? 0x01 : 0x05;
|
|
329
|
+
const moveDownCmd = template.no_onoff || template.no_onoff_down ? 0x01 : 0x05;
|
|
330
|
+
const moveRate = template.rate || 50;
|
|
331
|
+
return [
|
|
332
|
+
[input, 0x07, endpoint, 0x06, 0x00, 0x02],
|
|
333
|
+
[input, 0x86, endpoint, 0x08, 0x00, moveUpCmd, 0x00, moveRate],
|
|
334
|
+
[input, 0xC6, endpoint, 0x08, 0x00, moveDownCmd, 0x01, moveRate],
|
|
335
|
+
[input, 0x0B, endpoint, 0x08, 0x00, 0x03],
|
|
336
|
+
];
|
|
337
|
+
},
|
|
338
|
+
},
|
|
339
|
+
'dimmer_double': {
|
|
340
|
+
doubleInputs: true,
|
|
341
|
+
getInputActions: (inputs, endpoint, template) => {
|
|
342
|
+
const moveUpCmd = template.no_onoff || template.no_onoff_up ? 0x01 : 0x05;
|
|
343
|
+
const moveDownCmd = template.no_onoff || template.no_onoff_down ? 0x01 : 0x05;
|
|
344
|
+
const moveRate = template.rate || 50;
|
|
345
|
+
return [
|
|
346
|
+
[inputs[0], 0x07, endpoint, 0x06, 0x00, 0x01],
|
|
347
|
+
[inputs[0], 0x06, endpoint, 0x08, 0x00, moveUpCmd, 0x00, moveRate],
|
|
348
|
+
[inputs[0], 0x0B, endpoint, 0x08, 0x00, 0x03],
|
|
349
|
+
[inputs[1], 0x07, endpoint, 0x06, 0x00, 0x00],
|
|
350
|
+
[inputs[1], 0x06, endpoint, 0x08, 0x00, moveDownCmd, 0x01, moveRate],
|
|
351
|
+
[inputs[1], 0x0B, endpoint, 0x08, 0x00, 0x03],
|
|
352
|
+
];
|
|
353
|
+
},
|
|
354
|
+
},
|
|
355
|
+
'cover': {
|
|
356
|
+
cover: true,
|
|
357
|
+
doubleInputs: true,
|
|
358
|
+
getInputActions: (inputs, endpoint) => [
|
|
359
|
+
[inputs[0], 0x0D, endpoint, 0x02, 0x01, 0x00],
|
|
360
|
+
[inputs[0], 0x07, endpoint, 0x02, 0x01, 0x02],
|
|
361
|
+
[inputs[1], 0x0D, endpoint, 0x02, 0x01, 0x01],
|
|
362
|
+
[inputs[1], 0x07, endpoint, 0x02, 0x01, 0x02],
|
|
363
|
+
],
|
|
364
|
+
},
|
|
365
|
+
'cover_switch': {
|
|
366
|
+
cover: true,
|
|
367
|
+
doubleInputs: true,
|
|
368
|
+
getInputActions: (inputs, endpoint) => [
|
|
369
|
+
[inputs[0], 0x0D, endpoint, 0x02, 0x01, 0x00],
|
|
370
|
+
[inputs[0], 0x03, endpoint, 0x02, 0x01, 0x02],
|
|
371
|
+
[inputs[1], 0x0D, endpoint, 0x02, 0x01, 0x01],
|
|
372
|
+
[inputs[1], 0x03, endpoint, 0x02, 0x01, 0x02],
|
|
373
|
+
],
|
|
374
|
+
},
|
|
375
|
+
'cover_up': {
|
|
376
|
+
cover: true,
|
|
377
|
+
getInputActions: (input, endpoint) => [
|
|
378
|
+
[input, 0x0D, endpoint, 0x02, 0x01, 0x00],
|
|
379
|
+
],
|
|
380
|
+
},
|
|
381
|
+
'cover_down': {
|
|
382
|
+
cover: true,
|
|
383
|
+
getInputActions: (input, endpoint) => [
|
|
384
|
+
[input, 0x0D, endpoint, 0x02, 0x01, 0x01],
|
|
385
|
+
],
|
|
386
|
+
},
|
|
387
|
+
'scene': {
|
|
388
|
+
scene: true,
|
|
389
|
+
getInputActions: (input, endpoint, groupId, sceneId) => [
|
|
390
|
+
[input, 0x07, endpoint, 0x05, 0x00, 0x05, groupId & 0xff, groupId >> 8, sceneId],
|
|
391
|
+
],
|
|
392
|
+
getInputActions2: (input, endpoint, groupId, sceneId) => [
|
|
393
|
+
[input, 0x06, endpoint, 0x05, 0x00, 0x05, groupId & 0xff, groupId >> 8, sceneId],
|
|
394
|
+
],
|
|
395
|
+
},
|
|
396
|
+
'scene_switch': {
|
|
397
|
+
scene: true,
|
|
398
|
+
getInputActions: (input, endpoint, groupId, sceneId) => [
|
|
399
|
+
[input, 0x0D, endpoint, 0x05, 0x00, 0x05, groupId & 0xff, groupId >> 8, sceneId],
|
|
400
|
+
],
|
|
401
|
+
getInputActions2: (input, endpoint, groupId, sceneId) => [
|
|
402
|
+
[input, 0x03, endpoint, 0x05, 0x00, 0x05, groupId & 0xff, groupId >> 8, sceneId],
|
|
403
|
+
],
|
|
404
|
+
},
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
// first input
|
|
408
|
+
let input = 0;
|
|
409
|
+
// first client endpoint - depends on actual device
|
|
410
|
+
let endpoint = {'S1': 2, 'S2': 3, 'D1': 2, 'J1': 2, 'C4': 1}[meta.mapped.model];
|
|
411
|
+
// default group id
|
|
412
|
+
let groupId = 0;
|
|
413
|
+
|
|
414
|
+
const templates = Array.isArray(value.input_action_templates) ? value.input_action_templates :
|
|
415
|
+
[value.input_action_templates];
|
|
416
|
+
let resultingInputActions = [];
|
|
417
|
+
for (const template of templates) {
|
|
418
|
+
const templateType = templateTypes[template.type];
|
|
419
|
+
if (!templateType) {
|
|
420
|
+
throw new Error(`input_action_templates: Template type '${template.type}' is not valid ` +
|
|
421
|
+
`(valid types: ${Object.keys(templateTypes)})`);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (template.hasOwnProperty('input')) {
|
|
425
|
+
input = template.input;
|
|
426
|
+
}
|
|
427
|
+
if (template.hasOwnProperty('endpoint')) {
|
|
428
|
+
endpoint = template.endpoint;
|
|
429
|
+
}
|
|
430
|
+
// C4 cover endpoints only start at 5
|
|
431
|
+
if (templateType.cover && meta.mapped.model === 'C4' && endpoint < 5) {
|
|
432
|
+
endpoint += 4;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
let inputActions;
|
|
436
|
+
if (!templateType.doubleInputs) {
|
|
437
|
+
if (!templateType.scene) {
|
|
438
|
+
// single input, no scene(s)
|
|
439
|
+
inputActions = templateType.getInputActions(input, endpoint, template);
|
|
440
|
+
} else {
|
|
441
|
+
// scene(s) (always single input)
|
|
442
|
+
if (!template.hasOwnProperty('scene_id')) {
|
|
443
|
+
throw new Error(`input_action_templates: Need an attribute 'scene_id' for '${template.type}'`);
|
|
444
|
+
}
|
|
445
|
+
if (template.hasOwnProperty('group_id')) {
|
|
446
|
+
groupId = template.group_id;
|
|
447
|
+
}
|
|
448
|
+
inputActions = templateType.getInputActions(input, endpoint, groupId, template.scene_id);
|
|
449
|
+
|
|
450
|
+
if (template.hasOwnProperty('scene_id_2')) {
|
|
451
|
+
if (template.hasOwnProperty('group_id_2')) {
|
|
452
|
+
groupId = template.group_id_2;
|
|
453
|
+
}
|
|
454
|
+
inputActions = inputActions.concat(templateType.getInputActions2(input, endpoint, groupId,
|
|
455
|
+
template.scene_id_2));
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
} else {
|
|
459
|
+
// double inputs
|
|
460
|
+
input = template.hasOwnProperty('inputs') ? template.inputs : [input, input + 1];
|
|
461
|
+
inputActions = templateType.getInputActions(input, endpoint, template);
|
|
462
|
+
}
|
|
463
|
+
resultingInputActions = resultingInputActions.concat(inputActions);
|
|
464
|
+
|
|
465
|
+
meta.logger.warn(`ubisys: Using input(s) ${input} and endpoint ${endpoint} for '${template.type}'.`);
|
|
466
|
+
// input might by now be an array (in case of double inputs)
|
|
467
|
+
input = (Array.isArray(input) ? Math.max(...input) : input) + 1;
|
|
468
|
+
endpoint += 1;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
meta.logger.debug(`ubisys: input_actions to be sent to '${meta.options.friendlyName}': ` +
|
|
472
|
+
JSON.stringify(resultingInputActions));
|
|
473
|
+
await devMgmtEp.write(
|
|
474
|
+
'manuSpecificUbisysDeviceSetup',
|
|
475
|
+
{'inputActions': {elementType: 'octetStr', elements: resultingInputActions}},
|
|
476
|
+
manufacturerOptions.ubisysNull,
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// re-read effective settings and dump them to the log
|
|
481
|
+
ubisys.tz.configure_device_setup.convertGet(entity, key, meta);
|
|
482
|
+
},
|
|
483
|
+
|
|
484
|
+
convertGet: async (entity, key, meta) => {
|
|
485
|
+
const log = (dataRead) => {
|
|
486
|
+
meta.logger.warn(
|
|
487
|
+
`ubisys: Device setup read for '${meta.options.friendly_name}': ${JSON.stringify(utils.toSnakeCase(dataRead))}`);
|
|
488
|
+
};
|
|
489
|
+
const devMgmtEp = meta.device.getEndpoint(232);
|
|
490
|
+
log(await devMgmtEp.read('manuSpecificUbisysDeviceSetup', ['inputConfigurations'], manufacturerOptions.ubisysNull));
|
|
491
|
+
log(await devMgmtEp.read('manuSpecificUbisysDeviceSetup', ['inputActions'], manufacturerOptions.ubisysNull));
|
|
492
|
+
},
|
|
493
|
+
},
|
|
494
|
+
},
|
|
495
|
+
};
|
|
496
|
+
|
|
9
497
|
module.exports = [
|
|
10
498
|
{
|
|
11
499
|
zigbeeModel: ['S1 (5501)'],
|
|
@@ -20,7 +508,7 @@ module.exports = [
|
|
|
20
508
|
e.power_on_behavior()],
|
|
21
509
|
fromZigbee: [fz.on_off, fz.metering, fz.command_toggle, fz.command_on, fz.command_off, fz.command_recall, fz.command_move,
|
|
22
510
|
fz.command_stop, fz.power_on_behavior],
|
|
23
|
-
toZigbee: [tz.on_off, tz.metering_power, tz.
|
|
511
|
+
toZigbee: [tz.on_off, tz.metering_power, ubisys.tz.configure_device_setup, tz.power_on_behavior],
|
|
24
512
|
endpoint: (device) => {
|
|
25
513
|
return {'l1': 1, 's1': 2, 'meter': 3};
|
|
26
514
|
},
|
|
@@ -62,7 +550,7 @@ module.exports = [
|
|
|
62
550
|
e.power_on_behavior()],
|
|
63
551
|
fromZigbee: [fz.on_off, fz.metering, fz.command_toggle, fz.command_on, fz.command_off, fz.command_recall, fz.command_move,
|
|
64
552
|
fz.command_stop, fz.power_on_behavior],
|
|
65
|
-
toZigbee: [tz.on_off, tz.metering_power, tz.
|
|
553
|
+
toZigbee: [tz.on_off, tz.metering_power, ubisys.tz.configure_device_setup, tz.power_on_behavior],
|
|
66
554
|
endpoint: (device) => {
|
|
67
555
|
return {'l1': 1, 's1': 2, 'meter': 4};
|
|
68
556
|
},
|
|
@@ -105,7 +593,7 @@ module.exports = [
|
|
|
105
593
|
e.power_on_behavior().withEndpoint('l1'), e.power_on_behavior().withEndpoint('l2')],
|
|
106
594
|
fromZigbee: [fz.on_off, fz.metering, fz.command_toggle, fz.command_on, fz.command_off, fz.command_recall, fz.command_move,
|
|
107
595
|
fz.command_stop, fz.power_on_behavior],
|
|
108
|
-
toZigbee: [tz.on_off, tz.metering_power, tz.
|
|
596
|
+
toZigbee: [tz.on_off, tz.metering_power, ubisys.tz.configure_device_setup, tz.power_on_behavior],
|
|
109
597
|
endpoint: (device) => {
|
|
110
598
|
return {'l1': 1, 'l2': 2, 's1': 3, 's2': 4, 'meter': 5};
|
|
111
599
|
},
|
|
@@ -149,10 +637,10 @@ module.exports = [
|
|
|
149
637
|
vendor: 'Ubisys',
|
|
150
638
|
description: 'Universal dimmer D1',
|
|
151
639
|
fromZigbee: [fz.on_off, fz.brightness, fz.metering, fz.command_toggle, fz.command_on, fz.command_off, fz.command_recall,
|
|
152
|
-
fz.command_move, fz.command_stop, fz.lighting_ballast_configuration, fz.level_config, fz.
|
|
153
|
-
fz.
|
|
154
|
-
toZigbee: [tz.light_onoff_brightness, tz.ballast_config, tz.level_config, tz.
|
|
155
|
-
tz.
|
|
640
|
+
fz.command_move, fz.command_stop, fz.lighting_ballast_configuration, fz.level_config, ubisys.fz.dimmer_setup,
|
|
641
|
+
ubisys.fz.dimmer_setup_genLevelCtrl],
|
|
642
|
+
toZigbee: [tz.light_onoff_brightness, tz.ballast_config, tz.level_config, ubisys.tz.dimmer_setup,
|
|
643
|
+
ubisys.tz.dimmer_setup_genLevelCtrl, ubisys.tz.configure_device_setup, tz.ignore_transition],
|
|
156
644
|
exposes: [e.light_brightness().withLevelConfig(), e.power(),
|
|
157
645
|
exposes.numeric('ballast_physical_minimum_level', ea.ALL).withValueMin(1).withValueMax(254)
|
|
158
646
|
.withDescription('Specifies the minimum light output the ballast can achieve.'),
|
|
@@ -215,7 +703,7 @@ module.exports = [
|
|
|
215
703
|
description: 'Shutter control J1',
|
|
216
704
|
fromZigbee: [fz.cover_position_tilt, fz.metering],
|
|
217
705
|
toZigbee: [tz.cover_state, tz.cover_position_tilt, tz.metering_power,
|
|
218
|
-
tz.
|
|
706
|
+
ubisys.tz.configure_j1, ubisys.tz.configure_device_setup],
|
|
219
707
|
exposes: [e.cover_position_tilt(),
|
|
220
708
|
e.power().withAccess(ea.STATE_GET).withEndpoint('meter').withProperty('power')],
|
|
221
709
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
@@ -251,7 +739,7 @@ module.exports = [
|
|
|
251
739
|
vendor: 'Ubisys',
|
|
252
740
|
description: 'Control unit C4',
|
|
253
741
|
fromZigbee: [fz.legacy.ubisys_c4_scenes, fz.legacy.ubisys_c4_onoff, fz.legacy.ubisys_c4_level, fz.legacy.ubisys_c4_cover],
|
|
254
|
-
toZigbee: [tz.
|
|
742
|
+
toZigbee: [ubisys.tz.configure_device_setup],
|
|
255
743
|
exposes: [e.action([
|
|
256
744
|
'1_scene_*', '1_on', '1_off', '1_toggle', '1_level_move_down', '1_level_move_up',
|
|
257
745
|
'2_scene_*', '2_on', '2_off', '2_toggle', '2_level_move_down', '2_level_move_up',
|
package/devices/xiaomi.js
CHANGED
|
@@ -1001,7 +1001,7 @@ module.exports = [
|
|
|
1001
1001
|
meta: {battery: {voltageToPercentage: '3V_2850_3000_log'}},
|
|
1002
1002
|
fromZigbee: [fz.xiaomi_basic, fz.ias_water_leak_alarm_1],
|
|
1003
1003
|
toZigbee: [],
|
|
1004
|
-
exposes: [e.battery(), e.water_leak(), e.battery_low(), e.battery_voltage(), e.temperature()],
|
|
1004
|
+
exposes: [e.battery(), e.water_leak(), e.battery_low(), e.battery_voltage(), e.temperature(), e.power_outage_count()],
|
|
1005
1005
|
},
|
|
1006
1006
|
{
|
|
1007
1007
|
zigbeeModel: ['lumi.flood.agl02'],
|
|
@@ -1021,7 +1021,7 @@ module.exports = [
|
|
|
1021
1021
|
description: 'Mi/Aqara smart home cube',
|
|
1022
1022
|
meta: {battery: {voltageToPercentage: '3V_2850_3000_log'}},
|
|
1023
1023
|
fromZigbee: [fz.xiaomi_basic, fz.MFKZQ01LM_action_multistate, fz.MFKZQ01LM_action_analog],
|
|
1024
|
-
exposes: [e.battery(), e.battery_voltage(), e.angle('action_angle'),
|
|
1024
|
+
exposes: [e.battery(), e.battery_voltage(), e.angle('action_angle'), e.temperature(), e.power_outage_count(),
|
|
1025
1025
|
e.cube_side('action_from_side'), e.cube_side('action_side'), e.cube_side('action_to_side'),
|
|
1026
1026
|
e.action(['shake', 'wakeup', 'fall', 'tap', 'slide', 'flip180', 'flip90', 'rotate_left', 'rotate_right'])],
|
|
1027
1027
|
toZigbee: [],
|