Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
4 changes: 4 additions & 0 deletions custom_components/plugwise_usb/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ async def async_node_discovered(node_event: NodeEvent, mac: str) -> None:
await api_stick.disconnect()
raise ConfigEntryNotReady("Failed to connect to Circle+") 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
Expand Down Expand Up @@ -152,9 +154,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


Expand Down
58 changes: 55 additions & 3 deletions custom_components/plugwise_usb/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,33 @@

from __future__ import annotations

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
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
import serial.tools.list_ports

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"


@callback
Expand Down Expand Up @@ -123,3 +136,42 @@ async def async_step_manual_path(
),
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,
)

4 changes: 2 additions & 2 deletions custom_components/plugwise_usb/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
15 changes: 15 additions & 0 deletions custom_components/plugwise_usb/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,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",
Expand Down Expand Up @@ -235,3 +249,4 @@
}
}
}

18 changes: 18 additions & 0 deletions custom_components/plugwise_usb/util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"""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

if len(mac) != 16:
return False
return True
Loading