aiohomematic 2025.10.20b0__tar.gz → 2025.10.21__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.
- {aiohomematic-2025.10.20b0/aiohomematic.egg-info → aiohomematic-2025.10.21}/PKG-INFO +1 -1
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/central/__init__.py +55 -1
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/client/__init__.py +17 -15
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/const.py +3 -3
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/custom/data_point.py +5 -5
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/support.py +2 -2
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/store/dynamic.py +6 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/support.py +1 -53
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21/aiohomematic.egg-info}/PKG-INFO +1 -1
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/pyproject.toml +1 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/LICENSE +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/MANIFEST.in +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/README.md +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/__init__.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/async_support.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/central/decorators.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/central/rpc_server.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/client/_rpc_errors.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/client/json_rpc.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/client/rpc_proxy.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/context.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/converter.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/decorators.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/exceptions.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/hmcli.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/__init__.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/calculated/__init__.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/calculated/climate.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/calculated/data_point.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/calculated/operating_voltage_level.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/calculated/support.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/custom/__init__.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/custom/climate.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/custom/const.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/custom/cover.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/custom/definition.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/custom/light.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/custom/lock.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/custom/siren.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/custom/support.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/custom/switch.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/custom/valve.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/data_point.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/device.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/event.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/generic/__init__.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/generic/action.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/generic/binary_sensor.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/generic/button.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/generic/data_point.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/generic/number.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/generic/select.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/generic/sensor.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/generic/switch.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/generic/text.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/hub/__init__.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/hub/binary_sensor.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/hub/button.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/hub/data_point.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/hub/number.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/hub/select.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/hub/sensor.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/hub/switch.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/hub/text.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/update.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/property_decorators.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/py.typed +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/rega_scripts/fetch_all_device_data.fn +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/rega_scripts/get_program_descriptions.fn +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/rega_scripts/get_serial.fn +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/rega_scripts/get_system_variable_descriptions.fn +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/rega_scripts/set_program_state.fn +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/rega_scripts/set_system_variable.fn +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/store/__init__.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/store/persistent.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/store/visibility.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/validator.py +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic.egg-info/SOURCES.txt +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic.egg-info/dependency_links.txt +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic.egg-info/requires.txt +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic.egg-info/top_level.txt +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/requirements.txt +0 -0
- {aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aiohomematic
|
|
3
|
-
Version: 2025.10.
|
|
3
|
+
Version: 2025.10.21
|
|
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>
|
|
@@ -110,6 +110,7 @@ from aiohomematic.const import (
|
|
|
110
110
|
DEVICE_FIRMWARE_CHECK_INTERVAL,
|
|
111
111
|
DEVICE_FIRMWARE_DELIVERING_CHECK_INTERVAL,
|
|
112
112
|
DEVICE_FIRMWARE_UPDATING_CHECK_INTERVAL,
|
|
113
|
+
IDENTIFIER_SEPARATOR,
|
|
113
114
|
IGNORE_FOR_UN_IGNORE_PARAMETERS,
|
|
114
115
|
IP_ANY_V4,
|
|
115
116
|
LOCAL_HOST,
|
|
@@ -173,12 +174,16 @@ from aiohomematic.store import (
|
|
|
173
174
|
from aiohomematic.support import (
|
|
174
175
|
LogContextMixin,
|
|
175
176
|
PayloadMixin,
|
|
176
|
-
|
|
177
|
+
check_or_create_directory,
|
|
178
|
+
check_password,
|
|
177
179
|
extract_device_addresses_from_device_descriptions,
|
|
178
180
|
extract_exc_args,
|
|
179
181
|
get_channel_no,
|
|
180
182
|
get_device_address,
|
|
181
183
|
get_ip_addr,
|
|
184
|
+
is_hostname,
|
|
185
|
+
is_ipv4_address,
|
|
186
|
+
is_port,
|
|
182
187
|
)
|
|
183
188
|
|
|
184
189
|
__all__ = ["CentralConfig", "CentralUnit", "INTERFACE_EVENT_SCHEMA"]
|
|
@@ -2219,6 +2224,55 @@ class CentralConnectionState:
|
|
|
2219
2224
|
)
|
|
2220
2225
|
|
|
2221
2226
|
|
|
2227
|
+
def check_config(
|
|
2228
|
+
*,
|
|
2229
|
+
central_name: str,
|
|
2230
|
+
host: str,
|
|
2231
|
+
username: str,
|
|
2232
|
+
password: str,
|
|
2233
|
+
storage_directory: str,
|
|
2234
|
+
callback_host: str | None,
|
|
2235
|
+
callback_port_xml_rpc: int | None,
|
|
2236
|
+
json_port: int | None,
|
|
2237
|
+
interface_configs: AbstractSet[hmcl.InterfaceConfig] | None = None,
|
|
2238
|
+
) -> list[str]:
|
|
2239
|
+
"""Check config. Throws BaseHomematicException on failure."""
|
|
2240
|
+
config_failures: list[str] = []
|
|
2241
|
+
if central_name and IDENTIFIER_SEPARATOR in central_name:
|
|
2242
|
+
config_failures.append(f"Instance name must not contain {IDENTIFIER_SEPARATOR}")
|
|
2243
|
+
|
|
2244
|
+
if not (is_hostname(hostname=host) or is_ipv4_address(address=host)):
|
|
2245
|
+
config_failures.append("Invalid hostname or ipv4 address")
|
|
2246
|
+
if not username:
|
|
2247
|
+
config_failures.append("Username must not be empty")
|
|
2248
|
+
if not password:
|
|
2249
|
+
config_failures.append("Password is required")
|
|
2250
|
+
if not check_password(password=password):
|
|
2251
|
+
config_failures.append("Password is not valid")
|
|
2252
|
+
try:
|
|
2253
|
+
check_or_create_directory(directory=storage_directory)
|
|
2254
|
+
except BaseHomematicException as bhexc:
|
|
2255
|
+
config_failures.append(extract_exc_args(exc=bhexc)[0])
|
|
2256
|
+
if callback_host and not (is_hostname(hostname=callback_host) or is_ipv4_address(address=callback_host)):
|
|
2257
|
+
config_failures.append("Invalid callback hostname or ipv4 address")
|
|
2258
|
+
if callback_port_xml_rpc and not is_port(port=callback_port_xml_rpc):
|
|
2259
|
+
config_failures.append("Invalid xml rpc callback port")
|
|
2260
|
+
if json_port and not is_port(port=json_port):
|
|
2261
|
+
config_failures.append("Invalid json port")
|
|
2262
|
+
if interface_configs and not _has_primary_client(interface_configs=interface_configs):
|
|
2263
|
+
config_failures.append(f"No primary interface ({', '.join(PRIMARY_CLIENT_CANDIDATE_INTERFACES)}) defined")
|
|
2264
|
+
|
|
2265
|
+
return config_failures
|
|
2266
|
+
|
|
2267
|
+
|
|
2268
|
+
def _has_primary_client(*, interface_configs: AbstractSet[hmcl.InterfaceConfig]) -> bool:
|
|
2269
|
+
"""Check if all configured clients exists in central."""
|
|
2270
|
+
for interface_config in interface_configs:
|
|
2271
|
+
if interface_config.interface in PRIMARY_CLIENT_CANDIDATE_INTERFACES:
|
|
2272
|
+
return True
|
|
2273
|
+
return False
|
|
2274
|
+
|
|
2275
|
+
|
|
2222
2276
|
def _get_new_data_points(
|
|
2223
2277
|
*,
|
|
2224
2278
|
new_devices: set[Device],
|
|
@@ -58,8 +58,7 @@ from aiohomematic import central as hmcu
|
|
|
58
58
|
from aiohomematic.client.json_rpc import AioJsonRpcAioHttpClient
|
|
59
59
|
from aiohomematic.client.rpc_proxy import AioXmlRpcProxy, BaseRpcProxy
|
|
60
60
|
from aiohomematic.const import (
|
|
61
|
-
|
|
62
|
-
CALLBACK_WARN_DISARM_INTERVAL,
|
|
61
|
+
CALLBACK_WARN_INTERVAL,
|
|
63
62
|
DATETIME_FORMAT_MILLIS,
|
|
64
63
|
DEFAULT_MAX_WORKERS,
|
|
65
64
|
DP_KEY_VALUE,
|
|
@@ -146,6 +145,7 @@ class Client(ABC, LogContextMixin):
|
|
|
146
145
|
self._last_value_send_cache = CommandCache(interface_id=client_config.interface_id)
|
|
147
146
|
self._available: bool = True
|
|
148
147
|
self._connection_error_count: int = 0
|
|
148
|
+
self._is_callback_alive: bool = True
|
|
149
149
|
self._is_initialized: bool = False
|
|
150
150
|
self._ping_pong_cache: Final = PingPongCache(
|
|
151
151
|
central=client_config.central, interface_id=client_config.interface_id
|
|
@@ -395,7 +395,7 @@ class Client(ABC, LogContextMixin):
|
|
|
395
395
|
return False
|
|
396
396
|
if not self.supports_push_updates:
|
|
397
397
|
return True
|
|
398
|
-
return (datetime.now() - self.modified_at).total_seconds() <
|
|
398
|
+
return (datetime.now() - self.modified_at).total_seconds() < CALLBACK_WARN_INTERVAL
|
|
399
399
|
|
|
400
400
|
def is_callback_alive(self) -> bool:
|
|
401
401
|
"""Return if XmlRPC-Server is alive based on received events for this client."""
|
|
@@ -404,29 +404,31 @@ class Client(ABC, LogContextMixin):
|
|
|
404
404
|
if (
|
|
405
405
|
last_events_dt := self.central.get_last_event_seen_for_interface(interface_id=self.interface_id)
|
|
406
406
|
) is not None:
|
|
407
|
-
if (
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
407
|
+
if (seconds_since_last_event := (datetime.now() - last_events_dt).total_seconds()) > CALLBACK_WARN_INTERVAL:
|
|
408
|
+
if self._is_callback_alive:
|
|
409
|
+
self.central.fire_interface_event(
|
|
410
|
+
interface_id=self.interface_id,
|
|
411
|
+
interface_event_type=InterfaceEventType.CALLBACK,
|
|
412
|
+
data={
|
|
413
|
+
EventKey.AVAILABLE: False,
|
|
414
|
+
EventKey.SECONDS_SINCE_LAST_EVENT: int(seconds_since_last_event),
|
|
415
|
+
},
|
|
416
|
+
)
|
|
417
|
+
self._is_callback_alive = False
|
|
418
418
|
_LOGGER.warning(
|
|
419
419
|
"IS_CALLBACK_ALIVE: Callback for %s has not received events for %is",
|
|
420
420
|
self.interface_id,
|
|
421
421
|
seconds_since_last_event,
|
|
422
422
|
)
|
|
423
423
|
return False
|
|
424
|
-
|
|
424
|
+
|
|
425
|
+
if not self._is_callback_alive:
|
|
425
426
|
self.central.fire_interface_event(
|
|
426
427
|
interface_id=self.interface_id,
|
|
427
428
|
interface_event_type=InterfaceEventType.CALLBACK,
|
|
428
429
|
data={EventKey.AVAILABLE: True},
|
|
429
430
|
)
|
|
431
|
+
self._is_callback_alive = True
|
|
430
432
|
return True
|
|
431
433
|
|
|
432
434
|
@abstractmethod
|
|
@@ -19,7 +19,7 @@ import sys
|
|
|
19
19
|
from types import MappingProxyType
|
|
20
20
|
from typing import Any, Final, NamedTuple, Required, TypeAlias, TypedDict
|
|
21
21
|
|
|
22
|
-
VERSION: Final = "2025.10.
|
|
22
|
+
VERSION: Final = "2025.10.21"
|
|
23
23
|
|
|
24
24
|
# Detect test speedup mode via environment
|
|
25
25
|
_TEST_SPEEDUP: Final = (
|
|
@@ -133,8 +133,7 @@ WAIT_FOR_CALLBACK: Final[int | None] = None
|
|
|
133
133
|
SCHEDULER_NOT_STARTED_SLEEP: Final = 0.2 if _TEST_SPEEDUP else 10
|
|
134
134
|
SCHEDULER_LOOP_SLEEP: Final = 0.2 if _TEST_SPEEDUP else 5
|
|
135
135
|
|
|
136
|
-
|
|
137
|
-
CALLBACK_WARN_DISARM_INTERVAL: Final = CONNECTION_CHECKER_INTERVAL * 20
|
|
136
|
+
CALLBACK_WARN_INTERVAL: Final = CONNECTION_CHECKER_INTERVAL * 40
|
|
138
137
|
|
|
139
138
|
# Path
|
|
140
139
|
PROGRAM_SET_PATH_ROOT: Final = "program/set"
|
|
@@ -304,6 +303,7 @@ class EventKey(StrEnum):
|
|
|
304
303
|
INTERFACE_ID = "interface_id"
|
|
305
304
|
MODEL = "model"
|
|
306
305
|
PARAMETER = "parameter"
|
|
306
|
+
PONG_MISMATCH_ALLOWED = "pong_mismatch_allowed"
|
|
307
307
|
PONG_MISMATCH_COUNT = "pong_mismatch_count"
|
|
308
308
|
SECONDS_SINCE_LAST_EVENT = "seconds_since_last_event"
|
|
309
309
|
TYPE = "type"
|
{aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/custom/data_point.py
RENAMED
|
@@ -204,12 +204,12 @@ class CustomDataPoint(BaseDataPoint):
|
|
|
204
204
|
def _init_data_points(self) -> None:
|
|
205
205
|
"""Init data point collection."""
|
|
206
206
|
# Add repeating fields
|
|
207
|
-
for field_name, parameter in self._device_def.get(
|
|
207
|
+
for field_name, parameter in self._device_def.get(CDPD.REPEATABLE_FIELDS, {}).items():
|
|
208
208
|
if dp := self._device.get_generic_data_point(channel_address=self._channel.address, parameter=parameter):
|
|
209
209
|
self._add_data_point(field=field_name, data_point=dp, is_visible=False)
|
|
210
210
|
|
|
211
211
|
# Add visible repeating fields
|
|
212
|
-
for field_name, parameter in self._device_def.get(
|
|
212
|
+
for field_name, parameter in self._device_def.get(CDPD.VISIBLE_REPEATABLE_FIELDS, {}).items():
|
|
213
213
|
if dp := self._device.get_generic_data_point(channel_address=self._channel.address, parameter=parameter):
|
|
214
214
|
self._add_data_point(field=field_name, data_point=dp, is_visible=True)
|
|
215
215
|
|
|
@@ -229,11 +229,11 @@ class CustomDataPoint(BaseDataPoint):
|
|
|
229
229
|
|
|
230
230
|
# Add device fields
|
|
231
231
|
self._add_data_points(
|
|
232
|
-
field_dict_name=
|
|
232
|
+
field_dict_name=CDPD.FIELDS,
|
|
233
233
|
)
|
|
234
234
|
# Add visible device fields
|
|
235
235
|
self._add_data_points(
|
|
236
|
-
field_dict_name=
|
|
236
|
+
field_dict_name=CDPD.VISIBLE_FIELDS,
|
|
237
237
|
is_visible=True,
|
|
238
238
|
)
|
|
239
239
|
|
|
@@ -243,7 +243,7 @@ class CustomDataPoint(BaseDataPoint):
|
|
|
243
243
|
if hmed.get_include_default_data_points(device_profile=self._device_profile):
|
|
244
244
|
self._mark_data_points(custom_data_point_def=hmed.get_default_data_points())
|
|
245
245
|
|
|
246
|
-
def _add_data_points(self, *, field_dict_name:
|
|
246
|
+
def _add_data_points(self, *, field_dict_name: CDPD, is_visible: bool | None = None) -> None:
|
|
247
247
|
"""Add data points to custom data point."""
|
|
248
248
|
fields = self._device_def.get(field_dict_name, {})
|
|
249
249
|
for channel_no, channel in fields.items():
|
|
@@ -32,7 +32,7 @@ from aiohomematic.const import (
|
|
|
32
32
|
ParameterType,
|
|
33
33
|
)
|
|
34
34
|
from aiohomematic.model import device as hmd
|
|
35
|
-
from aiohomematic.model.custom import
|
|
35
|
+
from aiohomematic.model.custom.const import CDPD
|
|
36
36
|
from aiohomematic.support import to_bool
|
|
37
37
|
|
|
38
38
|
__all__ = [
|
|
@@ -565,7 +565,7 @@ def check_channel_is_the_only_primary_channel(
|
|
|
565
565
|
device_has_multiple_channels: bool,
|
|
566
566
|
) -> bool:
|
|
567
567
|
"""Check if this channel is the only primary channel."""
|
|
568
|
-
primary_channel: int = device_def[
|
|
568
|
+
primary_channel: int = device_def[CDPD.PRIMARY_CHANNEL]
|
|
569
569
|
return bool(primary_channel == current_channel_no and device_has_multiple_channels is False)
|
|
570
570
|
|
|
571
571
|
|
|
@@ -386,6 +386,11 @@ class PingPongCache:
|
|
|
386
386
|
self._pending_pong_logged: bool = False
|
|
387
387
|
self._unknown_pong_logged: bool = False
|
|
388
388
|
|
|
389
|
+
@property
|
|
390
|
+
def allowed_delta(self) -> int:
|
|
391
|
+
"""Return the allowed delta."""
|
|
392
|
+
return self._allowed_delta
|
|
393
|
+
|
|
389
394
|
@property
|
|
390
395
|
def high_pending_pongs(self) -> bool:
|
|
391
396
|
"""Check, if store contains too many pending pongs."""
|
|
@@ -515,6 +520,7 @@ class PingPongCache:
|
|
|
515
520
|
EventKey.TYPE: event_type,
|
|
516
521
|
EventKey.DATA: {
|
|
517
522
|
EventKey.CENTRAL_NAME: self._central.name,
|
|
523
|
+
EventKey.PONG_MISMATCH_ALLOWED: mismatch_count <= self._allowed_delta,
|
|
518
524
|
EventKey.PONG_MISMATCH_COUNT: mismatch_count,
|
|
519
525
|
},
|
|
520
526
|
}
|
|
@@ -10,7 +10,7 @@ from __future__ import annotations
|
|
|
10
10
|
|
|
11
11
|
import base64
|
|
12
12
|
from collections import defaultdict
|
|
13
|
-
from collections.abc import Callable, Collection, Mapping
|
|
13
|
+
from collections.abc import Callable, Collection, Mapping
|
|
14
14
|
import contextlib
|
|
15
15
|
from dataclasses import dataclass
|
|
16
16
|
from datetime import datetime
|
|
@@ -30,7 +30,6 @@ from typing import Any, Final, cast
|
|
|
30
30
|
|
|
31
31
|
import orjson
|
|
32
32
|
|
|
33
|
-
from aiohomematic import client as hmcl
|
|
34
33
|
from aiohomematic.const import (
|
|
35
34
|
ADDRESS_SEPARATOR,
|
|
36
35
|
ALLOWED_HOSTNAME_PATTERN,
|
|
@@ -38,12 +37,10 @@ from aiohomematic.const import (
|
|
|
38
37
|
CHANNEL_ADDRESS_PATTERN,
|
|
39
38
|
DEVICE_ADDRESS_PATTERN,
|
|
40
39
|
HTMLTAG_PATTERN,
|
|
41
|
-
IDENTIFIER_SEPARATOR,
|
|
42
40
|
INIT_DATETIME,
|
|
43
41
|
ISO_8859_1,
|
|
44
42
|
MAX_CACHE_AGE,
|
|
45
43
|
NO_CACHE_ENTRY,
|
|
46
|
-
PRIMARY_CLIENT_CANDIDATE_INTERFACES,
|
|
47
44
|
TIMEOUT,
|
|
48
45
|
UTF_8,
|
|
49
46
|
CommandRxMode,
|
|
@@ -95,55 +92,6 @@ def build_xml_rpc_headers(
|
|
|
95
92
|
return [("Authorization", f"Basic {base64_message}")]
|
|
96
93
|
|
|
97
94
|
|
|
98
|
-
def check_config(
|
|
99
|
-
*,
|
|
100
|
-
central_name: str,
|
|
101
|
-
host: str,
|
|
102
|
-
username: str,
|
|
103
|
-
password: str,
|
|
104
|
-
storage_directory: str,
|
|
105
|
-
callback_host: str | None,
|
|
106
|
-
callback_port_xml_rpc: int | None,
|
|
107
|
-
json_port: int | None,
|
|
108
|
-
interface_configs: AbstractSet[hmcl.InterfaceConfig] | None = None,
|
|
109
|
-
) -> list[str]:
|
|
110
|
-
"""Check config. Throws BaseHomematicException on failure."""
|
|
111
|
-
config_failures: list[str] = []
|
|
112
|
-
if central_name and IDENTIFIER_SEPARATOR in central_name:
|
|
113
|
-
config_failures.append(f"Instance name must not contain {IDENTIFIER_SEPARATOR}")
|
|
114
|
-
|
|
115
|
-
if not (is_hostname(hostname=host) or is_ipv4_address(address=host)):
|
|
116
|
-
config_failures.append("Invalid hostname or ipv4 address")
|
|
117
|
-
if not username:
|
|
118
|
-
config_failures.append("Username must not be empty")
|
|
119
|
-
if not password:
|
|
120
|
-
config_failures.append("Password is required")
|
|
121
|
-
if not check_password(password=password):
|
|
122
|
-
config_failures.append("Password is not valid")
|
|
123
|
-
try:
|
|
124
|
-
check_or_create_directory(directory=storage_directory)
|
|
125
|
-
except BaseHomematicException as bhexc:
|
|
126
|
-
config_failures.append(extract_exc_args(exc=bhexc)[0])
|
|
127
|
-
if callback_host and not (is_hostname(hostname=callback_host) or is_ipv4_address(address=callback_host)):
|
|
128
|
-
config_failures.append("Invalid callback hostname or ipv4 address")
|
|
129
|
-
if callback_port_xml_rpc and not is_port(port=callback_port_xml_rpc):
|
|
130
|
-
config_failures.append("Invalid xml rpc callback port")
|
|
131
|
-
if json_port and not is_port(port=json_port):
|
|
132
|
-
config_failures.append("Invalid json port")
|
|
133
|
-
if interface_configs and not has_primary_client(interface_configs=interface_configs):
|
|
134
|
-
config_failures.append(f"No primary interface ({', '.join(PRIMARY_CLIENT_CANDIDATE_INTERFACES)}) defined")
|
|
135
|
-
|
|
136
|
-
return config_failures
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
def has_primary_client(*, interface_configs: AbstractSet[hmcl.InterfaceConfig]) -> bool:
|
|
140
|
-
"""Check if all configured clients exists in central."""
|
|
141
|
-
for interface_config in interface_configs:
|
|
142
|
-
if interface_config.interface in PRIMARY_CLIENT_CANDIDATE_INTERFACES:
|
|
143
|
-
return True
|
|
144
|
-
return False
|
|
145
|
-
|
|
146
|
-
|
|
147
95
|
def delete_file(directory: str, file_name: str) -> None: # kwonly: disable
|
|
148
96
|
"""Delete the file. File can contain a wildcard."""
|
|
149
97
|
if os.path.exists(directory):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aiohomematic
|
|
3
|
-
Version: 2025.10.
|
|
3
|
+
Version: 2025.10.21
|
|
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>
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/calculated/__init__.py
RENAMED
|
File without changes
|
{aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/calculated/climate.py
RENAMED
|
File without changes
|
{aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/calculated/data_point.py
RENAMED
|
File without changes
|
|
File without changes
|
{aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/calculated/support.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/custom/definition.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/generic/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/generic/binary_sensor.py
RENAMED
|
File without changes
|
|
File without changes
|
{aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/generic/data_point.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/model/hub/binary_sensor.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/rega_scripts/get_serial.fn
RENAMED
|
File without changes
|
|
File without changes
|
{aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic/rega_scripts/set_program_state.fn
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{aiohomematic-2025.10.20b0 → aiohomematic-2025.10.21}/aiohomematic.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|