Skip to content

Commit

Permalink
Add block device update type (#413)
Browse files Browse the repository at this point in the history
  • Loading branch information
thecode authored Aug 16, 2023
1 parent 9ab6835 commit 2fe6d00
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 32 deletions.
12 changes: 6 additions & 6 deletions aioshelly/block_device/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Shelly Gen1 CoAP block based device."""

from .coap import COAP
from .device import BLOCK_VALUE_UNIT, Block, BlockDevice

__all__ = ["COAP", "BLOCK_VALUE_UNIT", "Block", "BlockDevice"]
"""Shelly Gen1 CoAP block based device."""

from .coap import COAP
from .device import BLOCK_VALUE_UNIT, Block, BlockDevice, BlockUpdateType

__all__ = ["COAP", "BLOCK_VALUE_UNIT", "Block", "BlockDevice", "BlockUpdateType"]
16 changes: 12 additions & 4 deletions aioshelly/block_device/coap.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import logging
import socket
import struct
from enum import Enum, auto
from types import TracebackType
from typing import Callable, cast

Expand All @@ -23,6 +24,13 @@ class InvalidMessage(CoapError):
"""Raised during COAP message parsing errors."""


class CoapType(Enum):
"""Coap message type."""

PERIODIC = auto()
REPLY = auto()


class CoapMessage:
"""Represents a received coap message."""

Expand All @@ -31,6 +39,7 @@ def __init__(self, sender_addr: tuple[str, int], payload: bytes) -> None:
self.ip = sender_addr[0]
self.port = sender_addr[1]
self.options: dict[int, bytes] = {}
self.coap_type = CoapType.REPLY

try:
self.vttkl, self.code, self.mid = struct.unpack("!BBH", payload[:4])
Expand Down Expand Up @@ -73,13 +82,12 @@ def __init__(self, sender_addr: tuple[str, int], payload: bytes) -> None:
) from err

if self.code == 30:
coap_type = "periodic"
else:
coap_type = "reply"
self.coap_type = CoapType.PERIODIC

_LOGGER.debug(
"CoapMessage: ip=%s, type=%s(%s), options=%s, payload=%s",
self.ip,
coap_type,
self.coap_type,
self.code,
self.options,
self.payload,
Expand Down
23 changes: 18 additions & 5 deletions aioshelly/block_device/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import asyncio
import logging
from enum import Enum, auto
from http import HTTPStatus
from typing import Any, Callable, cast

Expand All @@ -21,7 +22,7 @@
WrongShellyGen,
)
from ..json import json_loads
from .coap import COAP, CoapMessage
from .coap import COAP, CoapMessage, CoapType

BLOCK_VALUE_UNIT = "U"
BLOCK_VALUE_TYPE = "T"
Expand All @@ -44,6 +45,14 @@
_LOGGER = logging.getLogger(__name__)


class BlockUpdateType(Enum):
"""Block Update type."""

COAP_PERIODIC = auto()
COAP_REPLY = auto()
INITIALIZED = auto()


class BlockDevice:
"""Shelly block device representation."""

Expand Down Expand Up @@ -149,7 +158,7 @@ async def initialize(self, async_init: bool = False) -> None:
self._initializing = False

if self._update_listener:
self._update_listener(self)
self._update_listener(self, BlockUpdateType.INITIALIZED)

def shutdown(self) -> None:
"""Shutdown device."""
Expand All @@ -172,7 +181,7 @@ def _coap_message_received(self, msg: CoapMessage) -> None:
if not msg.payload:
return
if "G" in msg.payload:
self._update_s(msg.payload)
self._update_s(msg.payload, msg.coap_type)
path = "s"
elif "blk" in msg.payload:
self._update_d(msg.payload)
Expand Down Expand Up @@ -218,12 +227,16 @@ def _update_d(self, data: dict[str, Any]) -> None:

self.blocks = blocks

def _update_s(self, data: dict[str, Any]) -> None:
def _update_s(self, data: dict[str, Any], coap_type: CoapType) -> None:
"""Device update from cit/s call."""
self.coap_s = {info[1]: info[2] for info in data["G"]}

if self._update_listener and self.initialized:
self._update_listener(self)
if coap_type == CoapType.PERIODIC:
self._update_listener(self, BlockUpdateType.COAP_PERIODIC)
return

self._update_listener(self, BlockUpdateType.COAP_REPLY)

def subscribe_updates(self, update_listener: Callable) -> None:
"""Subscribe to device status updates."""
Expand Down
12 changes: 6 additions & 6 deletions aioshelly/rpc_device/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Shelly Gen2 RPC based device."""

from .device import RpcDevice, UpdateType
from .wsrpc import WsServer

__all__ = ["RpcDevice", "UpdateType", "WsServer"]
"""Shelly Gen2 RPC based device."""

from .device import RpcDevice, RpcUpdateType
from .wsrpc import WsServer

__all__ = ["RpcDevice", "RpcUpdateType", "WsServer"]
16 changes: 8 additions & 8 deletions aioshelly/rpc_device/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ def mergedicts(dict1: dict, dict2: dict) -> dict:
return result


class UpdateType(Enum):
"""Update type."""
class RpcUpdateType(Enum):
"""RPC Update type."""

EVENT = auto()
STATUS = auto()
Expand Down Expand Up @@ -107,19 +107,19 @@ def _on_notification(
self, method: str, params: dict[str, Any] | None = None
) -> None:
"""Received status notification from device."""
update_type = UpdateType.UNKNOWN
update_type = RpcUpdateType.UNKNOWN
if params is not None:
if method == "NotifyFullStatus":
self._status = params
update_type = UpdateType.STATUS
update_type = RpcUpdateType.STATUS
elif method == "NotifyStatus" and self._status is not None:
self._status = dict(mergedicts(self._status, params))
update_type = UpdateType.STATUS
update_type = RpcUpdateType.STATUS
elif method == "NotifyEvent":
self._event = params
update_type = UpdateType.EVENT
update_type = RpcUpdateType.EVENT
elif method == NOTIFY_WS_CLOSED:
update_type = UpdateType.DISCONNECTED
update_type = RpcUpdateType.DISCONNECTED

if not self._initializing and not self.initialized:
loop = asyncio.get_running_loop()
Expand Down Expand Up @@ -182,7 +182,7 @@ async def initialize(self, async_init: bool = False) -> None:
self._initializing = False

if self._update_listener and self.initialized:
self._update_listener(self, UpdateType.INITIALIZED)
self._update_listener(self, RpcUpdateType.INITIALIZED)

async def shutdown(self) -> None:
"""Shutdown device and remove the listener.
Expand Down
6 changes: 3 additions & 3 deletions example.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import aiohttp

import aioshelly
from aioshelly.block_device import BLOCK_VALUE_UNIT, COAP, BlockDevice
from aioshelly.block_device import BLOCK_VALUE_UNIT, COAP, BlockDevice, BlockUpdateType
from aioshelly.common import ConnectionOptions
from aioshelly.const import MODEL_NAMES, WS_API_URL
from aioshelly.exceptions import (
Expand All @@ -26,7 +26,7 @@
ShellyError,
WrongShellyGen,
)
from aioshelly.rpc_device import RpcDevice, UpdateType, WsServer
from aioshelly.rpc_device import RpcDevice, RpcUpdateType, WsServer

coap_context = COAP()
ws_context = WsServer()
Expand Down Expand Up @@ -135,7 +135,7 @@ async def connect_and_print_device(


def device_updated(
cb_device: BlockDevice | RpcDevice, update_type: UpdateType = UpdateType.UNKNOWN
cb_device: BlockDevice | RpcDevice, update_type: BlockUpdateType | RpcUpdateType
) -> None:
"""Device updated callback."""
print()
Expand Down

0 comments on commit 2fe6d00

Please sign in to comment.