homebridge-tuya-without-developer-account 1.0.7 → 1.0.9
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 +14 -0
- package/README.md +30 -0
- package/SUPPORTED_DEVICES.md +6 -0
- package/config.schema.json +24 -2
- package/dist/platform.js +13 -1
- package/dist/shared/accessories/characteristic/Light.js +40 -7
- package/homebridge-ui/public/index.html +47 -13
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.0.9
|
|
4
|
+
|
|
5
|
+
- Fixed the custom Homebridge settings UI so toggling Adaptive Lighting marks the config as changed and enables **Save Configuration**.
|
|
6
|
+
- Save now performs a final existing-auth check before blocking, so normal configuration-only changes are not prevented when a QR auth token is already saved.
|
|
7
|
+
- Name and AC override UI changes also mark the custom config as dirty more reliably.
|
|
8
|
+
|
|
9
|
+
## 1.0.8
|
|
10
|
+
|
|
11
|
+
- Added optional HomeKit Adaptive Lighting support for eligible Tuya lights.
|
|
12
|
+
- Added global `options.enableAdaptiveLighting` setting in the Homebridge custom UI.
|
|
13
|
+
- Added per-device `deviceOverrides[].adaptiveLighting.enabled` override support.
|
|
14
|
+
- 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.
|
|
15
|
+
- Added safer logging when Adaptive Lighting is disabled or skipped for unsupported lights.
|
|
16
|
+
|
|
3
17
|
## 1.0.7
|
|
4
18
|
|
|
5
19
|
- Added Smart Pet Feeder support for `quick_feed`, `manual_feed`, `slow_feed`, `feed_state`, battery, and charging state.
|
package/README.md
CHANGED
|
@@ -247,6 +247,36 @@ 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
|
+
|
|
254
|
+
### v1.0.9 UI save-state fix
|
|
255
|
+
|
|
256
|
+
Version 1.0.9 fixes the custom settings UI so changing the Adaptive Lighting checkbox immediately enables **Save Configuration**. If QR authentication is already saved, the UI performs a final auth check during save and no longer blocks normal configuration-only changes.
|
|
257
|
+
|
|
258
|
+
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**.
|
|
259
|
+
|
|
260
|
+
Adaptive Lighting is applied only to Tuya light accessories that expose both:
|
|
261
|
+
|
|
262
|
+
- Brightness
|
|
263
|
+
- A real white color-temperature datapoint, such as `temp_value` or `temp_value_v2`
|
|
264
|
+
|
|
265
|
+
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.
|
|
266
|
+
|
|
267
|
+
Advanced per-device override example:
|
|
268
|
+
|
|
269
|
+
```json
|
|
270
|
+
{
|
|
271
|
+
"id": "YOUR_LIGHT_DEVICE_ID",
|
|
272
|
+
"adaptiveLighting": {
|
|
273
|
+
"enabled": true
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
Set `enabled` to `false` to disable Adaptive Lighting for one device even when the global option is enabled.
|
|
279
|
+
|
|
250
280
|
## Troubleshooting
|
|
251
281
|
|
|
252
282
|
### Plugin starts from cache only and logs `Each device override must include an "id"`
|
package/SUPPORTED_DEVICES.md
CHANGED
|
@@ -229,3 +229,9 @@ Supported when Tuya exposes `master_mode`. Alarm-triggered state is detected fro
|
|
|
229
229
|
### Aroma Diffuser with Empty Schema
|
|
230
230
|
|
|
231
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": {
|
|
@@ -194,6 +215,7 @@
|
|
|
194
215
|
"expanded": false,
|
|
195
216
|
"items": [
|
|
196
217
|
"options.homeWhitelist",
|
|
218
|
+
"options.enableAdaptiveLighting",
|
|
197
219
|
"options.deviceOverrides",
|
|
198
220
|
"options.debug",
|
|
199
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.");
|
|
@@ -150,6 +151,16 @@ class TuyaPlatform {
|
|
|
150
151
|
delete item.alarm;
|
|
151
152
|
}
|
|
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
|
+
}
|
|
153
164
|
seenIds.add(id);
|
|
154
165
|
validOverrides.push(item);
|
|
155
166
|
}
|
|
@@ -279,7 +290,8 @@ class TuyaPlatform {
|
|
|
279
290
|
airConditioner: deviceConfig?.airConditioner ? JSON.stringify(deviceConfig.airConditioner) : undefined,
|
|
280
291
|
petFeeder: deviceConfig?.petFeeder ? JSON.stringify(deviceConfig.petFeeder) : undefined,
|
|
281
292
|
alarm: deviceConfig?.alarm ? JSON.stringify(deviceConfig.alarm) : undefined,
|
|
282
|
-
|
|
293
|
+
globalAdaptiveLighting: !!this.options.enableAdaptiveLighting,
|
|
294
|
+
adaptiveLighting: deviceConfig?.adaptiveLighting ? JSON.stringify(deviceConfig.adaptiveLighting) : undefined,
|
|
283
295
|
};
|
|
284
296
|
const { changed: configChanged } = this.configHash.hasConfigChanged(device.id, configToHash);
|
|
285
297
|
device.configChanged = configChanged;
|
|
@@ -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">
|
|
@@ -178,6 +191,7 @@
|
|
|
178
191
|
let currentConfig = null;
|
|
179
192
|
let pollTimer = null;
|
|
180
193
|
let isAuthenticated = false;
|
|
194
|
+
let isDirty = false;
|
|
181
195
|
let detectedDevices = [];
|
|
182
196
|
|
|
183
197
|
const $ = (id) => document.getElementById(id);
|
|
@@ -213,6 +227,7 @@
|
|
|
213
227
|
cfg.options = cfg.options && typeof cfg.options === 'object' ? cfg.options : {};
|
|
214
228
|
cfg.options.userCode = getUserCode();
|
|
215
229
|
cfg.options.projectType = '3';
|
|
230
|
+
cfg.options.enableAdaptiveLighting = $('tuyaNodevAdaptiveLighting')?.checked === true;
|
|
216
231
|
if (!Array.isArray(cfg.options.deviceOverrides)) {
|
|
217
232
|
cfg.options.deviceOverrides = [];
|
|
218
233
|
}
|
|
@@ -255,6 +270,11 @@
|
|
|
255
270
|
if (homebridge.enableSaveButton) homebridge.enableSaveButton();
|
|
256
271
|
}
|
|
257
272
|
|
|
273
|
+
function markDirty() {
|
|
274
|
+
isDirty = true;
|
|
275
|
+
enableSaving();
|
|
276
|
+
}
|
|
277
|
+
|
|
258
278
|
function getDeviceName(id) {
|
|
259
279
|
const device = detectedDevices.find((item) => item.id === id);
|
|
260
280
|
return device ? device.name : '';
|
|
@@ -403,9 +423,7 @@
|
|
|
403
423
|
const name = getDeviceName(id) || id;
|
|
404
424
|
setAcStatus(`AC override saved in plugin config for ${name}: ${minTemperature}–${maxTemperature} °C, step ${temperatureStep} °C. Click Save Configuration when ready.`, 'success');
|
|
405
425
|
homebridge.toast.success('AC temperature override added to config.', 'Tuya');
|
|
406
|
-
|
|
407
|
-
enableSaving();
|
|
408
|
-
}
|
|
426
|
+
markDirty();
|
|
409
427
|
} catch (e) {
|
|
410
428
|
setAcStatus(e.message || 'Failed to add AC override.', 'danger');
|
|
411
429
|
}
|
|
@@ -439,9 +457,7 @@
|
|
|
439
457
|
setAcStatus('Selected AC override was removed from the plugin config. Click Save Configuration when ready.', 'success');
|
|
440
458
|
homebridge.toast.success('AC temperature override removed from config.', 'Tuya');
|
|
441
459
|
}
|
|
442
|
-
|
|
443
|
-
enableSaving();
|
|
444
|
-
}
|
|
460
|
+
markDirty();
|
|
445
461
|
}
|
|
446
462
|
|
|
447
463
|
async function checkAuth(showSuccessToast = false) {
|
|
@@ -586,18 +602,24 @@
|
|
|
586
602
|
}
|
|
587
603
|
|
|
588
604
|
async function saveConfig() {
|
|
589
|
-
if (!isAuthenticated) {
|
|
590
|
-
setStatus('Scan and approve the QR code before saving.', 'warning');
|
|
591
|
-
return;
|
|
592
|
-
}
|
|
593
|
-
|
|
594
605
|
const userCode = getUserCode();
|
|
595
606
|
if (!userCode) {
|
|
596
607
|
setStatus('User Code is required before saving.', 'warning');
|
|
608
|
+
homebridge.toast.error('User Code is required before saving.', 'Tuya');
|
|
597
609
|
return;
|
|
598
610
|
}
|
|
599
611
|
|
|
600
612
|
try {
|
|
613
|
+
if (!isAuthenticated) {
|
|
614
|
+
// Do a last auth check here. This prevents normal configuration-only changes
|
|
615
|
+
// such as Adaptive Lighting from being blocked when the token already exists
|
|
616
|
+
// but this UI session has not marked itself authenticated yet.
|
|
617
|
+
const auth = await homebridge.request('/auth/status', { userCode });
|
|
618
|
+
isAuthenticated = !!auth.authenticated;
|
|
619
|
+
if (!isAuthenticated) {
|
|
620
|
+
throw new Error('Scan and approve the QR code, or click Check Existing Auth, before saving.');
|
|
621
|
+
}
|
|
622
|
+
}
|
|
601
623
|
homebridge.showSpinner();
|
|
602
624
|
$('tuyaNodevSave').disabled = true;
|
|
603
625
|
|
|
@@ -635,11 +657,13 @@
|
|
|
635
657
|
|
|
636
658
|
if (saveTimedOut && verified) {
|
|
637
659
|
const message = 'Configuration appears to be saved, but Homebridge UI did not return a save confirmation. Close this settings window and restart Homebridge.';
|
|
660
|
+
isDirty = false;
|
|
638
661
|
homebridge.toast.success(message, 'Tuya');
|
|
639
662
|
setStatus(message, 'success');
|
|
640
663
|
return;
|
|
641
664
|
}
|
|
642
665
|
|
|
666
|
+
isDirty = false;
|
|
643
667
|
homebridge.toast.success('Configuration saved. Restart Homebridge to load devices.', 'Tuya');
|
|
644
668
|
setStatus('Configuration saved. Restart Homebridge to load devices.', 'success');
|
|
645
669
|
} catch (e) {
|
|
@@ -647,7 +671,7 @@
|
|
|
647
671
|
homebridge.toast.error(e.message || 'Failed to save configuration.', 'Tuya');
|
|
648
672
|
} finally {
|
|
649
673
|
homebridge.hideSpinner();
|
|
650
|
-
if (isAuthenticated) {
|
|
674
|
+
if (isAuthenticated || isDirty) {
|
|
651
675
|
$('tuyaNodevSave').disabled = false;
|
|
652
676
|
}
|
|
653
677
|
}
|
|
@@ -665,6 +689,7 @@
|
|
|
665
689
|
ensureConfig();
|
|
666
690
|
$('tuyaNodevName').value = currentConfig.name || 'Tuya without developer account';
|
|
667
691
|
$('tuyaNodevUserCode').value = currentConfig.options?.userCode || '';
|
|
692
|
+
$('tuyaNodevAdaptiveLighting').checked = currentConfig.options?.enableAdaptiveLighting === true;
|
|
668
693
|
|
|
669
694
|
if (homebridge.showSchemaForm) homebridge.showSchemaForm();
|
|
670
695
|
window.homebridge.addEventListener('configChanged', (event) => {
|
|
@@ -675,6 +700,7 @@
|
|
|
675
700
|
ensureConfig();
|
|
676
701
|
if (block.name) $('tuyaNodevName').value = block.name;
|
|
677
702
|
if (block.options?.userCode) $('tuyaNodevUserCode').value = block.options.userCode;
|
|
703
|
+
$('tuyaNodevAdaptiveLighting').checked = block.options?.enableAdaptiveLighting === true;
|
|
678
704
|
renderAcOverrides();
|
|
679
705
|
}
|
|
680
706
|
});
|
|
@@ -683,7 +709,15 @@
|
|
|
683
709
|
$('tuyaNodevCheck').addEventListener('click', () => checkAuth(true));
|
|
684
710
|
$('tuyaNodevClear').addEventListener('click', clearAuth);
|
|
685
711
|
$('tuyaNodevSave').addEventListener('click', saveConfig);
|
|
686
|
-
$('tuyaNodevName').addEventListener('input',
|
|
712
|
+
$('tuyaNodevName').addEventListener('input', async () => {
|
|
713
|
+
await syncConfigToUi();
|
|
714
|
+
markDirty();
|
|
715
|
+
});
|
|
716
|
+
$('tuyaNodevAdaptiveLighting').addEventListener('change', async () => {
|
|
717
|
+
await syncConfigToUi();
|
|
718
|
+
markDirty();
|
|
719
|
+
setStatus('Adaptive Lighting setting changed. Click Save Configuration when ready.', 'info');
|
|
720
|
+
});
|
|
687
721
|
$('tuyaNodevUserCode').addEventListener('input', () => {
|
|
688
722
|
isAuthenticated = false;
|
|
689
723
|
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.9",
|
|
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",
|