Skip to content

Commit

Permalink
Merge pull request #973 from robbrad/feat/calendar
Browse files Browse the repository at this point in the history
feat: adding calendar for Bins in Custom Component
  • Loading branch information
robbrad authored Nov 7, 2024
2 parents dde4170 + 4445d38 commit c67a216
Show file tree
Hide file tree
Showing 6 changed files with 695 additions and 7 deletions.
17 changes: 11 additions & 6 deletions custom_components/uk_bin_collection/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# init.py

"""The UK Bin Collection integration."""

import asyncio
Expand All @@ -14,7 +16,7 @@

from homeassistant.util import dt as dt_util

from .const import DOMAIN, LOG_PREFIX
from .const import DOMAIN, LOG_PREFIX, PLATFORMS
from uk_bin_collection.uk_bin_collection.collect_data import UKBinCollectionApp

_LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -99,11 +101,11 @@ async def async_setup_entry(
hass.data[DOMAIN][config_entry.entry_id] = {"coordinator": coordinator}
_LOGGER.debug(f"{LOG_PREFIX} Coordinator stored in hass.data under entry_id={config_entry.entry_id}.")

# Forward the setup to the sensor platform without awaiting
# Forward the setup to all platforms (sensor and calendar)
hass.async_create_task(
hass.config_entries.async_forward_entry_setups(config_entry, ["sensor"])
hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS)
)
_LOGGER.debug(f"{LOG_PREFIX} Setup forwarded to 'sensor' platform.")
_LOGGER.debug(f"{LOG_PREFIX} Setup forwarded to platforms: {PLATFORMS}.")

return True

Expand All @@ -116,11 +118,15 @@ async def async_unload_entry(
unload_ok = await hass.config_entries.async_forward_entry_unload(
config_entry, "sensor"
)
calendar_unload_ok = await hass.config_entries.async_forward_entry_unload(
config_entry, "calendar"
)
unload_ok = unload_ok and calendar_unload_ok
if unload_ok:
hass.data[DOMAIN].pop(config_entry.entry_id)
_LOGGER.debug(f"{LOG_PREFIX} Unloaded and removed coordinator for entry_id={config_entry.entry_id}.")
else:
_LOGGER.warning(f"{LOG_PREFIX} Failed to unload 'sensor' platform for entry_id={config_entry.entry_id}.")
_LOGGER.warning(f"{LOG_PREFIX} Failed to unload one or more platforms for entry_id={config_entry.entry_id}.")

return unload_ok

Expand Down Expand Up @@ -203,7 +209,6 @@ async def _async_update_data(self) -> dict:
_LOGGER.exception(f"{LOG_PREFIX} Unexpected error: {exc}")
raise UpdateFailed(f"Unexpected error: {exc}") from exc


@staticmethod
def process_bin_data(data: dict) -> dict:
"""Process raw data to determine the next collection dates."""
Expand Down
137 changes: 137 additions & 0 deletions custom_components/uk_bin_collection/calendar.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
"""Calendar platform support for UK Bin Collection Data."""

import logging
import uuid
from datetime import datetime, timedelta
from typing import Any, Dict, List, Optional

from homeassistant.components.calendar import CalendarEntity, CalendarEvent
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from .const import DOMAIN, LOG_PREFIX
from homeassistant.helpers.update_coordinator import CoordinatorEntity, DataUpdateCoordinator

_LOGGER = logging.getLogger(__name__)


class UKBinCollectionCalendar(CoordinatorEntity, CalendarEntity):
"""Calendar entity for UK Bin Collection Data."""

def __init__(
self,
coordinator: DataUpdateCoordinator,
bin_type: str,
unique_id: str,
name: str,
) -> None:
"""Initialize the calendar entity."""
super().__init__(coordinator)
self._bin_type = bin_type
self._unique_id = unique_id
self._name = name
self._attr_unique_id = unique_id

# Optionally, set device_info if you have device grouping
self._attr_device_info = {
"identifiers": {(DOMAIN, unique_id)},
"name": f"{self._name} Device",
"manufacturer": "UK Bin Collection",
"model": "Bin Collection Calendar",
"sw_version": "1.0",
}

@property
def name(self) -> str:
"""Return the name of the calendar."""
return self._name

@property
def event(self) -> Optional[CalendarEvent]:
"""Return the next collection event."""
collection_date = self.coordinator.data.get(self._bin_type)
if not collection_date:
_LOGGER.debug(f"{LOG_PREFIX} No collection date available for '{self._bin_type}'.")
return None

return self._create_calendar_event(collection_date)

async def async_get_events(
self, hass: HomeAssistant, start_date: datetime, end_date: datetime
) -> List[CalendarEvent]:
"""Return all events within a specific time frame."""
events: List[CalendarEvent] = []
collection_date = self.coordinator.data.get(self._bin_type)

if not collection_date:
return events

if start_date.date() <= collection_date <= end_date.date():
events.append(self._create_calendar_event(collection_date))

return events

def _create_calendar_event(self, collection_date: datetime.date) -> CalendarEvent:
"""Create a CalendarEvent for a given collection date."""
return CalendarEvent(
summary=f"{self._bin_type} Collection",
start=collection_date,
end=collection_date + timedelta(days=1),
uid=f"{self.unique_id}_{collection_date.isoformat()}",
)

@property
def unique_id(self) -> str:
"""Return a unique ID for the calendar."""
return self._unique_id

@callback
def _handle_coordinator_update(self) -> None:
"""Handle updates from the coordinator and refresh calendar state."""
self.async_write_ha_state()


async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up UK Bin Collection Calendar from a config entry."""
_LOGGER.info(f"{LOG_PREFIX} Setting up UK Bin Collection Calendar platform.")

# Retrieve the coordinator from hass.data
coordinator: DataUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id]["coordinator"]

# Create calendar entities
entities = []
for bin_type in coordinator.data.keys():
unique_id = calc_unique_calendar_id(config_entry.entry_id, bin_type)
name = f"{coordinator.name} {bin_type} Calendar"
entities.append(
UKBinCollectionCalendar(
coordinator=coordinator,
bin_type=bin_type,
unique_id=unique_id,
name=name,
)
)

# Register all calendar entities with Home Assistant
async_add_entities(entities)
_LOGGER.debug(f"{LOG_PREFIX} Calendar entities added: {[entity.name for entity in entities]}")


async def async_unload_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_remove_entities: Any,
) -> bool:
"""Unload a config entry."""
# Unloading is handled in init.py
return True


def calc_unique_calendar_id(entry_id: str, bin_type: str) -> str:
"""Calculate a unique ID for the calendar."""
return f"{entry_id}_{bin_type}_calendar"
2 changes: 2 additions & 0 deletions custom_components/uk_bin_collection/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

DEVICE_CLASS = "bin_collection_schedule"

PLATFORMS = ["sensor", "calendar"]

SELENIUM_SERVER_URLS = ["http://localhost:4444", "http://selenium:4444"]

BROWSER_BINARIES = ["chromium", "chromium-browser", "google-chrome"]
3 changes: 2 additions & 1 deletion custom_components/uk_bin_collection/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@
"issue_tracker": "https://github.com/robbrad/UKBinCollectionData/issues",
"requirements": ["uk-bin-collection>=0.112.1"],
"version": "0.112.1",
"zeroconf": []
"zeroconf": [],
"platforms": ["sensor", "calendar"]
}
1 change: 1 addition & 0 deletions custom_components/uk_bin_collection/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
STATE_ATTR_NEXT_COLLECTION,
DEVICE_CLASS,
STATE_ATTR_COLOUR,
PLATFORMS,
)
from uk_bin_collection.uk_bin_collection.collect_data import UKBinCollectionApp

Expand Down
Loading

0 comments on commit c67a216

Please sign in to comment.