homebridge-tuya-plus 3.13.0 → 3.13.1-dev.1
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/CODEOWNERS +5 -0
- package/.github/workflows/publish-dev.yml +91 -0
- package/Readme.MD +33 -0
- package/config.schema.json +231 -12
- package/index.js +21 -5
- package/lib/BaseAccessory.js +7 -0
- package/lib/DehumidifierAccessory.js +1 -1
- package/lib/IrrigationSystemAccessory.js +611 -0
- package/lib/OilDiffuserAccessory.js +1 -1
- package/lib/SimpleFanLightAccessory.js +54 -12
- package/lib/SimpleGarageDoorAccessory.js +178 -262
- package/lib/TuyaAccessory.js +56 -27
- package/lib/TuyaDiscovery.js +52 -19
- package/lib/WledDimmerAccessory.js +703 -0
- package/package.json +1 -1
- package/test/IrrigationSystemAccessory.test.js +446 -0
- package/test/SimpleFanLightAccessory.test.js +299 -0
- package/test/SimpleGarageDoorAccessory.test.js +258 -538
- package/test/TuyaAccessory.protocol.test.js +528 -0
- package/test/getCategory.test.js +65 -0
- package/test/support/mocks.js +30 -2
- package/wiki/Supported-Device-Types.md +167 -22
- package/wiki/User-documented-device-config.md +57 -0
|
@@ -1,8 +1,19 @@
|
|
|
1
1
|
const BaseAccessory = require('./BaseAccessory');
|
|
2
2
|
|
|
3
|
+
// HomeKit color temperature is expressed in mireds (reciprocal megakelvin):
|
|
4
|
+
// LOW mireds = cool/blue, HIGH mireds = warm/orange.
|
|
5
|
+
// Tuya's standard `temp_value` DP runs the other way: 0 = warmest, scale = coolest.
|
|
6
|
+
// The two scales must therefore be inverted when converting between them.
|
|
7
|
+
// Defaults below describe a typical CCT lamp range of ~2700K..6500K.
|
|
8
|
+
const DEFAULT_MIN_WHITE_COLOR = 154; // ~6500K (coolest, lowest mired)
|
|
9
|
+
const DEFAULT_MAX_WHITE_COLOR = 370; // ~2700K (warmest, highest mired)
|
|
10
|
+
const COLOR_TEMP_TUYA_SCALE = 1000; // Tuya temp_value range: 0 (warm) .. 1000 (cool)
|
|
11
|
+
|
|
3
12
|
class SimpleFanLightAccessory extends BaseAccessory {
|
|
4
13
|
static getCategory(Categories) {
|
|
5
|
-
|
|
14
|
+
// HAP has no combined fan+light category; the fan is the primary
|
|
15
|
+
// service, so categorise as a FAN (matches SimpleFanAccessory).
|
|
16
|
+
return Categories.FAN;
|
|
6
17
|
}
|
|
7
18
|
|
|
8
19
|
constructor(...props) {
|
|
@@ -34,10 +45,24 @@ class SimpleFanLightAccessory extends BaseAccessory {
|
|
|
34
45
|
this.useLight = this._coerceBoolean(this.device.context.useLight, true);
|
|
35
46
|
this.useBrightness = this._coerceBoolean(this.device.context.useBrightness, true);
|
|
36
47
|
this.useColorTemp = this._coerceBoolean(this.device.context.useColorTemp, true);
|
|
48
|
+
|
|
49
|
+
let minWhiteColor = parseInt(this.device.context.minWhiteColor);
|
|
50
|
+
let maxWhiteColor = parseInt(this.device.context.maxWhiteColor);
|
|
51
|
+
if (isNaN(minWhiteColor)) minWhiteColor = DEFAULT_MIN_WHITE_COLOR;
|
|
52
|
+
if (isNaN(maxWhiteColor)) maxWhiteColor = DEFAULT_MAX_WHITE_COLOR;
|
|
53
|
+
if (maxWhiteColor <= minWhiteColor) {
|
|
54
|
+
this.log.warn(`${this.device.context.name}: maxWhiteColor (${maxWhiteColor}) must be greater than minWhiteColor (${minWhiteColor}); falling back to defaults.`);
|
|
55
|
+
minWhiteColor = DEFAULT_MIN_WHITE_COLOR;
|
|
56
|
+
maxWhiteColor = DEFAULT_MAX_WHITE_COLOR;
|
|
57
|
+
}
|
|
58
|
+
this.minWhiteColor = minWhiteColor;
|
|
59
|
+
this.maxWhiteColor = maxWhiteColor;
|
|
60
|
+
|
|
37
61
|
this.maxSpeed = parseInt(this.device.context.maxSpeed) || 6;
|
|
38
62
|
this.fanDefaultSpeed = parseInt(this.device.context.fanDefaultSpeed) || 1;
|
|
39
63
|
this.fanCurrentSpeed = 0;
|
|
40
64
|
this.useStrings = this._coerceBoolean(this.device.context.useStrings, true);
|
|
65
|
+
this.singleDpWrites = this._coerceBoolean(this.device.context.singleDpWrites, false);
|
|
41
66
|
|
|
42
67
|
const characteristicFanOn = serviceFan.getCharacteristic(Characteristic.On)
|
|
43
68
|
.updateValue(this._getFanOn(dps[this.dpFanOn]))
|
|
@@ -79,7 +104,7 @@ class SimpleFanLightAccessory extends BaseAccessory {
|
|
|
79
104
|
|
|
80
105
|
if (this.useColorTemp) {
|
|
81
106
|
characteristicColorTemp = serviceLightbulb.getCharacteristic(Characteristic.ColorTemperature)
|
|
82
|
-
.setProps({ minValue:
|
|
107
|
+
.setProps({ minValue: this.minWhiteColor, maxValue: this.maxWhiteColor })
|
|
83
108
|
.updateValue(this.convertColorTempFromTuyaToHomeKit(dps[this.dpColorTemp]))
|
|
84
109
|
.onGet(() => this.getColorTemp())
|
|
85
110
|
.onSet(value => this.setColorTemp(value));
|
|
@@ -126,6 +151,17 @@ class SimpleFanLightAccessory extends BaseAccessory {
|
|
|
126
151
|
return !!dp;
|
|
127
152
|
}
|
|
128
153
|
|
|
154
|
+
// Fan on/speed touch two DPs (60 + 62) at once. By default we send them in a
|
|
155
|
+
// single legacy control packet, which is what most fans expect. Some firmwares
|
|
156
|
+
// (e.g. certain `fsd` ceiling fans) silently ignore any LAN packet carrying more
|
|
157
|
+
// than one DP, so when `singleDpWrites` is set we send each DP as its own packet
|
|
158
|
+
// instead — mirroring how Tuya's own cloud issues these commands.
|
|
159
|
+
_writeFanState(payload) {
|
|
160
|
+
return this.singleDpWrites
|
|
161
|
+
? this.setMultiStateAsync(payload)
|
|
162
|
+
: this.setMultiStateLegacyAsync(payload);
|
|
163
|
+
}
|
|
164
|
+
|
|
129
165
|
setFanOn(value) {
|
|
130
166
|
if (value === false) {
|
|
131
167
|
this.fanCurrentSpeed = 0;
|
|
@@ -136,7 +172,7 @@ class SimpleFanLightAccessory extends BaseAccessory {
|
|
|
136
172
|
[this.dpFanOn]: true,
|
|
137
173
|
[this.dpRotationSpeed]: this.useStrings ? target.toString() : target
|
|
138
174
|
};
|
|
139
|
-
return this.
|
|
175
|
+
return this._writeFanState(payload);
|
|
140
176
|
}
|
|
141
177
|
}
|
|
142
178
|
|
|
@@ -150,7 +186,7 @@ class SimpleFanLightAccessory extends BaseAccessory {
|
|
|
150
186
|
[this.dpFanOn]: false,
|
|
151
187
|
[this.dpRotationSpeed]: this.useStrings ? this.fanDefaultSpeed.toString() : this.fanDefaultSpeed
|
|
152
188
|
};
|
|
153
|
-
return this.
|
|
189
|
+
return this._writeFanState(payload);
|
|
154
190
|
} else {
|
|
155
191
|
const tuya = this.convertRotationSpeedFromHomeKitToTuya(value);
|
|
156
192
|
this.fanCurrentSpeed = tuya;
|
|
@@ -158,7 +194,7 @@ class SimpleFanLightAccessory extends BaseAccessory {
|
|
|
158
194
|
[this.dpFanOn]: true,
|
|
159
195
|
[this.dpRotationSpeed]: this.useStrings ? tuya.toString() : tuya
|
|
160
196
|
};
|
|
161
|
-
return this.
|
|
197
|
+
return this._writeFanState(payload);
|
|
162
198
|
}
|
|
163
199
|
}
|
|
164
200
|
|
|
@@ -238,19 +274,25 @@ class SimpleFanLightAccessory extends BaseAccessory {
|
|
|
238
274
|
}
|
|
239
275
|
|
|
240
276
|
convertColorTempFromTuyaToHomeKit(value) {
|
|
277
|
+
const min = this.minWhiteColor ?? DEFAULT_MIN_WHITE_COLOR;
|
|
278
|
+
const max = this.maxWhiteColor ?? DEFAULT_MAX_WHITE_COLOR;
|
|
241
279
|
let v = parseInt(value);
|
|
242
280
|
if (isNaN(v)) v = 0;
|
|
243
|
-
v = Math.max(0, Math.min(
|
|
244
|
-
|
|
245
|
-
|
|
281
|
+
v = Math.max(0, Math.min(COLOR_TEMP_TUYA_SCALE, v));
|
|
282
|
+
// Invert: Tuya 0 (warm) -> max mireds (warm); Tuya scale (cool) -> min mireds (cool).
|
|
283
|
+
const hk = Math.round(max - (v * (max - min)) / COLOR_TEMP_TUYA_SCALE);
|
|
284
|
+
return Math.max(min, Math.min(max, hk));
|
|
246
285
|
}
|
|
247
286
|
|
|
248
287
|
convertColorTempFromHomeKitToTuya(value) {
|
|
288
|
+
const min = this.minWhiteColor ?? DEFAULT_MIN_WHITE_COLOR;
|
|
289
|
+
const max = this.maxWhiteColor ?? DEFAULT_MAX_WHITE_COLOR;
|
|
249
290
|
let v = parseInt(value);
|
|
250
|
-
if (isNaN(v)) v =
|
|
251
|
-
v = Math.max(
|
|
252
|
-
|
|
253
|
-
|
|
291
|
+
if (isNaN(v)) v = min;
|
|
292
|
+
v = Math.max(min, Math.min(max, v));
|
|
293
|
+
// Invert: max mireds (warm) -> Tuya 0 (warm); min mireds (cool) -> Tuya scale (cool).
|
|
294
|
+
const tuya = Math.round(((max - v) * COLOR_TEMP_TUYA_SCALE) / (max - min));
|
|
295
|
+
return Math.max(0, Math.min(COLOR_TEMP_TUYA_SCALE, tuya));
|
|
254
296
|
}
|
|
255
297
|
}
|
|
256
298
|
|