diff --git a/changelog.txt b/changelog.txt index 2d1a813a..b09c568b 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,8 @@ Version 0.13.2 (2022-01-04) - Fix cover state +- Move delete_devices from RPCFunctions to central +- Move new_devices from RPCFunctions to central +- Add method to delete a single device to central Version 0.13.1 (2022-01-04) - Use generic climate profiles list diff --git a/hahomematic/central_unit.py b/hahomematic/central_unit.py index 7cbf3564..997fb9b5 100644 --- a/hahomematic/central_unit.py +++ b/hahomematic/central_unit.py @@ -32,6 +32,7 @@ FILE_DEVICES, FILE_NAMES, FILE_PARAMSETS, + HH_EVENT_DELETE_DEVICES, HM_VIRTUAL_REMOTE_HM, HM_VIRTUAL_REMOTE_HMIP, LOCALHOST, @@ -209,6 +210,87 @@ def create_devices(self) -> None: _LOGGER.exception("CentralUnit.init: Failed to create entities") raise Exception("entity-creation-error") from err + async def delete_device(self, interface_id: str, device_address: str) -> None: + """Delete devices from central_unit.""" + _LOGGER.debug( + "CentralUnit.delete_device: interface_id = %s, device_address = %s", + interface_id, + device_address, + ) + addresses: list[str] = [] + if hm_device := self.hm_devices.get(device_address): + addresses.append(device_address) + addresses.extend(hm_device.channels) + + await self.delete_devices(interface_id=interface_id, addresses=addresses) + args: list[Any] = [HH_EVENT_DELETE_DEVICES, addresses] + if self.callback_system_event is not None and callable(self.callback_system_event): + self.callback_system_event(HH_EVENT_DELETE_DEVICES, *args) # pylint: disable=not-callable + + async def delete_devices(self, interface_id: str, addresses: list[str]) -> None: + """Delete devices from central_unit.""" + _LOGGER.debug( + "CentralUnit.delete_devices: interface_id = %s, addresses = %s", + interface_id, + str(addresses), + ) + + await self.raw_devices.cleanup( + interface_id=interface_id, deleted_addresses=addresses + ) + + for address in addresses: + try: + if ":" in address: + self.paramsets.remove( + interface_id=interface_id, channel_address=address + ) + self.names.remove(address=address) + if hm_device := self.hm_devices.get(address): + hm_device.remove_event_subscriptions() + hm_device.remove_from_collections() + del self.hm_devices[address] + except KeyError: + _LOGGER.exception("Failed to delete: %s", address) + await self.paramsets.save() + await self.names.save() + + async def add_new_devices( + self, interface_id: str, dev_descriptions: list[dict[str, Any]] + ) -> None: + """Async implementation""" + _LOGGER.debug( + "CentralUnit.add_new_devices: interface_id = %s, dev_descriptions = %s", + interface_id, + len(dev_descriptions), + ) + + if interface_id not in self.clients: + _LOGGER.error( + "RPCFunctions.newDevices: Missing client for interface_id %s.", + interface_id, + ) + return None + + # We need this list to avoid adding duplicates. + known_addresses = [ + dev_desc[ATTR_HM_ADDRESS] + for dev_desc in self.raw_devices.get_device_descriptions(interface_id) + ] + client = self.clients[interface_id] + for dev_desc in dev_descriptions: + try: + if dev_desc[ATTR_HM_ADDRESS] not in known_addresses: + self.raw_devices.add_device_description(interface_id, dev_desc) + await client.fetch_paramsets(dev_desc) + except Exception: + _LOGGER.exception("RPCFunctions.newDevices: Exception") + await self.raw_devices.save() + await self.paramsets.save() + await client.fetch_names() + await self.names.save() + create_devices(self) + async def stop(self) -> None: """ then shut down our XML-RPC server. diff --git a/hahomematic/device.py b/hahomematic/device.py index 02fbfac6..6925a9d6 100644 --- a/hahomematic/device.py +++ b/hahomematic/device.py @@ -156,6 +156,11 @@ def device_address(self) -> str: """Return the address.""" return self._device_address + @property + def channels(self) -> list[str]: + """Return the channels.""" + return self._channels + def add_hm_entity(self, hm_entity: BaseEntity) -> None: """Add a hm entity to a device.""" if isinstance(hm_entity, GenericEntity): diff --git a/hahomematic/devices/climate.py b/hahomematic/devices/climate.py index d6a69089..ae99391e 100644 --- a/hahomematic/devices/climate.py +++ b/hahomematic/devices/climate.py @@ -490,7 +490,7 @@ def _current_profile_name(self) -> str | None: def _profiles(self) -> dict[str, int]: """Return the profile groups.""" profiles: dict[str, int] = {} - for i in range(self._e_active_profile.min, self._e_active_profile.max+1): + for i in range(self._e_active_profile.min, self._e_active_profile.max + 1): profiles[f"Profile {i}"] = i return profiles diff --git a/hahomematic/xml_rpc_server.py b/hahomematic/xml_rpc_server.py index 1513e6b6..6a133dbe 100644 --- a/hahomematic/xml_rpc_server.py +++ b/hahomematic/xml_rpc_server.py @@ -13,7 +13,6 @@ import hahomematic.central_unit as hm_central from hahomematic.const import ( - ATTR_HM_ADDRESS, HH_EVENT_DELETE_DEVICES, HH_EVENT_ERROR, HH_EVENT_LIST_DEVICES, @@ -25,7 +24,6 @@ PORT_ANY, ) from hahomematic.decorators import callback_event, callback_system_event -from hahomematic.device import create_devices _LOGGER = logging.getLogger(__name__) @@ -110,45 +108,13 @@ def newDevices( We react on that and add those devices as well. """ - async def _async_new_devices(central_unit: hm_central.CentralUnit) -> None: - """Async implementation""" - _LOGGER.debug( - "RPCFunctions.newDevices: interface_id = %s, dev_descriptions = %s", - interface_id, - len(dev_descriptions), - ) - - if interface_id not in central_unit.clients: - _LOGGER.error( - "RPCFunctions.newDevices: Missing client for interface_id %s.", - interface_id, - ) - return None - - # We need this list to avoid adding duplicates. - known_addresses = [ - dd[ATTR_HM_ADDRESS] - for dd in central_unit.raw_devices.get_device_descriptions(interface_id) - ] - client = central_unit.clients[interface_id] - for dd in dev_descriptions: - try: - if dd[ATTR_HM_ADDRESS] not in known_addresses: - central_unit.raw_devices.add_device_description( - interface_id, dd - ) - await client.fetch_paramsets(dd) - except Exception: - _LOGGER.exception("RPCFunctions.newDevices: Exception") - await central_unit.raw_devices.save() - await central_unit.paramsets.save() - await client.fetch_names() - await central_unit.names.save() - create_devices(central_unit) - central: hm_central.CentralUnit | None if central := self._xml_rpc_server.get_central(interface_id): - central.run_coroutine(_async_new_devices(central)) + central.run_coroutine( + central.add_new_devices( + interface_id=interface_id, dev_descriptions=dev_descriptions + ) + ) @callback_system_event(HH_EVENT_DELETE_DEVICES) def deleteDevices(self, interface_id: str, addresses: list[str]) -> None: @@ -157,37 +123,11 @@ def deleteDevices(self, interface_id: str, addresses: list[str]) -> None: We react on that and remove those devices as well. """ - async def _async_delete_devices(central_unit: hm_central.CentralUnit) -> None: - """async implementation.""" - _LOGGER.debug( - "RPCFunctions.deleteDevices: interface_id = %s, addresses = %s", - interface_id, - str(addresses), - ) - - await central_unit.raw_devices.cleanup( - interface_id=interface_id, deleted_addresses=addresses - ) - - for address in addresses: - try: - if ":" in address: - central_unit.paramsets.remove( - interface_id=interface_id, channel_address=address - ) - central_unit.names.remove(address=address) - if hm_device := central_unit.hm_devices.get(address): - hm_device.remove_event_subscriptions() - hm_device.remove_from_collections() - del central_unit.hm_devices[address] - except KeyError: - _LOGGER.exception("Failed to delete: %s", address) - await central_unit.paramsets.save() - await central_unit.names.save() - central: hm_central.CentralUnit | None if central := self._xml_rpc_server.get_central(interface_id): - central.run_coroutine(_async_delete_devices(central)) + central.run_coroutine( + central.delete_devices(interface_id=interface_id, addresses=addresses) + ) @callback_system_event(HH_EVENT_UPDATE_DEVICE) # pylint: disable=no-self-use diff --git a/setup.py b/setup.py index ea57e400..6f176922 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ def readme(): }, PACKAGE_NAME = "hahomematic" HERE = os.path.abspath(os.path.dirname(__file__)) -VERSION = "0.13.1" +VERSION = "0.13.2" PACKAGES = find_packages(exclude=["tests", "tests.*", "dist", "build"])