velbus-aio 2021.8.7__py3-none-any.whl → 2025.11.0__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.
- scripts/parse_specs.py +156 -0
- velbus_aio-2025.11.0.dist-info/METADATA +71 -0
- velbus_aio-2025.11.0.dist-info/RECORD +194 -0
- {velbus_aio-2021.8.7.dist-info → velbus_aio-2025.11.0.dist-info}/WHEEL +1 -1
- velbus_aio-2025.11.0.dist-info/top_level.txt +3 -0
- velbusaio/channels.py +443 -109
- velbusaio/command_registry.py +126 -13
- velbusaio/const.py +36 -12
- velbusaio/controller.py +252 -177
- velbusaio/discovery.py +2 -2
- velbusaio/exceptions.py +22 -0
- velbusaio/handler.py +311 -145
- velbusaio/helpers.py +6 -18
- velbusaio/message.py +46 -132
- velbusaio/messages/__init__.py +12 -2
- velbusaio/messages/blind_status.py +16 -25
- velbusaio/messages/bus_active.py +3 -9
- velbusaio/messages/bus_error_counter_status.py +3 -4
- velbusaio/messages/bus_error_counter_status_request.py +3 -4
- velbusaio/messages/bus_off.py +3 -4
- velbusaio/messages/channel_name_part1.py +49 -33
- velbusaio/messages/channel_name_part2.py +49 -33
- velbusaio/messages/channel_name_part3.py +49 -33
- velbusaio/messages/channel_name_request.py +26 -12
- velbusaio/messages/clear_led.py +3 -4
- velbusaio/messages/counter_status.py +3 -17
- velbusaio/messages/counter_status_request.py +6 -6
- velbusaio/messages/counter_value.py +44 -0
- velbusaio/messages/cover_down.py +4 -29
- velbusaio/messages/cover_off.py +5 -29
- velbusaio/messages/cover_position.py +4 -19
- velbusaio/messages/cover_up.py +4 -27
- velbusaio/messages/dali_device_settings.py +178 -0
- velbusaio/messages/dali_device_settings_request.py +53 -0
- velbusaio/messages/dali_dim_value_status.py +44 -0
- velbusaio/messages/dimmer_channel_status.py +6 -19
- velbusaio/messages/dimmer_status.py +14 -31
- velbusaio/messages/edge_set_color.py +114 -0
- velbusaio/messages/edge_set_custom_color.py +56 -0
- velbusaio/messages/fast_blinking_led.py +3 -4
- velbusaio/messages/forced_off.py +3 -4
- velbusaio/messages/forced_on.py +3 -4
- velbusaio/messages/interface_status_request.py +3 -4
- velbusaio/messages/ir_receiver_status.py +18 -0
- velbusaio/messages/kwh_status.py +3 -19
- velbusaio/messages/light_value_request.py +3 -4
- velbusaio/messages/memo_text.py +3 -5
- velbusaio/messages/memory_data.py +3 -16
- velbusaio/messages/memory_data_block.py +3 -4
- velbusaio/messages/memory_dump_request.py +3 -4
- velbusaio/messages/module_status.py +107 -55
- velbusaio/messages/module_status_request.py +7 -6
- velbusaio/messages/module_subtype.py +11 -19
- velbusaio/messages/module_type.py +132 -21
- velbusaio/messages/module_type_request.py +1 -0
- velbusaio/messages/psu_load.py +56 -0
- velbusaio/messages/psu_values.py +53 -0
- velbusaio/messages/push_button_status.py +3 -16
- velbusaio/messages/raw.py +74 -0
- velbusaio/messages/read_data_block_from_memory.py +3 -4
- velbusaio/messages/read_data_from_memory.py +3 -4
- velbusaio/messages/realtime_clock_status_request.py +3 -4
- velbusaio/messages/receive_buffer_full.py +3 -4
- velbusaio/messages/receive_ready.py +3 -4
- velbusaio/messages/relay_status.py +13 -42
- velbusaio/messages/restore_dimmer.py +33 -24
- velbusaio/messages/select_program.py +35 -0
- velbusaio/messages/sensor_settings_request.py +3 -4
- velbusaio/messages/sensor_temp_request.py +3 -4
- velbusaio/messages/sensor_temperature.py +15 -19
- velbusaio/messages/set_date.py +10 -30
- velbusaio/messages/set_daylight_saving.py +8 -24
- velbusaio/messages/set_dimmer.py +43 -41
- velbusaio/messages/set_led.py +3 -4
- velbusaio/messages/set_realtime_clock.py +10 -30
- velbusaio/messages/set_temperature.py +3 -4
- velbusaio/messages/slider_status.py +16 -20
- velbusaio/messages/slow_blinking_led.py +3 -4
- velbusaio/messages/start_relay_blinking_timer.py +3 -4
- velbusaio/messages/start_relay_timer.py +3 -4
- velbusaio/messages/switch_relay_off.py +3 -16
- velbusaio/messages/switch_relay_on.py +3 -16
- velbusaio/messages/switch_to_comfort.py +4 -15
- velbusaio/messages/switch_to_day.py +4 -15
- velbusaio/messages/switch_to_night.py +4 -15
- velbusaio/messages/switch_to_safe.py +4 -15
- velbusaio/messages/temp_sensor_settings_part1.py +3 -4
- velbusaio/messages/temp_sensor_settings_part2.py +27 -0
- velbusaio/messages/temp_sensor_settings_part3.py +27 -0
- velbusaio/messages/temp_sensor_settings_part4.py +27 -0
- velbusaio/messages/temp_sensor_settings_request.py +3 -4
- velbusaio/messages/temp_sensor_status.py +34 -35
- velbusaio/messages/temp_set_cooling.py +3 -13
- velbusaio/messages/temp_set_heating.py +3 -13
- velbusaio/messages/update_led_status.py +3 -4
- velbusaio/messages/very_fast_blinking_led.py +3 -4
- velbusaio/messages/write_data_to_memory.py +3 -4
- velbusaio/messages/write_memory_block.py +3 -4
- velbusaio/messages/write_module_address_and_serial_number.py +3 -4
- velbusaio/module.py +680 -158
- velbusaio/module_spec/01.json +62 -0
- velbusaio/module_spec/02.json +16 -0
- velbusaio/module_spec/03.json +23 -0
- velbusaio/module_spec/04.json +283 -0
- velbusaio/module_spec/05.json +54 -0
- velbusaio/module_spec/06.json +110 -0
- velbusaio/module_spec/07.json +16 -0
- velbusaio/module_spec/08.json +38 -0
- velbusaio/module_spec/09.json +30 -0
- velbusaio/module_spec/0A.json +58 -0
- velbusaio/module_spec/0B.json +58 -0
- velbusaio/module_spec/0C.json +18 -0
- velbusaio/module_spec/0E.json +25 -0
- velbusaio/module_spec/0F.json +16 -0
- velbusaio/module_spec/10.json +111 -0
- velbusaio/module_spec/11.json +111 -0
- velbusaio/module_spec/12.json +73 -0
- velbusaio/module_spec/13.json +4 -0
- velbusaio/module_spec/14.json +16 -0
- velbusaio/module_spec/15.json +83 -0
- velbusaio/module_spec/16.json +129 -0
- velbusaio/module_spec/17.json +129 -0
- velbusaio/module_spec/18.json +129 -0
- velbusaio/module_spec/1A.json +79 -0
- velbusaio/module_spec/1B.json +107 -0
- velbusaio/module_spec/1D.json +89 -0
- velbusaio/module_spec/1E.json +306 -0
- velbusaio/module_spec/1F.json +178 -0
- velbusaio/module_spec/20.json +178 -0
- velbusaio/module_spec/21.json +326 -0
- velbusaio/module_spec/22.json +426 -0
- velbusaio/module_spec/23.json +129 -0
- velbusaio/module_spec/24.json +30 -0
- velbusaio/module_spec/25.json +3 -0
- velbusaio/module_spec/28.json +454 -0
- velbusaio/module_spec/29.json +235 -0
- velbusaio/module_spec/2A.json +239 -0
- velbusaio/module_spec/2B.json +239 -0
- velbusaio/module_spec/2C.json +257 -0
- velbusaio/module_spec/2D.json +270 -0
- velbusaio/module_spec/2E.json +215 -0
- velbusaio/module_spec/2F.json +211 -0
- velbusaio/module_spec/30.json +58 -0
- velbusaio/module_spec/31.json +465 -0
- velbusaio/module_spec/32.json +385 -0
- velbusaio/module_spec/33.json +249 -0
- velbusaio/module_spec/34.json +313 -0
- velbusaio/module_spec/35.json +313 -0
- velbusaio/module_spec/36.json +313 -0
- velbusaio/module_spec/37.json +333 -0
- velbusaio/module_spec/38.json +111 -0
- velbusaio/module_spec/39.json +4 -0
- velbusaio/module_spec/3A.json +306 -0
- velbusaio/module_spec/3B.json +306 -0
- velbusaio/module_spec/3C.json +306 -0
- velbusaio/module_spec/3D.json +454 -0
- velbusaio/module_spec/3E.json +302 -0
- velbusaio/module_spec/3F.json +4 -0
- velbusaio/module_spec/40.json +4 -0
- velbusaio/module_spec/41.json +241 -0
- velbusaio/module_spec/42.json +4 -0
- velbusaio/module_spec/43.json +23 -0
- velbusaio/module_spec/44.json +38 -0
- velbusaio/module_spec/45.json +4 -0
- velbusaio/module_spec/48.json +111 -0
- velbusaio/module_spec/49.json +111 -0
- velbusaio/module_spec/4A.json +89 -0
- velbusaio/module_spec/4B.json +138 -0
- velbusaio/module_spec/4C.json +129 -0
- velbusaio/module_spec/4D.json +108 -0
- velbusaio/module_spec/4E.json +787 -0
- velbusaio/module_spec/4F.json +114 -0
- velbusaio/module_spec/50.json +114 -0
- velbusaio/module_spec/51.json +114 -0
- velbusaio/module_spec/52.json +456 -0
- velbusaio/module_spec/54.json +270 -0
- velbusaio/module_spec/55.json +270 -0
- velbusaio/module_spec/56.json +270 -0
- velbusaio/module_spec/57.json +260 -0
- velbusaio/module_spec/5A.json +4 -0
- velbusaio/module_spec/5B.json +4 -0
- velbusaio/module_spec/5C.json +90 -0
- velbusaio/module_spec/5F.json +78 -0
- velbusaio/module_spec/60.json +4 -0
- velbusaio/module_spec/61.json +89 -0
- velbusaio/module_spec/broadcast.json +67 -0
- velbusaio/module_spec/ignore.json +22 -0
- velbusaio/protocol.py +243 -0
- velbusaio/py.typed +0 -0
- velbusaio/raw_message.py +149 -0
- velbusaio/util.py +55 -0
- velbusaio/vlp_reader.py +249 -0
- velbus_aio-2021.8.7.dist-info/METADATA +0 -66
- velbus_aio-2021.8.7.dist-info/RECORD +0 -90
- velbus_aio-2021.8.7.dist-info/top_level.txt +0 -1
- velbusaio/messages/meteo_raw.py +0 -52
- velbusaio/module_registry.py +0 -64
- velbusaio/moduleprotocol/protocol.json +0 -25540
- velbusaio/parser.py +0 -142
- {velbus_aio-2021.8.7.dist-info → velbus_aio-2025.11.0.dist-info/licenses}/LICENSE +0 -0
velbusaio/controller.py
CHANGED
|
@@ -1,227 +1,299 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
3
|
-
"""
|
|
1
|
+
"""Main interface for the velbusaio lib."""
|
|
2
|
+
|
|
4
3
|
from __future__ import annotations
|
|
5
4
|
|
|
6
5
|
import asyncio
|
|
7
6
|
import logging
|
|
8
|
-
import
|
|
7
|
+
import pathlib
|
|
8
|
+
import re
|
|
9
9
|
import ssl
|
|
10
|
+
import time
|
|
11
|
+
from urllib.parse import urlparse
|
|
10
12
|
|
|
11
13
|
import serial
|
|
12
|
-
import
|
|
14
|
+
import serial_asyncio_fast
|
|
13
15
|
|
|
14
|
-
from velbusaio.
|
|
16
|
+
from velbusaio.channels import (
|
|
17
|
+
Blind,
|
|
18
|
+
Button,
|
|
19
|
+
ButtonCounter,
|
|
20
|
+
Dimmer,
|
|
21
|
+
EdgeLit,
|
|
22
|
+
LightSensor,
|
|
23
|
+
Memo,
|
|
24
|
+
Relay,
|
|
25
|
+
SelectedProgram,
|
|
26
|
+
Sensor,
|
|
27
|
+
SensorNumber,
|
|
28
|
+
Temperature,
|
|
29
|
+
ThermostatChannel,
|
|
30
|
+
)
|
|
31
|
+
from velbusaio.exceptions import VelbusConnectionFailed
|
|
15
32
|
from velbusaio.handler import PacketHandler
|
|
16
33
|
from velbusaio.helpers import get_cache_dir
|
|
34
|
+
from velbusaio.message import Message
|
|
17
35
|
from velbusaio.messages.module_type_request import ModuleTypeRequestMessage
|
|
18
36
|
from velbusaio.messages.set_date import SetDate
|
|
19
37
|
from velbusaio.messages.set_daylight_saving import SetDaylightSaving
|
|
20
38
|
from velbusaio.messages.set_realtime_clock import SetRealtimeClock
|
|
21
39
|
from velbusaio.module import Module
|
|
22
|
-
from velbusaio.
|
|
40
|
+
from velbusaio.protocol import VelbusProtocol
|
|
41
|
+
from velbusaio.raw_message import RawMessage
|
|
42
|
+
from velbusaio.vlp_reader import VlpFile
|
|
23
43
|
|
|
24
44
|
|
|
25
45
|
class Velbus:
|
|
26
|
-
"""
|
|
27
|
-
A velbus controller
|
|
28
|
-
"""
|
|
46
|
+
"""A velbus controller."""
|
|
29
47
|
|
|
30
|
-
def __init__(
|
|
48
|
+
def __init__(
|
|
49
|
+
self,
|
|
50
|
+
dsn: str,
|
|
51
|
+
cache_dir: str = get_cache_dir(),
|
|
52
|
+
vlp_file: str | None = None,
|
|
53
|
+
one_address: int | None = None,
|
|
54
|
+
) -> None:
|
|
55
|
+
"""Init the Velbus controller."""
|
|
31
56
|
self._log = logging.getLogger("velbus")
|
|
32
|
-
|
|
33
|
-
self.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
self.
|
|
38
|
-
self.
|
|
39
|
-
|
|
40
|
-
self.
|
|
41
|
-
self.
|
|
57
|
+
|
|
58
|
+
self._protocol = VelbusProtocol(
|
|
59
|
+
message_received_callback=self._on_message_received,
|
|
60
|
+
connection_lost_callback=self._on_connection_lost,
|
|
61
|
+
)
|
|
62
|
+
self._closing = False
|
|
63
|
+
self._auto_reconnect = True
|
|
64
|
+
|
|
65
|
+
self._destination = dsn
|
|
66
|
+
self._handler = PacketHandler(self, one_address)
|
|
67
|
+
self._modules: dict[int, Module] = {}
|
|
68
|
+
self._submodules: list[int] = []
|
|
69
|
+
self._send_queue: asyncio.Queue = asyncio.Queue()
|
|
70
|
+
self._vlp_file = vlp_file
|
|
71
|
+
self._cache_dir: str = cache_dir
|
|
72
|
+
|
|
73
|
+
def get_cache_dir(self) -> str:
|
|
74
|
+
return self._cache_dir
|
|
75
|
+
|
|
76
|
+
async def _on_message_received(self, msg: RawMessage) -> None:
|
|
77
|
+
"""On message received function."""
|
|
78
|
+
await self._handler.handle(msg)
|
|
79
|
+
|
|
80
|
+
def _on_connection_lost(self, exc: Exception) -> None:
|
|
81
|
+
"""Respond to Protocol connection lost."""
|
|
82
|
+
if self._auto_reconnect and not self._closing:
|
|
83
|
+
self._log.debug("Reconnecting to transport")
|
|
84
|
+
asyncio.ensure_future(self.connect())
|
|
42
85
|
|
|
43
86
|
async def add_module(
|
|
44
87
|
self,
|
|
45
|
-
addr:
|
|
46
|
-
typ:
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
build_week=None,
|
|
88
|
+
addr: int,
|
|
89
|
+
typ: int,
|
|
90
|
+
serial: int | None = None,
|
|
91
|
+
memorymap: int | None = None,
|
|
92
|
+
build_year: int | None = None,
|
|
93
|
+
build_week: int | None = None,
|
|
52
94
|
) -> None:
|
|
53
|
-
"""
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
self.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
serial=serial,
|
|
67
|
-
build_year=build_year,
|
|
68
|
-
build_week=build_week,
|
|
69
|
-
memorymap=memorymap,
|
|
70
|
-
)
|
|
71
|
-
self._modules[addr].initialize(self.send)
|
|
72
|
-
await self._modules[addr].load()
|
|
95
|
+
"""Add a found module to the module cache."""
|
|
96
|
+
module = Module.factory(
|
|
97
|
+
addr,
|
|
98
|
+
typ,
|
|
99
|
+
serial=serial,
|
|
100
|
+
build_year=build_year,
|
|
101
|
+
build_week=build_week,
|
|
102
|
+
memorymap=memorymap,
|
|
103
|
+
cache_dir=self._cache_dir,
|
|
104
|
+
)
|
|
105
|
+
await module.initialize(self.send)
|
|
106
|
+
self._modules[addr] = module
|
|
107
|
+
self._log.info(f"Found module {addr}: {module}")
|
|
73
108
|
|
|
74
|
-
|
|
109
|
+
def add_submodules(self, module: Module, subList: dict[int, int]) -> None:
|
|
110
|
+
"""Add submodules address to module."""
|
|
75
111
|
for sub_num, sub_addr in subList.items():
|
|
76
112
|
if sub_addr == 0xFF:
|
|
77
113
|
continue
|
|
78
114
|
self._submodules.append(sub_addr)
|
|
79
|
-
|
|
80
|
-
self._modules[sub_addr] =
|
|
81
|
-
|
|
115
|
+
module._sub_address[sub_num] = sub_addr
|
|
116
|
+
self._modules[sub_addr] = module
|
|
117
|
+
module.cleanupSubChannels()
|
|
82
118
|
|
|
83
|
-
def
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
return pickle.load(fl)
|
|
87
|
-
except OSError:
|
|
88
|
-
pass
|
|
119
|
+
def addr_is_submodule(self, addr: int) -> bool:
|
|
120
|
+
"""Check if an address is a submodule."""
|
|
121
|
+
return addr in self._submodules
|
|
89
122
|
|
|
90
123
|
def get_modules(self) -> dict:
|
|
91
|
-
"""
|
|
92
|
-
Return the module cache
|
|
93
|
-
"""
|
|
124
|
+
"""Return the module cache."""
|
|
94
125
|
return self._modules
|
|
95
126
|
|
|
96
|
-
def get_module(self, addr:
|
|
97
|
-
"""
|
|
98
|
-
|
|
99
|
-
"""
|
|
100
|
-
if addr in self._modules.keys():
|
|
127
|
+
def get_module(self, addr: int) -> None | Module:
|
|
128
|
+
"""Get a module on an address."""
|
|
129
|
+
if addr in self._modules:
|
|
101
130
|
return self._modules[addr]
|
|
102
131
|
return None
|
|
103
132
|
|
|
104
|
-
def get_channels(self, addr:
|
|
105
|
-
"""
|
|
106
|
-
Get the channels for an address
|
|
107
|
-
"""
|
|
133
|
+
def get_channels(self, addr: int) -> None | dict:
|
|
134
|
+
"""Get the channels for an address."""
|
|
108
135
|
if addr in self._modules:
|
|
109
136
|
return (self._modules[addr]).get_channels()
|
|
110
137
|
return None
|
|
111
138
|
|
|
112
139
|
async def stop(self) -> None:
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
self.
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
async def connect(self
|
|
119
|
-
"""
|
|
120
|
-
|
|
121
|
-
"""
|
|
140
|
+
"""Stop the controller."""
|
|
141
|
+
self._closing = True
|
|
142
|
+
self._auto_reconnect = False
|
|
143
|
+
self._protocol.close()
|
|
144
|
+
|
|
145
|
+
async def connect(self) -> None:
|
|
146
|
+
"""Connect to the bus and load all the data."""
|
|
147
|
+
await self._handler.read_protocol_data()
|
|
122
148
|
# connect to the bus
|
|
123
|
-
if ":" in self.
|
|
149
|
+
if ":" in self._destination:
|
|
124
150
|
# tcp/ip combination
|
|
125
|
-
if
|
|
126
|
-
|
|
151
|
+
if not re.search(r"^[A-Za-z0-9+.\-]+://", self._destination):
|
|
152
|
+
# if no scheme, then add the tcp://
|
|
153
|
+
self._destination = f"tcp://{self._destination}"
|
|
154
|
+
parts = urlparse(self._destination)
|
|
155
|
+
if parts.scheme == "tls":
|
|
127
156
|
ctx = ssl._create_unverified_context()
|
|
128
157
|
else:
|
|
129
|
-
tmp = self._dsn.split(":")
|
|
130
158
|
ctx = None
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
159
|
+
try:
|
|
160
|
+
(
|
|
161
|
+
_transport,
|
|
162
|
+
_protocol,
|
|
163
|
+
) = await asyncio.get_event_loop().create_connection(
|
|
164
|
+
lambda: self._protocol,
|
|
165
|
+
host=parts.hostname,
|
|
166
|
+
port=parts.port,
|
|
167
|
+
ssl=ctx,
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
except (ConnectionRefusedError, OSError) as err:
|
|
171
|
+
raise VelbusConnectionFailed from err
|
|
134
172
|
else:
|
|
135
173
|
# serial port
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
await self.scan()
|
|
174
|
+
try:
|
|
175
|
+
_transport, _protocol = (
|
|
176
|
+
await serial_asyncio_fast.create_serial_connection(
|
|
177
|
+
asyncio.get_event_loop(),
|
|
178
|
+
lambda: self._protocol,
|
|
179
|
+
url=self._destination,
|
|
180
|
+
baudrate=38400,
|
|
181
|
+
bytesize=serial.EIGHTBITS,
|
|
182
|
+
parity=serial.PARITY_NONE,
|
|
183
|
+
stopbits=serial.STOPBITS_ONE,
|
|
184
|
+
xonxoff=0,
|
|
185
|
+
rtscts=1,
|
|
186
|
+
)
|
|
187
|
+
)
|
|
188
|
+
except (FileNotFoundError, serial.SerialException) as err:
|
|
189
|
+
raise VelbusConnectionFailed from err
|
|
153
190
|
|
|
154
|
-
async def
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
await self.
|
|
159
|
-
|
|
160
|
-
self.
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
191
|
+
async def start(self) -> None:
|
|
192
|
+
# if auth is required send the auth key
|
|
193
|
+
parts = urlparse(self._destination)
|
|
194
|
+
if parts.username:
|
|
195
|
+
await self._protocol.write_auth_key(parts.username)
|
|
196
|
+
|
|
197
|
+
if self._vlp_file:
|
|
198
|
+
# use the vlp file to load the modules
|
|
199
|
+
vlp = VlpFile(self._vlp_file)
|
|
200
|
+
await vlp.read()
|
|
201
|
+
for mod_data in vlp.get():
|
|
202
|
+
# Convert hex address string to decimal integer
|
|
203
|
+
addr = mod_data.get_addr().split(",")
|
|
204
|
+
decimal_addr = int(addr[0], 16)
|
|
205
|
+
await self.add_module(
|
|
206
|
+
decimal_addr,
|
|
207
|
+
mod_data.get_type(),
|
|
208
|
+
serial=mod_data.get_serial(),
|
|
209
|
+
memorymap=mod_data.get_memory(),
|
|
210
|
+
build_year=int(mod_data.get_build()[0:2]),
|
|
211
|
+
build_week=int(mod_data.get_build()[2:4]),
|
|
212
|
+
)
|
|
213
|
+
# handle submodules
|
|
214
|
+
if len(addr) > 1:
|
|
215
|
+
self.add_submodules(
|
|
216
|
+
self._modules[decimal_addr], dict(enumerate(addr[1:]))
|
|
217
|
+
)
|
|
218
|
+
# load module data, special for dali
|
|
219
|
+
if mod_data.get_type() == 0x45 or mod_data.get_type() == 0x5A:
|
|
220
|
+
await self._modules[decimal_addr].load()
|
|
221
|
+
else:
|
|
222
|
+
module = self._modules[decimal_addr]
|
|
223
|
+
await module.load_from_vlp(mod_data)
|
|
224
|
+
await module.wait_for_status_messages()
|
|
165
225
|
else:
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
226
|
+
# make sure the cachedir exists
|
|
227
|
+
pathlib.Path(self._cache_dir).mkdir(parents=True, exist_ok=True)
|
|
228
|
+
# scan the bus
|
|
229
|
+
await self._handler.scan()
|
|
230
|
+
|
|
231
|
+
async def scan(self) -> None:
|
|
232
|
+
"""Service endpoint to restart the scan"""
|
|
233
|
+
await self._handler.scan(True)
|
|
234
|
+
|
|
235
|
+
async def sendTypeRequestMessage(self, address: int) -> None:
|
|
236
|
+
msg = ModuleTypeRequestMessage(address)
|
|
237
|
+
await self.send(msg)
|
|
238
|
+
|
|
239
|
+
async def send(self, msg: Message) -> None:
|
|
240
|
+
"""Send a packet."""
|
|
241
|
+
await self._protocol.send_message(
|
|
242
|
+
RawMessage(
|
|
243
|
+
priority=msg.priority,
|
|
244
|
+
address=msg.address,
|
|
245
|
+
rtr=msg.rtr,
|
|
246
|
+
data=msg.data_to_binary(),
|
|
174
247
|
)
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
def get_all_sensor(
|
|
251
|
+
self,
|
|
252
|
+
) -> list[ButtonCounter | Temperature | LightSensor | SensorNumber]:
|
|
253
|
+
return self._get_all("sensor")
|
|
254
|
+
|
|
255
|
+
def get_all_switch(self) -> list[Relay]:
|
|
256
|
+
return self._get_all("switch")
|
|
175
257
|
|
|
176
|
-
|
|
177
|
-
""
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
""
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
async def _parser_task(self) -> None:
|
|
217
|
-
"""
|
|
218
|
-
Task to parser the received queue
|
|
219
|
-
"""
|
|
220
|
-
while True:
|
|
221
|
-
packet = await self._parser.wait_for_packet()
|
|
222
|
-
await self._handler.handle(packet)
|
|
223
|
-
|
|
224
|
-
def get_all(self, class_name: str) -> list:
|
|
258
|
+
def get_all_binary_sensor(self) -> list[Button]:
|
|
259
|
+
return self._get_all("binary_sensor")
|
|
260
|
+
|
|
261
|
+
def get_all_button(self) -> list[Button | ButtonCounter]:
|
|
262
|
+
return self._get_all("button")
|
|
263
|
+
|
|
264
|
+
def get_all_climate(self) -> list[Temperature]:
|
|
265
|
+
return self._get_all("climate")
|
|
266
|
+
|
|
267
|
+
def get_all_cover(self) -> list[Blind]:
|
|
268
|
+
return self._get_all("cover")
|
|
269
|
+
|
|
270
|
+
def get_all_select(self) -> list[SelectedProgram]:
|
|
271
|
+
return self._get_all("select")
|
|
272
|
+
|
|
273
|
+
def get_all_light(self) -> list[Dimmer]:
|
|
274
|
+
return self._get_all("light")
|
|
275
|
+
|
|
276
|
+
def get_all_led(self) -> list[Button]:
|
|
277
|
+
return self._get_all("led")
|
|
278
|
+
|
|
279
|
+
def _get_all(
|
|
280
|
+
self, class_name: str
|
|
281
|
+
) -> list[
|
|
282
|
+
Blind
|
|
283
|
+
| Button
|
|
284
|
+
| ButtonCounter
|
|
285
|
+
| Sensor
|
|
286
|
+
| ThermostatChannel
|
|
287
|
+
| Dimmer
|
|
288
|
+
| Temperature
|
|
289
|
+
| SensorNumber
|
|
290
|
+
| LightSensor
|
|
291
|
+
| Relay
|
|
292
|
+
| EdgeLit
|
|
293
|
+
| Memo
|
|
294
|
+
| SelectedProgram
|
|
295
|
+
]:
|
|
296
|
+
"""Get all channels."""
|
|
225
297
|
lst = []
|
|
226
298
|
for addr, mod in (self.get_modules()).items():
|
|
227
299
|
if addr in self._submodules:
|
|
@@ -232,9 +304,12 @@ class Velbus:
|
|
|
232
304
|
return lst
|
|
233
305
|
|
|
234
306
|
async def sync_clock(self) -> None:
|
|
235
|
-
"""
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
await self.send(
|
|
239
|
-
await self.send(
|
|
240
|
-
|
|
307
|
+
"""Will send all the needed messages to sync the clock."""
|
|
308
|
+
lclt = time.localtime()
|
|
309
|
+
await self.send(SetRealtimeClock(wday=lclt[6], hour=lclt[3], min=lclt[4]))
|
|
310
|
+
await self.send(SetDate(day=lclt[2], mon=lclt[1], year=lclt[0]))
|
|
311
|
+
await self.send(SetDaylightSaving(ds=not lclt[8]))
|
|
312
|
+
|
|
313
|
+
async def wait_on_all_messages_sent_async(self) -> None:
|
|
314
|
+
"""Wait for all messages to be sent."""
|
|
315
|
+
await self._protocol.wait_on_all_messages_sent_async()
|
velbusaio/discovery.py
CHANGED
|
@@ -12,7 +12,7 @@ class VelbusDiscoveryProtocol(asyncio.DatagramProtocol):
|
|
|
12
12
|
def __init__(self, target: Address):
|
|
13
13
|
self.target = target
|
|
14
14
|
|
|
15
|
-
def connection_made(self, transport: asyncio.transports.DatagramTransport):
|
|
15
|
+
def connection_made(self, transport: asyncio.transports.DatagramTransport) -> None:
|
|
16
16
|
self.transport = transport
|
|
17
17
|
sock = transport.get_extra_info("socket")
|
|
18
18
|
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
@@ -20,7 +20,7 @@ class VelbusDiscoveryProtocol(asyncio.DatagramProtocol):
|
|
|
20
20
|
string = "Velbus Navigation Request"
|
|
21
21
|
self.transport.sendto(string.encode(), self.target)
|
|
22
22
|
|
|
23
|
-
def datagram_received(self, data: bytes | str, addr: Address):
|
|
23
|
+
def datagram_received(self, data: bytes | str, addr: Address) -> None:
|
|
24
24
|
# data received: b'{"message": "Velbus Navigation Guidance", "hostname": "Velbus", "model": "signum18", "id": "7b95834e", "velbus_port": 27015, "velbus_auth": false}' ('192.168.1.9', 32767)
|
|
25
25
|
try:
|
|
26
26
|
json_data = json.loads(data)
|
velbusaio/exceptions.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class VelbusException(Exception):
|
|
5
|
+
"""Velbus Exception."""
|
|
6
|
+
|
|
7
|
+
def __init__(self, value: str) -> None:
|
|
8
|
+
Exception.__init__(self)
|
|
9
|
+
self.value = value
|
|
10
|
+
|
|
11
|
+
def __str__(self):
|
|
12
|
+
return repr(self.value)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class VelbusConnectionFailed(VelbusException):
|
|
16
|
+
def __init__(self) -> None:
|
|
17
|
+
super().__init__("Connection setup failed")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class VelbusConnectionTerminated(VelbusException):
|
|
21
|
+
def __init__(self) -> None:
|
|
22
|
+
super().__init__("Connection terminated")
|