homebridge-tuya-plus 3.14.0-dev.3 → 3.14.0-dev.30
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/.github/workflows/publish-dev.yml +298 -31
- package/AGENTS.md +120 -0
- package/CLAUDE.md +1 -0
- package/Changelog.md +21 -0
- package/Readme.MD +8 -24
- package/config.schema.json +122 -68
- package/index.js +498 -269
- package/lib/BaseAccessory.js +52 -15
- package/lib/ConvectorAccessory.js +1 -1
- package/lib/CustomMultiOutletAccessory.js +2 -1
- package/lib/IrrigationSystemAccessory.js +98 -95
- package/lib/MultiOutletAccessory.js +2 -1
- package/lib/OilDiffuserAccessory.js +10 -8
- package/lib/RGBTWLightAccessory.js +13 -11
- package/lib/RGBTWOutletAccessory.js +11 -9
- package/lib/SimpleBlindsAccessory.js +5 -5
- package/lib/SimpleDimmer2Accessory.js +1 -1
- package/lib/SimpleDimmerAccessory.js +10 -6
- package/lib/SimpleGarageDoorAccessory.js +78 -24
- package/lib/SimpleHeaterAccessory.js +3 -3
- package/lib/SwitchAccessory.js +2 -1
- package/lib/TWLightAccessory.js +2 -2
- package/lib/TuyaAccessory.js +0 -1
- package/lib/TuyaCloudApi.js +320 -0
- package/lib/TuyaCloudDevice.js +327 -0
- package/lib/TuyaCloudMessaging.js +219 -0
- package/lib/TuyaDevice.js +266 -0
- package/lib/ValveAccessory.js +16 -12
- package/lib/VerticalBlindsWithTilt.js +5 -3
- package/lib/WledDimmerAccessory.js +46 -81
- package/package.json +5 -3
- package/scripts/cleanup-pr-tags.js +122 -0
- package/test/BaseAccessory.test.js +94 -15
- package/test/IrrigationSystemAccessory.test.js +134 -31
- package/test/MultiOutletAccessory.test.js +18 -3
- package/test/RGBTWLightAccessory.test.js +3 -3
- package/test/SimpleFanLightAccessory.test.js +3 -3
- package/test/SimpleGarageDoorAccessory.test.js +63 -9
- package/test/TuyaCloudApi.test.js +221 -0
- package/test/TuyaCloudDevice.test.js +304 -0
- package/test/TuyaCloudMessaging.test.js +113 -0
- package/test/TuyaDevice.test.js +252 -0
- package/test/getCategory.test.js +1 -0
- package/test/index.test.js +183 -0
- package/test/support/mocks.js +13 -0
- package/wiki/Supported-Device-Types.md +60 -28
- package/wiki/Tuya-Cloud-Setup.md +71 -0
package/config.schema.json
CHANGED
|
@@ -19,12 +19,65 @@
|
|
|
19
19
|
"placeholder": "Timeout (millisecond) for device IP address discovery",
|
|
20
20
|
"default": 60000
|
|
21
21
|
},
|
|
22
|
+
"cloud": {
|
|
23
|
+
"type": "object",
|
|
24
|
+
"title": "Tuya Cloud fallback (optional)",
|
|
25
|
+
"description": "<a href='https://github.com/adrianjagielak/homebridge-tuya-plus/blob/main/wiki/Tuya-Cloud-Setup.md'>Tuya Cloud API Setup</a>.",
|
|
26
|
+
"properties": {
|
|
27
|
+
"accessId": {
|
|
28
|
+
"type": "string",
|
|
29
|
+
"title": "Access ID / Client ID"
|
|
30
|
+
},
|
|
31
|
+
"accessKey": {
|
|
32
|
+
"type": "string",
|
|
33
|
+
"title": "Access Secret / Client Secret"
|
|
34
|
+
},
|
|
35
|
+
"region": {
|
|
36
|
+
"type": "string",
|
|
37
|
+
"title": "Data center / region",
|
|
38
|
+
"default": "us",
|
|
39
|
+
"description": "Must match the region your Tuya / Smart Life app account is registered in.",
|
|
40
|
+
"oneOf": [
|
|
41
|
+
{ "title": "Central Europe", "enum": ["eu"] },
|
|
42
|
+
{ "title": "Western Europe (Azure)", "enum": ["eu-w"] },
|
|
43
|
+
{ "title": "Western America", "enum": ["us"] },
|
|
44
|
+
{ "title": "Eastern America (Azure)", "enum": ["us-e"] },
|
|
45
|
+
{ "title": "China", "enum": ["cn"] },
|
|
46
|
+
{ "title": "India", "enum": ["in"] },
|
|
47
|
+
{ "title": "Singapore", "enum": ["sg"] }
|
|
48
|
+
]
|
|
49
|
+
},
|
|
50
|
+
"username": {
|
|
51
|
+
"type": "string",
|
|
52
|
+
"title": "App account email/phone (Smart Home projects only)",
|
|
53
|
+
"description": "For 'Smart Home' Cloud projects: the Tuya / Smart Life app account that owns the devices. Leave blank for 'Custom' projects (which link devices by QR scan)."
|
|
54
|
+
},
|
|
55
|
+
"password": {
|
|
56
|
+
"type": "string",
|
|
57
|
+
"title": "App account password (Smart Home projects only)"
|
|
58
|
+
},
|
|
59
|
+
"countryCode": {
|
|
60
|
+
"type": "string",
|
|
61
|
+
"title": "Country calling code (Smart Home projects only)",
|
|
62
|
+
"placeholder": "e.g. 1, 44, 48"
|
|
63
|
+
},
|
|
64
|
+
"schema": {
|
|
65
|
+
"type": "string",
|
|
66
|
+
"title": "App (Smart Home projects only)",
|
|
67
|
+
"default": "tuyaSmart",
|
|
68
|
+
"oneOf": [
|
|
69
|
+
{ "title": "Tuya Smart", "enum": ["tuyaSmart"] },
|
|
70
|
+
{ "title": "Smart Life", "enum": ["smartlife"] }
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
},
|
|
22
75
|
"devices": {
|
|
23
76
|
"type": "array",
|
|
24
77
|
"orderable": false,
|
|
25
78
|
"items": {
|
|
26
79
|
"type": "object",
|
|
27
|
-
"required": ["type", "name", "id"
|
|
80
|
+
"required": ["type", "name", "id"],
|
|
28
81
|
"properties": {
|
|
29
82
|
"type": {
|
|
30
83
|
"type": "string",
|
|
@@ -37,7 +90,7 @@
|
|
|
37
90
|
{
|
|
38
91
|
"title": "Switch Gang / Basic Switch",
|
|
39
92
|
"enum": ["Switch"]
|
|
40
|
-
},
|
|
93
|
+
},
|
|
41
94
|
{
|
|
42
95
|
"title": "Smart Plug / Barely Smart Power Strip",
|
|
43
96
|
"enum": ["Outlet"]
|
|
@@ -136,8 +189,9 @@
|
|
|
136
189
|
}
|
|
137
190
|
},
|
|
138
191
|
"key": {
|
|
139
|
-
"title": "Tuya Key",
|
|
192
|
+
"title": "Tuya Key (local key — required for LAN devices)",
|
|
140
193
|
"type": "string",
|
|
194
|
+
"description": "The device's local key. Required for normal (LAN) devices. A device with no key is treated as cloud-only and is reached through the Tuya Cloud session configured above (e.g. a battery-powered 'sleepy' irrigation timer).",
|
|
141
195
|
"condition": {
|
|
142
196
|
"functionBody": "return model.devices && model.devices[arrayIndices].type !== 'null';"
|
|
143
197
|
}
|
|
@@ -249,9 +303,9 @@
|
|
|
249
303
|
"title": "Sync brightness to WLED (IP[:port])",
|
|
250
304
|
"placeholder": "192.168.1.100",
|
|
251
305
|
"condition": {
|
|
252
|
-
"functionBody": "return model.devices && model.devices[arrayIndices] && ['WledDimmer'
|
|
306
|
+
"functionBody": "return model.devices && model.devices[arrayIndices] && ['WledDimmer'].includes(model.devices[arrayIndices].type);"
|
|
253
307
|
}
|
|
254
|
-
},
|
|
308
|
+
},
|
|
255
309
|
"dpColorTemperature": {
|
|
256
310
|
"type": "integer",
|
|
257
311
|
"placeholder": "3",
|
|
@@ -352,6 +406,12 @@
|
|
|
352
406
|
"functionBody": "return model.devices && model.devices[arrayIndices] && ['AirConditioner'].includes(model.devices[arrayIndices].type);"
|
|
353
407
|
}
|
|
354
408
|
},
|
|
409
|
+
"noAuto": {
|
|
410
|
+
"type": "boolean",
|
|
411
|
+
"condition": {
|
|
412
|
+
"functionBody": "return model.devices && model.devices[arrayIndices] && ['AirConditioner'].includes(model.devices[arrayIndices].type);"
|
|
413
|
+
}
|
|
414
|
+
},
|
|
355
415
|
"cmdCool": {
|
|
356
416
|
"type": "string",
|
|
357
417
|
"placeholder": "COOL",
|
|
@@ -366,6 +426,32 @@
|
|
|
366
426
|
"functionBody": "return model.devices && model.devices[arrayIndices] && ['AirConditioner'].includes(model.devices[arrayIndices].type);"
|
|
367
427
|
}
|
|
368
428
|
},
|
|
429
|
+
"cmdAuto": {
|
|
430
|
+
"type": "string",
|
|
431
|
+
"placeholder": "AUTO",
|
|
432
|
+
"condition": {
|
|
433
|
+
"functionBody": "return model.devices && model.devices[arrayIndices] && ['AirConditioner'].includes(model.devices[arrayIndices].type);"
|
|
434
|
+
}
|
|
435
|
+
},
|
|
436
|
+
"noRotationSpeed": {
|
|
437
|
+
"type": "boolean",
|
|
438
|
+
"condition": {
|
|
439
|
+
"functionBody": "return model.devices && model.devices[arrayIndices] && ['AirConditioner'].includes(model.devices[arrayIndices].type);"
|
|
440
|
+
}
|
|
441
|
+
},
|
|
442
|
+
"fanSpeedSteps": {
|
|
443
|
+
"type": "integer",
|
|
444
|
+
"placeholder": "3",
|
|
445
|
+
"condition": {
|
|
446
|
+
"functionBody": "return model.devices && model.devices[arrayIndices] && ['AirConditioner'].includes(model.devices[arrayIndices].type);"
|
|
447
|
+
}
|
|
448
|
+
},
|
|
449
|
+
"noChildLock": {
|
|
450
|
+
"type": "boolean",
|
|
451
|
+
"condition": {
|
|
452
|
+
"functionBody": "return model.devices && model.devices[arrayIndices] && ['AirConditioner', 'Convector'].includes(model.devices[arrayIndices].type);"
|
|
453
|
+
}
|
|
454
|
+
},
|
|
369
455
|
"noSwing": {
|
|
370
456
|
"type": "boolean",
|
|
371
457
|
"condition": {
|
|
@@ -500,30 +586,31 @@
|
|
|
500
586
|
"functionBody": "return model.devices && model.devices[arrayIndices] && ['Convector'].includes(model.devices[arrayIndices].type);"
|
|
501
587
|
}
|
|
502
588
|
},
|
|
503
|
-
"
|
|
589
|
+
"noTemperatureUnit": {
|
|
504
590
|
"type": "boolean",
|
|
505
591
|
"condition": {
|
|
506
592
|
"functionBody": "return model.devices && model.devices[arrayIndices] && ['Convector'].includes(model.devices[arrayIndices].type);"
|
|
507
593
|
}
|
|
508
594
|
},
|
|
509
|
-
"
|
|
510
|
-
"type": "
|
|
595
|
+
"temperatureDivisor": {
|
|
596
|
+
"type": "integer",
|
|
597
|
+
"placeholder": "1",
|
|
511
598
|
"condition": {
|
|
512
|
-
"functionBody": "return model.devices && model.devices[arrayIndices] && ['
|
|
599
|
+
"functionBody": "return model.devices && model.devices[arrayIndices] && ['SimpleHeater', 'AirConditioner'].includes(model.devices[arrayIndices].type);"
|
|
513
600
|
}
|
|
514
601
|
},
|
|
515
|
-
"
|
|
602
|
+
"currentTemperatureDivisor": {
|
|
516
603
|
"type": "integer",
|
|
517
604
|
"placeholder": "1",
|
|
518
605
|
"condition": {
|
|
519
|
-
"functionBody": "return model.devices && model.devices[arrayIndices] && ['
|
|
606
|
+
"functionBody": "return model.devices && model.devices[arrayIndices] && ['AirConditioner'].includes(model.devices[arrayIndices].type);"
|
|
520
607
|
}
|
|
521
608
|
},
|
|
522
609
|
"thresholdTemperatureDivisor": {
|
|
523
610
|
"type": "integer",
|
|
524
611
|
"placeholder": "1",
|
|
525
612
|
"condition": {
|
|
526
|
-
"functionBody": "return model.devices && model.devices[arrayIndices] && ['SimpleHeater'].includes(model.devices[arrayIndices].type);"
|
|
613
|
+
"functionBody": "return model.devices && model.devices[arrayIndices] && ['SimpleHeater', 'AirConditioner'].includes(model.devices[arrayIndices].type);"
|
|
527
614
|
}
|
|
528
615
|
},
|
|
529
616
|
"temperatureOffset": {
|
|
@@ -648,7 +735,7 @@
|
|
|
648
735
|
"type": "integer",
|
|
649
736
|
"title": "Number of Valves / Zones",
|
|
650
737
|
"placeholder": 4,
|
|
651
|
-
"description": "How many valves/zones the controller has. They are assumed to be on data-points 1, 2, 3, … For non-sequential data-points use the 'valves' list instead.",
|
|
738
|
+
"description": "How many valves/zones the controller has. They are assumed to be on data-points 1, 2, 3, … (over the cloud these map to the codes switch_1, switch_2, … automatically). For non-sequential data-points use the 'valves' list instead.",
|
|
652
739
|
"condition": {
|
|
653
740
|
"functionBody": "return model.devices && model.devices[arrayIndices] && ['IrrigationSystem'].includes(model.devices[arrayIndices].type);"
|
|
654
741
|
}
|
|
@@ -661,9 +748,21 @@
|
|
|
661
748
|
"items": {
|
|
662
749
|
"type": "object",
|
|
663
750
|
"properties": {
|
|
664
|
-
"name": {
|
|
665
|
-
|
|
666
|
-
|
|
751
|
+
"name": {
|
|
752
|
+
"type": "string",
|
|
753
|
+
"title": "Zone name",
|
|
754
|
+
"placeholder": "Lawn"
|
|
755
|
+
},
|
|
756
|
+
"dp": {
|
|
757
|
+
"type": ["integer", "string"],
|
|
758
|
+
"title": "Data-point (numeric id, or cloud code)",
|
|
759
|
+
"placeholder": "1 or switch_1"
|
|
760
|
+
},
|
|
761
|
+
"defaultDuration": {
|
|
762
|
+
"type": "integer",
|
|
763
|
+
"title": "Default run time (seconds, 0 = run indefinitely)",
|
|
764
|
+
"placeholder": 600
|
|
765
|
+
}
|
|
667
766
|
}
|
|
668
767
|
},
|
|
669
768
|
"condition": {
|
|
@@ -724,9 +823,9 @@
|
|
|
724
823
|
}
|
|
725
824
|
},
|
|
726
825
|
"dpBattery": {
|
|
727
|
-
"type": "integer",
|
|
728
|
-
"placeholder": 46,
|
|
729
|
-
"title": "Battery data-point",
|
|
826
|
+
"type": ["integer", "string"],
|
|
827
|
+
"placeholder": "46 or battery_percentage",
|
|
828
|
+
"title": "Battery data-point (numeric id, or cloud code)",
|
|
730
829
|
"condition": {
|
|
731
830
|
"functionBody": "return model.devices && model.devices[arrayIndices] && ['IrrigationSystem'].includes(model.devices[arrayIndices].type) && !model.devices[arrayIndices].noBattery;"
|
|
732
831
|
}
|
|
@@ -741,62 +840,17 @@
|
|
|
741
840
|
}
|
|
742
841
|
},
|
|
743
842
|
"dpCharging": {
|
|
744
|
-
"type": "integer",
|
|
745
|
-
"placeholder": 101,
|
|
746
|
-
"title": "Charging-status data-point",
|
|
843
|
+
"type": ["integer", "string"],
|
|
844
|
+
"placeholder": "101 or charge_state",
|
|
845
|
+
"title": "Charging-status data-point (numeric id, or cloud code)",
|
|
747
846
|
"description": "Boolean data-point reporting whether the battery is charging (e.g. solar / USB-C units). HomeKit shows Charging / Not Charging; if your controller doesn't report this, it shows Not Chargeable.",
|
|
748
847
|
"condition": {
|
|
749
848
|
"functionBody": "return model.devices && model.devices[arrayIndices] && ['IrrigationSystem'].includes(model.devices[arrayIndices].type) && !model.devices[arrayIndices].noBattery;"
|
|
750
849
|
}
|
|
751
|
-
},
|
|
752
|
-
"noRainSensor": {
|
|
753
|
-
"type": "boolean",
|
|
754
|
-
"title": "No rain sensor",
|
|
755
|
-
"description": "Disable the rain sensor (use if your controller doesn't report 'rain_sensor_state').",
|
|
756
|
-
"condition": {
|
|
757
|
-
"functionBody": "return model.devices && model.devices[arrayIndices] && ['IrrigationSystem'].includes(model.devices[arrayIndices].type);"
|
|
758
|
-
}
|
|
759
|
-
},
|
|
760
|
-
"rainSensorType": {
|
|
761
|
-
"type": "string",
|
|
762
|
-
"title": "Rain sensor type",
|
|
763
|
-
"default": "contact",
|
|
764
|
-
"oneOf": [
|
|
765
|
-
{ "title": "Contact sensor (Open / Closed)", "enum": ["contact"] },
|
|
766
|
-
{ "title": "Leak sensor (Leak Detected / Dry — note: fires critical alerts)", "enum": ["leak"] }
|
|
767
|
-
],
|
|
768
|
-
"condition": {
|
|
769
|
-
"functionBody": "return model.devices && model.devices[arrayIndices] && ['IrrigationSystem'].includes(model.devices[arrayIndices].type) && !model.devices[arrayIndices].noRainSensor;"
|
|
770
|
-
}
|
|
771
|
-
},
|
|
772
|
-
"rainInverted": {
|
|
773
|
-
"type": "boolean",
|
|
774
|
-
"title": "Invert rain sensor",
|
|
775
|
-
"description": "Flip the reported state if 'raining' and 'dry' appear reversed in HomeKit.",
|
|
776
|
-
"condition": {
|
|
777
|
-
"functionBody": "return model.devices && model.devices[arrayIndices] && ['IrrigationSystem'].includes(model.devices[arrayIndices].type) && !model.devices[arrayIndices].noRainSensor;"
|
|
778
|
-
}
|
|
779
|
-
},
|
|
780
|
-
"dpRain": {
|
|
781
|
-
"type": "integer",
|
|
782
|
-
"placeholder": 49,
|
|
783
|
-
"title": "Rain sensor data-point",
|
|
784
|
-
"condition": {
|
|
785
|
-
"functionBody": "return model.devices && model.devices[arrayIndices] && ['IrrigationSystem'].includes(model.devices[arrayIndices].type) && !model.devices[arrayIndices].noRainSensor;"
|
|
786
|
-
}
|
|
787
|
-
},
|
|
788
|
-
"rainOnValue": {
|
|
789
|
-
"type": "string",
|
|
790
|
-
"placeholder": "rain",
|
|
791
|
-
"title": "Rain sensor 'raining' value",
|
|
792
|
-
"description": "The enum value the device reports when it is raining (default 'rain').",
|
|
793
|
-
"condition": {
|
|
794
|
-
"functionBody": "return model.devices && model.devices[arrayIndices] && ['IrrigationSystem'].includes(model.devices[arrayIndices].type) && !model.devices[arrayIndices].noRainSensor;"
|
|
795
|
-
}
|
|
796
850
|
}
|
|
797
851
|
}
|
|
798
852
|
}
|
|
799
853
|
}
|
|
800
854
|
}
|
|
801
855
|
}
|
|
802
|
-
}
|
|
856
|
+
}
|