aiohomematic 2025.8.6__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.
Potentially problematic release.
This version of aiohomematic might be problematic. Click here for more details.
- aiohomematic/__init__.py +47 -0
- aiohomematic/async_support.py +146 -0
- aiohomematic/caches/__init__.py +10 -0
- aiohomematic/caches/dynamic.py +554 -0
- aiohomematic/caches/persistent.py +459 -0
- aiohomematic/caches/visibility.py +774 -0
- aiohomematic/central/__init__.py +2034 -0
- aiohomematic/central/decorators.py +110 -0
- aiohomematic/central/xml_rpc_server.py +267 -0
- aiohomematic/client/__init__.py +1746 -0
- aiohomematic/client/json_rpc.py +1193 -0
- aiohomematic/client/xml_rpc.py +222 -0
- aiohomematic/const.py +795 -0
- aiohomematic/context.py +8 -0
- aiohomematic/converter.py +82 -0
- aiohomematic/decorators.py +188 -0
- aiohomematic/exceptions.py +145 -0
- aiohomematic/hmcli.py +159 -0
- aiohomematic/model/__init__.py +137 -0
- aiohomematic/model/calculated/__init__.py +65 -0
- aiohomematic/model/calculated/climate.py +230 -0
- aiohomematic/model/calculated/data_point.py +319 -0
- aiohomematic/model/calculated/operating_voltage_level.py +311 -0
- aiohomematic/model/calculated/support.py +174 -0
- aiohomematic/model/custom/__init__.py +175 -0
- aiohomematic/model/custom/climate.py +1334 -0
- aiohomematic/model/custom/const.py +146 -0
- aiohomematic/model/custom/cover.py +741 -0
- aiohomematic/model/custom/data_point.py +318 -0
- aiohomematic/model/custom/definition.py +861 -0
- aiohomematic/model/custom/light.py +1092 -0
- aiohomematic/model/custom/lock.py +389 -0
- aiohomematic/model/custom/siren.py +268 -0
- aiohomematic/model/custom/support.py +40 -0
- aiohomematic/model/custom/switch.py +172 -0
- aiohomematic/model/custom/valve.py +112 -0
- aiohomematic/model/data_point.py +1109 -0
- aiohomematic/model/decorators.py +173 -0
- aiohomematic/model/device.py +1347 -0
- aiohomematic/model/event.py +210 -0
- aiohomematic/model/generic/__init__.py +211 -0
- aiohomematic/model/generic/action.py +32 -0
- aiohomematic/model/generic/binary_sensor.py +28 -0
- aiohomematic/model/generic/button.py +25 -0
- aiohomematic/model/generic/data_point.py +162 -0
- aiohomematic/model/generic/number.py +73 -0
- aiohomematic/model/generic/select.py +36 -0
- aiohomematic/model/generic/sensor.py +72 -0
- aiohomematic/model/generic/switch.py +52 -0
- aiohomematic/model/generic/text.py +27 -0
- aiohomematic/model/hub/__init__.py +334 -0
- aiohomematic/model/hub/binary_sensor.py +22 -0
- aiohomematic/model/hub/button.py +26 -0
- aiohomematic/model/hub/data_point.py +332 -0
- aiohomematic/model/hub/number.py +37 -0
- aiohomematic/model/hub/select.py +47 -0
- aiohomematic/model/hub/sensor.py +35 -0
- aiohomematic/model/hub/switch.py +42 -0
- aiohomematic/model/hub/text.py +28 -0
- aiohomematic/model/support.py +599 -0
- aiohomematic/model/update.py +136 -0
- aiohomematic/py.typed +0 -0
- aiohomematic/rega_scripts/fetch_all_device_data.fn +75 -0
- aiohomematic/rega_scripts/get_program_descriptions.fn +30 -0
- aiohomematic/rega_scripts/get_serial.fn +44 -0
- aiohomematic/rega_scripts/get_system_variable_descriptions.fn +30 -0
- aiohomematic/rega_scripts/set_program_state.fn +12 -0
- aiohomematic/rega_scripts/set_system_variable.fn +15 -0
- aiohomematic/support.py +482 -0
- aiohomematic/validator.py +65 -0
- aiohomematic-2025.8.6.dist-info/METADATA +69 -0
- aiohomematic-2025.8.6.dist-info/RECORD +77 -0
- aiohomematic-2025.8.6.dist-info/WHEEL +5 -0
- aiohomematic-2025.8.6.dist-info/licenses/LICENSE +21 -0
- aiohomematic-2025.8.6.dist-info/top_level.txt +2 -0
- aiohomematic_support/__init__.py +1 -0
- aiohomematic_support/client_local.py +349 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""Decorators for central used within aiohomematic."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Awaitable, Callable
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
from functools import wraps
|
|
8
|
+
import inspect
|
|
9
|
+
import logging
|
|
10
|
+
from typing import Any, Final, cast
|
|
11
|
+
|
|
12
|
+
from aiohomematic import central as hmcu, client as hmcl
|
|
13
|
+
from aiohomematic.central import xml_rpc_server as xmlrpc
|
|
14
|
+
from aiohomematic.const import BackendSystemEvent
|
|
15
|
+
from aiohomematic.exceptions import AioHomematicException
|
|
16
|
+
from aiohomematic.support import extract_exc_args
|
|
17
|
+
|
|
18
|
+
_LOGGER: Final = logging.getLogger(__name__)
|
|
19
|
+
_INTERFACE_ID: Final = "interface_id"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def callback_backend_system(system_event: BackendSystemEvent) -> Callable:
|
|
23
|
+
"""Check if backend_system_callback is set and call it AFTER original function."""
|
|
24
|
+
|
|
25
|
+
def decorator_backend_system_callback[**P, R](
|
|
26
|
+
func: Callable[P, R | Awaitable[R]],
|
|
27
|
+
) -> Callable[P, R | Awaitable[R]]:
|
|
28
|
+
"""Decorate callback system events."""
|
|
29
|
+
|
|
30
|
+
@wraps(func)
|
|
31
|
+
async def async_wrapper_backend_system_callback(*args: P.args, **kwargs: P.kwargs) -> R:
|
|
32
|
+
"""Wrap async callback system events."""
|
|
33
|
+
return_value = cast(R, await func(*args, **kwargs)) # type: ignore[misc]
|
|
34
|
+
await _exec_backend_system_callback(*args, **kwargs)
|
|
35
|
+
return return_value
|
|
36
|
+
|
|
37
|
+
@wraps(func)
|
|
38
|
+
def wrapper_backend_system_callback(*args: P.args, **kwargs: P.kwargs) -> R:
|
|
39
|
+
"""Wrap callback system events."""
|
|
40
|
+
return_value = cast(R, func(*args, **kwargs))
|
|
41
|
+
try:
|
|
42
|
+
unit = args[0]
|
|
43
|
+
central: hmcu.CentralUnit | None = None
|
|
44
|
+
if isinstance(unit, hmcu.CentralUnit):
|
|
45
|
+
central = unit
|
|
46
|
+
if central is None and isinstance(unit, xmlrpc.RPCFunctions):
|
|
47
|
+
central = unit.get_central(interface_id=str(args[1]))
|
|
48
|
+
if central:
|
|
49
|
+
central.looper.create_task(
|
|
50
|
+
_exec_backend_system_callback(*args, **kwargs),
|
|
51
|
+
name="wrapper_backend_system_callback",
|
|
52
|
+
)
|
|
53
|
+
except Exception as exc:
|
|
54
|
+
_LOGGER.warning(
|
|
55
|
+
"EXEC_BACKEND_SYSTEM_CALLBACK failed: Problem with identifying central: %s",
|
|
56
|
+
extract_exc_args(exc=exc),
|
|
57
|
+
)
|
|
58
|
+
return return_value
|
|
59
|
+
|
|
60
|
+
async def _exec_backend_system_callback(*args: Any, **kwargs: Any) -> None:
|
|
61
|
+
"""Execute the callback for a system event."""
|
|
62
|
+
|
|
63
|
+
if not ((len(args) > 1 and not kwargs) or (len(args) == 1 and kwargs)):
|
|
64
|
+
_LOGGER.warning("EXEC_BACKEND_SYSTEM_CALLBACK failed: *args not supported for callback_system_event")
|
|
65
|
+
try:
|
|
66
|
+
args = args[1:]
|
|
67
|
+
interface_id: str = args[0] if len(args) > 0 else str(kwargs[_INTERFACE_ID])
|
|
68
|
+
if client := hmcl.get_client(interface_id=interface_id):
|
|
69
|
+
client.modified_at = datetime.now()
|
|
70
|
+
client.central.fire_backend_system_callback(system_event=system_event, **kwargs)
|
|
71
|
+
except Exception as exc: # pragma: no cover
|
|
72
|
+
_LOGGER.warning(
|
|
73
|
+
"EXEC_BACKEND_SYSTEM_CALLBACK failed: Unable to reduce kwargs for backend_system_callback"
|
|
74
|
+
)
|
|
75
|
+
raise AioHomematicException(
|
|
76
|
+
f"args-exception backend_system_callback [{extract_exc_args(exc=exc)}]"
|
|
77
|
+
) from exc
|
|
78
|
+
|
|
79
|
+
if inspect.iscoroutinefunction(func):
|
|
80
|
+
return async_wrapper_backend_system_callback
|
|
81
|
+
return wrapper_backend_system_callback
|
|
82
|
+
|
|
83
|
+
return decorator_backend_system_callback
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def callback_event[**P, R](
|
|
87
|
+
func: Callable[P, R],
|
|
88
|
+
) -> Callable:
|
|
89
|
+
"""Check if event_callback is set and call it AFTER original function."""
|
|
90
|
+
|
|
91
|
+
@wraps(func)
|
|
92
|
+
async def async_wrapper_event_callback(*args: P.args, **kwargs: P.kwargs) -> R:
|
|
93
|
+
"""Wrap callback events."""
|
|
94
|
+
return_value = cast(R, await func(*args, **kwargs)) # type: ignore[misc]
|
|
95
|
+
_exec_event_callback(*args, **kwargs)
|
|
96
|
+
return return_value
|
|
97
|
+
|
|
98
|
+
def _exec_event_callback(*args: Any, **kwargs: Any) -> None:
|
|
99
|
+
"""Execute the callback for a data_point event."""
|
|
100
|
+
try:
|
|
101
|
+
args = args[1:]
|
|
102
|
+
interface_id: str = args[0] if len(args) > 1 else str(kwargs[_INTERFACE_ID])
|
|
103
|
+
if client := hmcl.get_client(interface_id=interface_id):
|
|
104
|
+
client.modified_at = datetime.now()
|
|
105
|
+
client.central.fire_backend_parameter_callback(*args, **kwargs)
|
|
106
|
+
except Exception as exc: # pragma: no cover
|
|
107
|
+
_LOGGER.warning("EXEC_DATA_POINT_EVENT_CALLBACK failed: Unable to reduce kwargs for event_callback")
|
|
108
|
+
raise AioHomematicException(f"args-exception event_callback [{extract_exc_args(exc=exc)}]") from exc
|
|
109
|
+
|
|
110
|
+
return async_wrapper_event_callback
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
"""
|
|
2
|
+
XML-RPC server module.
|
|
3
|
+
|
|
4
|
+
Provides the XML-RPC server which handles communication
|
|
5
|
+
with the CCU or Homegear.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import contextlib
|
|
11
|
+
import logging
|
|
12
|
+
import threading
|
|
13
|
+
from typing import Any, Final
|
|
14
|
+
from xmlrpc.server import SimpleXMLRPCRequestHandler, SimpleXMLRPCServer
|
|
15
|
+
|
|
16
|
+
from aiohomematic import central as hmcu
|
|
17
|
+
from aiohomematic.central.decorators import callback_backend_system
|
|
18
|
+
from aiohomematic.const import IP_ANY_V4, PORT_ANY, BackendSystemEvent
|
|
19
|
+
from aiohomematic.support import find_free_port
|
|
20
|
+
|
|
21
|
+
_LOGGER: Final = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# pylint: disable=invalid-name
|
|
25
|
+
class RPCFunctions:
|
|
26
|
+
"""The XML-RPC functions the CCU or Homegear will expect."""
|
|
27
|
+
|
|
28
|
+
def __init__(self, xml_rpc_server: XmlRpcServer) -> None:
|
|
29
|
+
"""Init RPCFunctions."""
|
|
30
|
+
self._xml_rpc_server: Final = xml_rpc_server
|
|
31
|
+
|
|
32
|
+
def event(self, interface_id: str, channel_address: str, parameter: str, value: Any) -> None:
|
|
33
|
+
"""If a device emits some sort event, we will handle it here."""
|
|
34
|
+
if central := self.get_central(interface_id):
|
|
35
|
+
central.looper.create_task(
|
|
36
|
+
central.data_point_event(
|
|
37
|
+
interface_id=interface_id,
|
|
38
|
+
channel_address=channel_address,
|
|
39
|
+
parameter=parameter,
|
|
40
|
+
value=value,
|
|
41
|
+
),
|
|
42
|
+
name=f"event-{interface_id}-{channel_address}-{parameter}",
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
@callback_backend_system(system_event=BackendSystemEvent.ERROR)
|
|
46
|
+
def error(self, interface_id: str, error_code: str, msg: str) -> None:
|
|
47
|
+
"""When some error occurs the CCU / Homegear will send its error message here."""
|
|
48
|
+
_LOGGER.warning(
|
|
49
|
+
"ERROR failed: interface_id = %s, error_code = %i, message = %s",
|
|
50
|
+
interface_id,
|
|
51
|
+
int(error_code),
|
|
52
|
+
str(msg),
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
def listDevices(self, interface_id: str) -> list[dict[str, Any]]:
|
|
56
|
+
"""Return already existing devices to CCU / Homegear."""
|
|
57
|
+
if central := self.get_central(interface_id):
|
|
58
|
+
return [dict(device_description) for device_description in central.list_devices(interface_id=interface_id)]
|
|
59
|
+
return []
|
|
60
|
+
|
|
61
|
+
def newDevices(self, interface_id: str, device_descriptions: list[dict[str, Any]]) -> None:
|
|
62
|
+
"""Add new devices send from backend."""
|
|
63
|
+
central: hmcu.CentralUnit | None
|
|
64
|
+
if central := self.get_central(interface_id):
|
|
65
|
+
central.looper.create_task(
|
|
66
|
+
central.add_new_devices(interface_id=interface_id, device_descriptions=tuple(device_descriptions)),
|
|
67
|
+
name=f"newDevices-{interface_id}",
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
def deleteDevices(self, interface_id: str, addresses: list[str]) -> None:
|
|
71
|
+
"""Delete devices send from backend."""
|
|
72
|
+
central: hmcu.CentralUnit | None
|
|
73
|
+
if central := self.get_central(interface_id):
|
|
74
|
+
central.looper.create_task(
|
|
75
|
+
central.delete_devices(interface_id=interface_id, addresses=tuple(addresses)),
|
|
76
|
+
name=f"deleteDevices-{interface_id}",
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
@callback_backend_system(system_event=BackendSystemEvent.UPDATE_DEVICE)
|
|
80
|
+
def updateDevice(self, interface_id: str, address: str, hint: int) -> None:
|
|
81
|
+
"""
|
|
82
|
+
Update a device.
|
|
83
|
+
|
|
84
|
+
Irrelevant, as currently only changes to link
|
|
85
|
+
partners are reported.
|
|
86
|
+
"""
|
|
87
|
+
_LOGGER.debug(
|
|
88
|
+
"UPDATEDEVICE: interface_id = %s, address = %s, hint = %s",
|
|
89
|
+
interface_id,
|
|
90
|
+
address,
|
|
91
|
+
str(hint),
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
@callback_backend_system(system_event=BackendSystemEvent.REPLACE_DEVICE)
|
|
95
|
+
def replaceDevice(self, interface_id: str, old_device_address: str, new_device_address: str) -> None:
|
|
96
|
+
"""Replace a device. Probably irrelevant for us."""
|
|
97
|
+
_LOGGER.debug(
|
|
98
|
+
"REPLACEDEVICE: interface_id = %s, oldDeviceAddress = %s, newDeviceAddress = %s",
|
|
99
|
+
interface_id,
|
|
100
|
+
old_device_address,
|
|
101
|
+
new_device_address,
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
@callback_backend_system(system_event=BackendSystemEvent.RE_ADDED_DEVICE)
|
|
105
|
+
def readdedDevice(self, interface_id: str, addresses: list[str]) -> None:
|
|
106
|
+
"""
|
|
107
|
+
Re-Add device from backend.
|
|
108
|
+
|
|
109
|
+
Probably irrelevant for us.
|
|
110
|
+
Gets called when a known devices is put into learn-mode
|
|
111
|
+
while installation mode is active.
|
|
112
|
+
"""
|
|
113
|
+
_LOGGER.debug(
|
|
114
|
+
"READDEDDEVICES: interface_id = %s, addresses = %s",
|
|
115
|
+
interface_id,
|
|
116
|
+
str(addresses),
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
def get_central(self, interface_id: str) -> hmcu.CentralUnit | None:
|
|
120
|
+
"""Return the central by interface_id."""
|
|
121
|
+
return self._xml_rpc_server.get_central(interface_id)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
# Restrict to specific paths.
|
|
125
|
+
class RequestHandler(SimpleXMLRPCRequestHandler):
|
|
126
|
+
"""We handle requests to / and /RPC2."""
|
|
127
|
+
|
|
128
|
+
rpc_paths = (
|
|
129
|
+
"/",
|
|
130
|
+
"/RPC2",
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class AioHomematicXMLRPCServer(SimpleXMLRPCServer):
|
|
135
|
+
"""
|
|
136
|
+
Simple XML-RPC server.
|
|
137
|
+
|
|
138
|
+
Simple XML-RPC server that allows functions and a single instance
|
|
139
|
+
to be installed to handle requests. The default implementation
|
|
140
|
+
attempts to dispatch XML-RPC calls to the functions or instance
|
|
141
|
+
installed in the server. Override the _dispatch method inherited
|
|
142
|
+
from SimpleXMLRPCDispatcher to change this behavior.
|
|
143
|
+
|
|
144
|
+
This implementation adds an additional method:
|
|
145
|
+
system_listMethods(self, interface_id: str.
|
|
146
|
+
"""
|
|
147
|
+
|
|
148
|
+
def system_listMethods(self, interface_id: str | None = None) -> list[str]:
|
|
149
|
+
"""
|
|
150
|
+
Return a list of the methods supported by the server.
|
|
151
|
+
|
|
152
|
+
system.listMethods() => ['add', 'subtract', 'multiple']
|
|
153
|
+
Required for HomeMatic CCU usage.
|
|
154
|
+
"""
|
|
155
|
+
return SimpleXMLRPCServer.system_listMethods(self)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class XmlRpcServer(threading.Thread):
|
|
159
|
+
"""XML-RPC server thread to handle messages from CCU / Homegear."""
|
|
160
|
+
|
|
161
|
+
_initialized: bool = False
|
|
162
|
+
_instances: Final[dict[tuple[str, int], XmlRpcServer]] = {}
|
|
163
|
+
|
|
164
|
+
def __init__(
|
|
165
|
+
self,
|
|
166
|
+
ip_addr: str,
|
|
167
|
+
port: int,
|
|
168
|
+
) -> None:
|
|
169
|
+
"""Init XmlRPC server."""
|
|
170
|
+
if self._initialized:
|
|
171
|
+
return
|
|
172
|
+
self._initialized = True
|
|
173
|
+
self._listen_ip_addr: Final = ip_addr
|
|
174
|
+
self._listen_port: Final[int] = find_free_port() if port == PORT_ANY else port
|
|
175
|
+
self._address: Final[tuple[str, int]] = (ip_addr, self._listen_port)
|
|
176
|
+
self._instances[self._address] = self
|
|
177
|
+
threading.Thread.__init__(self, name=f"XmlRpcServer {ip_addr}:{self._listen_port}")
|
|
178
|
+
self._simple_xml_rpc_server = AioHomematicXMLRPCServer(
|
|
179
|
+
addr=self._address,
|
|
180
|
+
requestHandler=RequestHandler,
|
|
181
|
+
logRequests=False,
|
|
182
|
+
allow_none=True,
|
|
183
|
+
)
|
|
184
|
+
self._simple_xml_rpc_server.register_introspection_functions()
|
|
185
|
+
self._simple_xml_rpc_server.register_multicall_functions()
|
|
186
|
+
self._simple_xml_rpc_server.register_instance(RPCFunctions(self), allow_dotted_names=True)
|
|
187
|
+
self._centrals: Final[dict[str, hmcu.CentralUnit]] = {}
|
|
188
|
+
|
|
189
|
+
def __new__(cls, ip_addr: str, port: int) -> XmlRpcServer: # noqa: PYI034
|
|
190
|
+
"""Create new XmlRPC server."""
|
|
191
|
+
if (xml_rpc := cls._instances.get((ip_addr, port))) is None:
|
|
192
|
+
_LOGGER.debug("Creating XmlRpc server")
|
|
193
|
+
return super().__new__(cls)
|
|
194
|
+
return xml_rpc
|
|
195
|
+
|
|
196
|
+
def run(self) -> None:
|
|
197
|
+
"""Run the XmlRPC-Server thread."""
|
|
198
|
+
_LOGGER.debug(
|
|
199
|
+
"RUN: Starting XmlRPC-Server listening on http://%s:%i",
|
|
200
|
+
self._listen_ip_addr,
|
|
201
|
+
self._listen_port,
|
|
202
|
+
)
|
|
203
|
+
if self._simple_xml_rpc_server:
|
|
204
|
+
self._simple_xml_rpc_server.serve_forever()
|
|
205
|
+
|
|
206
|
+
def stop(self) -> None:
|
|
207
|
+
"""Stop the XmlRPC-Server."""
|
|
208
|
+
_LOGGER.debug("STOP: Shutting down XmlRPC-Server")
|
|
209
|
+
self._simple_xml_rpc_server.shutdown()
|
|
210
|
+
_LOGGER.debug("STOP: Stopping XmlRPC-Server")
|
|
211
|
+
self._simple_xml_rpc_server.server_close()
|
|
212
|
+
# Ensure the server thread has actually terminated to avoid slow teardown
|
|
213
|
+
with contextlib.suppress(RuntimeError):
|
|
214
|
+
self.join(timeout=1.0)
|
|
215
|
+
_LOGGER.debug("STOP: XmlRPC-Server stopped")
|
|
216
|
+
if self._address in self._instances:
|
|
217
|
+
del self._instances[self._address]
|
|
218
|
+
|
|
219
|
+
@property
|
|
220
|
+
def listen_ip_addr(self) -> str:
|
|
221
|
+
"""Return the local ip address."""
|
|
222
|
+
return self._listen_ip_addr
|
|
223
|
+
|
|
224
|
+
@property
|
|
225
|
+
def listen_port(self) -> int:
|
|
226
|
+
"""Return the local port."""
|
|
227
|
+
return self._listen_port
|
|
228
|
+
|
|
229
|
+
@property
|
|
230
|
+
def started(self) -> bool:
|
|
231
|
+
"""Return if thread is active."""
|
|
232
|
+
return self._started.is_set() is True # type: ignore[attr-defined]
|
|
233
|
+
|
|
234
|
+
def add_central(self, central: hmcu.CentralUnit) -> None:
|
|
235
|
+
"""Register a central in the XmlRPC-Server."""
|
|
236
|
+
if not self._centrals.get(central.name):
|
|
237
|
+
self._centrals[central.name] = central
|
|
238
|
+
|
|
239
|
+
def remove_central(self, central: hmcu.CentralUnit) -> None:
|
|
240
|
+
"""Unregister a central from XmlRPC-Server."""
|
|
241
|
+
if self._centrals.get(central.name):
|
|
242
|
+
del self._centrals[central.name]
|
|
243
|
+
|
|
244
|
+
def get_central(self, interface_id: str) -> hmcu.CentralUnit | None:
|
|
245
|
+
"""Return a central by interface_id."""
|
|
246
|
+
for central in self._centrals.values():
|
|
247
|
+
if central.has_client(interface_id=interface_id):
|
|
248
|
+
return central
|
|
249
|
+
return None
|
|
250
|
+
|
|
251
|
+
@property
|
|
252
|
+
def no_central_assigned(self) -> bool:
|
|
253
|
+
"""Return if no central is assigned."""
|
|
254
|
+
return len(self._centrals) == 0
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def create_xml_rpc_server(ip_addr: str = IP_ANY_V4, port: int = PORT_ANY) -> XmlRpcServer:
|
|
258
|
+
"""Register the xml rpc server."""
|
|
259
|
+
xml_rpc = XmlRpcServer(ip_addr=ip_addr, port=port)
|
|
260
|
+
if not xml_rpc.started:
|
|
261
|
+
xml_rpc.start()
|
|
262
|
+
_LOGGER.debug(
|
|
263
|
+
"CREATE_XML_RPC_SERVER: Starting XmlRPC-Server listening on %s:%i",
|
|
264
|
+
xml_rpc.listen_ip_addr,
|
|
265
|
+
xml_rpc.listen_port,
|
|
266
|
+
)
|
|
267
|
+
return xml_rpc
|