aiohomematic 2025.10.5__tar.gz → 2025.10.7__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.5 → aiohomematic-2025.10.7}/PKG-INFO +1 -1
  2. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/caches/visibility.py +2 -2
  3. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/central/__init__.py +49 -45
  4. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/central/decorators.py +2 -2
  5. aiohomematic-2025.10.5/aiohomematic/central/xml_rpc_server.py → aiohomematic-2025.10.7/aiohomematic/central/rpc_server.py +66 -49
  6. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/client/__init__.py +118 -78
  7. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/client/json_rpc.py +1 -1
  8. aiohomematic-2025.10.5/aiohomematic/client/xml_rpc.py → aiohomematic-2025.10.7/aiohomematic/client/rpc_proxy.py +78 -43
  9. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/const.py +31 -4
  10. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/model/custom/switch.py +1 -0
  11. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/support.py +3 -3
  12. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic.egg-info/PKG-INFO +1 -1
  13. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic.egg-info/SOURCES.txt +2 -2
  14. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/pyproject.toml +2 -2
  15. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/tests/test_json_rpc_client_integration.py +2 -2
  16. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/tests/test_xml_rpc_proxy_integration.py +2 -2
  17. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/LICENSE +0 -0
  18. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/README.md +0 -0
  19. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/__init__.py +0 -0
  20. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/async_support.py +0 -0
  21. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/caches/__init__.py +0 -0
  22. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/caches/dynamic.py +0 -0
  23. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/caches/persistent.py +0 -0
  24. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/client/_rpc_errors.py +0 -0
  25. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/context.py +0 -0
  26. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/converter.py +0 -0
  27. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/decorators.py +0 -0
  28. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/exceptions.py +0 -0
  29. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/hmcli.py +0 -0
  30. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/model/__init__.py +0 -0
  31. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/model/calculated/__init__.py +0 -0
  32. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/model/calculated/climate.py +0 -0
  33. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/model/calculated/data_point.py +0 -0
  34. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/model/calculated/operating_voltage_level.py +0 -0
  35. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/model/calculated/support.py +0 -0
  36. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/model/custom/__init__.py +0 -0
  37. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/model/custom/climate.py +0 -0
  38. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/model/custom/const.py +0 -0
  39. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/model/custom/cover.py +0 -0
  40. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/model/custom/data_point.py +0 -0
  41. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/model/custom/definition.py +0 -0
  42. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/model/custom/light.py +0 -0
  43. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/model/custom/lock.py +0 -0
  44. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/model/custom/siren.py +0 -0
  45. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/model/custom/support.py +0 -0
  46. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/model/custom/valve.py +0 -0
  47. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/model/data_point.py +0 -0
  48. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/model/device.py +0 -0
  49. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/model/event.py +0 -0
  50. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/model/generic/__init__.py +0 -0
  51. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/model/generic/action.py +0 -0
  52. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/model/generic/binary_sensor.py +0 -0
  53. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/model/generic/button.py +0 -0
  54. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/model/generic/data_point.py +0 -0
  55. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/model/generic/number.py +0 -0
  56. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/model/generic/select.py +0 -0
  57. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/model/generic/sensor.py +0 -0
  58. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/model/generic/switch.py +0 -0
  59. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/model/generic/text.py +0 -0
  60. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/model/hub/__init__.py +0 -0
  61. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/model/hub/binary_sensor.py +0 -0
  62. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/model/hub/button.py +0 -0
  63. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/model/hub/data_point.py +0 -0
  64. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/model/hub/number.py +0 -0
  65. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/model/hub/select.py +0 -0
  66. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/model/hub/sensor.py +0 -0
  67. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/model/hub/switch.py +0 -0
  68. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/model/hub/text.py +0 -0
  69. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/model/support.py +0 -0
  70. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/model/update.py +0 -0
  71. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/property_decorators.py +0 -0
  72. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/py.typed +0 -0
  73. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/rega_scripts/fetch_all_device_data.fn +0 -0
  74. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/rega_scripts/get_program_descriptions.fn +0 -0
  75. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/rega_scripts/get_serial.fn +0 -0
  76. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/rega_scripts/get_system_variable_descriptions.fn +0 -0
  77. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/rega_scripts/set_program_state.fn +0 -0
  78. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/rega_scripts/set_system_variable.fn +0 -0
  79. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic/validator.py +0 -0
  80. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic.egg-info/dependency_links.txt +0 -0
  81. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic.egg-info/requires.txt +0 -0
  82. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic.egg-info/top_level.txt +0 -0
  83. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic_support/__init__.py +0 -0
  84. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/aiohomematic_support/client_local.py +0 -0
  85. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/setup.cfg +0 -0
  86. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/tests/test_action.py +0 -0
  87. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/tests/test_async_support.py +0 -0
  88. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/tests/test_binary_sensor.py +0 -0
  89. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/tests/test_button.py +0 -0
  90. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/tests/test_calculated_support.py +0 -0
  91. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/tests/test_central.py +0 -0
  92. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/tests/test_central_pydevccu.py +0 -0
  93. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/tests/test_climate.py +0 -0
  94. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/tests/test_cover.py +0 -0
  95. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/tests/test_decorator.py +0 -0
  96. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/tests/test_device.py +0 -0
  97. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/tests/test_dynamic_caches.py +0 -0
  98. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/tests/test_entity.py +0 -0
  99. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/tests/test_event.py +0 -0
  100. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/tests/test_json_rpc.py +0 -0
  101. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/tests/test_kwonly_lint.py +0 -0
  102. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/tests/test_light.py +0 -0
  103. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/tests/test_lock.py +0 -0
  104. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/tests/test_logging_support.py +0 -0
  105. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/tests/test_number.py +0 -0
  106. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/tests/test_select.py +0 -0
  107. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/tests/test_sensor.py +0 -0
  108. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/tests/test_siren.py +0 -0
  109. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/tests/test_support.py +0 -0
  110. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/tests/test_support_extra.py +0 -0
  111. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/tests/test_switch.py +0 -0
  112. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/tests/test_text.py +0 -0
  113. {aiohomematic-2025.10.5 → aiohomematic-2025.10.7}/tests/test_valve.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aiohomematic
3
- Version: 2025.10.5
3
+ Version: 2025.10.7
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
@@ -1331,7 +1332,7 @@ class CentralUnit(LogContextMixin, PayloadMixin):
1331
1332
  self._data_point_key_event_subscriptions[data_point.dpk] = []
1332
1333
  self._data_point_key_event_subscriptions[data_point.dpk].append(data_point.event)
1333
1334
  if (
1334
- not data_point.channel.device.client.supports_xml_rpc
1335
+ not data_point.channel.device.client.supports_rpc_callback
1335
1336
  and data_point.state_path not in self._data_point_path_event_subscriptions
1336
1337
  ):
1337
1338
  self._data_point_path_event_subscriptions[data_point.state_path] = data_point.dpk
@@ -1971,18 +1972,18 @@ class CentralConfig:
1971
1972
  username: str,
1972
1973
  client_session: ClientSession | None = None,
1973
1974
  callback_host: str | None = None,
1974
- callback_port: int | None = None,
1975
- default_callback_port: int = PORT_ANY,
1975
+ callback_port_xml_rpc: int | None = None,
1976
+ default_callback_port_xml_rpc: int = PORT_ANY,
1976
1977
  delay_new_device_creation: bool = DEFAULT_DELAY_NEW_DEVICE_CREATION,
1977
1978
  enable_device_firmware_check: bool = DEFAULT_ENABLE_DEVICE_FIRMWARE_CHECK,
1978
1979
  enable_program_scan: bool = DEFAULT_ENABLE_PROGRAM_SCAN,
1979
1980
  enable_sysvar_scan: bool = DEFAULT_ENABLE_SYSVAR_SCAN,
1980
1981
  hm_master_poll_after_send_intervals: tuple[int, ...] = DEFAULT_HM_MASTER_POLL_AFTER_SEND_INTERVALS,
1981
1982
  ignore_custom_device_definition_models: frozenset[str] = DEFAULT_IGNORE_CUSTOM_DEVICE_DEFINITION_MODELS,
1982
- interfaces_requiring_periodic_refresh: frozenset[Interface] = INTERFACES_REQUIRING_PERIODIC_REFRESH,
1983
+ interfaces_requiring_periodic_refresh: frozenset[Interface] = DEFAULT_INTERFACES_REQUIRING_PERIODIC_REFRESH,
1983
1984
  json_port: int | None = None,
1984
1985
  listen_ip_addr: str | None = None,
1985
- listen_port: int | None = None,
1986
+ listen_port_xml_rpc: int | None = None,
1986
1987
  max_read_workers: int = DEFAULT_MAX_READ_WORKERS,
1987
1988
  periodic_refresh_interval: int = DEFAULT_PERIODIC_REFRESH_INTERVAL,
1988
1989
  program_markers: tuple[DescriptionMarker | str, ...] = DEFAULT_PROGRAM_MARKERS,
@@ -1997,11 +1998,14 @@ class CentralConfig:
1997
1998
  ) -> None:
1998
1999
  """Init the client config."""
1999
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
+ )
2000
2004
  self.callback_host: Final = callback_host
2001
- self.callback_port: Final = callback_port
2005
+ self.callback_port_xml_rpc: Final = callback_port_xml_rpc
2002
2006
  self.central_id: Final = central_id
2003
2007
  self.client_session: Final = client_session
2004
- self.default_callback_port: Final = default_callback_port
2008
+ self.default_callback_port_xml_rpc: Final = default_callback_port_xml_rpc
2005
2009
  self.delay_new_device_creation: Final = delay_new_device_creation
2006
2010
  self.enable_device_firmware_check: Final = enable_device_firmware_check
2007
2011
  self.enable_program_scan: Final = enable_program_scan
@@ -2012,7 +2016,7 @@ class CentralConfig:
2012
2016
  self.interfaces_requiring_periodic_refresh: Final = frozenset(interfaces_requiring_periodic_refresh or ())
2013
2017
  self.json_port: Final = json_port
2014
2018
  self.listen_ip_addr: Final = listen_ip_addr
2015
- self.listen_port: Final = listen_port
2019
+ self.listen_port_xml_rpc: Final = listen_port_xml_rpc
2016
2020
  self.max_read_workers = max_read_workers
2017
2021
  self.name: Final = name
2018
2022
  self.password: Final = password
@@ -2029,9 +2033,9 @@ class CentralConfig:
2029
2033
  self.verify_tls: Final = verify_tls
2030
2034
 
2031
2035
  @property
2032
- def enable_server(self) -> bool:
2036
+ def enable_xml_rpc_server(self) -> bool:
2033
2037
  """Return if server and connection checker should be started."""
2034
- return self.start_direct is False
2038
+ return self.requires_xml_rpc_server and self.start_direct is False
2035
2039
 
2036
2040
  @property
2037
2041
  def load_un_ignore(self) -> bool:
@@ -2066,7 +2070,7 @@ class CentralConfig:
2066
2070
  password=self.password,
2067
2071
  storage_folder=self.storage_folder,
2068
2072
  callback_host=self.callback_host,
2069
- callback_port=self.callback_port,
2073
+ callback_port_xml_rpc=self.callback_port_xml_rpc,
2070
2074
  json_port=self.json_port,
2071
2075
  interface_configs=self._interface_configs,
2072
2076
  ):
@@ -2091,9 +2095,9 @@ class CentralConfig:
2091
2095
  url = f"{url}:{self.json_port}"
2092
2096
  return f"{url}"
2093
2097
 
2094
- def create_json_rpc_client(self, *, central: CentralUnit) -> JsonRpcAioHttpClient:
2098
+ def create_json_rpc_client(self, *, central: CentralUnit) -> AioJsonRpcAioHttpClient:
2095
2099
  """Create a json rpc client."""
2096
- return JsonRpcAioHttpClient(
2100
+ return AioJsonRpcAioHttpClient(
2097
2101
  username=self.username,
2098
2102
  password=self.password,
2099
2103
  device_url=central.url,
@@ -2110,38 +2114,38 @@ class CentralConnectionState:
2110
2114
  def __init__(self) -> None:
2111
2115
  """Init the CentralConnectionStatus."""
2112
2116
  self._json_issues: Final[list[str]] = []
2113
- self._xml_proxy_issues: Final[list[str]] = []
2117
+ self._rpc_proxy_issues: Final[list[str]] = []
2114
2118
 
2115
2119
  def add_issue(self, *, issuer: ConnectionProblemIssuer, iid: str) -> bool:
2116
2120
  """Add issue to collection."""
2117
- if isinstance(issuer, JsonRpcAioHttpClient) and iid not in self._json_issues:
2121
+ if isinstance(issuer, AioJsonRpcAioHttpClient) and iid not in self._json_issues:
2118
2122
  self._json_issues.append(iid)
2119
2123
  _LOGGER.debug("add_issue: add issue [%s] for JsonRpcAioHttpClient", iid)
2120
2124
  return True
2121
- if isinstance(issuer, XmlRpcProxy) and iid not in self._xml_proxy_issues:
2122
- 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)
2123
2127
  _LOGGER.debug("add_issue: add issue [%s] for %s", iid, issuer.interface_id)
2124
2128
  return True
2125
2129
  return False
2126
2130
 
2127
2131
  def remove_issue(self, *, issuer: ConnectionProblemIssuer, iid: str) -> bool:
2128
2132
  """Add issue to collection."""
2129
- if isinstance(issuer, JsonRpcAioHttpClient) and iid in self._json_issues:
2133
+ if isinstance(issuer, AioJsonRpcAioHttpClient) and iid in self._json_issues:
2130
2134
  self._json_issues.remove(iid)
2131
2135
  _LOGGER.debug("remove_issue: removing issue [%s] for JsonRpcAioHttpClient", iid)
2132
2136
  return True
2133
- if isinstance(issuer, XmlRpcProxy) and issuer.interface_id in self._xml_proxy_issues:
2134
- 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)
2135
2139
  _LOGGER.debug("remove_issue: removing issue [%s] for %s", iid, issuer.interface_id)
2136
2140
  return True
2137
2141
  return False
2138
2142
 
2139
2143
  def has_issue(self, *, issuer: ConnectionProblemIssuer, iid: str) -> bool:
2140
2144
  """Add issue to collection."""
2141
- if isinstance(issuer, JsonRpcAioHttpClient):
2145
+ if isinstance(issuer, AioJsonRpcAioHttpClient):
2142
2146
  return iid in self._json_issues
2143
- if isinstance(issuer, XmlRpcProxy):
2144
- return iid in self._xml_proxy_issues
2147
+ if isinstance(issuer, (AioXmlRpcProxy)):
2148
+ return iid in self._rpc_proxy_issues
2145
2149
 
2146
2150
  def handle_exception_log(
2147
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(
@@ -12,7 +12,7 @@ from __future__ import annotations
12
12
  import contextlib
13
13
  import logging
14
14
  import threading
15
- from typing import Any, Final
15
+ from typing import Any, Final, cast
16
16
  from xmlrpc.server import SimpleXMLRPCRequestHandler, SimpleXMLRPCServer
17
17
 
18
18
  from aiohomematic import central as hmcu
@@ -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,11 +171,11 @@ 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
- _instances: Final[dict[tuple[str, int], XmlRpcServer]] = {}
178
+ _instances: Final[dict[tuple[str, int], RpcServer]] = {}
179
179
 
180
180
  def __init__(
181
181
  self,
@@ -190,46 +190,31 @@ 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)
204
193
  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
194
+ self._simple_rpc_server: SimpleXMLRPCServer
195
+ self._instances[self._address] = self
196
+ threading.Thread.__init__(self, name=f"RpcServer {ip_addr}:{self._listen_port}")
212
197
 
213
198
  def run(self) -> None:
214
- """Run the XmlRPC-Server thread."""
199
+ """Run the RPC-Server thread."""
215
200
  _LOGGER.debug(
216
- "RUN: Starting XmlRPC-Server listening on http://%s:%i",
201
+ "RUN: Starting RPC-Server listening on %s:%i",
217
202
  self._listen_ip_addr,
218
203
  self._listen_port,
219
204
  )
220
- if self._simple_xml_rpc_server:
221
- self._simple_xml_rpc_server.serve_forever()
205
+ if self._simple_rpc_server:
206
+ self._simple_rpc_server.serve_forever()
222
207
 
223
208
  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()
209
+ """Stop the RPC-Server."""
210
+ _LOGGER.debug("STOP: Shutting down RPC-Server")
211
+ self._simple_rpc_server.shutdown()
212
+ _LOGGER.debug("STOP: Stopping RPC-Server")
213
+ self._simple_rpc_server.server_close()
229
214
  # Ensure the server thread has actually terminated to avoid slow teardown
230
215
  with contextlib.suppress(RuntimeError):
231
216
  self.join(timeout=1.0)
232
- _LOGGER.debug("STOP: XmlRPC-Server stopped")
217
+ _LOGGER.debug("STOP: RPC-Server stopped")
233
218
  if self._address in self._instances:
234
219
  del self._instances[self._address]
235
220
 
@@ -249,12 +234,12 @@ class XmlRpcServer(threading.Thread):
249
234
  return self._started.is_set() is True # type: ignore[attr-defined]
250
235
 
251
236
  def add_central(self, *, central: hmcu.CentralUnit) -> None:
252
- """Register a central in the XmlRPC-Server."""
237
+ """Register a central in the RPC-Server."""
253
238
  if not self._centrals.get(central.name):
254
239
  self._centrals[central.name] = central
255
240
 
256
241
  def remove_central(self, *, central: hmcu.CentralUnit) -> None:
257
- """Unregister a central from XmlRPC-Server."""
242
+ """Unregister a central from RPC-Server."""
258
243
  if self._centrals.get(central.name):
259
244
  del self._centrals[central.name]
260
245
 
@@ -271,14 +256,46 @@ class XmlRpcServer(threading.Thread):
271
256
  return len(self._centrals) == 0
272
257
 
273
258
 
259
+ class XmlRpcServer(RpcServer):
260
+ """XML-RPC server thread to handle messages from the backend."""
261
+
262
+ def __init__(
263
+ self,
264
+ *,
265
+ ip_addr: str,
266
+ port: int,
267
+ ) -> None:
268
+ """Init XmlRPC server."""
269
+
270
+ if self._initialized:
271
+ return
272
+ super().__init__(ip_addr=ip_addr, port=port)
273
+ self._simple_rpc_server = HomematicXMLRPCServer(
274
+ addr=self._address,
275
+ requestHandler=RequestHandler,
276
+ logRequests=False,
277
+ allow_none=True,
278
+ )
279
+ self._simple_rpc_server.register_introspection_functions()
280
+ self._simple_rpc_server.register_multicall_functions()
281
+ self._simple_rpc_server.register_instance(RPCFunctions(rpc_server=self), allow_dotted_names=True)
282
+
283
+ def __new__(cls, ip_addr: str, port: int) -> XmlRpcServer: # noqa: PYI034 # kwonly: disable
284
+ """Create new RPC server."""
285
+ if (rpc := cls._instances.get((ip_addr, port))) is None:
286
+ _LOGGER.debug("Creating XmlRpc server")
287
+ return super().__new__(cls)
288
+ return cast(XmlRpcServer, rpc)
289
+
290
+
274
291
  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()
292
+ """Register the rpc server."""
293
+ rpc = XmlRpcServer(ip_addr=ip_addr, port=port)
294
+ if not rpc.started:
295
+ rpc.start()
279
296
  _LOGGER.debug(
280
297
  "CREATE_XML_RPC_SERVER: Starting XmlRPC-Server listening on %s:%i",
281
- xml_rpc.listen_ip_addr,
282
- xml_rpc.listen_port,
298
+ rpc.listen_ip_addr,
299
+ rpc.listen_port,
283
300
  )
284
- return xml_rpc
301
+ return rpc