Skip to content

Commit ffbc1a3

Browse files
committed
chore: cleanup
1 parent 7e1d14f commit ffbc1a3

2 files changed

Lines changed: 31 additions & 32 deletions

File tree

‎src/uiprotect/data/public_devices.py‎

Lines changed: 6 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -169,37 +169,20 @@ def is_active(self) -> bool:
169169
170170
The server does not emit a stop event when the siren finishes its
171171
timed run, so ``sirenStatus.isActive`` in the WS payload stays
172-
``True`` until the next update. We therefore derive the real state
173-
from ``turn_off_at`` when it is available: if the calculated end time
174-
is still in the future the siren is still active; once it has passed
175-
the siren has stopped regardless of what the server field says.
172+
``True`` until the next update. We therefore AND the server flag
173+
with a clock check against ``turn_off_at``: a manual stop clears
174+
``isActive`` immediately, and a timed expiry is caught by the
175+
clock check even though the server flag is still ``True``.
176176
If no timing information is present we fall back to the raw server flag.
177177
"""
178178
turn_off_at = self.siren_status.turn_off_at
179179
if turn_off_at is not None:
180-
return datetime.now(UTC) < turn_off_at
180+
return self.siren_status.is_active and datetime.now(UTC) < turn_off_at
181181
return self.siren_status.is_active
182182

183-
def _normalize_siren_duration(
184-
self, duration: int | SirenDuration | None
185-
) -> SirenDuration:
186-
if duration is None:
187-
return SirenDuration.FIVE
188-
if isinstance(duration, SirenDuration):
189-
return duration
190-
try:
191-
return SirenDuration(duration)
192-
except ValueError as err:
193-
raise BadRequest(
194-
"duration must be one of the supported siren durations "
195-
f"{', '.join(str(item.value) for item in SirenDuration)} seconds"
196-
) from err
197-
198183
async def play(self, duration: int | SirenDuration | None = None) -> None:
199184
"""Play the siren. ``duration`` may be a supported integer or :class:`SirenDuration`; defaults to 5 seconds."""
200-
await self._api.play_siren_public(
201-
self.id, duration=self._normalize_siren_duration(duration)
202-
)
185+
await self._api.play_siren_public(self.id, duration=duration)
203186

204187
async def stop(self) -> None:
205188
"""Stop an active siren."""

‎tests/test_api_public.py‎

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,28 @@ def test_siren_model_from_unifi_dict() -> None:
453453
)
454454
assert siren_expired.is_active is False
455455

456+
# Manual stop before the timer elapsed: server clears isActive but may
457+
# leave activatedAt/duration populated. The clock check would still say
458+
# "active"; the server flag must win.
459+
future_at_ms = int((time.time() + 10) * 1000)
460+
siren_stopped = Siren.from_unifi_dict(
461+
id=SIREN_ID,
462+
modelKey="siren",
463+
state="CONNECTED",
464+
name="Front Siren",
465+
mac="AA:BB:CC:DD:EE:FF",
466+
volume=80,
467+
ledSettings={"isEnabled": True},
468+
sirenStatus={"isActive": False, "activatedAt": future_at_ms, "duration": 5},
469+
connectionType="lora",
470+
wirelessConnectionState={
471+
"signalState": {"signalQuality": 85, "signalStrength": -45},
472+
"batteryStatus": {"percentage": 90, "isLow": False},
473+
"bridge": None,
474+
},
475+
)
476+
assert siren_stopped.is_active is False
477+
456478

457479
def test_relay_model_from_unifi_dict() -> None:
458480
relay = Relay.from_unifi_dict(
@@ -1682,22 +1704,16 @@ async def test_siren_device_action_helpers(
16821704
assert siren.siren_status.turn_off_at is None
16831705
assert siren.is_active is False
16841706

1685-
# play() with no args: _normalize_siren_duration(None) → SirenDuration.FIVE
1707+
# play() forwards duration unchanged; play_siren_public is the single
1708+
# validation/normalization site (covered by test_play_siren_public_*).
16861709
await siren.play()
1687-
protect_client.play_siren_public.assert_awaited_with(
1688-
SIREN_ID, duration=SirenDuration.FIVE
1689-
)
1710+
protect_client.play_siren_public.assert_awaited_with(SIREN_ID, duration=None)
16901711

1691-
# play() with a SirenDuration enum: returned as-is
16921712
await siren.play(duration=SirenDuration.TEN)
16931713
protect_client.play_siren_public.assert_awaited_with(
16941714
SIREN_ID, duration=SirenDuration.TEN
16951715
)
16961716

1697-
# play() with an invalid int: _normalize_siren_duration raises BadRequest
1698-
with pytest.raises(BadRequest):
1699-
await siren.play(duration=99)
1700-
17011717
protect_client.play_siren_public.reset_mock()
17021718
await siren.play(duration=5)
17031719
protect_client.play_siren_public.assert_awaited_once_with(SIREN_ID, duration=5)

0 commit comments

Comments
 (0)