pycloudedge 0.1.4.dev2__tar.gz → 0.1.4.dev4__tar.gz

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.
Files changed (33) hide show
  1. {pycloudedge-0.1.4.dev2 → pycloudedge-0.1.4.dev4}/.gitignore +3 -1
  2. {pycloudedge-0.1.4.dev2 → pycloudedge-0.1.4.dev4}/PKG-INFO +1 -1
  3. {pycloudedge-0.1.4.dev2 → pycloudedge-0.1.4.dev4}/cloudedge/__init__.py +9 -1
  4. {pycloudedge-0.1.4.dev2 → pycloudedge-0.1.4.dev4}/cloudedge/_version.py +3 -3
  5. {pycloudedge-0.1.4.dev2 → pycloudedge-0.1.4.dev4}/cloudedge/client.py +355 -35
  6. {pycloudedge-0.1.4.dev2 → pycloudedge-0.1.4.dev4}/pycloudedge.egg-info/PKG-INFO +1 -1
  7. {pycloudedge-0.1.4.dev2 → pycloudedge-0.1.4.dev4}/.env.example +0 -0
  8. {pycloudedge-0.1.4.dev2 → pycloudedge-0.1.4.dev4}/LICENSE +0 -0
  9. {pycloudedge-0.1.4.dev2 → pycloudedge-0.1.4.dev4}/MANIFEST.in +0 -0
  10. {pycloudedge-0.1.4.dev2 → pycloudedge-0.1.4.dev4}/README.md +0 -0
  11. {pycloudedge-0.1.4.dev2 → pycloudedge-0.1.4.dev4}/cloudedge/cli.py +0 -0
  12. {pycloudedge-0.1.4.dev2 → pycloudedge-0.1.4.dev4}/cloudedge/constants.py +0 -0
  13. {pycloudedge-0.1.4.dev2 → pycloudedge-0.1.4.dev4}/cloudedge/exceptions.py +0 -0
  14. {pycloudedge-0.1.4.dev2 → pycloudedge-0.1.4.dev4}/cloudedge/iot_parameters.py +0 -0
  15. {pycloudedge-0.1.4.dev2 → pycloudedge-0.1.4.dev4}/cloudedge/logging_config.py +0 -0
  16. {pycloudedge-0.1.4.dev2 → pycloudedge-0.1.4.dev4}/cloudedge/utils.py +0 -0
  17. {pycloudedge-0.1.4.dev2 → pycloudedge-0.1.4.dev4}/cloudedge/validators.py +0 -0
  18. {pycloudedge-0.1.4.dev2 → pycloudedge-0.1.4.dev4}/examples/README.md +0 -0
  19. {pycloudedge-0.1.4.dev2 → pycloudedge-0.1.4.dev4}/examples/basic_example.py +0 -0
  20. {pycloudedge-0.1.4.dev2 → pycloudedge-0.1.4.dev4}/examples/device_control.py +0 -0
  21. {pycloudedge-0.1.4.dev2 → pycloudedge-0.1.4.dev4}/examples/network_ping_status.py +0 -0
  22. {pycloudedge-0.1.4.dev2 → pycloudedge-0.1.4.dev4}/pycloudedge.egg-info/SOURCES.txt +0 -0
  23. {pycloudedge-0.1.4.dev2 → pycloudedge-0.1.4.dev4}/pycloudedge.egg-info/dependency_links.txt +0 -0
  24. {pycloudedge-0.1.4.dev2 → pycloudedge-0.1.4.dev4}/pycloudedge.egg-info/entry_points.txt +0 -0
  25. {pycloudedge-0.1.4.dev2 → pycloudedge-0.1.4.dev4}/pycloudedge.egg-info/requires.txt +0 -0
  26. {pycloudedge-0.1.4.dev2 → pycloudedge-0.1.4.dev4}/pycloudedge.egg-info/top_level.txt +0 -0
  27. {pycloudedge-0.1.4.dev2 → pycloudedge-0.1.4.dev4}/pyproject.toml +0 -0
  28. {pycloudedge-0.1.4.dev2 → pycloudedge-0.1.4.dev4}/requirements-dev.txt +0 -0
  29. {pycloudedge-0.1.4.dev2 → pycloudedge-0.1.4.dev4}/requirements.txt +0 -0
  30. {pycloudedge-0.1.4.dev2 → pycloudedge-0.1.4.dev4}/setup.cfg +0 -0
  31. {pycloudedge-0.1.4.dev2 → pycloudedge-0.1.4.dev4}/setup.py +0 -0
  32. {pycloudedge-0.1.4.dev2 → pycloudedge-0.1.4.dev4}/tests/test_basic.py +0 -0
  33. {pycloudedge-0.1.4.dev2 → pycloudedge-0.1.4.dev4}/tests/test_improvements.py +0 -0
@@ -118,4 +118,6 @@ cloudedge/_version.py
118
118
 
119
119
  # CloudEdge specific
120
120
  .cloudedge_session_cache
121
- .home_id
121
+ .home_id
122
+
123
+ publish.sh
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pycloudedge
3
- Version: 0.1.4.dev2
3
+ Version: 0.1.4.dev4
4
4
  Summary: Python library for CloudEdge cameras
5
5
  Home-page: https://github.com/fradaloisio/pycloudedge
6
6
  Author: Francesco D'Aloisio
@@ -9,7 +9,12 @@ Author: Francesco D'Aloisio
9
9
  Date: September 16, 2025
10
10
  """
11
11
 
12
- from .client import CloudEdgeClient
12
+ from .client import (
13
+ CloudEdgeClient,
14
+ DEVICE_STATUS_ONLINE,
15
+ DEVICE_STATUS_DORMANCY,
16
+ DEVICE_STATUS_OFFLINE,
17
+ )
13
18
  from .exceptions import (
14
19
  CloudEdgeError, AuthenticationError, DeviceNotFoundError,
15
20
  ConfigurationError, NetworkError, ValidationError, RateLimitError
@@ -24,6 +29,9 @@ __author__ = "Francesco D'Aloisio"
24
29
 
25
30
  __all__ = [
26
31
  'CloudEdgeClient',
32
+ 'DEVICE_STATUS_ONLINE',
33
+ 'DEVICE_STATUS_DORMANCY',
34
+ 'DEVICE_STATUS_OFFLINE',
27
35
  'CloudEdgeError',
28
36
  'AuthenticationError',
29
37
  'DeviceNotFoundError',
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '0.1.4.dev2'
32
- __version_tuple__ = version_tuple = (0, 1, 4, 'dev2')
31
+ __version__ = version = '0.1.4.dev4'
32
+ __version_tuple__ = version_tuple = (0, 1, 4, 'dev4')
33
33
 
34
- __commit_id__ = commit_id = 'gf40c10343'
34
+ __commit_id__ = commit_id = 'gc5b1ac2e4'
@@ -44,6 +44,11 @@ from .validators import validate_email, validate_country_code, validate_phone_co
44
44
  from .logging_config import get_logger
45
45
  from .utils import retry_on_failure
46
46
 
47
+ # Device online status values returned by get_device_online_status()
48
+ DEVICE_STATUS_ONLINE = "online"
49
+ DEVICE_STATUS_DORMANCY = "dormancy"
50
+ DEVICE_STATUS_OFFLINE = "offline"
51
+
47
52
  # API list keys → readable product type when deviceTypeName is a CDN image URL
48
53
  _DEVICE_LIST_CATEGORY_LABELS = {
49
54
  "snap": "Camera",
@@ -1091,7 +1096,279 @@ class CloudEdgeClient:
1091
1096
  raise NetworkError(f"Device status request failed: {e}")
1092
1097
  except json.JSONDecodeError:
1093
1098
  raise CloudEdgeError("Failed to parse device status response")
1094
-
1099
+
1100
+ def get_device_online_status(self, serial_number: str) -> str:
1101
+ """Query the precise online state of a device via OpenAPI.
1102
+
1103
+ Unlike :meth:`get_device_status` (which uses a legacy endpoint and
1104
+ only distinguishes online/offline), this method calls
1105
+ ``/openapi/device/status`` and can distinguish three states:
1106
+
1107
+ * ``"online"`` – camera is awake and reachable
1108
+ * ``"dormancy"`` – battery camera is asleep (must be woken before commands)
1109
+ * ``"offline"`` – camera is unreachable
1110
+
1111
+ Use the module-level constants :data:`DEVICE_STATUS_ONLINE`,
1112
+ :data:`DEVICE_STATUS_DORMANCY`, :data:`DEVICE_STATUS_OFFLINE` for
1113
+ comparisons.
1114
+
1115
+ Args:
1116
+ serial_number (str): Device serial number (snNum).
1117
+
1118
+ Returns:
1119
+ str: ``"online"``, ``"dormancy"``, ``"offline"``, or ``"unknown"``.
1120
+
1121
+ Raises:
1122
+ AuthenticationError: If not authenticated.
1123
+ NetworkError: If the HTTP request fails.
1124
+ """
1125
+ if not self.session_data:
1126
+ raise AuthenticationError("Not authenticated - call authenticate() first")
1127
+
1128
+ iot_keys = self.session_data.get('iotPlatformKeys', {})
1129
+ if not iot_keys or 'accessid' not in iot_keys or 'accesskey' not in iot_keys:
1130
+ self._log("No OpenAPI credentials — cannot query dormancy status")
1131
+ return "unknown"
1132
+
1133
+ access_id = iot_keys['accessid']
1134
+ access_key = iot_keys['accesskey']
1135
+ formatted_sn = self._format_sn(serial_number)
1136
+
1137
+ signature, timeout = self._get_signature_for_openapi(
1138
+ '/openapi/device/status', 'query', access_key
1139
+ )
1140
+ params = {
1141
+ 'accessid': access_id,
1142
+ 'expires': timeout,
1143
+ 'signature': signature,
1144
+ 'action': 'query',
1145
+ 'deviceid': formatted_sn,
1146
+ }
1147
+ headers = {
1148
+ "Accept": "*/*",
1149
+ "User-Agent": DEFAULT_HEADERS['User-Agent'],
1150
+ }
1151
+
1152
+ try:
1153
+ response = self._make_request(
1154
+ 'GET',
1155
+ f"{self.OPENAPI_BASE_URL}/openapi/device/status",
1156
+ headers=headers,
1157
+ params=params,
1158
+ timeout=DEFAULT_TIMEOUT,
1159
+ )
1160
+ data = response.json()
1161
+ status = data.get('status', 'unknown')
1162
+ self._log(f"Device {serial_number} online status: {status}")
1163
+ return status
1164
+ except requests.exceptions.RequestException as e:
1165
+ raise NetworkError(f"Device status request failed: {e}")
1166
+ except json.JSONDecodeError:
1167
+ raise CloudEdgeError("Failed to parse device status response")
1168
+
1169
+ def wake_device(
1170
+ self,
1171
+ serial_number: str,
1172
+ device_id: Optional[Union[int, str]] = None,
1173
+ ) -> bool:
1174
+ """Send a wake signal to a dormant battery camera.
1175
+
1176
+ Uses two independent mechanisms for reliability:
1177
+
1178
+ 1. **OpenAPI** ``/openapi/device/awaken`` — cloud-to-camera push via
1179
+ the IoT platform (requires OpenAPI credentials).
1180
+ 2. **REST** ``/v1/app/bell/remote/wake`` — legacy bell/doorbell wake
1181
+ signal (requires *device_id*).
1182
+
1183
+ Both methods are attempted; success is reported if at least one
1184
+ succeeds. Call :meth:`wait_for_online` or :meth:`ensure_online`
1185
+ afterwards to confirm the camera has woken.
1186
+
1187
+ Args:
1188
+ serial_number (str): Device serial number (snNum).
1189
+ device_id (int | str | None): Numeric device ID (deviceID).
1190
+ Optional but recommended — without it only method 1 is used.
1191
+
1192
+ Returns:
1193
+ bool: ``True`` if at least one wake method succeeded.
1194
+
1195
+ Raises:
1196
+ AuthenticationError: If not authenticated.
1197
+ """
1198
+ if not self.session_data:
1199
+ raise AuthenticationError("Not authenticated - call authenticate() first")
1200
+
1201
+ success = False
1202
+
1203
+ # Method 1 — OpenAPI /openapi/device/awaken
1204
+ iot_keys = self.session_data.get('iotPlatformKeys', {})
1205
+ if iot_keys and 'accessid' in iot_keys and 'accesskey' in iot_keys:
1206
+ try:
1207
+ access_id = iot_keys['accessid']
1208
+ access_key = iot_keys['accesskey']
1209
+ formatted_sn = self._format_sn(serial_number)
1210
+ sid = (formatted_sn + str(int(time.time() * 1000)))[:30]
1211
+
1212
+ signature, timeout = self._get_signature_for_openapi(
1213
+ '/openapi/device/awaken', 'set', access_key
1214
+ )
1215
+ params = {
1216
+ 'accessid': access_id,
1217
+ 'expires': timeout,
1218
+ 'signature': signature,
1219
+ 'action': 'set',
1220
+ 'deviceid': formatted_sn,
1221
+ 'sid': sid,
1222
+ }
1223
+ headers = {
1224
+ "Accept": "*/*",
1225
+ "User-Agent": DEFAULT_HEADERS['User-Agent'],
1226
+ }
1227
+ response = self._make_request(
1228
+ 'GET',
1229
+ f"{self.OPENAPI_BASE_URL}/openapi/device/awaken",
1230
+ headers=headers,
1231
+ params=params,
1232
+ timeout=DEFAULT_TIMEOUT,
1233
+ )
1234
+ if response.status_code == 200:
1235
+ self._log(f"OpenAPI wake sent for {serial_number}")
1236
+ success = True
1237
+ except Exception as e:
1238
+ self._log(f"OpenAPI wake failed: {e}")
1239
+
1240
+ # Method 2 — /v1/app/bell/remote/wake (requires device_id)
1241
+ if device_id is not None:
1242
+ try:
1243
+ device_body = self._generate_device_body({'deviceID': str(device_id)})
1244
+ headers = {
1245
+ "Accept": "*/*",
1246
+ "Content-Type": "application/x-www-form-urlencoded",
1247
+ "User-Agent": DEFAULT_HEADERS['User-Agent'],
1248
+ }
1249
+ response = self._make_request(
1250
+ 'POST',
1251
+ f"{self.BASE_URL}/v1/app/bell/remote/wake",
1252
+ headers=headers,
1253
+ data=device_body,
1254
+ timeout=DEFAULT_TIMEOUT,
1255
+ )
1256
+ if response.json().get('resultCode') in ('1001', '0'):
1257
+ self._log(f"Bell wake sent for device_id={device_id}")
1258
+ success = True
1259
+ except Exception as e:
1260
+ self._log(f"Bell wake failed: {e}")
1261
+
1262
+ if not success:
1263
+ self._log(
1264
+ f"All wake methods failed for {serial_number} — device may be offline"
1265
+ )
1266
+ return success
1267
+
1268
+ def wait_for_online(
1269
+ self,
1270
+ serial_number: str,
1271
+ timeout: float = 30.0,
1272
+ poll_interval: float = 2.0,
1273
+ ) -> bool:
1274
+ """Poll until the device reports ``"online"`` status or the timeout expires.
1275
+
1276
+ Typically called right after :meth:`wake_device` to confirm the camera
1277
+ has woken up before issuing commands.
1278
+
1279
+ Args:
1280
+ serial_number (str): Device serial number.
1281
+ timeout (float): Maximum seconds to wait (default 30).
1282
+ poll_interval (float): Seconds between status checks (default 2).
1283
+
1284
+ Returns:
1285
+ bool: ``True`` if the camera came online within *timeout* seconds.
1286
+ """
1287
+ deadline = time.time() + timeout
1288
+ while time.time() < deadline:
1289
+ try:
1290
+ status = self.get_device_online_status(serial_number)
1291
+ if status == DEVICE_STATUS_ONLINE:
1292
+ self._log(f"Device {serial_number} is now online")
1293
+ return True
1294
+ self._log(
1295
+ f"Device {serial_number} status={status}, "
1296
+ f"waiting ({deadline - time.time():.0f}s left)..."
1297
+ )
1298
+ except Exception as e:
1299
+ self._log(f"Status check error: {e}")
1300
+
1301
+ remaining = deadline - time.time()
1302
+ if remaining > 0:
1303
+ time.sleep(min(poll_interval, remaining))
1304
+
1305
+ self._log(f"Timeout waiting for {serial_number} to come online")
1306
+ return False
1307
+
1308
+ def ensure_online(
1309
+ self,
1310
+ serial_number: str,
1311
+ device_id: Optional[Union[int, str]] = None,
1312
+ timeout: float = 35.0,
1313
+ auto_wake: bool = True,
1314
+ ) -> bool:
1315
+ """Ensure a camera is online, waking it first if dormant.
1316
+
1317
+ This is the recommended guard to call before any command that requires
1318
+ the camera to be awake (e.g. :meth:`set_device_config`,
1319
+ :meth:`get_device_config` on live parameters).
1320
+
1321
+ Behaviour:
1322
+
1323
+ * **online** → returns ``True`` immediately.
1324
+ * **dormancy** → sends a wake signal (if *auto_wake* is ``True``),
1325
+ then polls until online or *timeout* expires.
1326
+ * **offline** → returns ``False`` immediately (offline cameras cannot
1327
+ be woken over the cloud).
1328
+ * **unknown** → returns ``False`` (credentials or network issue).
1329
+
1330
+ Args:
1331
+ serial_number (str): Device serial number.
1332
+ device_id (int | str | None): Numeric device ID, passed to
1333
+ :meth:`wake_device` for the secondary wake method.
1334
+ timeout (float): Max seconds to wait after waking (default 35).
1335
+ auto_wake (bool): Send wake signal when dormant (default ``True``).
1336
+
1337
+ Returns:
1338
+ bool: ``True`` if the camera is (or becomes) online.
1339
+
1340
+ Example::
1341
+
1342
+ if client.ensure_online(sn, device_id):
1343
+ client.set_device_config(sn, {"1": 1})
1344
+ else:
1345
+ print("Camera unreachable — skipping command")
1346
+ """
1347
+ status = self.get_device_online_status(serial_number)
1348
+
1349
+ if status == DEVICE_STATUS_ONLINE:
1350
+ return True
1351
+
1352
+ if status == DEVICE_STATUS_OFFLINE:
1353
+ self._log(f"Device {serial_number} is offline — cannot wake")
1354
+ return False
1355
+
1356
+ if status == DEVICE_STATUS_DORMANCY:
1357
+ if not auto_wake:
1358
+ self._log(
1359
+ f"Device {serial_number} is dormant but auto_wake=False"
1360
+ )
1361
+ return False
1362
+ self._log(f"Device {serial_number} is dormant — sending wake signal")
1363
+ self.wake_device(serial_number, device_id)
1364
+ return self.wait_for_online(serial_number, timeout=timeout)
1365
+
1366
+ # Unknown status
1367
+ self._log(
1368
+ f"Device {serial_number} status={status!r} — cannot determine reachability"
1369
+ )
1370
+ return False
1371
+
1095
1372
  def get_device_config(self, device_serial: str,
1096
1373
  parameter_codes: Optional[List[str]] = None) -> Optional[Dict]:
1097
1374
  """
@@ -1178,24 +1455,58 @@ class CloudEdgeClient:
1178
1455
  except json.JSONDecodeError as e:
1179
1456
  raise ConfigurationError(f"Failed to parse config response: {e}")
1180
1457
 
1181
- def set_device_config(self, device_serial: str, parameters: Dict[str, Any]) -> bool:
1182
- """
1183
- Set device configuration parameters.
1184
-
1458
+ def set_device_config(
1459
+ self,
1460
+ device_serial: str,
1461
+ parameters: Dict[str, Any],
1462
+ auto_wake: bool = True,
1463
+ device_id: Optional[Union[int, str]] = None,
1464
+ ) -> bool:
1465
+ """Set device configuration parameters.
1466
+
1467
+ For battery cameras that may be in ``dormancy`` state, pass
1468
+ ``auto_wake=True`` (default) together with *device_id* so the method
1469
+ will automatically wake the camera and wait for it to come online
1470
+ before sending the command.
1471
+
1185
1472
  Args:
1186
- device_serial (str): Device serial number
1187
- parameters (Dict[str, Any]): Parameter codes and values to set
1188
-
1473
+ device_serial (str): Device serial number.
1474
+ parameters (Dict[str, Any]): Parameter codes and values to set,
1475
+ e.g. ``{"1": 1}`` or ``{"PIR_SWITCH": 1}``.
1476
+ auto_wake (bool): Wake a dormant camera automatically before
1477
+ issuing the command (default ``True``).
1478
+ device_id (int | str | None): Numeric device ID used by the
1479
+ secondary wake method. Ignored when *auto_wake* is ``False``.
1480
+
1189
1481
  Returns:
1190
- bool: True if successful, False otherwise
1191
-
1482
+ bool: ``True`` if successful.
1483
+
1192
1484
  Raises:
1193
- AuthenticationError: If not authenticated
1194
- ConfigurationError: If configuration setting fails
1485
+ AuthenticationError: If not authenticated.
1486
+ ConfigurationError: If configuration setting fails.
1195
1487
  """
1196
1488
  if not self.session_data:
1197
1489
  raise AuthenticationError("Not authenticated - call authenticate() first")
1198
-
1490
+
1491
+ # Wake dormant battery cameras before sending the command
1492
+ if auto_wake:
1493
+ status = self.get_device_online_status(device_serial)
1494
+ if status == DEVICE_STATUS_DORMANCY:
1495
+ self._log(
1496
+ f"set_device_config: device {device_serial} is dormant — waking first"
1497
+ )
1498
+ self.wake_device(device_serial, device_id)
1499
+ if not self.wait_for_online(device_serial):
1500
+ raise ConfigurationError(
1501
+ "Device did not come online after wake signal",
1502
+ details={"device_serial": device_serial},
1503
+ )
1504
+ elif status == DEVICE_STATUS_OFFLINE:
1505
+ raise ConfigurationError(
1506
+ "Device is offline — cannot send configuration",
1507
+ details={"device_serial": device_serial},
1508
+ )
1509
+
1199
1510
  self._log(f"Setting device configuration for SN: {device_serial}")
1200
1511
 
1201
1512
  # Check if we have OpenAPI credentials
@@ -1294,45 +1605,54 @@ class CloudEdgeClient:
1294
1605
 
1295
1606
  return None
1296
1607
 
1297
- def set_device_parameter(self, device_name: str, parameter_name: str,
1298
- value: Union[int, str, float]) -> bool:
1299
- """
1300
- Set a single device parameter by name.
1301
-
1608
+ def set_device_parameter(
1609
+ self,
1610
+ device_name: str,
1611
+ parameter_name: str,
1612
+ value: Union[int, str, float],
1613
+ auto_wake: bool = True,
1614
+ ) -> bool:
1615
+ """Set a single device parameter by name.
1616
+
1302
1617
  Args:
1303
- device_name (str): Device name
1304
- parameter_name (str): Parameter name (e.g., "FRONT_LIGHT_SWITCH")
1305
- value (Union[int, str, float]): Parameter value
1306
-
1618
+ device_name (str): Device name.
1619
+ parameter_name (str): Parameter name, e.g. ``"FRONT_LIGHT_SWITCH"``.
1620
+ value (int | str | float): Parameter value.
1621
+ auto_wake (bool): Wake a dormant camera before sending the command
1622
+ (default ``True``). The device's numeric ID is resolved
1623
+ automatically from the device list.
1624
+
1307
1625
  Returns:
1308
- bool: True if successful, False otherwise
1309
-
1626
+ bool: ``True`` if successful.
1627
+
1310
1628
  Raises:
1311
- DeviceNotFoundError: If device not found
1312
- ConfigurationError: If parameter is invalid or setting fails
1629
+ DeviceNotFoundError: If device not found.
1630
+ ConfigurationError: If parameter is invalid or setting fails.
1313
1631
  """
1314
- # Find device
1315
1632
  device = self.find_device_by_name(device_name)
1316
1633
  if not device:
1317
1634
  raise DeviceNotFoundError(f"Device '{device_name}' not found")
1318
-
1319
- # Get parameter code
1635
+
1320
1636
  parameter_code = get_parameter_code_by_name(parameter_name)
1321
1637
  if not parameter_code:
1322
1638
  raise ConfigurationError(
1323
1639
  f"Unknown parameter: {parameter_name}",
1324
- details={"parameter_name": parameter_name, "device": device_name}
1640
+ details={"parameter_name": parameter_name, "device": device_name},
1325
1641
  )
1326
-
1327
- # Set parameter
1642
+
1328
1643
  parameters = {parameter_code: value}
1329
- success = self.set_device_config(device['serial_number'], parameters)
1330
-
1644
+ success = self.set_device_config(
1645
+ device['serial_number'],
1646
+ parameters,
1647
+ auto_wake=auto_wake,
1648
+ device_id=device.get('device_id'),
1649
+ )
1650
+
1331
1651
  if success:
1332
1652
  param_display = get_parameter_name(parameter_code)
1333
1653
  formatted_value = format_parameter_value(param_display, value)
1334
1654
  self._log(f"Set {param_display} = {formatted_value} on device '{device_name}'")
1335
-
1655
+
1336
1656
  return success
1337
1657
 
1338
1658
  def get_device_info(self, device_name: str, include_config: bool = True) -> Optional[Dict]:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pycloudedge
3
- Version: 0.1.4.dev2
3
+ Version: 0.1.4.dev4
4
4
  Summary: Python library for CloudEdge cameras
5
5
  Home-page: https://github.com/fradaloisio/pycloudedge
6
6
  Author: Francesco D'Aloisio