Skip to content

Commit c2b1427

Browse files
authored
Merge pull request #234 from loopj/expose-status-type
Expose what status type a controller is using for state updates
2 parents dc18459 + a1662f6 commit c2b1427

File tree

3 files changed

+33
-13
lines changed

3 files changed

+33
-13
lines changed

src/aiovantage/_controllers/base.py

+30-11
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import asyncio
22
from collections.abc import Callable
33
from dataclasses import fields
4+
from enum import Enum
45
from typing import TYPE_CHECKING, TypeVar, cast
56

67
from aiovantage._logger import logger
@@ -25,13 +26,23 @@
2526
T = TypeVar("T", bound=SystemObject)
2627

2728

29+
class StatusType(Enum):
30+
"""The type of status that the controller is using for state monitoring."""
31+
32+
OBJECT = 1
33+
"""Object status events from the Enhanced Log."""
34+
35+
CATEGORY = 2
36+
"""Category status events, eg: S:LOAD, S:BLIND, etc."""
37+
38+
2839
class Controller(QuerySet[T], EventDispatcher):
2940
"""Base controller for managing collections of Vantage objects."""
3041

3142
vantage_types: tuple[str, ...]
3243
"""The Vantage object types that this controller will fetch."""
3344

34-
category_status: bool = False
45+
force_category_status: bool = False
3546
"""Whether to force the controller to handle 'STATUS' categories."""
3647

3748
def __init__(self, vantage: "Vantage") -> None:
@@ -43,8 +54,8 @@ def __init__(self, vantage: "Vantage") -> None:
4354
self._vantage = vantage
4455
self._objects: dict[int, T] = {}
4556
self._initialized = False
46-
self._state_monitoring_enabled = False
47-
self._state_monitoring_unsubs: list[Callable[[], None]] = []
57+
self._status_type: StatusType | None = None
58+
self._status_unsubs: list[Callable[[], None]] = []
4859
self._lock = asyncio.Lock()
4960

5061
QuerySet[T].__init__(self, self._objects, self._lazy_initialize)
@@ -58,6 +69,11 @@ def __contains__(self, vid: int) -> bool:
5869
"""Return True if the object with the given Vantage ID exists."""
5970
return vid in self._objects
6071

72+
@property
73+
def status_type(self) -> StatusType | None:
74+
"""Return the type of status event that the controller is monitoring."""
75+
return self._status_type
76+
6177
async def initialize(
6278
self, *, fetch_state: bool = True, enable_state_monitoring: bool = True
6379
) -> None:
@@ -141,7 +157,7 @@ async def fetch_state(self) -> None:
141157

142158
async def enable_state_monitoring(self) -> None:
143159
"""Monitor for state changes on objects managed by this controller."""
144-
if self._state_monitoring_enabled:
160+
if self._status_type is not None:
145161
return
146162

147163
# Start the event stream if it isn't already running
@@ -152,39 +168,42 @@ async def enable_state_monitoring(self) -> None:
152168
# If these are not supported—either due to older firmware, or if the
153169
# controller explicitly requesting category statuses, we'll fall back to
154170
# "category" status events.
155-
if event_conn.supports_enhanced_log and not self.category_status:
171+
if event_conn.supports_enhanced_log and not self.force_category_status:
156172
# Subscribe to "object status" events from the Enhanced Log.
157173
status_unsub = self._vantage.event_stream.subscribe_enhanced_log(
158174
self._handle_enhanced_log_event, "STATUS", "STATUSEX"
159175
)
176+
177+
self._status_type = StatusType.OBJECT
160178
else:
161179
# Subscribe to "STATUS {category}" updates
162180
status_unsub = self._vantage.event_stream.subscribe_status(
163181
self._handle_status_event
164182
)
165183

184+
self._status_type = StatusType.CATEGORY
185+
166186
# Subscribe to reconnect events from the event stream
167187
reconnect_unsub = self._vantage.event_stream.subscribe(
168188
Reconnected, self._handle_reconnect_event
169189
)
170190

171191
# Keep track of the subscriptions so we can unsubscribe later
172-
self._state_monitoring_unsubs.extend([status_unsub, reconnect_unsub])
173-
self._state_monitoring_enabled = True
192+
self._status_unsubs.extend([status_unsub, reconnect_unsub])
174193

175194
logger.info("%s subscribed to state changes", type(self).__name__)
176195

177196
async def disable_state_monitoring(self) -> None:
178197
"""Stop monitoring for state changes on objects managed by this controller."""
179-
if not self._state_monitoring_enabled:
198+
if self._status_type is None:
180199
return
181200

182201
# Unsubscribe status and reconnect events
183-
while self._state_monitoring_unsubs:
184-
unsub = self._state_monitoring_unsubs.pop()
202+
while self._status_unsubs:
203+
unsub = self._status_unsubs.pop()
185204
unsub()
186205

187-
self._state_monitoring_enabled = False
206+
self._status_type = None
188207

189208
logger.info("%s unsubscribed from state changes", type(self).__name__)
190209

src/aiovantage/_controllers/gmem.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ class GMemController(Controller[GMem]):
77
"""GMem (variables) controller."""
88

99
vantage_types = ("GMem",)
10-
category_status = True
10+
force_category_status = True

src/aiovantage/controllers.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
from ._controllers.anemo_sensors import AnemoSensorsController
3131
from ._controllers.areas import AreasController
3232
from ._controllers.back_boxes import BackBoxesController
33-
from ._controllers.base import Controller
33+
from ._controllers.base import Controller, StatusType
3434
from ._controllers.blind_groups import BlindGroupsController, BlindGroupTypes
3535
from ._controllers.blinds import BlindsController, BlindTypes
3636
from ._controllers.buttons import ButtonsController
@@ -75,6 +75,7 @@
7575
"RGBLoadsController",
7676
"RGBLoadTypes",
7777
"StationsController",
78+
"StatusType",
7879
"TasksController",
7980
"TemperaturesController",
8081
"ThermostatsController",

0 commit comments

Comments
 (0)