Skip to content

Commit d5796e5

Browse files
mikejhillCopilot
andcommitted
fix: parse last_activity_date as datetime for HA timestamp sensor
SensorDeviceClass.TIMESTAMP requires a datetime object, not a string. The value_fn was returning the raw ISO string from start_utc, causing a ValueError on entity registration. - Add _last_activity_datetime() helper using dt_util.parse_datetime - Returns None for missing or unparseable dates (graceful degradation) - Add tests for None start_utc and unparseable date strings Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 2c23f2d commit d5796e5

File tree

2 files changed

+29
-2
lines changed

2 files changed

+29
-2
lines changed

custom_components/rouvy/sensor.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import logging
66
from collections.abc import Callable
77
from dataclasses import dataclass
8+
from datetime import datetime
89
from typing import TYPE_CHECKING, Any
910

1011
from homeassistant.components.sensor import (
@@ -21,6 +22,7 @@
2122
)
2223
from homeassistant.core import HomeAssistant
2324
from homeassistant.helpers.entity_platform import AddEntitiesCallback
25+
from homeassistant.util import dt as dt_util
2426

2527
from .api_client.models import Activity, ActivityTypeStats, RouvyCoordinatorData
2628
from .data import RouvyConfigEntry
@@ -55,6 +57,14 @@ def _last_activity(data: RouvyCoordinatorData) -> Activity | None:
5557
return None
5658

5759

60+
def _last_activity_datetime(data: RouvyCoordinatorData) -> datetime | None:
61+
"""Get the start time of the most recent activity as a datetime."""
62+
act = _last_activity(data)
63+
if act and act.start_utc:
64+
return dt_util.parse_datetime(act.start_utc)
65+
return None
66+
67+
5868
@dataclass(frozen=True, kw_only=True)
5969
class RouvySensorDescription(SensorEntityDescription):
6070
"""Describe a Rouvy sensor."""
@@ -245,7 +255,7 @@ class RouvySensorDescription(SensorEntityDescription):
245255
key="last_activity_date",
246256
translation_key="last_activity_date",
247257
device_class=SensorDeviceClass.TIMESTAMP,
248-
value_fn=lambda d: a.start_utc if (a := _last_activity(d)) else None,
258+
value_fn=_last_activity_datetime,
249259
),
250260
RouvySensorDescription(
251261
key="total_activities",

tests/test_ha_sensor.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
from __future__ import annotations
88

9+
from datetime import UTC, datetime
10+
911
from custom_components.rouvy.api_client.models import (
1012
Activity,
1113
ActivitySummary,
@@ -649,7 +651,8 @@ def test_returns_start_utc(self) -> None:
649651
from custom_components.rouvy.sensor import SENSOR_DESCRIPTIONS
650652

651653
desc = next(s for s in SENSOR_DESCRIPTIONS if s.key == "last_activity_date")
652-
assert desc.value_fn(d) == "2026-04-01T07:30:00Z"
654+
result = desc.value_fn(d)
655+
assert result == datetime(2026, 4, 1, 7, 30, tzinfo=UTC)
653656

654657
def test_returns_none_when_no_summary(self) -> None:
655658
d = _make_data_with_activities(summary=None)
@@ -658,6 +661,20 @@ def test_returns_none_when_no_summary(self) -> None:
658661
desc = next(s for s in SENSOR_DESCRIPTIONS if s.key == "last_activity_date")
659662
assert desc.value_fn(d) is None
660663

664+
def test_returns_none_when_start_utc_is_none(self) -> None:
665+
d = _make_data_with_activities([_make_activity(start_utc=None)])
666+
from custom_components.rouvy.sensor import SENSOR_DESCRIPTIONS
667+
668+
desc = next(s for s in SENSOR_DESCRIPTIONS if s.key == "last_activity_date")
669+
assert desc.value_fn(d) is None
670+
671+
def test_returns_none_for_unparseable_date(self) -> None:
672+
d = _make_data_with_activities([_make_activity(start_utc="not-a-date")])
673+
from custom_components.rouvy.sensor import SENSOR_DESCRIPTIONS
674+
675+
desc = next(s for s in SENSOR_DESCRIPTIONS if s.key == "last_activity_date")
676+
assert desc.value_fn(d) is None
677+
661678

662679
class TestTotalActivitiesSensor:
663680
"""Verify total_activities sensor value extraction."""

0 commit comments

Comments
 (0)