pyvlx 0.2.27__py3-none-any.whl

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 (84) hide show
  1. pyvlx/__init__.py +21 -0
  2. pyvlx/api/__init__.py +23 -0
  3. pyvlx/api/activate_scene.py +63 -0
  4. pyvlx/api/api_event.py +73 -0
  5. pyvlx/api/command_send.py +85 -0
  6. pyvlx/api/factory_default.py +34 -0
  7. pyvlx/api/frame_creation.py +202 -0
  8. pyvlx/api/frames/__init__.py +76 -0
  9. pyvlx/api/frames/alias_array.py +45 -0
  10. pyvlx/api/frames/frame.py +56 -0
  11. pyvlx/api/frames/frame_activate_scene.py +92 -0
  12. pyvlx/api/frames/frame_activation_log_updated.py +14 -0
  13. pyvlx/api/frames/frame_command_send.py +280 -0
  14. pyvlx/api/frames/frame_discover_nodes.py +64 -0
  15. pyvlx/api/frames/frame_error_notification.py +42 -0
  16. pyvlx/api/frames/frame_facory_default.py +32 -0
  17. pyvlx/api/frames/frame_get_all_nodes_information.py +218 -0
  18. pyvlx/api/frames/frame_get_limitation.py +127 -0
  19. pyvlx/api/frames/frame_get_local_time.py +38 -0
  20. pyvlx/api/frames/frame_get_network_setup.py +64 -0
  21. pyvlx/api/frames/frame_get_node_information.py +223 -0
  22. pyvlx/api/frames/frame_get_protocol_version.py +53 -0
  23. pyvlx/api/frames/frame_get_scene_list.py +82 -0
  24. pyvlx/api/frames/frame_get_state.py +47 -0
  25. pyvlx/api/frames/frame_get_version.py +72 -0
  26. pyvlx/api/frames/frame_helper.py +40 -0
  27. pyvlx/api/frames/frame_house_status_monitor_disable_cfm.py +14 -0
  28. pyvlx/api/frames/frame_house_status_monitor_disable_req.py +14 -0
  29. pyvlx/api/frames/frame_house_status_monitor_enable_cfm.py +14 -0
  30. pyvlx/api/frames/frame_house_status_monitor_enable_req.py +14 -0
  31. pyvlx/api/frames/frame_leave_learn_state.py +41 -0
  32. pyvlx/api/frames/frame_node_information_changed.py +57 -0
  33. pyvlx/api/frames/frame_node_state_position_changed_notification.py +84 -0
  34. pyvlx/api/frames/frame_password_change.py +114 -0
  35. pyvlx/api/frames/frame_password_enter.py +70 -0
  36. pyvlx/api/frames/frame_reboot.py +32 -0
  37. pyvlx/api/frames/frame_set_node_name.py +73 -0
  38. pyvlx/api/frames/frame_set_utc.py +45 -0
  39. pyvlx/api/frames/frame_status_request.py +212 -0
  40. pyvlx/api/get_all_nodes_information.py +46 -0
  41. pyvlx/api/get_limitation.py +64 -0
  42. pyvlx/api/get_local_time.py +34 -0
  43. pyvlx/api/get_network_setup.py +34 -0
  44. pyvlx/api/get_node_information.py +42 -0
  45. pyvlx/api/get_protocol_version.py +40 -0
  46. pyvlx/api/get_scene_list.py +49 -0
  47. pyvlx/api/get_state.py +43 -0
  48. pyvlx/api/get_version.py +34 -0
  49. pyvlx/api/house_status_monitor.py +52 -0
  50. pyvlx/api/leave_learn_state.py +33 -0
  51. pyvlx/api/password_enter.py +39 -0
  52. pyvlx/api/reboot.py +33 -0
  53. pyvlx/api/session_id.py +20 -0
  54. pyvlx/api/set_node_name.py +32 -0
  55. pyvlx/api/set_utc.py +31 -0
  56. pyvlx/api/status_request.py +48 -0
  57. pyvlx/config.py +54 -0
  58. pyvlx/connection.py +182 -0
  59. pyvlx/const.py +685 -0
  60. pyvlx/dataobjects.py +161 -0
  61. pyvlx/discovery.py +100 -0
  62. pyvlx/exception.py +26 -0
  63. pyvlx/heartbeat.py +79 -0
  64. pyvlx/klf200gateway.py +167 -0
  65. pyvlx/lightening_device.py +102 -0
  66. pyvlx/log.py +4 -0
  67. pyvlx/node.py +74 -0
  68. pyvlx/node_helper.py +165 -0
  69. pyvlx/node_updater.py +162 -0
  70. pyvlx/nodes.py +99 -0
  71. pyvlx/on_off_switch.py +44 -0
  72. pyvlx/opening_device.py +644 -0
  73. pyvlx/parameter.py +357 -0
  74. pyvlx/py.typed +0 -0
  75. pyvlx/pyvlx.py +124 -0
  76. pyvlx/scene.py +53 -0
  77. pyvlx/scenes.py +60 -0
  78. pyvlx/slip.py +48 -0
  79. pyvlx/string_helper.py +20 -0
  80. pyvlx-0.2.27.dist-info/METADATA +122 -0
  81. pyvlx-0.2.27.dist-info/RECORD +84 -0
  82. pyvlx-0.2.27.dist-info/WHEEL +5 -0
  83. pyvlx-0.2.27.dist-info/licenses/LICENSE +165 -0
  84. pyvlx-0.2.27.dist-info/top_level.txt +1 -0
pyvlx/dataobjects.py ADDED
@@ -0,0 +1,161 @@
1
+ """Module for Dataobjects."""
2
+ import time
3
+ from datetime import datetime
4
+ from typing import Optional
5
+
6
+ from .const import (
7
+ DHCPParameter, GatewayState, GatewaySubState,
8
+ LeaveLearnStateConfirmationStatus)
9
+
10
+
11
+ class DtoLocalTime:
12
+ """Dataobject to hold KLF200 Time Data."""
13
+
14
+ def __init__(self, utctime: Optional[datetime] = None, localtime: Optional[datetime] = None):
15
+ """Initialize DtoLocalTime class."""
16
+ if utctime is None:
17
+ utctime = datetime.fromtimestamp(0)
18
+ if localtime is None:
19
+ localtime = datetime.fromtimestamp(0)
20
+ self.utctime = utctime
21
+ self.localtime = localtime
22
+
23
+ def __str__(self) -> str:
24
+ """Return human readable string."""
25
+ return (
26
+ '<{} utctime="{}" localtime="{}"/>'.format(
27
+ type(self).__name__, self.utctime, self.localtime)
28
+ )
29
+
30
+ def from_payload(self, payload: bytes) -> None:
31
+ """Build the Dto From Data."""
32
+ self.utctime = datetime.fromtimestamp(int.from_bytes(payload[0:4], "big"))
33
+ weekday = payload[11] - 1
34
+ if weekday == -1:
35
+ weekday = 6
36
+
37
+ self.localtime = datetime.fromtimestamp(time.mktime(
38
+ (int.from_bytes(payload[9:11], byteorder='big') + 1900, # Year
39
+ payload[8], # month
40
+ payload[7], # day
41
+ payload[6], # hour
42
+ payload[5], # minute
43
+ payload[4], # second
44
+ weekday,
45
+ int.from_bytes(payload[12:14], byteorder='big'), # day of year
46
+ int.from_bytes(payload[14:15], byteorder='big', signed=True))))
47
+
48
+ def to_payload(self) -> bytes:
49
+ """Build the Dto From Data."""
50
+ payload = b''
51
+ payload = int(self.utctime.timestamp()).to_bytes(4, byteorder='big')
52
+ payload += self.localtime.second.to_bytes(1, "big")
53
+ payload += self.localtime.minute.to_bytes(1, "big")
54
+ payload += self.localtime.hour.to_bytes(1, "big")
55
+ payload += self.localtime.day.to_bytes(1, "big")
56
+ payload += self.localtime.month.to_bytes(1, "big")
57
+ payload += (self.localtime.year - 1900).to_bytes(2, "big")
58
+ if self.localtime.weekday == 6:
59
+ payload += (0).to_bytes(1, "big")
60
+ else:
61
+ payload += (self.localtime.weekday() + 1).to_bytes(1, "big")
62
+ payload += self.localtime.timetuple().tm_yday.to_bytes(2, "big")
63
+ payload += (self.localtime.timetuple().tm_isdst).to_bytes(1, "big", signed=True)
64
+ return payload
65
+
66
+
67
+ class DtoNetworkSetup:
68
+ """Dataobject to hold KLF200 Network Setup."""
69
+
70
+ def __init__(self,
71
+ ipaddress: Optional[str] = None,
72
+ gateway: Optional[str] = None,
73
+ netmask: Optional[str] = None,
74
+ dhcp: Optional[DHCPParameter] = None):
75
+ """Initialize DtoNetworkSetup class."""
76
+ self.ipaddress = ipaddress
77
+ self.gateway = gateway
78
+ self.netmask = netmask
79
+ self.dhcp = dhcp
80
+
81
+ def __str__(self) -> str:
82
+ """Return human readable string."""
83
+ return '<{} ipaddress="{}" gateway="{}" gateway="{}" dhcp="{}"/>'.format(
84
+ type(self).__name__, self.ipaddress, self.gateway,
85
+ self.gateway, self.dhcp
86
+ )
87
+
88
+
89
+ class DtoProtocolVersion:
90
+ """KLF 200 Dataobject for Protocol version."""
91
+
92
+ def __init__(self, majorversion: Optional[int] = None, minorversion: Optional[int] = None):
93
+ """Initialize DtoProtocolVersion class."""
94
+ self.majorversion = majorversion
95
+ self.minorversion = minorversion
96
+
97
+ def __str__(self) -> str:
98
+ """Return human readable string."""
99
+ return (
100
+ '<{} majorversion="{}" minorversion="{}"/>'.format(
101
+ type(self).__name__, self.majorversion, self.minorversion
102
+ )
103
+ )
104
+
105
+
106
+ class DtoState:
107
+ """Data Object for Gateway State."""
108
+
109
+ def __init__(self, gateway_state: Optional[GatewayState] = None, gateway_sub_state: Optional[GatewaySubState] = None):
110
+ """Initialize DtoState class."""
111
+ self.gateway_state = gateway_state
112
+ self.gateway_sub_state = gateway_sub_state
113
+
114
+ def __str__(self) -> str:
115
+ """Return human readable string."""
116
+ return (
117
+ '<{} gateway_state="{}" gateway_sub_state="{}"/>'.format(
118
+ type(self).__name__, self.gateway_state, self.gateway_sub_state
119
+ )
120
+ )
121
+
122
+
123
+ class DtoVersion:
124
+ """Object for KLF200 Version Information."""
125
+
126
+ def __init__(self,
127
+ softwareversion: Optional[str] = None,
128
+ hardwareversion: Optional[int] = None,
129
+ productgroup: Optional[int] = None,
130
+ producttype: Optional[int] = None):
131
+ """Initialize DtoVersion class."""
132
+ self.softwareversion = softwareversion
133
+ self.hardwareversion = hardwareversion
134
+ self.productgroup = productgroup
135
+ self.producttype = producttype
136
+
137
+ def __str__(self) -> str:
138
+ """Return human readable string."""
139
+ return (
140
+ '<{} softwareversion="{}" hardwareversion="{}" '
141
+ 'productgroup="{}" producttype="{}"/>'.format(
142
+ type(self).__name__,
143
+ self.softwareversion, self.hardwareversion, self.productgroup, self.producttype
144
+ )
145
+ )
146
+
147
+
148
+ class DtoLeaveLearnState:
149
+ """Dataobject to hold KLF200 Data."""
150
+
151
+ def __init__(self, status: Optional[LeaveLearnStateConfirmationStatus] = None):
152
+ """Initialize DtoLeaveLearnState class."""
153
+ self.status = status
154
+
155
+ def __str__(self) -> str:
156
+ """Return human readable string."""
157
+ return (
158
+ '<{} status="{}"/>'.format(
159
+ type(self).__name__, self.status
160
+ )
161
+ )
pyvlx/discovery.py ADDED
@@ -0,0 +1,100 @@
1
+ """Module to discover Velux KLF200 devices on the network."""
2
+ import asyncio
3
+ from asyncio import Event, Future, Task
4
+ from dataclasses import dataclass
5
+ from typing import Any, Optional, Set
6
+
7
+ from zeroconf import IPVersion
8
+ from zeroconf.asyncio import (
9
+ AsyncServiceBrowser, AsyncServiceInfo, AsyncZeroconf)
10
+
11
+ SERVICE_STARTS_WITH: str = "VELUX_KLF_LAN"
12
+ SERVICE_TYPE: str = "_http._tcp.local."
13
+
14
+
15
+ @dataclass
16
+ class VeluxHost():
17
+ """Class to store Velux KLF200 host information."""
18
+
19
+ hostname: str
20
+ ip_address: str
21
+
22
+
23
+ def sanitize_hostname(hostname: str) -> str:
24
+ """Sanitize KLF200 hostname."""
25
+ hostname = hostname.upper()
26
+ hostname = hostname.replace("._HTTP._TCP.LOCAL.", "")
27
+ # KLF report it's hostname within Zeroconf ServiceInfo with the "LAN_" prefix, on DHCP requests without
28
+ hostname = hostname.replace("LAN_", "")
29
+ return hostname
30
+
31
+
32
+ class VeluxDiscovery():
33
+ """Class to discover Velux KLF200 devices on the network."""
34
+
35
+ hosts: list[VeluxHost] = []
36
+ infos: list[AsyncServiceInfo | None] = []
37
+
38
+ def __init__(self, zeroconf: AsyncZeroconf,) -> None:
39
+ """Initialize VeluxDiscovery object."""
40
+ self.zc: AsyncZeroconf = zeroconf
41
+
42
+ async def _async_discover_hosts(self, min_wait_time: float, expected_hosts: int | None) -> None:
43
+ """Listen for zeroconf ServiceInfo."""
44
+ self.hosts.clear()
45
+ service_names: list[str] = []
46
+ tasks: Set[Task] = set()
47
+ got_host: Event = Event()
48
+
49
+ def add_info_and_host(fut: Future) -> None:
50
+ info: AsyncServiceInfo = fut.result()
51
+ self.infos.append(info)
52
+ hostname = sanitize_hostname(info.name)
53
+ host = VeluxHost(
54
+ hostname=hostname,
55
+ ip_address=info.parsed_addresses(version=IPVersion.V4Only)[0],
56
+ )
57
+ self.hosts.append(host)
58
+ got_host.set()
59
+
60
+ def handler(name: str, **kwargs: Any) -> None: # pylint: disable=W0613:unused-argument
61
+ if name.startswith(SERVICE_STARTS_WITH):
62
+ if name not in service_names:
63
+ service_names.append(name)
64
+ task = asyncio.create_task(self.zc.async_get_service_info(type_=SERVICE_TYPE, name=name))
65
+ tasks.add(task)
66
+ task.add_done_callback(add_info_and_host)
67
+ task.add_done_callback(tasks.remove)
68
+
69
+ browser: AsyncServiceBrowser = AsyncServiceBrowser(self.zc.zeroconf, SERVICE_TYPE, handlers=[handler])
70
+ if expected_hosts:
71
+ while len(self.hosts) < expected_hosts:
72
+ await got_host.wait()
73
+ got_host.clear()
74
+ while not self.hosts:
75
+ await asyncio.sleep(min_wait_time)
76
+ await browser.async_cancel()
77
+ if tasks:
78
+ await asyncio.gather(*tasks)
79
+
80
+ async def async_discover_hosts(
81
+ self,
82
+ timeout: float = 10,
83
+ min_wait_time: float = 3,
84
+ expected_hosts: Optional[int] = None
85
+ ) -> bool:
86
+ """Return true if Velux KLF200 devices found on the network.
87
+
88
+ This function creates a zeroconf AsyncServiceBrowser and
89
+ waits min_wait_time (seconds) for ServiceInfos from hosts.
90
+ Some devices may take some time to respond (i.e. if they currently have a high CPU load).
91
+ If one or more Hosts are found, the function cancels the ServiceBrowser and returns true.
92
+ If expected_hosts is set, the function ignores min_wait_time and returns true once expected_hosts are found.
93
+ If timeout (seconds) is exceeded, the function returns false.
94
+ """
95
+ try:
96
+ async with asyncio.timeout(timeout):
97
+ await self._async_discover_hosts(min_wait_time, expected_hosts)
98
+ except TimeoutError:
99
+ return False
100
+ return True
pyvlx/exception.py ADDED
@@ -0,0 +1,26 @@
1
+ """Module for PyVLX Exceptions."""
2
+ from typing import Any
3
+
4
+
5
+ class PyVLXException(Exception):
6
+ """Default PyVLX Exception."""
7
+
8
+ def __init__(self, description: str, **kwargs: Any):
9
+ """Initialize PyVLXException class."""
10
+ super().__init__(description)
11
+ self.description = description
12
+ self.parameter = kwargs
13
+
14
+ def _format_parameter(self) -> str:
15
+ return " ".join(
16
+ [
17
+ '%s="%s"' % (key, value)
18
+ for (key, value) in sorted(self.parameter.items())
19
+ ]
20
+ )
21
+
22
+ def __str__(self) -> str:
23
+ """Return object as readable string."""
24
+ return '<{} description="{}" {}/>'.format(
25
+ type(self).__name__, self.description, self._format_parameter()
26
+ )
pyvlx/heartbeat.py ADDED
@@ -0,0 +1,79 @@
1
+ """Module for sending get state requests to API in regular periods."""
2
+ import asyncio
3
+ from typing import TYPE_CHECKING, Any
4
+
5
+ from .api import GetState
6
+ from .api.status_request import StatusRequest
7
+ from .exception import PyVLXException
8
+ from .log import PYVLXLOG
9
+ from .opening_device import Blind, DualRollerShutter
10
+
11
+ if TYPE_CHECKING:
12
+ from pyvlx import PyVLX
13
+
14
+
15
+ class Heartbeat:
16
+ """Class for sending heartbeats to API."""
17
+
18
+ def __init__(
19
+ self, pyvlx: "PyVLX", interval: int = 30, load_all_states: bool = True
20
+ ):
21
+ """Initialize Heartbeat object."""
22
+ PYVLXLOG.debug("Heartbeat __init__")
23
+ self.pyvlx = pyvlx
24
+ self.interval = interval
25
+ self.load_all_states = load_all_states
26
+ self.task: Any = None
27
+
28
+ async def _run(self) -> None:
29
+ PYVLXLOG.debug("Heartbeat: task started")
30
+ while True:
31
+ PYVLXLOG.debug("Heartbeat: sleeping")
32
+ await asyncio.sleep(self.interval)
33
+ PYVLXLOG.debug("Heartbeat: pulsing")
34
+ try:
35
+ await self.pulse()
36
+ except (OSError, PyVLXException) as e:
37
+ PYVLXLOG.debug("Heartbeat: pulsing failed: %s", e)
38
+
39
+ async def _start(self) -> None:
40
+ if self.task is not None:
41
+ await self.stop()
42
+ PYVLXLOG.debug("Heartbeat: creating task")
43
+ self.task = asyncio.create_task(self._run())
44
+
45
+ def start(self) -> None:
46
+ """Start heartbeat."""
47
+ PYVLXLOG.debug("Heartbeat start")
48
+ asyncio.run_coroutine_threadsafe(self._start(), self.pyvlx.loop)
49
+
50
+ @property
51
+ def stopped(self) -> bool:
52
+ """Return Heartbeat running state."""
53
+ return self.task is None
54
+
55
+ async def stop(self) -> None:
56
+ """Stop heartbeat."""
57
+ if self.task is not None:
58
+ self.task.cancel()
59
+ self.task = None
60
+ PYVLXLOG.debug("Heartbeat stopped")
61
+ else:
62
+ PYVLXLOG.debug("Heartbeat was not running")
63
+
64
+ async def pulse(self) -> None:
65
+ """Send get state request to API to keep the connection alive."""
66
+ PYVLXLOG.debug("Heartbeat pulse")
67
+ get_state = GetState(pyvlx=self.pyvlx)
68
+ await get_state.do_api_call()
69
+ if not get_state.success:
70
+ raise PyVLXException("Unable to send get state.")
71
+
72
+ # If nodes contain Blind or DualRollerShutter device, refresh orientation or upper/lower curtain positions because House Monitoring
73
+ # delivers wrong values for FP1, FP2 and FP3 parameter
74
+ for node in self.pyvlx.nodes:
75
+ if isinstance(node, (Blind, DualRollerShutter)) or self.load_all_states:
76
+ status_request = StatusRequest(self.pyvlx, node.node_id)
77
+ await status_request.do_api_call()
78
+ # give user requests a chance
79
+ await asyncio.sleep(0.5)
pyvlx/klf200gateway.py ADDED
@@ -0,0 +1,167 @@
1
+ """Module for basic klf200 gateway functions."""
2
+
3
+ from typing import TYPE_CHECKING, Awaitable, Callable, List, Optional
4
+
5
+ from .api import (
6
+ FactoryDefault, GetLocalTime, GetNetworkSetup, GetProtocolVersion,
7
+ GetState, GetVersion, HouseStatusMonitorDisable, HouseStatusMonitorEnable,
8
+ LeaveLearnState, PasswordEnter, Reboot, SetUTC)
9
+ from .dataobjects import (
10
+ DtoLocalTime, DtoNetworkSetup, DtoProtocolVersion, DtoState, DtoVersion)
11
+ from .exception import PyVLXException
12
+
13
+ if TYPE_CHECKING:
14
+ from pyvlx import PyVLX
15
+
16
+ CallbackType = Callable[["Klf200Gateway"], Awaitable[None]]
17
+
18
+
19
+ class Klf200Gateway:
20
+ """Class for node abstraction."""
21
+
22
+ def __init__(self, pyvlx: "PyVLX"):
23
+ """Initialize Node object."""
24
+ self.pyvlx = pyvlx
25
+ self.state: Optional[DtoState] = None
26
+ self.network_setup: Optional[DtoNetworkSetup] = None
27
+ self.password: Optional[str] = None
28
+ self.time: Optional[DtoLocalTime] = None
29
+ self.protocol_version: Optional[DtoProtocolVersion] = None
30
+ self.version: Optional[DtoVersion] = None
31
+ self.device_updated_cbs: List[CallbackType] = []
32
+ self.house_status_monitor_enabled = False
33
+
34
+ def register_device_updated_cb(self, device_updated_cb: CallbackType) -> None:
35
+ """Register device updated callback."""
36
+ self.device_updated_cbs.append(device_updated_cb)
37
+
38
+ def unregister_device_updated_cb(self, device_updated_cb: CallbackType) -> None:
39
+ """Unregister device updated callback."""
40
+ self.device_updated_cbs.remove(device_updated_cb)
41
+
42
+ async def after_update(self) -> None:
43
+ """Execute callbacks after internal state has been changed."""
44
+ for device_updated_cb in self.device_updated_cbs:
45
+ # pylint: disable=not-callable
46
+ await device_updated_cb(self)
47
+
48
+ async def get_state(self) -> bool:
49
+ """Retrieve state from API."""
50
+ get_state = GetState(pyvlx=self.pyvlx)
51
+ await get_state.do_api_call()
52
+ if not get_state.success:
53
+ raise PyVLXException("Unable to retrieve state")
54
+ self.state = get_state.state
55
+ return get_state.success
56
+
57
+ async def get_network_setup(self) -> bool:
58
+ """Retrieve network setup from API."""
59
+ get_network_setup = GetNetworkSetup(pyvlx=self.pyvlx)
60
+ await get_network_setup.do_api_call()
61
+ if not get_network_setup.success:
62
+ raise PyVLXException("Unable to retrieve network setup")
63
+ self.network_setup = get_network_setup.networksetup
64
+ return get_network_setup.success
65
+
66
+ async def get_version(self) -> bool:
67
+ """Retrieve version from API."""
68
+ get_version = GetVersion(pyvlx=self.pyvlx)
69
+ await get_version.do_api_call()
70
+ if not get_version.success:
71
+ raise PyVLXException("Unable to retrieve version")
72
+ self.version = get_version.version
73
+ return get_version.success
74
+
75
+ async def get_protocol_version(self) -> bool:
76
+ """Retrieve protocol version from API."""
77
+ get_protocol_version = GetProtocolVersion(pyvlx=self.pyvlx)
78
+ await get_protocol_version.do_api_call()
79
+ if not get_protocol_version.success:
80
+ raise PyVLXException("Unable to retrieve protocol version")
81
+ self.protocol_version = get_protocol_version.protocolversion
82
+ return get_protocol_version.success
83
+
84
+ async def leave_learn_state(self) -> bool:
85
+ """Leave Learn state from API."""
86
+ leave_learn_state = LeaveLearnState(pyvlx=self.pyvlx)
87
+ await leave_learn_state.do_api_call()
88
+ if not leave_learn_state.success:
89
+ raise PyVLXException("Unable to leave learn state")
90
+ return leave_learn_state.success
91
+
92
+ async def set_utc(self) -> bool:
93
+ """Set UTC Clock."""
94
+ setutc = SetUTC(pyvlx=self.pyvlx)
95
+ await setutc.do_api_call()
96
+ if not setutc.success:
97
+ raise PyVLXException("Unable to set utc.")
98
+ return setutc.success
99
+
100
+ async def set_rtc_time_zone(self) -> None:
101
+ """Set the RTC Time Zone."""
102
+ # idontwant = setrtctimezone(pyvlx=self.pyvlx)
103
+ raise PyVLXException("KLF 200 RTC Timezone Set not implemented")
104
+ # return setrtctimezone.success
105
+
106
+ async def reboot(self) -> bool:
107
+ """Reboot gateway."""
108
+ reboot = Reboot(pyvlx=self.pyvlx)
109
+ await reboot.do_api_call()
110
+ if not reboot.success:
111
+ raise PyVLXException("Unable to reboot gateway.")
112
+ await self.pyvlx.disconnect()
113
+ return reboot.success
114
+
115
+ async def set_factory_default(self) -> bool:
116
+ """Set Gateway to Factory Default."""
117
+ factorydefault = FactoryDefault(pyvlx=self.pyvlx)
118
+ await factorydefault.do_api_call()
119
+ if not factorydefault.success:
120
+ raise PyVLXException("Unable to factory Default Reset gateway.")
121
+ return factorydefault.success
122
+
123
+ async def get_local_time(self) -> bool:
124
+ """Get local time from gateway."""
125
+ getlocaltime = GetLocalTime(pyvlx=self.pyvlx)
126
+ await getlocaltime.do_api_call()
127
+ if not getlocaltime.success:
128
+ raise PyVLXException("Unable to get local time.")
129
+ self.time = getlocaltime.localtime
130
+ return getlocaltime.success
131
+
132
+ async def password_enter(self, password: str) -> bool:
133
+ """Get enter Password for gateway."""
134
+ self.password = password
135
+ passwordenter = PasswordEnter(pyvlx=self.pyvlx, password=self.password)
136
+ await passwordenter.do_api_call()
137
+ if not passwordenter.success:
138
+ raise PyVLXException("Login to KLF 200 failed, check credentials")
139
+ return passwordenter.success
140
+
141
+ async def house_status_monitor_enable(self, pyvlx: "PyVLX") -> None:
142
+ """Enable house status monitor."""
143
+ status_monitor_enable = HouseStatusMonitorEnable(pyvlx=pyvlx)
144
+ await status_monitor_enable.do_api_call()
145
+ if not status_monitor_enable.success:
146
+ raise PyVLXException("Unable enable house status monitor.")
147
+ self.house_status_monitor_enabled = True
148
+
149
+ async def house_status_monitor_disable(self, pyvlx: "PyVLX", timeout: Optional[int] = None) -> None:
150
+ """Disable house status monitor."""
151
+ status_monitor_disable = HouseStatusMonitorDisable(pyvlx=pyvlx)
152
+ if timeout is not None:
153
+ status_monitor_disable.timeout_in_seconds = timeout
154
+ await status_monitor_disable.do_api_call()
155
+ if not status_monitor_disable.success:
156
+ raise PyVLXException("Unable disable house status monitor.")
157
+ self.house_status_monitor_enabled = False
158
+
159
+ def __str__(self) -> str:
160
+ """Return object as readable string."""
161
+ return '<{} state="{}" network_setup="{}" version="{}" protocol_version="{}"/>'.format(
162
+ type(self).__name__,
163
+ str(self.state),
164
+ str(self.network_setup),
165
+ str(self.version),
166
+ str(self.protocol_version),
167
+ )
@@ -0,0 +1,102 @@
1
+ """Module for lights."""
2
+ from typing import TYPE_CHECKING, Optional
3
+
4
+ from .api import CommandSend
5
+ from .node import Node
6
+ from .parameter import Intensity
7
+
8
+ if TYPE_CHECKING:
9
+ from pyvlx import PyVLX
10
+
11
+
12
+ class LighteningDevice(Node):
13
+ """Meta class for turning on device with one main parameter for intensity."""
14
+
15
+ def __init__(self, pyvlx: "PyVLX", node_id: int, name: str, serial_number: Optional[str]):
16
+ """Initialize turning on device.
17
+
18
+ Parameters:
19
+ * pyvlx: PyVLX object
20
+ * node_id: internal id for addressing nodes.
21
+ Provided by KLF 200 device
22
+ * name: node name
23
+ * serial_number: serial number of the node.
24
+
25
+ """
26
+ super().__init__(
27
+ pyvlx=pyvlx, node_id=node_id, name=name, serial_number=serial_number
28
+ )
29
+ self.intensity = Intensity()
30
+
31
+ async def set_intensity(self, intensity: Intensity, wait_for_completion: bool = True) -> None:
32
+ """Set light to desired intensity.
33
+
34
+ Parameters:
35
+ * intensity: Intensity object containing the target intensity.
36
+ * wait_for_completion: If set, function will return
37
+ after device has reached target intensity.
38
+
39
+ """
40
+ command = CommandSend(
41
+ pyvlx=self.pyvlx,
42
+ wait_for_completion=wait_for_completion,
43
+ node_id=self.node_id,
44
+ parameter=intensity,
45
+ )
46
+ await command.send()
47
+ await self.after_update()
48
+
49
+ async def turn_on(self, wait_for_completion: bool = True) -> None:
50
+ """Turn on light.
51
+
52
+ Parameters:
53
+ * wait_for_completion: If set, function will return
54
+ after device has reached target intensity.
55
+
56
+ """
57
+ await self.set_intensity(
58
+ intensity=Intensity(intensity_percent=0),
59
+ wait_for_completion=wait_for_completion,
60
+ )
61
+
62
+ async def turn_off(self, wait_for_completion: bool = True) -> None:
63
+ """Turn off light.
64
+
65
+ Parameters:
66
+ * wait_for_completion: If set, function will return
67
+ after device has reached target intensity.
68
+
69
+ """
70
+ await self.set_intensity(
71
+ intensity=Intensity(intensity_percent=100),
72
+ wait_for_completion=wait_for_completion,
73
+ )
74
+
75
+
76
+ class Light(LighteningDevice):
77
+ """Light object."""
78
+
79
+ def __init__(self, pyvlx: "PyVLX", node_id: int, name: str, serial_number: Optional[str]):
80
+ """Initialize Light class.
81
+
82
+ Parameters:
83
+ * pyvlx: PyVLX object
84
+ * node_id: internal id for addressing nodes.
85
+ Provided by KLF 200 device
86
+ * name: node name
87
+ * serial_number: serial number of the node.
88
+
89
+ """
90
+ super().__init__(
91
+ pyvlx=pyvlx, node_id=node_id, name=name, serial_number=serial_number
92
+ )
93
+
94
+ def __str__(self) -> str:
95
+ """Return object as readable string."""
96
+ return (
97
+ '<{} name="{}" '
98
+ 'node_id="{}" '
99
+ 'serial_number="{}"/>'.format(
100
+ type(self).__name__, self.name, self.node_id, self.serial_number
101
+ )
102
+ )
pyvlx/log.py ADDED
@@ -0,0 +1,4 @@
1
+ """Module for global PyVLX logging object."""
2
+ import logging
3
+
4
+ PYVLXLOG = logging.getLogger("pyvlx")