Skip to content

Commit cdeb935

Browse files
committed
Add BetweenIntervalTriggerEvent and handle intermediate triggers
- Introduced BetweenIntervalTriggerEvent in timer events. - Updated ServiceStatusController to handle BetweenIntervalTriggerEvent and publish service statuses. - Enhanced Timer class to emit long interval events upon receiving BetweenIntervalTriggerEvent. - Added tests for the new event handling in ServiceStatusController and Timer. Signed-off-by: Johannes Ott <mail@johannes-ott.net>
1 parent 158c116 commit cdeb935

6 files changed

Lines changed: 103 additions & 0 deletions

File tree

solaredge2mqtt/core/status/__init__.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
ServiceOfflineEvent,
66
ServiceOnlineEvent,
77
)
8+
from solaredge2mqtt.core.timer.events import (
9+
BetweenIntervalTriggerEvent,
10+
)
811

912

1013
class ServiceStatusController:
@@ -79,6 +82,12 @@ async def _update_service_status(self, service_name: str, is_online: bool):
7982

8083
self._reset_debounce(service_name)
8184

85+
@EventBus.subscribe(BetweenIntervalTriggerEvent)
86+
async def handle_intermediate_trigger(self, event: BetweenIntervalTriggerEvent):
87+
logger.info("Resend status")
88+
for service_name, is_online in self._status.items():
89+
await self._publish_service_status(service_name, is_online)
90+
8291
async def _publish_service_status(self, service_name: str, is_online: bool):
8392
self._status[service_name] = is_online
8493
self._reset_debounce(service_name)

solaredge2mqtt/core/timer/__init__.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from solaredge2mqtt.core.events import EventBus
44
from solaredge2mqtt.core.timer.events import (
5+
BetweenIntervalTriggerEvent,
56
Interval1MinTriggerEvent,
67
Interval5MinTriggerEvent,
78
Interval10MinTriggerEvent,
@@ -39,3 +40,12 @@ async def loop(self) -> None:
3940

4041
if timestamp % 900 == 0:
4142
await EventBus.emit(Interval15MinTriggerEvent())
43+
44+
@EventBus.subscribe(BetweenIntervalTriggerEvent)
45+
async def on_between_interval_trigger(
46+
self, event: BetweenIntervalTriggerEvent
47+
) -> None:
48+
await EventBus.emit(Interval1MinTriggerEvent())
49+
await EventBus.emit(Interval5MinTriggerEvent())
50+
await EventBus.emit(Interval10MinTriggerEvent())
51+
await EventBus.emit(Interval15MinTriggerEvent())

solaredge2mqtt/core/timer/events.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
class IntervalBaseTriggerEvent(BaseEvent): ... # pragma: no cover
55

66

7+
class BetweenIntervalTriggerEvent(BaseEvent): ... # pragma: no cover
8+
9+
710
class Interval1MinTriggerEvent(BaseEvent): ... # pragma: no cover
811

912

solaredge2mqtt/services/homeassistant/service.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
from __future__ import annotations
22

3+
import asyncio
34
from typing import TYPE_CHECKING, Any
45

56
from solaredge2mqtt.core.events import EventBus
67
from solaredge2mqtt.core.logging import logger
78
from solaredge2mqtt.core.mqtt.events import (
89
MQTTPublishEvent,
910
)
11+
from solaredge2mqtt.core.timer.events import (
12+
BetweenIntervalTriggerEvent,
13+
)
1014
from solaredge2mqtt.services.energy.events import EnergyReadEvent
1115
from solaredge2mqtt.services.forecast.events import ForecastEvent
1216
from solaredge2mqtt.services.homeassistant.events import (
@@ -186,6 +190,9 @@ def availability_topic(self, component: Component) -> str:
186190

187191
@EventBus.subscribe(HomeAssistantStatusEvent)
188192
async def homeassistant_status(self, event: HomeAssistantStatusEvent) -> None:
193+
expected_topic = f"{self.settings.homeassistant.topic_prefix}/status"
194+
if event.topic != expected_topic:
195+
return
189196
if event.input.status == HomeAssistantStatus.ONLINE:
190197
logger.info("Home Assistant status changed to online resend discovery")
191198
for topic, entity in self._send_entities.items():
@@ -199,6 +206,11 @@ async def homeassistant_status(self, event: HomeAssistantStatusEvent) -> None:
199206
)
200207
)
201208

209+
await asyncio.sleep(10)
210+
211+
logger.info("Resending long interval triggered data to Home Assistant")
212+
await EventBus.emit(BetweenIntervalTriggerEvent())
213+
202214
@staticmethod
203215
def property_parser(prop, name: str, path: list[str]) -> dict | None:
204216
entity: dict | None = None

tests/core/status/test_service_status_controller.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
ServiceOfflineEvent,
1212
ServiceOnlineEvent,
1313
)
14+
from solaredge2mqtt.core.timer.events import BetweenIntervalTriggerEvent
1415

1516

1617
class TestServiceStatusControllerInit:
@@ -483,3 +484,33 @@ class Service2Offline(ServiceOfflineEvent):
483484
# Both should now be offline
484485
assert controller._status["service1"] is False
485486
assert controller._status["service2"] is False
487+
488+
489+
class TestServiceStatusControllerHandleIntermediateTrigger:
490+
"""Tests for ServiceStatusController.handle_intermediate_trigger."""
491+
492+
@pytest.mark.asyncio
493+
async def test_handle_intermediate_trigger_publishes_all_service_statuses(self):
494+
"""
495+
Test that BetweenIntervalTriggerEvent republishes
496+
all known service statuses.
497+
"""
498+
controller = ServiceStatusController()
499+
controller._status = {"modbus": True, "mqtt": False}
500+
501+
with patch.object(EventBus, "emit", new_callable=AsyncMock) as mock_emit:
502+
await controller.handle_intermediate_trigger(BetweenIntervalTriggerEvent())
503+
504+
emitted_topics = [call.args[0].topic for call in mock_emit.call_args_list]
505+
assert "status/modbus" in emitted_topics
506+
assert "status/mqtt" in emitted_topics
507+
508+
@pytest.mark.asyncio
509+
async def test_handle_intermediate_trigger_empty_status_emits_nothing(self):
510+
"""Test that handle_intermediate_trigger with no services emits nothing."""
511+
controller = ServiceStatusController()
512+
513+
with patch.object(EventBus, "emit", new_callable=AsyncMock) as mock_emit:
514+
await controller.handle_intermediate_trigger(BetweenIntervalTriggerEvent())
515+
516+
mock_emit.assert_not_called()

tests/core/timer/test_timer.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
from solaredge2mqtt.core.timer import Timer
88
from solaredge2mqtt.core.timer.events import (
9+
BetweenIntervalTriggerEvent,
910
Interval1MinTriggerEvent,
1011
Interval5MinTriggerEvent,
1112
Interval10MinTriggerEvent,
@@ -175,3 +176,40 @@ async def test_timer_with_different_base_intervals(self, mock_event_bus):
175176
await timer.loop()
176177

177178
assert mock_event_bus.emit.called
179+
180+
181+
class TestTimerBetweenIntervalTrigger:
182+
"""Tests for Timer.on_between_interval_trigger handler."""
183+
184+
@pytest.mark.asyncio
185+
async def test_between_interval_trigger_emits_all_long_intervals(
186+
self, mock_event_bus
187+
):
188+
"""
189+
Test that BetweenIntervalTriggerEvent causes
190+
all long intervals to be emitted.
191+
"""
192+
timer = Timer(5)
193+
event = BetweenIntervalTriggerEvent()
194+
195+
await timer.on_between_interval_trigger(event)
196+
197+
emitted_types = [
198+
type(call.args[0]) for call in mock_event_bus.emit.call_args_list
199+
]
200+
assert Interval1MinTriggerEvent in emitted_types
201+
assert Interval5MinTriggerEvent in emitted_types
202+
assert Interval10MinTriggerEvent in emitted_types
203+
assert Interval15MinTriggerEvent in emitted_types
204+
205+
@pytest.mark.asyncio
206+
async def test_between_interval_trigger_emits_exactly_four_events(
207+
self, mock_event_bus
208+
):
209+
"""Test that exactly the four long-interval events are emitted."""
210+
timer = Timer(5)
211+
event = BetweenIntervalTriggerEvent()
212+
213+
await timer.on_between_interval_trigger(event)
214+
215+
assert mock_event_bus.emit.call_count == 4

0 commit comments

Comments
 (0)