@@ -51,6 +51,68 @@ class _ComponentData(Generic[T]):
5151 receiver : Peekable [T ]
5252
5353
54+ @dataclass
55+ class _BlockingStatus :
56+ min_duration_sec : float
57+ max_duration_sec : float
58+
59+ def __post_init__ (self ):
60+ self .last_blocking_duration_sec : float = self .min_duration_sec
61+ self .blocked_until : Optional [datetime ] = None
62+
63+ def block (self ) -> float :
64+ """Block battery.
65+
66+ Battery can be unblocked using `self.unblock()` method.
67+
68+ Returns:
69+ For how long (in seconds) the battery is blocked.
70+ """
71+ now = datetime .now (tz = timezone .utc )
72+
73+ # If is not blocked
74+ if self .blocked_until is None :
75+ self .last_blocking_duration_sec = self .min_duration_sec
76+ self .blocked_until = now + timedelta (
77+ seconds = self .last_blocking_duration_sec
78+ )
79+ return self .last_blocking_duration_sec
80+
81+ # If still blocked, then do nothing
82+ if self .blocked_until > now :
83+ return 0.0
84+
85+ # If previous blocking time expired, then blocked it once again.
86+ # Increase last blocking time, unless it reach the maximum.
87+ self .last_blocking_duration_sec = min (
88+ 2 * self .last_blocking_duration_sec , self .max_duration_sec
89+ )
90+ self .blocked_until = now + timedelta (seconds = self .last_blocking_duration_sec )
91+
92+ return self .last_blocking_duration_sec
93+
94+ def unblock (self ):
95+ """Unblock battery.
96+
97+ This will reset duration of the next blocking timeout.
98+
99+ Battery can be blocked using `self.block()` method.
100+ """
101+ self .blocked_until = None
102+
103+ def is_blocked (self ) -> bool :
104+ """Return if battery is blocked.
105+
106+ Battery can be blocked if last request for that battery failed.
107+
108+ Returns:
109+ True if battery is blocked, False otherwise.
110+ """
111+ if self .blocked_until is None :
112+ return False
113+ return self .blocked_until > datetime .now (tz = timezone .utc )
114+
115+
54116class BatteryStatusTracker (AsyncConstructible ):
55117 """Class for tracking if battery is working.
56118
@@ -79,10 +141,7 @@ class BatteryStatusTracker(AsyncConstructible):
79141 _battery_id : int
80142 _max_data_age : float
81143 _last_status : BatteryStatus
82- _min_blocking_duration_sec : float
83- _max_blocking_duration_sec : float
84- _blocked_until : Optional [datetime ]
85- _last_blocking_duration_sec : float
144+ _blocking_status : _BlockingStatus
86145 _battery : _ComponentData [BatteryData ]
87146 _inverter : _ComponentData [InverterData ]
88147
@@ -112,10 +171,7 @@ async def async_new(
112171
113172 self ._last_status = BatteryStatus .WORKING
114173
115- self ._min_blocking_duration_sec = 1.0
116- self ._max_blocking_duration_sec = max_blocking_duration_sec
117- self ._blocked_until = None
118- self ._last_blocking_duration_sec = self ._min_blocking_duration_sec
174+ self ._blocking_status = _BlockingStatus (1.0 , max_blocking_duration_sec )
119175
120176 inverter_id = self ._find_adjacent_inverter_id (battery_id )
121177 if inverter_id is None :
@@ -200,9 +256,7 @@ def is_blocked(self) -> bool:
200256 Returns:
201257 True if battery is blocked, False otherwise.
202258 """
203- if self ._blocked_until is None :
204- return False
205- return self ._blocked_until > datetime .now (tz = timezone .utc )
259+ return self ._blocking_status .is_blocked ()
206260
207261 def unblock (self ) -> None :
208262 """Unblock battery.
@@ -211,7 +265,7 @@ def unblock(self) -> None:
211265
212266 Battery can be blocked using `self.block()` method.
213267 """
214- self ._blocked_until = None
268+ self ._blocking_status . unblock ()
215269
216270 def block (self ) -> float :
217271 """Block battery.
@@ -221,26 +275,7 @@ def block(self) -> float:
221275 Returns:
222276 For how long (in seconds) the battery is blocked.
223277 """
224- now = datetime .now (tz = timezone .utc )
225-
226- if self ._blocked_until is None :
227- self ._last_blocking_duration_sec = self ._min_blocking_duration_sec
228- self ._blocked_until = now + timedelta (
229- seconds = self ._last_blocking_duration_sec
230- )
231- return self ._last_blocking_duration_sec
232-
233- # If still blocked, then do nothing
234- if self ._blocked_until > now :
235- return 0.0
236-
237- # Increase blocking duration twice or until it reach the max.
238- self ._last_blocking_duration_sec = min (
239- 2 * self ._last_blocking_duration_sec , self ._max_blocking_duration_sec
240- )
241- self ._blocked_until = now + timedelta (seconds = self ._last_blocking_duration_sec )
242-
243- return self ._last_blocking_duration_sec
278+ return self ._blocking_status .block ()
244279
245280 @property
246281 def blocked_until (self ) -> Optional [datetime ]:
@@ -250,11 +285,7 @@ def blocked_until(self) -> Optional[datetime]:
250285 Timestamp when the battery will be unblocked. Return None if battery is
251286 not blocked.
252287 """
253- if self ._blocked_until is None or self ._blocked_until < datetime .now (
254- tz = timezone .utc
255- ):
256- return None
257- return self ._blocked_until
288+ return self ._blocking_status .blocked_until
258289
259290 def _no_critical_error (self , msg : Union [BatteryData , InverterData ]) -> bool :
260291 """Check if battery or inverter message has any critical error.
0 commit comments