From 82762f63d3abb0d3127ae90a0d7660ecbb4f4e3e Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 2 Feb 2026 08:39:50 +0100 Subject: [PATCH 01/20] Start adding options-flow for plus pairing --- custom_components/plugwise_usb/config_flow.py | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/custom_components/plugwise_usb/config_flow.py b/custom_components/plugwise_usb/config_flow.py index 654c1863..20fa480c 100644 --- a/custom_components/plugwise_usb/config_flow.py +++ b/custom_components/plugwise_usb/config_flow.py @@ -9,7 +9,7 @@ import voluptuous as vol from homeassistant.components import usb -from homeassistant.config_entries import SOURCE_USER, ConfigFlow +from homeassistant.config_entries import SOURCE_USER, ConfigEntry, ConfigFlow from homeassistant.const import CONF_BASE from homeassistant.core import callback from homeassistant.data_entry_flow import FlowResult @@ -17,6 +17,7 @@ from .const import CONF_MANUAL_PATH, CONF_USB_PATH, DOMAIN, MANUAL_PATH +CONF_ZIGBEE_MAC: Final[str] = "zigbee_mac" @callback def plugwise_stick_entries(hass): @@ -123,3 +124,30 @@ async def async_step_manual_path( ), errors=errors, ) + + @staticmethod + @callback + def async_get_options_flow( + config_entry: ConfigEntry, + ) -> PlugwiseOptionsFlowHandler: + """Get the options flow for this handler.""" + return PlugwiseOptionsFlowHandler(config_entry) + + +class PlugwiseUSBOptionsFlowHandler(OptionsFlow): + """Plugwise USB options flow.""" + + async def async_step_user( + self, user_input: dict[str, Any] | None = None + ) -> ConfigFlowResult: + """Handle the input of the plus-device MAC address.""" + errors: dict[str, str] = {} + if user_input is not None: + errors, mac = await validate_mac(user_input) # check if zb-mac address has a valid format + if not errors: + #execute pair_plus_device function - use CirclePlusConnectRequest + return self.async_show_form( + step_id=SOURCE_USER, + data_schema=vol.Schema({vol.Required(CONF_ZIGBEE_MAC): str}), + errors=errors, + ) From a0c37967442d66d0c60cb9a1e7283fc0bf8424dd Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 2 Feb 2026 08:44:23 +0100 Subject: [PATCH 02/20] Start adding pair_plus_device function --- custom_components/plugwise_usb/__init__.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/custom_components/plugwise_usb/__init__.py b/custom_components/plugwise_usb/__init__.py index 8a35c6b5..cce73c1c 100644 --- a/custom_components/plugwise_usb/__init__.py +++ b/custom_components/plugwise_usb/__init__.py @@ -133,6 +133,17 @@ async def disable_production(call: ServiceCall) -> bool: ) from exc return result + async def pair_plus_device(call: ServiceCall) -> bool: + """Pair a plus device.""" + mac = mac = call.data[ATTR_MAC] + try: + result = await api_stick.plus_pair_request(mac) + except (NodeError, StickError) as exc: + raise HomeAssistantError( + f"Pairing with Plus-device failed for {mac}: {exc}" + ) from exc + return result + hass.services.async_register( DOMAIN, SERVICE_ENABLE_PRODUCTION, enable_production, SERVICE_USB_DEVICE_SCHEMA ) From beeef652d774171429d6657e08369743bfa5c845 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 2 Feb 2026 16:11:14 +0100 Subject: [PATCH 03/20] Add validate_mac function --- custom_components/plugwise_usb/util.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 custom_components/plugwise_usb/util.py diff --git a/custom_components/plugwise_usb/util.py b/custom_components/plugwise_usb/util.py new file mode 100644 index 00000000..1745ced8 --- /dev/null +++ b/custom_components/plugwise_usb/util.py @@ -0,0 +1,19 @@ +"""Plugwise USB helper functions.""" + +from __future__ import annotations + +import re + +def validate_mac(mac: str) -> bool: + """Validate the supplied string is in a ZigBee MAC address format.""" + try: + if not re.match("^[A-F0-9]+$", mac): + return False + except TypeError: + return False + + try: + _ = int(mac, 16) + except ValueError: + return False + return True \ No newline at end of file From b089b172a0d47a6b953801940c78879ef173cb99 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 2 Feb 2026 16:48:53 +0100 Subject: [PATCH 04/20] Update config_flow --- custom_components/plugwise_usb/config_flow.py | 24 +++++++++++++++---- custom_components/plugwise_usb/strings.json | 10 ++++++++ custom_components/plugwise_usb/util.py | 3 ++- tests/conftest.py | 1 + 4 files changed, 32 insertions(+), 6 deletions(-) diff --git a/custom_components/plugwise_usb/config_flow.py b/custom_components/plugwise_usb/config_flow.py index 20fa480c..c2bb1bed 100644 --- a/custom_components/plugwise_usb/config_flow.py +++ b/custom_components/plugwise_usb/config_flow.py @@ -5,17 +5,24 @@ from typing import Any from plugwise_usb import Stick -from plugwise_usb.exceptions import StickError +from plugwise_usb.exceptions import NodeError, StickError import voluptuous as vol from homeassistant.components import usb -from homeassistant.config_entries import SOURCE_USER, ConfigEntry, ConfigFlow +from homeassistant.config_entries import ( + SOURCE_USER, + ConfigEntry, + ConfigFlow, + OptionsFlow, +) from homeassistant.const import CONF_BASE from homeassistant.core import callback from homeassistant.data_entry_flow import FlowResult +from homeassistant.exceptions import HomeAssistantError import serial.tools.list_ports from .const import CONF_MANUAL_PATH, CONF_USB_PATH, DOMAIN, MANUAL_PATH +from .util import validate_mac CONF_ZIGBEE_MAC: Final[str] = "zigbee_mac" @@ -143,9 +150,16 @@ async def async_step_user( """Handle the input of the plus-device MAC address.""" errors: dict[str, str] = {} if user_input is not None: - errors, mac = await validate_mac(user_input) # check if zb-mac address has a valid format - if not errors: - #execute pair_plus_device function - use CirclePlusConnectRequest + valid = await validate_mac(user_input) + if not valid: + try: + coordinator.api_stick.plus_pair_request(user_input) + except NodeError: + raise HomeAssistantError(f"Pairing of Plus-device {user_input} failed") + return None + + errors["init"] = "invalid mac" + return self.async_show_form( step_id=SOURCE_USER, data_schema=vol.Schema({vol.Required(CONF_ZIGBEE_MAC): str}), diff --git a/custom_components/plugwise_usb/strings.json b/custom_components/plugwise_usb/strings.json index bb1ecefc..c1c8943a 100644 --- a/custom_components/plugwise_usb/strings.json +++ b/custom_components/plugwise_usb/strings.json @@ -22,6 +22,16 @@ "stick_init": "Initialization of Plugwise USB-stick failed" } }, + "options": { + "step": { + "user": { + "data": { + "mac": " ZigBee MAC" + }, + "description": "Pair Plus-device, please enter:" + } + } + }, "services": { "enable_production":{ "name": "Enable production logging", diff --git a/custom_components/plugwise_usb/util.py b/custom_components/plugwise_usb/util.py index 1745ced8..cdc829c5 100644 --- a/custom_components/plugwise_usb/util.py +++ b/custom_components/plugwise_usb/util.py @@ -4,6 +4,7 @@ import re + def validate_mac(mac: str) -> bool: """Validate the supplied string is in a ZigBee MAC address format.""" try: @@ -16,4 +17,4 @@ def validate_mac(mac: str) -> bool: _ = int(mac, 16) except ValueError: return False - return True \ No newline at end of file + return True diff --git a/tests/conftest.py b/tests/conftest.py index 4bd5335b..2e2a5a6f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -11,6 +11,7 @@ from custom_components.plugwise_usb.const import CONF_USB_PATH, DOMAIN from homeassistant.core import HomeAssistant + from pytest_homeassistant_custom_component.common import MockConfigEntry TEST_MAC: Final[str] = "01:23:45:67:AB" From c85a892cb4326ccb327152c3d5104678a72bb763 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 2 Feb 2026 19:04:07 +0100 Subject: [PATCH 05/20] Fixes --- custom_components/plugwise_usb/__init__.py | 2 +- custom_components/plugwise_usb/config_flow.py | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/custom_components/plugwise_usb/__init__.py b/custom_components/plugwise_usb/__init__.py index cce73c1c..9f18e2a5 100644 --- a/custom_components/plugwise_usb/__init__.py +++ b/custom_components/plugwise_usb/__init__.py @@ -135,7 +135,7 @@ async def disable_production(call: ServiceCall) -> bool: async def pair_plus_device(call: ServiceCall) -> bool: """Pair a plus device.""" - mac = mac = call.data[ATTR_MAC] + mac = call.data[ATTR_MAC] try: result = await api_stick.plus_pair_request(mac) except (NodeError, StickError) as exc: diff --git a/custom_components/plugwise_usb/config_flow.py b/custom_components/plugwise_usb/config_flow.py index c2bb1bed..c7d01e7a 100644 --- a/custom_components/plugwise_usb/config_flow.py +++ b/custom_components/plugwise_usb/config_flow.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Any +from typing import Any, Final from plugwise_usb import Stick from plugwise_usb.exceptions import NodeError, StickError @@ -13,6 +13,7 @@ SOURCE_USER, ConfigEntry, ConfigFlow, + ConfigFlowResult, OptionsFlow, ) from homeassistant.const import CONF_BASE @@ -136,9 +137,9 @@ async def async_step_manual_path( @callback def async_get_options_flow( config_entry: ConfigEntry, - ) -> PlugwiseOptionsFlowHandler: + ) -> PlugwiseUSBOptionsFlowHandler: """Get the options flow for this handler.""" - return PlugwiseOptionsFlowHandler(config_entry) + return PlugwiseUSBOptionsFlowHandler(config_entry) class PlugwiseUSBOptionsFlowHandler(OptionsFlow): @@ -148,14 +149,15 @@ async def async_step_user( self, user_input: dict[str, Any] | None = None ) -> ConfigFlowResult: """Handle the input of the plus-device MAC address.""" + coordinator = self.config_entry.runtime_data errors: dict[str, str] = {} if user_input is not None: valid = await validate_mac(user_input) if not valid: try: coordinator.api_stick.plus_pair_request(user_input) - except NodeError: - raise HomeAssistantError(f"Pairing of Plus-device {user_input} failed") + except NodeError as exc: + raise HomeAssistantError(f"Pairing of Plus-device {user_input} failed") from exc return None errors["init"] = "invalid mac" From ac4970fe99522264819ee25ac4567f30b615d9b0 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 2 Feb 2026 19:10:58 +0100 Subject: [PATCH 06/20] Focus on options-flow --- custom_components/plugwise_usb/__init__.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/custom_components/plugwise_usb/__init__.py b/custom_components/plugwise_usb/__init__.py index 9f18e2a5..8a35c6b5 100644 --- a/custom_components/plugwise_usb/__init__.py +++ b/custom_components/plugwise_usb/__init__.py @@ -133,17 +133,6 @@ async def disable_production(call: ServiceCall) -> bool: ) from exc return result - async def pair_plus_device(call: ServiceCall) -> bool: - """Pair a plus device.""" - mac = call.data[ATTR_MAC] - try: - result = await api_stick.plus_pair_request(mac) - except (NodeError, StickError) as exc: - raise HomeAssistantError( - f"Pairing with Plus-device failed for {mac}: {exc}" - ) from exc - return result - hass.services.async_register( DOMAIN, SERVICE_ENABLE_PRODUCTION, enable_production, SERVICE_USB_DEVICE_SCHEMA ) From 51622a8300ca357107b4180d018285fc525cc86a Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 2 Feb 2026 19:19:20 +0100 Subject: [PATCH 07/20] Test with LOGGER.debug() --- custom_components/plugwise_usb/config_flow.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/custom_components/plugwise_usb/config_flow.py b/custom_components/plugwise_usb/config_flow.py index c7d01e7a..e07a6235 100644 --- a/custom_components/plugwise_usb/config_flow.py +++ b/custom_components/plugwise_usb/config_flow.py @@ -22,7 +22,7 @@ from homeassistant.exceptions import HomeAssistantError import serial.tools.list_ports -from .const import CONF_MANUAL_PATH, CONF_USB_PATH, DOMAIN, MANUAL_PATH +from .const import CONF_MANUAL_PATH, CONF_USB_PATH, DOMAIN, LOGGER, MANUAL_PATH from .util import validate_mac CONF_ZIGBEE_MAC: Final[str] = "zigbee_mac" @@ -147,15 +147,15 @@ class PlugwiseUSBOptionsFlowHandler(OptionsFlow): async def async_step_user( self, user_input: dict[str, Any] | None = None - ) -> ConfigFlowResult: + ) -> ConfigFlowResult | None: """Handle the input of the plus-device MAC address.""" coordinator = self.config_entry.runtime_data errors: dict[str, str] = {} if user_input is not None: - valid = await validate_mac(user_input) - if not valid: + if validate_mac(user_input): try: - coordinator.api_stick.plus_pair_request(user_input) + # coordinator.api_stick.plus_pair_request(user_input) + LOGGER.debug("Fake call to api_stick.plus_pair_request with %s", user_input) except NodeError as exc: raise HomeAssistantError(f"Pairing of Plus-device {user_input} failed") from exc return None From 97c9dc140e551bd8a6bbc6f8d7eccce6416758b3 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 2 Feb 2026 19:21:21 +0100 Subject: [PATCH 08/20] Disable unused --- custom_components/plugwise_usb/config_flow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/plugwise_usb/config_flow.py b/custom_components/plugwise_usb/config_flow.py index e07a6235..dc7a4120 100644 --- a/custom_components/plugwise_usb/config_flow.py +++ b/custom_components/plugwise_usb/config_flow.py @@ -149,7 +149,7 @@ async def async_step_user( self, user_input: dict[str, Any] | None = None ) -> ConfigFlowResult | None: """Handle the input of the plus-device MAC address.""" - coordinator = self.config_entry.runtime_data + # coordinator = self.config_entry.runtime_data errors: dict[str, str] = {} if user_input is not None: if validate_mac(user_input): From f644aee5639a23f2d4a718d42a5bb9945571c026 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 3 Feb 2026 11:43:03 +0100 Subject: [PATCH 09/20] Add init to options_flow --- custom_components/plugwise_usb/config_flow.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/custom_components/plugwise_usb/config_flow.py b/custom_components/plugwise_usb/config_flow.py index dc7a4120..cb441ff3 100644 --- a/custom_components/plugwise_usb/config_flow.py +++ b/custom_components/plugwise_usb/config_flow.py @@ -145,16 +145,20 @@ def async_get_options_flow( class PlugwiseUSBOptionsFlowHandler(OptionsFlow): """Plugwise USB options flow.""" + def __init__(self, config_entry: ConfigEntry) -> None: + """Initialize options flow.""" + self.coordinator = self.config_entry.runtime_data + async def async_step_user( self, user_input: dict[str, Any] | None = None ) -> ConfigFlowResult | None: """Handle the input of the plus-device MAC address.""" - # coordinator = self.config_entry.runtime_data + errors: dict[str, str] = {} if user_input is not None: if validate_mac(user_input): try: - # coordinator.api_stick.plus_pair_request(user_input) + # self.coordinator.api_stick.plus_pair_request(user_input) LOGGER.debug("Fake call to api_stick.plus_pair_request with %s", user_input) except NodeError as exc: raise HomeAssistantError(f"Pairing of Plus-device {user_input} failed") from exc From 3981b19a2549e4fb62ff693ad5ddd48b7284dd34 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 9 Feb 2026 13:18:23 +0100 Subject: [PATCH 10/20] OptionFlow updates: enter mac address, no error at wrong entry --- custom_components/plugwise_usb/config_flow.py | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/custom_components/plugwise_usb/config_flow.py b/custom_components/plugwise_usb/config_flow.py index cb441ff3..bbc61775 100644 --- a/custom_components/plugwise_usb/config_flow.py +++ b/custom_components/plugwise_usb/config_flow.py @@ -2,6 +2,7 @@ from __future__ import annotations +from copy import deepcopy from typing import Any, Final from plugwise_usb import Stick @@ -10,7 +11,6 @@ from homeassistant.components import usb from homeassistant.config_entries import ( - SOURCE_USER, ConfigEntry, ConfigFlow, ConfigFlowResult, @@ -23,10 +23,14 @@ import serial.tools.list_ports from .const import CONF_MANUAL_PATH, CONF_USB_PATH, DOMAIN, LOGGER, MANUAL_PATH +from .coordinator import PlugwiseUSBDataUpdateCoordinator from .util import validate_mac +type PlugwiseUSBConfigEntry = ConfigEntry[PlugwiseUSBDataUpdateCoordinator] + CONF_ZIGBEE_MAC: Final[str] = "zigbee_mac" + @callback def plugwise_stick_entries(hass): """Return existing connections for Plugwise USB-stick domain.""" @@ -136,7 +140,7 @@ async def async_step_manual_path( @staticmethod @callback def async_get_options_flow( - config_entry: ConfigEntry, + config_entry: PlugwiseUSBConfigEntry ) -> PlugwiseUSBOptionsFlowHandler: """Get the options flow for this handler.""" return PlugwiseUSBOptionsFlowHandler(config_entry) @@ -147,27 +151,30 @@ class PlugwiseUSBOptionsFlowHandler(OptionsFlow): def __init__(self, config_entry: ConfigEntry) -> None: """Initialize options flow.""" - self.coordinator = self.config_entry.runtime_data + pass + # self.data = deepcopy(dict(config_entry.data)) - async def async_step_user( + async def async_step_init( self, user_input: dict[str, Any] | None = None ) -> ConfigFlowResult | None: """Handle the input of the plus-device MAC address.""" - + # coordinator = self.config_entry.runtime_data errors: dict[str, str] = {} if user_input is not None: - if validate_mac(user_input): + mac = user_input["zigbee_mac"] + if validate_mac(mac): try: - # self.coordinator.api_stick.plus_pair_request(user_input) - LOGGER.debug("Fake call to api_stick.plus_pair_request with %s", user_input) + # self.coordinator.api_stick.plus_pair_request(mac) + LOGGER.warning("Fake call to api_stick.plus_pair_request with %s", mac) except NodeError as exc: - raise HomeAssistantError(f"Pairing of Plus-device {user_input} failed") from exc - return None + raise HomeAssistantError(f"Pairing of Plus-device {mac} failed") from exc + return self.async_create_entry(title="", data=user_input) - errors["init"] = "invalid mac" + errors["init"] = "invalid_mac" return self.async_show_form( - step_id=SOURCE_USER, + step_id="init", data_schema=vol.Schema({vol.Required(CONF_ZIGBEE_MAC): str}), errors=errors, ) + From d5ecc53d41bd0ec4c69096e3532229289cb81b3d Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 9 Feb 2026 13:19:05 +0100 Subject: [PATCH 11/20] Correct validate_mac() --- custom_components/plugwise_usb/util.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/custom_components/plugwise_usb/util.py b/custom_components/plugwise_usb/util.py index cdc829c5..863bb6dc 100644 --- a/custom_components/plugwise_usb/util.py +++ b/custom_components/plugwise_usb/util.py @@ -4,7 +4,6 @@ import re - def validate_mac(mac: str) -> bool: """Validate the supplied string is in a ZigBee MAC address format.""" try: @@ -13,8 +12,6 @@ def validate_mac(mac: str) -> bool: except TypeError: return False - try: - _ = int(mac, 16) - except ValueError: + if len(mac) != 16: return False - return True + return True \ No newline at end of file From 459bc3748856e894677bc2c972abdde7df027a9b Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 9 Feb 2026 13:19:41 +0100 Subject: [PATCH 12/20] Update strings.json --- custom_components/plugwise_usb/strings.json | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/custom_components/plugwise_usb/strings.json b/custom_components/plugwise_usb/strings.json index c1c8943a..a9952a68 100644 --- a/custom_components/plugwise_usb/strings.json +++ b/custom_components/plugwise_usb/strings.json @@ -24,12 +24,16 @@ }, "options": { "step": { - "user": { + "init": { + "title": "Pair Plus-device", + "description": "Please enter the ZigBee MAC address:", "data": { - "mac": " ZigBee MAC" - }, - "description": "Pair Plus-device, please enter:" + "zigbee_mac": "16-bit MAC" + } } + }, + "error": { + "invalid_mac": "MAC is invalid, please retry" } }, "services": { @@ -245,3 +249,4 @@ } } } + From 744c8c60983ad349b59f316cf8ad351e0620c8a3 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Mon, 9 Feb 2026 13:36:44 +0100 Subject: [PATCH 13/20] Update __init__.py --- custom_components/plugwise_usb/__init__.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/custom_components/plugwise_usb/__init__.py b/custom_components/plugwise_usb/__init__.py index 8a35c6b5..c6d21ad8 100644 --- a/custom_components/plugwise_usb/__init__.py +++ b/custom_components/plugwise_usb/__init__.py @@ -104,7 +104,10 @@ async def async_node_discovered(node_event: NodeEvent, mac: str) -> None: await api_stick.discover_coordinator(load=False) except StickError as exc: await api_stick.disconnect() - raise ConfigEntryNotReady("Failed to connect to Circle+") from exc + # raise ConfigEntryNotReady("Failed to connect to Circle+") from exc + _LOGGER.warning("Failed to connect to Circle+") + + _LOGGER.info("Discovery of the Plugwise network coordinator has finished") # Load platforms to allow them to register for node events await hass.config_entries.async_forward_entry_setups( @@ -152,9 +155,11 @@ async def disable_production(call: ServiceCall) -> bool: while True: await asyncio.sleep(1) + _LOGGER.debug("Discovering network...") if api_stick.network_discovered: break + _LOGGER.debug("INIT done.") return True From 1ee6d455d138a33ec2dc1e089a733ef219658db1 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 10 Feb 2026 09:55:48 +0100 Subject: [PATCH 14/20] Fix error handling --- custom_components/plugwise_usb/config_flow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/plugwise_usb/config_flow.py b/custom_components/plugwise_usb/config_flow.py index bbc61775..16a10d1e 100644 --- a/custom_components/plugwise_usb/config_flow.py +++ b/custom_components/plugwise_usb/config_flow.py @@ -170,7 +170,7 @@ async def async_step_init( raise HomeAssistantError(f"Pairing of Plus-device {mac} failed") from exc return self.async_create_entry(title="", data=user_input) - errors["init"] = "invalid_mac" + errors[CONF_BASE] = "invalid_mac" return self.async_show_form( step_id="init", From 37569ccd23bae75fe16c2149198c0c390483d552 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 10 Feb 2026 09:56:58 +0100 Subject: [PATCH 15/20] Revert removing raise --- custom_components/plugwise_usb/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/custom_components/plugwise_usb/__init__.py b/custom_components/plugwise_usb/__init__.py index c6d21ad8..bab857ce 100644 --- a/custom_components/plugwise_usb/__init__.py +++ b/custom_components/plugwise_usb/__init__.py @@ -104,8 +104,7 @@ async def async_node_discovered(node_event: NodeEvent, mac: str) -> None: await api_stick.discover_coordinator(load=False) except StickError as exc: await api_stick.disconnect() - # raise ConfigEntryNotReady("Failed to connect to Circle+") from exc - _LOGGER.warning("Failed to connect to Circle+") + raise ConfigEntryNotReady("Failed to connect to Circle+") from exc _LOGGER.info("Discovery of the Plugwise network coordinator has finished") From bc0e70b2c3ab78b29bb364eee90b827c6085d87c Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 13 Feb 2026 19:33:05 +0100 Subject: [PATCH 16/20] Link to v0.48.0a1, bump to v0.59.0a0 test-version --- custom_components/plugwise_usb/manifest.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/custom_components/plugwise_usb/manifest.json b/custom_components/plugwise_usb/manifest.json index d822359d..44c18f28 100644 --- a/custom_components/plugwise_usb/manifest.json +++ b/custom_components/plugwise_usb/manifest.json @@ -9,6 +9,6 @@ "iot_class": "local_polling", "issue_tracker": "https://github.com/plugwise/python-plugwise-usb/issues", "loggers": ["plugwise_usb"], - "requirements": ["plugwise-usb==0.47.2"], - "version": "0.58.2" + "requirements": ["plugwise-usb@git+https://github.com/plugwise/python-plugwise-usb@pair-plus#plugwise-usb==0.48.0a1"], + "version": "0.59.0a0" } From f9c85aad818be79f757f2946cb3da77f7b3acf5f Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 13 Feb 2026 19:36:11 +0100 Subject: [PATCH 17/20] Link to pair-plus-request() --- custom_components/plugwise_usb/config_flow.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/custom_components/plugwise_usb/config_flow.py b/custom_components/plugwise_usb/config_flow.py index 16a10d1e..0420cd49 100644 --- a/custom_components/plugwise_usb/config_flow.py +++ b/custom_components/plugwise_usb/config_flow.py @@ -151,21 +151,18 @@ class PlugwiseUSBOptionsFlowHandler(OptionsFlow): def __init__(self, config_entry: ConfigEntry) -> None: """Initialize options flow.""" - pass - # self.data = deepcopy(dict(config_entry.data)) + self.coordinator = self.config_entry.runtime_data async def async_step_init( self, user_input: dict[str, Any] | None = None ) -> ConfigFlowResult | None: """Handle the input of the plus-device MAC address.""" - # coordinator = self.config_entry.runtime_data errors: dict[str, str] = {} if user_input is not None: mac = user_input["zigbee_mac"] if validate_mac(mac): try: - # self.coordinator.api_stick.plus_pair_request(mac) - LOGGER.warning("Fake call to api_stick.plus_pair_request with %s", mac) + self.coordinator.api_stick.plus_pair_request(mac) except NodeError as exc: raise HomeAssistantError(f"Pairing of Plus-device {mac} failed") from exc return self.async_create_entry(title="", data=user_input) From 3bfc6680c613c9ddcf16746c32dd45bab4f5c8c2 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 13 Feb 2026 19:38:30 +0100 Subject: [PATCH 18/20] Import SOURCE_USER --- custom_components/plugwise_usb/config_flow.py | 1 + 1 file changed, 1 insertion(+) diff --git a/custom_components/plugwise_usb/config_flow.py b/custom_components/plugwise_usb/config_flow.py index 0420cd49..124ab60d 100644 --- a/custom_components/plugwise_usb/config_flow.py +++ b/custom_components/plugwise_usb/config_flow.py @@ -11,6 +11,7 @@ from homeassistant.components import usb from homeassistant.config_entries import ( + SOURCE_USER, ConfigEntry, ConfigFlow, ConfigFlowResult, From 7597daaade1035d417b23150ce031df3658d32e7 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 13 Feb 2026 19:39:24 +0100 Subject: [PATCH 19/20] Clean up --- custom_components/plugwise_usb/config_flow.py | 3 +-- custom_components/plugwise_usb/util.py | 3 ++- tests/conftest.py | 1 - 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/custom_components/plugwise_usb/config_flow.py b/custom_components/plugwise_usb/config_flow.py index 124ab60d..01177269 100644 --- a/custom_components/plugwise_usb/config_flow.py +++ b/custom_components/plugwise_usb/config_flow.py @@ -2,7 +2,6 @@ from __future__ import annotations -from copy import deepcopy from typing import Any, Final from plugwise_usb import Stick @@ -23,7 +22,7 @@ from homeassistant.exceptions import HomeAssistantError import serial.tools.list_ports -from .const import CONF_MANUAL_PATH, CONF_USB_PATH, DOMAIN, LOGGER, MANUAL_PATH +from .const import CONF_MANUAL_PATH, CONF_USB_PATH, DOMAIN, MANUAL_PATH from .coordinator import PlugwiseUSBDataUpdateCoordinator from .util import validate_mac diff --git a/custom_components/plugwise_usb/util.py b/custom_components/plugwise_usb/util.py index 863bb6dc..e0aaadc1 100644 --- a/custom_components/plugwise_usb/util.py +++ b/custom_components/plugwise_usb/util.py @@ -4,6 +4,7 @@ import re + def validate_mac(mac: str) -> bool: """Validate the supplied string is in a ZigBee MAC address format.""" try: @@ -14,4 +15,4 @@ def validate_mac(mac: str) -> bool: if len(mac) != 16: return False - return True \ No newline at end of file + return True diff --git a/tests/conftest.py b/tests/conftest.py index 2e2a5a6f..4bd5335b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -11,7 +11,6 @@ from custom_components.plugwise_usb.const import CONF_USB_PATH, DOMAIN from homeassistant.core import HomeAssistant - from pytest_homeassistant_custom_component.common import MockConfigEntry TEST_MAC: Final[str] = "01:23:45:67:AB" From 2a754c103afd13ee9e167af117e3ee1dd3765030 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 13 Feb 2026 19:47:29 +0100 Subject: [PATCH 20/20] Update CHANGELOG --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5588bdbd..75e9af57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## v0.59.0(a0) + +- Test new feature: pairing of Plus-device (untested!!) + ## v0.58.2 - 2026-01-29 - Update plugwise_usb to [v0.47.2](https://github.com/plugwise/python-plugwise-usb/releases/tag/v0.47.2) fixing a bug.