From d19da5465ce9969f8926557d030046b19859d1e3 Mon Sep 17 00:00:00 2001 From: gluap <44007906+gluap@users.noreply.github.com> Date: Mon, 9 Oct 2023 08:43:15 +0200 Subject: [PATCH 1/8] Update services.yaml remove sync_devices, seems this isn't supported by HA in this way any more. --- custom_components/duofern/services.yaml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/custom_components/duofern/services.yaml b/custom_components/duofern/services.yaml index 4560c62..bb17c17 100644 --- a/custom_components/duofern/services.yaml +++ b/custom_components/duofern/services.yaml @@ -1,16 +1,12 @@ # Describes the format for available Wink services start_pairing: - description: Pair duofern devices. Remember - There is no pairing UI. To pick up the newly paired devices later, you need to call sync_devices and possibly restart HA. + description: Pair duofern devices. Remember - There is no pairing UI. To pick up the newly paired devices later, you need to restart HA. fields: timeout: description: timeout in seconds example: 60 -sync_devices: - description: Re-sync Devices (trigger after pairing, if it does not work, a restart of homeassistant may help). Also writes duofern config file. - May raise warnings for already-created devices. Do not worry. - clean_config: description: Clean the duofern config. More info in the readme. From bdfe9a905127573a4fcba4c63c07e7a5d271caa6 Mon Sep 17 00:00:00 2001 From: gluap <44007906+gluap@users.noreply.github.com> Date: Mon, 9 Oct 2023 08:43:57 +0200 Subject: [PATCH 2/8] Update __init__.py remove sync devices - this way seems not supported by HA any more. --- custom_components/duofern/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/custom_components/duofern/__init__.py b/custom_components/duofern/__init__.py index 3c0d910..0f67319 100644 --- a/custom_components/duofern/__init__.py +++ b/custom_components/duofern/__init__.py @@ -107,8 +107,8 @@ def start_unpairing(call: ServiceCall) -> None: _LOGGER.warning("start pairing") getDuofernStick(hass).unpair(call.data.get('timeout', 60)) - def sync_devices(call: ServiceCall) -> None: - stick.sync_devices() + #def sync_devices(call: ServiceCall) -> None: + # stick.sync_devices() def dump_device_state(call: ServiceCall) -> None: _LOGGER.warning(getDuofernStick(hass).duofern_parser.modules) @@ -181,7 +181,7 @@ def set_update_interval(call: ServiceCall) -> None: hass.services.register(DOMAIN, 'start_pairing', start_pairing, PAIRING_SCHEMA) hass.services.register(DOMAIN, 'start_unpairing', start_unpairing, PAIRING_SCHEMA) - hass.services.register(DOMAIN, 'sync_devices', sync_devices) + # hass.services.register(DOMAIN, 'sync_devices', sync_devices) hass.services.register(DOMAIN, 'clean_config', clean_config) hass.services.register(DOMAIN, 'dump_device_state', dump_device_state) hass.services.register(DOMAIN, 'ask_for_update', ask_for_update, UPDATE_SCHEMA) From 8faba7e83cae86695c967fb48da46690d989e78a Mon Sep 17 00:00:00 2001 From: gluap Date: Sat, 30 Dec 2023 16:29:46 +0100 Subject: [PATCH 3/8] remove non-working sync-devices command --- readme.md | 7 ------- sync_devices.png | Bin 29202 -> 0 bytes 2 files changed, 7 deletions(-) delete mode 100644 sync_devices.png diff --git a/readme.md b/readme.md index 292c2e7..e01ef55 100644 --- a/readme.md +++ b/readme.md @@ -47,13 +47,6 @@ There are some services you can call via the service interface. A few of these t ![Pairing](./pairing.png) -``duofern.sync_devices`` will force-sync any newly discovered devices. - -![sync](./sync_devices.png) - -Please use the renaming feature in the homeassistant GUI to arrive at human readable -names for your deices. - ``duofern.ask_for_update`` Ask duofern devices to re-send their state in case. Can be used in setups where RF is finnicky. diff --git a/sync_devices.png b/sync_devices.png deleted file mode 100644 index c75b02dd060658a776ed43d4d7676cbda931f26e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29202 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
TR0VNhz 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 Date: Sat, 30 Dec 2023 23:36:38 +0100 Subject: [PATCH 4/8] add remove device command --- custom_components/duofern/__init__.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/custom_components/duofern/__init__.py b/custom_components/duofern/__init__.py index 0f67319..fe4cc49 100644 --- a/custom_components/duofern/__init__.py +++ b/custom_components/duofern/__init__.py @@ -25,7 +25,7 @@ _LOGGER = logging.getLogger(__name__) from .const import DOMAIN, DUOFERN_COMPONENTS - +from homeassistant.helpers.device_registry import DeviceEntry # Validation of the user's configuration CONFIG_SCHEMA = vol.Schema({DOMAIN: vol.Schema({ vol.Optional('serial_port', @@ -37,6 +37,21 @@ }, extra=vol.ALLOW_EXTRA) +async def async_remove_config_entry_device( + hass: HomeAssistant, config_entry: ConfigEntry, device_entry: DeviceEntry +) -> bool: + """Remove a config entry from a device.""" + stick = getDuofernStick(hass) + try: + del(stick.duofern_parser.modules["by_code"][device_entry.name]) + stick.config['devices'] = [dev for dev in stick.config['devices'] if dev['id'] != device_entry.name] + except: + logging.warning(device_entry) + logging.warning(device_entry.__dict__) + stick.sync_devices() + return True + + def setup(hass: HomeAssistant, config: ConfigType) -> bool: """Setup the duofern stick for communicating with the duofern devices via entities""" configEntries = hass.config_entries.async_entries(DOMAIN) From 15066111426e42b376820294c8049ca6c136e1e8 Mon Sep 17 00:00:00 2001 From: gluap Date: Sun, 31 Dec 2023 13:12:54 +0100 Subject: [PATCH 5/8] Add reload facility --- custom_components/duofern/__init__.py | 68 +++++++++++++++++++++--- custom_components/duofern/domain_data.py | 15 ++++++ 2 files changed, 76 insertions(+), 7 deletions(-) diff --git a/custom_components/duofern/__init__.py b/custom_components/duofern/__init__.py index fe4cc49..56ce1c1 100644 --- a/custom_components/duofern/__init__.py +++ b/custom_components/duofern/__init__.py @@ -1,10 +1,11 @@ +import asyncio import logging import os import re from typing import Any # from homeassistant.const import 'serial_port', 'config_file', 'code' -from homeassistant.core import HomeAssistant, ServiceCall +from homeassistant.core import HomeAssistant, ServiceCall, callback from homeassistant.config_entries import ConfigEntry import homeassistant.helpers.config_validation as cv import voluptuous as vol @@ -25,7 +26,14 @@ _LOGGER = logging.getLogger(__name__) from .const import DOMAIN, DUOFERN_COMPONENTS +from .domain_data import _getData +from custom_components.duofern.domain_data import getDuofernStick, isDeviceSetUp, saveDeviceAsSetUp, unsetupDevice + from homeassistant.helpers.device_registry import DeviceEntry + +SERVICES = ['start_pairing', 'start_unpairing', 'clean_config', 'dump_device_state', 'ask_for_update', + 'set_update_interval'] + # Validation of the user's configuration CONFIG_SCHEMA = vol.Schema({DOMAIN: vol.Schema({ vol.Optional('serial_port', @@ -38,12 +46,12 @@ async def async_remove_config_entry_device( - hass: HomeAssistant, config_entry: ConfigEntry, device_entry: DeviceEntry + hass: HomeAssistant, config_entry: ConfigEntry, device_entry: DeviceEntry ) -> bool: """Remove a config entry from a device.""" stick = getDuofernStick(hass) try: - del(stick.duofern_parser.modules["by_code"][device_entry.name]) + del (stick.duofern_parser.modules["by_code"][device_entry.name]) stick.config['devices'] = [dev for dev in stick.config['devices'] if dev['id'] != device_entry.name] except: logging.warning(device_entry) @@ -52,6 +60,50 @@ async def async_remove_config_entry_device( return True +async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: + """Unload deCONZ config entry.""" + + stick = getDuofernStick(hass) + stick.stop() + try: + stick.serial_connection.close() + except: + _LOGGER.exception("closing serial connection failed") + + await asyncio.sleep(0.5) + + + + for duofernDevice in stick.config['devices']: + _LOGGER.info(f"unsetting up device {duofernDevice}") + duofernId: str = duofernDevice['id'] + if not isDeviceSetUp(hass, duofernId): + continue + _LOGGER.info(f"unsetting up device {duofernDevice}") + unsetupDevice(hass, duofernId) + + for component in DUOFERN_COMPONENTS: + hass.async_create_task( + hass.config_entries.async_forward_entry_unload(config_entry, component) + ) + + + newstick = DuofernStickThreaded(serial_port=stick.port, system_code=stick.system_code, + config_file_json=stick.config_file, + ephemeral=False) + newstick.start() + hass.data[DOMAIN]['stick'] = newstick + del stick + + return True + + +@callback +def async_unload_services(hass: HomeAssistant) -> None: + for service in SERVICES: + hass.services.async_remove(DOMAIN, service) + + def setup(hass: HomeAssistant, config: ConfigType) -> bool: """Setup the duofern stick for communicating with the duofern devices via entities""" configEntries = hass.config_entries.async_entries(DOMAIN) @@ -122,7 +174,7 @@ def start_unpairing(call: ServiceCall) -> None: _LOGGER.warning("start pairing") getDuofernStick(hass).unpair(call.data.get('timeout', 60)) - #def sync_devices(call: ServiceCall) -> None: + # def sync_devices(call: ServiceCall) -> None: # stick.sync_devices() def dump_device_state(call: ServiceCall) -> None: @@ -152,8 +204,9 @@ def get_device_id(hass_entity_id): _LOGGER.info("Asking specific devices for update") device_ids = [get_device_id(i) for i in hass_device_id] except Exception: - _LOGGER.exception(f"Exception while getting device id {call}, {call.data}, i know {hass.data[DOMAIN]['deviceByHassId']}, fyi deviceByID is {hass.data[DOMAIN]['devices']}") - for id,dev in hass.data[DOMAIN]['deviceByHassId'].items(): + _LOGGER.exception( + f"Exception while getting device id {call}, {call.data}, i know {hass.data[DOMAIN]['deviceByHassId']}, fyi deviceByID is {hass.data[DOMAIN]['devices']}") + for id, dev in hass.data[DOMAIN]['deviceByHassId'].items(): _LOGGER.warning(f"{id}, {dev.__dict__}") raise if device_ids is None: @@ -165,7 +218,8 @@ def get_device_id(hass_entity_id): for device_id in device_ids: if device_id is not None: if device_id not in hass.data[DOMAIN]['stick'].duofern_parser.modules['by_code']: - _LOGGER.warning(f"{device_id} is not a valid duofern device, I only know {hass.data[DOMAIN]['stick'].duofern_parser.modules['by_code'].keys()}. Gonna handle the other devices in {device_ids} though.") + _LOGGER.warning( + f"{device_id} is not a valid duofern device, I only know {hass.data[DOMAIN]['stick'].duofern_parser.modules['by_code'].keys()}. Gonna handle the other devices in {device_ids} though.") continue _LOGGER.info(f"asking {device_id} for update") getDuofernStick(hass).command(device_id, 'getStatus') diff --git a/custom_components/duofern/domain_data.py b/custom_components/duofern/domain_data.py index 804dd35..a79d151 100644 --- a/custom_components/duofern/domain_data.py +++ b/custom_components/duofern/domain_data.py @@ -6,6 +6,7 @@ from custom_components.duofern.const import DOMAIN + class DuofernDomainData(TypedDict): stick: DuofernStickThreaded devices: dict[str, Entity] @@ -15,13 +16,26 @@ class DuofernDomainData(TypedDict): def getDuofernStick(hass: HomeAssistant) -> DuofernStickThreaded: return _getData(hass)['stick'] + def isDeviceSetUp(hass: HomeAssistant, duofernId: str, subIdWithinHassDevice: str = "") -> bool: return (duofernId + subIdWithinHassDevice) in _getData(hass)['devices'] + def saveDeviceAsSetUp(hass: HomeAssistant, device: Entity, duofernId: str, subIdWithinHassDevice: str = "") -> None: _getData(hass)['devices'][duofernId + subIdWithinHassDevice] = device _getData(hass)['deviceByHassId'][device.unique_id] = device + +def unsetupDevice(hass: HomeAssistant, duofernId: str) -> None: + device_ids = [d for d in _getData(hass)['devices'] if d.startswith(duofernId)] + unique_ids = [_getData(hass)['devices'][d].unique_id for d in device_ids] + for did in device_ids: + if did in _getData(hass)['devices']: + del _getData(hass)['devices'][did] + for uid in unique_ids: + del _getData(hass)['deviceByHassId'][uid] + + def setupDomainData(hass: HomeAssistant, stick: DuofernStickThreaded) -> None: hass.data[DOMAIN] = DuofernDomainData({ 'stick': stick, @@ -29,5 +43,6 @@ def setupDomainData(hass: HomeAssistant, stick: DuofernStickThreaded) -> None: 'deviceByHassId': {} }) + def _getData(hass: HomeAssistant) -> DuofernDomainData: return cast(DuofernDomainData, hass.data[DOMAIN]) From 7480585f1c04d3e3571b51bba0770d7126a6c467 Mon Sep 17 00:00:00 2001 From: gluap Date: Sun, 31 Dec 2023 13:28:17 +0100 Subject: [PATCH 6/8] remove the clean_config service as it's not required any more with the now-possible device deletion. add capability to reload integration docs --- custom_components/duofern/__init__.py | 9 ++++---- readme.md | 31 +++------------------------ 2 files changed, 8 insertions(+), 32 deletions(-) diff --git a/custom_components/duofern/__init__.py b/custom_components/duofern/__init__.py index 56ce1c1..e299e33 100644 --- a/custom_components/duofern/__init__.py +++ b/custom_components/duofern/__init__.py @@ -64,6 +64,7 @@ async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> """Unload deCONZ config entry.""" stick = getDuofernStick(hass) + stick.sync_devices() stick.stop() try: stick.serial_connection.close() @@ -174,8 +175,8 @@ def start_unpairing(call: ServiceCall) -> None: _LOGGER.warning("start pairing") getDuofernStick(hass).unpair(call.data.get('timeout', 60)) - # def sync_devices(call: ServiceCall) -> None: - # stick.sync_devices() + def sync_devices(call: ServiceCall) -> None: + stick.sync_devices() def dump_device_state(call: ServiceCall) -> None: _LOGGER.warning(getDuofernStick(hass).duofern_parser.modules) @@ -250,8 +251,8 @@ def set_update_interval(call: ServiceCall) -> None: hass.services.register(DOMAIN, 'start_pairing', start_pairing, PAIRING_SCHEMA) hass.services.register(DOMAIN, 'start_unpairing', start_unpairing, PAIRING_SCHEMA) - # hass.services.register(DOMAIN, 'sync_devices', sync_devices) - hass.services.register(DOMAIN, 'clean_config', clean_config) + hass.services.register(DOMAIN, 'sync_devices', sync_devices) + #hass.services.register(DOMAIN, 'clean_config', clean_config) hass.services.register(DOMAIN, 'dump_device_state', dump_device_state) hass.services.register(DOMAIN, 'ask_for_update', ask_for_update, UPDATE_SCHEMA) hass.services.register(DOMAIN, 'set_update_interval', set_update_interval, UPDATE_INTERVAL_SCHEMA) diff --git a/readme.md b/readme.md index e01ef55..3de7be9 100644 --- a/readme.md +++ b/readme.md @@ -43,7 +43,7 @@ To use ``pyduofern`` within [Homeassistant](https://home-assistant.io/), add the There are some services you can call via the service interface. A few of these to get you started: -``duofern.start_pairing`` starts the pairing mode for a given number of seconds. +``duofern.start_pairing`` starts the pairing mode for a given number of seconds. After pairing reload the integration to make the new devices visible. ![Pairing](./pairing.png) @@ -54,30 +54,5 @@ Ask duofern devices to re-send their state in case. Can be used in setups where ``duofern.dump_device_state`` Dump the current last received state for all duofern modules as a warning level message to the log. This reflects the current state of all RF messages received from devices - What's not here wasn't received by the stick or came in garbled. -``duofern.clean_config`` -> **Warning** -> You should absolutely NOT use it if you have been running duofern for a long time and your covers have "human" names in the .duofern.json file. That option hasn't been used for a long time though - it is still from the time when homeassistant had no UI way of renaming entities/devices. - -**Use when:** -- you have "ghost" devices that do not correspond to a physical device - -**Use like this:** -- If you want to be sure you can go back: backup ``duofern.json``. -- Call ``duofern.clean_config``. -- Restart homeassistant. -- Observe that all your duofern devices are now disabled/unavailable. -- Toggle/move all your duofern devices at the device to make sure that they send messages for homeassistant to pick up. -- You can diagnose what devices were picked up again using ``duofern.dump_device_state``. -- Once all devices are there: call ``duofern.sync_devices``. -- Restart homeassistant for good measure. -- Observe that the devices are now back. -- If some are still missing: toggle them at the device and diagnose using ``dump_device_state`` until they are found again. -- Once they are: ``duofern.sync_devices``, final restart. -- Everything works. -- If not: maybe you want to return to your backed-up ``duofern.json``. - -The duofern python module keeps a list of devices that are paired. ``clean_config`` throws that list away. - -In normal operation, the list should rebuild itself - whenever a message is received from a device that was previously paired it should appear in the list. -It's not very well tested because it's not a common situation. I ran it, restarted homeassistant, and my devices became available again after a few seconds. - +``duofern.sync_devices`` +Write the duofern config file with the known devices. normally not required from the user. \ No newline at end of file From 82aa3356cb104d25c29818329049fa142afc425d Mon Sep 17 00:00:00 2001 From: gluap Date: Sun, 31 Dec 2023 18:53:43 +0100 Subject: [PATCH 7/8] fix issue when removing devices not present in duofern config. --- custom_components/duofern/__init__.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/custom_components/duofern/__init__.py b/custom_components/duofern/__init__.py index e299e33..5d70664 100644 --- a/custom_components/duofern/__init__.py +++ b/custom_components/duofern/__init__.py @@ -50,13 +50,9 @@ async def async_remove_config_entry_device( ) -> bool: """Remove a config entry from a device.""" stick = getDuofernStick(hass) - try: - del (stick.duofern_parser.modules["by_code"][device_entry.name]) - stick.config['devices'] = [dev for dev in stick.config['devices'] if dev['id'] != device_entry.name] - except: - logging.warning(device_entry) - logging.warning(device_entry.__dict__) - stick.sync_devices() + if device_entry.name in stick.duofern_parser.modules["by_code"]: + del stick.duofern_parser.modules["by_code"][device_entry.name] + stick.config['devices'] = [dev for dev in stick.config['devices'] if dev['id'] != device_entry.name] return True From 9308026350c1124a659fa826d5118ebe3e0777a6 Mon Sep 17 00:00:00 2001 From: gluap Date: Sun, 31 Dec 2023 19:48:50 +0100 Subject: [PATCH 8/8] return unknown state if none is known --- custom_components/duofern/cover.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/custom_components/duofern/cover.py b/custom_components/duofern/cover.py index 5292030..f5c6d9c 100644 --- a/custom_components/duofern/cover.py +++ b/custom_components/duofern/cover.py @@ -102,8 +102,10 @@ def current_cover_position(self) -> int | None: return self._state @property - def is_closed(self) -> bool: + def is_closed(self) -> bool | None: """Return true if cover is close.""" + if self._state is None: + return None return self._state == 0 @property