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.
@@ -1,25 +1,44 @@
1
- import asyncio, eventlet, eventlet.wsgi, math, os, requests, sys
2
- from flask import Flask, request, jsonify
3
- from flask_socketio import SocketIO
4
- from kasa import Credentials, Discover, Device, Module, UnsupportedDeviceException
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 = Flask(__name__)
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
- requests.post(self.url, json={"level": "debug", "message": message.strip()})
22
+ async with ClientSession() as session:
23
+ await session.post(self.url, json={"level": "debug", "message": message.strip()})
19
24
 
20
- logger.add(RemoteLogger(logging_server_url), level="DEBUG")
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
- app.logger = logger
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 serialize_child(child: Device):
31
- return {
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
- app.logger.debug(f"Serializing device: {device.host}")
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.hw_info["mac"],
56
- "sw_ver": device.sys_info.get("sw_ver") or device.sys_info.get("fw_ver")
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
- app.logger.debug(f"Serializing device for discovery: {device.host}")
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", device.sys_info.get("model"))
119
+ "model": device._discovery_info.get("device_model") or device.sys_info.get("model")
97
120
  }
98
121
 
99
- app.logger.debug(f"Serializing device features: {device.host}")
100
- feature_info = {
101
- **({"brightness": device.modules[Module.Light].is_dimmable} if Module.Light in device.modules else {}),
102
- **({"color_temp": device.modules[Module.Light].is_variable_color_temp} if Module.Light in device.modules else {}),
103
- **({"hsv": device.modules[Module.Light].is_color} if Module.Light in device.modules else {})
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(username=None, password=None, additional_broadcasts=None, manual_devices=None):
112
- app.logger.debug("Starting device discovery")
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
- app.logger.debug(f"Discovered device has been updated: {device.host}")
121
- except UnsupportedDeviceException as e:
122
- app.logger.warning(f"Unsupported device found during discovery: {device.host} - {str(e)}")
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
- app.logger.error(f"Error updating device during discovery: {device.host} - {str(e)}")
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
- app.logger.debug(f"Starting discovery on broadcast: {broadcast}")
129
- discovered_devices = await Discover.discover(target=broadcast, credentials=creds, on_discovered=on_discovered)
130
- for ip, dev in discovered_devices.items():
131
- if ip not in devices:
132
- devices[ip] = dev
133
- app.logger.debug(f"Added device {ip} from broadcast {broadcast} to devices list")
134
- app.logger.debug(f"Discovered {len(discovered_devices)} devices on broadcast {broadcast}")
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
- app.logger.error(f"Error processing broadcast {broadcast}: {str(e)}", exc_info=True)
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
- app.logger.debug(f"Device {host} already exists in devices list, skipping.")
171
+ logger.debug(f"Device {host} already discovered.")
141
172
  return
142
173
  try:
143
- app.logger.debug(f"Starting discovery for device: {host}")
144
- discovered_device = await Discover.discover_single(host=host, credentials=creds)
145
- await on_discovered(discovered_device)
146
- devices[host] = discovered_device
147
- app.logger.debug(f"Discovered device: {host}")
148
- except UnsupportedDeviceException as e:
149
- app.logger.warning(f"Unsupported device found during discovery: {host} - {str(e)}")
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
- app.logger.error(f"Error discovering device {host}: {str(e)}", exc_info=True)
182
+ logger.error(f"Error discovering manual device {host}: {str(e)}", exc_info=True)
152
183
 
153
- await asyncio.gather(*(discover_on_broadcast(broadcast) for broadcast in broadcasts))
154
- await asyncio.gather(*(discover_manual_device(host) for host in manual_devices))
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
- tasks = []
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
- app.logger.debug(f"Native HomeKit Support found for device {ip} and was not added to update tasks")
199
+ logger.debug(f"Native HomeKit support found for device {ip}, skipping.")
167
200
  continue
168
201
  except Exception:
169
- app.logger.debug(f"Native HomeKit Support not found for device {ip}")
202
+ logger.debug(f"No native HomeKit support for device {ip}")
170
203
 
171
- try:
172
- dev_type = dev.sys_info.get("mic_type") or dev.sys_info.get("type")
173
- if dev_type and dev_type not in UNSUPPORTED_TYPES:
174
- tasks.append(create_device_info(ip, dev))
175
- app.logger.debug(f"Device {ip} added to update tasks")
176
- else:
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
- try:
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 ip, info in results:
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
- app.logger.debug(f"Device discovery completed with {len(all_device_info)} devices found")
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
- created_device_info = {}
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
- device_info = custom_sysinfo_config_serializer(dev)
198
- sys_info = device_info["sys_info"]
199
- device_config = device_info["device_config"]
200
- device_info = custom_discovery_feature_serializer(dev)
201
- disc_info = device_info["disc_info"]
202
- feature_info = device_info["feature_info"]
203
- created_device_info[ip] = {
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
- app.logger.debug(f"Created device info for {ip}")
210
- return ip, created_device_info[ip]
234
+ logger.debug(f"Created device info for {ip}")
235
+ return ip, device_info
211
236
  except Exception as e:
212
- app.logger.error(f"Error creating device info for {ip}: {str(e)}")
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
- app.logger.debug(f"Getting sys info for device: {device_config['host']}")
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 = device["sys_info"]
225
- return {"sys_info": sys_info}
245
+ return {"sys_info": device["sys_info"]}
226
246
  except Exception as e:
227
- app.logger.error(f"Error getting sys info: {str(e)}")
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(device_config, feature, action, value, child_num=None):
233
- app.logger.debug(f"Controlling device: {device_config['host']}, feature: {feature}, action: {action}, child_num: {child_num if child_num is not None else 'N/A'}")
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 Exception(f"Unsupported feature: {feature}")
274
+ raise ValueError(f"Unsupported feature or missing module: {feature}")
249
275
 
250
- app.logger.debug(f"Controlled device {device_config['host']} with feature: {feature}, action: {action}, child_num: {child_num if child_num is not None else 'N/A'}")
276
+ logger.debug(f"Device {device_config['host']} controlled successfully")
251
277
  return {"status": "success"}
252
278
  except Exception as e:
253
- app.logger.error(f"Error controlling device {device_config['host']}: {str(e)}")
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 value > 0 and value < 100:
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[Module.Light]
269
- range = light.valid_temperature_range
270
- if value < range[0]:
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[Module.Light]
278
- hsv_value = {feature: value[feature]}
279
- await getattr(light, action)(hsv_value)
280
-
281
- def run_async(func, *args):
282
- loop = asyncio.get_event_loop()
283
- return loop.run_until_complete(func(*args))
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 discover():
309
+ async def discover_route():
287
310
  try:
288
311
  auth = request.authorization
289
- username = f'{auth.username}' if auth else None
290
- password = f'{auth.password}' if auth else None
291
- additional_broadcasts = request.json.get('additionalBroadcasts', [])
292
- manual_devices = request.json.get('manualDevices', [])
293
- app.logger.debug(f"Starting device discovery with additionalBroadcasts: {additional_broadcasts} and manualDevices: {manual_devices}")
294
- devices_info = run_async(discover_devices, username, password, additional_broadcasts, manual_devices)
295
- app.logger.debug(f"Device discovery completed with {len(devices_info)} devices found")
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
- app.logger.error(f"An error occurred during device discovery: {str(e)}", exc_info=True)
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
- data = request.json
304
- device_config = data['device_config']
305
- credentials = device_config.get('credentials')
306
- device_config.update({'credentials': Credentials(username=credentials['username'], password=credentials['password'])} if credentials else {})
307
- app.logger.debug(f"Getting system info for device: {device_config['host']}")
308
- sys_info = run_async(get_sys_info, device_config)
309
- return jsonify(sys_info)
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
- data = request.json
314
- device_config = data['device_config']
315
- credentials = device_config.get('credentials')
316
- device_config.update({'credentials': Credentials(username=credentials['username'], password=credentials['password'])} if credentials else {})
317
- feature = data['feature']
318
- action = data['action']
319
- value = data['value']
320
- child_num = data.get('child_num')
321
- app.logger.debug(f"Controlling device: {device_config['host']}, action: {action}, child_num: {child_num}")
322
- result = run_async(control_device, device_config, feature, action, value, child_num)
323
- return jsonify(result)
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
- app.logger.info(f"Starting server on port {port}")
332
- eventlet.wsgi.server(eventlet.listen(('127.0.0.1', port)), app)
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.9', '3.10', '3.11', '3.12', '3.13'];
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":"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"}
1
+ {"version":3,"file":"pythonChecker.js","sourceRoot":"","sources":["../../src/python/pythonChecker.ts"],"names":[],"mappings":"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"}