homebridge-tuya-community 3.3.0
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/.eslintrc.js +29 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
- package/.github/ISSUE_TEMPLATE/new-device.md +24 -0
- package/.github/workflows/codeql-analysis.yml +67 -0
- package/.github/workflows/eslint.yml +28 -0
- package/Changelog.md +87 -0
- package/LICENSE +21 -0
- package/Readme.MD +99 -0
- package/assets/Tuya-Plugin-Branding.png +0 -0
- package/bin/cli-decode.js +197 -0
- package/bin/cli-find.js +207 -0
- package/bin/cli.js +13 -0
- package/config-example.MD +43 -0
- package/config.schema.json +554 -0
- package/index.js +288 -0
- package/lib/AirConditionerAccessory.js +445 -0
- package/lib/AirPurifierAccessory.js +531 -0
- package/lib/BaseAccessory.js +292 -0
- package/lib/ConvectorAccessory.js +313 -0
- package/lib/CustomMultiLightAccessory.js +70 -0
- package/lib/CustomMultiOutletAccessory.js +111 -0
- package/lib/DehumidifierAccessory.js +301 -0
- package/lib/EnergyCharacteristics.js +86 -0
- package/lib/GarageDoorAccessory.js +307 -0
- package/lib/MultiLightAccessory.js +64 -0
- package/lib/MultiOutletAccessory.js +106 -0
- package/lib/OilDiffuserAccessory.js +480 -0
- package/lib/OutletAccessory.js +83 -0
- package/lib/RGBTWLightAccessory.js +234 -0
- package/lib/RGBTWOutletAccessory.js +296 -0
- package/lib/SimpleBlindsAccessory.js +298 -0
- package/lib/SimpleDimmer2Accessory.js +54 -0
- package/lib/SimpleDimmerAccessory.js +54 -0
- package/lib/SimpleFanAccessory.js +132 -0
- package/lib/SimpleFanLightAccessory.js +205 -0
- package/lib/SimpleHeaterAccessory.js +154 -0
- package/lib/SimpleLightAccessory.js +39 -0
- package/lib/SingleLightAccessory.js +45 -0
- package/lib/SwitchAccessory.js +106 -0
- package/lib/TWLightAccessory.js +91 -0
- package/lib/TuyaAccessory.js +744 -0
- package/lib/TuyaDiscovery.js +278 -0
- package/lib/ValveAccessory.js +150 -0
- package/package.json +49 -0
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
const BaseAccessory = require('./BaseAccessory');
|
|
2
|
+
|
|
3
|
+
const BLINDS_OPENING = 'opening';
|
|
4
|
+
const BLINDS_CLOSING = 'closing';
|
|
5
|
+
const BLINDS_STOPPED = 'stopped';
|
|
6
|
+
|
|
7
|
+
const BLINDS_OPEN = 100;
|
|
8
|
+
const BLINDS_CLOSED = 0;
|
|
9
|
+
|
|
10
|
+
class SimpleBlindsAccessory extends BaseAccessory {
|
|
11
|
+
static getCategory(Categories) {
|
|
12
|
+
return Categories.WINDOW_COVERING;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
constructor(...props) {
|
|
16
|
+
super(...props);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
_registerPlatformAccessory() {
|
|
20
|
+
const {Service} = this.hap;
|
|
21
|
+
|
|
22
|
+
this.accessory.addService(Service.WindowCovering, this.device.context.name);
|
|
23
|
+
|
|
24
|
+
super._registerPlatformAccessory();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
_registerCharacteristics(dps) {
|
|
28
|
+
const {Service, Characteristic} = this.hap;
|
|
29
|
+
const service = this.accessory.getService(Service.WindowCovering);
|
|
30
|
+
this._checkServiceName(service, this.device.context.name);
|
|
31
|
+
this.dpBlindType = parseInt(this.device.context.dpBlindType) || 1;
|
|
32
|
+
this.dpAction = this._getCustomDP(this.device.context.dpAction) || '1';
|
|
33
|
+
|
|
34
|
+
//setting the defaults if no one declares anything.
|
|
35
|
+
this.OPEN_COMMAND = '';
|
|
36
|
+
this.CLOSE_COMMAND = '';
|
|
37
|
+
this.STOP_COMMAND = '';
|
|
38
|
+
|
|
39
|
+
if(this.dpBlindType === 1)
|
|
40
|
+
{
|
|
41
|
+
this.log.debug('Blinds Type 1 Selected');
|
|
42
|
+
this.OPEN_COMMAND = 'open';
|
|
43
|
+
this.CLOSE_COMMAND = 'close';
|
|
44
|
+
this.STOP_COMMAND = 'stop';
|
|
45
|
+
} else if (this.dpBlindType === 2)
|
|
46
|
+
{
|
|
47
|
+
this.log.debug('Blinds Type 2 Detected');
|
|
48
|
+
this.OPEN_COMMAND = 'on';
|
|
49
|
+
this.CLOSE_COMMAND = 'off';
|
|
50
|
+
this.STOP_COMMAND = 'stop';
|
|
51
|
+
|
|
52
|
+
} else if (this.dpBlindType === 3) {
|
|
53
|
+
this.log.debug('Blinds Type 3 Selected');
|
|
54
|
+
//this one here should fix https://github.com/iRayanKhan/homebridge-tuya/issues/444 when the type is set to 0
|
|
55
|
+
this.OPEN_COMMAND = '1';
|
|
56
|
+
this.CLOSE_COMMAND = '2';
|
|
57
|
+
this.STOP_COMMAND = '3';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let _cmdOpen = this.OPEN_COMMAND;
|
|
61
|
+
if (this.device.context.cmdOpen) {
|
|
62
|
+
_cmdOpen = ('' + this.device.context.cmdOpen).trim();
|
|
63
|
+
}
|
|
64
|
+
let _cmdClose = this.CLOSE_COMMAND;
|
|
65
|
+
|
|
66
|
+
if (this.device.context.cmdClose) {
|
|
67
|
+
_cmdClose = ('' + this.device.context.cmdClose).trim();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
let _cmdStop = this.STOP_COMMAND;
|
|
71
|
+
if (this.device.context.cmdStop) {
|
|
72
|
+
_cmdStop = ('' + this.device.context.cmdStop).trim();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
this.cmdStop = _cmdStop;
|
|
76
|
+
this.cmdOpen = _cmdOpen;
|
|
77
|
+
this.cmdClose = _cmdClose;
|
|
78
|
+
if (this.device.context.flipState) {
|
|
79
|
+
this.cmdOpen = _cmdClose;
|
|
80
|
+
this.cmdClose = _cmdOpen;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
this.duration = parseInt(this.device.context.timeToOpen) || 45;
|
|
84
|
+
const endingDuration = parseInt(this.device.context.timeToTighten) || 0;
|
|
85
|
+
this.minPosition = endingDuration ? Math.round(endingDuration * -100 / (this.duration - endingDuration)) : BLINDS_CLOSED;
|
|
86
|
+
|
|
87
|
+
// If the blinds are closed, note it; if not, assume open because there is no way to know where it is
|
|
88
|
+
this.assumedPosition = dps[this.dpAction] === this.cmdClose ? this.minPosition : BLINDS_OPEN;
|
|
89
|
+
this.assumedState = BLINDS_STOPPED;
|
|
90
|
+
this.changeTime = this.targetPosition = false;
|
|
91
|
+
|
|
92
|
+
const characteristicCurrentPosition = service.getCharacteristic(Characteristic.CurrentPosition)
|
|
93
|
+
.updateValue(this._getCurrentPosition(dps[this.dpAction]))
|
|
94
|
+
.on('get', this.getCurrentPosition.bind(this));
|
|
95
|
+
|
|
96
|
+
const characteristicTargetPosition = service.getCharacteristic(Characteristic.TargetPosition)
|
|
97
|
+
.updateValue(this._getTargetPosition(dps[this.dpAction]))
|
|
98
|
+
.on('get', this.getTargetPosition.bind(this))
|
|
99
|
+
.on('set', this.setTargetPosition.bind(this));
|
|
100
|
+
|
|
101
|
+
const characteristicPositionState = service.getCharacteristic(Characteristic.PositionState)
|
|
102
|
+
.updateValue(this._getPositionState())
|
|
103
|
+
.on('get', this.getPositionState.bind(this));
|
|
104
|
+
|
|
105
|
+
this.device.on('change', changes => {
|
|
106
|
+
this.log.debug('[TuyaAccessory] Blinds saw change to ' + changes[this.dpAction]);
|
|
107
|
+
if (changes.hasOwnProperty(this.dpAction)) {
|
|
108
|
+
switch (changes[this.dpAction]) {
|
|
109
|
+
case this.cmdOpen: // Starting to open
|
|
110
|
+
this.assumedState = BLINDS_OPENING;
|
|
111
|
+
characteristicPositionState.updateValue(Characteristic.PositionState.INCREASING);
|
|
112
|
+
|
|
113
|
+
// Only if change was external or someone internally asked for open
|
|
114
|
+
if (this.targetPosition === false || this.targetPosition === BLINDS_OPEN) {
|
|
115
|
+
this.targetPosition = false;
|
|
116
|
+
|
|
117
|
+
const durationToOpen = Math.abs(this.assumedPosition - BLINDS_OPEN) * this.duration * 10;
|
|
118
|
+
this.changeTime = Date.now() - durationToOpen;
|
|
119
|
+
|
|
120
|
+
this.log.debug('[TuyaAccessory] Blinds will be marked open in ' + durationToOpen + 'ms');
|
|
121
|
+
|
|
122
|
+
if (this.changeTimeout) clearTimeout(this.changeTimeout);
|
|
123
|
+
this.changeTimeout = setTimeout(() => {
|
|
124
|
+
characteristicCurrentPosition.updateValue(BLINDS_OPEN);
|
|
125
|
+
characteristicPositionState.updateValue(Characteristic.PositionState.STOPPED);
|
|
126
|
+
this.changeTime = false;
|
|
127
|
+
this.assumedPosition = BLINDS_OPEN;
|
|
128
|
+
this.assumedState = BLINDS_STOPPED;
|
|
129
|
+
this.log.debug('[TuyaAccessory] Blinds marked open');
|
|
130
|
+
}, durationToOpen);
|
|
131
|
+
}
|
|
132
|
+
break;
|
|
133
|
+
|
|
134
|
+
case this.cmdClose: // Starting to close
|
|
135
|
+
this.assumedState = BLINDS_CLOSING;
|
|
136
|
+
characteristicPositionState.updateValue(Characteristic.PositionState.DECREASING);
|
|
137
|
+
|
|
138
|
+
// Only if change was external or someone internally asked for close
|
|
139
|
+
if (this.targetPosition === false || this.targetPosition === BLINDS_CLOSED) {
|
|
140
|
+
this.targetPosition = false;
|
|
141
|
+
|
|
142
|
+
const durationToClose = Math.abs(this.assumedPosition - BLINDS_CLOSED) * this.duration * 10;
|
|
143
|
+
this.changeTime = Date.now() - durationToClose;
|
|
144
|
+
|
|
145
|
+
this.log.debug('[TuyaAccessory] Blinds will be marked closed in ' + durationToClose + 'ms');
|
|
146
|
+
|
|
147
|
+
if (this.changeTimeout) clearTimeout(this.changeTimeout);
|
|
148
|
+
this.changeTimeout = setTimeout(() => {
|
|
149
|
+
characteristicCurrentPosition.updateValue(BLINDS_CLOSED);
|
|
150
|
+
characteristicPositionState.updateValue(Characteristic.PositionState.STOPPED);
|
|
151
|
+
this.changeTime = false;
|
|
152
|
+
this.assumedPosition = this.minPosition;
|
|
153
|
+
this.assumedState = BLINDS_STOPPED;
|
|
154
|
+
this.log.debug('[TuyaAccessory] Blinds marked closed');
|
|
155
|
+
}, durationToClose);
|
|
156
|
+
}
|
|
157
|
+
break;
|
|
158
|
+
|
|
159
|
+
case this.cmdStop: // Stopped in middle
|
|
160
|
+
if (this.changeTimeout) clearTimeout(this.changeTimeout);
|
|
161
|
+
|
|
162
|
+
this.log.debug('[TuyaAccessory] Blinds last change was ' + this.changeTime + '; ' + (Date.now() - this.changeTime) + 'ms ago');
|
|
163
|
+
|
|
164
|
+
if (this.changeTime) {
|
|
165
|
+
/*
|
|
166
|
+
this.assumedPosition = Math.min(100 - this.minPosition, Math.max(0, Math.round((Date.now() - this.changeTime) / (10 * this.duration))));
|
|
167
|
+
if (this.assumedState === BLINDS_CLOSING) this.assumedPosition = 100 - this.assumedPosition;
|
|
168
|
+
else this.assumedPosition += this.minPosition;
|
|
169
|
+
*/
|
|
170
|
+
const disposition = ((Date.now() - this.changeTime) / (10 * this.duration));
|
|
171
|
+
if (this.assumedState === BLINDS_CLOSING) {
|
|
172
|
+
this.assumedPosition = BLINDS_OPEN - disposition;
|
|
173
|
+
} else {
|
|
174
|
+
this.assumedPosition = this.minPosition + disposition;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const adjustedPosition = Math.max(0, Math.round(this.assumedPosition));
|
|
179
|
+
characteristicCurrentPosition.updateValue(adjustedPosition);
|
|
180
|
+
characteristicTargetPosition.updateValue(adjustedPosition);
|
|
181
|
+
characteristicPositionState.updateValue(Characteristic.PositionState.STOPPED);
|
|
182
|
+
this.log.debug('[TuyaAccessory] Blinds marked stopped at ' + adjustedPosition + '; assumed to be at ' + this.assumedPosition);
|
|
183
|
+
|
|
184
|
+
this.changeTime = this.targetPosition = false;
|
|
185
|
+
this.assumedState = BLINDS_STOPPED;
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
getCurrentPosition(callback) {
|
|
193
|
+
this.getState(this.dpAction, (err, dp) => {
|
|
194
|
+
if (err) return callback(err);
|
|
195
|
+
|
|
196
|
+
callback(null, this._getCurrentPosition(dp));
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
_getCurrentPosition(dp) {
|
|
201
|
+
switch (dp) {
|
|
202
|
+
case this.cmdOpen:
|
|
203
|
+
return BLINDS_OPEN;
|
|
204
|
+
|
|
205
|
+
case this.cmdClose:
|
|
206
|
+
return BLINDS_CLOSED;
|
|
207
|
+
|
|
208
|
+
default:
|
|
209
|
+
return Math.max(BLINDS_CLOSED, Math.round(this.assumedPosition));
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
getTargetPosition(callback) {
|
|
214
|
+
this.getState(this.dpAction, (err, dp) => {
|
|
215
|
+
if (err) return callback(err);
|
|
216
|
+
|
|
217
|
+
callback(null, this._getTargetPosition(dp));
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
_getTargetPosition(dp) {
|
|
222
|
+
switch (dp) {
|
|
223
|
+
case this.cmdOpen:
|
|
224
|
+
return BLINDS_OPEN;
|
|
225
|
+
|
|
226
|
+
case this.cmdClose:
|
|
227
|
+
return BLINDS_CLOSED;
|
|
228
|
+
|
|
229
|
+
default:
|
|
230
|
+
return Math.max(BLINDS_CLOSED, Math.round(this.assumedPosition));
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
setTargetPosition(value, callback) {
|
|
235
|
+
this.log.debug('[TuyaAccessory] Blinds asked to move from ' + this.assumedPosition + ' to ' + value);
|
|
236
|
+
|
|
237
|
+
if (this.changeTimeout) clearTimeout(this.changeTimeout);
|
|
238
|
+
this.targetPosition = value;
|
|
239
|
+
|
|
240
|
+
if (this.changeTime !== false) {
|
|
241
|
+
this.log.debug('[TuyaAccessory] Blinds ' + (this.assumedState === BLINDS_CLOSING ? 'closing' : 'opening') + ' had started ' + this.changeTime + '; ' + (Date.now() - this.changeTime) + 'ms ago');
|
|
242
|
+
const disposition = ((Date.now() - this.changeTime) / (10 * this.duration));
|
|
243
|
+
if (this.assumedState === BLINDS_CLOSING) {
|
|
244
|
+
this.assumedPosition = BLINDS_OPEN - disposition;
|
|
245
|
+
} else {
|
|
246
|
+
this.assumedPosition = this.minPosition + disposition;
|
|
247
|
+
}
|
|
248
|
+
this.log.debug("[TuyaAccessory] Blinds' adjusted assumedPosition is " + this.assumedPosition);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const duration = Math.abs(this.assumedPosition - value) * this.duration * 10;
|
|
252
|
+
|
|
253
|
+
if (Math.abs(value - this.assumedPosition) < 1) {
|
|
254
|
+
return this.setState(this.dpAction, this.cmdStop, callback);
|
|
255
|
+
} else if (value > this.assumedPosition) {
|
|
256
|
+
this.assumedState = BLINDS_OPENING;
|
|
257
|
+
this.setState(this.dpAction, this.cmdOpen, callback);
|
|
258
|
+
this.changeTime = Date.now() - Math.abs(this.assumedPosition - this.minPosition) * this.duration * 10;
|
|
259
|
+
} else {
|
|
260
|
+
this.assumedState = BLINDS_CLOSING;
|
|
261
|
+
this.setState(this.dpAction, this.cmdClose, callback);
|
|
262
|
+
this.changeTime = Date.now() - Math.abs(this.assumedPosition - BLINDS_OPEN) * this.duration * 10;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (value !== BLINDS_OPEN && value !== BLINDS_CLOSED) {
|
|
266
|
+
this.log.debug('[TuyaAccessory] Blinds will stop in ' + duration + 'ms');
|
|
267
|
+
this.log.debug('[TuyaAccessory] Blinds assumed started ' + this.changeTime + '; ' + (Date.now() - this.changeTime) + 'ms ago');
|
|
268
|
+
this.changeTimeout = setTimeout(() => {
|
|
269
|
+
this.log.debug('[TuyaAccessory] Blinds asked to stop');
|
|
270
|
+
this.setState(this.dpAction, this.cmdStop);
|
|
271
|
+
}, duration);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
getPositionState(callback) {
|
|
276
|
+
const state = this._getPositionState();
|
|
277
|
+
process.nextTick(() => {
|
|
278
|
+
callback(null, state);
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
_getPositionState() {
|
|
283
|
+
const {Characteristic} = this.hap;
|
|
284
|
+
|
|
285
|
+
switch (this.assumedState) {
|
|
286
|
+
case BLINDS_OPENING:
|
|
287
|
+
return Characteristic.PositionState.INCREASING;
|
|
288
|
+
|
|
289
|
+
case BLINDS_CLOSING:
|
|
290
|
+
return Characteristic.PositionState.DECREASING;
|
|
291
|
+
|
|
292
|
+
default:
|
|
293
|
+
return Characteristic.PositionState.STOPPED;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
module.exports = SimpleBlindsAccessory;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const BaseAccessory = require('./BaseAccessory');
|
|
2
|
+
|
|
3
|
+
class SimpleDimmer2Accessory extends BaseAccessory {
|
|
4
|
+
static getCategory(Categories) {
|
|
5
|
+
return Categories.LIGHTBULB;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
constructor(...props) {
|
|
9
|
+
super(...props);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
_registerPlatformAccessory() {
|
|
13
|
+
const {Service} = this.hap;
|
|
14
|
+
|
|
15
|
+
this.accessory.addService(Service.Lightbulb, this.device.context.name);
|
|
16
|
+
|
|
17
|
+
super._registerPlatformAccessory();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
_registerCharacteristics(dps) {
|
|
21
|
+
const {Service, Characteristic} = this.hap;
|
|
22
|
+
const service = this.accessory.getService(Service.Lightbulb);
|
|
23
|
+
this._checkServiceName(service, this.device.context.name);
|
|
24
|
+
|
|
25
|
+
this.dpPower = this._getCustomDP(this.device.context.dpPower) || '1';
|
|
26
|
+
this.dpBrightness = '3'; //this._getCustomDP(this.device.context.dpBrightness) || this._getCustomDP(this.device.context.dp) || '2';
|
|
27
|
+
|
|
28
|
+
const characteristicOn = service.getCharacteristic(Characteristic.On)
|
|
29
|
+
.updateValue(dps[this.dpPower])
|
|
30
|
+
.on('get', this.getState.bind(this, this.dpPower))
|
|
31
|
+
.on('set', this.setState.bind(this, this.dpPower));
|
|
32
|
+
|
|
33
|
+
const characteristicBrightness = service.getCharacteristic(Characteristic.Brightness)
|
|
34
|
+
.updateValue(this.convertBrightnessFromTuyaToHomeKit(dps[this.dpBrightness]))
|
|
35
|
+
.on('get', this.getBrightness.bind(this))
|
|
36
|
+
.on('set', this.setBrightness.bind(this));
|
|
37
|
+
|
|
38
|
+
this.device.on('change', changes => {
|
|
39
|
+
if (changes.hasOwnProperty(this.dpPower) && characteristicOn.value !== changes[this.dpPower]) characteristicOn.updateValue(changes[this.dpPower]);
|
|
40
|
+
if (changes.hasOwnProperty(this.dpBrightness) && this.convertBrightnessFromHomeKitToTuya(characteristicBrightness.value) !== changes[this.dpBrightness])
|
|
41
|
+
characteristicBrightness.updateValue(this.convertBrightnessFromTuyaToHomeKit(changes[this.dpBrightness]));
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
getBrightness(callback) {
|
|
46
|
+
callback(null, this.convertBrightnessFromTuyaToHomeKit(this.device.state[this.dpBrightness]));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
setBrightness(value, callback) {
|
|
50
|
+
this.setState(this.dpBrightness, this.convertBrightnessFromHomeKitToTuya(value), callback);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
module.exports = SimpleDimmer2Accessory;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const BaseAccessory = require('./BaseAccessory');
|
|
2
|
+
|
|
3
|
+
class SimpleDimmerAccessory extends BaseAccessory {
|
|
4
|
+
static getCategory(Categories) {
|
|
5
|
+
return Categories.LIGHTBULB;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
constructor(...props) {
|
|
9
|
+
super(...props);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
_registerPlatformAccessory() {
|
|
13
|
+
const {Service} = this.hap;
|
|
14
|
+
|
|
15
|
+
this.accessory.addService(Service.Lightbulb, this.device.context.name);
|
|
16
|
+
|
|
17
|
+
super._registerPlatformAccessory();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
_registerCharacteristics(dps) {
|
|
21
|
+
const {Service, Characteristic} = this.hap;
|
|
22
|
+
const service = this.accessory.getService(Service.Lightbulb);
|
|
23
|
+
this._checkServiceName(service, this.device.context.name);
|
|
24
|
+
|
|
25
|
+
this.dpPower = this._getCustomDP(this.device.context.dpPower) || '1';
|
|
26
|
+
this.dpBrightness = this._getCustomDP(this.device.context.dpBrightness) || this._getCustomDP(this.device.context.dp) || '2';
|
|
27
|
+
|
|
28
|
+
const characteristicOn = service.getCharacteristic(Characteristic.On)
|
|
29
|
+
.updateValue(dps[this.dpPower])
|
|
30
|
+
.on('get', this.getState.bind(this, this.dpPower))
|
|
31
|
+
.on('set', this.setState.bind(this, this.dpPower));
|
|
32
|
+
|
|
33
|
+
const characteristicBrightness = service.getCharacteristic(Characteristic.Brightness)
|
|
34
|
+
.updateValue(this.convertBrightnessFromTuyaToHomeKit(dps[this.dpBrightness]))
|
|
35
|
+
.on('get', this.getBrightness.bind(this))
|
|
36
|
+
.on('set', this.setBrightness.bind(this));
|
|
37
|
+
|
|
38
|
+
this.device.on('change', changes => {
|
|
39
|
+
if (changes.hasOwnProperty(this.dpPower) && characteristicOn.value !== changes[this.dpPower]) characteristicOn.updateValue(changes[this.dpPower]);
|
|
40
|
+
if (changes.hasOwnProperty(this.dpBrightness) && this.convertBrightnessFromHomeKitToTuya(characteristicBrightness.value) !== changes[this.dpBrightness])
|
|
41
|
+
characteristicBrightness.updateValue(this.convertBrightnessFromTuyaToHomeKit(changes[this.dpBrightness]));
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
getBrightness(callback) {
|
|
46
|
+
callback(null, this.convertBrightnessFromTuyaToHomeKit(this.device.state[this.dpBrightness]));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
setBrightness(value, callback) {
|
|
50
|
+
this.setState(this.dpBrightness, this.convertBrightnessFromHomeKitToTuya(value), callback);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
module.exports = SimpleDimmerAccessory;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
const BaseAccessory = require('./BaseAccessory');
|
|
2
|
+
|
|
3
|
+
class SimpleFanAccessory extends BaseAccessory {
|
|
4
|
+
static getCategory(Categories) {
|
|
5
|
+
return Categories.FAN;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
constructor(...props) {
|
|
9
|
+
super(...props);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
_registerPlatformAccessory() {
|
|
13
|
+
const {Service} = this.hap;
|
|
14
|
+
this.accessory.addService(Service.Fan, this.device.context.name);
|
|
15
|
+
super._registerPlatformAccessory();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
_registerCharacteristics(dps) {
|
|
19
|
+
const {Service, Characteristic} = this.hap;
|
|
20
|
+
const serviceFan = this.accessory.getService(Service.Fan);
|
|
21
|
+
this._checkServiceName(serviceFan, this.device.context.name);
|
|
22
|
+
this.dpFanOn = this._getCustomDP(this.device.context.dpFanOn) || '1';
|
|
23
|
+
this.dpRotationSpeed = this._getCustomDP(this.device.context.dpRotationSpeed) || '3';
|
|
24
|
+
this.maxSpeed = parseInt(this.device.context.maxSpeed) || 3;
|
|
25
|
+
// This variable is here so that we can set the fans to turn onto speed one instead of 3 on start.
|
|
26
|
+
this.fanDefaultSpeed = parseInt(this.device.context.fanDefaultSpeed) || 1;
|
|
27
|
+
// This variable is here as a workaround to allow for the on/off function to work.
|
|
28
|
+
this.fanCurrentSpeed = 0;
|
|
29
|
+
// Add setting to use .toString() on return values or not.
|
|
30
|
+
this.useStrings = this._coerceBoolean(this.device.context.useStrings, true);
|
|
31
|
+
|
|
32
|
+
const characteristicFanOn = serviceFan.getCharacteristic(Characteristic.On)
|
|
33
|
+
.updateValue(this._getFanOn(dps[this.dpFanOn]))
|
|
34
|
+
.on('get', this.getFanOn.bind(this))
|
|
35
|
+
.on('set', this.setFanOn.bind(this));
|
|
36
|
+
|
|
37
|
+
const characteristicRotationSpeed = serviceFan.getCharacteristic(Characteristic.RotationSpeed)
|
|
38
|
+
.setProps({
|
|
39
|
+
minValue: 0,
|
|
40
|
+
maxValue: 100,
|
|
41
|
+
minStep: Math.max(100 / this.maxSpeed)
|
|
42
|
+
})
|
|
43
|
+
.updateValue(this.convertRotationSpeedFromTuyaToHomeKit(dps[this.dpRotationSpeed]))
|
|
44
|
+
.on('get', this.getSpeed.bind(this))
|
|
45
|
+
.on('set', this.setSpeed.bind(this));
|
|
46
|
+
|
|
47
|
+
this.device.on('change', (changes, state) => {
|
|
48
|
+
|
|
49
|
+
if (changes.hasOwnProperty(this.dpFanOn) && characteristicFanOn.value !== changes[this.dpFanOn])
|
|
50
|
+
characteristicFanOn.updateValue(changes[this.dpFanOn]);
|
|
51
|
+
|
|
52
|
+
if (changes.hasOwnProperty(this.dpRotationSpeed) && this.convertRotationSpeedFromHomeKitToTuya(characteristicRotationSpeed.value) !== changes[this.dpRotationSpeed])
|
|
53
|
+
characteristicRotationSpeed.updateValue(this.convertRotationSpeedFromTuyaToHomeKit(changes[this.dpRotationSpeed]));
|
|
54
|
+
|
|
55
|
+
this.log.debug('SimpleFan changed: ' + JSON.stringify(state));
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/*************************** FAN ***************************/
|
|
60
|
+
// Get the Current Fan State
|
|
61
|
+
getFanOn(callback) {
|
|
62
|
+
this.getState(this.dpFanOn, (err, dp) => {
|
|
63
|
+
if (err) return callback(err);
|
|
64
|
+
callback(null, this._getFanOn(dp));
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
_getFanOn(dp) {
|
|
69
|
+
const {Characteristic} = this.hap;
|
|
70
|
+
return dp;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
setFanOn(value, callback) {
|
|
74
|
+
const {Characteristic} = this.hap;
|
|
75
|
+
// This uses the multistate set command to send the fan on and speed request in one call.
|
|
76
|
+
if (value == false ) {
|
|
77
|
+
this.fanCurrentSpeed = 0;
|
|
78
|
+
// This will turn off the fan speed if it is set to be 0.
|
|
79
|
+
return this.setState(this.dpFanOn, false, callback);
|
|
80
|
+
} else {
|
|
81
|
+
if (this.fanCurrentSpeed === 0) {
|
|
82
|
+
// The current fanDefaultSpeed Variable is there to have the fan set to a sensible default if turned on.
|
|
83
|
+
if (this.useStrings) {
|
|
84
|
+
return this.setMultiStateLegacy({[this.dpFanOn]: value, [this.dpRotationSpeed]: this.fanDefaultSpeed.toString()}, callback);
|
|
85
|
+
} else {
|
|
86
|
+
return this.setMultiStateLegacy({[this.dpFanOn]: value, [this.dpRotationSpeed]: this.fanDefaultSpeed}, callback);
|
|
87
|
+
}
|
|
88
|
+
} else {
|
|
89
|
+
// The current fanCurrentSpeed Variable is there to ensure the fan speed doesn't change if the fan is already on.
|
|
90
|
+
if (this.useStrings) {
|
|
91
|
+
return this.setMultiStateLegacy({[this.dpFanOn]: value, [this.dpRotationSpeed]: this.fanCurrentSpeed.toString()}, callback);
|
|
92
|
+
} else {
|
|
93
|
+
return this.setMultiStateLegacy({[this.dpFanOn]: value, [this.dpRotationSpeed]: this.fanCurrentSpeed}, callback);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
callback();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Get the Current Fan Speed
|
|
101
|
+
getSpeed(callback) {
|
|
102
|
+
this.getState(this.dpRotationSpeed, (err, dp) => {
|
|
103
|
+
if (err) return callback(err);
|
|
104
|
+
callback(null, this.convertRotationSpeedFromTuyaToHomeKit(this.device.state[this.dpRotationSpeed]));
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Set the new fan speed
|
|
109
|
+
setSpeed(value, callback) {
|
|
110
|
+
const {Characteristic} = this.hap;
|
|
111
|
+
if (value === 0) {
|
|
112
|
+
// This is to set the fan speed variable to be 1 when the fan is off.
|
|
113
|
+
if (this.useStrings) {
|
|
114
|
+
return this.setMultiStateLegacy({[this.dpFanOn]: false, [this.dpRotationSpeed]: this.fanDefaultSpeed.toString()}, callback);
|
|
115
|
+
} else {
|
|
116
|
+
return this.setMultiStateLegacy({[this.dpFanOn]: false, [this.dpRotationSpeed]: this.fanDefaultSpeed}, callback);
|
|
117
|
+
}
|
|
118
|
+
} else {
|
|
119
|
+
// This is to set the fan speed variable to match the current speed.
|
|
120
|
+
this.fanCurrentSpeed = this.convertRotationSpeedFromHomeKitToTuya(value);
|
|
121
|
+
// This uses the multistatelegacy set command to send the fan on and speed request in one call.
|
|
122
|
+
if (this.useStrings) {
|
|
123
|
+
return this.setMultiStateLegacy({[this.dpFanOn]: true, [this.dpRotationSpeed]: this.convertRotationSpeedFromHomeKitToTuya(value).toString()}, callback);
|
|
124
|
+
} else {
|
|
125
|
+
return this.setMultiStateLegacy({[this.dpFanOn]: true, [this.dpRotationSpeed]: this.convertRotationSpeedFromHomeKitToTuya(value)}, callback);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
callback();
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
module.exports = SimpleFanAccessory;
|