zcc-helper 3.8.dev3__tar.gz → 3.8.1.dev1__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.
- {zcc_helper-3.8.dev3/zcc_helper.egg-info → zcc_helper-3.8.1.dev1}/PKG-INFO +1 -1
- {zcc_helper-3.8.dev3 → zcc_helper-3.8.1.dev1}/zcc/constants.py +1 -1
- {zcc_helper-3.8.dev3 → zcc_helper-3.8.1.dev1}/zcc/controller.py +38 -17
- {zcc_helper-3.8.dev3 → zcc_helper-3.8.1.dev1}/zcc/device.py +4 -2
- {zcc_helper-3.8.dev3 → zcc_helper-3.8.1.dev1}/zcc/protocol.py +2 -1
- {zcc_helper-3.8.dev3 → zcc_helper-3.8.1.dev1}/zcc/watchdog.py +10 -9
- {zcc_helper-3.8.dev3 → zcc_helper-3.8.1.dev1/zcc_helper.egg-info}/PKG-INFO +1 -1
- {zcc_helper-3.8.dev3 → zcc_helper-3.8.1.dev1}/LICENSE.txt +0 -0
- {zcc_helper-3.8.dev3 → zcc_helper-3.8.1.dev1}/README.md +0 -0
- {zcc_helper-3.8.dev3 → zcc_helper-3.8.1.dev1}/setup.cfg +0 -0
- {zcc_helper-3.8.dev3 → zcc_helper-3.8.1.dev1}/setup.py +0 -0
- {zcc_helper-3.8.dev3 → zcc_helper-3.8.1.dev1}/tests/test_device.py +0 -0
- {zcc_helper-3.8.dev3 → zcc_helper-3.8.1.dev1}/zcc/__init__.py +0 -0
- {zcc_helper-3.8.dev3 → zcc_helper-3.8.1.dev1}/zcc/__main__.py +0 -0
- {zcc_helper-3.8.dev3 → zcc_helper-3.8.1.dev1}/zcc/description.py +0 -0
- {zcc_helper-3.8.dev3 → zcc_helper-3.8.1.dev1}/zcc/discovery.py +0 -0
- {zcc_helper-3.8.dev3 → zcc_helper-3.8.1.dev1}/zcc/errors.py +0 -0
- {zcc_helper-3.8.dev3 → zcc_helper-3.8.1.dev1}/zcc/manufacture_info.py +0 -0
- {zcc_helper-3.8.dev3 → zcc_helper-3.8.1.dev1}/zcc/socket.py +0 -0
- {zcc_helper-3.8.dev3 → zcc_helper-3.8.1.dev1}/zcc/trace.py +0 -0
- {zcc_helper-3.8.dev3 → zcc_helper-3.8.1.dev1}/zcc.py +0 -0
- {zcc_helper-3.8.dev3 → zcc_helper-3.8.1.dev1}/zcc_helper.egg-info/SOURCES.txt +0 -0
- {zcc_helper-3.8.dev3 → zcc_helper-3.8.1.dev1}/zcc_helper.egg-info/dependency_links.txt +0 -0
- {zcc_helper-3.8.dev3 → zcc_helper-3.8.1.dev1}/zcc_helper.egg-info/top_level.txt +0 -0
|
@@ -5,12 +5,18 @@
|
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
7
7
|
import asyncio
|
|
8
|
-
import datetime
|
|
9
8
|
import logging
|
|
10
9
|
import socket
|
|
11
10
|
import time
|
|
12
11
|
import uuid
|
|
13
12
|
from pprint import pformat
|
|
13
|
+
|
|
14
|
+
# Set to True to ignore missing device states during initial fetch
|
|
15
|
+
IGNORE_MISSING_STATES = True
|
|
16
|
+
|
|
17
|
+
# Set to True to ignore missing manufacture_info during initial fetch (e.g. for older firmware versions)
|
|
18
|
+
IGNORE_MISSING_MANUFACTURE_INFO = True
|
|
19
|
+
|
|
14
20
|
from typing import Dict, List
|
|
15
21
|
|
|
16
22
|
from zcc.constants import LEVEL_BY_VERBOSITY, NAME, VERSION
|
|
@@ -139,6 +145,11 @@ class ControlPoint:
|
|
|
139
145
|
filter(lambda device: device.type == "garagedoor", self.devices.values())
|
|
140
146
|
)
|
|
141
147
|
|
|
148
|
+
@property
|
|
149
|
+
def warnings(self) -> List[ControlPointDevice]:
|
|
150
|
+
"""Return a list of devices with empty or missing states (incomplete data)."""
|
|
151
|
+
return [device for device in self.devices.values() if not device.states]
|
|
152
|
+
|
|
142
153
|
async def connect(self, fast: bool = False) -> bool:
|
|
143
154
|
"""Connect to ZCC, build device table and subscribe to updates"""
|
|
144
155
|
self.logger.info("Connecting to ZCC %s:%d", self.host, self.port)
|
|
@@ -264,7 +275,7 @@ class ControlPoint:
|
|
|
264
275
|
self.disconnect()
|
|
265
276
|
|
|
266
277
|
def describe(self) -> str:
|
|
267
|
-
"""Return a string representation of ZCC including devices"""
|
|
278
|
+
"""Return a string representation of ZCC including devices and warnings."""
|
|
268
279
|
header = "+" + "-" * 130 + "+"
|
|
269
280
|
if self.host:
|
|
270
281
|
description = header + "\n"
|
|
@@ -301,6 +312,12 @@ class ControlPoint:
|
|
|
301
312
|
)
|
|
302
313
|
)
|
|
303
314
|
description += header + "\n"
|
|
315
|
+
|
|
316
|
+
# Add warnings section if there are devices with incomplete data
|
|
317
|
+
if self.warnings:
|
|
318
|
+
description += "| WARNING: One or more devices have incomplete data. Check for WARNINGS and upgrade firmware with Zimi app |\n"
|
|
319
|
+
description += "+" + "-" * 130 + "+\n"
|
|
320
|
+
|
|
304
321
|
description_details = []
|
|
305
322
|
for key in self.manufacture_infos:
|
|
306
323
|
description_details.append(self.manufacture_infos[key].describe())
|
|
@@ -340,12 +357,18 @@ class ControlPoint:
|
|
|
340
357
|
try:
|
|
341
358
|
await asyncio.wait_for(
|
|
342
359
|
self.manufacture_info_ready,
|
|
343
|
-
timeout=ControlPointProtocol.
|
|
360
|
+
timeout=ControlPointProtocol.DEVICE_INFO_TIMEOUT,
|
|
344
361
|
)
|
|
345
362
|
except asyncio.exceptions.TimeoutError as error:
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
363
|
+
if IGNORE_MISSING_MANUFACTURE_INFO:
|
|
364
|
+
self.logger.warning(
|
|
365
|
+
"ZCC connection warning - timeout waiting for manufacture_info."
|
|
366
|
+
)
|
|
367
|
+
else:
|
|
368
|
+
raise ControlPointError(
|
|
369
|
+
"ZCC connection failed - didn't receive manufacture_info."
|
|
370
|
+
) from error
|
|
371
|
+
# ) from error
|
|
349
372
|
|
|
350
373
|
await asyncio.sleep(ControlPointProtocol.STEP_TIMEOUT)
|
|
351
374
|
|
|
@@ -386,17 +409,19 @@ class ControlPoint:
|
|
|
386
409
|
ControlPointProtocol.get("states"), response_expected=False
|
|
387
410
|
)
|
|
388
411
|
|
|
389
|
-
self.logger.debug("Over-riding states_ready future")
|
|
390
|
-
self.states_ready.set_result(True)
|
|
391
|
-
|
|
392
412
|
try:
|
|
393
413
|
await asyncio.wait_for(
|
|
394
414
|
self.states_ready, timeout=ControlPointProtocol.DEVICE_GET_TIMEOUT
|
|
395
415
|
)
|
|
396
416
|
except asyncio.exceptions.TimeoutError as error:
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
417
|
+
if IGNORE_MISSING_STATES:
|
|
418
|
+
self.logger.info(
|
|
419
|
+
"ZCC connection warning - timeout waiting for device states."
|
|
420
|
+
)
|
|
421
|
+
else:
|
|
422
|
+
raise ControlPointError(
|
|
423
|
+
"ZCC connection failed - didn't receive any states."
|
|
424
|
+
) from error
|
|
400
425
|
|
|
401
426
|
await asyncio.sleep(ControlPointProtocol.STEP_TIMEOUT)
|
|
402
427
|
|
|
@@ -443,11 +468,7 @@ class ControlPoint:
|
|
|
443
468
|
await self.re_connect()
|
|
444
469
|
return
|
|
445
470
|
|
|
446
|
-
self.logger.debug(
|
|
447
|
-
"notify() received at %s:\n%s",
|
|
448
|
-
datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
|
449
|
-
response,
|
|
450
|
-
)
|
|
471
|
+
self.logger.debug("notify() received:\n%s", response)
|
|
451
472
|
|
|
452
473
|
if response.get(ControlPointProtocol.AUTH_APP_FAILED, None):
|
|
453
474
|
self.logger.error("Authorisation failed\n%s", pformat(response))
|
|
@@ -64,6 +64,8 @@ class ControlPointDevice:
|
|
|
64
64
|
@property
|
|
65
65
|
def __states(self) -> str:
|
|
66
66
|
"""Gets a descriptive string of the device state"""
|
|
67
|
+
if self.type == "behaviourlink":
|
|
68
|
+
return ""
|
|
67
69
|
description = ""
|
|
68
70
|
try:
|
|
69
71
|
key = list(self.states["controlState"].keys())[0]
|
|
@@ -76,9 +78,9 @@ class ControlPointDevice:
|
|
|
76
78
|
if fan_speed:
|
|
77
79
|
description += "/" + str(fan_speed)
|
|
78
80
|
except IndexError:
|
|
79
|
-
|
|
81
|
+
description = "WARNING"
|
|
80
82
|
except KeyError:
|
|
81
|
-
|
|
83
|
+
description = "WARNING"
|
|
82
84
|
return description
|
|
83
85
|
|
|
84
86
|
@property
|
|
@@ -16,7 +16,8 @@ class ControlPointProtocol:
|
|
|
16
16
|
START_SESSION_FAILED = "start_session_failed"
|
|
17
17
|
START_SESSION_SUCCESS = "start_session_success"
|
|
18
18
|
START_SESSION_TIMEOUT = 30
|
|
19
|
-
DEVICE_GET_TIMEOUT =
|
|
19
|
+
DEVICE_GET_TIMEOUT = 30
|
|
20
|
+
DEVICE_INFO_TIMEOUT = 120
|
|
20
21
|
CONTROLPOINT_PROPERTIES = "controlpoint_properties"
|
|
21
22
|
CONTROLPOINT_STATES = "controlpoint_states"
|
|
22
23
|
CONTROLPOINT_ACTIONS = "controlpoint_actions"
|
|
@@ -1,44 +1,45 @@
|
|
|
1
|
-
|
|
1
|
+
'''ZCC Watchdog Timer Class'''
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import logging
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class ControlPointWatchdog:
|
|
8
|
-
|
|
8
|
+
'''A class to create a timer with callback.'''
|
|
9
9
|
|
|
10
10
|
def __init__(self, timeout: int, callback):
|
|
11
|
-
|
|
11
|
+
|
|
12
|
+
self.logger = logging.getLogger('ControlPointWatchdog')
|
|
12
13
|
|
|
13
14
|
self._busy = False
|
|
14
15
|
self._timeout = timeout
|
|
15
16
|
self._callback = callback
|
|
16
17
|
self._task = None
|
|
17
|
-
self.logger.debug(
|
|
18
|
+
self.logger.debug(
|
|
19
|
+
"Created watchdog timer for %d seconds", self._timeout)
|
|
18
20
|
|
|
19
21
|
async def _job(self):
|
|
20
22
|
await asyncio.sleep(self._timeout)
|
|
21
|
-
self.logger.debug("Watchdog timer expired after %d seconds", self._timeout)
|
|
22
23
|
self._busy = True
|
|
23
24
|
await self._callback()
|
|
24
25
|
self._busy = False
|
|
25
26
|
|
|
26
27
|
def cancel(self):
|
|
27
|
-
|
|
28
|
+
'''Cancel the timer.'''
|
|
28
29
|
if not self._busy:
|
|
29
30
|
self._task.cancel()
|
|
30
31
|
|
|
31
32
|
def pause(self):
|
|
32
|
-
|
|
33
|
+
'''Pause the timer'''
|
|
33
34
|
if self._task:
|
|
34
35
|
self.cancel()
|
|
35
36
|
|
|
36
37
|
def reset(self):
|
|
37
|
-
|
|
38
|
+
'''Reset the timer.'''
|
|
38
39
|
if self._task:
|
|
39
40
|
self.cancel()
|
|
40
41
|
self.start()
|
|
41
42
|
|
|
42
43
|
def start(self):
|
|
43
|
-
|
|
44
|
+
'''Start the timer.'''
|
|
44
45
|
self._task = asyncio.ensure_future(self._job())
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|