aiohomematic 2025.10.4__tar.gz → 2025.10.6__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.

Potentially problematic release.


This version of aiohomematic might be problematic. Click here for more details.

Files changed (113) hide show
  1. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/PKG-INFO +1 -1
  2. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/caches/visibility.py +2 -2
  3. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/central/__init__.py +51 -46
  4. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/central/decorators.py +2 -2
  5. aiohomematic-2025.10.4/aiohomematic/central/xml_rpc_server.py → aiohomematic-2025.10.6/aiohomematic/central/rpc_server.py +62 -47
  6. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/client/__init__.py +118 -78
  7. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/client/json_rpc.py +1 -1
  8. aiohomematic-2025.10.4/aiohomematic/client/xml_rpc.py → aiohomematic-2025.10.6/aiohomematic/client/rpc_proxy.py +78 -43
  9. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/const.py +31 -4
  10. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/model/custom/switch.py +1 -0
  11. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/support.py +4 -4
  12. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic.egg-info/PKG-INFO +1 -1
  13. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic.egg-info/SOURCES.txt +2 -2
  14. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/pyproject.toml +2 -2
  15. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/tests/test_json_rpc_client_integration.py +2 -2
  16. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/tests/test_xml_rpc_proxy_integration.py +2 -2
  17. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/LICENSE +0 -0
  18. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/README.md +0 -0
  19. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/__init__.py +0 -0
  20. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/async_support.py +0 -0
  21. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/caches/__init__.py +0 -0
  22. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/caches/dynamic.py +0 -0
  23. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/caches/persistent.py +0 -0
  24. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/client/_rpc_errors.py +0 -0
  25. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/context.py +0 -0
  26. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/converter.py +0 -0
  27. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/decorators.py +0 -0
  28. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/exceptions.py +0 -0
  29. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/hmcli.py +0 -0
  30. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/model/__init__.py +0 -0
  31. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/model/calculated/__init__.py +0 -0
  32. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/model/calculated/climate.py +0 -0
  33. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/model/calculated/data_point.py +0 -0
  34. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/model/calculated/operating_voltage_level.py +0 -0
  35. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/model/calculated/support.py +0 -0
  36. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/model/custom/__init__.py +0 -0
  37. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/model/custom/climate.py +0 -0
  38. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/model/custom/const.py +0 -0
  39. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/model/custom/cover.py +0 -0
  40. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/model/custom/data_point.py +0 -0
  41. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/model/custom/definition.py +0 -0
  42. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/model/custom/light.py +0 -0
  43. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/model/custom/lock.py +0 -0
  44. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/model/custom/siren.py +0 -0
  45. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/model/custom/support.py +0 -0
  46. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/model/custom/valve.py +0 -0
  47. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/model/data_point.py +0 -0
  48. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/model/device.py +0 -0
  49. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/model/event.py +0 -0
  50. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/model/generic/__init__.py +0 -0
  51. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/model/generic/action.py +0 -0
  52. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/model/generic/binary_sensor.py +0 -0
  53. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/model/generic/button.py +0 -0
  54. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/model/generic/data_point.py +0 -0
  55. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/model/generic/number.py +0 -0
  56. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/model/generic/select.py +0 -0
  57. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/model/generic/sensor.py +0 -0
  58. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/model/generic/switch.py +0 -0
  59. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/model/generic/text.py +0 -0
  60. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/model/hub/__init__.py +0 -0
  61. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/model/hub/binary_sensor.py +0 -0
  62. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/model/hub/button.py +0 -0
  63. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/model/hub/data_point.py +0 -0
  64. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/model/hub/number.py +0 -0
  65. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/model/hub/select.py +0 -0
  66. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/model/hub/sensor.py +0 -0
  67. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/model/hub/switch.py +0 -0
  68. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/model/hub/text.py +0 -0
  69. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/model/support.py +0 -0
  70. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/model/update.py +0 -0
  71. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/property_decorators.py +0 -0
  72. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/py.typed +0 -0
  73. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/rega_scripts/fetch_all_device_data.fn +0 -0
  74. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/rega_scripts/get_program_descriptions.fn +0 -0
  75. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/rega_scripts/get_serial.fn +0 -0
  76. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/rega_scripts/get_system_variable_descriptions.fn +0 -0
  77. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/rega_scripts/set_program_state.fn +0 -0
  78. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/rega_scripts/set_system_variable.fn +0 -0
  79. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic/validator.py +0 -0
  80. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic.egg-info/dependency_links.txt +0 -0
  81. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic.egg-info/requires.txt +0 -0
  82. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic.egg-info/top_level.txt +0 -0
  83. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic_support/__init__.py +0 -0
  84. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/aiohomematic_support/client_local.py +0 -0
  85. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/setup.cfg +0 -0
  86. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/tests/test_action.py +0 -0
  87. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/tests/test_async_support.py +0 -0
  88. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/tests/test_binary_sensor.py +0 -0
  89. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/tests/test_button.py +0 -0
  90. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/tests/test_calculated_support.py +0 -0
  91. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/tests/test_central.py +0 -0
  92. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/tests/test_central_pydevccu.py +0 -0
  93. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/tests/test_climate.py +0 -0
  94. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/tests/test_cover.py +0 -0
  95. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/tests/test_decorator.py +0 -0
  96. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/tests/test_device.py +0 -0
  97. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/tests/test_dynamic_caches.py +0 -0
  98. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/tests/test_entity.py +0 -0
  99. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/tests/test_event.py +0 -0
  100. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/tests/test_json_rpc.py +0 -0
  101. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/tests/test_kwonly_lint.py +0 -0
  102. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/tests/test_light.py +0 -0
  103. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/tests/test_lock.py +0 -0
  104. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/tests/test_logging_support.py +0 -0
  105. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/tests/test_number.py +0 -0
  106. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/tests/test_select.py +0 -0
  107. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/tests/test_sensor.py +0 -0
  108. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/tests/test_siren.py +0 -0
  109. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/tests/test_support.py +0 -0
  110. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/tests/test_support_extra.py +0 -0
  111. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/tests/test_switch.py +0 -0
  112. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/tests/test_text.py +0 -0
  113. {aiohomematic-2025.10.4 → aiohomematic-2025.10.6}/tests/test_valve.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aiohomematic
3
- Version: 2025.10.4
3
+ Version: 2025.10.6
4
4
  Summary: Homematic interface for Home Assistant running on Python 3.
5
5
  Home-page: https://github.com/sukramj/aiohomematic
6
6
  Author-email: SukramJ <sukramj@icloud.com>, Daniel Perna <danielperna84@gmail.com>
@@ -171,8 +171,8 @@ _IGNORED_PARAMETERS: Final[frozenset[TParameterName]] = frozenset(
171
171
  "CLEAR_ERROR",
172
172
  "CLEAR_WINDOW_OPEN_SYMBOL",
173
173
  "CLOCK",
174
- "CMD_RETL", # CuXD
175
- "CMD_RETS", # CuXD
174
+ "CMD_RETL", # CUxD
175
+ "CMD_RETS", # CUxD
176
176
  "CONTROL_DIFFERENTIAL_TEMPERATURE",
177
177
  "DATE_TIME_UNKNOWN",
178
178
  "DECISION_VALUE",
@@ -82,10 +82,10 @@ from aiohomematic.async_support import Looper, loop_check
82
82
  from aiohomematic.caches.dynamic import CentralDataCache, DeviceDetailsCache
83
83
  from aiohomematic.caches.persistent import DeviceDescriptionCache, ParamsetDescriptionCache
84
84
  from aiohomematic.caches.visibility import ParameterVisibilityCache
85
- from aiohomematic.central import xml_rpc_server as xmlrpc
85
+ from aiohomematic.central import rpc_server as rpc
86
86
  from aiohomematic.central.decorators import callback_backend_system, callback_event
87
- from aiohomematic.client.json_rpc import JsonRpcAioHttpClient
88
- from aiohomematic.client.xml_rpc import XmlRpcProxy
87
+ from aiohomematic.client.json_rpc import AioJsonRpcAioHttpClient
88
+ from aiohomematic.client.rpc_proxy import AioXmlRpcProxy
89
89
  from aiohomematic.const import (
90
90
  CALLBACK_TYPE,
91
91
  CATEGORIES,
@@ -98,6 +98,7 @@ from aiohomematic.const import (
98
98
  DEFAULT_ENABLE_SYSVAR_SCAN,
99
99
  DEFAULT_HM_MASTER_POLL_AFTER_SEND_INTERVALS,
100
100
  DEFAULT_IGNORE_CUSTOM_DEVICE_DEFINITION_MODELS,
101
+ DEFAULT_INTERFACES_REQUIRING_PERIODIC_REFRESH,
101
102
  DEFAULT_MAX_READ_WORKERS,
102
103
  DEFAULT_PERIODIC_REFRESH_INTERVAL,
103
104
  DEFAULT_PROGRAM_MARKERS,
@@ -112,7 +113,6 @@ from aiohomematic.const import (
112
113
  DEVICE_FIRMWARE_DELIVERING_CHECK_INTERVAL,
113
114
  DEVICE_FIRMWARE_UPDATING_CHECK_INTERVAL,
114
115
  IGNORE_FOR_UN_IGNORE_PARAMETERS,
115
- INTERFACES_REQUIRING_PERIODIC_REFRESH,
116
116
  IP_ANY_V4,
117
117
  LOCAL_HOST,
118
118
  PORT_ANY,
@@ -137,6 +137,7 @@ from aiohomematic.const import (
137
137
  Parameter,
138
138
  ParamsetKey,
139
139
  ProxyInitState,
140
+ RpcServerType,
140
141
  SourceOfDeviceCreation,
141
142
  SystemInformation,
142
143
  )
@@ -180,7 +181,7 @@ _LOGGER_EVENT: Final = logging.getLogger(f"{__package__}.event")
180
181
 
181
182
  # {central_name, central}
182
183
  CENTRAL_INSTANCES: Final[dict[str, CentralUnit]] = {}
183
- ConnectionProblemIssuer = JsonRpcAioHttpClient | XmlRpcProxy
184
+ ConnectionProblemIssuer = AioJsonRpcAioHttpClient | AioXmlRpcProxy
184
185
 
185
186
  INTERFACE_EVENT_SCHEMA = vol.Schema(
186
187
  {
@@ -208,8 +209,8 @@ class CentralUnit(LogContextMixin, PayloadMixin):
208
209
  self._url: Final = self._config.create_central_url()
209
210
  self._model: str | None = None
210
211
  self._looper = Looper()
211
- self._xml_rpc_server: xmlrpc.XmlRpcServer | None = None
212
- self._json_rpc_client: JsonRpcAioHttpClient | None = None
212
+ self._xml_rpc_server: rpc.XmlRpcServer | None = None
213
+ self._json_rpc_client: AioJsonRpcAioHttpClient | None = None
213
214
 
214
215
  # Caches for the backend data
215
216
  self._data_cache: Final = CentralDataCache(central=self)
@@ -248,9 +249,9 @@ class CentralUnit(LogContextMixin, PayloadMixin):
248
249
  self._version: str | None = None
249
250
  # store last event received datetime by interface_id
250
251
  self._last_event_seen_for_interface: Final[dict[str, datetime]] = {}
251
- self._xml_rpc_callback_ip: str = IP_ANY_V4
252
+ self._rpc_callback_ip: str = IP_ANY_V4
252
253
  self._listen_ip_addr: str = IP_ANY_V4
253
- self._listen_port: int = PORT_ANY
254
+ self._listen_port_xml_rpc: int = PORT_ANY
254
255
 
255
256
  @property
256
257
  def available(self) -> bool:
@@ -260,7 +261,7 @@ class CentralUnit(LogContextMixin, PayloadMixin):
260
261
  @property
261
262
  def callback_ip_addr(self) -> str:
262
263
  """Return the xml rpc server callback ip address."""
263
- return self._xml_rpc_callback_ip
264
+ return self._rpc_callback_ip
264
265
 
265
266
  @info_property(log_context=True)
266
267
  def url(self) -> str:
@@ -327,7 +328,7 @@ class CentralUnit(LogContextMixin, PayloadMixin):
327
328
  return all(client.is_callback_alive() for client in self._clients.values())
328
329
 
329
330
  @property
330
- def json_rpc_client(self) -> JsonRpcAioHttpClient:
331
+ def json_rpc_client(self) -> AioJsonRpcAioHttpClient:
331
332
  """Return the json rpc client."""
332
333
  if not self._json_rpc_client:
333
334
  self._json_rpc_client = self._config.create_json_rpc_client(central=self)
@@ -363,9 +364,9 @@ class CentralUnit(LogContextMixin, PayloadMixin):
363
364
  return self._listen_ip_addr
364
365
 
365
366
  @property
366
- def listen_port(self) -> int:
367
+ def listen_port_xml_rpc(self) -> int:
367
368
  """Return the xml rpc listening server port."""
368
- return self._listen_port
369
+ return self._listen_port_xml_rpc
369
370
 
370
371
  @property
371
372
  def looper(self) -> Looper:
@@ -483,22 +484,22 @@ class CentralUnit(LogContextMixin, PayloadMixin):
483
484
  if self._config.enabled_interface_configs and (
484
485
  ip_addr := await self._identify_ip_addr(port=self._config.connection_check_port)
485
486
  ):
486
- self._xml_rpc_callback_ip = ip_addr
487
+ self._rpc_callback_ip = ip_addr
487
488
  self._listen_ip_addr = self._config.listen_ip_addr if self._config.listen_ip_addr else ip_addr
488
489
 
489
- listen_port: int = (
490
- self._config.listen_port
491
- if self._config.listen_port
492
- else self._config.callback_port or self._config.default_callback_port
490
+ port_xml_rpc: int = (
491
+ self._config.listen_port_xml_rpc
492
+ if self._config.listen_port_xml_rpc
493
+ else self._config.callback_port_xml_rpc or self._config.default_callback_port_xml_rpc
493
494
  )
494
495
  try:
495
496
  if (
496
- xml_rpc_server := xmlrpc.create_xml_rpc_server(ip_addr=self._listen_ip_addr, port=listen_port)
497
- if self._config.enable_server
497
+ xml_rpc_server := rpc.create_xml_rpc_server(ip_addr=self._listen_ip_addr, port=port_xml_rpc)
498
+ if self._config.enable_xml_rpc_server
498
499
  else None
499
500
  ):
500
501
  self._xml_rpc_server = xml_rpc_server
501
- self._listen_port = xml_rpc_server.listen_port
502
+ self._listen_port_xml_rpc = xml_rpc_server.listen_port
502
503
  self._xml_rpc_server.add_central(central=self)
503
504
  except OSError as oserr:
504
505
  self._state = CentralUnitState.STOPPED_BY_ERROR
@@ -514,7 +515,7 @@ class CentralUnit(LogContextMixin, PayloadMixin):
514
515
  )
515
516
  else:
516
517
  self._clients_started = await self._start_clients()
517
- if self._config.enable_server:
518
+ if self._config.enable_xml_rpc_server:
518
519
  self._start_scheduler()
519
520
 
520
521
  self._state = CentralUnitState.RUNNING
@@ -1166,7 +1167,8 @@ class CentralUnit(LogContextMixin, PayloadMixin):
1166
1167
  return tuple(
1167
1168
  dev_desc
1168
1169
  for dev_desc in device_descriptions
1169
- if (dev_desc["ADDRESS"] if dev_desc["PARENT"] == "" else dev_desc["PARENT"]) not in known_addresses
1170
+ if (dev_desc["ADDRESS"] if not (parent_address := dev_desc.get("PARENT")) else parent_address)
1171
+ not in known_addresses
1170
1172
  )
1171
1173
 
1172
1174
  def _check_for_new_device_addresses(self, *, interface_id: str | None = None) -> Mapping[str, set[str]]:
@@ -1330,7 +1332,7 @@ class CentralUnit(LogContextMixin, PayloadMixin):
1330
1332
  self._data_point_key_event_subscriptions[data_point.dpk] = []
1331
1333
  self._data_point_key_event_subscriptions[data_point.dpk].append(data_point.event)
1332
1334
  if (
1333
- not data_point.channel.device.client.supports_xml_rpc
1335
+ not data_point.channel.device.client.supports_rpc_callback
1334
1336
  and data_point.state_path not in self._data_point_path_event_subscriptions
1335
1337
  ):
1336
1338
  self._data_point_path_event_subscriptions[data_point.state_path] = data_point.dpk
@@ -1970,18 +1972,18 @@ class CentralConfig:
1970
1972
  username: str,
1971
1973
  client_session: ClientSession | None = None,
1972
1974
  callback_host: str | None = None,
1973
- callback_port: int | None = None,
1974
- default_callback_port: int = PORT_ANY,
1975
+ callback_port_xml_rpc: int | None = None,
1976
+ default_callback_port_xml_rpc: int = PORT_ANY,
1975
1977
  delay_new_device_creation: bool = DEFAULT_DELAY_NEW_DEVICE_CREATION,
1976
1978
  enable_device_firmware_check: bool = DEFAULT_ENABLE_DEVICE_FIRMWARE_CHECK,
1977
1979
  enable_program_scan: bool = DEFAULT_ENABLE_PROGRAM_SCAN,
1978
1980
  enable_sysvar_scan: bool = DEFAULT_ENABLE_SYSVAR_SCAN,
1979
1981
  hm_master_poll_after_send_intervals: tuple[int, ...] = DEFAULT_HM_MASTER_POLL_AFTER_SEND_INTERVALS,
1980
1982
  ignore_custom_device_definition_models: frozenset[str] = DEFAULT_IGNORE_CUSTOM_DEVICE_DEFINITION_MODELS,
1981
- interfaces_requiring_periodic_refresh: frozenset[Interface] = INTERFACES_REQUIRING_PERIODIC_REFRESH,
1983
+ interfaces_requiring_periodic_refresh: frozenset[Interface] = DEFAULT_INTERFACES_REQUIRING_PERIODIC_REFRESH,
1982
1984
  json_port: int | None = None,
1983
1985
  listen_ip_addr: str | None = None,
1984
- listen_port: int | None = None,
1986
+ listen_port_xml_rpc: int | None = None,
1985
1987
  max_read_workers: int = DEFAULT_MAX_READ_WORKERS,
1986
1988
  periodic_refresh_interval: int = DEFAULT_PERIODIC_REFRESH_INTERVAL,
1987
1989
  program_markers: tuple[DescriptionMarker | str, ...] = DEFAULT_PROGRAM_MARKERS,
@@ -1996,11 +1998,14 @@ class CentralConfig:
1996
1998
  ) -> None:
1997
1999
  """Init the client config."""
1998
2000
  self._interface_configs: Final = interface_configs
2001
+ self.requires_xml_rpc_server: Final = any(
2002
+ ic for ic in interface_configs if ic.rpc_server == RpcServerType.XML_RPC
2003
+ )
1999
2004
  self.callback_host: Final = callback_host
2000
- self.callback_port: Final = callback_port
2005
+ self.callback_port_xml_rpc: Final = callback_port_xml_rpc
2001
2006
  self.central_id: Final = central_id
2002
2007
  self.client_session: Final = client_session
2003
- self.default_callback_port: Final = default_callback_port
2008
+ self.default_callback_port_xml_rpc: Final = default_callback_port_xml_rpc
2004
2009
  self.delay_new_device_creation: Final = delay_new_device_creation
2005
2010
  self.enable_device_firmware_check: Final = enable_device_firmware_check
2006
2011
  self.enable_program_scan: Final = enable_program_scan
@@ -2011,7 +2016,7 @@ class CentralConfig:
2011
2016
  self.interfaces_requiring_periodic_refresh: Final = frozenset(interfaces_requiring_periodic_refresh or ())
2012
2017
  self.json_port: Final = json_port
2013
2018
  self.listen_ip_addr: Final = listen_ip_addr
2014
- self.listen_port: Final = listen_port
2019
+ self.listen_port_xml_rpc: Final = listen_port_xml_rpc
2015
2020
  self.max_read_workers = max_read_workers
2016
2021
  self.name: Final = name
2017
2022
  self.password: Final = password
@@ -2028,9 +2033,9 @@ class CentralConfig:
2028
2033
  self.verify_tls: Final = verify_tls
2029
2034
 
2030
2035
  @property
2031
- def enable_server(self) -> bool:
2036
+ def enable_xml_rpc_server(self) -> bool:
2032
2037
  """Return if server and connection checker should be started."""
2033
- return self.start_direct is False
2038
+ return self.requires_xml_rpc_server and self.start_direct is False
2034
2039
 
2035
2040
  @property
2036
2041
  def load_un_ignore(self) -> bool:
@@ -2065,7 +2070,7 @@ class CentralConfig:
2065
2070
  password=self.password,
2066
2071
  storage_folder=self.storage_folder,
2067
2072
  callback_host=self.callback_host,
2068
- callback_port=self.callback_port,
2073
+ callback_port_xml_rpc=self.callback_port_xml_rpc,
2069
2074
  json_port=self.json_port,
2070
2075
  interface_configs=self._interface_configs,
2071
2076
  ):
@@ -2090,9 +2095,9 @@ class CentralConfig:
2090
2095
  url = f"{url}:{self.json_port}"
2091
2096
  return f"{url}"
2092
2097
 
2093
- def create_json_rpc_client(self, *, central: CentralUnit) -> JsonRpcAioHttpClient:
2098
+ def create_json_rpc_client(self, *, central: CentralUnit) -> AioJsonRpcAioHttpClient:
2094
2099
  """Create a json rpc client."""
2095
- return JsonRpcAioHttpClient(
2100
+ return AioJsonRpcAioHttpClient(
2096
2101
  username=self.username,
2097
2102
  password=self.password,
2098
2103
  device_url=central.url,
@@ -2109,38 +2114,38 @@ class CentralConnectionState:
2109
2114
  def __init__(self) -> None:
2110
2115
  """Init the CentralConnectionStatus."""
2111
2116
  self._json_issues: Final[list[str]] = []
2112
- self._xml_proxy_issues: Final[list[str]] = []
2117
+ self._rpc_proxy_issues: Final[list[str]] = []
2113
2118
 
2114
2119
  def add_issue(self, *, issuer: ConnectionProblemIssuer, iid: str) -> bool:
2115
2120
  """Add issue to collection."""
2116
- if isinstance(issuer, JsonRpcAioHttpClient) and iid not in self._json_issues:
2121
+ if isinstance(issuer, AioJsonRpcAioHttpClient) and iid not in self._json_issues:
2117
2122
  self._json_issues.append(iid)
2118
2123
  _LOGGER.debug("add_issue: add issue [%s] for JsonRpcAioHttpClient", iid)
2119
2124
  return True
2120
- if isinstance(issuer, XmlRpcProxy) and iid not in self._xml_proxy_issues:
2121
- self._xml_proxy_issues.append(iid)
2125
+ if isinstance(issuer, AioXmlRpcProxy) and iid not in self._rpc_proxy_issues:
2126
+ self._rpc_proxy_issues.append(iid)
2122
2127
  _LOGGER.debug("add_issue: add issue [%s] for %s", iid, issuer.interface_id)
2123
2128
  return True
2124
2129
  return False
2125
2130
 
2126
2131
  def remove_issue(self, *, issuer: ConnectionProblemIssuer, iid: str) -> bool:
2127
2132
  """Add issue to collection."""
2128
- if isinstance(issuer, JsonRpcAioHttpClient) and iid in self._json_issues:
2133
+ if isinstance(issuer, AioJsonRpcAioHttpClient) and iid in self._json_issues:
2129
2134
  self._json_issues.remove(iid)
2130
2135
  _LOGGER.debug("remove_issue: removing issue [%s] for JsonRpcAioHttpClient", iid)
2131
2136
  return True
2132
- if isinstance(issuer, XmlRpcProxy) and issuer.interface_id in self._xml_proxy_issues:
2133
- self._xml_proxy_issues.remove(iid)
2137
+ if isinstance(issuer, AioXmlRpcProxy) and issuer.interface_id in self._rpc_proxy_issues:
2138
+ self._rpc_proxy_issues.remove(iid)
2134
2139
  _LOGGER.debug("remove_issue: removing issue [%s] for %s", iid, issuer.interface_id)
2135
2140
  return True
2136
2141
  return False
2137
2142
 
2138
2143
  def has_issue(self, *, issuer: ConnectionProblemIssuer, iid: str) -> bool:
2139
2144
  """Add issue to collection."""
2140
- if isinstance(issuer, JsonRpcAioHttpClient):
2145
+ if isinstance(issuer, AioJsonRpcAioHttpClient):
2141
2146
  return iid in self._json_issues
2142
- if isinstance(issuer, XmlRpcProxy):
2143
- return iid in self._xml_proxy_issues
2147
+ if isinstance(issuer, (AioXmlRpcProxy)):
2148
+ return iid in self._rpc_proxy_issues
2144
2149
 
2145
2150
  def handle_exception_log(
2146
2151
  self,
@@ -12,7 +12,7 @@ import logging
12
12
  from typing import Any, Final, cast
13
13
 
14
14
  from aiohomematic import central as hmcu, client as hmcl
15
- from aiohomematic.central import xml_rpc_server as xmlrpc
15
+ from aiohomematic.central import rpc_server as rpc
16
16
  from aiohomematic.const import BackendSystemEvent
17
17
  from aiohomematic.exceptions import AioHomematicException
18
18
  from aiohomematic.support import extract_exc_args
@@ -48,7 +48,7 @@ def callback_backend_system(system_event: BackendSystemEvent) -> Callable:
48
48
  central: hmcu.CentralUnit | None = None
49
49
  if isinstance(unit, hmcu.CentralUnit):
50
50
  central = unit
51
- if central is None and isinstance(unit, xmlrpc.RPCFunctions):
51
+ if central is None and isinstance(unit, rpc.RPCFunctions):
52
52
  central = unit.get_central(interface_id=str(args[1]))
53
53
  if central:
54
54
  central.looper.create_task(
@@ -25,14 +25,14 @@ _LOGGER: Final = logging.getLogger(__name__)
25
25
 
26
26
  # pylint: disable=invalid-name
27
27
  class RPCFunctions:
28
- """The XML-RPC functions the backend will expect."""
28
+ """The RPC functions the backend will expect."""
29
29
 
30
- # Disable kw-only linter for this class since XML-RPC signatures are positional by protocol
30
+ # Disable kw-only linter
31
31
  __kwonly_check__ = False
32
32
 
33
- def __init__(self, *, xml_rpc_server: XmlRpcServer) -> None:
33
+ def __init__(self, *, rpc_server: RpcServer) -> None:
34
34
  """Init RPCFunctions."""
35
- self._xml_rpc_server: Final = xml_rpc_server
35
+ self._rpc_server: Final = rpc_server
36
36
 
37
37
  def event(self, interface_id: str, channel_address: str, parameter: str, value: Any, /) -> None:
38
38
  """If a device emits some sort event, we will handle it here."""
@@ -50,13 +50,13 @@ class RPCFunctions:
50
50
  @callback_backend_system(system_event=BackendSystemEvent.ERROR)
51
51
  def error(self, interface_id: str, error_code: str, msg: str, /) -> None:
52
52
  """When some error occurs the backend will send its error message here."""
53
- # Structured boundary log (warning level). XML-RPC server received error notification.
53
+ # Structured boundary log (warning level). RPC server received error notification.
54
54
  try:
55
55
  raise RuntimeError(str(msg))
56
56
  except RuntimeError as err:
57
57
  log_boundary_error(
58
58
  logger=_LOGGER,
59
- boundary="xml-rpc-server",
59
+ boundary="rpc-server",
60
60
  action="error",
61
61
  err=err,
62
62
  level=logging.WARNING,
@@ -137,7 +137,7 @@ class RPCFunctions:
137
137
 
138
138
  def get_central(self, *, interface_id: str) -> hmcu.CentralUnit | None:
139
139
  """Return the central by interface_id."""
140
- return self._xml_rpc_server.get_central(interface_id=interface_id)
140
+ return self._rpc_server.get_central(interface_id=interface_id)
141
141
 
142
142
 
143
143
  # Restrict to specific paths.
@@ -150,7 +150,7 @@ class RequestHandler(SimpleXMLRPCRequestHandler):
150
150
  )
151
151
 
152
152
 
153
- class AioHomematicXMLRPCServer(SimpleXMLRPCServer):
153
+ class HomematicXMLRPCServer(SimpleXMLRPCServer):
154
154
  """
155
155
  Simple XML-RPC server.
156
156
 
@@ -171,8 +171,8 @@ class AioHomematicXMLRPCServer(SimpleXMLRPCServer):
171
171
  return SimpleXMLRPCServer.system_listMethods(self)
172
172
 
173
173
 
174
- class XmlRpcServer(threading.Thread):
175
- """XML-RPC server thread to handle messages from the backend."""
174
+ class RpcServer(threading.Thread):
175
+ """RPC server thread to handle messages from the backend."""
176
176
 
177
177
  _initialized: bool = False
178
178
  _instances: Final[dict[tuple[str, int], XmlRpcServer]] = {}
@@ -190,46 +190,30 @@ class XmlRpcServer(threading.Thread):
190
190
  self._listen_ip_addr: Final = ip_addr
191
191
  self._listen_port: Final[int] = find_free_port() if port == PORT_ANY else port
192
192
  self._address: Final[tuple[str, int]] = (ip_addr, self._listen_port)
193
- self._instances[self._address] = self
194
- threading.Thread.__init__(self, name=f"XmlRpcServer {ip_addr}:{self._listen_port}")
195
- self._simple_xml_rpc_server = AioHomematicXMLRPCServer(
196
- addr=self._address,
197
- requestHandler=RequestHandler,
198
- logRequests=False,
199
- allow_none=True,
200
- )
201
- self._simple_xml_rpc_server.register_introspection_functions()
202
- self._simple_xml_rpc_server.register_multicall_functions()
203
- self._simple_xml_rpc_server.register_instance(RPCFunctions(xml_rpc_server=self), allow_dotted_names=True)
193
+ threading.Thread.__init__(self, name=f"RpcServer {ip_addr}:{self._listen_port}")
204
194
  self._centrals: Final[dict[str, hmcu.CentralUnit]] = {}
205
-
206
- def __new__(cls, ip_addr: str, port: int) -> XmlRpcServer: # noqa: PYI034 # kwonly: disable
207
- """Create new XmlRPC server."""
208
- if (xml_rpc := cls._instances.get((ip_addr, port))) is None:
209
- _LOGGER.debug("Creating XmlRpc server")
210
- return super().__new__(cls)
211
- return xml_rpc
195
+ self._simple_rpc_server: SimpleXMLRPCServer
212
196
 
213
197
  def run(self) -> None:
214
- """Run the XmlRPC-Server thread."""
198
+ """Run the RPC-Server thread."""
215
199
  _LOGGER.debug(
216
- "RUN: Starting XmlRPC-Server listening on http://%s:%i",
200
+ "RUN: Starting RPC-Server listening on %s:%i",
217
201
  self._listen_ip_addr,
218
202
  self._listen_port,
219
203
  )
220
- if self._simple_xml_rpc_server:
221
- self._simple_xml_rpc_server.serve_forever()
204
+ if self._simple_rpc_server:
205
+ self._simple_rpc_server.serve_forever()
222
206
 
223
207
  def stop(self) -> None:
224
- """Stop the XmlRPC-Server."""
225
- _LOGGER.debug("STOP: Shutting down XmlRPC-Server")
226
- self._simple_xml_rpc_server.shutdown()
227
- _LOGGER.debug("STOP: Stopping XmlRPC-Server")
228
- self._simple_xml_rpc_server.server_close()
208
+ """Stop the RPC-Server."""
209
+ _LOGGER.debug("STOP: Shutting down RPC-Server")
210
+ self._simple_rpc_server.shutdown()
211
+ _LOGGER.debug("STOP: Stopping RPC-Server")
212
+ self._simple_rpc_server.server_close()
229
213
  # Ensure the server thread has actually terminated to avoid slow teardown
230
214
  with contextlib.suppress(RuntimeError):
231
215
  self.join(timeout=1.0)
232
- _LOGGER.debug("STOP: XmlRPC-Server stopped")
216
+ _LOGGER.debug("STOP: RPC-Server stopped")
233
217
  if self._address in self._instances:
234
218
  del self._instances[self._address]
235
219
 
@@ -249,12 +233,12 @@ class XmlRpcServer(threading.Thread):
249
233
  return self._started.is_set() is True # type: ignore[attr-defined]
250
234
 
251
235
  def add_central(self, *, central: hmcu.CentralUnit) -> None:
252
- """Register a central in the XmlRPC-Server."""
236
+ """Register a central in the RPC-Server."""
253
237
  if not self._centrals.get(central.name):
254
238
  self._centrals[central.name] = central
255
239
 
256
240
  def remove_central(self, *, central: hmcu.CentralUnit) -> None:
257
- """Unregister a central from XmlRPC-Server."""
241
+ """Unregister a central from RPC-Server."""
258
242
  if self._centrals.get(central.name):
259
243
  del self._centrals[central.name]
260
244
 
@@ -271,14 +255,45 @@ class XmlRpcServer(threading.Thread):
271
255
  return len(self._centrals) == 0
272
256
 
273
257
 
258
+ class XmlRpcServer(RpcServer):
259
+ """XML-RPC server thread to handle messages from the backend."""
260
+
261
+ def __init__(
262
+ self,
263
+ *,
264
+ ip_addr: str,
265
+ port: int,
266
+ ) -> None:
267
+ """Init XmlRPC server."""
268
+
269
+ super().__init__(ip_addr=ip_addr, port=port)
270
+ self._instances[self._address] = self
271
+ self._simple_rpc_server = HomematicXMLRPCServer(
272
+ addr=self._address,
273
+ requestHandler=RequestHandler,
274
+ logRequests=False,
275
+ allow_none=True,
276
+ )
277
+ self._simple_rpc_server.register_introspection_functions()
278
+ self._simple_rpc_server.register_multicall_functions()
279
+ self._simple_rpc_server.register_instance(RPCFunctions(rpc_server=self), allow_dotted_names=True)
280
+
281
+ def __new__(cls, ip_addr: str, port: int) -> XmlRpcServer: # noqa: PYI034 # kwonly: disable
282
+ """Create new RPC server."""
283
+ if (rpc := cls._instances.get((ip_addr, port))) is None:
284
+ _LOGGER.debug("Creating XmlRpc server")
285
+ return super().__new__(cls)
286
+ return rpc
287
+
288
+
274
289
  def create_xml_rpc_server(*, ip_addr: str = IP_ANY_V4, port: int = PORT_ANY) -> XmlRpcServer:
275
- """Register the xml rpc server."""
276
- xml_rpc = XmlRpcServer(ip_addr=ip_addr, port=port)
277
- if not xml_rpc.started:
278
- xml_rpc.start()
290
+ """Register the rpc server."""
291
+ rpc = XmlRpcServer(ip_addr=ip_addr, port=port)
292
+ if not rpc.started:
293
+ rpc.start()
279
294
  _LOGGER.debug(
280
295
  "CREATE_XML_RPC_SERVER: Starting XmlRPC-Server listening on %s:%i",
281
- xml_rpc.listen_ip_addr,
282
- xml_rpc.listen_port,
296
+ rpc.listen_ip_addr,
297
+ rpc.listen_port,
283
298
  )
284
- return xml_rpc
299
+ return rpc