Skip to content

Commit

Permalink
Merge pull request #69 from rine77/33-feature_request-lunch-as-calend…
Browse files Browse the repository at this point in the history
…ar-entries

calendar for canteen lunches
  • Loading branch information
rine77 authored Dec 25, 2024
2 parents f6018c7 + 4918b90 commit a1a65f8
Show file tree
Hide file tree
Showing 3 changed files with 147 additions and 6 deletions.
25 changes: 25 additions & 0 deletions custom_components/homeassistantedupage/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from .homeassistant_edupage import Edupage
from edupage_api.lunches import Lunch

from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from .const import DOMAIN, CONF_PHPSESSID, CONF_SUBDOMAIN, CONF_STUDENT_ID
Expand Down Expand Up @@ -93,11 +94,35 @@ async def fetch_data():
if canceled_lessons:
timetable_data_canceled[current_date] = canceled_lessons

canteen_menu_data = {}
canteen_calendar_enabled = True
for offset in range(14):
current_date = today + timedelta(days=offset)
try:
lunch = await edupage.get_lunches(current_date)
except Exception as e:
_LOGGER.error(f"Failed to fetch lunch data for {current_date}: {e}")
lunch = None
canteen_calendar_enabled = False
break
meals_to_add = []
if lunch is not None and lunch.menus is not None and len(lunch.menus) > 0:
_LOGGER.debug(f"Lunch for {current_date}: {lunch}")
meals_to_add.append(lunch)

if meals_to_add:
_LOGGER.debug(f"Daily menu for {current_date}: {lessons_to_add}")
canteen_menu_data[current_date] = meals_to_add
else:
_LOGGER.warning(f"INIT No daily menu found for {current_date}")

return_data = {
"student": {"id": student.person_id, "name": student.name},
"grades": grades,
"subjects": subjects,
"timetable": timetable_data,
"canteen_menu": canteen_menu_data,
"canteen_calendar_enabled": canteen_calendar_enabled,
"cancelled_lessons": timetable_data_canceled,
"notifications": notifications,
}
Expand Down
108 changes: 106 additions & 2 deletions custom_components/homeassistantedupage/calendar.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,35 @@
from homeassistant.components.calendar import CalendarEntity, CalendarEvent
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from .const import DOMAIN
from zoneinfo import ZoneInfo
from edupage_api.timetables import Lesson
from edupage_api.lunches import Lunch

_LOGGER = logging.getLogger("custom_components.homeassistant_edupage")
_LOGGER.debug("CALENDAR Edupage calendar.py is being loaded")

async def async_setup_entry(hass, entry, async_add_entities: AddEntitiesCallback) -> None:
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback) -> None:
"""Set up Edupage calendar entities."""
_LOGGER.debug("CALENDAR called async_setup_entry")

coordinator = hass.data[DOMAIN][entry.entry_id]

calendars = []

edupage_calendar = EdupageCalendar(coordinator, entry.data)
calendars.append(edupage_calendar)

if coordinator.data.get("canteen_calendar_enabled", {}):
edupage_canteen_calendar = EdupageCanteenCalendar(coordinator, entry.data)
calendars.append(edupage_canteen_calendar)
_LOGGER.debug("Canteen calendar added")
else:
_LOGGER.debug("Canteen calendar skipped, calendar disabled due to exceptions")

async_add_entities([edupage_calendar])
async_add_entities(calendars)

_LOGGER.debug("CALENDAR async_setup_entry finished.")

Expand Down Expand Up @@ -170,3 +183,94 @@ def find_lesson_now_or_next_across_days(self) -> Optional[CalendarEvent]:
return self.map_lesson_to_calender_event(next_lesson, day)

return None

class EdupageCanteenCalendar(CoordinatorEntity, CalendarEntity):
"""Representation of an Edupage canteen calendar entity."""

def __init__(self, coordinator, data):
super().__init__(coordinator)
self._data = data
self._events = []
self._attr_name = "Edupage Canteen Calendar"
_LOGGER.debug(f"CALENDAR Initialized EdupageCanteenCalendar with data: {data}")

@property
def unique_id(self):
"""Return a unique ID for this calendar."""
return f"edupage_canteen_calendar"

@property
def name(self):
"""Return the name of the calendar."""
return f"Edupage - Canteen"

@property
def extra_state_attributes(self):
"""Return the extra state attributes."""
return {
"unique_id": self.unique_id,
"other_info": "debug info"
}

@property
def available(self) -> bool:
"""Return True if the calendar is available."""
_LOGGER.debug("CALENDAR Checking availability of Edupage Canteen Calendar")
return True

@property
def event(self):
"""Return the next upcoming event or None if no event exists."""
return self.find_meal_now_or_next_across_days()

async def async_get_events(self, hass, start_date: datetime, end_date: datetime):
"""Return events in a specific date range."""
events = []

_LOGGER.debug(f"CALENDAR Fetching canteen calendar between {start_date} and {end_date}")
canteen_menu = self.coordinator.data.get("canteen_menu", {})
_LOGGER.debug(f"CALENDAR Coordinator data: {self.coordinator.data}")
_LOGGER.debug(f"CALENDAR Fetched canteen_menu data: {canteen_menu}")

if not canteen_menu:
_LOGGER.warning("CALENDAR Canteen menu data is missing.")
return events

current_date = start_date.date()
while current_date <= end_date.date():
events.extend(self.get_events(canteen_menu, current_date))
current_date += timedelta(days=1)

_LOGGER.debug(f"CALENDAR Fetched {len(events)} events from {start_date} to {end_date}")
return events

def get_events(self, canteen_menu, current_date):
events = []
daily_menu = canteen_menu.get(current_date)
if daily_menu:
for meal in daily_menu:
_LOGGER.debug(f"CALENDAR Meal attributes: {vars(meal)}")
events.append(
self.map_meal_to_calender_event(meal, current_date)
)
return events


def map_meal_to_calender_event(self, meal: Lunch, day: date) -> CalendarEvent:
local_tz = ZoneInfo(self.hass.config.time_zone)
start_time = datetime.combine(day, meal.served_from.time()).astimezone(local_tz)
end_time = datetime.combine(day, meal.served_to.time()).astimezone(local_tz)
summary = "Lunch"
description = meal.title

cal_event = CalendarEvent(
start=start_time,
end=end_time,
summary=summary,
description=description
)
return cal_event

def find_meal_now_or_next_across_days(self) -> Optional[CalendarEvent]:
# TODO implement
return None
20 changes: 16 additions & 4 deletions custom_components/homeassistantedupage/homeassistant_edupage.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,16 @@ async def login(self, username: str, password: str, subdomain: str):

except CaptchaException as e:
_LOGGER.error("EDUPAGE login failed: CAPTCHA needed. %s", e)
return False
return False

except SecondFactorFailedException as e:
#TODO hier müsste man dann irgendwie abfangen, falls die session mal abgelaufen ist. und dies dann auch irgendwie via HA sauber zum Nutzer bringen!?
_LOGGER.error("EDUPAGE login failed: 2FA error. %s", e)
return False
return False

except Exception as e:
_LOGGER.error("EDUPAGE unexpected login error: %s", e)
return False
return False

async def get_classes(self):

Expand Down Expand Up @@ -101,7 +101,7 @@ async def get_classrooms(self):
return all_classrooms
except Exception as e:
raise UpdateFailed(F"EDUPAGE error updating get_classrooms data from API: {e}")

async def get_teachers(self):

try:
Expand All @@ -122,6 +122,18 @@ async def get_timetable(self, EduStudent, date):
_LOGGER.error(f"EDUPAGE error updating get_timetable() data for {date}: {e}")
raise UpdateFailed(f"EDUPAGE error updating get_timetable() data for {date}: {e}")

async def get_lunches(self, date):
try:
lunches_data = await self.hass.async_add_executor_job(self.api.get_lunches, date)
if lunches_data is None:
_LOGGER.debug("EDUPAGE lunches is None")
else:
_LOGGER.debug(f"EDUPAGE lunches_data for {date}: {lunches_data}")
return lunches_data
except Exception as e:
_LOGGER.error(f"EDUPAGE error updating get_lunches() data for {date}: {e}")
raise UpdateFailed(f"EDUPAGE error updating get_lunches() data for {date}: {e}")

async def async_update(self):

pass

0 comments on commit a1a65f8

Please sign in to comment.