@@ -378,8 +378,13 @@ async def _restore_state(self) -> None:
378378 last_seen = dt_util .parse_datetime (
379379 self ._attr_extra_state_attributes [ATTR_LAST_SEEN ]
380380 )
381- self ._attr_extra_state_attributes [ATTR_LAST_SEEN ] = last_seen
382- self ._prev_seen = last_seen
381+ if last_seen is None :
382+ self ._attr_extra_state_attributes [ATTR_LAST_SEEN ] = None
383+ else :
384+ self ._attr_extra_state_attributes [ATTR_LAST_SEEN ] = dt_util .as_local (
385+ last_seen
386+ )
387+ self ._prev_seen = dt_util .as_utc (last_seen )
383388 if self .source_type in _SOURCE_TYPE_NON_GPS and (
384389 self .latitude is None or self .longitude is None
385390 ):
@@ -410,21 +415,21 @@ async def _entity_updated( # noqa: C901
410415 # attributes defined by _LAST_SEEN_ATTRS, as a datetime.
411416
412417 def get_last_seen () -> datetime | None :
413- """Get last_seen from one of the possible attributes."""
418+ """Get last_seen (in UTC) from one of the possible attributes."""
414419 if (raw_last_seen := new_attrs .get (_LAST_SEEN_ATTRS )) is None :
415420 return None
416421 if isinstance (raw_last_seen , datetime ):
417- return raw_last_seen
422+ return dt_util . as_utc ( raw_last_seen )
418423 with suppress (TypeError , ValueError ):
419424 return dt_util .utc_from_timestamp (float (raw_last_seen ))
420425 with suppress (TypeError ):
421- return dt_util .parse_datetime (raw_last_seen )
426+ if (parsed_last_seen := dt_util .parse_datetime (raw_last_seen )) is None :
427+ return None
428+ return dt_util .as_utc (parsed_last_seen )
422429 return None
423430
424- # Make sure last_seen is timezone aware in local timezone.
425- # Note that dt_util.as_local assumes naive datetime is in local timezone.
426431 # Use last_updated from the new state object if no valid "last seen" was found.
427- last_seen = dt_util . as_local ( get_last_seen () or new_state .last_updated )
432+ last_seen = get_last_seen () or new_state .last_updated
428433
429434 old_last_seen = entity .seen
430435 if old_last_seen and last_seen < old_last_seen :
@@ -549,8 +554,8 @@ def get_last_seen() -> datetime | None:
549554 "last_seen not newer than previous update (%s) <= (%s)" ,
550555 self .entity_id ,
551556 entity_id ,
552- last_seen ,
553- self ._prev_seen ,
557+ dt_util . as_local ( last_seen ) ,
558+ dt_util . as_local ( self ._prev_seen ) ,
554559 )
555560 return
556561
@@ -563,7 +568,7 @@ def get_last_seen() -> datetime | None:
563568 if _entity .source_type
564569 ),
565570 ATTR_LAST_ENTITY_ID : entity_id ,
566- ATTR_LAST_SEEN : _nearest_second (last_seen ),
571+ ATTR_LAST_SEEN : dt_util . as_local ( _nearest_second (last_seen ) ),
567572 }
568573 if charging is not None :
569574 attrs [ATTR_BATTERY_CHARGING ] = charging
@@ -617,6 +622,9 @@ def _set_state(
617622 assert attributes
618623 last_ent = cast (str , attributes [ATTR_LAST_ENTITY_ID ])
619624 last_seen = cast (datetime , attributes [ATTR_LAST_SEEN ])
625+ # It's ok that last_seen is in local tz and self._prev_seen is in UTC.
626+ # last_seen's value will automatically be converted to UTC during the
627+ # subtraction operation.
620628 seconds = (last_seen - self ._prev_seen ).total_seconds ()
621629 min_seconds = MIN_SPEED_SECONDS
622630 if last_ent != prev_ent :
0 commit comments