diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f9f1439..8d4b8783 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,8 @@ # Changelog -## Ongoing +## v0.60.0(a0) +- Test new feature: pairing of Plus-device (untested!!) - Improve raise-message for no paired Plus-device, via PR[#399](https://github.com/plugwise/plugwise_usb-beta/pull/399) ## v0.59.3 diff --git a/custom_components/plugwise_usb/__init__.py b/custom_components/plugwise_usb/__init__.py index a54b4c0d..7573eec3 100644 --- a/custom_components/plugwise_usb/__init__.py +++ b/custom_components/plugwise_usb/__init__.py @@ -105,6 +105,8 @@ async def async_node_discovered(node_event: NodeEvent, mac: str) -> None: await api_stick.disconnect() raise ConfigEntryNotReady("No connected Plus-device found, pair with one first e.g. via Source") from exc + _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( config_entry, PLUGWISE_USB_PLATFORMS @@ -151,9 +153,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 diff --git a/custom_components/plugwise_usb/config_flow.py b/custom_components/plugwise_usb/config_flow.py index ad0cc527..bf8ea620 100644 --- a/custom_components/plugwise_usb/config_flow.py +++ b/custom_components/plugwise_usb/config_flow.py @@ -1,18 +1,31 @@ """Config flow for Plugwise USB integration.""" -from typing import Any +from typing import Any, Final 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, ConfigFlow, ConfigFlowResult +from homeassistant.config_entries import ( + SOURCE_USER, + ConfigEntry, + ConfigFlow, + ConfigFlowResult, + OptionsFlow, +) from homeassistant.const import CONF_BASE from homeassistant.core import callback from homeassistant.data_entry_flow import FlowResult +from homeassistant.exceptions import HomeAssistantError from .const import CONF_MANUAL_PATH, CONF_USB_PATH, DOMAIN, MANUAL_PATH +from .coordinator import PlugwiseUSBDataUpdateCoordinator +from .util import validate_mac + +type PlugwiseUSBConfigEntry = ConfigEntry[PlugwiseUSBDataUpdateCoordinator] + +CONF_ZIGBEE_MAC: Final[str] = "zigbee_mac" STICK_RECONF_SCHEMA = vol.Schema( { @@ -164,3 +177,42 @@ async def async_step_reconfigure( description_placeholders={"title": reconfigure_entry.title}, errors=errors, ) + + @staticmethod + @callback + def async_get_options_flow( + config_entry: PlugwiseUSBConfigEntry + ) -> PlugwiseUSBOptionsFlowHandler: + """Get the options flow for this handler.""" + return PlugwiseUSBOptionsFlowHandler(config_entry) + + +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_init( + self, user_input: dict[str, Any] | None = None + ) -> ConfigFlowResult | None: + """Handle the input of the plus-device MAC address.""" + 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) + except NodeError as exc: + raise HomeAssistantError(f"Pairing of Plus-device {mac} failed") from exc + return self.async_create_entry(title="", data=user_input) + + errors[CONF_BASE] = "invalid_mac" + + return self.async_show_form( + step_id="init", + data_schema=vol.Schema({vol.Required(CONF_ZIGBEE_MAC): str}), + errors=errors, + ) + diff --git a/custom_components/plugwise_usb/manifest.json b/custom_components/plugwise_usb/manifest.json index ace55a6e..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.8"], - "version": "0.59.3" + "requirements": ["plugwise-usb@git+https://github.com/plugwise/python-plugwise-usb@pair-plus#plugwise-usb==0.48.0a1"], + "version": "0.59.0a0" } diff --git a/custom_components/plugwise_usb/strings.json b/custom_components/plugwise_usb/strings.json index 3f5dde0a..8f4025f0 100644 --- a/custom_components/plugwise_usb/strings.json +++ b/custom_components/plugwise_usb/strings.json @@ -32,6 +32,20 @@ "stick_init": "Initialization of Plugwise USB-stick failed" } }, + "options": { + "step": { + "init": { + "title": "Pair Plus-device", + "description": "Please enter the ZigBee MAC address:", + "data": { + "zigbee_mac": "16-bit MAC" + } + } + }, + "error": { + "invalid_mac": "MAC is invalid, please retry" + } + }, "services": { "enable_production":{ "name": "Enable production logging", @@ -245,3 +259,4 @@ } } } + diff --git a/custom_components/plugwise_usb/translations/en.json b/custom_components/plugwise_usb/translations/en.json index ac3b0a02..cfa66bd4 100644 --- a/custom_components/plugwise_usb/translations/en.json +++ b/custom_components/plugwise_usb/translations/en.json @@ -222,6 +222,20 @@ } } }, + "options": { + "error": { + "invalid_mac": "MAC is invalid, please retry" + }, + "step": { + "init": { + "data": { + "zigbee_mac": "16-bit MAC" + }, + "description": "Please enter the ZigBee MAC address:", + "title": "Pair Plus-device" + } + } + }, "services": { "disable_production": { "description": "Enter the mac of the Node: (data = mac: 0123456789ABCDEF)", diff --git a/custom_components/plugwise_usb/translations/nl.json b/custom_components/plugwise_usb/translations/nl.json index 12148eb0..7d724e59 100644 --- a/custom_components/plugwise_usb/translations/nl.json +++ b/custom_components/plugwise_usb/translations/nl.json @@ -32,6 +32,20 @@ "stick_init": "Initaliseren van USB-stick mislukt" } }, + "options": { + "step": { + "init": { + "title": "Pair Plus-apparaat", + "description": "Voer het ZigBee MAC adres in:", + "data": { + "zigbee_mac": "16-bit MAC" + } + } + }, + "error": { + "invalid_mac": "Ongeldig MAC adres, voer een geldig adres in" + } + }, "services": { "enable_production":{ "name": "Zet productie-loggen aan", diff --git a/custom_components/plugwise_usb/util.py b/custom_components/plugwise_usb/util.py new file mode 100644 index 00000000..95fd108b --- /dev/null +++ b/custom_components/plugwise_usb/util.py @@ -0,0 +1,16 @@ +"""Plugwise USB helper functions.""" + +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 + + if len(mac) != 16: + return False + return True