Просмотр исходного кода

Import wifi_ssid and check-in/out times from both backends; fix naive-datetime tz bug in CalDAV

- CalDAV and API backends now populate wifi_ssid from X-ROOMCAL-DATA / API response
- API backend now parses checkin_time / checkout_time into check_in_time / check_out_time
- Fix: CalDAV _fetch was passing already-naive local datetimes through dt_util.as_local(),
  which treated them as UTC and applied a UTC offset shift when classifying current vs next;
  comparisons now use naive datetimes against a naive now, matching coordinator behavior
ZiB 4 месяцев назад
Родитель
Сommit
3bb0ee65d6

+ 6 - 1
custom_components/room_cal_ha/backends/api.py

@@ -1,7 +1,7 @@
 from __future__ import annotations
 
 import logging
-from datetime import date, timedelta
+from datetime import date, time, timedelta
 from typing import TYPE_CHECKING
 
 from homeassistant.helpers.aiohttp_client import async_get_clientsession
@@ -115,10 +115,15 @@ def _parse_reservation(data: dict | None) -> ReservationData | None:
     return None
   checkin_str = data.get("checkin_date")
   checkout_str = data.get("checkout_date")
+  checkin_time_str = data.get("checkin_time")
+  checkout_time_str = data.get("checkout_time")
   return ReservationData(
     guest_name=data.get("guest_name"),
     check_in_date=date.fromisoformat(checkin_str) if checkin_str else None,
+    check_in_time=time.fromisoformat(checkin_time_str) if checkin_time_str else None,
     check_out_date=date.fromisoformat(checkout_str) if checkout_str else None,
+    check_out_time=time.fromisoformat(checkout_time_str) if checkout_time_str else None,
+    wifi_ssid=data.get("wifi_ssid"),
     wifi_key=data.get("wifi_key"),
     door_code=data.get("door_code"),
   )

+ 7 - 9
custom_components/room_cal_ha/backends/caldav.py

@@ -66,10 +66,11 @@ class CalDavBackend(ReservationBackend):
       _LOGGER.warning("CalDAV calendar %r not found", cal_name)
       return None, None
 
-    now = dt_util.now()
+    now_aware = dt_util.now()
+    now = now_aware.replace(tzinfo=None)
     events = calendar.date_search(
-      start=now,
-      end=now + timedelta(days=90),
+      start=now_aware,
+      end=now_aware + timedelta(days=90),
       expand=True,
     )
 
@@ -85,12 +86,8 @@ class CalDavBackend(ReservationBackend):
 
     for res in parsed:
       if res.check_in_date and res.check_out_date:
-        check_in = dt_util.as_local(
-          datetime.combine(res.check_in_date, res.check_in_time or datetime.min.time())
-        )
-        check_out = dt_util.as_local(
-          datetime.combine(res.check_out_date, res.check_out_time or datetime.max.time().replace(microsecond=0))
-        )
+        check_in = datetime.combine(res.check_in_date, res.check_in_time or datetime.min.time())
+        check_out = datetime.combine(res.check_out_date, res.check_out_time or datetime.max.time().replace(microsecond=0))
         if check_in <= now <= check_out:
           current = res
         elif check_in > now and nxt is None:
@@ -149,6 +146,7 @@ def _parse_event(event: caldav.Event) -> ReservationData | None:
         check_in_time=check_in_time,
         check_out_date=check_out_date,
         check_out_time=check_out_time,
+        wifi_ssid=d.get("wifi_ssid"),
         wifi_key=d.get("wifi_key"),
         door_code=d.get("door_code"),
       )

+ 12 - 1
tests/test_api_backend.py

@@ -1,6 +1,6 @@
 """Unit tests for RoomCalApiBackend."""
 from contextlib import asynccontextmanager
-from datetime import date, timedelta
+from datetime import date, time, timedelta
 from unittest.mock import AsyncMock, MagicMock, patch
 
 import pytest
@@ -51,7 +51,10 @@ def _reservation_slot(guest, checkin, checkout):
   return {
     "guest_name": guest,
     "checkin_date": str(checkin),
+    "checkin_time": "14:00",
     "checkout_date": str(checkout),
+    "checkout_time": "11:00",
+    "wifi_ssid": "GuestNet",
     "wifi_key": "wifikey",
     "door_code": "1234",
   }
@@ -65,7 +68,10 @@ def test_parse_reservation_maps_fields():
   raw = {
     "guest_name": "Alice",
     "checkin_date": "2026-07-01",
+    "checkin_time": "14:00",
     "checkout_date": "2026-07-05",
+    "checkout_time": "11:00",
+    "wifi_ssid": "GuestNet",
     "wifi_key": "secret",
     "door_code": "4567",
   }
@@ -73,7 +79,10 @@ def test_parse_reservation_maps_fields():
   assert isinstance(res, ReservationData)
   assert res.guest_name == "Alice"
   assert res.check_in_date == date(2026, 7, 1)
+  assert res.check_in_time == time(14, 0)
   assert res.check_out_date == date(2026, 7, 5)
+  assert res.check_out_time == time(11, 0)
+  assert res.wifi_ssid == "GuestNet"
   assert res.wifi_key == "secret"
   assert res.door_code == "4567"
 
@@ -86,7 +95,9 @@ def test_parse_reservation_missing_dates_gives_none_fields():
   res = _parse_reservation({"guest_name": "Bob"})
   assert res.guest_name == "Bob"
   assert res.check_in_date is None
+  assert res.check_in_time is None
   assert res.check_out_date is None
+  assert res.check_out_time is None
 
 
 # ---------------------------------------------------------------------------

+ 4 - 1
tests/test_caldav_backend.py

@@ -16,6 +16,7 @@ from custom_components.room_cal_ha.models import ReservationData
 
 _X_DATA = {
   "guest_name": "Alice Smith",
+  "wifi_ssid": "GuestNetwork",
   "wifi_key": "secret-key",
   "door_code": "4567",
 }
@@ -57,6 +58,7 @@ def test_parse_valid_event_returns_reservation_data(hass: HomeAssistant):
 
   assert isinstance(result, ReservationData)
   assert result.guest_name == "Alice Smith"
+  assert result.wifi_ssid == "GuestNetwork"
   assert result.wifi_key == "secret-key"
   assert result.door_code == "4567"
 
@@ -145,12 +147,13 @@ def test_parse_event_missing_dtend_still_returns_result(hass: HomeAssistant):
 
 
 def test_parse_event_x_data_missing_optional_fields(hass: HomeAssistant):
-  """X-ROOMCAL-DATA may omit wifi_key / door_code — those become None."""
+  """X-ROOMCAL-DATA may omit wifi_ssid / wifi_key / door_code — those become None."""
   sparse = {"guest_name": "Bob"}
   event = _make_event("Guest: Bob", "20260801T140000", "20260803T120000", sparse)
   result = _parse_event(event)
 
   assert result is not None
   assert result.guest_name == "Bob"
+  assert result.wifi_ssid is None
   assert result.wifi_key is None
   assert result.door_code is None