homebridge-tuya-without-developer-account 1.0.6 → 1.0.8
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/CHANGELOG.md +16 -0
- package/README.md +32 -0
- package/SUPPORTED_DEVICES.md +21 -0
- package/config.schema.json +67 -2
- package/dist/platform.js +43 -1
- package/dist/shared/accessories/DiffuserAccessory.js +7 -0
- package/dist/shared/accessories/PetFeederAccessory.js +59 -88
- package/dist/shared/accessories/SecuritySystemAccessory.js +144 -5
- package/dist/shared/accessories/characteristic/Light.js +40 -7
- package/homebridge-ui/public/index.html +20 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.0.8
|
|
4
|
+
|
|
5
|
+
- Added optional HomeKit Adaptive Lighting support for eligible Tuya lights.
|
|
6
|
+
- Added global `options.enableAdaptiveLighting` setting in the Homebridge custom UI.
|
|
7
|
+
- Added per-device `deviceOverrides[].adaptiveLighting.enabled` override support.
|
|
8
|
+
- Adaptive Lighting is enabled only when a light exposes both Brightness and a real ColorTemperature DP. RGB-only lights, brightness-only dimmers, switches, and outlets are skipped automatically.
|
|
9
|
+
- Added safer logging when Adaptive Lighting is disabled or skipped for unsupported lights.
|
|
10
|
+
|
|
11
|
+
## 1.0.7
|
|
12
|
+
|
|
13
|
+
- Added Smart Pet Feeder support for `quick_feed`, `manual_feed`, `slow_feed`, `feed_state`, battery, and charging state.
|
|
14
|
+
- Added optional `deviceOverrides[].petFeeder.manualFeedAmount` and `deviceOverrides[].petFeeder.exposeSlowFeed`.
|
|
15
|
+
- Added Tuya alarm panel support as a HomeKit Security System using `master_mode`, `master_state`, and optional tamper/battery DPs.
|
|
16
|
+
- Added optional `deviceOverrides[].alarm` fields for alarm sound, muffling, and notification switches.
|
|
17
|
+
- Added clearer logging when Tuya returns an empty schema for aroma diffusers. Diffuser scenes remain exposed separately.
|
|
18
|
+
|
|
3
19
|
## 1.0.6
|
|
4
20
|
|
|
5
21
|
- Fixed a Homebridge UI issue where clicking **Save Configuration** could leave the custom settings page spinner running indefinitely even when QR authentication data had already been saved.
|
package/README.md
CHANGED
|
@@ -247,6 +247,31 @@ Fahrenheit display examples:
|
|
|
247
247
|
|
|
248
248
|
HomeKit stores temperature characteristic metadata in Celsius. Do not enter Fahrenheit values in the plugin config.
|
|
249
249
|
|
|
250
|
+
|
|
251
|
+
## Adaptive Lighting
|
|
252
|
+
|
|
253
|
+
Version 1.0.8 adds optional HomeKit Adaptive Lighting support. Enable it in the Homebridge plugin settings with **Enable Adaptive Lighting for eligible CCT/RGBCW lights**.
|
|
254
|
+
|
|
255
|
+
Adaptive Lighting is applied only to Tuya light accessories that expose both:
|
|
256
|
+
|
|
257
|
+
- Brightness
|
|
258
|
+
- A real white color-temperature datapoint, such as `temp_value` or `temp_value_v2`
|
|
259
|
+
|
|
260
|
+
The plugin automatically skips RGB-only lights, brightness-only dimmers such as DP10 dimmer plugs, outlets, switches, and devices without a real color-temperature datapoint. HomeKit automatic mode may send periodic color-temperature updates while Adaptive Lighting is active.
|
|
261
|
+
|
|
262
|
+
Advanced per-device override example:
|
|
263
|
+
|
|
264
|
+
```json
|
|
265
|
+
{
|
|
266
|
+
"id": "YOUR_LIGHT_DEVICE_ID",
|
|
267
|
+
"adaptiveLighting": {
|
|
268
|
+
"enabled": true
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
Set `enabled` to `false` to disable Adaptive Lighting for one device even when the global option is enabled.
|
|
274
|
+
|
|
250
275
|
## Troubleshooting
|
|
251
276
|
|
|
252
277
|
### Plugin starts from cache only and logs `Each device override must include an "id"`
|
|
@@ -313,3 +338,10 @@ If this still happens after upgrading, open the plugin settings, clear the saved
|
|
|
313
338
|
|
|
314
339
|
Version **1.0.5** adds support for DP10-style Tuya dimmer plugs that expose `switch_led` and `bright_value_v2`. These are exposed in HomeKit as Lightbulb accessories with On and Brightness. If the accessory was previously shown as **Not Supported**, remove only that cached accessory in Homebridge UI and restart Homebridge after upgrading.
|
|
315
340
|
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
## Version 1.0.7 device support
|
|
344
|
+
|
|
345
|
+
This release adds native support for Tuya Smart Pet Feeders and Tuya alarm panels that expose `master_mode`. Pet feeders expose quick/manual feed controls, optional slow-feed control, feed-state sensor, and battery when available. Alarm panels are exposed as HomeKit Security System accessories, with optional extra switches controlled through `deviceOverrides[].alarm`.
|
|
346
|
+
|
|
347
|
+
Aroma diffuser devices whose Tuya QR cloud schema is empty remain visible as unsupported direct devices, but any diffuser scenes returned by Tuya are still exposed separately.
|
package/SUPPORTED_DEVICES.md
CHANGED
|
@@ -214,3 +214,24 @@ Supported from **v1.0.5**. These devices normally report category `tgq` and expo
|
|
|
214
214
|
|
|
215
215
|
They are exposed to HomeKit as a Lightbulb with On and Brightness. After upgrading from an older version where the device showed as unsupported, remove the affected cached accessory from Homebridge UI and restart Homebridge.
|
|
216
216
|
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
## Added in 1.0.7
|
|
220
|
+
|
|
221
|
+
### Smart Pet Feeder
|
|
222
|
+
|
|
223
|
+
Supported when Tuya exposes one or more of: `quick_feed`, `manual_feed`, `slow_feed`, `feed_state`, `battery_percentage`, `charge_state`.
|
|
224
|
+
|
|
225
|
+
### Alarm / Security System
|
|
226
|
+
|
|
227
|
+
Supported when Tuya exposes `master_mode`. Alarm-triggered state is detected from `master_state=alarm`, `sos_state=true`, or `master_mode=sos` when available. Optional extra switches can be enabled through `deviceOverrides[].alarm`.
|
|
228
|
+
|
|
229
|
+
### Aroma Diffuser with Empty Schema
|
|
230
|
+
|
|
231
|
+
If Tuya QR cloud returns an empty schema for the diffuser device, direct device control cannot be mapped. The plugin keeps diffuser Tuya scenes exposed and logs a clearer explanation.
|
|
232
|
+
|
|
233
|
+
## Adaptive Lighting support
|
|
234
|
+
|
|
235
|
+
Adaptive Lighting is supported for eligible Tuya light accessories that expose both brightness and real white color-temperature datapoints, for example CCT, CW, or RGBCW lights.
|
|
236
|
+
|
|
237
|
+
Not eligible: simple on/off lights, outlets, switches, brightness-only dimmers, DP10 dimmer plugs, and RGB-only lights without a real white-temperature datapoint.
|
package/config.schema.json
CHANGED
|
@@ -36,6 +36,12 @@
|
|
|
36
36
|
"type": "string"
|
|
37
37
|
}
|
|
38
38
|
},
|
|
39
|
+
"enableAdaptiveLighting": {
|
|
40
|
+
"type": "boolean",
|
|
41
|
+
"title": "Enable Adaptive Lighting for eligible lights",
|
|
42
|
+
"description": "Optional. Enables HomeKit Adaptive Lighting only for Tuya lights that expose both brightness and real white color-temperature controls. RGB-only lights and dimmer plugs are skipped.",
|
|
43
|
+
"default": false
|
|
44
|
+
},
|
|
39
45
|
"deviceOverrides": {
|
|
40
46
|
"type": "array",
|
|
41
47
|
"title": "Device Overrides",
|
|
@@ -57,8 +63,23 @@
|
|
|
57
63
|
"default": false
|
|
58
64
|
},
|
|
59
65
|
"adaptiveLighting": {
|
|
60
|
-
"
|
|
61
|
-
"
|
|
66
|
+
"title": "Adaptive Lighting Override",
|
|
67
|
+
"description": "Optional per-device override. Use enabled=true to force-enable for this device, or enabled=false to disable it even when global Adaptive Lighting is enabled.",
|
|
68
|
+
"oneOf": [
|
|
69
|
+
{
|
|
70
|
+
"type": "boolean",
|
|
71
|
+
"title": "Enabled"
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"type": "object",
|
|
75
|
+
"properties": {
|
|
76
|
+
"enabled": {
|
|
77
|
+
"type": "boolean",
|
|
78
|
+
"title": "Enabled"
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
],
|
|
62
83
|
"default": false
|
|
63
84
|
},
|
|
64
85
|
"schema": {
|
|
@@ -110,6 +131,49 @@
|
|
|
110
131
|
"default": 1
|
|
111
132
|
}
|
|
112
133
|
}
|
|
134
|
+
},
|
|
135
|
+
"petFeeder": {
|
|
136
|
+
"type": "object",
|
|
137
|
+
"title": "Smart Pet Feeder Options",
|
|
138
|
+
"description": "Optional settings for Tuya pet feeders. The plugin exposes quick/manual feed switches, feed-state sensor, and battery when supported.",
|
|
139
|
+
"properties": {
|
|
140
|
+
"manualFeedAmount": {
|
|
141
|
+
"type": "integer",
|
|
142
|
+
"title": "Manual feed amount",
|
|
143
|
+
"description": "Portions sent when the Manual Feed switch is turned on.",
|
|
144
|
+
"minimum": 1,
|
|
145
|
+
"maximum": 12,
|
|
146
|
+
"default": 1
|
|
147
|
+
},
|
|
148
|
+
"exposeSlowFeed": {
|
|
149
|
+
"type": "boolean",
|
|
150
|
+
"title": "Expose Slow Feed switch",
|
|
151
|
+
"description": "Expose slow_feed as a HomeKit switch when the device supports it.",
|
|
152
|
+
"default": true
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
"alarm": {
|
|
157
|
+
"type": "object",
|
|
158
|
+
"title": "Alarm Options",
|
|
159
|
+
"description": "Optional extra switches for Tuya alarm panels. The main alarm is exposed as a HomeKit Security System when master_mode is available.",
|
|
160
|
+
"properties": {
|
|
161
|
+
"exposeAlarmSoundSwitch": {
|
|
162
|
+
"type": "boolean",
|
|
163
|
+
"title": "Expose Alarm Sound switch",
|
|
164
|
+
"default": false
|
|
165
|
+
},
|
|
166
|
+
"exposeMufflingSwitch": {
|
|
167
|
+
"type": "boolean",
|
|
168
|
+
"title": "Expose Mute/Muffling switch",
|
|
169
|
+
"default": false
|
|
170
|
+
},
|
|
171
|
+
"exposeNotificationSwitches": {
|
|
172
|
+
"type": "boolean",
|
|
173
|
+
"title": "Expose Call/SMS/App/Low Battery switches",
|
|
174
|
+
"default": false
|
|
175
|
+
}
|
|
176
|
+
}
|
|
113
177
|
}
|
|
114
178
|
},
|
|
115
179
|
"required": [
|
|
@@ -151,6 +215,7 @@
|
|
|
151
215
|
"expanded": false,
|
|
152
216
|
"items": [
|
|
153
217
|
"options.homeWhitelist",
|
|
218
|
+
"options.enableAdaptiveLighting",
|
|
154
219
|
"options.deviceOverrides",
|
|
155
220
|
"options.debug",
|
|
156
221
|
"options.debugLevel"
|
package/dist/platform.js
CHANGED
|
@@ -47,6 +47,7 @@ class TuyaPlatform {
|
|
|
47
47
|
// Old Tuya IoT OpenAPI credentials, local LAN mode, username/password login, and hybrid mode are not accepted.
|
|
48
48
|
this.config.mode = "cloud";
|
|
49
49
|
this.options.projectType = "3";
|
|
50
|
+
this.options.enableAdaptiveLighting = this.options.enableAdaptiveLighting === true;
|
|
50
51
|
|
|
51
52
|
if (!this.options.userCode || String(this.options.userCode).trim().length === 0) {
|
|
52
53
|
this.log.error("[Tuya QR] Missing Tuya User Code. Open Homebridge UI → Plugins → Tuya without developer account for Homebridge → Settings, generate/scan the QR code, then save.");
|
|
@@ -122,6 +123,44 @@ class TuyaPlatform {
|
|
|
122
123
|
delete item.airConditioner;
|
|
123
124
|
}
|
|
124
125
|
}
|
|
126
|
+
if (item.petFeeder && typeof item.petFeeder === 'object') {
|
|
127
|
+
const normalizedPetFeeder = {};
|
|
128
|
+
const manualFeedAmount = Number(item.petFeeder.manualFeedAmount);
|
|
129
|
+
if (Number.isFinite(manualFeedAmount)) {
|
|
130
|
+
normalizedPetFeeder.manualFeedAmount = Math.max(1, Math.min(12, Math.round(manualFeedAmount)));
|
|
131
|
+
}
|
|
132
|
+
if (typeof item.petFeeder.exposeSlowFeed === 'boolean') {
|
|
133
|
+
normalizedPetFeeder.exposeSlowFeed = item.petFeeder.exposeSlowFeed;
|
|
134
|
+
}
|
|
135
|
+
if (Object.keys(normalizedPetFeeder).length > 0) {
|
|
136
|
+
item.petFeeder = normalizedPetFeeder;
|
|
137
|
+
} else {
|
|
138
|
+
delete item.petFeeder;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (item.alarm && typeof item.alarm === 'object') {
|
|
142
|
+
const normalizedAlarm = {};
|
|
143
|
+
for (const key of ['exposeAlarmSoundSwitch', 'exposeMufflingSwitch', 'exposeNotificationSwitches']) {
|
|
144
|
+
if (typeof item.alarm[key] === 'boolean') {
|
|
145
|
+
normalizedAlarm[key] = item.alarm[key];
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (Object.keys(normalizedAlarm).length > 0) {
|
|
149
|
+
item.alarm = normalizedAlarm;
|
|
150
|
+
} else {
|
|
151
|
+
delete item.alarm;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (item.adaptiveLighting !== undefined) {
|
|
155
|
+
if (typeof item.adaptiveLighting === 'boolean') {
|
|
156
|
+
item.adaptiveLighting = { enabled: item.adaptiveLighting };
|
|
157
|
+
} else if (item.adaptiveLighting && typeof item.adaptiveLighting === 'object' && typeof item.adaptiveLighting.enabled === 'boolean') {
|
|
158
|
+
item.adaptiveLighting = { enabled: item.adaptiveLighting.enabled };
|
|
159
|
+
} else {
|
|
160
|
+
this.log.warn('[Tuya QR] Ignoring invalid adaptiveLighting override for id "%s". Use true/false or { enabled: true/false }.', id);
|
|
161
|
+
delete item.adaptiveLighting;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
125
164
|
seenIds.add(id);
|
|
126
165
|
validOverrides.push(item);
|
|
127
166
|
}
|
|
@@ -249,7 +288,10 @@ class TuyaPlatform {
|
|
|
249
288
|
unbridged: deviceConfig?.unbridged ?? false,
|
|
250
289
|
schemaOverrides: deviceConfig?.schema ? JSON.stringify(deviceConfig.schema) : undefined,
|
|
251
290
|
airConditioner: deviceConfig?.airConditioner ? JSON.stringify(deviceConfig.airConditioner) : undefined,
|
|
252
|
-
|
|
291
|
+
petFeeder: deviceConfig?.petFeeder ? JSON.stringify(deviceConfig.petFeeder) : undefined,
|
|
292
|
+
alarm: deviceConfig?.alarm ? JSON.stringify(deviceConfig.alarm) : undefined,
|
|
293
|
+
globalAdaptiveLighting: !!this.options.enableAdaptiveLighting,
|
|
294
|
+
adaptiveLighting: deviceConfig?.adaptiveLighting ? JSON.stringify(deviceConfig.adaptiveLighting) : undefined,
|
|
253
295
|
};
|
|
254
296
|
const { changed: configChanged } = this.configHash.hasConfigChanged(device.id, configToHash);
|
|
255
297
|
device.configChanged = configChanged;
|
|
@@ -23,6 +23,13 @@ class DiffuserAccessory extends BaseAccessory_1.default {
|
|
|
23
23
|
requiredSchema() {
|
|
24
24
|
return [SCHEMA_CODE.SPRAY_ON];
|
|
25
25
|
}
|
|
26
|
+
|
|
27
|
+
checkRequirements() {
|
|
28
|
+
if (this.device && Array.isArray(this.device.schema) && this.device.schema.length === 0) {
|
|
29
|
+
this.log.warn('Tuya returned an empty schema for this diffuser. Direct diffuser control cannot be mapped until Tuya exposes switch_spray or equivalent DPs. Any Tuya scenes for this diffuser will still be exposed separately.');
|
|
30
|
+
}
|
|
31
|
+
return super.checkRequirements();
|
|
32
|
+
}
|
|
26
33
|
configureServices() {
|
|
27
34
|
// Main Switch
|
|
28
35
|
(0, On_1.configureOn)(this, undefined, this.getSchema(...SCHEMA_CODE.ON));
|
|
@@ -4,136 +4,107 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const BaseAccessory_1 = __importDefault(require("./BaseAccessory"));
|
|
7
|
-
const
|
|
7
|
+
const Name_1 = require("./characteristic/Name");
|
|
8
8
|
const SCHEMA_CODE = {
|
|
9
|
-
ACTIVE: ['switch'],
|
|
10
|
-
LIGHT: ['light'],
|
|
11
9
|
QUICK_FEED: ['quick_feed'],
|
|
12
10
|
SLOW_FEED: ['slow_feed'],
|
|
13
11
|
MANUAL_FEED: ['manual_feed'],
|
|
14
|
-
MEAL_PLAN: ['meal_plan'],
|
|
15
|
-
BATTERY_PERCENTAGE: ['battery_percentage'],
|
|
16
|
-
FEED_REPORT: ['feed_report'],
|
|
17
12
|
FEED_STATE: ['feed_state'],
|
|
18
13
|
};
|
|
19
14
|
class PetFeederAccessory extends BaseAccessory_1.default {
|
|
20
15
|
requiredSchema() {
|
|
21
|
-
|
|
16
|
+
// Tuya pet feeders often do not expose a generic "switch" DP. A feeder is
|
|
17
|
+
// considered supported when it has at least one command DP we can expose.
|
|
18
|
+
return [[...SCHEMA_CODE.QUICK_FEED, ...SCHEMA_CODE.MANUAL_FEED, ...SCHEMA_CODE.SLOW_FEED]];
|
|
19
|
+
}
|
|
20
|
+
getPetFeederConfig() {
|
|
21
|
+
const config = this.device ? this.platform.getDeviceConfig(this.device) : undefined;
|
|
22
|
+
const feeder = (config && typeof config.petFeeder === 'object') ? config.petFeeder : {};
|
|
23
|
+
const manualFeedAmount = Number(feeder.manualFeedAmount);
|
|
24
|
+
return {
|
|
25
|
+
manualFeedAmount: Number.isFinite(manualFeedAmount) ? Math.max(1, Math.min(12, Math.round(manualFeedAmount))) : 1,
|
|
26
|
+
exposeSlowFeed: feeder.exposeSlowFeed !== false,
|
|
27
|
+
};
|
|
22
28
|
}
|
|
23
29
|
configureServices() {
|
|
24
|
-
(0, Active_1.configureActive)(this, this.mainService(), this.getSchema(...SCHEMA_CODE.ACTIVE));
|
|
25
|
-
this.configureLight();
|
|
26
30
|
this.configureQuickFeed();
|
|
27
|
-
this.configureSlowFeed();
|
|
28
31
|
this.configureManualFeed();
|
|
29
|
-
this.
|
|
30
|
-
this.configureBatteryPercentage();
|
|
31
|
-
this.configureFeedReport();
|
|
32
|
+
this.configureSlowFeed();
|
|
32
33
|
this.configureFeedState();
|
|
33
34
|
}
|
|
34
|
-
|
|
35
|
-
return this.accessory.getService(this.Service.Switch)
|
|
36
|
-
|| this.accessory.addService(this.Service.Switch);
|
|
37
|
-
}
|
|
38
|
-
configureLight() {
|
|
39
|
-
const schema = this.getSchema(...SCHEMA_CODE.LIGHT);
|
|
40
|
-
if (!schema) {
|
|
41
|
-
this.log.warn('Light is not supported.');
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
this.mainService().getCharacteristic(this.Characteristic.On)
|
|
45
|
-
.onSet(async (value) => {
|
|
46
|
-
await this.sendCommands([{ code: schema.code, value: value }]);
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
configureQuickFeed() {
|
|
50
|
-
const schema = this.getSchema(...SCHEMA_CODE.QUICK_FEED);
|
|
35
|
+
configureActionSwitch(schema, name, subtype, onSet) {
|
|
51
36
|
if (!schema) {
|
|
52
|
-
this.log.warn('Quick feed is not supported.');
|
|
53
37
|
return;
|
|
54
38
|
}
|
|
55
|
-
this.
|
|
39
|
+
const service = this.accessory.getServiceById(this.Service.Switch, subtype)
|
|
40
|
+
|| this.accessory.addService(this.Service.Switch, name, subtype);
|
|
41
|
+
(0, Name_1.configureName)(this, service, name);
|
|
42
|
+
service.getCharacteristic(this.Characteristic.On)
|
|
43
|
+
.onGet(() => {
|
|
44
|
+
this.checkOnlineStatus();
|
|
45
|
+
// quick_feed/manual_feed are momentary actions. Always display them as off.
|
|
46
|
+
return false;
|
|
47
|
+
})
|
|
56
48
|
.onSet(async (value) => {
|
|
57
49
|
if (value) {
|
|
58
|
-
await
|
|
50
|
+
await onSet();
|
|
59
51
|
}
|
|
52
|
+
setTimeout(() => service.getCharacteristic(this.Characteristic.On).updateValue(false), 500);
|
|
60
53
|
});
|
|
61
54
|
}
|
|
62
|
-
|
|
63
|
-
const schema = this.getSchema(...SCHEMA_CODE.SLOW_FEED);
|
|
55
|
+
configureBooleanSwitch(schema, name, subtype) {
|
|
64
56
|
if (!schema) {
|
|
65
|
-
this.log.warn('Slow feed is not supported.');
|
|
66
57
|
return;
|
|
67
58
|
}
|
|
68
|
-
this.
|
|
69
|
-
.
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const schema = this.getSchema(...SCHEMA_CODE.MANUAL_FEED);
|
|
77
|
-
if (!schema) {
|
|
78
|
-
this.log.warn('Manual feed is not supported.');
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
this.mainService().getCharacteristic(this.Characteristic.On)
|
|
59
|
+
const service = this.accessory.getServiceById(this.Service.Switch, subtype)
|
|
60
|
+
|| this.accessory.addService(this.Service.Switch, name, subtype);
|
|
61
|
+
(0, Name_1.configureName)(this, service, name);
|
|
62
|
+
service.getCharacteristic(this.Characteristic.On)
|
|
63
|
+
.onGet(() => {
|
|
64
|
+
this.checkOnlineStatus();
|
|
65
|
+
return !!(this.getStatus(schema.code)?.value ?? false);
|
|
66
|
+
})
|
|
82
67
|
.onSet(async (value) => {
|
|
83
|
-
|
|
84
|
-
await this.sendCommands([{ code: schema.code, value: 1 }]);
|
|
85
|
-
}
|
|
68
|
+
await this.sendCommands([{ code: schema.code, value: !!value }], true);
|
|
86
69
|
});
|
|
87
70
|
}
|
|
88
|
-
|
|
89
|
-
const schema = this.getSchema(...SCHEMA_CODE.
|
|
90
|
-
|
|
91
|
-
this.
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
this.mainService().getCharacteristic(this.Characteristic.On)
|
|
95
|
-
.onSet(async (value) => {
|
|
96
|
-
if (value) {
|
|
97
|
-
await this.sendCommands([{ code: schema.code, value: value }]);
|
|
98
|
-
}
|
|
71
|
+
configureQuickFeed() {
|
|
72
|
+
const schema = this.getSchema(...SCHEMA_CODE.QUICK_FEED);
|
|
73
|
+
this.configureActionSwitch(schema, `${this.device?.name || 'Pet Feeder'} Quick Feed`, 'quick_feed', async () => {
|
|
74
|
+
await this.sendCommands([{ code: schema.code, value: true }], true);
|
|
99
75
|
});
|
|
100
76
|
}
|
|
101
|
-
|
|
102
|
-
const schema = this.getSchema(...SCHEMA_CODE.
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
this.mainService().getCharacteristic(this.Characteristic.BatteryLevel)
|
|
108
|
-
.onGet(() => {
|
|
109
|
-
const status = this.getStatus(schema.code);
|
|
110
|
-
return status.value;
|
|
77
|
+
configureManualFeed() {
|
|
78
|
+
const schema = this.getSchema(...SCHEMA_CODE.MANUAL_FEED);
|
|
79
|
+
const { manualFeedAmount } = this.getPetFeederConfig();
|
|
80
|
+
this.configureActionSwitch(schema, `${this.device?.name || 'Pet Feeder'} Manual Feed`, 'manual_feed', async () => {
|
|
81
|
+
await this.sendCommands([{ code: schema.code, value: manualFeedAmount }], true);
|
|
111
82
|
});
|
|
112
83
|
}
|
|
113
|
-
|
|
114
|
-
const schema = this.getSchema(...SCHEMA_CODE.
|
|
115
|
-
|
|
116
|
-
|
|
84
|
+
configureSlowFeed() {
|
|
85
|
+
const schema = this.getSchema(...SCHEMA_CODE.SLOW_FEED);
|
|
86
|
+
const { exposeSlowFeed } = this.getPetFeederConfig();
|
|
87
|
+
if (!exposeSlowFeed) {
|
|
117
88
|
return;
|
|
118
89
|
}
|
|
119
|
-
this.
|
|
120
|
-
.onGet(() => {
|
|
121
|
-
const status = this.getStatus(schema.code);
|
|
122
|
-
return status.value;
|
|
123
|
-
});
|
|
90
|
+
this.configureBooleanSwitch(schema, `${this.device?.name || 'Pet Feeder'} Slow Feed`, 'slow_feed');
|
|
124
91
|
}
|
|
125
92
|
configureFeedState() {
|
|
126
93
|
const schema = this.getSchema(...SCHEMA_CODE.FEED_STATE);
|
|
127
94
|
if (!schema) {
|
|
128
|
-
this.log.warn('Feed state is not supported.');
|
|
129
95
|
return;
|
|
130
96
|
}
|
|
131
|
-
this.
|
|
97
|
+
const service = this.accessory.getServiceById(this.Service.OccupancySensor, 'feed_state')
|
|
98
|
+
|| this.accessory.addService(this.Service.OccupancySensor, `${this.device?.name || 'Pet Feeder'} Feeding`, 'feed_state');
|
|
99
|
+
(0, Name_1.configureName)(this, service, `${this.device?.name || 'Pet Feeder'} Feeding`);
|
|
100
|
+
const { OCCUPANCY_DETECTED, OCCUPANCY_NOT_DETECTED } = this.Characteristic.OccupancyDetected;
|
|
101
|
+
service.getCharacteristic(this.Characteristic.OccupancyDetected)
|
|
132
102
|
.onGet(() => {
|
|
133
|
-
|
|
134
|
-
|
|
103
|
+
this.checkOnlineStatus();
|
|
104
|
+
const value = this.getStatus(schema.code)?.value;
|
|
105
|
+
return value === 'feeding' ? OCCUPANCY_DETECTED : OCCUPANCY_NOT_DETECTED;
|
|
135
106
|
});
|
|
136
107
|
}
|
|
137
108
|
}
|
|
138
109
|
exports.default = PetFeederAccessory;
|
|
139
|
-
//# sourceMappingURL=PetFeederAccessory.js.map
|
|
110
|
+
//# sourceMappingURL=PetFeederAccessory.js.map
|
|
@@ -4,11 +4,19 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const BaseAccessory_1 = __importDefault(require("./BaseAccessory"));
|
|
7
|
-
const SecuritySystemState_1 = require("./characteristic/SecuritySystemState");
|
|
8
7
|
const Name_1 = require("./characteristic/Name");
|
|
9
8
|
const SCHEMA_CODE = {
|
|
10
9
|
MASTER_MODE: ['master_mode'],
|
|
10
|
+
MASTER_STATE: ['master_state'],
|
|
11
11
|
SOS_STATE: ['sos_state'],
|
|
12
|
+
TAMPER_ALARM: ['temper_alarm', 'tamper_alarm'],
|
|
13
|
+
ALARM_SOUND: ['switch_alarm_sound'],
|
|
14
|
+
MUFFLING: ['muffling'],
|
|
15
|
+
ALARM_CALL: ['switch_alarm_call'],
|
|
16
|
+
ALARM_SMS: ['switch_alarm_sms'],
|
|
17
|
+
ALARM_PROPEL: ['switch_alarm_propel'],
|
|
18
|
+
LOW_BATTERY_ALERT: ['switch_low_battery'],
|
|
19
|
+
MODE_DELAY_SOUND: ['switch_mode_dl_sound'],
|
|
12
20
|
};
|
|
13
21
|
class SecuritySystemAccessory extends BaseAccessory_1.default {
|
|
14
22
|
constructor() {
|
|
@@ -16,15 +24,146 @@ class SecuritySystemAccessory extends BaseAccessory_1.default {
|
|
|
16
24
|
this.isNightArm = false;
|
|
17
25
|
}
|
|
18
26
|
requiredSchema() {
|
|
19
|
-
|
|
27
|
+
// Some Tuya alarm panels expose master_mode + master_state, but not sos_state.
|
|
28
|
+
return [SCHEMA_CODE.MASTER_MODE];
|
|
29
|
+
}
|
|
30
|
+
getAlarmConfig() {
|
|
31
|
+
const config = this.device ? this.platform.getDeviceConfig(this.device) : undefined;
|
|
32
|
+
const alarm = (config && typeof config.alarm === 'object') ? config.alarm : {};
|
|
33
|
+
return {
|
|
34
|
+
exposeAlarmSoundSwitch: !!alarm.exposeAlarmSoundSwitch,
|
|
35
|
+
exposeMufflingSwitch: !!alarm.exposeMufflingSwitch,
|
|
36
|
+
exposeNotificationSwitches: !!alarm.exposeNotificationSwitches,
|
|
37
|
+
};
|
|
20
38
|
}
|
|
21
39
|
configureServices() {
|
|
22
40
|
const service = this.accessory.getService(this.Service.SecuritySystem)
|
|
23
41
|
|| this.accessory.addService(this.Service.SecuritySystem);
|
|
24
42
|
(0, Name_1.configureName)(this, service, this.device.name);
|
|
25
|
-
|
|
26
|
-
|
|
43
|
+
this.configureCurrentState(service);
|
|
44
|
+
this.configureTargetState(service);
|
|
45
|
+
this.configureTamper(service);
|
|
46
|
+
this.configureExtraSwitches();
|
|
47
|
+
}
|
|
48
|
+
mapTuyaModeToHomeKit(value, current = true) {
|
|
49
|
+
const Current = this.Characteristic.SecuritySystemCurrentState;
|
|
50
|
+
const Target = this.Characteristic.SecuritySystemTargetState;
|
|
51
|
+
const map = current ? {
|
|
52
|
+
disarmed: Current.DISARMED,
|
|
53
|
+
arm: Current.AWAY_ARM,
|
|
54
|
+
home: this.isNightArm ? Current.NIGHT_ARM : Current.STAY_ARM,
|
|
55
|
+
sos: Current.ALARM_TRIGGERED,
|
|
56
|
+
} : {
|
|
57
|
+
disarmed: Target.DISARM,
|
|
58
|
+
arm: Target.AWAY_ARM,
|
|
59
|
+
home: this.isNightArm ? Target.NIGHT_ARM : Target.STAY_ARM,
|
|
60
|
+
sos: Target.AWAY_ARM,
|
|
61
|
+
};
|
|
62
|
+
return map[value] ?? (current ? Current.DISARMED : Target.DISARM);
|
|
63
|
+
}
|
|
64
|
+
mapHomeKitTargetToTuya(value) {
|
|
65
|
+
const Target = this.Characteristic.SecuritySystemTargetState;
|
|
66
|
+
switch (value) {
|
|
67
|
+
case Target.DISARM:
|
|
68
|
+
return 'disarmed';
|
|
69
|
+
case Target.STAY_ARM:
|
|
70
|
+
case Target.NIGHT_ARM:
|
|
71
|
+
return 'home';
|
|
72
|
+
case Target.AWAY_ARM:
|
|
73
|
+
default:
|
|
74
|
+
return 'arm';
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
isAlarmTriggered() {
|
|
78
|
+
const masterStateSchema = this.getSchema(...SCHEMA_CODE.MASTER_STATE);
|
|
79
|
+
if (masterStateSchema && this.getStatus(masterStateSchema.code)?.value === 'alarm') {
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
const sosStateSchema = this.getSchema(...SCHEMA_CODE.SOS_STATE);
|
|
83
|
+
if (sosStateSchema && this.getStatus(sosStateSchema.code)?.value) {
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
const masterModeSchema = this.getSchema(...SCHEMA_CODE.MASTER_MODE);
|
|
87
|
+
if (masterModeSchema && this.getStatus(masterModeSchema.code)?.value === 'sos') {
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
configureCurrentState(service) {
|
|
93
|
+
const masterModeSchema = this.getSchema(...SCHEMA_CODE.MASTER_MODE);
|
|
94
|
+
service.getCharacteristic(this.Characteristic.SecuritySystemCurrentState)
|
|
95
|
+
.onGet(() => {
|
|
96
|
+
this.checkOnlineStatus();
|
|
97
|
+
if (this.isAlarmTriggered()) {
|
|
98
|
+
return this.Characteristic.SecuritySystemCurrentState.ALARM_TRIGGERED;
|
|
99
|
+
}
|
|
100
|
+
return this.mapTuyaModeToHomeKit(this.getStatus(masterModeSchema.code)?.value, true);
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
configureTargetState(service) {
|
|
104
|
+
const masterModeSchema = this.getSchema(...SCHEMA_CODE.MASTER_MODE);
|
|
105
|
+
service.getCharacteristic(this.Characteristic.SecuritySystemTargetState)
|
|
106
|
+
.onGet(() => {
|
|
107
|
+
this.checkOnlineStatus();
|
|
108
|
+
return this.mapTuyaModeToHomeKit(this.getStatus(masterModeSchema.code)?.value, false);
|
|
109
|
+
})
|
|
110
|
+
.onSet(async (value) => {
|
|
111
|
+
this.isNightArm = value === this.Characteristic.SecuritySystemTargetState.NIGHT_ARM;
|
|
112
|
+
const commands = [{ code: masterModeSchema.code, value: this.mapHomeKitTargetToTuya(value) }];
|
|
113
|
+
const sosStateSchema = this.getSchema(...SCHEMA_CODE.SOS_STATE);
|
|
114
|
+
if (sosStateSchema && value === this.Characteristic.SecuritySystemTargetState.DISARM) {
|
|
115
|
+
commands.push({ code: sosStateSchema.code, value: false });
|
|
116
|
+
}
|
|
117
|
+
await this.sendCommands(commands, true);
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
configureTamper(service) {
|
|
121
|
+
const schema = this.getSchema(...SCHEMA_CODE.TAMPER_ALARM);
|
|
122
|
+
if (!schema) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (!service.testCharacteristic(this.Characteristic.StatusTampered)) {
|
|
126
|
+
service.addOptionalCharacteristic(this.Characteristic.StatusTampered);
|
|
127
|
+
}
|
|
128
|
+
const { TAMPERED, NOT_TAMPERED } = this.Characteristic.StatusTampered;
|
|
129
|
+
service.getCharacteristic(this.Characteristic.StatusTampered)
|
|
130
|
+
.onGet(() => {
|
|
131
|
+
this.checkOnlineStatus();
|
|
132
|
+
return this.getStatus(schema.code)?.value ? TAMPERED : NOT_TAMPERED;
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
configureExtraSwitches() {
|
|
136
|
+
const config = this.getAlarmConfig();
|
|
137
|
+
if (config.exposeAlarmSoundSwitch) {
|
|
138
|
+
this.configureBooleanSwitch(this.getSchema(...SCHEMA_CODE.ALARM_SOUND), `${this.device.name} Alarm Sound`, 'switch_alarm_sound');
|
|
139
|
+
}
|
|
140
|
+
if (config.exposeMufflingSwitch) {
|
|
141
|
+
this.configureBooleanSwitch(this.getSchema(...SCHEMA_CODE.MUFFLING), `${this.device.name} Mute`, 'muffling');
|
|
142
|
+
}
|
|
143
|
+
if (config.exposeNotificationSwitches) {
|
|
144
|
+
this.configureBooleanSwitch(this.getSchema(...SCHEMA_CODE.ALARM_CALL), `${this.device.name} Alarm Call`, 'switch_alarm_call');
|
|
145
|
+
this.configureBooleanSwitch(this.getSchema(...SCHEMA_CODE.ALARM_SMS), `${this.device.name} Alarm SMS`, 'switch_alarm_sms');
|
|
146
|
+
this.configureBooleanSwitch(this.getSchema(...SCHEMA_CODE.ALARM_PROPEL), `${this.device.name} App Push`, 'switch_alarm_propel');
|
|
147
|
+
this.configureBooleanSwitch(this.getSchema(...SCHEMA_CODE.LOW_BATTERY_ALERT), `${this.device.name} Low Battery Alert`, 'switch_low_battery');
|
|
148
|
+
this.configureBooleanSwitch(this.getSchema(...SCHEMA_CODE.MODE_DELAY_SOUND), `${this.device.name} Mode Delay Sound`, 'switch_mode_dl_sound');
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
configureBooleanSwitch(schema, name, subtype) {
|
|
152
|
+
if (!schema) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
const service = this.accessory.getServiceById(this.Service.Switch, subtype)
|
|
156
|
+
|| this.accessory.addService(this.Service.Switch, name, subtype);
|
|
157
|
+
(0, Name_1.configureName)(this, service, name);
|
|
158
|
+
service.getCharacteristic(this.Characteristic.On)
|
|
159
|
+
.onGet(() => {
|
|
160
|
+
this.checkOnlineStatus();
|
|
161
|
+
return !!(this.getStatus(schema.code)?.value ?? false);
|
|
162
|
+
})
|
|
163
|
+
.onSet(async (value) => {
|
|
164
|
+
await this.sendCommands([{ code: schema.code, value: !!value }], true);
|
|
165
|
+
});
|
|
27
166
|
}
|
|
28
167
|
}
|
|
29
168
|
exports.default = SecuritySystemAccessory;
|
|
30
|
-
//# sourceMappingURL=SecuritySystemAccessory.js.map
|
|
169
|
+
//# sourceMappingURL=SecuritySystemAccessory.js.map
|
|
@@ -249,20 +249,53 @@ function configureLight(accessory, service, onSchema, brightSchema, tempSchema,
|
|
|
249
249
|
}
|
|
250
250
|
configureAdaptiveLighting(accessory, service, brightSchema, tempSchema);
|
|
251
251
|
}
|
|
252
|
-
function
|
|
252
|
+
function getAdaptiveLightingOverride(config) {
|
|
253
|
+
if (!config || config.adaptiveLighting === undefined) {
|
|
254
|
+
return undefined;
|
|
255
|
+
}
|
|
256
|
+
if (typeof config.adaptiveLighting === 'boolean') {
|
|
257
|
+
return config.adaptiveLighting;
|
|
258
|
+
}
|
|
259
|
+
if (config.adaptiveLighting && typeof config.adaptiveLighting === 'object' && typeof config.adaptiveLighting.enabled === 'boolean') {
|
|
260
|
+
return config.adaptiveLighting.enabled;
|
|
261
|
+
}
|
|
262
|
+
return undefined;
|
|
263
|
+
}
|
|
264
|
+
function shouldEnableAdaptiveLighting(accessory) {
|
|
253
265
|
const config = accessory.platform.getDeviceConfig(accessory.device);
|
|
254
|
-
|
|
266
|
+
const override = getAdaptiveLightingOverride(config);
|
|
267
|
+
if (override !== undefined) {
|
|
268
|
+
return override;
|
|
269
|
+
}
|
|
270
|
+
return accessory.platform.options.enableAdaptiveLighting === true;
|
|
271
|
+
}
|
|
272
|
+
function configureAdaptiveLighting(accessory, service, brightSchema, tempSchema) {
|
|
273
|
+
if (!shouldEnableAdaptiveLighting(accessory)) {
|
|
255
274
|
accessory.log.info('Adaptive Lighting disabled.');
|
|
256
275
|
return;
|
|
257
276
|
}
|
|
258
|
-
accessory.log.info('Adaptive Lighting enabled.');
|
|
259
277
|
if (!brightSchema || !tempSchema) {
|
|
260
|
-
accessory.log.
|
|
278
|
+
accessory.log.info('Adaptive Lighting skipped: this light does not expose both brightness and a real color-temperature DP.');
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
if (!service.testCharacteristic(accessory.Characteristic.Brightness) || !service.testCharacteristic(accessory.Characteristic.ColorTemperature)) {
|
|
282
|
+
accessory.log.info('Adaptive Lighting skipped: HomeKit Lightbulb service is missing Brightness or ColorTemperature.');
|
|
261
283
|
return;
|
|
262
284
|
}
|
|
263
285
|
const { AdaptiveLightingController } = accessory.platform.api.hap;
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
286
|
+
if (!AdaptiveLightingController) {
|
|
287
|
+
accessory.log.warn('Adaptive Lighting skipped: this Homebridge/HAP-NodeJS version does not expose AdaptiveLightingController.');
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
try {
|
|
291
|
+
const controller = new AdaptiveLightingController(service);
|
|
292
|
+
accessory.accessory.configureController(controller);
|
|
293
|
+
accessory.adaptiveLightingController = controller;
|
|
294
|
+
accessory.log.info('Adaptive Lighting enabled for eligible CCT/RGBCW light.');
|
|
295
|
+
}
|
|
296
|
+
catch (error) {
|
|
297
|
+
accessory.log.warn(`Adaptive Lighting setup failed: ${error instanceof Error ? error.message : error}`);
|
|
298
|
+
}
|
|
267
299
|
}
|
|
300
|
+
|
|
268
301
|
//# sourceMappingURL=Light.js.map
|
|
@@ -114,6 +114,19 @@
|
|
|
114
114
|
</div>
|
|
115
115
|
</div>
|
|
116
116
|
|
|
117
|
+
|
|
118
|
+
<div class="tuya-nodev-card">
|
|
119
|
+
<div class="tuya-nodev-title">Adaptive Lighting</div>
|
|
120
|
+
<p class="tuya-nodev-small mb-3">
|
|
121
|
+
Enable HomeKit Adaptive Lighting only for eligible Tuya lights that expose both brightness and real white color-temperature controls. RGB-only lights, dimmer plugs, switches, and outlets are skipped automatically.
|
|
122
|
+
</p>
|
|
123
|
+
<div class="form-check">
|
|
124
|
+
<input id="tuyaNodevAdaptiveLighting" class="form-check-input" type="checkbox">
|
|
125
|
+
<label class="form-check-label" for="tuyaNodevAdaptiveLighting">Enable Adaptive Lighting for eligible CCT/RGBCW lights</label>
|
|
126
|
+
</div>
|
|
127
|
+
<small class="form-text text-muted">HomeKit automatic mode may send color-temperature updates roughly once per minute while Adaptive Lighting is active.</small>
|
|
128
|
+
</div>
|
|
129
|
+
|
|
117
130
|
<div class="tuya-nodev-card">
|
|
118
131
|
<div class="tuya-nodev-title">Air Conditioner Temperature Overrides</div>
|
|
119
132
|
<p class="tuya-nodev-small mb-3">
|
|
@@ -213,6 +226,7 @@
|
|
|
213
226
|
cfg.options = cfg.options && typeof cfg.options === 'object' ? cfg.options : {};
|
|
214
227
|
cfg.options.userCode = getUserCode();
|
|
215
228
|
cfg.options.projectType = '3';
|
|
229
|
+
cfg.options.enableAdaptiveLighting = $('tuyaNodevAdaptiveLighting')?.checked === true;
|
|
216
230
|
if (!Array.isArray(cfg.options.deviceOverrides)) {
|
|
217
231
|
cfg.options.deviceOverrides = [];
|
|
218
232
|
}
|
|
@@ -665,6 +679,7 @@
|
|
|
665
679
|
ensureConfig();
|
|
666
680
|
$('tuyaNodevName').value = currentConfig.name || 'Tuya without developer account';
|
|
667
681
|
$('tuyaNodevUserCode').value = currentConfig.options?.userCode || '';
|
|
682
|
+
$('tuyaNodevAdaptiveLighting').checked = currentConfig.options?.enableAdaptiveLighting === true;
|
|
668
683
|
|
|
669
684
|
if (homebridge.showSchemaForm) homebridge.showSchemaForm();
|
|
670
685
|
window.homebridge.addEventListener('configChanged', (event) => {
|
|
@@ -675,6 +690,7 @@
|
|
|
675
690
|
ensureConfig();
|
|
676
691
|
if (block.name) $('tuyaNodevName').value = block.name;
|
|
677
692
|
if (block.options?.userCode) $('tuyaNodevUserCode').value = block.options.userCode;
|
|
693
|
+
$('tuyaNodevAdaptiveLighting').checked = block.options?.enableAdaptiveLighting === true;
|
|
678
694
|
renderAcOverrides();
|
|
679
695
|
}
|
|
680
696
|
});
|
|
@@ -684,6 +700,10 @@
|
|
|
684
700
|
$('tuyaNodevClear').addEventListener('click', clearAuth);
|
|
685
701
|
$('tuyaNodevSave').addEventListener('click', saveConfig);
|
|
686
702
|
$('tuyaNodevName').addEventListener('input', syncConfigToUi);
|
|
703
|
+
$('tuyaNodevAdaptiveLighting').addEventListener('change', async () => {
|
|
704
|
+
await syncConfigToUi();
|
|
705
|
+
if (isAuthenticated) enableSaving();
|
|
706
|
+
});
|
|
687
707
|
$('tuyaNodevUserCode').addEventListener('input', () => {
|
|
688
708
|
isAuthenticated = false;
|
|
689
709
|
disableSaving();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "homebridge-tuya-without-developer-account",
|
|
3
3
|
"displayName": "Tuya without developer account for Homebridge",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.8",
|
|
5
5
|
"description": "Homebridge plugin for Tuya and Smart Life devices using QR cloud authentication without a Tuya IoT developer account.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "Kosztyk",
|