homebridge-kasa-python 3.1.0 → 3.2.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +98 -40
- package/config.schema.json +26 -19
- package/dist/config.d.ts +7 -9
- package/dist/config.js +39 -99
- package/dist/config.js.map +1 -1
- package/dist/devices/baseDevice.d.ts +3 -1
- package/dist/devices/baseDevice.js +49 -20
- package/dist/devices/baseDevice.js.map +1 -1
- package/dist/devices/baseParent.d.ts +1 -0
- package/dist/devices/baseParent.js +30 -10
- package/dist/devices/baseParent.js.map +1 -1
- package/dist/devices/descriptorHelpers.d.ts +3 -3
- package/dist/devices/descriptorHelpers.js +18 -6
- package/dist/devices/descriptorHelpers.js.map +1 -1
- package/dist/devices/deviceManager.js +0 -13
- package/dist/devices/deviceManager.js.map +1 -1
- package/dist/devices/deviceTypes.d.ts +1 -1
- package/dist/devices/deviceTypes.js.map +1 -1
- package/dist/devices/homekitLightBulb.d.ts +4 -0
- package/dist/devices/homekitLightBulb.js +53 -22
- package/dist/devices/homekitLightBulb.js.map +1 -1
- package/dist/devices/homekitPlug.d.ts +0 -1
- package/dist/devices/homekitPlug.js +8 -5
- package/dist/devices/homekitPlug.js.map +1 -1
- package/dist/devices/homekitPowerStrip.js +8 -3
- package/dist/devices/homekitPowerStrip.js.map +1 -1
- package/dist/platform.d.ts +1 -3
- package/dist/platform.js +13 -25
- package/dist/platform.js.map +1 -1
- package/dist/utils.js +6 -2
- package/dist/utils.js.map +1 -1
- package/package.json +5 -5
- package/requirements.txt +1 -1
package/README.md
CHANGED
|
@@ -15,43 +15,119 @@
|
|
|
15
15
|
<a href="https://www.npmjs.com/package/homebridge-kasa-python/v/latest"><img src="https://img.shields.io/npm/v/homebridge-kasa-python/latest?label=npm%40latest&color=blue" alt="latest npm version"></a>
|
|
16
16
|
<a href="https://pypi.org/project/python-kasa/"><img src="https://img.shields.io/badge/Python%40latest-3.11%2C%203.12%2C%203.13-blue" alt="latest PyPI pyversions"></a>
|
|
17
17
|
<a href="https://www.npmjs.com/package/homebridge-kasa-python/v/beta"><img src="https://img.shields.io/npm/v/homebridge-kasa-python/beta?label=npm%40beta&color=red" alt="beta npm version"></a>
|
|
18
|
-
<a href="https://pypi.org/project/python-kasa/"><img src="https://img.shields.io/badge/Python%
|
|
18
|
+
<a href="https://pypi.org/project/python-kasa/"><img src="https://img.shields.io/badge/Python%40beta-3.11%2C%203.12%2C%203.13-red" alt="beta PyPI pyversions"></a>
|
|
19
19
|
<a href="https://www.npmjs.com/package/homebridge-kasa-python/v/latest"><img src="https://img.shields.io/npm/dt/homebridge-kasa-python?color=brightgreen" alt="npm downloads total"></a>
|
|
20
20
|
<a href="https://www.paypal.me/ZeliardM/USD/"><img src="https://img.shields.io/badge/donate-paypal-orange" alt="donate paypal"></a>
|
|
21
21
|
<a href="https://github.com/sponsors/ZeliardM"><img src="https://img.shields.io/badge/donate-github-orange" alt="donate github"></a>
|
|
22
22
|
<a href="https://github.com/homebridge/homebridge/wiki/Verified-Plugins"><img src="https://img.shields.io/badge/homebridge-verified-blueviolet" alt="homebridge verified"></a>
|
|
23
23
|
</p>
|
|
24
24
|
|
|
25
|
-
<div align="center">
|
|
26
|
-
|
|
27
|
-
> ## IMPORTANT!!!
|
|
28
|
-
>Python Supported Versions: 3.11, 3.12, and 3.13.
|
|
29
|
-
|
|
30
|
-
</div>
|
|
31
|
-
|
|
32
25
|
This is a [Homebridge](https://github.com/homebridge/homebridge) plug-in based on the Python-Kasa API Library to interact with TP-Link Kasa/Tapo Devices.
|
|
33
26
|
|
|
34
27
|
This plug-in will automatically discover your TP-Link Kasa/Tapo Devices on your network locally only and configure them to be used in HomeKit.
|
|
35
28
|
|
|
36
29
|
Automatic Discovery may be possible only for some devices. If your device is not discovered automatically, try adding the IP Address into the Manual Devices List. Some newer devices require the Username and Password for your TP-Link Kasa/Tapo Cloud Account. Credentials can be enabled and provided in the plug-in settings.
|
|
37
30
|
|
|
38
|
-
|
|
39
|
-
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
-
|
|
47
|
-
|
|
31
|
+
## Requirements
|
|
32
|
+
- Homebridge Supported Versions: 1.8.0 and 2.0.0-beta.0 or later.
|
|
33
|
+
- Node.js Supported Versions: 20, 22, and 24.
|
|
34
|
+
- Python Supported Versions: 3.11, 3.12, and 3.13.
|
|
35
|
+
- A supported Kasa/Tapo device.
|
|
36
|
+
- Enabling Third Party Compatibility in the Tapo/Kasa App can improve device compatibility.
|
|
37
|
+
|
|
38
|
+
## Current Supported and Tested Devices
|
|
39
|
+
- I currently have used this plug-in with the HS300 (US) Power Strip and the KP115 (US) Plug. All other devices are yet to be tested and fully supported. If you have a device that is a plug, power strip, wall switch, bulb, or light strip and it does work as expected in HomeKit, please let me know and I can add it to the list of tested and supported devices. I don't have a lot of devices so cannot fully test everything.
|
|
40
|
+
|
|
41
|
+
## Features
|
|
42
|
+
- Automatically discover TP-Link Kasa/Tapo Devices locally only on your network.
|
|
43
|
+
- Currently Plugs, Power Strips, Wall Switches, Bulbs, and Light Strips are supported by the plug-in and added to Homebridge.
|
|
44
|
+
- This plug-in will by default filter out Native HomeKit/Matter Devices, this can be disabled to implement all supported devices on the network if desired.
|
|
45
|
+
- Change Device States for Plugs, Change Device States for Power Strips, Change Device State and Supports Dimming for Wall Switches, Change Device State and Supports Hue, Saturation, and Value (HSV) and Color Temperature Adjustments, and Dimming for Bulbs and Light Strips that support those options. The KS240 Dual Fan and Light Dimmer Wall Switch is also supported if Native HomeKit/Matter Device Filtering is Disabled.
|
|
46
|
+
- OutletInUse on devices that support energy monitoring can follow real device power usage even when the extra HomeKit energy characteristics are not enabled.
|
|
47
|
+
- Energy monitoring characteristics (Volts, Amperes, Watts, and KiloWattHours) can be enabled separately for supported devices.
|
|
48
|
+
- Supported Devices from the API are listed below, Devices with an asterisks ('*') next to the specific firmware will require the Username and Password for your TP-Link Kasa/Tapo Cloud Account to connect and function correctly. If your device is not listed below, it does not mean it won't work, but there may be issues. </p>*NOTE - Not All Devices Listed Below Are Supported By This Plug-In. These Devices Are Supported By The Python-Kasa API Library And Could Be Supported By The Plug-In In The Future.*</p>
|
|
49
|
+
|
|
50
|
+
## Installation
|
|
51
|
+
- Install from the Homebridge UI or with npm.
|
|
52
|
+
- Most users can install the plug-in, click Save, and restart Homebridge.
|
|
53
|
+
- On first startup, or any time the Python requirements change, the plug-in will verify Python, create/update the virtual environment, and install/update the required Python packages automatically.
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
npm install -g homebridge-kasa-python
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Configuration Notes
|
|
60
|
+
- Enable Credentials only if your devices require authentication. Some newer Kasa/Tapo devices will not work without the TP-Link Kasa/Tapo Cloud Account Username and Password.
|
|
61
|
+
- Manual Devices now only require the device host or IP Address. If discovery is not working, try the Manual Devices List and Additional Broadcast Addresses before assuming a device is unsupported.
|
|
62
|
+
- Hide HomeKit or Matter Devices is enabled by default so supported native HomeKit/Matter devices are not duplicated in Homebridge.
|
|
63
|
+
- Wait Time Update controls how long similar commands are combined before being sent to a device.
|
|
64
|
+
- Advanced Python Logging only shows detailed Python-side logs when Homebridge Debug Mode is enabled.
|
|
65
|
+
|
|
66
|
+
## Energy Notes
|
|
67
|
+
- Enable Energy Monitoring adds the extra HomeKit energy monitoring characteristics (Volts, Amperes, Watts, and KiloWattHours) for supported devices.
|
|
68
|
+
- OutletInUse for devices that support energy reporting is based on the device reported power usage, even when Enable Energy Monitoring is disabled.
|
|
69
|
+
- Outlet In Use Power Threshold defaults to 2 Watts and can be set as low as 0.001 in the configuration.
|
|
70
|
+
- OutletInUse is true when reported power is greater than the configured threshold and false when the reported power is less than or equal to the configured threshold.
|
|
71
|
+
- OutletInUse updates from energy reporting are debounced across two consecutive polling updates to help reduce noise from very small changes in reported power.
|
|
72
|
+
- Log Energy Monitoring Events enables logging of the extra energy characteristics only.
|
|
73
|
+
|
|
74
|
+
## Example Configuration
|
|
75
|
+
```json
|
|
76
|
+
{
|
|
77
|
+
"bridge": {
|
|
78
|
+
"name": "Homebridge",
|
|
79
|
+
"username": "11:22:33:AA:BB:CC",
|
|
80
|
+
"port": 12345,
|
|
81
|
+
"pin": "001-02-003"
|
|
82
|
+
},
|
|
83
|
+
"description": "This is an example configuration file.",
|
|
84
|
+
"platforms": [
|
|
85
|
+
{
|
|
86
|
+
"platform": "KasaPython",
|
|
87
|
+
"name": "KasaPython",
|
|
88
|
+
"enableCredentials": true,
|
|
89
|
+
"username": "Username",
|
|
90
|
+
"password": "Password",
|
|
91
|
+
"enableEnergyMonitoring": true,
|
|
92
|
+
"powerThreshold": 1.0,
|
|
93
|
+
"logEnergyMonitoring": false,
|
|
94
|
+
"hideHomeKitMatter": true,
|
|
95
|
+
"pollingInterval": 5,
|
|
96
|
+
"discoveryPollingInterval": 300,
|
|
97
|
+
"offlineInterval": 7,
|
|
98
|
+
"waitTimeUpdate": 100,
|
|
99
|
+
"pythonPath": "/usr/bin/python3",
|
|
100
|
+
"advancedPythonLogging": false,
|
|
101
|
+
"additionalBroadcasts": [
|
|
102
|
+
"192.168.1.255",
|
|
103
|
+
"192.168.2.255"
|
|
104
|
+
],
|
|
105
|
+
"manualDevices": [
|
|
106
|
+
{
|
|
107
|
+
"host": "192.168.1.100"
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"host": "192.168.2.100"
|
|
111
|
+
}
|
|
112
|
+
],
|
|
113
|
+
"excludeMacAddresses": [
|
|
114
|
+
"AA:BB:CC:11:22:33",
|
|
115
|
+
"CC:BB:AA:33:22:11"
|
|
116
|
+
],
|
|
117
|
+
"includeMacAddresses": [
|
|
118
|
+
"DD:EE:FF:44:55:66",
|
|
119
|
+
"FF:EE:DD:66:55:44"
|
|
120
|
+
]
|
|
121
|
+
}
|
|
122
|
+
],
|
|
123
|
+
"accessories": []
|
|
124
|
+
}
|
|
125
|
+
```
|
|
48
126
|
|
|
49
127
|
## Kasa devices
|
|
50
|
-
|
|
51
128
|
Some newer Kasa devices require authentication. These are marked with [*] in the list below.<br>Hub-Connected Devices may work across TAPO/KASA branded hubs even if they don't work across the native apps.
|
|
52
129
|
|
|
53
130
|
### Plugs
|
|
54
|
-
|
|
55
131
|
- **EP10**
|
|
56
132
|
- Hardware: 1.0 (US) / Firmware: 1.0.2
|
|
57
133
|
- **EP25**
|
|
@@ -91,7 +167,6 @@ Some newer Kasa devices require authentication. These are marked with [*] in the
|
|
|
91
167
|
- Hardware: 1.0 (US) / Firmware: 1.0.0
|
|
92
168
|
|
|
93
169
|
### Power Strips
|
|
94
|
-
|
|
95
170
|
- **EP40**
|
|
96
171
|
- Hardware: 1.0 (US) / Firmware: 1.0.2
|
|
97
172
|
- **EP40M**
|
|
@@ -116,7 +191,6 @@ Some newer Kasa devices require authentication. These are marked with [*] in the
|
|
|
116
191
|
- Hardware: 3.0 (US) / Firmware: 1.0.4
|
|
117
192
|
|
|
118
193
|
### Wall Switches
|
|
119
|
-
|
|
120
194
|
- **ES20M**
|
|
121
195
|
- Hardware: 1.0 (US) / Firmware: 1.0.11
|
|
122
196
|
- Hardware: 1.0 (US) / Firmware: 1.0.8
|
|
@@ -164,7 +238,6 @@ Some newer Kasa devices require authentication. These are marked with [*] in the
|
|
|
164
238
|
- Hardware: 1.0 (US) / Firmware: 1.0.7[*]
|
|
165
239
|
|
|
166
240
|
### Bulbs
|
|
167
|
-
|
|
168
241
|
- **KL110**
|
|
169
242
|
- Hardware: 1.0 (US) / Firmware: 1.8.11
|
|
170
243
|
- **KL110B**
|
|
@@ -195,7 +268,6 @@ Some newer Kasa devices require authentication. These are marked with [*] in the
|
|
|
195
268
|
- Hardware: 1.0 (US) / Firmware: 1.8.11
|
|
196
269
|
|
|
197
270
|
### Light Strips
|
|
198
|
-
|
|
199
271
|
- **KL400L5**
|
|
200
272
|
- Hardware: 1.0 (US) / Firmware: 1.0.5
|
|
201
273
|
- Hardware: 1.0 (US) / Firmware: 1.0.8
|
|
@@ -211,26 +283,21 @@ Some newer Kasa devices require authentication. These are marked with [*] in the
|
|
|
211
283
|
- Hardware: 2.0 (US) / Firmware: 1.0.9
|
|
212
284
|
|
|
213
285
|
### Hubs
|
|
214
|
-
|
|
215
286
|
- **KH100**
|
|
216
287
|
- Hardware: 1.0 (EU) / Firmware: 1.2.3[*]
|
|
217
288
|
- Hardware: 1.0 (EU) / Firmware: 1.5.12[*]
|
|
218
289
|
- Hardware: 1.0 (UK) / Firmware: 1.5.6[*]
|
|
219
290
|
|
|
220
291
|
### Hub-Connected Devices
|
|
221
|
-
|
|
222
292
|
- **KE100**
|
|
223
293
|
- Hardware: 1.0 (EU) / Firmware: 2.4.0[*]
|
|
224
294
|
- Hardware: 1.0 (EU) / Firmware: 2.8.0[*]
|
|
225
295
|
- Hardware: 1.0 (UK) / Firmware: 2.8.0[*]
|
|
226
296
|
|
|
227
|
-
|
|
228
297
|
## Tapo devices
|
|
229
|
-
|
|
230
298
|
All Tapo devices require authentication.<br>Hub-Connected Devices may work across TAPO/KASA branded hubs even if they don't work across the native apps.
|
|
231
299
|
|
|
232
300
|
### Plugs
|
|
233
|
-
|
|
234
301
|
- **P100**
|
|
235
302
|
- Hardware: 1.0.0 (US) / Firmware: 1.1.3
|
|
236
303
|
- Hardware: 1.0.0 (US) / Firmware: 1.3.7
|
|
@@ -259,7 +326,6 @@ All Tapo devices require authentication.<br>Hub-Connected Devices may work acros
|
|
|
259
326
|
- Hardware: 1.0 (US) / Firmware: 1.0.3
|
|
260
327
|
|
|
261
328
|
### Power Strips
|
|
262
|
-
|
|
263
329
|
- **P210M**
|
|
264
330
|
- Hardware: 1.0 (US) / Firmware: 1.0.3
|
|
265
331
|
- **P300**
|
|
@@ -277,7 +343,6 @@ All Tapo devices require authentication.<br>Hub-Connected Devices may work acros
|
|
|
277
343
|
- Hardware: 1.0 (US) / Firmware: 1.0.2
|
|
278
344
|
|
|
279
345
|
### Wall Switches
|
|
280
|
-
|
|
281
346
|
- **S210**
|
|
282
347
|
- Hardware: 1.0 (EU) / Firmware: 1.9.0
|
|
283
348
|
- **S220**
|
|
@@ -296,7 +361,6 @@ All Tapo devices require authentication.<br>Hub-Connected Devices may work acros
|
|
|
296
361
|
- Hardware: 1.0 (US) / Firmware: 1.2.2
|
|
297
362
|
|
|
298
363
|
### Bulbs
|
|
299
|
-
|
|
300
364
|
- **L430C**
|
|
301
365
|
- Hardware: 1.0 (EU) / Firmware: 1.0.4
|
|
302
366
|
- **L430P**
|
|
@@ -320,7 +384,6 @@ All Tapo devices require authentication.<br>Hub-Connected Devices may work acros
|
|
|
320
384
|
- Hardware: 1.0 (EU) / Firmware: 1.1.2
|
|
321
385
|
|
|
322
386
|
### Light Strips
|
|
323
|
-
|
|
324
387
|
- **L900-10**
|
|
325
388
|
- Hardware: 1.0 (EU) / Firmware: 1.0.17
|
|
326
389
|
- Hardware: 1.0 (US) / Firmware: 1.0.11
|
|
@@ -337,7 +400,6 @@ All Tapo devices require authentication.<br>Hub-Connected Devices may work acros
|
|
|
337
400
|
- Hardware: 1.0 (US) / Firmware: 1.1.2
|
|
338
401
|
|
|
339
402
|
### Cameras
|
|
340
|
-
|
|
341
403
|
- **C100**
|
|
342
404
|
- Hardware: 4.0 / Firmware: 1.3.14
|
|
343
405
|
- **C110**
|
|
@@ -363,7 +425,6 @@ All Tapo devices require authentication.<br>Hub-Connected Devices may work acros
|
|
|
363
425
|
- Hardware: 3.0 / Firmware: 1.3.11
|
|
364
426
|
|
|
365
427
|
### Doorbells and chimes
|
|
366
|
-
|
|
367
428
|
- **D100C**
|
|
368
429
|
- Hardware: 1.0 (US) / Firmware: 1.1.3
|
|
369
430
|
- **D130**
|
|
@@ -372,14 +433,12 @@ All Tapo devices require authentication.<br>Hub-Connected Devices may work acros
|
|
|
372
433
|
- Hardware: 1.20 (EU) / Firmware: 1.1.19
|
|
373
434
|
|
|
374
435
|
### Vacuums
|
|
375
|
-
|
|
376
436
|
- **RV20 Max Plus**
|
|
377
437
|
- Hardware: 1.0 (EU) / Firmware: 1.0.7
|
|
378
438
|
- **RV30 Max**
|
|
379
439
|
- Hardware: 1.0 (US) / Firmware: 1.2.0
|
|
380
440
|
|
|
381
441
|
### Hubs
|
|
382
|
-
|
|
383
442
|
- **H100**
|
|
384
443
|
- Hardware: 1.0 (AU) / Firmware: 1.5.23
|
|
385
444
|
- Hardware: 1.0 (EU) / Firmware: 1.2.3
|
|
@@ -391,7 +450,6 @@ All Tapo devices require authentication.<br>Hub-Connected Devices may work acros
|
|
|
391
450
|
- Hardware: 1.0 (US) / Firmware: 1.3.6
|
|
392
451
|
|
|
393
452
|
### Hub-Connected Devices
|
|
394
|
-
|
|
395
453
|
- **S200B**
|
|
396
454
|
- Hardware: 1.0 (EU) / Firmware: 1.11.0
|
|
397
455
|
- Hardware: 1.0 (US) / Firmware: 1.12.0
|
|
@@ -414,5 +472,5 @@ All Tapo devices require authentication.<br>Hub-Connected Devices may work acros
|
|
|
414
472
|
- Hardware: 1.0 (EU) / Firmware: 1.7.0
|
|
415
473
|
- Hardware: 1.0 (US) / Firmware: 1.8.0
|
|
416
474
|
|
|
417
|
-
|
|
418
|
-
-
|
|
475
|
+
## Credits
|
|
476
|
+
- Huge thanks to rytilahti and all the developers at python-kasa for the [Python-Kasa API](https://github.com/python-kasa/python-kasa), plasticrake for the [Unofficial API documentation](https://github.com/plasticrake/tplink-smarthome-api), and maxileith for [Excellent Python Implementation](https://github.com/maxileith/homebridge-appletv-enhanced).
|
package/config.schema.json
CHANGED
|
@@ -47,6 +47,19 @@
|
|
|
47
47
|
"description": "Enable to add energy monitoring characteristics (Volts, Amperes, Watts, KiloWattHours) to supported devices.",
|
|
48
48
|
"default": false
|
|
49
49
|
},
|
|
50
|
+
"powerThreshold": {
|
|
51
|
+
"title": "Outlet In Use Power Threshold (Watts)",
|
|
52
|
+
"type": "number",
|
|
53
|
+
"description": "Power threshold used to mark OutletInUse as true on devices with energy reporting support. Values must be at least 0.001 and may use up to three decimal places.",
|
|
54
|
+
"default": 2,
|
|
55
|
+
"minimum": 0.001
|
|
56
|
+
},
|
|
57
|
+
"logEnergyMonitoring": {
|
|
58
|
+
"title": "Log Energy Monitoring Events",
|
|
59
|
+
"type": "boolean",
|
|
60
|
+
"description": "Enable logging of power/energy monitoring characteristic updates (Volts, Amperes, Watts, KiloWattHours).",
|
|
61
|
+
"default": false
|
|
62
|
+
},
|
|
50
63
|
"hideHomeKitMatter": {
|
|
51
64
|
"title": "Hide HomeKit or Matter Devices",
|
|
52
65
|
"type": "boolean",
|
|
@@ -90,14 +103,6 @@
|
|
|
90
103
|
"host": {
|
|
91
104
|
"type": "string",
|
|
92
105
|
"title": "Host"
|
|
93
|
-
},
|
|
94
|
-
"alias": {
|
|
95
|
-
"type": "string",
|
|
96
|
-
"title": "Alias",
|
|
97
|
-
"readonly": true,
|
|
98
|
-
"condition": {
|
|
99
|
-
"functionBody": "return model.manualDevices && model.manualDevices[arrayIndices] && model.manualDevices[arrayIndices].host && model.manualDevices[arrayIndices].host !== '';"
|
|
100
|
-
}
|
|
101
106
|
}
|
|
102
107
|
}
|
|
103
108
|
},
|
|
@@ -139,12 +144,6 @@
|
|
|
139
144
|
"type": "boolean",
|
|
140
145
|
"description": "Enable detailed logging for Python scripts. Only shows logs when Debug Mode in Homebridge is enabled.",
|
|
141
146
|
"default": false
|
|
142
|
-
},
|
|
143
|
-
"logEnergyMonitoring": {
|
|
144
|
-
"title": "Log Energy Monitoring Events",
|
|
145
|
-
"type": "boolean",
|
|
146
|
-
"description": "Enable logging of power/energy monitoring characteristic updates (Volts, Amperes, Watts, KiloWattHours).",
|
|
147
|
-
"default": false
|
|
148
147
|
}
|
|
149
148
|
}
|
|
150
149
|
},
|
|
@@ -165,7 +164,17 @@
|
|
|
165
164
|
"type": "help",
|
|
166
165
|
"helpvalue": "Username and Password will be required for specific devices only."
|
|
167
166
|
},
|
|
168
|
-
|
|
167
|
+
{
|
|
168
|
+
"type": "fieldset",
|
|
169
|
+
"title": "Energy (Optional)",
|
|
170
|
+
"description": "Configure energy-based Outlet In Use handling and optional energy characteristics.",
|
|
171
|
+
"expandable": true,
|
|
172
|
+
"items": [
|
|
173
|
+
"enableEnergyMonitoring",
|
|
174
|
+
"powerThreshold",
|
|
175
|
+
"logEnergyMonitoring"
|
|
176
|
+
]
|
|
177
|
+
},
|
|
169
178
|
{
|
|
170
179
|
"type": "fieldset",
|
|
171
180
|
"title": "HomeKit (Optional)",
|
|
@@ -210,8 +219,7 @@
|
|
|
210
219
|
"key": "manualDevices",
|
|
211
220
|
"type": "array",
|
|
212
221
|
"items": [
|
|
213
|
-
"manualDevices[].host"
|
|
214
|
-
"manualDevices[].alias"
|
|
222
|
+
"manualDevices[].host"
|
|
215
223
|
]
|
|
216
224
|
},
|
|
217
225
|
{
|
|
@@ -262,8 +270,7 @@
|
|
|
262
270
|
"items": [
|
|
263
271
|
"waitTimeUpdate",
|
|
264
272
|
"pythonPath",
|
|
265
|
-
"advancedPythonLogging"
|
|
266
|
-
"logEnergyMonitoring"
|
|
273
|
+
"advancedPythonLogging"
|
|
267
274
|
]
|
|
268
275
|
}
|
|
269
276
|
]
|
package/dist/config.d.ts
CHANGED
|
@@ -11,6 +11,8 @@ export interface KasaPythonConfigInput {
|
|
|
11
11
|
username?: string;
|
|
12
12
|
password?: string;
|
|
13
13
|
enableEnergyMonitoring?: boolean;
|
|
14
|
+
powerThreshold?: number;
|
|
15
|
+
logEnergyMonitoring?: boolean;
|
|
14
16
|
hideHomeKitMatter?: boolean;
|
|
15
17
|
pollingInterval?: number;
|
|
16
18
|
discoveryPollingInterval?: number;
|
|
@@ -22,14 +24,17 @@ export interface KasaPythonConfigInput {
|
|
|
22
24
|
waitTimeUpdate?: number;
|
|
23
25
|
pythonPath?: string;
|
|
24
26
|
advancedPythonLogging?: boolean;
|
|
25
|
-
logEnergyMonitoring?: boolean;
|
|
26
27
|
}
|
|
27
28
|
export type KasaPythonConfig = {
|
|
28
29
|
name: string;
|
|
29
30
|
enableCredentials: boolean;
|
|
30
31
|
username: string;
|
|
31
32
|
password: string;
|
|
32
|
-
|
|
33
|
+
energyOptions: {
|
|
34
|
+
enableEnergyMonitoring: boolean;
|
|
35
|
+
powerThreshold: number;
|
|
36
|
+
logEnergyMonitoring: boolean;
|
|
37
|
+
};
|
|
33
38
|
homekitOptions: {
|
|
34
39
|
hideHomeKitMatter: boolean;
|
|
35
40
|
};
|
|
@@ -46,14 +51,7 @@ export type KasaPythonConfig = {
|
|
|
46
51
|
waitTimeUpdate: number;
|
|
47
52
|
pythonPath?: string;
|
|
48
53
|
advancedPythonLogging: boolean;
|
|
49
|
-
logEnergyMonitoring: boolean;
|
|
50
54
|
};
|
|
51
55
|
};
|
|
52
56
|
export declare const defaultConfig: KasaPythonConfig;
|
|
53
|
-
export declare function migrateManualDevices(manualDevices: (string | ConfigDevice)[] | undefined | null): {
|
|
54
|
-
manualDevices: ConfigDevice[];
|
|
55
|
-
changed: boolean;
|
|
56
|
-
};
|
|
57
57
|
export declare function parseConfig(config: Record<string, unknown>): KasaPythonConfig;
|
|
58
|
-
export declare function loadPlatformConfigFromStorage(storagePath: string): Promise<KasaPythonConfig>;
|
|
59
|
-
export declare function persistDiscoveredAliases(storagePath: string, aliasesByHost: Map<string, string>): Promise<KasaPythonConfig | undefined>;
|
package/dist/config.js
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import fs from 'node:fs/promises';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { PLATFORM_NAME } from './settings.js';
|
|
4
1
|
import { isObjectLike } from './utils.js';
|
|
5
2
|
export class ConfigParseError extends Error {
|
|
6
3
|
errors;
|
|
@@ -33,7 +30,11 @@ export const defaultConfig = {
|
|
|
33
30
|
enableCredentials: false,
|
|
34
31
|
username: '',
|
|
35
32
|
password: '',
|
|
36
|
-
|
|
33
|
+
energyOptions: {
|
|
34
|
+
enableEnergyMonitoring: false,
|
|
35
|
+
powerThreshold: 2,
|
|
36
|
+
logEnergyMonitoring: false,
|
|
37
|
+
},
|
|
37
38
|
homekitOptions: {
|
|
38
39
|
hideHomeKitMatter: true,
|
|
39
40
|
},
|
|
@@ -50,34 +51,25 @@ export const defaultConfig = {
|
|
|
50
51
|
waitTimeUpdate: 100,
|
|
51
52
|
pythonPath: '',
|
|
52
53
|
advancedPythonLogging: false,
|
|
53
|
-
logEnergyMonitoring: false,
|
|
54
54
|
},
|
|
55
55
|
};
|
|
56
|
-
const MISSING_ALIAS_PLACEHOLDER = 'Will Be Filled By Plug-In Automatically';
|
|
57
56
|
function isNonEmptyString(value) {
|
|
58
57
|
return typeof value === 'string' && value.trim().length > 0;
|
|
59
58
|
}
|
|
60
|
-
|
|
59
|
+
function normalizeManualDevices(manualDevices) {
|
|
61
60
|
if (!manualDevices || manualDevices.length === 0) {
|
|
62
|
-
return
|
|
61
|
+
return [];
|
|
63
62
|
}
|
|
64
|
-
|
|
65
|
-
const migratedDevices = manualDevices.map(device => {
|
|
63
|
+
return manualDevices.flatMap(device => {
|
|
66
64
|
if (typeof device === 'string') {
|
|
67
|
-
|
|
68
|
-
return { host: device, alias: MISSING_ALIAS_PLACEHOLDER };
|
|
65
|
+
return isNonEmptyString(device) ? [{ host: device }] : [];
|
|
69
66
|
}
|
|
70
67
|
const migratedDevice = device;
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
changed = true;
|
|
68
|
+
if (!isNonEmptyString(migratedDevice.host)) {
|
|
69
|
+
return [];
|
|
74
70
|
}
|
|
75
|
-
return {
|
|
76
|
-
host: migratedDevice.host,
|
|
77
|
-
alias,
|
|
78
|
-
};
|
|
71
|
+
return [{ host: migratedDevice.host }];
|
|
79
72
|
});
|
|
80
|
-
return { manualDevices: migratedDevices, changed };
|
|
81
73
|
}
|
|
82
74
|
function validateManualDevices(manualDevices, errors) {
|
|
83
75
|
if (manualDevices === undefined) {
|
|
@@ -92,9 +84,6 @@ function validateManualDevices(manualDevices, errors) {
|
|
|
92
84
|
if (!isNonEmptyString(entry)) {
|
|
93
85
|
errors.push(`\`manualDevices[${index}]\` should not be an empty string.`);
|
|
94
86
|
}
|
|
95
|
-
else {
|
|
96
|
-
errors.push(`\`manualDevices[${index}]\` should be an object with \`host\` and \`alias\`.`);
|
|
97
|
-
}
|
|
98
87
|
return;
|
|
99
88
|
}
|
|
100
89
|
if (!isObjectLike(entry)) {
|
|
@@ -105,12 +94,6 @@ function validateManualDevices(manualDevices, errors) {
|
|
|
105
94
|
if (!isNonEmptyString(device.host)) {
|
|
106
95
|
errors.push(`\`manualDevices[${index}].host\` should be a non-empty string.`);
|
|
107
96
|
}
|
|
108
|
-
if (!isNonEmptyString(device.alias)) {
|
|
109
|
-
errors.push(`\`manualDevices[${index}].alias\` should be a non-empty string.`);
|
|
110
|
-
}
|
|
111
|
-
if ('breakoutChildDevices' in device) {
|
|
112
|
-
errors.push(`\`manualDevices[${index}]\` uses unsupported legacy field \`breakoutChildDevices\`.`);
|
|
113
|
-
}
|
|
114
97
|
});
|
|
115
98
|
}
|
|
116
99
|
function validateConfig(config) {
|
|
@@ -120,6 +103,8 @@ function validateConfig(config) {
|
|
|
120
103
|
validateType(config, 'username', 'string', errors);
|
|
121
104
|
validateType(config, 'password', 'string', errors);
|
|
122
105
|
validateType(config, 'enableEnergyMonitoring', 'boolean', errors);
|
|
106
|
+
validateType(config, 'powerThreshold', 'number', errors);
|
|
107
|
+
validateType(config, 'logEnergyMonitoring', 'boolean', errors);
|
|
123
108
|
validateType(config, 'hideHomeKitMatter', 'boolean', errors);
|
|
124
109
|
validateType(config, 'pollingInterval', 'number', errors);
|
|
125
110
|
validateType(config, 'discoveryPollingInterval', 'number', errors);
|
|
@@ -137,31 +122,49 @@ function validateConfig(config) {
|
|
|
137
122
|
validateType(config, 'waitTimeUpdate', 'number', errors);
|
|
138
123
|
validateType(config, 'pythonPath', 'string', errors);
|
|
139
124
|
validateType(config, 'advancedPythonLogging', 'boolean', errors);
|
|
140
|
-
|
|
125
|
+
validatePowerThreshold(config.powerThreshold, errors);
|
|
141
126
|
return errors;
|
|
142
127
|
}
|
|
128
|
+
function validatePowerThreshold(value, errors) {
|
|
129
|
+
if (value === undefined) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (typeof value !== 'number' || Number.isNaN(value)) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
if (value < 0.001) {
|
|
136
|
+
errors.push('`powerThreshold` should be at least 0.001.');
|
|
137
|
+
}
|
|
138
|
+
if (Math.abs(Math.round(value * 1000) - (value * 1000)) > Number.EPSILON * 1000) {
|
|
139
|
+
errors.push('`powerThreshold` should use no more than three decimal places.');
|
|
140
|
+
}
|
|
141
|
+
}
|
|
143
142
|
function validateType(config, key, expectedType, errors) {
|
|
144
143
|
if (config[key] !== undefined && typeof config[key] !== expectedType) {
|
|
145
144
|
errors.push(`\`${key}\` should be a ${expectedType}.`);
|
|
146
145
|
}
|
|
147
146
|
}
|
|
148
147
|
export function parseConfig(config) {
|
|
148
|
+
if (!isObjectLike(config)) {
|
|
149
|
+
throw new ConfigParseError('Error parsing config');
|
|
150
|
+
}
|
|
149
151
|
const errors = validateConfig(config);
|
|
150
152
|
if (errors.length > 0) {
|
|
151
153
|
throw new ConfigParseError('Error parsing config', errors);
|
|
152
154
|
}
|
|
153
|
-
if (!isObjectLike(config)) {
|
|
154
|
-
throw new ConfigParseError('Error parsing config');
|
|
155
|
-
}
|
|
156
155
|
const parsedConfig = { ...defaultConfig, ...config };
|
|
157
|
-
const
|
|
156
|
+
const normalizedManualDevices = normalizeManualDevices(parsedConfig.manualDevices)
|
|
158
157
|
?? defaultConfig.discoveryOptions.manualDevices;
|
|
159
158
|
return {
|
|
160
159
|
name: parsedConfig.name ?? defaultConfig.name,
|
|
161
160
|
enableCredentials: parsedConfig.enableCredentials ?? defaultConfig.enableCredentials,
|
|
162
161
|
username: parsedConfig.username ?? defaultConfig.username,
|
|
163
162
|
password: parsedConfig.password ?? defaultConfig.password,
|
|
164
|
-
|
|
163
|
+
energyOptions: {
|
|
164
|
+
enableEnergyMonitoring: parsedConfig.enableEnergyMonitoring ?? defaultConfig.energyOptions.enableEnergyMonitoring,
|
|
165
|
+
powerThreshold: parsedConfig.powerThreshold ?? defaultConfig.energyOptions.powerThreshold,
|
|
166
|
+
logEnergyMonitoring: parsedConfig.logEnergyMonitoring ?? defaultConfig.energyOptions.logEnergyMonitoring,
|
|
167
|
+
},
|
|
165
168
|
homekitOptions: {
|
|
166
169
|
hideHomeKitMatter: parsedConfig.hideHomeKitMatter ?? defaultConfig.homekitOptions.hideHomeKitMatter,
|
|
167
170
|
},
|
|
@@ -170,7 +173,7 @@ export function parseConfig(config) {
|
|
|
170
173
|
discoveryPollingInterval: (parsedConfig.discoveryPollingInterval ?? defaultConfig.discoveryOptions.discoveryPollingInterval) * 1000,
|
|
171
174
|
offlineInterval: (parsedConfig.offlineInterval ?? defaultConfig.discoveryOptions.offlineInterval) * 24 * 60 * 60 * 1000,
|
|
172
175
|
additionalBroadcasts: parsedConfig.additionalBroadcasts ?? defaultConfig.discoveryOptions.additionalBroadcasts,
|
|
173
|
-
manualDevices:
|
|
176
|
+
manualDevices: normalizedManualDevices,
|
|
174
177
|
excludeMacAddresses: parsedConfig.excludeMacAddresses ?? defaultConfig.discoveryOptions.excludeMacAddresses,
|
|
175
178
|
includeMacAddresses: parsedConfig.includeMacAddresses ?? defaultConfig.discoveryOptions.includeMacAddresses,
|
|
176
179
|
},
|
|
@@ -178,70 +181,7 @@ export function parseConfig(config) {
|
|
|
178
181
|
waitTimeUpdate: parsedConfig.waitTimeUpdate ?? defaultConfig.advancedOptions.waitTimeUpdate,
|
|
179
182
|
pythonPath: parsedConfig.pythonPath ?? defaultConfig.advancedOptions.pythonPath,
|
|
180
183
|
advancedPythonLogging: parsedConfig.advancedPythonLogging ?? defaultConfig.advancedOptions.advancedPythonLogging,
|
|
181
|
-
logEnergyMonitoring: parsedConfig.logEnergyMonitoring ?? defaultConfig.advancedOptions.logEnergyMonitoring,
|
|
182
184
|
},
|
|
183
185
|
};
|
|
184
186
|
}
|
|
185
|
-
function getConfigPath(storagePath) {
|
|
186
|
-
return path.join(storagePath, 'config.json');
|
|
187
|
-
}
|
|
188
|
-
async function readHomebridgeConfig(storagePath) {
|
|
189
|
-
const data = await fs.readFile(getConfigPath(storagePath), 'utf8');
|
|
190
|
-
return JSON.parse(data);
|
|
191
|
-
}
|
|
192
|
-
async function writeHomebridgeConfig(storagePath, fileConfig) {
|
|
193
|
-
await fs.writeFile(getConfigPath(storagePath), JSON.stringify(fileConfig, null, 2), 'utf8');
|
|
194
|
-
}
|
|
195
|
-
function getPlatformSection(fileConfig, platformName) {
|
|
196
|
-
return fileConfig.platforms?.find(platformConfig => platformConfig.platform === platformName);
|
|
197
|
-
}
|
|
198
|
-
export async function loadPlatformConfigFromStorage(storagePath) {
|
|
199
|
-
const fileConfig = await readHomebridgeConfig(storagePath);
|
|
200
|
-
const platformSection = getPlatformSection(fileConfig, PLATFORM_NAME);
|
|
201
|
-
if (!platformSection) {
|
|
202
|
-
throw new ConfigParseError('KasaPython configuration missing in config file.');
|
|
203
|
-
}
|
|
204
|
-
const { manualDevices, changed } = migrateManualDevices(platformSection.manualDevices);
|
|
205
|
-
if (platformSection.manualDevices !== undefined) {
|
|
206
|
-
platformSection.manualDevices = manualDevices;
|
|
207
|
-
}
|
|
208
|
-
const parsedConfig = parseConfig(platformSection);
|
|
209
|
-
if (changed) {
|
|
210
|
-
await writeHomebridgeConfig(storagePath, fileConfig);
|
|
211
|
-
}
|
|
212
|
-
return parsedConfig;
|
|
213
|
-
}
|
|
214
|
-
export async function persistDiscoveredAliases(storagePath, aliasesByHost) {
|
|
215
|
-
if (aliasesByHost.size === 0) {
|
|
216
|
-
return undefined;
|
|
217
|
-
}
|
|
218
|
-
const fileConfig = await readHomebridgeConfig(storagePath);
|
|
219
|
-
const platformSection = getPlatformSection(fileConfig, PLATFORM_NAME);
|
|
220
|
-
if (!platformSection) {
|
|
221
|
-
throw new ConfigParseError('KasaPython configuration missing in config file.');
|
|
222
|
-
}
|
|
223
|
-
const manualDevices = platformSection.manualDevices;
|
|
224
|
-
if (!Array.isArray(manualDevices) || manualDevices.length === 0) {
|
|
225
|
-
return undefined;
|
|
226
|
-
}
|
|
227
|
-
let changed = false;
|
|
228
|
-
for (const entry of manualDevices) {
|
|
229
|
-
if (!isObjectLike(entry)) {
|
|
230
|
-
continue;
|
|
231
|
-
}
|
|
232
|
-
const device = entry;
|
|
233
|
-
const host = typeof device.host === 'string' ? device.host : undefined;
|
|
234
|
-
const nextAlias = host ? aliasesByHost.get(host) : undefined;
|
|
235
|
-
if (!nextAlias || device.alias === nextAlias) {
|
|
236
|
-
continue;
|
|
237
|
-
}
|
|
238
|
-
device.alias = nextAlias;
|
|
239
|
-
changed = true;
|
|
240
|
-
}
|
|
241
|
-
if (!changed) {
|
|
242
|
-
return undefined;
|
|
243
|
-
}
|
|
244
|
-
await writeHomebridgeConfig(storagePath, fileConfig);
|
|
245
|
-
return parseConfig(platformSection);
|
|
246
|
-
}
|
|
247
187
|
//# sourceMappingURL=config.js.map
|