@Mathieu - thanks for the pointer. I wrote a pyscript that
- for every calendar included/excluded (see code) adds a “calendar.name_ext” entity with 4 attributes
current_event_brief:
upcoming_event_brief: 9:00 Justin and Alex
start_time: 2023-02-27T09:00:00-05:00
till: 12hr
- adds calendar.all entity that aggegates included calendars
current_event_brief:
upcoming_event_brief: 9:00 (Steve / alex)
start_time: 2023-02-27T09:00:00-05:00
till: 12hr
import aiohttp
import datetime
from datetime import timezone
import json
from homeassistant.const import EVENT_STATE_CHANGED
ha_base_url = "http://192.168.XX.YY:8123"
access_token = "YOUR_TOKEN"
now = datetime.datetime.now()
start = now.isoformat()
end = now + datetime.timedelta(days=7)
events_filter = f"start={start}&end={end}"
headers = {
'Authorization': f"Bearer {access_token}",
'Content-Type': 'application/json'
}
include_calendars = []
exclude_calendars = ['calendar.birthdays', 'calendar.holidays_in_united_state']
if include_calendars and len(include_calendars) == 0:
calendar_url = f"{ha_base_url}/api/calendars"
async with aiohttp.ClientSession(headers=headers, connector=aiohttp.TCPConnector(ssl=False)) as session:
async with session.get(calendar_url) as response:
try:
calendars = json.loads(response.text())
include_calendars = [calendar['entity_id'] for calendar in calendars]
finally:
pass
all_calendar_events = []
def add_briefs(include_calendars, exclude_calendars):
for calendar in include_calendars:
if calendar in exclude_calendars:
continue
calendar_url = f"{ha_base_url}/api/calendars/{calendar}?{events_filter}"
async with aiohttp.ClientSession(headers=headers, connector=aiohttp.TCPConnector(ssl=False)) as session:
async with session.get(calendar_url) as response:
calendar_events = json.loads(response.text())
# add calendar name to each event
for event in calendar_events:
event['calendar.entity_id'] = calendar
if 'dateTime' not in event['start']:
event['start']['dateTime'] = event['start']['date'] + 'T00:00:00'
event['all_day'] = True
else:
event['all_day'] = False
start_time = datetime.datetime.fromisoformat(event['start']['dateTime']).astimezone()
dow = start_time.strftime('%a')
hm = start_time.strftime('%-I:%M')
duration = start_time - datetime.datetime.now().astimezone()
hours = duration.seconds // 3600 + (duration.days * 24)
minutes = (duration.seconds // 60) % 60
if hours > 0:
duration_str = f"{hours}hr"
else:
duration_str = f"{minutes}m"
event['till'] = f"{duration_str}"
# remove common words from summary
summary = event['summary'].replace('Meeting with ', '')
summary = summary.replace('Meeting ', '')
summary = summary.replace('Call with ', '')
summary = summary.replace('Discussion', '')
summary = summary.replace('Introduction Call ', '')
summary = summary.replace('Call ', '')
summary = summary.replace(' with ', ' ')
summary = summary.replace(' - ', ' ')
summary = summary.replace(' @ ', ' ')
summary = summary.strip(' -@:')
if (hours > 24):
event['brief'] = f"{dow} {hm} {summary}"
else:
event['brief'] = f"{hm} {summary}"
filtered_calendar_events = calendar_events
filtered_calendar_events = [event for event in filtered_calendar_events if event['start']['dateTime'] > now.isoformat()]
filtered_calendar_events = [event for event in filtered_calendar_events if not event['all_day']]
filtered_calendar_events = [event for event in filtered_calendar_events if 'Personal Commitment' not in event['summary']]
filtered_calendar_events = [event for event in filtered_calendar_events if 'summary' in event and len(event['summary'])>0]
all_calendar_events.extend(filtered_calendar_events)
# sort events by ascending start time
sorted_calendar_events = sorted(filtered_calendar_events, key=lambda event: event['start']['dateTime'])
current_events = [event for event in sorted_calendar_events if event['end']['dateTime'] < now.isoformat()]
upcoming_events = [event for event in sorted_calendar_events if event['end']['dateTime'] > now.isoformat()]
if len(current_events) > 0:
next_event = current_events[0]
state.set(f"{calendar}_ext",
current_event_brief=next_event['brief'],
end_time=next_event['end']['dateTime'])
else:
state.set(f"{calendar}_ext", current_event_brief='')
if len(upcoming_events) > 0:
next_event = upcoming_events[0]
state.set(f"{calendar}_ext",
upcoming_event_brief=next_event['brief'],
start_time=next_event['start']['dateTime'],
till=next_event['till'])
else:
state.set(f"{calendar}_ext", upcoming_event_brief='')
sorted_calendar_events = sorted(all_calendar_events, key=lambda event: event['start']['dateTime'])
current_events = [event for event in sorted_calendar_events if event['end']['dateTime'] < now.isoformat()]
upcoming_events = [event for event in sorted_calendar_events if event['end']['dateTime'] > now.isoformat()]
if len(current_events) > 0:
next_event = current_events[0]
state.set("calendar.all", current_event_brief=next_event['brief'], end_time=next_event['end']['dateTime'])
else:
state.set("calendar.all", current_event_brief='')
if len(upcoming_events) > 0:
next_event = upcoming_events[0]
state.set("calendar.all", next_event['till'],
upcoming_event_brief=next_event['brief'],
start_time=next_event['start']['dateTime'],
till=next_event['till'])
else:
state.set("calendar.all", upcoming_event_brief='')
@event_trigger(EVENT_STATE_CHANGED, "entity_id.startswith('calendar.') and not entity_id.endswith('_ext') and 'calendar.all' not in entity_id")
def monitor_calendar_change(entity_id=None, new_state=None, old_state=None):
add_briefs(include_calendars, exclude_calendars)
add_briefs(include_calendars, exclude_calendars)