1
mirror of https://github.com/home-assistant/core synced 2024-10-04 07:58:43 +02:00
ha-core/homeassistant/components/calendar/__init__.py

215 lines
6.5 KiB
Python
Raw Normal View History

"""Support for Google Calendar event device sensors."""
2017-01-07 11:24:25 +01:00
from datetime import timedelta
import logging
import re
from aiohttp import web
from homeassistant.components import http
from homeassistant.const import STATE_OFF, STATE_ON
from homeassistant.helpers.config_validation import ( # noqa
2019-07-31 21:25:30 +02:00
PLATFORM_SCHEMA,
PLATFORM_SCHEMA_BASE,
time_period_str,
)
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.template import DATE_STR_FORMAT
from homeassistant.util import dt
_LOGGER = logging.getLogger(__name__)
2019-07-31 21:25:30 +02:00
DOMAIN = "calendar"
ENTITY_ID_FORMAT = DOMAIN + ".{}"
SCAN_INTERVAL = timedelta(seconds=60)
2018-06-15 19:37:46 +02:00
async def async_setup(hass, config):
"""Track states and offer events for calendars."""
component = hass.data[DOMAIN] = EntityComponent(
2019-07-31 21:25:30 +02:00
_LOGGER, DOMAIN, hass, SCAN_INTERVAL, DOMAIN
)
2018-06-15 19:37:46 +02:00
hass.http.register_view(CalendarListView(component))
hass.http.register_view(CalendarEventView(component))
2018-07-03 19:20:42 +02:00
# Doesn't work in prod builds of the frontend: home-assistant-polymer#1289
# hass.components.frontend.async_register_built_in_panel(
2018-07-03 19:20:42 +02:00
# 'calendar', 'calendar', 'hass:calendar')
2018-06-15 19:37:46 +02:00
await component.async_setup(config)
return True
def get_date(date):
"""Get the dateTime from date or dateTime as a local."""
2019-07-31 21:25:30 +02:00
if "date" in date:
return dt.start_of_local_day(
dt.dt.datetime.combine(dt.parse_date(date["date"]), dt.dt.time.min)
)
return dt.as_local(dt.parse_datetime(date["dateTime"]))
def normalize_event(event):
"""Normalize a calendar event."""
normalized_event = {}
2019-07-31 21:25:30 +02:00
start = event.get("start")
end = event.get("end")
start = get_date(start) if start is not None else None
end = get_date(end) if end is not None else None
2019-07-31 21:25:30 +02:00
normalized_event["dt_start"] = start
normalized_event["dt_end"] = end
start = start.strftime(DATE_STR_FORMAT) if start is not None else None
end = end.strftime(DATE_STR_FORMAT) if end is not None else None
2019-07-31 21:25:30 +02:00
normalized_event["start"] = start
normalized_event["end"] = end
# cleanup the string so we don't have a bunch of double+ spaces
2019-07-31 21:25:30 +02:00
summary = event.get("summary", "")
normalized_event["message"] = re.sub(" +", "", summary).strip()
normalized_event["location"] = event.get("location", "")
normalized_event["description"] = event.get("description", "")
normalized_event["all_day"] = "date" in event["start"]
return normalized_event
def calculate_offset(event, offset):
"""Calculate event offset.
Return the updated event with the offset_time included.
"""
2019-07-31 21:25:30 +02:00
summary = event.get("summary", "")
# check if we have an offset tag in the message
# time is HH:MM or MM
2019-07-31 21:25:30 +02:00
reg = "{}([+-]?[0-9]{{0,2}}(:[0-9]{{0,2}})?)".format(offset)
search = re.search(reg, summary)
if search and search.group(1):
time = search.group(1)
2019-07-31 21:25:30 +02:00
if ":" not in time:
if time[0] == "+" or time[0] == "-":
time = "{}0:{}".format(time[0], time[1:])
else:
2019-07-31 21:25:30 +02:00
time = "0:{}".format(time)
offset_time = time_period_str(time)
2019-07-31 21:25:30 +02:00
summary = (summary[: search.start()] + summary[search.end() :]).strip()
event["summary"] = summary
else:
offset_time = dt.dt.timedelta() # default it
2019-07-31 21:25:30 +02:00
event["offset_time"] = offset_time
return event
def is_offset_reached(event):
"""Have we reached the offset time specified in the event title."""
2019-07-31 21:25:30 +02:00
start = get_date(event["start"])
if start is None or event["offset_time"] == dt.dt.timedelta():
return False
2019-07-31 21:25:30 +02:00
return start + event["offset_time"] <= dt.now(start.tzinfo)
class CalendarEventDevice(Entity):
"""A calendar event device."""
@property
def event(self):
"""Return the next upcoming event."""
raise NotImplementedError()
@property
def state_attributes(self):
"""Return the entity state attributes."""
event = self.event
if event is None:
return None
event = normalize_event(event)
return {
2019-07-31 21:25:30 +02:00
"message": event["message"],
"all_day": event["all_day"],
"start_time": event["start"],
"end_time": event["end"],
"location": event["location"],
"description": event["description"],
}
@property
def state(self):
"""Return the state of the calendar event."""
event = self.event
if event is None:
return STATE_OFF
event = normalize_event(event)
2019-07-31 21:25:30 +02:00
start = event["dt_start"]
end = event["dt_end"]
if start is None or end is None:
return STATE_OFF
now = dt.now()
if start <= now < end:
return STATE_ON
return STATE_OFF
async def async_get_events(self, hass, start_date, end_date):
"""Return calendar events within a datetime range."""
raise NotImplementedError()
class CalendarEventView(http.HomeAssistantView):
"""View to retrieve calendar content."""
2019-07-31 21:25:30 +02:00
url = "/api/calendars/{entity_id}"
name = "api:calendars:calendar"
def __init__(self, component):
"""Initialize calendar view."""
self.component = component
async def get(self, request, entity_id):
"""Return calendar events."""
2018-06-15 19:37:46 +02:00
entity = self.component.get_entity(entity_id)
2019-07-31 21:25:30 +02:00
start = request.query.get("start")
end = request.query.get("end")
if None in (start, end, entity):
return web.Response(status=400)
try:
start_date = dt.parse_datetime(start)
end_date = dt.parse_datetime(end)
except (ValueError, AttributeError):
return web.Response(status=400)
2018-06-15 19:37:46 +02:00
event_list = await entity.async_get_events(
2019-07-31 21:25:30 +02:00
request.app["hass"], start_date, end_date
)
return self.json(event_list)
2018-06-15 19:37:46 +02:00
class CalendarListView(http.HomeAssistantView):
"""View to retrieve calendar list."""
2019-07-31 21:25:30 +02:00
url = "/api/calendars"
2018-06-15 19:37:46 +02:00
name = "api:calendars"
def __init__(self, component):
"""Initialize calendar view."""
self.component = component
async def get(self, request):
"""Retrieve calendar list."""
2019-07-31 21:25:30 +02:00
hass = request.app["hass"]
2018-06-15 19:37:46 +02:00
calendar_list = []
for entity in self.component.entities:
state = hass.states.get(entity.entity_id)
2019-07-31 21:25:30 +02:00
calendar_list.append({"name": state.name, "entity_id": entity.entity_id})
2018-06-15 19:37:46 +02:00
2019-07-31 21:25:30 +02:00
return self.json(sorted(calendar_list, key=lambda x: x["name"]))