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.

Files changed (77) hide show
  1. aiohomematic/__init__.py +47 -0
  2. aiohomematic/async_support.py +146 -0
  3. aiohomematic/caches/__init__.py +10 -0
  4. aiohomematic/caches/dynamic.py +554 -0
  5. aiohomematic/caches/persistent.py +459 -0
  6. aiohomematic/caches/visibility.py +774 -0
  7. aiohomematic/central/__init__.py +2034 -0
  8. aiohomematic/central/decorators.py +110 -0
  9. aiohomematic/central/xml_rpc_server.py +267 -0
  10. aiohomematic/client/__init__.py +1746 -0
  11. aiohomematic/client/json_rpc.py +1193 -0
  12. aiohomematic/client/xml_rpc.py +222 -0
  13. aiohomematic/const.py +795 -0
  14. aiohomematic/context.py +8 -0
  15. aiohomematic/converter.py +82 -0
  16. aiohomematic/decorators.py +188 -0
  17. aiohomematic/exceptions.py +145 -0
  18. aiohomematic/hmcli.py +159 -0
  19. aiohomematic/model/__init__.py +137 -0
  20. aiohomematic/model/calculated/__init__.py +65 -0
  21. aiohomematic/model/calculated/climate.py +230 -0
  22. aiohomematic/model/calculated/data_point.py +319 -0
  23. aiohomematic/model/calculated/operating_voltage_level.py +311 -0
  24. aiohomematic/model/calculated/support.py +174 -0
  25. aiohomematic/model/custom/__init__.py +175 -0
  26. aiohomematic/model/custom/climate.py +1334 -0
  27. aiohomematic/model/custom/const.py +146 -0
  28. aiohomematic/model/custom/cover.py +741 -0
  29. aiohomematic/model/custom/data_point.py +318 -0
  30. aiohomematic/model/custom/definition.py +861 -0
  31. aiohomematic/model/custom/light.py +1092 -0
  32. aiohomematic/model/custom/lock.py +389 -0
  33. aiohomematic/model/custom/siren.py +268 -0
  34. aiohomematic/model/custom/support.py +40 -0
  35. aiohomematic/model/custom/switch.py +172 -0
  36. aiohomematic/model/custom/valve.py +112 -0
  37. aiohomematic/model/data_point.py +1109 -0
  38. aiohomematic/model/decorators.py +173 -0
  39. aiohomematic/model/device.py +1347 -0
  40. aiohomematic/model/event.py +210 -0
  41. aiohomematic/model/generic/__init__.py +211 -0
  42. aiohomematic/model/generic/action.py +32 -0
  43. aiohomematic/model/generic/binary_sensor.py +28 -0
  44. aiohomematic/model/generic/button.py +25 -0
  45. aiohomematic/model/generic/data_point.py +162 -0
  46. aiohomematic/model/generic/number.py +73 -0
  47. aiohomematic/model/generic/select.py +36 -0
  48. aiohomematic/model/generic/sensor.py +72 -0
  49. aiohomematic/model/generic/switch.py +52 -0
  50. aiohomematic/model/generic/text.py +27 -0
  51. aiohomematic/model/hub/__init__.py +334 -0
  52. aiohomematic/model/hub/binary_sensor.py +22 -0
  53. aiohomematic/model/hub/button.py +26 -0
  54. aiohomematic/model/hub/data_point.py +332 -0
  55. aiohomematic/model/hub/number.py +37 -0
  56. aiohomematic/model/hub/select.py +47 -0
  57. aiohomematic/model/hub/sensor.py +35 -0
  58. aiohomematic/model/hub/switch.py +42 -0
  59. aiohomematic/model/hub/text.py +28 -0
  60. aiohomematic/model/support.py +599 -0
  61. aiohomematic/model/update.py +136 -0
  62. aiohomematic/py.typed +0 -0
  63. aiohomematic/rega_scripts/fetch_all_device_data.fn +75 -0
  64. aiohomematic/rega_scripts/get_program_descriptions.fn +30 -0
  65. aiohomematic/rega_scripts/get_serial.fn +44 -0
  66. aiohomematic/rega_scripts/get_system_variable_descriptions.fn +30 -0
  67. aiohomematic/rega_scripts/set_program_state.fn +12 -0
  68. aiohomematic/rega_scripts/set_system_variable.fn +15 -0
  69. aiohomematic/support.py +482 -0
  70. aiohomematic/validator.py +65 -0
  71. aiohomematic-2025.8.6.dist-info/METADATA +69 -0
  72. aiohomematic-2025.8.6.dist-info/RECORD +77 -0
  73. aiohomematic-2025.8.6.dist-info/WHEEL +5 -0
  74. aiohomematic-2025.8.6.dist-info/licenses/LICENSE +21 -0
  75. aiohomematic-2025.8.6.dist-info/top_level.txt +2 -0
  76. aiohomematic_support/__init__.py +1 -0
  77. aiohomematic_support/client_local.py +349 -0
@@ -0,0 +1,77 @@
1
+ aiohomematic/__init__.py,sha256=VoLXfPmEFWVOFseEusar-BcfKhQ8pBthrA7M6_QkWg8,1741
2
+ aiohomematic/async_support.py,sha256=8KzftPu9CHRpxxXU8ElExYYadN9P6rSkMvLK5Ci2Xlo,5476
3
+ aiohomematic/const.py,sha256=qT1lXjFm1WIemDsV5owjuml2GqOcFVSUrbwGHCFC8gE,23474
4
+ aiohomematic/context.py,sha256=4CNoLd4PQ8MsLED_3D-Gcxo4h2UNlyQNd7QUE_0uvBA,253
5
+ aiohomematic/converter.py,sha256=eZ3iQPCfYZwqxQgUy4q6aMne_Bay-27f1HQuMRdabZ4,2922
6
+ aiohomematic/decorators.py,sha256=YD7zX9jBN-F-NdfKGNn0STqR6cndpoBCMuXaBvgvam4,7088
7
+ aiohomematic/exceptions.py,sha256=uMmbR-rWmCnseqmOPSHZ826YZ10uJrxG1c0w7VCYPZ0,4579
8
+ aiohomematic/hmcli.py,sha256=9oeXjCy50hkjMj7t1c9Cw0bz-LnXzjheKQBQDyGQQRk,4582
9
+ aiohomematic/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ aiohomematic/support.py,sha256=PgtFBHLVllnXS8AweQCNBiekL1__EgEAcGPjPNrqUi8,15741
11
+ aiohomematic/validator.py,sha256=Xxcmf2MzjIvzsDAfJsPR-cSN8S06sub-V3TCn6ronAs,1765
12
+ aiohomematic/caches/__init__.py,sha256=Uco9Y5fVmIPm_iRoLPWJnJNYfs9PY6VL-Jmj_3cT3yE,391
13
+ aiohomematic/caches/dynamic.py,sha256=CREQR87-I8rvvFOyrxaL9q161VKqEvyaRYi55fc-zos,21755
14
+ aiohomematic/caches/persistent.py,sha256=pKWvzOoIB55iFtCD9RoufrxOPsorZU2QH5IcXlRujRY,19586
15
+ aiohomematic/caches/visibility.py,sha256=mEOLz3WMzDtBp6ghh9Y0Gg_DUJBSH1SyqyCivFgY5wM,29843
16
+ aiohomematic/central/__init__.py,sha256=DaEtE_0ODH5GO_HBJt1OzZ2fIDG8r1UhE8odPTTZo0k,84226
17
+ aiohomematic/central/decorators.py,sha256=Lgc9SzvugZ45h_1anPjBNgzTjQ8BgDHnquE1PD48-XY,4899
18
+ aiohomematic/central/xml_rpc_server.py,sha256=xwcb57ow7YEUS4By0HSBwzRSEY8kC9nZ-7hl4M-EWqI,9957
19
+ aiohomematic/client/__init__.py,sha256=n2rNU3L1Xr453xsTCTM2mh5N1alp0yftAIienziB6ak,69753
20
+ aiohomematic/client/json_rpc.py,sha256=HGgCXcp1uCfDkBdHlMNV8OD5O2PfWFRYNmiX5Pv1FO8,46463
21
+ aiohomematic/client/xml_rpc.py,sha256=pUFdJZBtirNyNNyHbeMDOEh2mFkxQf_zrvjd56n1Lv4,8161
22
+ aiohomematic/model/__init__.py,sha256=AbrppmY8X5rJkBV3c7Ggndk16Qvl-1S0uUWJqqxmXUE,5380
23
+ aiohomematic/model/data_point.py,sha256=mMiUzGmZ_261iYV0FSCTCLGal7WTh69UqdMd_IfV0sg,39739
24
+ aiohomematic/model/decorators.py,sha256=sIxYsoyIE9aqbbrAnuFSvebLhNZC_DaMlEOBe6T_fRA,6718
25
+ aiohomematic/model/device.py,sha256=w6zWUFSI7yXbRyrFwfQxWzzk7kortIy8JhJGf7IG1fo,52435
26
+ aiohomematic/model/event.py,sha256=4dVuW3IBXkXcYhJUBtV8ax8dq4krtomA0wxbvXPuNCc,6935
27
+ aiohomematic/model/support.py,sha256=fXaGvD6ywaOmDihIAHjn-z3AuIn0Q3Wiq7EqcxUV6uc,19630
28
+ aiohomematic/model/update.py,sha256=r7Swejskx2Gg0F3zKaiZv5dBIuG49ZhvE3GlDG4PHB4,4880
29
+ aiohomematic/model/calculated/__init__.py,sha256=hzVYpyyXWUIf0skSLTrOhaczVg6mCDpPg1KJsRGWukE,2734
30
+ aiohomematic/model/calculated/climate.py,sha256=yu3mFBAO6Cb_r68v197uXseOl9P1jz7Dz0tUaCY-6FE,8436
31
+ aiohomematic/model/calculated/data_point.py,sha256=-ZC10Iz5opXqs2nkiwerF3caMIMdWwS9fVwYYaagTE0,11268
32
+ aiohomematic/model/calculated/operating_voltage_level.py,sha256=F09jW2rFtjxrsR25xkN-uZxJAdw_dcDr8hv-xvTiBCs,13932
33
+ aiohomematic/model/calculated/support.py,sha256=ht_KSFUT2Dze9wjw_o0Gp2-PGo4on0W0CZujhCWzwIk,6019
34
+ aiohomematic/model/custom/__init__.py,sha256=6-jN7l6DPIUSCANw15SwNQFyL8aXxn8Me8m8fNHkWKc,5987
35
+ aiohomematic/model/custom/climate.py,sha256=Sd3KofB1G085bKK1JKB7k4uaySD3W4SKbw-Fg9FH0PU,53930
36
+ aiohomematic/model/custom/const.py,sha256=ifJgooQqo43qEqfPQTIcyY7IcUeFvM0-y4WvP-_HgXM,4882
37
+ aiohomematic/model/custom/cover.py,sha256=aU8sSdKnGuiPBAO73GtmOMcYLoU7VmFldBxDf601dII,27474
38
+ aiohomematic/model/custom/data_point.py,sha256=jpDpQlZs2iYeI4dms1kkhzAMa__f9Dacvf93E6ad5xs,13843
39
+ aiohomematic/model/custom/definition.py,sha256=VQ8dROZSAVu-CarYc2jN_KrlvGI1kjhwUqD0ZR1qTUA,35402
40
+ aiohomematic/model/custom/light.py,sha256=IGT98GlqRftNzZpFP8NR_aQG_EQHyr191O5xkcpVaz4,43843
41
+ aiohomematic/model/custom/lock.py,sha256=6_xOeUFfVRJh_BjTY6RVLaCVmIozQo-uUMrbDqASt8g,11858
42
+ aiohomematic/model/custom/siren.py,sha256=5WLiYfUE7vBNVDlV8Whjqh-7PVMiReW-CI5MDH25R0s,9573
43
+ aiohomematic/model/custom/support.py,sha256=Rn6ikcSE2Hcqv34k2h02osXW45poS0hO3GX_wtTpMyE,1374
44
+ aiohomematic/model/custom/switch.py,sha256=XIhlGvtkZaId4jNE1XtYpJfFuvFofzlAN6tUPHzLgTA,6790
45
+ aiohomematic/model/custom/valve.py,sha256=u1mv5-hllX89GVN5Ir_svJcJOGdcS8JNYu0NAZsthTc,4147
46
+ aiohomematic/model/generic/__init__.py,sha256=qYNbPuFlanpDsrw9GKbWAT48pcNaAYWKgERpkv91HzU,7534
47
+ aiohomematic/model/generic/action.py,sha256=NMbMbNX6JSQH56M4CkkYstx1nb1nYuLXtFjZR2wg2UU,915
48
+ aiohomematic/model/generic/binary_sensor.py,sha256=jHyvrkHyFuWdVOQiBWmD25HZqwk6tU6VKuWiZyrt2kU,805
49
+ aiohomematic/model/generic/button.py,sha256=-6W7dEGq4bXvdEtQTtSA-54PurHD6in8umBaUiUtqNA,663
50
+ aiohomematic/model/generic/data_point.py,sha256=QTzbjXSmOmDKrr7nFl1YrRk14gN07Iv7FbFp58Ih69I,5861
51
+ aiohomematic/model/generic/number.py,sha256=6JIFPNED5WjghT1o0hM-OObhUXX42t-Cv7jAhNy52rw,2522
52
+ aiohomematic/model/generic/select.py,sha256=-DUuavyD7-CicNZboA_kAV4kBN6w7mtoP_5Sn_6fNgA,1388
53
+ aiohomematic/model/generic/sensor.py,sha256=dMjI0RSKIl6xI1VnTPFJe5HCUdfOKtfAXHLtX0Hwoiw,2171
54
+ aiohomematic/model/generic/switch.py,sha256=bzJKvLmGN7yBoWSTZCjiXRnq2sBduKvRVGohBGrjKSE,1757
55
+ aiohomematic/model/generic/text.py,sha256=ig-ilSa6CFlf65Sopg-E5jC9rK0Ma1vs2nzBxdprIpQ,772
56
+ aiohomematic/model/hub/__init__.py,sha256=3LTVY_pPKdJsHBDx8BFzV0nQwDM9zDU-7JqSlsPU_Wg,13492
57
+ aiohomematic/model/hub/binary_sensor.py,sha256=MG75YRZAkLOCs8ZVj2kr-EFgUte4v6dV_C3vdqZ-4jU,670
58
+ aiohomematic/model/hub/button.py,sha256=3NWWQDjsQ4aIi3WGOVTWNi8Ry2shsvmqdgLNF6SvOGg,810
59
+ aiohomematic/model/hub/data_point.py,sha256=44AXesJ6X9YGpNPDyDwHf6Z0VjB3thdgm040gILxxUc,10427
60
+ aiohomematic/model/hub/number.py,sha256=auwUxR2J5rF_ZKSKSDo4XSiZUcXJ0uYUiCJO94_vmQM,1145
61
+ aiohomematic/model/hub/select.py,sha256=UTng9oNf7y-x-2wMnd7UL8rZ0goyH8GsfQhz8MJrw8o,1579
62
+ aiohomematic/model/hub/sensor.py,sha256=q_QWbkt2rycHeJJ5hlBGJJg0-Rfum8vrLA8BoZZyGq4,1110
63
+ aiohomematic/model/hub/switch.py,sha256=W5iGOgL4A8BWFw5_rEYcfxyRVCnXIYy5tYt5sbpShCg,1312
64
+ aiohomematic/model/hub/text.py,sha256=LGgFQNHgXreCIzMe3j21KFiOjliLv5a0xJrnUb1_P9k,908
65
+ aiohomematic/rega_scripts/fetch_all_device_data.fn,sha256=GX9GDjWCfMgEPN6uP9RHD75IppcMth5NmRqVsQWUYJ4,3168
66
+ aiohomematic/rega_scripts/get_program_descriptions.fn,sha256=pGmj377MkqbZi6j-UBKQAsXTphwh1kDwDKqXij8zUBE,835
67
+ aiohomematic/rega_scripts/get_serial.fn,sha256=t1oeo-sB_EuVeiY24PLcxFSkdQVgEWGXzpemJQZFybY,1079
68
+ aiohomematic/rega_scripts/get_system_variable_descriptions.fn,sha256=UKXvC0_5lSApdQ2atJc0E5Stj5Zt3lqh0EcliokYu2c,849
69
+ aiohomematic/rega_scripts/set_program_state.fn,sha256=0bnv7lUj8FMjDZBz325tDVP61m04cHjVj4kIOnUUgpY,279
70
+ aiohomematic/rega_scripts/set_system_variable.fn,sha256=sTmr7vkPTPnPkor5cnLKlDvfsYRbGO1iq2z_2pMXq5E,383
71
+ aiohomematic-2025.8.6.dist-info/licenses/LICENSE,sha256=4kjAuxWtZ7ln4Gj-i8ZxrHEyPijP_bfevH5wjQxntnU,1069
72
+ aiohomematic_support/__init__.py,sha256=_0YtF4lTdC_k6-zrM2IefI0u0LMr_WA61gXAyeGLgbY,66
73
+ aiohomematic_support/client_local.py,sha256=cvkO3tcxSJKy5ZLVdeqDbvSmeottxqZJNI4ccdxIVD4,12524
74
+ aiohomematic-2025.8.6.dist-info/METADATA,sha256=Tzr48xIbtwyYn6YAreZC9k9Ri9x4F0ErG0erow_58zI,3244
75
+ aiohomematic-2025.8.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
76
+ aiohomematic-2025.8.6.dist-info/top_level.txt,sha256=5TDRlUWQPThIUwQjOj--aUo4UA-ow4m0sNhnoCBi5n8,34
77
+ aiohomematic-2025.8.6.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 Daniel Perna
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,2 @@
1
+ aiohomematic
2
+ aiohomematic_support
@@ -0,0 +1 @@
1
+ """Module to support aiohomematic testing with a local client."""
@@ -0,0 +1,349 @@
1
+ """The local client-object and its methods."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from _collections import defaultdict
6
+ from dataclasses import dataclass
7
+ from datetime import datetime
8
+ import importlib.resources
9
+ import logging
10
+ import os
11
+ from typing import Any, Final, cast
12
+
13
+ import orjson
14
+
15
+ from aiohomematic.client import _LOGGER, Client, _ClientConfig
16
+ from aiohomematic.const import (
17
+ ADDRESS_SEPARATOR,
18
+ DP_KEY_VALUE,
19
+ UTF_8,
20
+ WAIT_FOR_CALLBACK,
21
+ CallSource,
22
+ CommandRxMode,
23
+ DescriptionMarker,
24
+ DeviceDescription,
25
+ Interface,
26
+ ParameterData,
27
+ ParamsetKey,
28
+ ProductGroup,
29
+ ProgramData,
30
+ ProxyInitState,
31
+ SystemInformation,
32
+ SystemVariableData,
33
+ )
34
+ from aiohomematic.decorators import inspector
35
+ from aiohomematic.support import is_channel_address
36
+
37
+ LOCAL_SERIAL: Final = "0815_4711"
38
+ BACKEND_LOCAL: Final = "Local CCU"
39
+
40
+
41
+ class ClientLocal(Client): # pragma: no cover
42
+ """Local client object to provide access to locally stored files."""
43
+
44
+ def __init__(self, client_config: _ClientConfig, local_resources: LocalRessources) -> None:
45
+ """Initialize the Client."""
46
+ super().__init__(client_config=client_config)
47
+ self._local_resources = local_resources
48
+ self._paramset_descriptions_cache: dict[str, dict[ParamsetKey, dict[str, ParameterData]]] = defaultdict(
49
+ lambda: defaultdict(dict)
50
+ )
51
+
52
+ async def init_client(self) -> None:
53
+ """Init the client."""
54
+ self._system_information = await self._get_system_information()
55
+
56
+ @property
57
+ def available(self) -> bool:
58
+ """Return the availability of the client."""
59
+ return True
60
+
61
+ @property
62
+ def model(self) -> str:
63
+ """Return the model of the backend."""
64
+ return BACKEND_LOCAL
65
+
66
+ def get_product_group(self, model: str) -> ProductGroup:
67
+ """Return the product group."""
68
+ l_model = model.lower()
69
+ if l_model.startswith("hmipw"):
70
+ return ProductGroup.HMIPW
71
+ if l_model.startswith("hmip"):
72
+ return ProductGroup.HMIP
73
+ if l_model.startswith("hmw"):
74
+ return ProductGroup.HMW
75
+ if l_model.startswith("hm"):
76
+ return ProductGroup.HM
77
+ return ProductGroup.UNKNOWN
78
+
79
+ @property
80
+ def supports_ping_pong(self) -> bool:
81
+ """Return the supports_ping_pong info of the backend."""
82
+ return True
83
+
84
+ @property
85
+ def supports_push_updates(self) -> bool:
86
+ """Return the client supports push update."""
87
+ return True
88
+
89
+ async def initialize_proxy(self) -> ProxyInitState:
90
+ """Init the proxy has to tell the CCU / Homegear where to send the events."""
91
+ return ProxyInitState.INIT_SUCCESS
92
+
93
+ async def deinitialize_proxy(self) -> ProxyInitState:
94
+ """De-init to stop CCU from sending events for this remote."""
95
+ return ProxyInitState.DE_INIT_SUCCESS
96
+
97
+ async def stop(self) -> None:
98
+ """Stop depending services."""
99
+
100
+ @inspector(re_raise=False, measure_performance=True)
101
+ async def fetch_all_device_data(self) -> None:
102
+ """Fetch all device data from CCU."""
103
+
104
+ @inspector(re_raise=False, measure_performance=True)
105
+ async def fetch_device_details(self) -> None:
106
+ """Fetch names from backend."""
107
+
108
+ @inspector(re_raise=False, no_raise_return=False)
109
+ async def is_connected(self) -> bool:
110
+ """
111
+ Perform actions required for connectivity check.
112
+
113
+ Connection is not connected, if three consecutive checks fail.
114
+ Return connectivity state.
115
+ """
116
+ return True
117
+
118
+ def is_callback_alive(self) -> bool:
119
+ """Return if XmlRPC-Server is alive based on received events for this client."""
120
+ return True
121
+
122
+ @inspector(re_raise=False, no_raise_return=False)
123
+ async def check_connection_availability(self, handle_ping_pong: bool) -> bool:
124
+ """Send ping to CCU to generate PONG event."""
125
+ if handle_ping_pong and self.supports_ping_pong:
126
+ self._ping_pong_cache.handle_send_ping(ping_ts=datetime.now())
127
+ return True
128
+
129
+ @inspector()
130
+ async def execute_program(self, pid: str) -> bool:
131
+ """Execute a program on CCU / Homegear."""
132
+ return True
133
+
134
+ @inspector()
135
+ async def set_program_state(self, pid: str, state: bool) -> bool:
136
+ """Set the program state on CCU / Homegear."""
137
+ return True
138
+
139
+ @inspector(measure_performance=True)
140
+ async def set_system_variable(self, legacy_name: str, value: Any) -> bool:
141
+ """Set a system variable on CCU / Homegear."""
142
+ return True
143
+
144
+ @inspector()
145
+ async def delete_system_variable(self, name: str) -> bool:
146
+ """Delete a system variable from CCU / Homegear."""
147
+ return True
148
+
149
+ @inspector()
150
+ async def get_system_variable(self, name: str) -> str:
151
+ """Get single system variable from CCU / Homegear."""
152
+ return "Empty"
153
+
154
+ @inspector(re_raise=False)
155
+ async def get_all_system_variables(
156
+ self, markers: tuple[DescriptionMarker | str, ...]
157
+ ) -> tuple[SystemVariableData, ...]:
158
+ """Get all system variables from CCU / Homegear."""
159
+ return ()
160
+
161
+ @inspector(re_raise=False)
162
+ async def get_all_programs(self, markers: tuple[DescriptionMarker | str, ...]) -> tuple[ProgramData, ...]:
163
+ """Get all programs, if available."""
164
+ return ()
165
+
166
+ @inspector(re_raise=False, no_raise_return={})
167
+ async def get_all_rooms(self) -> dict[str, set[str]]:
168
+ """Get all rooms, if available."""
169
+ return {}
170
+
171
+ @inspector(re_raise=False, no_raise_return={})
172
+ async def get_all_functions(self) -> dict[str, set[str]]:
173
+ """Get all functions, if available."""
174
+ return {}
175
+
176
+ async def _get_system_information(self) -> SystemInformation:
177
+ """Get system information of the backend."""
178
+ return SystemInformation(available_interfaces=(Interface.BIDCOS_RF,), serial=LOCAL_SERIAL)
179
+
180
+ @inspector(re_raise=False, measure_performance=True)
181
+ async def list_devices(self) -> tuple[DeviceDescription, ...] | None:
182
+ """Get device descriptions from CCU / Homegear."""
183
+ if not self._local_resources:
184
+ _LOGGER.warning(
185
+ "LIST_DEVICES: missing local_resources in config for %s",
186
+ self.central.name,
187
+ )
188
+ return None
189
+ device_descriptions: list[DeviceDescription] = []
190
+ if local_device_descriptions := cast(
191
+ list[Any],
192
+ await self._load_all_json_files(
193
+ anchor=self._local_resources.anchor,
194
+ resource=self._local_resources.device_description_dir,
195
+ include_list=list(self._local_resources.address_device_translation.values()),
196
+ exclude_list=self._local_resources.ignore_devices_on_create,
197
+ ),
198
+ ):
199
+ for device_description in local_device_descriptions:
200
+ device_descriptions.extend(device_description)
201
+ return tuple(device_descriptions)
202
+
203
+ @inspector(log_level=logging.NOTSET)
204
+ async def get_value(
205
+ self,
206
+ channel_address: str,
207
+ paramset_key: ParamsetKey,
208
+ parameter: str,
209
+ call_source: CallSource = CallSource.MANUAL_OR_SCHEDULED,
210
+ ) -> Any:
211
+ """Return a value from CCU."""
212
+ return
213
+
214
+ @inspector(re_raise=False, no_raise_return=set())
215
+ async def set_value(
216
+ self,
217
+ channel_address: str,
218
+ paramset_key: ParamsetKey,
219
+ parameter: str,
220
+ value: Any,
221
+ wait_for_callback: int | None = WAIT_FOR_CALLBACK,
222
+ rx_mode: CommandRxMode | None = None,
223
+ check_against_pd: bool = False,
224
+ ) -> set[DP_KEY_VALUE]:
225
+ """Set single value on paramset VALUES."""
226
+ # store the send value in the last_value_send_cache
227
+ result = self._last_value_send_cache.add_set_value(
228
+ channel_address=channel_address, parameter=parameter, value=value
229
+ )
230
+ # fire an event to fake the state change for a simple parameter
231
+ await self.central.data_point_event(self.interface_id, channel_address, parameter, value)
232
+ return result
233
+
234
+ @inspector()
235
+ async def get_paramset(
236
+ self,
237
+ address: str,
238
+ paramset_key: ParamsetKey | str,
239
+ call_source: CallSource = CallSource.MANUAL_OR_SCHEDULED,
240
+ ) -> Any:
241
+ """
242
+ Return a paramset from CCU.
243
+
244
+ Address is usually the channel_address,
245
+ but for bidcos devices there is a master paramset at the device.
246
+ """
247
+ return {}
248
+
249
+ async def _get_paramset_description(
250
+ self, address: str, paramset_key: ParamsetKey
251
+ ) -> dict[str, ParameterData] | None:
252
+ """Get paramset description from CCU."""
253
+ if not self._local_resources:
254
+ _LOGGER.warning(
255
+ "GET_PARAMSET_DESCRIPTION: missing local_resources in config for %s",
256
+ self.central.name,
257
+ )
258
+ return None
259
+
260
+ if (
261
+ address not in self._paramset_descriptions_cache
262
+ and (file_name := self._local_resources.address_device_translation.get(address.split(ADDRESS_SEPARATOR)[0]))
263
+ and (
264
+ data := await self._load_json_file(
265
+ anchor=self._local_resources.anchor,
266
+ resource=self._local_resources.paramset_description_dir,
267
+ filename=file_name,
268
+ )
269
+ )
270
+ ):
271
+ self._paramset_descriptions_cache.update(data)
272
+
273
+ return self._paramset_descriptions_cache[address].get(paramset_key)
274
+
275
+ @inspector(measure_performance=True)
276
+ async def put_paramset(
277
+ self,
278
+ channel_address: str,
279
+ paramset_key_or_link_address: ParamsetKey | str,
280
+ values: Any,
281
+ wait_for_callback: int | None = WAIT_FOR_CALLBACK,
282
+ rx_mode: CommandRxMode | None = None,
283
+ check_against_pd: bool = False,
284
+ ) -> set[DP_KEY_VALUE]:
285
+ """
286
+ Set paramsets manually.
287
+
288
+ Address is usually the channel_address,
289
+ but for bidcos devices there is a master paramset at the device.
290
+ """
291
+ # store the send value in the last_value_send_cache
292
+ if isinstance(paramset_key_or_link_address, str) and is_channel_address(address=paramset_key_or_link_address):
293
+ result = set()
294
+ else:
295
+ result = self._last_value_send_cache.add_put_paramset(
296
+ channel_address=channel_address,
297
+ paramset_key=ParamsetKey(paramset_key_or_link_address),
298
+ values=values,
299
+ )
300
+
301
+ # fire an event to fake the state change for the content of a paramset
302
+ for parameter in values:
303
+ await self.central.data_point_event(self.interface_id, channel_address, parameter, values[parameter])
304
+ return result
305
+
306
+ async def _load_all_json_files(
307
+ self,
308
+ anchor: str,
309
+ resource: str,
310
+ include_list: list[str] | None = None,
311
+ exclude_list: list[str] | None = None,
312
+ ) -> list[Any] | None:
313
+ """Load all json files from disk into dict."""
314
+ if not include_list:
315
+ return []
316
+ if not exclude_list:
317
+ exclude_list = []
318
+ result: list[Any] = []
319
+ resource_path = os.path.join(str(importlib.resources.files(anchor)), resource)
320
+ for filename in os.listdir(resource_path):
321
+ if filename not in include_list or filename in exclude_list:
322
+ continue
323
+ if file_content := await self._load_json_file(anchor=anchor, resource=resource, filename=filename):
324
+ result.append(file_content)
325
+ return result
326
+
327
+ async def _load_json_file(self, anchor: str, resource: str, filename: str) -> Any | None:
328
+ """Load json file from disk into dict."""
329
+ package_path = str(importlib.resources.files(anchor))
330
+
331
+ def _perform_load() -> Any | None:
332
+ with open(
333
+ file=os.path.join(package_path, resource, filename),
334
+ encoding=UTF_8,
335
+ ) as fptr:
336
+ return orjson.loads(fptr.read())
337
+
338
+ return await self.central.looper.async_add_executor_job(_perform_load, name="load-json-file")
339
+
340
+
341
+ @dataclass(frozen=True, kw_only=True, slots=True)
342
+ class LocalRessources:
343
+ """Dataclass with information for local client."""
344
+
345
+ address_device_translation: dict[str, str]
346
+ ignore_devices_on_create: list[str]
347
+ anchor: str = "pydevccu"
348
+ device_description_dir: str = "device_descriptions"
349
+ paramset_description_dir: str = "paramset_descriptions"