homebridge-tuya-plus 3.1.2
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/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/lint.yml +23 -0
- package/Changelog.md +63 -0
- package/LICENSE +21 -0
- package/Readme.MD +106 -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 +538 -0
- package/eslint.config.mjs +15 -0
- package/index.js +242 -0
- package/lib/AirConditionerAccessory.js +445 -0
- package/lib/AirPurifierAccessory.js +532 -0
- package/lib/BaseAccessory.js +290 -0
- package/lib/ConvectorAccessory.js +313 -0
- package/lib/CustomMultiOutletAccessory.js +111 -0
- package/lib/DehumidifierAccessory.js +301 -0
- package/lib/DoorbellAccessory.js +208 -0
- package/lib/EnergyCharacteristics.js +86 -0
- package/lib/GarageDoorAccessory.js +307 -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 +299 -0
- package/lib/SimpleDimmer2Accessory.js +54 -0
- package/lib/SimpleDimmerAccessory.js +54 -0
- package/lib/SimpleFanAccessory.js +137 -0
- package/lib/SimpleFanLightAccessory.js +201 -0
- package/lib/SimpleHeaterAccessory.js +154 -0
- package/lib/SimpleLightAccessory.js +39 -0
- package/lib/SwitchAccessory.js +106 -0
- package/lib/TWLightAccessory.js +91 -0
- package/lib/TuyaAccessory.js +746 -0
- package/lib/TuyaDiscovery.js +165 -0
- package/lib/ValveAccessory.js +150 -0
- package/package.json +49 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
const BaseAccessory = require('./BaseAccessory');
|
|
2
|
+
|
|
3
|
+
class SimpleFanLightAccessory extends BaseAccessory {
|
|
4
|
+
static getCategory(Categories) {
|
|
5
|
+
return Categories.FANLIGHT;
|
|
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
|
+
this.accessory.addService(Service.Lightbulb, this.device.context.name + " Light");
|
|
16
|
+
super._registerPlatformAccessory();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
_registerCharacteristics(dps) {
|
|
20
|
+
const {Service, Characteristic} = this.hap;
|
|
21
|
+
const serviceFan = this.accessory.getService(Service.Fan);
|
|
22
|
+
const serviceLightbulb = this.accessory.getService(Service.Lightbulb);
|
|
23
|
+
this._checkServiceName(serviceFan, this.device.context.name);
|
|
24
|
+
this._checkServiceName(serviceLightbulb, this.device.context.name + " Light");
|
|
25
|
+
this.dpFanOn = this._getCustomDP(this.device.context.dpFanOn) || '1';
|
|
26
|
+
this.dpRotationSpeed = this._getCustomDP(this.device.context.dpRotationSpeed) || '3';
|
|
27
|
+
this.dpLightOn = this._getCustomDP(this.device.context.dpLightOn) || '9';
|
|
28
|
+
this.dpBrightness = this._getCustomDP(this.device.context.dpBrightness) || '10';
|
|
29
|
+
this.useLight = this._coerceBoolean(this.device.context.useLight, true);
|
|
30
|
+
this.useBrightness = this._coerceBoolean(this.device.context.useBrightness, false);
|
|
31
|
+
this.maxSpeed = parseInt(this.device.context.maxSpeed) || 3;
|
|
32
|
+
// This variable is here so that we can set the fans to turn onto speed one instead of 3 on start.
|
|
33
|
+
this.fanDefaultSpeed = parseInt(this.device.context.fanDefaultSpeed) || 1;
|
|
34
|
+
// This variable is here as a workaround to allow for the on/off function to work.
|
|
35
|
+
this.fanCurrentSpeed = 0;
|
|
36
|
+
// Add setting to use .toString() on return values or not.
|
|
37
|
+
this.useStrings = this._coerceBoolean(this.device.context.useStrings, true);
|
|
38
|
+
|
|
39
|
+
const characteristicFanOn = serviceFan.getCharacteristic(Characteristic.On)
|
|
40
|
+
.updateValue(this._getFanOn(dps[this.dpFanOn]))
|
|
41
|
+
.on('get', this.getFanOn.bind(this))
|
|
42
|
+
.on('set', this.setFanOn.bind(this));
|
|
43
|
+
|
|
44
|
+
const characteristicRotationSpeed = serviceFan.getCharacteristic(Characteristic.RotationSpeed)
|
|
45
|
+
.setProps({
|
|
46
|
+
minValue: 0,
|
|
47
|
+
maxValue: 100,
|
|
48
|
+
minStep: Math.max(100 / this.maxSpeed)
|
|
49
|
+
})
|
|
50
|
+
.updateValue(this.convertRotationSpeedFromTuyaToHomeKit(dps[this.dpRotationSpeed]))
|
|
51
|
+
.on('get', this.getSpeed.bind(this))
|
|
52
|
+
.on('set', this.setSpeed.bind(this));
|
|
53
|
+
|
|
54
|
+
let characteristicLightOn;
|
|
55
|
+
let characteristicBrightness;
|
|
56
|
+
if (this.useLight) {
|
|
57
|
+
characteristicLightOn = serviceLightbulb.getCharacteristic(Characteristic.On)
|
|
58
|
+
.updateValue(this._getLightOn(dps[this.dpLightOn]))
|
|
59
|
+
.on('get', this.getLightOn.bind(this))
|
|
60
|
+
.on('set', this.setLightOn.bind(this));
|
|
61
|
+
|
|
62
|
+
if (this.useBrightness) {
|
|
63
|
+
characteristicBrightness = serviceLightbulb.getCharacteristic(Characteristic.Brightness)
|
|
64
|
+
.setProps({
|
|
65
|
+
minValue: 0,
|
|
66
|
+
maxValue: 1000,
|
|
67
|
+
minStep: 100
|
|
68
|
+
})
|
|
69
|
+
.updateValue(this.convertBrightnessFromTuyaToHomeKit(dps[this.dpBrightness]))
|
|
70
|
+
.on('get', this.getBrightness.bind(this))
|
|
71
|
+
.on('set', this.setBrightness.bind(this));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
this.device.on('change', (changes, state) => {
|
|
76
|
+
|
|
77
|
+
if (changes.hasOwnProperty(this.dpFanOn) && characteristicFanOn.value !== changes[this.dpFanOn])
|
|
78
|
+
characteristicFanOn.updateValue(changes[this.dpFanOn]);
|
|
79
|
+
|
|
80
|
+
if (changes.hasOwnProperty(this.dpRotationSpeed) && this.convertRotationSpeedFromHomeKitToTuya(characteristicRotationSpeed.value) !== changes[this.dpRotationSpeed])
|
|
81
|
+
characteristicRotationSpeed.updateValue(this.convertRotationSpeedFromTuyaToHomeKit(changes[this.dpRotationSpeed]));
|
|
82
|
+
|
|
83
|
+
if (changes.hasOwnProperty(this.dpLightOn) && characteristicLightOn && characteristicLightOn.value !== changes[this.dpLightOn])
|
|
84
|
+
characteristicLightOn.updateValue(changes[this.dpLightOn]);
|
|
85
|
+
|
|
86
|
+
if (changes.hasOwnProperty(this.dpBrightness) && characteristicBrightness && characteristicBrightness.value !== changes[this.dpBrightness])
|
|
87
|
+
characteristicBrightness.updateValue(changes[this.dpBrightness]);
|
|
88
|
+
|
|
89
|
+
this.log.debug('SimpleFanLight changed: ' + JSON.stringify(state));
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/*************************** FAN ***************************/
|
|
94
|
+
// Get the Current Fan State
|
|
95
|
+
getFanOn(callback) {
|
|
96
|
+
this.getState(this.dpFanOn, (err, dp) => {
|
|
97
|
+
if (err) return callback(err);
|
|
98
|
+
callback(null, this._getFanOn(dp));
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
_getFanOn(dp) {
|
|
103
|
+
const {Characteristic} = this.hap;
|
|
104
|
+
return dp;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
setFanOn(value, callback) {
|
|
108
|
+
const {Characteristic} = this.hap;
|
|
109
|
+
// This uses the multistatelegacy set command to send the fan on and speed request in one call.
|
|
110
|
+
if (value == false ) {
|
|
111
|
+
this.fanCurrentSpeed = 0;
|
|
112
|
+
// This will turn off the fan speed if it is set to be 0.
|
|
113
|
+
return this.setState(this.dpFanOn, false, callback);
|
|
114
|
+
} else {
|
|
115
|
+
if (this.fanCurrentSpeed === 0) {
|
|
116
|
+
// The current fanDefaultSpeed Variable is there to have the fan set to a sensible default if turned on.
|
|
117
|
+
if (this.useStrings) {
|
|
118
|
+
return this.setMultiStateLegacy({[this.dpFanOn]: value, [this.dpRotationSpeed]: this.fanDefaultSpeed.toString()}, callback);
|
|
119
|
+
} else {
|
|
120
|
+
return this.setMultiStateLegacy({[this.dpFanOn]: value, [this.dpRotationSpeed]: this.fanDefaultSpeed}, callback);
|
|
121
|
+
}
|
|
122
|
+
} else {
|
|
123
|
+
// The current fanCurrentSpeed Variable is there to ensure the fan speed doesn't change if the fan is already on.
|
|
124
|
+
if (this.useStrings) {
|
|
125
|
+
return this.setMultiStateLegacy({[this.dpFanOn]: value, [this.dpRotationSpeed]: this.fanCurrentSpeed.toString()}, callback);
|
|
126
|
+
} else {
|
|
127
|
+
return this.setMultiStateLegacy({[this.dpFanOn]: value, [this.dpRotationSpeed]: this.fanCurrentSpeed}, callback);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Get the Current Fan Speed
|
|
134
|
+
getSpeed(callback) {
|
|
135
|
+
this.getState(this.dpRotationSpeed, (err, dp) => {
|
|
136
|
+
if (err) return callback(err);
|
|
137
|
+
callback(null, this.convertRotationSpeedFromTuyaToHomeKit(this.device.state[this.dpRotationSpeed]));
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Set the new fan speed
|
|
142
|
+
setSpeed(value, callback) {
|
|
143
|
+
const {Characteristic} = this.hap;
|
|
144
|
+
if (value === 0) {
|
|
145
|
+
// This is to set the fan speed variable to be 1 when the fan is off.
|
|
146
|
+
if (this.useStrings) {
|
|
147
|
+
return this.setMultiStateLegacy({[this.dpFanOn]: false, [this.dpRotationSpeed]: this.fanDefaultSpeed.toString()}, callback);
|
|
148
|
+
} else {
|
|
149
|
+
return this.setMultiStateLegacy({[this.dpFanOn]: false, [this.dpRotationSpeed]: this.fanDefaultSpeed}, callback);
|
|
150
|
+
}
|
|
151
|
+
} else {
|
|
152
|
+
// This is to set the fan speed variable to match the current speed.
|
|
153
|
+
this.fanCurrentSpeed = this.convertRotationSpeedFromHomeKitToTuya(value);
|
|
154
|
+
// This uses the multistatelegacy set command to send the fan on and speed request in one call.
|
|
155
|
+
if (this.useStrings) {
|
|
156
|
+
return this.setMultiStateLegacy({[this.dpFanOn]: true, [this.dpRotationSpeed]: this.convertRotationSpeedFromHomeKitToTuya(value).toString()}, callback);
|
|
157
|
+
} else {
|
|
158
|
+
return this.setMultiStateLegacy({[this.dpFanOn]: true, [this.dpRotationSpeed]: this.convertRotationSpeedFromHomeKitToTuya(value)}, callback);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/*************************** LIGHT ***************************/
|
|
164
|
+
//Lightbulb State
|
|
165
|
+
getLightOn(callback) {
|
|
166
|
+
this.getState(this.dpLightOn, (err, dp) => {
|
|
167
|
+
if (err) return callback(err);
|
|
168
|
+
callback(null, this._getLightOn(dp));
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
_getLightOn(dp) {
|
|
173
|
+
const {Characteristic} = this.hap;
|
|
174
|
+
return dp;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
setLightOn(value, callback) {
|
|
178
|
+
const {Characteristic} = this.hap;
|
|
179
|
+
return this.setState(this.dpLightOn, value, callback);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
//Lightbulb Brightness
|
|
183
|
+
getBrightness(callback) {
|
|
184
|
+
this.getState(this.dpBrightness, (err, dp) => {
|
|
185
|
+
if (err) return callback(err);
|
|
186
|
+
callback(null, this._getBrightness(dp));
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
_getBrightness(dp) {
|
|
191
|
+
const {Characteristic} = this.hap;
|
|
192
|
+
return dp;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
setBrightness(value, callback) {
|
|
196
|
+
const {Characteristic} = this.hap;
|
|
197
|
+
return this.setState(this.dpBrightness, value, callback);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
module.exports = SimpleFanLightAccessory;
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
const BaseAccessory = require('./BaseAccessory');
|
|
2
|
+
|
|
3
|
+
class SimpleHeaterAccessory extends BaseAccessory {
|
|
4
|
+
static getCategory(Categories) {
|
|
5
|
+
return Categories.AIR_HEATER;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
constructor(...props) {
|
|
9
|
+
super(...props);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
_registerPlatformAccessory() {
|
|
13
|
+
const {Service} = this.hap;
|
|
14
|
+
|
|
15
|
+
this.accessory.addService(Service.HeaterCooler, 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.HeaterCooler);
|
|
23
|
+
this._checkServiceName(service, this.device.context.name);
|
|
24
|
+
|
|
25
|
+
this.dpActive = this._getCustomDP(this.device.context.dpActive) || '1';
|
|
26
|
+
this.dpDesiredTemperature = this._getCustomDP(this.device.context.dpDesiredTemperature) || '2';
|
|
27
|
+
this.dpCurrentTemperature = this._getCustomDP(this.device.context.dpCurrentTemperature) || '3';
|
|
28
|
+
this.temperatureDivisor = parseInt(this.device.context.temperatureDivisor) || 1;
|
|
29
|
+
this.thresholdTemperatureDivisor = parseInt(this.device.context.thresholdTemperatureDivisor) || 1;
|
|
30
|
+
this.targetTemperatureDivisor = parseInt(this.device.context.targetTemperatureDivisor) || 1;
|
|
31
|
+
|
|
32
|
+
const characteristicActive = service.getCharacteristic(Characteristic.Active)
|
|
33
|
+
.updateValue(this._getActive(dps[this.dpActive]))
|
|
34
|
+
.on('get', this.getActive.bind(this))
|
|
35
|
+
.on('set', this.setActive.bind(this));
|
|
36
|
+
|
|
37
|
+
service.getCharacteristic(Characteristic.CurrentHeaterCoolerState)
|
|
38
|
+
.updateValue(this._getCurrentHeaterCoolerState(dps))
|
|
39
|
+
.on('get', this.getCurrentHeaterCoolerState.bind(this));
|
|
40
|
+
|
|
41
|
+
service.getCharacteristic(Characteristic.TargetHeaterCoolerState)
|
|
42
|
+
.setProps({
|
|
43
|
+
minValue: 1,
|
|
44
|
+
maxValue: 1,
|
|
45
|
+
validValues: [Characteristic.TargetHeaterCoolerState.HEAT]
|
|
46
|
+
})
|
|
47
|
+
.updateValue(this._getTargetHeaterCoolerState())
|
|
48
|
+
.on('get', this.getTargetHeaterCoolerState.bind(this))
|
|
49
|
+
.on('set', this.setTargetHeaterCoolerState.bind(this));
|
|
50
|
+
|
|
51
|
+
const characteristicCurrentTemperature = service.getCharacteristic(Characteristic.CurrentTemperature)
|
|
52
|
+
.updateValue(this._getDividedState(dps[this.dpCurrentTemperature], this.temperatureDivisor))
|
|
53
|
+
.on('get', this.getDividedState.bind(this, this.dpCurrentTemperature, this.temperatureDivisor));
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
const characteristicHeatingThresholdTemperature = service.getCharacteristic(Characteristic.HeatingThresholdTemperature)
|
|
57
|
+
.setProps({
|
|
58
|
+
minValue: this.device.context.minTemperature || 15,
|
|
59
|
+
maxValue: this.device.context.maxTemperature || 35,
|
|
60
|
+
minStep: this.device.context.minTemperatureSteps || 1
|
|
61
|
+
})
|
|
62
|
+
.updateValue(this._getDividedState(dps[this.dpDesiredTemperature], this.thresholdTemperatureDivisor))
|
|
63
|
+
.on('get', this.getDividedState.bind(this, this.dpDesiredTemperature, this.thresholdTemperatureDivisor))
|
|
64
|
+
.on('set', this.setTargetThresholdTemperature.bind(this));
|
|
65
|
+
|
|
66
|
+
this.characteristicHeatingThresholdTemperature = characteristicHeatingThresholdTemperature;
|
|
67
|
+
|
|
68
|
+
this.device.on('change', (changes, state) => {
|
|
69
|
+
if (changes.hasOwnProperty(this.dpActive)) {
|
|
70
|
+
const newActive = this._getActive(changes[this.dpActive]);
|
|
71
|
+
if (characteristicActive.value !== newActive) {
|
|
72
|
+
characteristicActive.updateValue(newActive);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (changes.hasOwnProperty(this.dpDesiredTemperature)) {
|
|
77
|
+
if (characteristicHeatingThresholdTemperature.value !== changes[this.dpDesiredTemperature])
|
|
78
|
+
characteristicHeatingThresholdTemperature.updateValue(changes[this.dpDesiredTemperature * this.targetTemperatureDivisor]);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (changes.hasOwnProperty(this.dpCurrentTemperature) && characteristicCurrentTemperature.value !== changes[this.dpCurrentTemperature]) characteristicCurrentTemperature.updateValue(this._getDividedState(changes[this.dpCurrentTemperature], this.temperatureDivisor));
|
|
82
|
+
|
|
83
|
+
this.log.info('SimpleHeater changed: ' + JSON.stringify(state));
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
getActive(callback) {
|
|
88
|
+
this.getState(this.dpActive, (err, dp) => {
|
|
89
|
+
if (err) return callback(err);
|
|
90
|
+
|
|
91
|
+
callback(null, this._getActive(dp));
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
_getActive(dp) {
|
|
96
|
+
const {Characteristic} = this.hap;
|
|
97
|
+
|
|
98
|
+
return dp ? Characteristic.Active.ACTIVE : Characteristic.Active.INACTIVE;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
setActive(value, callback) {
|
|
102
|
+
const {Characteristic} = this.hap;
|
|
103
|
+
|
|
104
|
+
switch (value) {
|
|
105
|
+
case Characteristic.Active.ACTIVE:
|
|
106
|
+
return this.setState(this.dpActive, true, callback);
|
|
107
|
+
|
|
108
|
+
case Characteristic.Active.INACTIVE:
|
|
109
|
+
return this.setState(this.dpActive, false, callback);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
callback();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
getCurrentHeaterCoolerState(callback) {
|
|
116
|
+
this.getState([this.dpActive], (err, dps) => {
|
|
117
|
+
if (err) return callback(err);
|
|
118
|
+
|
|
119
|
+
callback(null, this._getCurrentHeaterCoolerState(dps));
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
_getCurrentHeaterCoolerState(dps) {
|
|
124
|
+
const {Characteristic} = this.hap;
|
|
125
|
+
return dps[this.dpActive] ? Characteristic.CurrentHeaterCoolerState.HEATING : Characteristic.CurrentHeaterCoolerState.INACTIVE;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
getTargetHeaterCoolerState(callback) {
|
|
129
|
+
callback(null, this._getTargetHeaterCoolerState());
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
_getTargetHeaterCoolerState() {
|
|
133
|
+
const {Characteristic} = this.hap;
|
|
134
|
+
return Characteristic.TargetHeaterCoolerState.HEAT;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
setTargetHeaterCoolerState(value, callback) {
|
|
138
|
+
this.setState(this.dpActive, true, callback);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
setTargetThresholdTemperature(value, callback) {
|
|
142
|
+
this.setState(this.dpDesiredTemperature, value * this.thresholdTemperatureDivisor, err => {
|
|
143
|
+
if (err) return callback(err);
|
|
144
|
+
|
|
145
|
+
if (this.characteristicHeatingThresholdTemperature) {
|
|
146
|
+
this.characteristicHeatingThresholdTemperature.updateValue(value);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
callback();
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
module.exports = SimpleHeaterAccessory;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const BaseAccessory = require('./BaseAccessory');
|
|
2
|
+
|
|
3
|
+
class SimpleLightAccessory 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
|
+
|
|
27
|
+
const characteristicOn = service.getCharacteristic(Characteristic.On)
|
|
28
|
+
.updateValue(dps[this.dpPower])
|
|
29
|
+
.on('get', this.getState.bind(this, this.dpPower))
|
|
30
|
+
.on('set', this.setState.bind(this, this.dpPower));
|
|
31
|
+
|
|
32
|
+
this.device.on('change', (changes, state) => {
|
|
33
|
+
if (changes.hasOwnProperty(this.dpPower) && characteristicOn.value !== changes[this.dpPower]) characteristicOn.updateValue(changes[this.dpPower]);
|
|
34
|
+
this.log.info('SimpleLight changed: ' + JSON.stringify(state));
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
module.exports = SimpleLightAccessory;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
const BaseAccessory = require('./BaseAccessory');
|
|
2
|
+
const async = require('async');
|
|
3
|
+
|
|
4
|
+
class SwitchAccessory extends BaseAccessory {
|
|
5
|
+
static getCategory(Categories) {
|
|
6
|
+
return Categories.SWITCH;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
constructor(...props) {
|
|
10
|
+
super(...props);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
_registerPlatformAccessory() {
|
|
14
|
+
this._verifyCachedPlatformAccessory();
|
|
15
|
+
this._justRegistered = true;
|
|
16
|
+
|
|
17
|
+
super._registerPlatformAccessory();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
_verifyCachedPlatformAccessory() {
|
|
21
|
+
if (this._justRegistered) return;
|
|
22
|
+
|
|
23
|
+
const {Service} = this.hap;
|
|
24
|
+
|
|
25
|
+
const switchCount = parseInt(this.device.context.switchCount) || 1;
|
|
26
|
+
const _validServices = [];
|
|
27
|
+
for (let i = 0; i++ < switchCount;) {
|
|
28
|
+
let service = this.accessory.getServiceByUUIDAndSubType(Service.Switch, 'switch ' + i);
|
|
29
|
+
if (service) this._checkServiceName(service, this.device.context.name + ' ' + i);
|
|
30
|
+
else service = this.accessory.addService(Service.Switch, this.device.context.name + ' ' + i, 'switch ' + i);
|
|
31
|
+
|
|
32
|
+
_validServices.push(service);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
this.accessory.services
|
|
36
|
+
.filter(service => service.UUID === Service.Switch.UUID && !_validServices.includes(service))
|
|
37
|
+
.forEach(service => {
|
|
38
|
+
this.log.info('Removing', service.displayName);
|
|
39
|
+
this.accessory.removeService(service);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
_registerCharacteristics(dps) {
|
|
44
|
+
this._verifyCachedPlatformAccessory();
|
|
45
|
+
|
|
46
|
+
const {Service, Characteristic} = this.hap;
|
|
47
|
+
|
|
48
|
+
const characteristics = {};
|
|
49
|
+
this.accessory.services.forEach(service => {
|
|
50
|
+
if (service.UUID !== Service.Switch.UUID || !service.subtype) return false;
|
|
51
|
+
|
|
52
|
+
let match;
|
|
53
|
+
if ((match = service.subtype.match(/^switch (\d+)$/)) === null) return;
|
|
54
|
+
|
|
55
|
+
characteristics[match[1]] = service.getCharacteristic(Characteristic.On)
|
|
56
|
+
.updateValue(dps[match[1]])
|
|
57
|
+
.on('get', this.getPower.bind(this, match[1]))
|
|
58
|
+
.on('set', this.setPower.bind(this, match[1]));
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
this.device.on('change', (changes, state) => {
|
|
62
|
+
Object.keys(changes).forEach(key => {
|
|
63
|
+
if (characteristics[key] && characteristics[key].value !== changes[key]) characteristics[key].updateValue(changes[key]);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
getPower(dp, callback) {
|
|
69
|
+
callback(null, this.device.state[dp]);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
setPower(dp, value, callback) {
|
|
73
|
+
if (!this._pendingPower) {
|
|
74
|
+
this._pendingPower = {props: {}, callbacks: []};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (dp) {
|
|
78
|
+
if (this._pendingPower.timer) clearTimeout(this._pendingPower.timer);
|
|
79
|
+
|
|
80
|
+
this._pendingPower.props = {...this._pendingPower.props, ...{[dp]: value}};
|
|
81
|
+
this._pendingPower.callbacks.push(callback);
|
|
82
|
+
|
|
83
|
+
this._pendingPower.timer = setTimeout(() => {
|
|
84
|
+
this.setPower();
|
|
85
|
+
}, 500);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const callbacks = this._pendingPower.callbacks;
|
|
90
|
+
const callEachBack = err => {
|
|
91
|
+
async.eachSeries(callbacks, (callback, next) => {
|
|
92
|
+
try {
|
|
93
|
+
callback(err);
|
|
94
|
+
} catch (ex) {}
|
|
95
|
+
next();
|
|
96
|
+
});
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const newValue = this._pendingPower.props;
|
|
100
|
+
this._pendingPower = null;
|
|
101
|
+
|
|
102
|
+
this.setMultiState(newValue, callEachBack);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
module.exports = SwitchAccessory;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
const BaseAccessory = require('./BaseAccessory');
|
|
2
|
+
const async = require('async');
|
|
3
|
+
|
|
4
|
+
class TWLightAccessory extends BaseAccessory {
|
|
5
|
+
static getCategory(Categories) {
|
|
6
|
+
return Categories.LIGHTBULB;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
constructor(...props) {
|
|
10
|
+
super(...props);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
_registerPlatformAccessory() {
|
|
14
|
+
const {Service} = this.hap;
|
|
15
|
+
|
|
16
|
+
this.accessory.addService(Service.Lightbulb, this.device.context.name);
|
|
17
|
+
|
|
18
|
+
super._registerPlatformAccessory();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
_registerCharacteristics(dps) {
|
|
22
|
+
const {Service, Characteristic, AdaptiveLightingController} = this.hap;
|
|
23
|
+
const service = this.accessory.getService(Service.Lightbulb);
|
|
24
|
+
this._checkServiceName(service, this.device.context.name);
|
|
25
|
+
|
|
26
|
+
this.dpPower = this._getCustomDP(this.device.context.dpPower) || '1';
|
|
27
|
+
this.dpBrightness = this._getCustomDP(this.device.context.dpBrightness) || '2';
|
|
28
|
+
this.dpColorTemperature = this._getCustomDP(this.device.context.dpColorTemperature) || '3';
|
|
29
|
+
|
|
30
|
+
const characteristicOn = service.getCharacteristic(Characteristic.On)
|
|
31
|
+
.updateValue(dps[this.dpPower])
|
|
32
|
+
.on('get', this.getState.bind(this, this.dpPower))
|
|
33
|
+
.on('set', this.setState.bind(this, this.dpPower));
|
|
34
|
+
|
|
35
|
+
const characteristicBrightness = service.getCharacteristic(Characteristic.Brightness)
|
|
36
|
+
.updateValue(this.convertBrightnessFromTuyaToHomeKit(dps[this.dpBrightness]))
|
|
37
|
+
.on('get', this.getBrightness.bind(this))
|
|
38
|
+
.on('set', this.setBrightness.bind(this));
|
|
39
|
+
|
|
40
|
+
const characteristicColorTemperature = service.getCharacteristic(Characteristic.ColorTemperature)
|
|
41
|
+
.setProps({
|
|
42
|
+
minValue: 0,
|
|
43
|
+
maxValue: 600
|
|
44
|
+
})
|
|
45
|
+
.updateValue(this.convertColorTemperatureFromTuyaToHomeKit(dps[this.dpColorTemperature]))
|
|
46
|
+
.on('get', this.getColorTemperature.bind(this))
|
|
47
|
+
.on('set', this.setColorTemperature.bind(this));
|
|
48
|
+
|
|
49
|
+
this.characteristicColorTemperature = characteristicColorTemperature;
|
|
50
|
+
|
|
51
|
+
if (this.adaptiveLightingSupport()) {
|
|
52
|
+
this.adaptiveLightingController = new AdaptiveLightingController(service);
|
|
53
|
+
this.accessory.configureController(this.adaptiveLightingController);
|
|
54
|
+
this.accessory.adaptiveLightingController = this.adaptiveLightingController;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
this.device.on('change', (changes, state) => {
|
|
58
|
+
if (changes.hasOwnProperty(this.dpPower) && characteristicOn.value !== changes[this.dpPower]) characteristicOn.updateValue(changes[this.dpPower]);
|
|
59
|
+
|
|
60
|
+
if (changes.hasOwnProperty(this.dpBrightness) && this.convertBrightnessFromHomeKitToTuya(characteristicBrightness.value) !== changes[this.dpBrightness])
|
|
61
|
+
characteristicBrightness.updateValue(this.convertBrightnessFromTuyaToHomeKit(changes[this.dpBrightness]));
|
|
62
|
+
|
|
63
|
+
if (changes.hasOwnProperty(this.dpColorTemperature)) {
|
|
64
|
+
if (this.convertColorTemperatureFromHomeKitToTuya(characteristicColorTemperature.value) !== changes[this.dpColorTemperature])
|
|
65
|
+
characteristicColorTemperature.updateValue(this.convertColorTemperatureFromTuyaToHomeKit(changes[this.dpColorTemperature]));
|
|
66
|
+
} else if (changes[this.dpBrightness]) {
|
|
67
|
+
characteristicColorTemperature.updateValue(this.convertColorTemperatureFromTuyaToHomeKit(state[this.dpColorTemperature]));
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
getBrightness(callback) {
|
|
73
|
+
return callback(null, this.convertBrightnessFromTuyaToHomeKit(this.device.state[this.dpBrightness]));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
setBrightness(value, callback) {
|
|
77
|
+
return this.setState(this.dpBrightness, this.convertBrightnessFromHomeKitToTuya(value), callback);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
getColorTemperature(callback) {
|
|
81
|
+
callback(null, this.convertColorTemperatureFromTuyaToHomeKit(this.device.state[this.dpColorTemperature]));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
setColorTemperature(value, callback) {
|
|
85
|
+
if (value === 0) return callback(null, true);
|
|
86
|
+
|
|
87
|
+
this.setState(this.dpColorTemperature, this.convertColorTemperatureFromHomeKitToTuya(value), callback);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
module.exports = TWLightAccessory;
|