1
1
import asyncio
2
2
from collections .abc import Callable
3
3
from dataclasses import fields
4
+ from enum import Enum
4
5
from typing import TYPE_CHECKING , TypeVar , cast
5
6
6
7
from aiovantage ._logger import logger
25
26
T = TypeVar ("T" , bound = SystemObject )
26
27
27
28
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
+
28
39
class Controller (QuerySet [T ], EventDispatcher ):
29
40
"""Base controller for managing collections of Vantage objects."""
30
41
31
42
vantage_types : tuple [str , ...]
32
43
"""The Vantage object types that this controller will fetch."""
33
44
34
- category_status : bool = False
45
+ force_category_status : bool = False
35
46
"""Whether to force the controller to handle 'STATUS' categories."""
36
47
37
48
def __init__ (self , vantage : "Vantage" ) -> None :
@@ -43,8 +54,8 @@ def __init__(self, vantage: "Vantage") -> None:
43
54
self ._vantage = vantage
44
55
self ._objects : dict [int , T ] = {}
45
56
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 ]] = []
48
59
self ._lock = asyncio .Lock ()
49
60
50
61
QuerySet [T ].__init__ (self , self ._objects , self ._lazy_initialize )
@@ -58,6 +69,11 @@ def __contains__(self, vid: int) -> bool:
58
69
"""Return True if the object with the given Vantage ID exists."""
59
70
return vid in self ._objects
60
71
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
+
61
77
async def initialize (
62
78
self , * , fetch_state : bool = True , enable_state_monitoring : bool = True
63
79
) -> None :
@@ -141,7 +157,7 @@ async def fetch_state(self) -> None:
141
157
142
158
async def enable_state_monitoring (self ) -> None :
143
159
"""Monitor for state changes on objects managed by this controller."""
144
- if self ._state_monitoring_enabled :
160
+ if self ._status_type is not None :
145
161
return
146
162
147
163
# Start the event stream if it isn't already running
@@ -152,39 +168,42 @@ async def enable_state_monitoring(self) -> None:
152
168
# If these are not supported—either due to older firmware, or if the
153
169
# controller explicitly requesting category statuses, we'll fall back to
154
170
# "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 :
156
172
# Subscribe to "object status" events from the Enhanced Log.
157
173
status_unsub = self ._vantage .event_stream .subscribe_enhanced_log (
158
174
self ._handle_enhanced_log_event , "STATUS" , "STATUSEX"
159
175
)
176
+
177
+ self ._status_type = StatusType .OBJECT
160
178
else :
161
179
# Subscribe to "STATUS {category}" updates
162
180
status_unsub = self ._vantage .event_stream .subscribe_status (
163
181
self ._handle_status_event
164
182
)
165
183
184
+ self ._status_type = StatusType .CATEGORY
185
+
166
186
# Subscribe to reconnect events from the event stream
167
187
reconnect_unsub = self ._vantage .event_stream .subscribe (
168
188
Reconnected , self ._handle_reconnect_event
169
189
)
170
190
171
191
# 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 ])
174
193
175
194
logger .info ("%s subscribed to state changes" , type (self ).__name__ )
176
195
177
196
async def disable_state_monitoring (self ) -> None :
178
197
"""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 :
180
199
return
181
200
182
201
# 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 ()
185
204
unsub ()
186
205
187
- self ._state_monitoring_enabled = False
206
+ self ._status_type = None
188
207
189
208
logger .info ("%s unsubscribed from state changes" , type (self ).__name__ )
190
209
0 commit comments