From 0588b70b7180a786442784ac13518de87c2d20ac Mon Sep 17 00:00:00 2001 From: SukramJ Date: Wed, 4 Sep 2024 18:59:48 +0200 Subject: [PATCH] Cleanup member access (#1698) * Reduce number of required characters for an address identification * Cleanup member access --- changelog.md | 1 + hahomematic/caches/dynamic.py | 2 +- hahomematic/central/__init__.py | 237 +++++++++++++++------------- hahomematic/client/__init__.py | 56 ++++--- hahomematic/const.py | 4 +- hahomematic/platforms/device.py | 31 ++-- hahomematic/platforms/entity.py | 4 +- hahomematic_support/client_local.py | 2 +- tests/helper.py | 35 +++- tests/test_climate.py | 36 ++--- tests/test_cover.py | 30 ++-- tests/test_light.py | 160 +++++++++---------- tests/test_lock.py | 4 +- tests/test_number.py | 10 +- tests/test_select.py | 4 +- tests/test_support.py | 26 ++- tests/test_switch.py | 16 +- 17 files changed, 365 insertions(+), 293 deletions(-) diff --git a/changelog.md b/changelog.md index fa414fb4..b78dec50 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,7 @@ # Version 2024.9.5 (2024-09-03) - Improve device_description usage +- Reduce number of required characters for an address identification # Version 2024.9.4 (2024-09-03) diff --git a/hahomematic/caches/dynamic.py b/hahomematic/caches/dynamic.py index 67011432..8b8778fb 100644 --- a/hahomematic/caches/dynamic.py +++ b/hahomematic/caches/dynamic.py @@ -432,7 +432,7 @@ def _fire_event(mismatch_count: int) -> None: EVENT_INTERFACE_ID: self._interface_id, EVENT_TYPE: event_type, EVENT_DATA: { - EVENT_INSTANCE_NAME: self._central.config.name, + EVENT_INSTANCE_NAME: self._central.name, EVENT_PONG_MISMATCH_COUNT: mismatch_count, }, } diff --git a/hahomematic/central/__init__.py b/hahomematic/central/__init__.py index c52f3031..ca5e1be2 100644 --- a/hahomematic/central/__init__.py +++ b/hahomematic/central/__init__.py @@ -110,27 +110,20 @@ def __init__(self, central_config: CentralConfig) -> None: self._sema_add_devices: Final = asyncio.Semaphore() self._tasks: Final[set[asyncio.Future[Any]]] = set() # Keep the config for the central - self.config: Final = central_config - self._name: Final = central_config.name + self._config: Final = central_config self._model: str | None = None - self._connection_state: Final = central_config.connection_state self._looper = Looper() self._xml_rpc_server: xmlrpc.XmlRpcServer | None = None self._xml_rpc_server_ip_addr: str = IP_ANY_V4 self._xml_rpc_server_port: int = PORT_ANY + self._json_rpc_client: Final = central_config.json_rpc_client # Caches for CCU data - self.data_cache: Final[CentralDataCache] = CentralDataCache(central=self) - self.device_details: Final[DeviceDetailsCache] = DeviceDetailsCache(central=self) - self.device_descriptions: Final[DeviceDescriptionCache] = DeviceDescriptionCache( - central=self - ) - self.paramset_descriptions: Final[ParamsetDescriptionCache] = ParamsetDescriptionCache( - central=self - ) - self.parameter_visibility: Final[ParameterVisibilityCache] = ParameterVisibilityCache( - central=self - ) + self._data_cache: Final = CentralDataCache(central=self) + self._device_details: Final = DeviceDetailsCache(central=self) + self._device_descriptions: Final = DeviceDescriptionCache(central=self) + self._paramset_descriptions: Final = ParamsetDescriptionCache(central=self) + self._parameter_visibility: Final = ParameterVisibilityCache(central=self) self._primary_client: hmcl.Client | None = None # {interface_id, client} @@ -145,8 +138,6 @@ def __init__(self, central_config: CentralConfig) -> None: self._sysvar_entities: Final[dict[str, GenericSystemVariable]] = {} # {sysvar_name, program_button}U self._program_buttons: Final[dict[str, HmProgramButton]] = {} - # store last event received datetime by interface - self.last_events: Final[dict[str, datetime]] = {} # Signature: (name, *args) # e.g. DEVICES_CREATED, HUB_REFRESHED self._backend_system_callbacks: Final[set[Callable]] = set() @@ -157,13 +148,13 @@ def __init__(self, central_config: CentralConfig) -> None: # Events like INTERFACE, KEYPRESS, ... self._homematic_callbacks: Final[set[Callable]] = set() - self.json_rpc_client: Final[JsonRpcAioHttpClient] = central_config.create_json_rpc_client() - - CENTRAL_INSTANCES[self._name] = self + CENTRAL_INSTANCES[self.name] = self self._connection_checker: Final = ConnectionChecker(central=self) self._command_queue_handler: Final = CommandQueueHandler() self._hub: Hub = Hub(central=self) self._version: str | None = None + # store last event received datetime by interface + self.last_events: Final[dict[str, datetime]] = {} @property def available(self) -> bool: @@ -173,13 +164,38 @@ def available(self) -> bool: @property def central_url(self) -> str: """Return the central_orl from config.""" - return self.config.central_url + return self._config.central_url @property def clients(self) -> tuple[hmcl.Client, ...]: """Return all clients.""" return tuple(self._clients.values()) + @property + def command_queue_handler(self) -> CommandQueueHandler: + """Return the que handler for send commands.""" + return self._command_queue_handler + + @property + def config(self) -> CentralConfig: + """Return central config.""" + return self._config + + @property + def data_cache(self) -> CentralDataCache: + """Return data_cache cache.""" + return self._data_cache + + @property + def device_details(self) -> DeviceDetailsCache: + """Return device_details cache.""" + return self._device_details + + @property + def device_descriptions(self) -> DeviceDescriptionCache: + """Return device_descriptions cache.""" + return self._device_descriptions + @property def devices(self) -> tuple[HmDevice, ...]: """Return all devices.""" @@ -211,6 +227,16 @@ def is_alive(self) -> bool: """Return if XmlRPC-Server is alive.""" return all(client.is_callback_alive() for client in self._clients.values()) + @property + def paramset_descriptions(self) -> ParamsetDescriptionCache: + """Return paramset_descriptions cache.""" + return self._paramset_descriptions + + @property + def parameter_visibility(self) -> ParameterVisibilityCache: + """Return parameter_visibility cache.""" + return self._parameter_visibility + @property def primary_client(self) -> hmcl.Client | None: """Return the primary client of the backend.""" @@ -235,12 +261,12 @@ def model(self) -> str | None: @property def name(self) -> str: """Return the name of the backend.""" - return self._name + return self._config.name @property - def command_queue_handler(self) -> CommandQueueHandler: - """Return the que handler for send commands.""" - return self._command_queue_handler + def program_buttons(self) -> tuple[HmProgramButton, ...]: + """Return the program entities.""" + return tuple(self._program_buttons.values()) @property def started(self) -> bool: @@ -266,6 +292,14 @@ def sysvar_entities(self) -> tuple[GenericSystemVariable, ...]: """Return the sysvar entities.""" return tuple(self._sysvar_entities.values()) + @property + def version(self) -> str | None: + """Return the version of the backend.""" + if self._version is None: + versions = [client.version for client in self._clients.values() if client.version] + self._version = max(versions) if versions else None + return self._version + @property def xml_rpc_server_ip_addr(self) -> str: """Return the xml rpc server ip address.""" @@ -287,11 +321,6 @@ def remove_sysvar_entity(self, name: str) -> None: sysvar_entity.fire_device_removed_callback() del self._sysvar_entities[name] - @property - def program_buttons(self) -> tuple[HmProgramButton, ...]: - """Return the program entities.""" - return tuple(self._program_buttons.values()) - def add_program_button(self, program_button: HmProgramButton) -> None: """Add new program button.""" self._program_buttons[program_button.pid] = program_button @@ -302,54 +331,46 @@ def remove_program_button(self, pid: str) -> None: program_button.fire_device_removed_callback() del self._program_buttons[pid] - @property - def version(self) -> str | None: - """Return the version of the backend.""" - if self._version is None: - versions = [client.version for client in self._clients.values() if client.version] - self._version = max(versions) if versions else None - return self._version - async def save_caches( self, save_device_descriptions: bool = False, save_paramset_descriptions: bool = False ) -> None: """Save persistent caches.""" if save_device_descriptions: - await self.device_descriptions.save() + await self._device_descriptions.save() if save_paramset_descriptions: - await self.paramset_descriptions.save() + await self._paramset_descriptions.save() async def start(self) -> None: """Start processing of the central unit.""" if self._started: - _LOGGER.debug("START: Central %s already started", self._name) + _LOGGER.debug("START: Central %s already started", self.name) return - if self.config.interface_configs and ( + if self._config.interface_configs and ( ip_addr := await self._identify_ip_addr( - port=tuple(self.config.interface_configs)[0].port + port=tuple(self._config.interface_configs)[0].port ) ): self._xml_rpc_server_ip_addr = ip_addr self._xml_rpc_server = ( xmlrpc.create_xml_rpc_server( ip_addr=ip_addr, - port=self.config.callback_port or self.config.default_callback_port, + port=self._config.callback_port or self._config.default_callback_port, ) - if self.config.enable_server + if self._config.enable_server else None ) if self._xml_rpc_server: self._xml_rpc_server.add_central(self) self._xml_rpc_server_port = self._xml_rpc_server.port if self._xml_rpc_server else 0 - await self.parameter_visibility.load() - if self.config.start_direct: + await self._parameter_visibility.load() + if self._config.start_direct: if await self._create_clients(): for client in self._clients.values(): await self._refresh_device_descriptions(client=client) else: await self._start_clients() - if self.config.enable_server: + if self._config.enable_server: self._start_connection_checker() self._started = True @@ -357,13 +378,13 @@ async def start(self) -> None: async def stop(self) -> None: """Stop processing of the central unit.""" if not self._started: - _LOGGER.debug("STOP: Central %s not started", self._name) + _LOGGER.debug("STOP: Central %s not started", self.name) return await self.save_caches(save_device_descriptions=True, save_paramset_descriptions=True) self._stop_connection_checker() await self._stop_clients() - if self.json_rpc_client.is_activated: - await self.json_rpc_client.logout() + if self._json_rpc_client.is_activated: + await self._json_rpc_client.logout() if self._xml_rpc_server: # un-register this instance from XmlRPC-Server @@ -379,8 +400,8 @@ async def stop(self) -> None: ) _LOGGER.debug("STOP: Removing instance") - if self._name in CENTRAL_INSTANCES: - del CENTRAL_INSTANCES[self._name] + if self.name in CENTRAL_INSTANCES: + del CENTRAL_INSTANCES[self.name] await self._command_queue_handler.stop() @@ -464,17 +485,17 @@ async def _create_clients(self) -> bool: if len(self._clients) > 0: _LOGGER.warning( "CREATE_CLIENTS: Clients for %s are already created", - self._name, + self.name, ) return False - if len(self.config.interface_configs) == 0: + if len(self._config.interface_configs) == 0: _LOGGER.warning( "CREATE_CLIENTS failed: No Interfaces for %s defined", - self._name, + self.name, ) return False - for interface_config in self.config.interface_configs: + for interface_config in self._config.interface_configs: try: if client := await hmcl.create_client( central=self, @@ -491,7 +512,7 @@ async def _create_clients(self) -> bool: _LOGGER.debug( "CREATE_CLIENTS: Adding client %s to %s", client.interface_id, - self._name, + self.name, ) self._clients[client.interface_id] = client except BaseHomematicException as ex: @@ -509,11 +530,11 @@ async def _create_clients(self) -> bool: if self.has_clients: _LOGGER.debug( "CREATE_CLIENTS: All clients successfully created for %s", - self._name, + self.name, ) return True - _LOGGER.debug("CREATE_CLIENTS failed for %s", self._name) + _LOGGER.debug("CREATE_CLIENTS failed for %s", self.name) return False async def _init_clients(self) -> None: @@ -560,7 +581,7 @@ async def _identify_ip_addr(self, port: int) -> str: while ip_addr is None: try: ip_addr = await self.looper.async_add_executor_job( - get_ip_addr, self.config.host, port, name="get_ip_addr" + get_ip_addr, self._config.host, port, name="get_ip_addr" ) except HaHomematicException: ip_addr = "127.0.0.1" @@ -575,7 +596,7 @@ def _start_connection_checker(self) -> None: """Start the connection checker.""" _LOGGER.debug( "START_CONNECTION_CHECKER: Starting connection_checker for %s", - self._name, + self.name, ) self._connection_checker.start() @@ -584,17 +605,17 @@ def _stop_connection_checker(self) -> None: self._connection_checker.stop() _LOGGER.debug( "STOP_CONNECTION_CHECKER: Stopped connection_checker for %s", - self._name, + self.name, ) async def validate_config_and_get_system_information(self) -> SystemInformation: """Validate the central configuration.""" try: - if len(self.config.interface_configs) == 0: + if len(self._config.interface_configs) == 0: raise NoClients("validate_config: No clients defined.") system_information = SystemInformation() - for interface_config in self.config.interface_configs: + for interface_config in self._config.interface_configs: client = await hmcl.create_client(central=self, interface_config=interface_config) if not system_information.serial: system_information = client.system_information @@ -609,7 +630,7 @@ def get_client(self, interface_id: str) -> hmcl.Client: """Return a client by interface_id.""" if not self.has_client(interface_id=interface_id): raise HaHomematicException( - f"get_client: interface_id {interface_id} does not exist on {self._name}" + f"get_client: interface_id {interface_id} does not exist on {self.name}" ) return self._clients[interface_id] @@ -700,27 +721,27 @@ def has_client(self, interface_id: str) -> bool: def has_clients(self) -> bool: """Check if all configured clients exists in central.""" count_client = len(self._clients) - count_client_defined = len(self.config.interface_configs) + count_client_defined = len(self._config.interface_configs) return count_client > 0 and count_client == count_client_defined async def _load_caches(self) -> None: """Load files to caches.""" try: - await self.device_descriptions.load() - await self.paramset_descriptions.load() - await self.device_details.load() - await self.data_cache.load() + await self._device_descriptions.load() + await self._paramset_descriptions.load() + await self._device_details.load() + await self._data_cache.load() except orjson.JSONDecodeError: # pragma: no cover - _LOGGER.warning("LOAD_CACHES failed: Unable to load caches for %s", self._name) + _LOGGER.warning("LOAD_CACHES failed: Unable to load caches for %s", self.name) await self.clear_caches() async def _create_devices(self, new_device_addresses: dict[str, set[str]]) -> None: """Trigger creation of the objects that expose the functionality.""" if not self._clients: raise HaHomematicException( - f"CREATE_DEVICES failed: No clients initialized. Not starting central {self._name}." + f"CREATE_DEVICES failed: No clients initialized. Not starting central {self.name}." ) - _LOGGER.debug("CREATE_DEVICES: Starting to create devices for %s", self._name) + _LOGGER.debug("CREATE_DEVICES: Starting to create devices for %s", self.name) new_devices = set[HmDevice]() @@ -759,7 +780,7 @@ async def _create_devices(self, new_device_addresses: dict[str, set[str]]) -> No interface_id, device_address, ) - _LOGGER.debug("CREATE_DEVICES: Finished creating devices for %s", self._name) + _LOGGER.debug("CREATE_DEVICES: Finished creating devices for %s", self.name) if new_devices: new_entities = _get_new_entities(new_devices=new_devices) @@ -829,7 +850,7 @@ async def _add_new_devices( # We need this to avoid adding duplicates. known_addresses = tuple( dev_desc["ADDRESS"] - for dev_desc in self.device_descriptions.get_raw_device_descriptions( + for dev_desc in self._device_descriptions.get_raw_device_descriptions( interface_id=interface_id ) ) @@ -838,7 +859,7 @@ async def _add_new_devices( save_device_descriptions = False for dev_desc in device_descriptions: try: - self.device_descriptions.add_device_description( + self._device_descriptions.add_device_description( interface_id=interface_id, device_description=dev_desc ) save_device_descriptions = True @@ -857,15 +878,15 @@ async def _add_new_devices( save_paramset_descriptions=save_paramset_descriptions, ) if new_device_addresses := self._check_for_new_device_addresses(): - await self.device_details.load() - await self.data_cache.load() + await self._device_details.load() + await self._data_cache.load() await self._create_devices(new_device_addresses=new_device_addresses) def _check_for_new_device_addresses(self) -> dict[str, set[str]]: """Check if there are new devices, that needs to be created.""" new_device_addresses: dict[str, set[str]] = {} for interface_id in self.interface_ids: - if not self.paramset_descriptions.has_interface_id(interface_id=interface_id): + if not self._paramset_descriptions.has_interface_id(interface_id=interface_id): _LOGGER.debug( "CHECK_FOR_NEW_DEVICE_ADDRESSES: Skipping interface %s, missing paramsets", interface_id, @@ -875,7 +896,7 @@ def _check_for_new_device_addresses(self) -> dict[str, set[str]]: if interface_id not in new_device_addresses: new_device_addresses[interface_id] = set() - for device_address in self.device_descriptions.get_addresses( + for device_address in self._device_descriptions.get_addresses( interface_id=interface_id ): if device_address not in self._devices: @@ -960,7 +981,7 @@ async def event( @callback_backend_system(system_event=BackendSystemEvent.LIST_DEVICES) def list_devices(self, interface_id: str) -> list[DeviceDescription]: """Return already existing devices to CCU / Homegear.""" - result = self.device_descriptions.get_raw_device_descriptions(interface_id=interface_id) + result = self._device_descriptions.get_raw_device_descriptions(interface_id=interface_id) _LOGGER.debug( "LIST_DEVICES: interface_id = %s, channel_count = %i", interface_id, len(result) ) @@ -983,9 +1004,9 @@ def remove_device(self, device: HmDevice) -> None: return device.clear_collections() - self.device_descriptions.remove_device(device=device) - self.paramset_descriptions.remove_device(device=device) - self.device_details.remove_device(device=device) + self._device_descriptions.remove_device(device=device) + self._paramset_descriptions.remove_device(device=device) + self._device_details.remove_device(device=device) del self._devices[device.device_address] def remove_event_subscription(self, entity: BaseParameterEntity) -> None: @@ -1014,9 +1035,9 @@ async def fetch_program_data(self, include_internal: bool = False) -> None: @measure_execution_time async def load_and_refresh_entity_data(self, paramset_key: ParamsetKey | None = None) -> None: """Refresh entity data.""" - if paramset_key != ParamsetKey.MASTER and self.data_cache.is_empty: - await self.data_cache.load() - await self.data_cache.refresh_entity_data(paramset_key=paramset_key) + if paramset_key != ParamsetKey.MASTER and self._data_cache.is_empty: + await self._data_cache.load() + await self._data_cache.refresh_entity_data(paramset_key=paramset_key) async def get_system_variable(self, name: str) -> Any | None: """Get system variable from CCU / Homegear.""" @@ -1044,7 +1065,7 @@ async def set_install_mode( _LOGGER.warning( "SET_INSTALL_MODE: interface_id %s does not exist on %s", interface_id, - self._name, + self.name, ) return False return await self.get_client(interface_id=interface_id).set_install_mode( @@ -1061,11 +1082,11 @@ def get_parameters( ) -> list[str]: """Return all parameters from VALUES paramset.""" parameters: set[str] = set() - for channels in self.paramset_descriptions.raw_paramset_descriptions.values(): + for channels in self._paramset_descriptions.raw_paramset_descriptions.values(): for channel_address in channels: device_type: str | None = None if full_format: - device_type = self.device_descriptions.get_device_type( + device_type = self._device_descriptions.get_device_type( device_address=get_device_address(channel_address) ) for parameter, parameter_data in ( @@ -1187,10 +1208,10 @@ def get_un_ignore_candidates(self, include_master: bool = False) -> list[str]: async def clear_caches(self) -> None: """Clear all stored data.""" - await self.device_descriptions.clear() - await self.paramset_descriptions.clear() - self.device_details.clear() - self.data_cache.clear() + await self._device_descriptions.clear() + await self._paramset_descriptions.clear() + self._device_details.clear() + self._data_cache.clear() def register_homematic_callback(self, cb: Callable) -> CALLBACK_TYPE: """Register ha_event callback in central.""" @@ -1395,6 +1416,7 @@ def __init__( self.json_port: Final = json_port self.un_ignore_list: Final = un_ignore_list self.start_direct: Final = start_direct + self._json_rpc_client: JsonRpcAioHttpClient | None = None @property def central_url(self) -> str: @@ -1422,6 +1444,21 @@ def use_caches(self) -> bool: """Return if caches should be used.""" return self.start_direct is False + @property + def json_rpc_client(self) -> JsonRpcAioHttpClient: + """Return the json rpx client.""" + if not self._json_rpc_client: + self._json_rpc_client = JsonRpcAioHttpClient( + username=self.username, + password=self.password, + device_url=self.central_url, + connection_state=self.connection_state, + client_session=self.client_session, + tls=self.tls, + verify_tls=self.verify_tls, + ) + return self._json_rpc_client + def check_config(self) -> None: """Check config. Throws BaseHomematicException on failure.""" if config_failures := check_config( @@ -1446,18 +1483,6 @@ def create_central(self) -> CentralUnit: _LOGGER.warning("CREATE_CENTRAL: Not able to create a central: %s", bhex) raise - def create_json_rpc_client(self) -> JsonRpcAioHttpClient: - """Return the json rpc client.""" - return JsonRpcAioHttpClient( - username=self.username, - password=self.password, - device_url=self.central_url, - connection_state=self.connection_state, - client_session=self.client_session, - tls=self.tls, - verify_tls=self.verify_tls, - ) - class CentralConnectionState: """The central connection status.""" diff --git a/hahomematic/client/__init__.py b/hahomematic/client/__init__.py index 4e9ed787..4dad94e3 100644 --- a/hahomematic/client/__init__.py +++ b/hahomematic/client/__init__.py @@ -68,29 +68,22 @@ class Client(ABC): def __init__(self, client_config: _ClientConfig) -> None: """Initialize the Client.""" self._config: Final = client_config - self.central: Final[hmcu.CentralUnit] = client_config.central self._last_value_send_cache = CommandCache(interface_id=client_config.interface_id) - - self._json_rpc_client: Final = client_config.central.json_rpc_client - - self.interface: Final[str] = client_config.interface - self.interface_id: Final[str] = client_config.interface_id - self.version: Final[str] = client_config.version + self._json_rpc_client: Final = client_config.central.config.json_rpc_client self._available: bool = True self._connection_error_count: int = 0 self._is_callback_alive: bool = True - self.modified_at: datetime = INIT_DATETIME self._ping_pong_cache: Final = PingPongCache( central=client_config.central, interface_id=client_config.interface_id ) - self._proxy: XmlRpcProxy self._proxy_read: XmlRpcProxy - self.system_information: SystemInformation + self._system_information: SystemInformation + self.modified_at: datetime = INIT_DATETIME async def init_client(self) -> None: """Init the client.""" - self.system_information = await self._get_system_information() + self._system_information = await self._get_system_information() self._proxy = await self._config.get_xml_rpc_proxy( auth_enabled=self.system_information.auth_enabled ) @@ -103,6 +96,21 @@ def available(self) -> bool: """Return the availability of the client.""" return self._available + @property + def central(self) -> hmcu.CentralUnit: + """Return the central of the client.""" + return self._config.central + + @property + def interface(self) -> str: + """Return the interface of the client.""" + return self._config.interface + + @property + def interface_id(self) -> str: + """Return the interface id of the client.""" + return self._config.interface_id + @property def last_value_send_cache(self) -> CommandCache: """Return the last value send cache.""" @@ -118,6 +126,16 @@ def ping_pong_cache(self) -> PingPongCache: """Return the ping pong cache.""" return self._ping_pong_cache + @property + def system_information(self) -> SystemInformation: + """Return the system_information of the client.""" + return self._system_information + + @property + def version(self) -> str: + """Return the version id of the client.""" + return self._config.version + def get_product_group(self, device_type: str) -> ProductGroup: """Return the product group.""" if self.interface == InterfaceName.HMIP_RF: @@ -1113,20 +1131,14 @@ def __init__( self.interface_config: Final = interface_config self.interface: Final = interface_config.interface self.interface_id: Final = interface_config.interface_id - self._callback_host: Final[str] = ( - central.config.callback_host - if central.config.callback_host - else central.xml_rpc_server_ip_addr - ) - self._callback_port: Final[int] = ( - central.config.callback_port - if central.config.callback_port - else central.xml_rpc_server_port - ) self.has_credentials: Final[bool] = ( central.config.username is not None and central.config.password is not None ) - self.init_url: Final[str] = f"http://{self._callback_host}:{self._callback_port}" + self.init_url: Final[str] = f"http://{central.config.callback_host + if central.config.callback_host + else central.xml_rpc_server_ip_addr}:{central.config.callback_port + if central.config.callback_port + else central.xml_rpc_server_port}" self.xml_rpc_uri: Final = build_xml_rpc_uri( host=central.config.host, port=interface_config.port, diff --git a/hahomematic/const.py b/hahomematic/const.py index 7f341586..dad16e3f 100644 --- a/hahomematic/const.py +++ b/hahomematic/const.py @@ -37,8 +37,8 @@ # The CCU WebUI also supports ÄäÖöÜüß, but these characters are not supported by the XmlRPC servers CCU_PASSWORD_PATTERN: Final = re.compile(r"[A-Za-z0-9.!$():;#-]{0,}") # Pattern is bigger than needed -CHANNEL_ADDRESS_PATTERN: Final = re.compile(r"^[0-9a-zA-Z]{10,20}:[0-9]{1,3}$") -DEVICE_ADDRESS_PATTERN: Final = re.compile(r"^[0-9a-zA-Z]{10,20}$") +CHANNEL_ADDRESS_PATTERN: Final = re.compile(r"^[0-9a-zA-Z-]{5,20}:[0-9]{1,3}$") +DEVICE_ADDRESS_PATTERN: Final = re.compile(r"^[0-9a-zA-Z-]{5,20}$") ALLOWED_HOSTNAME_PATTERN: Final = re.compile(r"(?!-)[a-z0-9-]{1,63}(?|&([a-z0-9]+|#[0-9]{1,6}|#x[0-9a-f]{1,6});") diff --git a/hahomematic/platforms/device.py b/hahomematic/platforms/device.py index 068b191f..9c2dd00b 100644 --- a/hahomematic/platforms/device.py +++ b/hahomematic/platforms/device.py @@ -71,7 +71,7 @@ def __init__(self, central: hmcu.CentralUnit, interface_id: str, device_address: self._sub_device_channels: Final[dict[int, int]] = {} self._central: Final = central self._interface_id: Final = interface_id - self._interface: Final = central.device_details.get_interface(device_address) + self._interface: Final = central.device_details.get_interface(address=device_address) self._client: Final = central.get_client(interface_id=interface_id) self._device_address: Final = device_address self._device_description = self._get_device_description( @@ -114,7 +114,7 @@ def __init__(self, central: hmcu.CentralUnit, interface_id: str, device_address: device_address=device_address, device_type=self._device_type, ) - self.value_cache: Final = ValueCache(device=self) + self._value_cache: Final[ValueCache] = ValueCache(device=self) self._rooms: Final = central.device_details.get_device_rooms(device_address=device_address) self._update_entity: Final = HmUpdate(device=self) if self.is_updatable else None # pylint: disable=using-constant-test _LOGGER.debug( @@ -223,6 +223,11 @@ def generic_entities(self) -> tuple[GenericEntity, ...]: """Return the generic entities.""" return tuple(self._generic_entities.values()) + @property + def has_custom_entity_definition(self) -> bool: + """Return if custom_entity definition is available for the device.""" + return self._has_custom_entity_definition + @config_property def has_sub_devices(self) -> bool: """Return if device has multiple sub device channels.""" @@ -248,10 +253,10 @@ def ignore_for_custom_entity(self) -> bool: """Return if device should be ignored for custom entity.""" return self._ignore_for_custom_entity - @property - def has_custom_entity_definition(self) -> bool: - """Return if custom_entity definition is available for the device.""" - return self._has_custom_entity_definition + @config_property + def is_updatable(self) -> bool: + """Return if the device is updatable.""" + return self._is_updatable @config_property def manufacturer(self) -> str: @@ -290,16 +295,16 @@ def sub_type(self) -> str | None: """Return the sub_type of the device.""" return self._sub_type - @config_property - def is_updatable(self) -> bool: - """Return if the device is updatable.""" - return self._is_updatable - @property def update_entity(self) -> HmUpdate | None: """Return the device firmware update entity of the device.""" return self._update_entity + @property + def value_cache(self) -> ValueCache: + """Return the value_cache of the device.""" + return self._value_cache + @property def _e_unreach(self) -> GenericEntity | None: """Return th UNREACH entity.""" @@ -570,9 +575,9 @@ async def refresh_data() -> None: async def load_value_cache(self) -> None: """Init the parameter cache.""" if len(self._generic_entities) > 0: - await self.value_cache.init_base_entities() + await self._value_cache.init_base_entities() if len(self._generic_events) > 0: - await self.value_cache.init_readable_events() + await self._value_cache.init_readable_events() _LOGGER.debug( "INIT_DATA: Skipping load_data, missing entities for %s", self._device_address, diff --git a/hahomematic/platforms/entity.py b/hahomematic/platforms/entity.py index 6468d8de..be15f8dc 100644 --- a/hahomematic/platforms/entity.py +++ b/hahomematic/platforms/entity.py @@ -293,9 +293,7 @@ def __init__( self._function: Final = self._central.device_details.get_function_text( address=self._channel_address ) - self._client: Final[hmcl.Client] = device.central.get_client( - interface_id=device.interface_id - ) + self._client: Final[hmcl.Client] = device.client self._forced_usage: EntityUsage | None = None self._entity_name_data: Final = self._get_entity_name() diff --git a/hahomematic_support/client_local.py b/hahomematic_support/client_local.py index 34adc964..4ed5c849 100644 --- a/hahomematic_support/client_local.py +++ b/hahomematic_support/client_local.py @@ -45,7 +45,7 @@ def __init__(self, client_config: _ClientConfig, local_resources: LocalRessource async def init_client(self) -> None: """Init the client.""" - self.system_information = await self._get_system_information() + self._system_information = await self._get_system_information() @property def available(self) -> bool: diff --git a/tests/helper.py b/tests/helper.py index 8e46adf4..54c05dbf 100644 --- a/tests/helper.py +++ b/tests/helper.py @@ -6,7 +6,7 @@ import importlib.resources import logging import os -from typing import Any +from typing import Any, Final from unittest.mock import MagicMock, Mock, patch from aiohttp import ClientSession @@ -17,17 +17,19 @@ from hahomematic.client import Client, InterfaceConfig, _ClientConfig from hahomematic.const import BackendSystemEvent, InterfaceName from hahomematic.platforms.custom.entity import CustomEntity +from hahomematic.platforms.decorators import _get_public_attributes_by_decorator from hahomematic_support.client_local import ClientLocal, LocalRessources from tests import const _LOGGER = logging.getLogger(__name__) +EXCLUDE_METHODS_FROM_MOCKS: Final = [] +INCLUDE_PROPERTIES_IN_MOCKS: Final = [] GOT_DEVICES = False -# pylint: disable=protected-access - +# pylint: disable=protected-access class Factory: """Factory for a central with one local client.""" @@ -113,7 +115,7 @@ async def get_default_central( """Return a central based on give address_device_translation.""" central, client = await self.get_unpatched_default_central( address_device_translation=address_device_translation, - do_mock_client=do_mock_client, + do_mock_client=True, ignore_devices_on_create=ignore_devices_on_create, un_ignore_list=un_ignore_list, ) @@ -162,15 +164,34 @@ def load_device_description(central: CentralUnit, filename: str) -> Any: return dev_desc -def get_mock(instance, **kwargs): +def get_mock(instance: Any, **kwargs): """Create a mock and copy instance attributes over mock.""" if isinstance(instance, Mock): instance.__dict__.update(instance._mock_wraps.__dict__) return instance - mock = MagicMock(spec=instance, wraps=instance, **kwargs) mock.__dict__.update(instance.__dict__) - return mock + try: + for method_name in [ + prop + for prop in _get_not_mockable_method_names(instance) + if prop not in INCLUDE_PROPERTIES_IN_MOCKS and prop not in kwargs + ]: + setattr(mock, method_name, getattr(instance, method_name)) + except Exception: + pass + finally: + return mock + + +def _get_not_mockable_method_names(instance: Any) -> set[str]: + """Return all relevant method names for mocking.""" + methods: set[str] = set(_get_public_attributes_by_decorator(instance, property)) + + for method in dir(instance): + if method in EXCLUDE_METHODS_FROM_MOCKS: + methods.add(method) + return methods def _load_json_file(anchor: str, resource: str, filename: str) -> Any | None: diff --git a/tests/test_climate.py b/tests/test_climate.py index df6618c4..0cdc7f2d 100644 --- a/tests/test_climate.py +++ b/tests/test_climate.py @@ -82,7 +82,7 @@ async def test_cesimplerfthermostat( value=12.0, wait_for_callback=WAIT_FOR_CALLBACK, ) - assert mock_client.method_calls[-2] == last_call + assert mock_client.method_calls[-1] == last_call assert climate.target_temperature == 12.0 assert climate.current_temperature is None @@ -98,17 +98,17 @@ async def test_cesimplerfthermostat( # No new method call, because called methods has no implementation await climate.set_hvac_mode(HvacMode.HEAT) - assert mock_client.method_calls[-4] == last_call + assert mock_client.method_calls[-1] == last_call await climate.set_preset_mode(PresetMode.NONE) - assert mock_client.method_calls[-4] == last_call + assert mock_client.method_calls[-1] == last_call await climate.enable_away_mode_by_duration(hours=100, away_temperature=17.0) - assert mock_client.method_calls[-4] == last_call + assert mock_client.method_calls[-1] == last_call await climate.enable_away_mode_by_calendar( start=datetime.now(), end=datetime.now(), away_temperature=17.0 ) - assert mock_client.method_calls[-4] == last_call + assert mock_client.method_calls[-1] == last_call await climate.disable_away_mode() - assert mock_client.method_calls[-4] == last_call + assert mock_client.method_calls[-1] == last_call @pytest.mark.asyncio() @@ -147,7 +147,7 @@ async def test_cerfthermostat( assert climate.current_humidity is None assert climate.target_temperature is None await climate.set_temperature(12.0) - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU0000050:4", paramset_key="VALUES", parameter="SET_TEMPERATURE", @@ -174,7 +174,7 @@ async def test_cerfthermostat( assert climate.hvac_mode == HvacMode.HEAT await climate.set_hvac_mode(HvacMode.OFF) - assert mock_client.method_calls[-2] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU0000050:4", paramset_key="VALUES", values={"MANU_MODE": 12.0, "SET_TEMPERATURE": 4.5}, @@ -311,7 +311,7 @@ async def test_ceipthermostat( assert climate.target_temperature is None await climate.set_temperature(12.0) - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU1769958:1", paramset_key="VALUES", parameter="SET_POINT_TEMPERATURE", @@ -329,7 +329,7 @@ async def test_ceipthermostat( assert climate.preset_mode == PresetMode.NONE await climate.set_hvac_mode(HvacMode.OFF) - assert mock_client.method_calls[-2] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU1769958:1", paramset_key="VALUES", values={"CONTROL_MODE": 1, "SET_POINT_TEMPERATURE": 4.5}, @@ -339,7 +339,7 @@ async def test_ceipthermostat( assert climate.hvac_action == HvacAction.OFF await climate.set_hvac_mode(HvacMode.HEAT) - assert mock_client.method_calls[-2] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU1769958:1", paramset_key="VALUES", values={"CONTROL_MODE": 1, "SET_POINT_TEMPERATURE": 5.0}, @@ -354,7 +354,7 @@ async def test_ceipthermostat( PresetMode.NONE, ) await climate.set_preset_mode(PresetMode.BOOST) - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU1769958:1", paramset_key="VALUES", parameter="BOOST_MODE", @@ -365,7 +365,7 @@ async def test_ceipthermostat( assert climate.preset_mode == PresetMode.BOOST await climate.set_hvac_mode(HvacMode.AUTO) - assert mock_client.method_calls[-2] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU1769958:1", paramset_key="VALUES", values={"BOOST_MODE": False, "CONTROL_MODE": 0}, @@ -385,7 +385,7 @@ async def test_ceipthermostat( "week_program_6", ) await climate.set_preset_mode(PresetMode.NONE) - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU1769958:1", paramset_key="VALUES", parameter="BOOST_MODE", @@ -397,7 +397,7 @@ async def test_ceipthermostat( await central.event(const.INTERFACE_ID, "VCU1769958:1", "SET_POINT_MODE", ModeHmIP.AUTO.value) await climate.set_preset_mode(PresetMode.WEEK_PROGRAM_1) - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU1769958:1", paramset_key="VALUES", parameter="ACTIVE_PROFILE", @@ -408,7 +408,7 @@ async def test_ceipthermostat( with freeze_time("2023-03-03 08:00:00"): await climate.enable_away_mode_by_duration(hours=100, away_temperature=17.0) - assert mock_client.method_calls[-3] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU1769958:1", paramset_key="VALUES", values={ @@ -422,7 +422,7 @@ async def test_ceipthermostat( await climate.enable_away_mode_by_calendar( start=datetime(2000, 12, 1), end=datetime(2024, 12, 1), away_temperature=17.0 ) - assert mock_client.method_calls[-3] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU1769958:1", paramset_key="VALUES", values={ @@ -434,7 +434,7 @@ async def test_ceipthermostat( ) await climate.disable_away_mode() - assert mock_client.method_calls[-2] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU1769958:1", paramset_key="VALUES", values={ diff --git a/tests/test_cover.py b/tests/test_cover.py index 6259a4de..72c4617a 100644 --- a/tests/test_cover.py +++ b/tests/test_cover.py @@ -68,7 +68,7 @@ async def test_cecover( assert cover._channel_level == _CLOSED_LEVEL assert cover.is_closed is True await cover.set_position(position=81) - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU8537918:4", paramset_key="VALUES", parameter="LEVEL", @@ -78,7 +78,7 @@ async def test_cecover( assert cover.current_position == 81 assert cover.is_closed is False await cover.open() - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU8537918:4", paramset_key="VALUES", parameter="LEVEL", @@ -87,7 +87,7 @@ async def test_cecover( ) assert cover.current_position == 100 await cover.close() - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU8537918:4", paramset_key="VALUES", parameter="LEVEL", @@ -238,7 +238,7 @@ async def test_cewindowdrive( assert cover._channel_level == _WD_CLOSED_LEVEL assert cover.is_closed is True await cover.set_position(position=81) - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU0000350:1", paramset_key="VALUES", parameter="LEVEL", @@ -249,7 +249,7 @@ async def test_cewindowdrive( assert cover.is_closed is False await cover.open() - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU0000350:1", paramset_key="VALUES", parameter="LEVEL", @@ -258,7 +258,7 @@ async def test_cewindowdrive( ) assert cover.current_position == 100 await cover.close() - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU0000350:1", paramset_key="VALUES", parameter="LEVEL", @@ -413,14 +413,14 @@ async def test_ceblind( call_count = len(mock_client.method_calls) await cover.open_tilt() await central.event(const.INTERFACE_ID, "VCU0000144:1", "LEVEL_SLATS", _OPEN_TILT_LEVEL) - assert call_count == len(mock_client.method_calls) - 1 + assert call_count == len(mock_client.method_calls) await cover.close_tilt() await central.event(const.INTERFACE_ID, "VCU0000144:1", "LEVEL_SLATS", _CLOSED_LEVEL) call_count = len(mock_client.method_calls) await cover.close_tilt() await central.event(const.INTERFACE_ID, "VCU0000144:1", "LEVEL_SLATS", _CLOSED_LEVEL) - assert call_count == len(mock_client.method_calls) - 1 + assert call_count == len(mock_client.method_calls) await central.event(const.INTERFACE_ID, "VCU0000144:1", "LEVEL_SLATS", 0.4) call_count = len(mock_client.method_calls) @@ -656,7 +656,7 @@ async def test_ceipblind_hdm( assert cover.current_position == 0 assert cover.current_tilt_position == 0 await cover.set_position(position=81) - assert mock_client.method_calls[-3] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU3560967:1", paramset_key="VALUES", values={"LEVEL_2": _CLOSED_LEVEL, "LEVEL": 0.81}, @@ -667,7 +667,7 @@ async def test_ceipblind_hdm( assert cover.current_tilt_position == 0 await cover.open() - assert mock_client.method_calls[-3] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU3560967:1", paramset_key="VALUES", values={"LEVEL_2": _OPEN_TILT_LEVEL, "LEVEL": _OPEN_LEVEL}, @@ -679,7 +679,7 @@ async def test_ceipblind_hdm( assert cover.current_tilt_position == 100 await cover.close() - assert mock_client.method_calls[-3] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU3560967:1", paramset_key="VALUES", values={"LEVEL_2": _CLOSED_LEVEL, "LEVEL": _CLOSED_LEVEL}, @@ -691,7 +691,7 @@ async def test_ceipblind_hdm( assert cover.current_tilt_position == 0 await cover.open_tilt() - assert mock_client.method_calls[-3] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU3560967:1", paramset_key="VALUES", values={"LEVEL_2": _OPEN_TILT_LEVEL, "LEVEL": _CLOSED_LEVEL}, @@ -702,7 +702,7 @@ async def test_ceipblind_hdm( assert cover.current_tilt_position == 100 await cover.set_position(tilt_position=45) - assert mock_client.method_calls[-3] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU3560967:1", paramset_key="VALUES", values={"LEVEL_2": 0.45, "LEVEL": _CLOSED_LEVEL}, @@ -713,7 +713,7 @@ async def test_ceipblind_hdm( assert cover.current_tilt_position == 45 await cover.close_tilt() - assert mock_client.method_calls[-3] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU3560967:1", paramset_key="VALUES", values={"LEVEL_2": _CLOSED_LEVEL, "LEVEL": _CLOSED_LEVEL}, @@ -725,7 +725,7 @@ async def test_ceipblind_hdm( assert cover.current_tilt_position == 0 await cover.set_position(position=10, tilt_position=20) - assert mock_client.method_calls[-3] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU3560967:1", paramset_key="VALUES", values={"LEVEL_2": 0.2, "LEVEL": 0.1}, diff --git a/tests/test_light.py b/tests/test_light.py index 71faaa1d..8679e8fa 100644 --- a/tests/test_light.py +++ b/tests/test_light.py @@ -74,7 +74,7 @@ async def test_cedimmer( assert light.brightness == 0 assert light.brightness_pct == 0 await light.turn_on() - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU1399816:4", paramset_key="VALUES", parameter="LEVEL", @@ -84,7 +84,7 @@ async def test_cedimmer( assert light.brightness == 255 assert light.brightness_pct == 100 await light.turn_on(brightness=28) - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU1399816:4", paramset_key="VALUES", parameter="LEVEL", @@ -100,14 +100,14 @@ async def test_cedimmer( assert light.channel_brightness == 102 await light.turn_on(on_time=5.0, ramp_time=6.0) - assert mock_client.method_calls[-2] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU1399816:4", paramset_key="VALUES", values={"LEVEL": 0.10980392156862745, "RAMP_TIME": 6.0, "ON_TIME": 5.0}, wait_for_callback=WAIT_FOR_CALLBACK, ) await light.turn_on(on_time=5.0) - assert mock_client.method_calls[-2] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU1399816:4", paramset_key="VALUES", values={"ON_TIME": 5.0, "LEVEL": 0.10980392156862745}, @@ -115,7 +115,7 @@ async def test_cedimmer( ) await light.turn_off(ramp_time=6.0) - assert mock_client.method_calls[-2] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU1399816:4", paramset_key="VALUES", values={"RAMP_TIME": 6.0, "LEVEL": 0.0}, @@ -125,7 +125,7 @@ async def test_cedimmer( await light.turn_on() assert light.brightness == 255 await light.turn_off() - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU1399816:4", paramset_key="VALUES", parameter="LEVEL", @@ -136,7 +136,7 @@ async def test_cedimmer( await light.turn_off() light.set_on_time(0.5) await light.turn_on() - assert mock_client.method_calls[-2] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU1399816:4", paramset_key="VALUES", values={"ON_TIME": 0.5, "LEVEL": 1.0}, @@ -197,7 +197,7 @@ async def test_cecolordimmereffect( assert light.brightness == 0 await light.turn_on() - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU3747418:1", paramset_key="VALUES", parameter="LEVEL", @@ -206,7 +206,7 @@ async def test_cecolordimmereffect( ) assert light.brightness == 255 await light.turn_on(brightness=28) - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU3747418:1", paramset_key="VALUES", parameter="LEVEL", @@ -215,7 +215,7 @@ async def test_cecolordimmereffect( ) assert light.brightness == 28 await light.turn_off() - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU3747418:1", paramset_key="VALUES", parameter="LEVEL", @@ -226,14 +226,14 @@ async def test_cecolordimmereffect( assert light.hs_color == (0.0, 0.0) await light.turn_on(hs_color=(44.4, 69.3)) - assert mock_client.method_calls[-4] == call.set_value( + assert mock_client.method_calls[-2] == call.set_value( channel_address="VCU3747418:2", paramset_key="VALUES", parameter="COLOR", value=25, wait_for_callback=WAIT_FOR_CALLBACK, ) - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU3747418:1", paramset_key="VALUES", parameter="LEVEL", @@ -243,14 +243,14 @@ async def test_cecolordimmereffect( assert light.hs_color == (45.0, 100) await light.turn_on(hs_color=(0, 50)) - assert mock_client.method_calls[-4] == call.set_value( + assert mock_client.method_calls[-2] == call.set_value( channel_address="VCU3747418:2", paramset_key="VALUES", parameter="COLOR", value=0, wait_for_callback=WAIT_FOR_CALLBACK, ) - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU3747418:1", paramset_key="VALUES", parameter="LEVEL", @@ -261,14 +261,14 @@ async def test_cecolordimmereffect( await light.turn_on(effect="Slow color change") - assert mock_client.method_calls[-4] == call.set_value( + assert mock_client.method_calls[-2] == call.set_value( channel_address="VCU3747418:1", paramset_key="VALUES", parameter="LEVEL", value=1.0, wait_for_callback=WAIT_FOR_CALLBACK, ) - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU3747418:3", paramset_key="VALUES", parameter="PROGRAM", @@ -294,14 +294,14 @@ async def test_cecolordimmereffect( assert call_count == len(mock_client.method_calls) await light.turn_on(brightness=28, effect="Slow color change") - assert mock_client.method_calls[-4] == call.set_value( + assert mock_client.method_calls[-2] == call.set_value( channel_address="VCU3747418:1", paramset_key="VALUES", parameter="LEVEL", value=0.10980392156862745, wait_for_callback=WAIT_FOR_CALLBACK, ) - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU3747418:3", paramset_key="VALUES", parameter="PROGRAM", @@ -344,7 +344,7 @@ async def test_cecolortempdimmer( assert light.effects is None assert light.brightness == 0 await light.turn_on() - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU0000115:1", paramset_key="VALUES", parameter="LEVEL", @@ -353,7 +353,7 @@ async def test_cecolortempdimmer( ) assert light.brightness == 255 await light.turn_on(brightness=28) - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU0000115:1", paramset_key="VALUES", parameter="LEVEL", @@ -362,7 +362,7 @@ async def test_cecolortempdimmer( ) assert light.brightness == 28 await light.turn_off() - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU0000115:1", paramset_key="VALUES", parameter="LEVEL", @@ -373,14 +373,14 @@ async def test_cecolortempdimmer( assert light.color_temp == 500 await light.turn_on(color_temp=433) - assert mock_client.method_calls[-4] == call.set_value( + assert mock_client.method_calls[-2] == call.set_value( channel_address="VCU0000115:2", paramset_key="VALUES", parameter="LEVEL", value=0.1930835734870317, wait_for_callback=WAIT_FOR_CALLBACK, ) - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU0000115:1", paramset_key="VALUES", parameter="LEVEL", @@ -439,7 +439,7 @@ async def test_ceipfixedcolorlight( assert light.channel_brightness is None assert light.channel_hs_color is None await light.turn_on() - assert mock_client.method_calls[-3] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU3716619:8", paramset_key="VALUES", values={"COLOR": 7, "LEVEL": 1.0}, @@ -447,7 +447,7 @@ async def test_ceipfixedcolorlight( ) assert light.brightness == 255 await light.turn_on(brightness=28) - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU3716619:8", paramset_key="VALUES", parameter="LEVEL", @@ -456,7 +456,7 @@ async def test_ceipfixedcolorlight( ) assert light.brightness == 28 await light.turn_off() - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU3716619:8", paramset_key="VALUES", parameter="LEVEL", @@ -467,7 +467,7 @@ async def test_ceipfixedcolorlight( assert light.color_name == FixedColor.WHITE await light.turn_on(hs_color=(350, 50)) - assert mock_client.method_calls[-3] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU3716619:8", paramset_key="VALUES", values={"COLOR": 4, "LEVEL": 1.0}, @@ -476,7 +476,7 @@ async def test_ceipfixedcolorlight( assert light.color_name == FixedColor.RED await light.turn_on(hs_color=(0.0, 0.0)) - assert mock_client.method_calls[-3] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU3716619:8", paramset_key="VALUES", values={"COLOR": 7, "LEVEL": 1.0}, @@ -485,7 +485,7 @@ async def test_ceipfixedcolorlight( assert light.color_name == FixedColor.WHITE await light.turn_on(hs_color=(60.0, 50.0)) - assert mock_client.method_calls[-3] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU3716619:8", paramset_key="VALUES", values={"COLOR": 6, "LEVEL": 1.0}, @@ -494,7 +494,7 @@ async def test_ceipfixedcolorlight( assert light.color_name == FixedColor.YELLOW await light.turn_on(hs_color=(120, 50)) - assert mock_client.method_calls[-3] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU3716619:8", paramset_key="VALUES", values={"COLOR": 2, "LEVEL": 1.0}, @@ -503,7 +503,7 @@ async def test_ceipfixedcolorlight( assert light.color_name == FixedColor.GREEN await light.turn_on(hs_color=(180, 50)) - assert mock_client.method_calls[-3] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU3716619:8", paramset_key="VALUES", values={"COLOR": 3, "LEVEL": 1.0}, @@ -512,7 +512,7 @@ async def test_ceipfixedcolorlight( assert light.color_name == FixedColor.TURQUOISE await light.turn_on(hs_color=(240, 50)) - assert mock_client.method_calls[-3] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU3716619:8", paramset_key="VALUES", values={"COLOR": 1, "LEVEL": 1.0}, @@ -521,7 +521,7 @@ async def test_ceipfixedcolorlight( assert light.color_name == FixedColor.BLUE await light.turn_on(hs_color=(300, 50)) - assert mock_client.method_calls[-3] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU3716619:8", paramset_key="VALUES", values={"COLOR": 5, "LEVEL": 1.0}, @@ -539,7 +539,7 @@ async def test_ceipfixedcolorlight( await light.turn_off() light.set_on_time(18) await light.turn_on() - assert mock_client.method_calls[-2] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU3716619:8", paramset_key="VALUES", values={"DURATION_VALUE": 18, "LEVEL": 1.0}, @@ -549,7 +549,7 @@ async def test_ceipfixedcolorlight( await light.turn_off() light.set_on_time(17000) await light.turn_on() - assert mock_client.method_calls[-2] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU3716619:8", paramset_key="VALUES", values={"DURATION_UNIT": 1, "DURATION_VALUE": 283, "LEVEL": 1.0}, @@ -559,14 +559,14 @@ async def test_ceipfixedcolorlight( await light.turn_off() light.set_on_time(1000000) await light.turn_on() - assert mock_client.method_calls[-2] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU3716619:8", paramset_key="VALUES", values={"DURATION_UNIT": 2, "DURATION_VALUE": 277, "LEVEL": 1.0}, wait_for_callback=WAIT_FOR_CALLBACK, ) await light.turn_on(ramp_time=18) - assert mock_client.method_calls[-2] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU3716619:8", paramset_key="VALUES", values={"RAMP_TIME_VALUE": 18, "LEVEL": 1.0}, @@ -574,7 +574,7 @@ async def test_ceipfixedcolorlight( ) await light.turn_on(ramp_time=17000) - assert mock_client.method_calls[-2] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU3716619:8", paramset_key="VALUES", values={"RAMP_TIME_UNIT": 1, "RAMP_TIME_VALUE": 283, "LEVEL": 1.0}, @@ -582,7 +582,7 @@ async def test_ceipfixedcolorlight( ) await light.turn_on(ramp_time=1000000) - assert mock_client.method_calls[-2] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU3716619:8", paramset_key="VALUES", values={"RAMP_TIME_UNIT": 2, "RAMP_TIME_VALUE": 277, "LEVEL": 1.0}, @@ -647,7 +647,7 @@ async def test_ceipfixedcolorlightwired( assert light.is_on is False assert light.color_name == FixedColor.BLACK await light.turn_on() - assert mock_client.method_calls[-4] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU4704397:8", paramset_key="VALUES", values={"COLOR_BEHAVIOUR": 1, "COLOR": 7, "LEVEL": 1.0}, @@ -657,7 +657,7 @@ async def test_ceipfixedcolorlightwired( assert light.color_name == FixedColor.WHITE await light.turn_on(brightness=100) - assert mock_client.method_calls[-3] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU4704397:8", paramset_key="VALUES", values={"COLOR_BEHAVIOUR": 1, "LEVEL": 0.39215686274509803}, @@ -668,7 +668,7 @@ async def test_ceipfixedcolorlightwired( assert light.effect == ColorBehaviour.ON await light.turn_off() - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU4704397:8", paramset_key="VALUES", parameter="LEVEL", @@ -680,7 +680,7 @@ async def test_ceipfixedcolorlightwired( assert light.effect == ColorBehaviour.ON await light.turn_on(hs_color=(350, 50)) - assert mock_client.method_calls[-4] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU4704397:8", paramset_key="VALUES", values={"COLOR_BEHAVIOUR": 1, "COLOR": 4, "LEVEL": 1.0}, @@ -691,7 +691,7 @@ async def test_ceipfixedcolorlightwired( assert light.effect == ColorBehaviour.ON await light.turn_on(hs_color=(0.0, 0.0)) - assert mock_client.method_calls[-4] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU4704397:8", paramset_key="VALUES", values={"COLOR_BEHAVIOUR": 1, "COLOR": 7, "LEVEL": 1.0}, @@ -702,7 +702,7 @@ async def test_ceipfixedcolorlightwired( assert light.effect == ColorBehaviour.ON await light.turn_on(hs_color=(60.0, 50.0)) - assert mock_client.method_calls[-4] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU4704397:8", paramset_key="VALUES", values={"COLOR_BEHAVIOUR": 1, "COLOR": 6, "LEVEL": 1.0}, @@ -713,7 +713,7 @@ async def test_ceipfixedcolorlightwired( assert light.effect == ColorBehaviour.ON await light.turn_on(hs_color=(120, 50)) - assert mock_client.method_calls[-4] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU4704397:8", paramset_key="VALUES", values={"COLOR_BEHAVIOUR": 1, "COLOR": 2, "LEVEL": 1.0}, @@ -724,7 +724,7 @@ async def test_ceipfixedcolorlightwired( assert light.effect == ColorBehaviour.ON await light.turn_on(hs_color=(180, 50)) - assert mock_client.method_calls[-4] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU4704397:8", paramset_key="VALUES", values={"COLOR_BEHAVIOUR": 1, "COLOR": 3, "LEVEL": 1.0}, @@ -735,7 +735,7 @@ async def test_ceipfixedcolorlightwired( assert light.effect == ColorBehaviour.ON await light.turn_on(hs_color=(240, 50)) - assert mock_client.method_calls[-4] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU4704397:8", paramset_key="VALUES", values={"COLOR_BEHAVIOUR": 1, "COLOR": 1, "LEVEL": 1.0}, @@ -746,7 +746,7 @@ async def test_ceipfixedcolorlightwired( assert light.effect == ColorBehaviour.ON await light.turn_on(hs_color=(300, 50)) - assert mock_client.method_calls[-4] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU4704397:8", paramset_key="VALUES", values={"COLOR_BEHAVIOUR": 1, "COLOR": 5, "LEVEL": 1.0}, @@ -762,7 +762,7 @@ async def test_ceipfixedcolorlightwired( assert light.effect == ColorBehaviour.ON await light.turn_on(brightness=100) - assert mock_client.method_calls[-3] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU4704397:8", paramset_key="VALUES", values={"COLOR_BEHAVIOUR": 1, "LEVEL": 0.39215686274509803}, @@ -773,7 +773,7 @@ async def test_ceipfixedcolorlightwired( assert light.effect == ColorBehaviour.ON await light.turn_on(brightness=33) - assert mock_client.method_calls[-3] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU4704397:8", paramset_key="VALUES", values={"COLOR_BEHAVIOUR": 1, "LEVEL": 0.12941176470588237}, @@ -784,7 +784,7 @@ async def test_ceipfixedcolorlightwired( assert light.effect == ColorBehaviour.ON await light.turn_on(effect="FLASH_MIDDLE") - assert mock_client.method_calls[-3] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU4704397:8", paramset_key="VALUES", values={"COLOR_BEHAVIOUR": 6, "LEVEL": 0.12941176470588237}, @@ -795,7 +795,7 @@ async def test_ceipfixedcolorlightwired( assert light.effect == "FLASH_MIDDLE" await light.turn_on(brightness=66) - assert mock_client.method_calls[-3] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU4704397:8", paramset_key="VALUES", values={"COLOR_BEHAVIOUR": 6, "LEVEL": 0.25882352941176473}, @@ -809,7 +809,7 @@ async def test_ceipfixedcolorlightwired( light.set_on_time(18) await light.turn_on() - assert mock_client.method_calls[-3] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU4704397:8", paramset_key="VALUES", values={"COLOR_BEHAVIOUR": 6, "DURATION_VALUE": 18, "LEVEL": 1.0}, @@ -819,7 +819,7 @@ async def test_ceipfixedcolorlightwired( await light.turn_off() light.set_on_time(17000) await light.turn_on() - assert mock_client.method_calls[-3] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU4704397:8", paramset_key="VALUES", values={"COLOR_BEHAVIOUR": 6, "DURATION_UNIT": 1, "DURATION_VALUE": 283, "LEVEL": 1.0}, @@ -829,14 +829,14 @@ async def test_ceipfixedcolorlightwired( await light.turn_off() light.set_on_time(1000000) await light.turn_on() - assert mock_client.method_calls[-3] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU4704397:8", paramset_key="VALUES", values={"COLOR_BEHAVIOUR": 6, "DURATION_UNIT": 2, "DURATION_VALUE": 277, "LEVEL": 1.0}, wait_for_callback=WAIT_FOR_CALLBACK, ) await light.turn_on(ramp_time=18) - assert mock_client.method_calls[-3] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU4704397:8", paramset_key="VALUES", values={"COLOR_BEHAVIOUR": 6, "RAMP_TIME_VALUE": 18, "LEVEL": 1.0}, @@ -844,7 +844,7 @@ async def test_ceipfixedcolorlightwired( ) await light.turn_on(ramp_time=17000) - assert mock_client.method_calls[-3] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU4704397:8", paramset_key="VALUES", values={"COLOR_BEHAVIOUR": 6, "RAMP_TIME_UNIT": 1, "RAMP_TIME_VALUE": 283, "LEVEL": 1.0}, @@ -852,7 +852,7 @@ async def test_ceipfixedcolorlightwired( ) await light.turn_on(ramp_time=1000000) - assert mock_client.method_calls[-3] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU4704397:8", paramset_key="VALUES", values={"COLOR_BEHAVIOUR": 6, "RAMP_TIME_UNIT": 2, "RAMP_TIME_VALUE": 277, "LEVEL": 1.0}, @@ -871,7 +871,7 @@ async def test_ceipfixedcolorlightwired( await light.turn_off() await light.turn_on(effect="BLINKING_SLOW") - assert mock_client.method_calls[-3] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU4704397:8", paramset_key="VALUES", values={"COLOR_BEHAVIOUR": 2, "LEVEL": 1.0}, @@ -880,7 +880,7 @@ async def test_ceipfixedcolorlightwired( await light.turn_on(brightness=28) await light.turn_on(effect="FLASH_MIDDLE") - assert mock_client.method_calls[-3] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU4704397:8", paramset_key="VALUES", values={"COLOR_BEHAVIOUR": 6, "LEVEL": 0.10980392156862745}, @@ -946,7 +946,7 @@ async def test_ceiprgbwlight( assert light.brightness == 0 await light.turn_on() - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU5629873:1", paramset_key="VALUES", parameter="LEVEL", @@ -955,7 +955,7 @@ async def test_ceiprgbwlight( ) assert light.brightness == 255 await light.turn_on(brightness=28) - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU5629873:1", paramset_key="VALUES", parameter="LEVEL", @@ -964,7 +964,7 @@ async def test_ceiprgbwlight( ) assert light.brightness == 28 await light.turn_off() - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU5629873:1", paramset_key="VALUES", parameter="LEVEL", @@ -975,7 +975,7 @@ async def test_ceiprgbwlight( assert light.color_temp is None await light.turn_on(color_temp=300) - assert mock_client.method_calls[-3] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU5629873:1", paramset_key="VALUES", values={"COLOR_TEMPERATURE": 3333, "LEVEL": 1.0}, @@ -995,7 +995,7 @@ async def test_ceiprgbwlight( assert light.hs_color is None await light.turn_on(hs_color=(44.4, 69.3)) - assert mock_client.method_calls[-4] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU5629873:1", paramset_key="VALUES", values={"HUE": 44, "SATURATION": 0.693, "LEVEL": 1.0}, @@ -1004,7 +1004,7 @@ async def test_ceiprgbwlight( assert light.hs_color == (44, 69.3) await light.turn_on(hs_color=(0, 50)) - assert mock_client.method_calls[-4] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU5629873:1", paramset_key="VALUES", values={"HUE": 0, "SATURATION": 0.5, "LEVEL": 1.0}, @@ -1013,7 +1013,7 @@ async def test_ceiprgbwlight( assert light.hs_color == (0.0, 50.0) await light.turn_on(effect="EFFECT_01_END_CURRENT_PROFILE") - assert mock_client.method_calls[-2] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU5629873:1", paramset_key="VALUES", values={"EFFECT": 1, "LEVEL": 1.0}, @@ -1021,7 +1021,7 @@ async def test_ceiprgbwlight( ) await light.turn_on(hs_color=(44, 66), ramp_time=5) - assert mock_client.method_calls[-4] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU5629873:1", paramset_key=ParamsetKey.VALUES, values={ @@ -1036,7 +1036,7 @@ async def test_ceiprgbwlight( ) await light.turn_off(ramp_time=5) - assert mock_client.method_calls[-2] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU5629873:1", paramset_key=ParamsetKey.VALUES, values={ @@ -1049,7 +1049,7 @@ async def test_ceiprgbwlight( ) await light.turn_on(hs_color=(44, 66), ramp_time=5, on_time=8760) - assert mock_client.method_calls[-4] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU5629873:1", paramset_key=ParamsetKey.VALUES, values={ @@ -1100,7 +1100,7 @@ async def test_cecolordimmer( assert light.brightness == 0 assert light.brightness_pct == 0 await light.turn_on() - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU9973336:13", paramset_key="VALUES", parameter="LEVEL", @@ -1109,7 +1109,7 @@ async def test_cecolordimmer( ) assert light.brightness == 255 await light.turn_on(brightness=28) - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU9973336:13", paramset_key="VALUES", parameter="LEVEL", @@ -1118,7 +1118,7 @@ async def test_cecolordimmer( ) assert light.brightness == 28 await light.turn_off() - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU9973336:13", paramset_key="VALUES", parameter="LEVEL", @@ -1129,14 +1129,14 @@ async def test_cecolordimmer( assert light.hs_color == (0.0, 0.0) await light.turn_on(hs_color=(44.4, 69.3)) - assert mock_client.method_calls[-4] == call.set_value( + assert mock_client.method_calls[-2] == call.set_value( channel_address="VCU9973336:15", paramset_key="VALUES", parameter="COLOR", value=25, wait_for_callback=WAIT_FOR_CALLBACK, ) - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU9973336:13", paramset_key="VALUES", parameter="LEVEL", @@ -1146,14 +1146,14 @@ async def test_cecolordimmer( assert light.hs_color == (45.0, 100) await light.turn_on(hs_color=(0, 50)) - assert mock_client.method_calls[-4] == call.set_value( + assert mock_client.method_calls[-2] == call.set_value( channel_address="VCU9973336:15", paramset_key="VALUES", parameter="COLOR", value=0, wait_for_callback=WAIT_FOR_CALLBACK, ) - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU9973336:13", paramset_key="VALUES", parameter="LEVEL", @@ -1162,14 +1162,14 @@ async def test_cecolordimmer( ) assert light.hs_color == (0.0, 100.0) await light.turn_on(hs_color=(0, 1)) - assert mock_client.method_calls[-4] == call.set_value( + assert mock_client.method_calls[-2] == call.set_value( channel_address="VCU9973336:15", paramset_key="VALUES", parameter="COLOR", value=200, wait_for_callback=WAIT_FOR_CALLBACK, ) - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU9973336:13", paramset_key="VALUES", parameter="LEVEL", diff --git a/tests/test_lock.py b/tests/test_lock.py index 08c1d7ff..acf2c97b 100644 --- a/tests/test_lock.py +++ b/tests/test_lock.py @@ -49,7 +49,7 @@ async def test_cerflock( assert lock.is_locked is True await lock.unlock() - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU0000146:1", paramset_key="VALUES", parameter="STATE", @@ -58,7 +58,7 @@ async def test_cerflock( ) assert lock.is_locked is False await lock.lock() - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU0000146:1", paramset_key="VALUES", parameter="STATE", diff --git a/tests/test_number.py b/tests/test_number.py index 2e0f1562..8071f60f 100644 --- a/tests/test_number.py +++ b/tests/test_number.py @@ -52,7 +52,7 @@ async def test_hmfloat( assert efloat.values is None assert efloat.value is None await efloat.send_value(0.3) - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU0000011:3", paramset_key="VALUES", parameter="LEVEL", @@ -98,7 +98,7 @@ async def test_hmfloat_special( assert efloat.values is None assert efloat.value is None await efloat.send_value(8.0) - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU0000054:2", paramset_key="VALUES", parameter="SETPOINT", @@ -107,7 +107,7 @@ async def test_hmfloat_special( assert efloat.value == 8.0 await efloat.send_value("VENT_OPEN") - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU0000054:2", paramset_key="VALUES", parameter="SETPOINT", @@ -146,7 +146,7 @@ async def test_hminteger( assert einteger.values is None assert einteger.value is None await einteger.send_value(3) - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU4984404:1", paramset_key="VALUES", parameter="SET_POINT_MODE", @@ -155,7 +155,7 @@ async def test_hminteger( assert einteger.value == 3 await einteger.send_value(1.0) - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU4984404:1", paramset_key="VALUES", parameter="SET_POINT_MODE", diff --git a/tests/test_select.py b/tests/test_select.py index 3fa1c947..44b4d842 100644 --- a/tests/test_select.py +++ b/tests/test_select.py @@ -52,7 +52,7 @@ async def test_hmselect( assert select.values == ("CLOSED", "OPEN") assert select.value == "CLOSED" await select.send_value("OPEN") - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU6354483:1", paramset_key="VALUES", parameter="WINDOW_STATE", @@ -67,7 +67,7 @@ async def test_hmselect( assert select.value == "CLOSED" await select.send_value(1) - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU6354483:1", paramset_key="VALUES", parameter="WINDOW_STATE", diff --git a/tests/test_support.py b/tests/test_support.py index 662357fa..a245bcd8 100644 --- a/tests/test_support.py +++ b/tests/test_support.py @@ -12,7 +12,13 @@ from hahomematic.caches.visibility import _get_value_from_dict_by_wildcard_key from hahomematic.central import CentralUnit from hahomematic.client import Client -from hahomematic.const import INIT_DATETIME, EntityUsage, ParameterType, SysvarType +from hahomematic.const import ( + INIT_DATETIME, + VIRTUAL_REMOTE_ADDRESSES, + EntityUsage, + ParameterType, + SysvarType, +) from hahomematic.converter import _COMBINED_PARAMETER_TO_HM_CONVERTER, convert_hm_level_to_cpv from hahomematic.exceptions import HaHomematicException from hahomematic.platforms.support import ( @@ -576,10 +582,12 @@ def test_is_valid_ipv4_address() -> None: def test_is_device_address() -> None: """Test is_device_address.""" - assert is_device_address("123456789") is False + for address in VIRTUAL_REMOTE_ADDRESSES: + assert is_device_address(address) is True assert is_device_address("123456789:2") is False - assert is_device_address("1234567890") is True - assert is_device_address("1234567890-") is False + assert is_device_address("KEQ1234567") is True + assert is_device_address("001858A123B912") is True + assert is_device_address("1234567890#") is False assert is_device_address("123456789_:123") is False assert is_device_address("ABcdEFghIJ1234567890") is True assert is_device_address("12345678901234567890") is True @@ -588,10 +596,12 @@ def test_is_device_address() -> None: def test_is_channel_address() -> None: """Test is_channel_address.""" - assert is_channel_address("123456789") is False - assert is_channel_address("123456789:2") is False - assert is_channel_address("1234567890:1") is True - assert is_channel_address("1234567890:12") is True + for address in VIRTUAL_REMOTE_ADDRESSES: + assert is_channel_address(f"{address}:13") is True + assert is_channel_address("1234") is False + assert is_channel_address("1234:2") is False + assert is_channel_address("KEQ1234567:13") is True + assert is_channel_address("001858A123B912:1") is True assert is_channel_address("1234567890:123") is True assert is_channel_address("123456789_:123") is False assert is_channel_address("ABcdEFghIJ1234567890:123") is True diff --git a/tests/test_switch.py b/tests/test_switch.py index e2e4be1c..5ef9e232 100644 --- a/tests/test_switch.py +++ b/tests/test_switch.py @@ -50,7 +50,7 @@ async def test_ceswitch( assert switch.value is False assert switch.channel_value is False await switch.turn_on() - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU2128127:4", paramset_key="VALUES", parameter="STATE", @@ -59,7 +59,7 @@ async def test_ceswitch( ) assert switch.value is True await switch.turn_off() - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU2128127:4", paramset_key="VALUES", parameter="STATE", @@ -68,7 +68,7 @@ async def test_ceswitch( ) assert switch.value is False await switch.turn_on(on_time=60) - assert mock_client.method_calls[-2] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU2128127:4", paramset_key="VALUES", values={"ON_TIME": 60.0, "STATE": True}, @@ -79,7 +79,7 @@ async def test_ceswitch( await switch.turn_off() switch.set_on_time(35.4) await switch.turn_on() - assert mock_client.method_calls[-2] == call.put_paramset( + assert mock_client.method_calls[-1] == call.put_paramset( channel_address="VCU2128127:4", paramset_key="VALUES", values={"ON_TIME": 35.4, "STATE": True}, @@ -121,7 +121,7 @@ async def test_hmswitch( assert switch.value is None await switch.turn_on() - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU2128127:4", paramset_key="VALUES", parameter="STATE", @@ -129,7 +129,7 @@ async def test_hmswitch( ) assert switch.value is True await switch.turn_off() - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU2128127:4", paramset_key="VALUES", parameter="STATE", @@ -137,13 +137,13 @@ async def test_hmswitch( ) assert switch.value is False await switch.turn_on(on_time=60) - assert mock_client.method_calls[-3] == call.set_value( + assert mock_client.method_calls[-2] == call.set_value( channel_address="VCU2128127:4", paramset_key="VALUES", parameter="ON_TIME", value=60.0, ) - assert mock_client.method_calls[-2] == call.set_value( + assert mock_client.method_calls[-1] == call.set_value( channel_address="VCU2128127:4", paramset_key="VALUES", parameter="STATE",