|
9 | 9 | from ..api import StickEvent |
10 | 10 | from ..constants import UTF8 |
11 | 11 | from ..exceptions import NodeError, StickError |
12 | | -from ..helpers.util import version_to_model |
| 12 | +from ..helpers.util import validate_mac, version_to_model |
13 | 13 | from ..messages.requests import ( |
| 14 | + CirclePlusConnectRequest, |
14 | 15 | NodeInfoRequest, |
15 | 16 | NodePingRequest, |
16 | 17 | PlugwiseRequest, |
17 | 18 | StickInitRequest, |
| 19 | + StickNetworkInfoRequest, |
18 | 20 | ) |
19 | 21 | from ..messages.responses import ( |
20 | 22 | NodeInfoResponse, |
@@ -202,6 +204,55 @@ async def initialize_stick(self) -> None: |
202 | 204 | if not self._network_online: |
203 | 205 | raise StickError("Zigbee network connection to Circle+ is down.") |
204 | 206 |
|
| 207 | + async def pair_plus_device(self, mac: str) -> bool: |
| 208 | + """Pair Plus-device to Plugwise Stick. |
| 209 | +
|
| 210 | + According to https://roheve.wordpress.com/author/roheve/page/2/ |
| 211 | + The pairing process should look like: |
| 212 | + 0001 - 0002 (- 0003): StickNetworkInfoRequest - StickNetworkInfoResponse - (PlugwiseQueryCirclePlusEndResponse - @SevenW), |
| 213 | + 000A - 0011: StickInitRequest - StickInitResponse, |
| 214 | + 0004 - 0005: CirclePlusConnectRequest - CirclePlusConnectResponse, |
| 215 | + the Plus-device will then send a NodeRejoinResponse (0061). |
| 216 | +
|
| 217 | + Todo(?): Does this need repeating until pairing is successful? |
| 218 | + """ |
| 219 | + _LOGGER.debug("Pair Plus-device with mac: %s", mac) |
| 220 | + if not validate_mac(mac): |
| 221 | + raise NodeError(f"Pairing failed: MAC {mac} invalid") |
| 222 | + |
| 223 | + # Collect network info |
| 224 | + try: |
| 225 | + request = StickNetworkInfoRequest(self.send, None) |
| 226 | + info_response = await request.send() |
| 227 | + except MessageError as exc: |
| 228 | + raise NodeError(f"Pairing failed: {exc}") from exc |
| 229 | + if info_response is None: |
| 230 | + raise NodeError( |
| 231 | + "Pairing failed, StickNetworkInfoResponse is None" |
| 232 | + ) from None |
| 233 | + |
| 234 | + # Init Stick |
| 235 | + try: |
| 236 | + await self.initialize_stick() |
| 237 | + except StickError as exc: |
| 238 | + raise NodeError( |
| 239 | + f"Pairing failed, failed to initialize Stick: {exc}" |
| 240 | + ) from exc |
| 241 | + |
| 242 | + try: |
| 243 | + request = CirclePlusConnectRequest(self.send, bytes(mac, UTF8)) |
| 244 | + response = await request.send() |
| 245 | + except MessageError as exc: |
| 246 | + raise NodeError(f"Pairing failed: {exc}") from exc |
| 247 | + if response is None: |
| 248 | + raise NodeError( |
| 249 | + "Pairing failed, CirclePlusConnectResponse is None" |
| 250 | + ) from None |
| 251 | + if response.allowed.value != 1: |
| 252 | + raise NodeError("Pairing failed, not allowed") |
| 253 | + |
| 254 | + return True |
| 255 | + |
205 | 256 | async def get_node_details( |
206 | 257 | self, mac: str, ping_first: bool |
207 | 258 | ) -> tuple[NodeInfoResponse | None, NodePingResponse | None]: |
|
0 commit comments