diff --git a/changelog.md b/changelog.md index 5ab74154..03fd9d4b 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,7 @@ # Version 2024.8.15 (2024-08-26) - Avoid permanent cache save on remove device +- Check rx_mode - Ensure only one load/save of cache file at time - Small definition fix for DALI - Use ParameterData for paramset descriptions diff --git a/hahomematic/client/__init__.py b/hahomematic/client/__init__.py index b9f24e15..d926f149 100644 --- a/hahomematic/client/__init__.py +++ b/hahomematic/client/__init__.py @@ -24,6 +24,7 @@ VIRTUAL_REMOTE_TYPES, Backend, CallSource, + CommandRxMode, Description, DeviceDescription, ForcedDeviceAvailability, @@ -42,7 +43,13 @@ from hahomematic.performance import measure_execution_time from hahomematic.platforms.device import HmDevice from hahomematic.platforms.support import convert_value -from hahomematic.support import build_headers, build_xml_rpc_uri, get_device_address, reduce_args +from hahomematic.support import ( + build_headers, + build_xml_rpc_uri, + get_device_address, + reduce_args, + supports_rx_mode, +) _LOGGER: Final = logging.getLogger(__name__) @@ -427,7 +434,7 @@ async def _set_value( parameter: str, value: Any, wait_for_callback: int | None, - rx_mode: str | None = None, + rx_mode: CommandRxMode | None = None, check_against_pd: bool = False, ) -> set[ENTITY_KEY]: """Set single value on paramset VALUES.""" @@ -443,8 +450,12 @@ async def _set_value( else value ) _LOGGER.debug("SET_VALUE: %s, %s, %s", channel_address, parameter, checked_value) - if rx_mode: - await self._proxy.setValue(channel_address, parameter, checked_value, rx_mode) + if rx_mode and (device := self.central.get_device(address=channel_address)): + if supports_rx_mode(command_rx_mode=rx_mode, rx_modes=device.rx_modes): + await self._proxy.setValue(channel_address, parameter, checked_value, rx_mode) + else: + _LOGGER.warning("SET_VALUE failed: unsupported rx_mode: %s", rx_mode) + return set() else: await self._proxy.setValue(channel_address, parameter, checked_value) # store the send value in the last_value_send_cache @@ -493,7 +504,7 @@ async def set_value( parameter: str, value: Any, wait_for_callback: int | None = WAIT_FOR_CALLBACK, - rx_mode: str | None = None, + rx_mode: CommandRxMode | None = None, check_against_pd: bool = False, ) -> set[ENTITY_KEY]: """Set single value on paramset VALUES.""" @@ -546,7 +557,7 @@ async def put_paramset( paramset_key: ParamsetKey, values: dict[str, Any], wait_for_callback: int | None = WAIT_FOR_CALLBACK, - rx_mode: str | None = None, + rx_mode: CommandRxMode | None = None, check_against_pd: bool = False, ) -> set[ENTITY_KEY]: """ @@ -566,10 +577,14 @@ async def put_paramset( _LOGGER.debug( "PUT_PARAMSET: %s, %s, %s", channel_address, paramset_key, checked_values ) - if rx_mode: - await self._proxy.putParamset( - channel_address, paramset_key, checked_values, rx_mode - ) + if rx_mode and (device := self.central.get_device(address=channel_address)): + if supports_rx_mode(command_rx_mode=rx_mode, rx_modes=device.rx_modes): + await self._proxy.putParamset( + channel_address, paramset_key, checked_values, rx_mode + ) + else: + _LOGGER.warning("PUT_PARAMSET failed: unsupported rx_mode: %s", rx_mode) + return set() else: await self._proxy.putParamset(channel_address, paramset_key, checked_values) # store the send value in the last_value_send_cache diff --git a/hahomematic/const.py b/hahomematic/const.py index 62993dc4..12c4aa14 100644 --- a/hahomematic/const.py +++ b/hahomematic/const.py @@ -124,24 +124,14 @@ class DataOperationResult(Enum): class Description(StrEnum): """Enum with homematic device/paramset description attributes.""" - ADDRESS = "ADDRESS" - AVAILABLE_FIRMWARE = "AVAILABLE_FIRMWARE" - CHILDREN = "CHILDREN" DEFAULT = "DEFAULT" - FIRMWARE = "FIRMWARE" - FIRMWARE_UPDATABLE = "UPDATABLE" - FIRMWARE_UPDATE_STATE = "FIRMWARE_UPDATE_STATE" FLAGS = "FLAGS" MAX = "MAX" MIN = "MIN" NAME = "NAME" ID = "ID" OPERATIONS = "OPERATIONS" - PARAMSETS = "PARAMSETS" - PARENT = "PARENT" - PARENT_TYPE = "PARENT_TYPE" - SPECIAL = "SPECIAL" # Which has the following keys - SUBTYPE = "SUBTYPE" + SPECIAL = "SPECIAL" TYPE = "TYPE" UNIT = "UNIT" VALUE_LIST = "VALUE_LIST" @@ -401,6 +391,24 @@ class ProxyInitState(Enum): DE_INIT_SKIPPED = 16 +class RxMode(IntEnum): + """Enum for homematic rx modes.""" + + UNDEFINED = 0 + ALWAYS = 1 + BURST = 2 + CONFIG = 4 + WAKEUP = 8 + LAZY_CONFIG = 10 + + +class CommandRxMode(StrEnum): + """Enum for homematic rx modes for commands.""" + + BURST = "BURST" + WAKEUP = "WAKEUP" + + class SysvarType(StrEnum): """Enum for homematic sysvar types.""" @@ -606,4 +614,4 @@ class DeviceDescription(TypedDict, total=False): # TEAM_CHANNELS: list INTERFACE: str | None # ROAMING: int | None - RX_MODE: int | None + RX_MODE: int diff --git a/hahomematic/platforms/device.py b/hahomematic/platforms/device.py index 4eb21a10..418fd62c 100644 --- a/hahomematic/platforms/device.py +++ b/hahomematic/platforms/device.py @@ -41,6 +41,7 @@ ParameterData, ParamsetKey, ProductGroup, + RxMode, ) from hahomematic.exceptions import BaseHomematicException from hahomematic.platforms.custom import definition as hmed, entity as hmce @@ -56,6 +57,7 @@ check_or_create_directory, device_paramset_description_export_converter, get_entity_key, + get_rx_modes, reduce_args, ) @@ -98,6 +100,7 @@ def __init__(self, central: hmcu.CentralUnit, interface_id: str, device_address: ) self._device_type: Final = device_description["TYPE"] self._sub_type: Final = device_description.get("SUBTYPE") + self._rx_modes: Final = get_rx_modes(mode=device_description.get("RX_MODE", 0)) self._ignore_for_custom_entity: Final[bool] = ( central.parameter_visibility.device_type_is_ignored(device_type=self._device_type) @@ -290,6 +293,11 @@ def rooms(self) -> set[str]: """Return all rooms of the device.""" return self._rooms + @config_property + def rx_modes(self) -> tuple[RxMode, ...]: + """Return the rx mode.""" + return self._rx_modes + @config_property def sub_type(self) -> str | None: """Return the sub_type of the device.""" diff --git a/hahomematic/support.py b/hahomematic/support.py index 58a64462..42ac484c 100644 --- a/hahomematic/support.py +++ b/hahomematic/support.py @@ -29,8 +29,10 @@ INIT_DATETIME, MAX_CACHE_AGE, NO_CACHE_ENTRY, + CommandRxMode, ParameterData, ParamsetKey, + RxMode, SysvarType, ) from hahomematic.exceptions import BaseHomematicException, HaHomematicException @@ -480,3 +482,30 @@ def _make_value_hashable(value: Any) -> Any: return tuple(sorted(_make_value_hashable(e) for e in value)) return value + + +def get_rx_modes(mode: int) -> tuple[RxMode, ...]: + """Convert int to rx modes.""" + rx_modes: set[RxMode] = set() + if mode == 10: + rx_modes.add(RxMode.LAZY_CONFIG) + return tuple(rx_modes) + if mode & RxMode.WAKEUP: + mode -= RxMode.WAKEUP + rx_modes.add(RxMode.WAKEUP) + if mode & RxMode.CONFIG: + mode -= RxMode.CONFIG + rx_modes.add(RxMode.CONFIG) + if mode & RxMode.BURST: + mode -= RxMode.BURST + rx_modes.add(RxMode.BURST) + if mode & RxMode.ALWAYS: + rx_modes.add(RxMode.ALWAYS) + return tuple(rx_modes) + + +def supports_rx_mode(command_rx_mode: CommandRxMode, rx_modes: tuple[RxMode, ...]) -> bool: + """Check if rx mode is supported.""" + return (command_rx_mode == CommandRxMode.BURST and RxMode.BURST in rx_modes) or ( + command_rx_mode == CommandRxMode.WAKEUP and RxMode.WAKEUP in rx_modes + ) diff --git a/hahomematic_support/client_local.py b/hahomematic_support/client_local.py index 053e3774..f5d4d006 100644 --- a/hahomematic_support/client_local.py +++ b/hahomematic_support/client_local.py @@ -16,6 +16,7 @@ DEFAULT_ENCODING, ENTITY_KEY, CallSource, + CommandRxMode, InterfaceName, ParameterData, ParamsetKey, @@ -203,7 +204,7 @@ async def set_value( parameter: str, value: Any, wait_for_callback: int | None = WAIT_FOR_CALLBACK, - rx_mode: str | None = None, + rx_mode: CommandRxMode | None = None, check_against_pd: bool = False, ) -> set[ENTITY_KEY]: """Set single value on paramset VALUES.""" @@ -262,7 +263,7 @@ async def put_paramset( paramset_key: ParamsetKey, values: Any, wait_for_callback: int | None = WAIT_FOR_CALLBACK, - rx_mode: str | None = None, + rx_mode: CommandRxMode | None = None, check_against_pd: bool = False, ) -> set[ENTITY_KEY]: """