Skip to content

Commit 912081f

Browse files
committed
Fix current_runtime attribute on valve entities
The `watering_in_progress_notification` WebSocket event delivers `run_time` at the top level with no `stations` array. The coordinator was storing `watering_status.stations` as an empty list, and the valve entity reads `stations[0].run_time` to populate `current_runtime`, leaving the attribute as null whenever watering started via a WebSocket event. Synthesize a stations entry from the event's `current_station` and `run_time` so the stored shape matches the API response. Fixes #394
1 parent 8e2adc4 commit 912081f

2 files changed

Lines changed: 61 additions & 14 deletions

File tree

custom_components/bhyve/coordinator.py

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,31 @@ async def fetch_zone_landscape(
194194

195195
return landscapes
196196

197+
@staticmethod
198+
def _apply_watering_in_progress(
199+
device_data: dict[str, Any], event_data: dict[str, Any]
200+
) -> None:
201+
"""Apply a watering_in_progress_notification event to device state."""
202+
status = device_data.setdefault("status", {})
203+
current_station = event_data.get("current_station")
204+
run_time = event_data.get("run_time")
205+
stations = event_data.get("stations") or []
206+
# The event provides run_time at the top level without a stations array.
207+
# Synthesize one so the shape matches the API response and
208+
# current_runtime stays populated for valve entities.
209+
if not stations and run_time is not None and current_station is not None:
210+
stations = [{"station": current_station, "run_time": run_time}]
211+
status["watering_status"] = {
212+
"current_station": current_station,
213+
"program": event_data.get("program"),
214+
"run_time": run_time,
215+
"started_watering_station_at": event_data.get(
216+
"started_watering_station_at"
217+
),
218+
"stations": stations,
219+
}
220+
status["run_mode"] = event_data.get("mode", "manual")
221+
197222
async def async_handle_device_event(self, event_data: dict[str, Any]) -> None: # noqa: PLR0912
198223
"""Handle WebSocket device events."""
199224
if not self.data:
@@ -234,20 +259,7 @@ async def async_handle_device_event(self, event_data: dict[str, Any]) -> None:
234259
del device_data["status"]["watering_status"]
235260

236261
elif event == EVENT_WATERING_IN_PROGRESS:
237-
# Update watering status
238-
if "status" not in device_data:
239-
device_data["status"] = {}
240-
# Store the full watering status from the event
241-
device_data["status"]["watering_status"] = {
242-
"current_station": event_data.get("current_station"),
243-
"program": event_data.get("program"),
244-
"run_time": event_data.get("run_time"),
245-
"started_watering_station_at": event_data.get(
246-
"started_watering_station_at"
247-
),
248-
"stations": event_data.get("stations", []),
249-
}
250-
device_data["status"]["run_mode"] = event_data.get("mode", "manual")
262+
self._apply_watering_in_progress(device_data, event_data)
251263

252264
elif event == EVENT_WATERING_COMPLETE:
253265
# Clear watering status

tests/test_coordinator.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,3 +197,38 @@ async def test_handle_program_update_unknown_program(
197197
# Verify no changes and no update triggered
198198
assert "unknown-program-id" not in coordinator.data["programs"]
199199
mock_update.assert_not_called()
200+
201+
202+
class TestDeviceEventHandling:
203+
"""Test device event handling in coordinator."""
204+
205+
async def test_handle_watering_in_progress_synthesizes_stations(
206+
self, hass: HomeAssistant
207+
) -> None:
208+
"""
209+
Synthesize a stations entry from top-level run_time.
210+
211+
The `watering_in_progress_notification` WebSocket event provides
212+
`run_time` at the top level but does not include a `stations` array.
213+
Downstream entities read runtime from `watering_status.stations[0]`,
214+
so the coordinator must populate it so `current_runtime` is not lost.
215+
"""
216+
coordinator = create_mock_coordinator(hass)
217+
218+
event_data = {
219+
"event": "watering_in_progress_notification",
220+
"device_id": TEST_DEVICE_ID,
221+
"program": "e",
222+
"current_station": 1,
223+
"run_time": 14,
224+
"started_watering_station_at": "2020-01-09T20:29:59.000Z",
225+
}
226+
227+
with patch.object(coordinator, "async_set_updated_data"):
228+
await coordinator.async_handle_device_event(event_data)
229+
230+
watering_status = coordinator.data["devices"][TEST_DEVICE_ID]["device"][
231+
"status"
232+
]["watering_status"]
233+
assert watering_status["current_station"] == 1
234+
assert watering_status["stations"] == [{"station": 1, "run_time": 14}]

0 commit comments

Comments
 (0)