homebridge-kasa-python 2.7.0-beta.0 → 2.7.0-beta.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/README.md +159 -28
- package/dist/devices/create.js +14 -7
- package/dist/devices/create.js.map +1 -1
- package/dist/devices/homekitLightBulb.d.ts +12 -0
- package/dist/devices/homekitLightBulb.js +37 -0
- package/dist/devices/homekitLightBulb.js.map +1 -0
- package/dist/devices/{homekitPowerstrip.d.ts → homekitPowerStrip.d.ts} +3 -3
- package/dist/devices/{homekitPowerstrip.js → homekitPowerStrip.js} +1 -1
- package/dist/devices/{homekitPowerstrip.js.map → homekitPowerStrip.js.map} +1 -1
- package/dist/devices/kasaDevices.d.ts +10 -7
- package/dist/devices/kasaDevices.js +2 -8
- package/dist/devices/kasaDevices.js.map +1 -1
- package/dist/platform.js +2 -2
- package/dist/platform.js.map +1 -1
- package/dist/python/kasaApi.py +215 -177
- package/dist/python/pythonChecker.js +1 -1
- package/dist/python/pythonChecker.js.map +1 -1
- package/package.json +13 -11
- package/requirements.txt +3 -5
package/dist/python/kasaApi.py
CHANGED
|
@@ -1,25 +1,44 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import asyncio
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
import uvicorn
|
|
5
|
+
from typing import Any, Dict, List, Optional
|
|
6
|
+
|
|
7
|
+
from aiohttp import ClientSession
|
|
8
|
+
from kasa import Credentials, Device, Discover, Module, UnsupportedDeviceError
|
|
5
9
|
from loguru import logger
|
|
10
|
+
from quart import Quart, jsonify, request
|
|
6
11
|
|
|
7
|
-
app =
|
|
8
|
-
socketio = SocketIO(app, cors_allowed_origins="*")
|
|
12
|
+
app = Quart(__name__)
|
|
9
13
|
|
|
10
14
|
logging_server_url = os.getenv('LOGGING_SERVER_URL')
|
|
11
15
|
|
|
12
16
|
class RemoteLogger:
|
|
13
|
-
def __init__(self, url):
|
|
17
|
+
def __init__(self, url: str):
|
|
14
18
|
self.url = url
|
|
15
19
|
|
|
16
|
-
def write(self, message):
|
|
20
|
+
async def write(self, message: str):
|
|
17
21
|
if message.strip():
|
|
18
|
-
|
|
22
|
+
async with ClientSession() as session:
|
|
23
|
+
await session.post(self.url, json={"level": "debug", "message": message.strip()})
|
|
19
24
|
|
|
20
|
-
|
|
25
|
+
class AsyncLogHandler:
|
|
26
|
+
def __init__(self, logger_instance, url):
|
|
27
|
+
self.logger_instance = logger_instance
|
|
28
|
+
self.remote_logger = RemoteLogger(url)
|
|
21
29
|
|
|
22
|
-
|
|
30
|
+
async def async_log_writer(self, message: str):
|
|
31
|
+
await self.remote_logger.write(message)
|
|
32
|
+
|
|
33
|
+
def __call__(self, message: str):
|
|
34
|
+
loop = asyncio.get_event_loop()
|
|
35
|
+
if loop.is_running():
|
|
36
|
+
loop.create_task(self.async_log_writer(message))
|
|
37
|
+
else:
|
|
38
|
+
asyncio.run(self.async_log_writer(message))
|
|
39
|
+
|
|
40
|
+
async_log_handler = AsyncLogHandler(logger, logging_server_url)
|
|
41
|
+
logger.add(async_log_handler, level="DEBUG")
|
|
23
42
|
|
|
24
43
|
UNSUPPORTED_TYPES = {
|
|
25
44
|
'SMART.IPCAMERA',
|
|
@@ -27,62 +46,66 @@ UNSUPPORTED_TYPES = {
|
|
|
27
46
|
'SMART.TAPOHUB'
|
|
28
47
|
}
|
|
29
48
|
|
|
30
|
-
def
|
|
31
|
-
|
|
49
|
+
def get_light_info(light_module: Module) -> Dict[str, Any]:
|
|
50
|
+
light_info = {}
|
|
51
|
+
if light_module.is_dimmable:
|
|
52
|
+
light_info["brightness"] = light_module.brightness
|
|
53
|
+
if light_module.is_variable_color_temp:
|
|
54
|
+
light_info["color_temp"] = light_module.color_temp
|
|
55
|
+
if light_module.is_color:
|
|
56
|
+
hue, saturation, _ = light_module.hsv
|
|
57
|
+
light_info["hsv"] = {"hue": hue, "saturation": saturation}
|
|
58
|
+
return light_info
|
|
59
|
+
|
|
60
|
+
def serialize_child(child: Device) -> Dict[str, Any]:
|
|
61
|
+
child_info = {
|
|
32
62
|
"alias": child.alias,
|
|
33
|
-
**({"brightness": getattr(child.modules[Module.Light], "brightness")} if Module.Light in child.modules else {}),
|
|
34
|
-
**({"color_temp": getattr(child.modules[Module.Light], "color_temp")} if Module.Light in child.modules else {}),
|
|
35
|
-
**({"hsv": {
|
|
36
|
-
"hue": getattr(child.modules[Module.Light], "hsv")[0],
|
|
37
|
-
"saturation": getattr(child.modules[Module.Light], "hsv")[1]
|
|
38
|
-
}} if Module.Light in child.modules else {}),
|
|
39
63
|
"id": child.device_id.split("_", 1)[1],
|
|
40
64
|
"state": child.features["state"].value
|
|
41
65
|
}
|
|
66
|
+
light_module = child.modules.get(Module.Light)
|
|
67
|
+
if light_module:
|
|
68
|
+
child_info.update(get_light_info(light_module))
|
|
69
|
+
return child_info
|
|
42
70
|
|
|
43
|
-
def custom_sysinfo_config_serializer(device: Device):
|
|
44
|
-
|
|
45
|
-
|
|
71
|
+
def custom_sysinfo_config_serializer(device: Device) -> Dict[str, Any]:
|
|
72
|
+
logger.debug(f"Serializing device: {device.host}")
|
|
46
73
|
child_num = len(device.children) if device.children else 0
|
|
47
74
|
|
|
48
75
|
sys_info = {
|
|
49
|
-
"alias": device.alias,
|
|
76
|
+
"alias": device.alias or f'{device.device_type}_{device.host}',
|
|
50
77
|
"child_num": child_num,
|
|
51
78
|
"device_id": device.sys_info.get("deviceId") or device.sys_info.get("device_id"),
|
|
52
79
|
"device_type": device.config.connection_type.device_family.value,
|
|
53
80
|
"host": device.host,
|
|
54
81
|
"hw_ver": device.hw_info["hw_ver"],
|
|
55
|
-
"mac": device.
|
|
56
|
-
"sw_ver": device.
|
|
82
|
+
"mac": device.mac,
|
|
83
|
+
"sw_ver": device.hw_info["sw_ver"],
|
|
57
84
|
}
|
|
58
85
|
|
|
59
86
|
if child_num > 0:
|
|
60
87
|
sys_info["children"] = [serialize_child(child) for child in device.children]
|
|
61
88
|
else:
|
|
62
89
|
sys_info.update({
|
|
63
|
-
"state": device.features["state"].value
|
|
64
|
-
**({"brightness": getattr(device.modules[Module.Light], "brightness")} if Module.Light in device.modules else {}),
|
|
65
|
-
**({"color_temp": getattr(device.modules[Module.Light], "color_temp")} if Module.Light in device.modules else {}),
|
|
66
|
-
**({"hsv": {
|
|
67
|
-
"hue": getattr(device.modules[Module.Light], "hsv")[0],
|
|
68
|
-
"saturation": getattr(device.modules[Module.Light], "hsv")[1]
|
|
69
|
-
}} if Module.Light in device.modules else {})
|
|
90
|
+
"state": device.features["state"].value
|
|
70
91
|
})
|
|
92
|
+
light_module = device.modules.get(Module.Light)
|
|
93
|
+
if light_module:
|
|
94
|
+
sys_info.update(get_light_info(light_module))
|
|
71
95
|
|
|
72
96
|
device_config = {
|
|
73
97
|
"host": device.config.host,
|
|
74
98
|
"timeout": device.config.timeout,
|
|
99
|
+
"uses_http": device.config.uses_http,
|
|
75
100
|
**({"credentials": {
|
|
76
101
|
"username": device.config.credentials.username,
|
|
77
102
|
"password": device.config.credentials.password
|
|
78
|
-
}} if device.config.credentials else {
|
|
79
|
-
}),
|
|
103
|
+
}} if device.config.credentials else {}),
|
|
80
104
|
"connection_type": {
|
|
81
105
|
"device_family": device.config.connection_type.device_family.value,
|
|
82
106
|
"encryption_type": device.config.connection_type.encryption_type.value,
|
|
83
107
|
"https": device.config.connection_type.https
|
|
84
|
-
}
|
|
85
|
-
"uses_http": device.config.uses_http
|
|
108
|
+
}
|
|
86
109
|
}
|
|
87
110
|
|
|
88
111
|
return {
|
|
@@ -90,26 +113,33 @@ def custom_sysinfo_config_serializer(device: Device):
|
|
|
90
113
|
"device_config": device_config
|
|
91
114
|
}
|
|
92
115
|
|
|
93
|
-
def custom_discovery_feature_serializer(device: Device):
|
|
94
|
-
|
|
116
|
+
def custom_discovery_feature_serializer(device: Device) -> Dict[str, Any]:
|
|
117
|
+
logger.debug(f"Serializing device features: {device.host}")
|
|
95
118
|
disc_info = {
|
|
96
|
-
"model": device._discovery_info.get("device_model"
|
|
119
|
+
"model": device._discovery_info.get("device_model") or device.sys_info.get("model")
|
|
97
120
|
}
|
|
98
121
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
122
|
+
feature_info = {}
|
|
123
|
+
light_module = device.modules.get(Module.Light)
|
|
124
|
+
if light_module:
|
|
125
|
+
feature_info = {
|
|
126
|
+
"brightness": light_module.is_dimmable,
|
|
127
|
+
"color_temp": light_module.is_variable_color_temp,
|
|
128
|
+
"hsv": light_module.is_color
|
|
129
|
+
}
|
|
105
130
|
|
|
106
131
|
return {
|
|
107
132
|
"disc_info": disc_info,
|
|
108
133
|
"feature_info": feature_info
|
|
109
134
|
}
|
|
110
135
|
|
|
111
|
-
async def discover_devices(
|
|
112
|
-
|
|
136
|
+
async def discover_devices(
|
|
137
|
+
username: Optional[str] = None,
|
|
138
|
+
password: Optional[str] = None,
|
|
139
|
+
additional_broadcasts: Optional[List[str]] = None,
|
|
140
|
+
manual_devices: Optional[List[str]] = None
|
|
141
|
+
) -> Dict[str, Any]:
|
|
142
|
+
logger.debug("Starting device discovery")
|
|
113
143
|
devices = {}
|
|
114
144
|
broadcasts = ["255.255.255.255"] + (additional_broadcasts or [])
|
|
115
145
|
creds = Credentials(username, password) if username and password else None
|
|
@@ -117,216 +147,224 @@ async def discover_devices(username=None, password=None, additional_broadcasts=N
|
|
|
117
147
|
async def on_discovered(device: Device):
|
|
118
148
|
try:
|
|
119
149
|
await device.update()
|
|
120
|
-
|
|
121
|
-
except
|
|
122
|
-
|
|
150
|
+
logger.debug(f"Device updated: {device.host}")
|
|
151
|
+
except UnsupportedDeviceError as e:
|
|
152
|
+
logger.warning(f"Unsupported device during discovery: {device.host} - {str(e)}")
|
|
123
153
|
except Exception as e:
|
|
124
|
-
|
|
154
|
+
logger.error(f"Error updating device during discovery: {device.host} - {str(e)}")
|
|
125
155
|
|
|
126
|
-
async def discover_on_broadcast(broadcast):
|
|
156
|
+
async def discover_on_broadcast(broadcast: str):
|
|
157
|
+
logger.debug(f"Starting discovery on broadcast: {broadcast}")
|
|
127
158
|
try:
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
159
|
+
discovered = await Discover.discover(
|
|
160
|
+
target=broadcast,
|
|
161
|
+
credentials=creds,
|
|
162
|
+
on_discovered=on_discovered
|
|
163
|
+
)
|
|
164
|
+
devices.update(discovered)
|
|
165
|
+
logger.debug(f"Discovered {len(discovered)} devices on broadcast {broadcast}")
|
|
135
166
|
except Exception as e:
|
|
136
|
-
|
|
167
|
+
logger.error(f"Error during broadcast discovery {broadcast}: {str(e)}", exc_info=True)
|
|
137
168
|
|
|
138
|
-
async def discover_manual_device(host):
|
|
169
|
+
async def discover_manual_device(host: str):
|
|
139
170
|
if host in devices:
|
|
140
|
-
|
|
171
|
+
logger.debug(f"Device {host} already discovered.")
|
|
141
172
|
return
|
|
142
173
|
try:
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
await on_discovered(
|
|
146
|
-
devices[host] =
|
|
147
|
-
|
|
148
|
-
except
|
|
149
|
-
|
|
174
|
+
logger.debug(f"Discovering manual device: {host}")
|
|
175
|
+
device = await Discover.discover_single(host=host, credentials=creds)
|
|
176
|
+
await on_discovered(device)
|
|
177
|
+
devices[host] = device
|
|
178
|
+
logger.debug(f"Discovered manual device: {host}")
|
|
179
|
+
except UnsupportedDeviceError as e:
|
|
180
|
+
logger.warning(f"Unsupported manual device: {host} - {str(e)}")
|
|
150
181
|
except Exception as e:
|
|
151
|
-
|
|
182
|
+
logger.error(f"Error discovering manual device {host}: {str(e)}", exc_info=True)
|
|
152
183
|
|
|
153
|
-
|
|
154
|
-
|
|
184
|
+
discover_tasks = [discover_on_broadcast(bc) for bc in broadcasts]
|
|
185
|
+
manual_discover_tasks = [discover_manual_device(host) for host in (manual_devices or [])]
|
|
186
|
+
await asyncio.gather(*discover_tasks, *manual_discover_tasks)
|
|
155
187
|
|
|
156
188
|
all_device_info = {}
|
|
157
|
-
|
|
189
|
+
update_tasks = []
|
|
158
190
|
|
|
191
|
+
dev: Device
|
|
192
|
+
ip: str
|
|
159
193
|
for ip, dev in devices.items():
|
|
160
|
-
dev: Device
|
|
161
194
|
try:
|
|
162
195
|
components = await dev._raw_query("component_nego")
|
|
163
196
|
component_list = components.get("component_nego", {}).get("component_list", [])
|
|
164
197
|
homekit_component = next((item for item in component_list if item.get("id") == "homekit"), None)
|
|
165
198
|
if homekit_component:
|
|
166
|
-
|
|
199
|
+
logger.debug(f"Native HomeKit support found for device {ip}, skipping.")
|
|
167
200
|
continue
|
|
168
201
|
except Exception:
|
|
169
|
-
|
|
202
|
+
logger.debug(f"No native HomeKit support for device {ip}")
|
|
170
203
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
app.logger.debug(f"Device {ip} is unsupported and was not added to update tasks")
|
|
178
|
-
except Exception as e:
|
|
179
|
-
app.logger.error(f"Error adding device {ip} to update tasks: {str(e)}")
|
|
204
|
+
dev_type = dev.sys_info.get("mic_type") or dev.sys_info.get("type")
|
|
205
|
+
if dev_type and dev_type not in UNSUPPORTED_TYPES:
|
|
206
|
+
update_tasks.append(create_device_info(ip, dev))
|
|
207
|
+
logger.debug(f"Device {ip} added to update tasks")
|
|
208
|
+
else:
|
|
209
|
+
logger.debug(f"Device {ip} is unsupported")
|
|
180
210
|
|
|
181
|
-
|
|
182
|
-
results = await asyncio.gather(*tasks)
|
|
183
|
-
except Exception as e:
|
|
184
|
-
app.logger.error(f"Error updating device info: {str(e)}")
|
|
185
|
-
return {}
|
|
211
|
+
results = await asyncio.gather(*update_tasks, return_exceptions=True)
|
|
186
212
|
|
|
187
|
-
for
|
|
213
|
+
for result in results:
|
|
214
|
+
if isinstance(result, Exception):
|
|
215
|
+
logger.error(f"Error in device info creation: {str(result)}")
|
|
216
|
+
continue
|
|
217
|
+
ip, info = result
|
|
188
218
|
all_device_info[ip] = info
|
|
189
219
|
|
|
190
|
-
|
|
220
|
+
logger.debug(f"Device discovery completed with {len(all_device_info)} devices")
|
|
191
221
|
return all_device_info
|
|
192
222
|
|
|
193
|
-
async def create_device_info(ip, dev: Device):
|
|
194
|
-
|
|
195
|
-
app.logger.debug(f"Creating device info for {ip}")
|
|
223
|
+
async def create_device_info(ip: str, dev: Device):
|
|
224
|
+
logger.debug(f"Creating device info for {ip}")
|
|
196
225
|
try:
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
"sys_info": sys_info,
|
|
205
|
-
"disc_info": disc_info,
|
|
206
|
-
"feature_info": feature_info,
|
|
207
|
-
"device_config": device_config
|
|
226
|
+
sys_info_data = custom_sysinfo_config_serializer(dev)
|
|
227
|
+
feature_info_data = custom_discovery_feature_serializer(dev)
|
|
228
|
+
device_info = {
|
|
229
|
+
"sys_info": sys_info_data["sys_info"],
|
|
230
|
+
"disc_info": feature_info_data["disc_info"],
|
|
231
|
+
"feature_info": feature_info_data["feature_info"],
|
|
232
|
+
"device_config": sys_info_data["device_config"]
|
|
208
233
|
}
|
|
209
|
-
|
|
210
|
-
return ip,
|
|
234
|
+
logger.debug(f"Created device info for {ip}")
|
|
235
|
+
return ip, device_info
|
|
211
236
|
except Exception as e:
|
|
212
|
-
|
|
237
|
+
logger.error(f"Error creating device info for {ip}: {str(e)}")
|
|
213
238
|
return ip, {}
|
|
214
239
|
|
|
215
|
-
async def get_sys_info(device_config):
|
|
216
|
-
|
|
240
|
+
async def get_sys_info(device_config: Dict[str, Any]) -> Dict[str, Any]:
|
|
241
|
+
logger.debug(f"Fetching system info for device: {device_config['host']}")
|
|
217
242
|
dev = await Device.connect(config=Device.Config.from_dict(device_config))
|
|
218
243
|
try:
|
|
219
|
-
if not dev.alias:
|
|
220
|
-
app.logger.warning(f"Alias not found for device {dev.host}. Reconnecting and updating...")
|
|
221
|
-
await dev.disconnect()
|
|
222
|
-
dev = await Device.connect(config=Device.Config.from_dict(device_config))
|
|
223
244
|
device = custom_sysinfo_config_serializer(dev)
|
|
224
|
-
sys_info
|
|
225
|
-
return {"sys_info": sys_info}
|
|
245
|
+
return {"sys_info": device["sys_info"]}
|
|
226
246
|
except Exception as e:
|
|
227
|
-
|
|
247
|
+
logger.error(f"Error getting system info: {str(e)}")
|
|
228
248
|
return {}
|
|
229
249
|
finally:
|
|
230
250
|
await dev.disconnect()
|
|
231
251
|
|
|
232
|
-
async def control_device(
|
|
233
|
-
|
|
234
|
-
|
|
252
|
+
async def control_device(
|
|
253
|
+
device_config: Dict[str, Any],
|
|
254
|
+
feature: str,
|
|
255
|
+
action: str,
|
|
256
|
+
value: Any,
|
|
257
|
+
child_num: Optional[int] = None
|
|
258
|
+
) -> Dict[str, Any]:
|
|
259
|
+
logger.debug(f"Controlling device: {device_config['host']}, feature: {feature}, action: {action}, child_num: {child_num}")
|
|
235
260
|
dev = await Device.connect(config=Device.Config.from_dict(device_config))
|
|
236
261
|
try:
|
|
237
262
|
target = dev.children[child_num] if child_num is not None else dev
|
|
263
|
+
light = target.modules.get(Module.Light)
|
|
238
264
|
|
|
239
265
|
if feature == "state":
|
|
240
266
|
await getattr(target, action)()
|
|
241
|
-
elif feature == "brightness":
|
|
267
|
+
elif feature == "brightness" and light:
|
|
242
268
|
await handle_brightness(target, action, value)
|
|
243
|
-
elif feature == "color_temp":
|
|
269
|
+
elif feature == "color_temp" and light:
|
|
244
270
|
await handle_color_temp(target, action, value)
|
|
245
|
-
elif feature in ["hue", "saturation"]:
|
|
271
|
+
elif feature in ["hue", "saturation"] and light:
|
|
246
272
|
await handle_hsv(target, action, feature, value)
|
|
247
273
|
else:
|
|
248
|
-
raise
|
|
274
|
+
raise ValueError(f"Unsupported feature or missing module: {feature}")
|
|
249
275
|
|
|
250
|
-
|
|
276
|
+
logger.debug(f"Device {device_config['host']} controlled successfully")
|
|
251
277
|
return {"status": "success"}
|
|
252
278
|
except Exception as e:
|
|
253
|
-
|
|
279
|
+
logger.error(f"Error controlling device {device_config['host']}: {str(e)}")
|
|
254
280
|
return {"status": "error", "message": str(e)}
|
|
255
281
|
finally:
|
|
256
282
|
await dev.disconnect()
|
|
257
283
|
|
|
258
|
-
async def handle_brightness(target: Device, action, value):
|
|
284
|
+
async def handle_brightness(target: Device, action: str, value: int):
|
|
285
|
+
light = target.modules.get(Module.Light)
|
|
259
286
|
if value == 0:
|
|
260
287
|
await target.turn_off()
|
|
261
|
-
elif
|
|
262
|
-
light = target.modules[Module.Light]
|
|
288
|
+
elif 0 < value <= 100:
|
|
263
289
|
await getattr(light, action)(value)
|
|
264
290
|
else:
|
|
265
291
|
await target.turn_on()
|
|
266
292
|
|
|
267
|
-
async def handle_color_temp(target: Device, action, value):
|
|
268
|
-
light = target.modules
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
value = range[0]
|
|
272
|
-
elif value > range[1]:
|
|
273
|
-
value = range[1]
|
|
293
|
+
async def handle_color_temp(target: Device, action: str, value: int):
|
|
294
|
+
light = target.modules.get(Module.Light)
|
|
295
|
+
min_temp, max_temp = light.valid_temperature_range
|
|
296
|
+
value = max(min(value, max_temp), min_temp)
|
|
274
297
|
await getattr(light, action)(value)
|
|
275
298
|
|
|
276
|
-
async def handle_hsv(target: Device, action, feature, value):
|
|
277
|
-
light = target.modules
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
299
|
+
async def handle_hsv(target: Device, action: str, feature: str, value: Dict[str, int]):
|
|
300
|
+
light = target.modules.get(Module.Light)
|
|
301
|
+
hsv = list(light.hsv)
|
|
302
|
+
if feature == "hue":
|
|
303
|
+
hsv[0] = value["hue"]
|
|
304
|
+
elif feature == "saturation":
|
|
305
|
+
hsv[1] = value["saturation"]
|
|
306
|
+
await getattr(light, action)(tuple(hsv))
|
|
284
307
|
|
|
285
308
|
@app.route('/discover', methods=['POST'])
|
|
286
|
-
def
|
|
309
|
+
async def discover_route():
|
|
287
310
|
try:
|
|
288
311
|
auth = request.authorization
|
|
289
|
-
username =
|
|
290
|
-
password =
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
312
|
+
username = auth.username if auth else None
|
|
313
|
+
password = auth.password if auth else None
|
|
314
|
+
data = await request.get_json()
|
|
315
|
+
additional_broadcasts = data.get('additionalBroadcasts', [])
|
|
316
|
+
manual_devices = data.get('manualDevices', [])
|
|
317
|
+
logger.debug(f"Received discovery request with broadcasts: {additional_broadcasts} and manual devices: {manual_devices}")
|
|
318
|
+
devices_info = await discover_devices(username, password, additional_broadcasts, manual_devices)
|
|
319
|
+
logger.debug(f"Discovery completed with {len(devices_info)} devices found")
|
|
296
320
|
return jsonify(devices_info)
|
|
297
321
|
except Exception as e:
|
|
298
|
-
|
|
322
|
+
logger.exception(f"Error during discovery: {str(e)}")
|
|
299
323
|
return jsonify({"error": str(e)}), 500
|
|
300
324
|
|
|
301
325
|
@app.route('/getSysInfo', methods=['POST'])
|
|
302
|
-
def get_sys_info_route():
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
326
|
+
async def get_sys_info_route():
|
|
327
|
+
try:
|
|
328
|
+
data = await request.get_json()
|
|
329
|
+
device_config = data['device_config']
|
|
330
|
+
credentials = device_config.get('credentials')
|
|
331
|
+
if credentials:
|
|
332
|
+
device_config['credentials'] = Credentials(
|
|
333
|
+
username=credentials['username'],
|
|
334
|
+
password=credentials['password']
|
|
335
|
+
)
|
|
336
|
+
sys_info = await get_sys_info(device_config)
|
|
337
|
+
return jsonify(sys_info)
|
|
338
|
+
except Exception as e:
|
|
339
|
+
logger.exception(f"Error getting system info: {str(e)}")
|
|
340
|
+
return jsonify({"error": str(e)}), 500
|
|
310
341
|
|
|
311
342
|
@app.route('/controlDevice', methods=['POST'])
|
|
312
|
-
def control_device_route():
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
343
|
+
async def control_device_route():
|
|
344
|
+
try:
|
|
345
|
+
data = await request.get_json()
|
|
346
|
+
device_config = data['device_config']
|
|
347
|
+
credentials = device_config.get('credentials')
|
|
348
|
+
if credentials:
|
|
349
|
+
device_config['credentials'] = Credentials(
|
|
350
|
+
username=credentials['username'],
|
|
351
|
+
password=credentials['password']
|
|
352
|
+
)
|
|
353
|
+
feature = data['feature']
|
|
354
|
+
action = data['action']
|
|
355
|
+
value = data.get('value')
|
|
356
|
+
child_num = data.get('child_num')
|
|
357
|
+
result = await control_device(device_config, feature, action, value, child_num)
|
|
358
|
+
return jsonify(result)
|
|
359
|
+
except Exception as e:
|
|
360
|
+
logger.exception(f"Error controlling device: {str(e)}")
|
|
361
|
+
return jsonify({"error": str(e)}), 500
|
|
324
362
|
|
|
325
363
|
@app.route('/health', methods=['GET'])
|
|
326
|
-
def health_check():
|
|
364
|
+
async def health_check():
|
|
327
365
|
return jsonify({"status": "healthy"}), 200
|
|
328
366
|
|
|
329
367
|
if __name__ == '__main__':
|
|
330
368
|
port = int(sys.argv[1])
|
|
331
|
-
|
|
332
|
-
|
|
369
|
+
logger.info(f"Starting server on port {port}")
|
|
370
|
+
uvicorn.run(app, host="0.0.0.0", port=port, loop="asyncio")
|
|
@@ -4,7 +4,7 @@ import path from 'node:path';
|
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
5
|
import { delay, prefixLogger, runCommand } from '../utils.js';
|
|
6
6
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
7
|
-
const SUPPORTED_PYTHON_VERSIONS = ['3.
|
|
7
|
+
const SUPPORTED_PYTHON_VERSIONS = ['3.11', '3.12', '3.13'];
|
|
8
8
|
class PythonChecker {
|
|
9
9
|
log;
|
|
10
10
|
platform;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pythonChecker.js","sourceRoot":"","sources":["../../src/python/pythonChecker.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAGzC,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9D,MAAM,SAAS,GAAW,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AACvE,MAAM,yBAAyB,GAAa,CAAC,
|
|
1
|
+
{"version":3,"file":"pythonChecker.js","sourceRoot":"","sources":["../../src/python/pythonChecker.ts"],"names":[],"mappings":"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"}
|