aiohomematic 2025.10.1__py3-none-any.whl → 2025.10.3__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.

Files changed (57) hide show
  1. aiohomematic/async_support.py +8 -8
  2. aiohomematic/caches/dynamic.py +31 -26
  3. aiohomematic/caches/persistent.py +34 -32
  4. aiohomematic/caches/visibility.py +19 -7
  5. aiohomematic/central/__init__.py +88 -75
  6. aiohomematic/central/decorators.py +2 -2
  7. aiohomematic/central/xml_rpc_server.py +33 -25
  8. aiohomematic/client/__init__.py +72 -56
  9. aiohomematic/client/_rpc_errors.py +3 -3
  10. aiohomematic/client/json_rpc.py +33 -25
  11. aiohomematic/client/xml_rpc.py +14 -9
  12. aiohomematic/const.py +2 -1
  13. aiohomematic/converter.py +19 -19
  14. aiohomematic/exceptions.py +2 -1
  15. aiohomematic/model/__init__.py +4 -3
  16. aiohomematic/model/calculated/__init__.py +1 -1
  17. aiohomematic/model/calculated/climate.py +9 -9
  18. aiohomematic/model/calculated/data_point.py +13 -7
  19. aiohomematic/model/calculated/operating_voltage_level.py +2 -2
  20. aiohomematic/model/calculated/support.py +7 -7
  21. aiohomematic/model/custom/__init__.py +3 -3
  22. aiohomematic/model/custom/climate.py +57 -34
  23. aiohomematic/model/custom/cover.py +32 -18
  24. aiohomematic/model/custom/data_point.py +9 -7
  25. aiohomematic/model/custom/definition.py +23 -17
  26. aiohomematic/model/custom/light.py +52 -23
  27. aiohomematic/model/custom/lock.py +16 -12
  28. aiohomematic/model/custom/siren.py +8 -3
  29. aiohomematic/model/custom/switch.py +3 -2
  30. aiohomematic/model/custom/valve.py +3 -2
  31. aiohomematic/model/data_point.py +63 -49
  32. aiohomematic/model/device.py +48 -42
  33. aiohomematic/model/event.py +6 -5
  34. aiohomematic/model/generic/__init__.py +6 -4
  35. aiohomematic/model/generic/action.py +1 -1
  36. aiohomematic/model/generic/data_point.py +8 -6
  37. aiohomematic/model/generic/number.py +3 -3
  38. aiohomematic/model/generic/select.py +1 -1
  39. aiohomematic/model/generic/sensor.py +2 -2
  40. aiohomematic/model/generic/switch.py +3 -3
  41. aiohomematic/model/hub/__init__.py +17 -16
  42. aiohomematic/model/hub/data_point.py +12 -7
  43. aiohomematic/model/hub/number.py +3 -3
  44. aiohomematic/model/hub/select.py +3 -3
  45. aiohomematic/model/hub/text.py +2 -2
  46. aiohomematic/model/support.py +10 -9
  47. aiohomematic/model/update.py +6 -6
  48. aiohomematic/property_decorators.py +2 -0
  49. aiohomematic/support.py +44 -38
  50. aiohomematic/validator.py +6 -6
  51. {aiohomematic-2025.10.1.dist-info → aiohomematic-2025.10.3.dist-info}/METADATA +1 -1
  52. aiohomematic-2025.10.3.dist-info/RECORD +78 -0
  53. aiohomematic_support/client_local.py +26 -14
  54. aiohomematic-2025.10.1.dist-info/RECORD +0 -78
  55. {aiohomematic-2025.10.1.dist-info → aiohomematic-2025.10.3.dist-info}/WHEEL +0 -0
  56. {aiohomematic-2025.10.1.dist-info → aiohomematic-2025.10.3.dist-info}/licenses/LICENSE +0 -0
  57. {aiohomematic-2025.10.1.dist-info → aiohomematic-2025.10.3.dist-info}/top_level.txt +0 -0
@@ -52,7 +52,7 @@ def callback_backend_system(system_event: BackendSystemEvent) -> Callable:
52
52
  central = unit.get_central(interface_id=str(args[1]))
53
53
  if central:
54
54
  central.looper.create_task(
55
- _exec_backend_system_callback(*args, **kwargs),
55
+ target=_exec_backend_system_callback(*args, **kwargs),
56
56
  name="wrapper_backend_system_callback",
57
57
  )
58
58
  except Exception as exc:
@@ -123,7 +123,7 @@ def callback_event[**P, R](func: Callable[P, R]) -> Callable:
123
123
  unit = args[0]
124
124
  if isinstance(unit, hmcu.CentralUnit):
125
125
  unit.looper.create_task(
126
- _async_wrap_sync(_exec_event_callback, *args, **kwargs),
126
+ target=_async_wrap_sync(_exec_event_callback, *args, **kwargs),
127
127
  name="wrapper_event_callback",
128
128
  )
129
129
  return
@@ -27,15 +27,18 @@ _LOGGER: Final = logging.getLogger(__name__)
27
27
  class RPCFunctions:
28
28
  """The XML-RPC functions the backend will expect."""
29
29
 
30
- def __init__(self, xml_rpc_server: XmlRpcServer) -> None:
30
+ # Disable kw-only linter for this class since XML-RPC signatures are positional by protocol
31
+ __kwonly_check__ = False
32
+
33
+ def __init__(self, *, xml_rpc_server: XmlRpcServer) -> None:
31
34
  """Init RPCFunctions."""
32
35
  self._xml_rpc_server: Final = xml_rpc_server
33
36
 
34
- def event(self, interface_id: str, channel_address: str, parameter: str, value: Any) -> None:
37
+ def event(self, interface_id: str, channel_address: str, parameter: str, value: Any, /) -> None:
35
38
  """If a device emits some sort event, we will handle it here."""
36
- if central := self.get_central(interface_id):
39
+ if central := self.get_central(interface_id=interface_id):
37
40
  central.looper.create_task(
38
- central.data_point_event(
41
+ target=central.data_point_event(
39
42
  interface_id=interface_id,
40
43
  channel_address=channel_address,
41
44
  parameter=parameter,
@@ -45,7 +48,7 @@ class RPCFunctions:
45
48
  )
46
49
 
47
50
  @callback_backend_system(system_event=BackendSystemEvent.ERROR)
48
- def error(self, interface_id: str, error_code: str, msg: str) -> None:
51
+ def error(self, interface_id: str, error_code: str, msg: str, /) -> None:
49
52
  """When some error occurs the backend will send its error message here."""
50
53
  # Structured boundary log (warning level). XML-RPC server received error notification.
51
54
  try:
@@ -66,32 +69,34 @@ class RPCFunctions:
66
69
  str(msg),
67
70
  )
68
71
 
69
- def listDevices(self, interface_id: str) -> list[dict[str, Any]]:
72
+ def listDevices(self, interface_id: str, /) -> list[dict[str, Any]]:
70
73
  """Return already existing devices to the backend."""
71
- if central := self.get_central(interface_id):
74
+ if central := self.get_central(interface_id=interface_id):
72
75
  return [dict(device_description) for device_description in central.list_devices(interface_id=interface_id)]
73
76
  return []
74
77
 
75
- def newDevices(self, interface_id: str, device_descriptions: list[dict[str, Any]]) -> None:
78
+ def newDevices(self, interface_id: str, device_descriptions: list[dict[str, Any]], /) -> None:
76
79
  """Add new devices send from the backend."""
77
80
  central: hmcu.CentralUnit | None
78
- if central := self.get_central(interface_id):
81
+ if central := self.get_central(interface_id=interface_id):
79
82
  central.looper.create_task(
80
- central.add_new_devices(interface_id=interface_id, device_descriptions=tuple(device_descriptions)),
83
+ target=central.add_new_devices(
84
+ interface_id=interface_id, device_descriptions=tuple(device_descriptions)
85
+ ),
81
86
  name=f"newDevices-{interface_id}",
82
87
  )
83
88
 
84
- def deleteDevices(self, interface_id: str, addresses: list[str]) -> None:
89
+ def deleteDevices(self, interface_id: str, addresses: list[str], /) -> None:
85
90
  """Delete devices send from the backend."""
86
91
  central: hmcu.CentralUnit | None
87
- if central := self.get_central(interface_id):
92
+ if central := self.get_central(interface_id=interface_id):
88
93
  central.looper.create_task(
89
- central.delete_devices(interface_id=interface_id, addresses=tuple(addresses)),
94
+ target=central.delete_devices(interface_id=interface_id, addresses=tuple(addresses)),
90
95
  name=f"deleteDevices-{interface_id}",
91
96
  )
92
97
 
93
98
  @callback_backend_system(system_event=BackendSystemEvent.UPDATE_DEVICE)
94
- def updateDevice(self, interface_id: str, address: str, hint: int) -> None:
99
+ def updateDevice(self, interface_id: str, address: str, hint: int, /) -> None:
95
100
  """
96
101
  Update a device.
97
102
 
@@ -106,7 +111,7 @@ class RPCFunctions:
106
111
  )
107
112
 
108
113
  @callback_backend_system(system_event=BackendSystemEvent.REPLACE_DEVICE)
109
- def replaceDevice(self, interface_id: str, old_device_address: str, new_device_address: str) -> None:
114
+ def replaceDevice(self, interface_id: str, old_device_address: str, new_device_address: str, /) -> None:
110
115
  """Replace a device. Probably irrelevant for us."""
111
116
  _LOGGER.debug(
112
117
  "REPLACEDEVICE: interface_id = %s, oldDeviceAddress = %s, newDeviceAddress = %s",
@@ -116,7 +121,7 @@ class RPCFunctions:
116
121
  )
117
122
 
118
123
  @callback_backend_system(system_event=BackendSystemEvent.RE_ADDED_DEVICE)
119
- def readdedDevice(self, interface_id: str, addresses: list[str]) -> None:
124
+ def readdedDevice(self, interface_id: str, addresses: list[str], /) -> None:
120
125
  """
121
126
  Re-Add device from the backend.
122
127
 
@@ -130,9 +135,9 @@ class RPCFunctions:
130
135
  str(addresses),
131
136
  )
132
137
 
133
- def get_central(self, interface_id: str) -> hmcu.CentralUnit | None:
138
+ def get_central(self, *, interface_id: str) -> hmcu.CentralUnit | None:
134
139
  """Return the central by interface_id."""
135
- return self._xml_rpc_server.get_central(interface_id)
140
+ return self._xml_rpc_server.get_central(interface_id=interface_id)
136
141
 
137
142
 
138
143
  # Restrict to specific paths.
@@ -159,7 +164,9 @@ class AioHomematicXMLRPCServer(SimpleXMLRPCServer):
159
164
  system_listMethods(self, interface_id: str.
160
165
  """
161
166
 
162
- def system_listMethods(self, interface_id: str | None = None) -> list[str]:
167
+ __kwonly_check__ = False
168
+
169
+ def system_listMethods(self, interface_id: str | None = None, /) -> list[str]:
163
170
  """Return a list of the methods supported by the server."""
164
171
  return SimpleXMLRPCServer.system_listMethods(self)
165
172
 
@@ -172,6 +179,7 @@ class XmlRpcServer(threading.Thread):
172
179
 
173
180
  def __init__(
174
181
  self,
182
+ *,
175
183
  ip_addr: str,
176
184
  port: int,
177
185
  ) -> None:
@@ -192,10 +200,10 @@ class XmlRpcServer(threading.Thread):
192
200
  )
193
201
  self._simple_xml_rpc_server.register_introspection_functions()
194
202
  self._simple_xml_rpc_server.register_multicall_functions()
195
- self._simple_xml_rpc_server.register_instance(RPCFunctions(self), allow_dotted_names=True)
203
+ self._simple_xml_rpc_server.register_instance(RPCFunctions(xml_rpc_server=self), allow_dotted_names=True)
196
204
  self._centrals: Final[dict[str, hmcu.CentralUnit]] = {}
197
205
 
198
- def __new__(cls, ip_addr: str, port: int) -> XmlRpcServer: # noqa: PYI034
206
+ def __new__(cls, ip_addr: str, port: int) -> XmlRpcServer: # noqa: PYI034 # kwonly: disable
199
207
  """Create new XmlRPC server."""
200
208
  if (xml_rpc := cls._instances.get((ip_addr, port))) is None:
201
209
  _LOGGER.debug("Creating XmlRpc server")
@@ -240,17 +248,17 @@ class XmlRpcServer(threading.Thread):
240
248
  """Return if thread is active."""
241
249
  return self._started.is_set() is True # type: ignore[attr-defined]
242
250
 
243
- def add_central(self, central: hmcu.CentralUnit) -> None:
251
+ def add_central(self, *, central: hmcu.CentralUnit) -> None:
244
252
  """Register a central in the XmlRPC-Server."""
245
253
  if not self._centrals.get(central.name):
246
254
  self._centrals[central.name] = central
247
255
 
248
- def remove_central(self, central: hmcu.CentralUnit) -> None:
256
+ def remove_central(self, *, central: hmcu.CentralUnit) -> None:
249
257
  """Unregister a central from XmlRPC-Server."""
250
258
  if self._centrals.get(central.name):
251
259
  del self._centrals[central.name]
252
260
 
253
- def get_central(self, interface_id: str) -> hmcu.CentralUnit | None:
261
+ def get_central(self, *, interface_id: str) -> hmcu.CentralUnit | None:
254
262
  """Return a central by interface_id."""
255
263
  for central in self._centrals.values():
256
264
  if central.has_client(interface_id=interface_id):
@@ -263,7 +271,7 @@ class XmlRpcServer(threading.Thread):
263
271
  return len(self._centrals) == 0
264
272
 
265
273
 
266
- def create_xml_rpc_server(ip_addr: str = IP_ANY_V4, port: int = PORT_ANY) -> XmlRpcServer:
274
+ def create_xml_rpc_server(*, ip_addr: str = IP_ANY_V4, port: int = PORT_ANY) -> XmlRpcServer:
267
275
  """Register the xml rpc server."""
268
276
  xml_rpc = XmlRpcServer(ip_addr=ip_addr, port=port)
269
277
  if not xml_rpc.started: