diff --git a/custom_components/o365/__init__.py b/custom_components/o365/__init__.py index 21e21b2..a5c30a2 100644 --- a/custom_components/o365/__init__.py +++ b/custom_components/o365/__init__.py @@ -3,6 +3,7 @@ from functools import partial from aiohttp import web_response +from homeassistant.components import configurator from homeassistant.components.http import HomeAssistantView from homeassistant.core import callback from homeassistant.helpers import discovery @@ -36,9 +37,8 @@ DEFAULT_CACHE_PATH, DEFAULT_NAME, DOMAIN, - PRIMARY_DOMAIN_SCHEMA, - SECONDARY_DOMAIN_SCHEMA, ) +from .schema import LEGACY_SCHEMA, MULTI_ACCOUNT_SCHEMA from .utils import ( build_config_file_path, build_minimum_permissions, @@ -55,10 +55,10 @@ async def async_setup(hass, config): # validate_permissions(hass) conf = config.get(DOMAIN, {}) if CONF_ACCOUNTS not in conf: - accounts = [PRIMARY_DOMAIN_SCHEMA(conf)] + accounts = [LEGACY_SCHEMA(conf)] conf_type = CONST_CONFIG_TYPE_DICT else: - accounts = SECONDARY_DOMAIN_SCHEMA(conf)[CONF_ACCOUNTS] + accounts = MULTI_ACCOUNT_SCHEMA(conf)[CONF_ACCOUNTS] conf_type = CONST_CONFIG_TYPE_LIST for account in accounts: @@ -143,7 +143,7 @@ def _request_configuration(hass, url, callback_view, account_name): if DOMAIN not in hass.data: hass.data[DOMAIN] = {} - request_content = _create_request_content(url, callback_view, account_name) + request_content = _create_request_content(hass, url, callback_view, account_name) hass.data[DOMAIN][account_name] = request_content @@ -152,16 +152,19 @@ def _request_configuration_alt(hass, url, callback_view, account_name): if DOMAIN not in hass.data: hass.data[DOMAIN] = {} - request_content = _create_request_content_alt(url, callback_view, account_name) + request_content = _create_request_content_alt( + hass, url, callback_view, account_name + ) hass.data[DOMAIN][account_name] = request_content -def _create_request_content(url, callback_view, account_name): - configurator = callback_view.configurator +def _create_request_content(hass, url, callback_view, account_name): + o365configurator = callback_view.configurator display_name = f" - {account_name}" if account_name != CONST_PRIMARY else "" view_name = f"{DEFAULT_NAME}{display_name}" - return configurator.async_request_config( + return o365configurator.async_request_config( + hass, view_name, lambda _: None, link_name=CONFIGURATOR_LINK_NAME, @@ -171,11 +174,12 @@ def _create_request_content(url, callback_view, account_name): ) -def _create_request_content_alt(url, callback_view, account_name): - configurator = callback_view.configurator +def _create_request_content_alt(hass, url, callback_view, account_name): + o365configurator = callback_view.configurator display_name = f" - {account_name}" if account_name != CONST_PRIMARY else "" view_name = f"{DEFAULT_NAME}{display_name} - Alternative configuration" - return configurator.async_request_config( + return o365configurator.async_request_config( + hass, view_name, callback_view.alt_callback, link_name=CONFIGURATOR_LINK_NAME, @@ -193,7 +197,7 @@ def _request_authorization(hass, conf, account, account_name, conf_type): url, state = account.con.get_authorization_url( requested_scopes=scope, redirect_uri=callback_url ) - _LOGGER.info( + _LOGGER.warning( "No token, or token doesn't have all required permissions; requesting authorization" ) callback_view = O365AuthCallbackView( @@ -230,7 +234,7 @@ def __init__( self._callback = callback_url self._hass = hass self._account_name = account_name - self.configurator = self._hass.components.configurator + self.configurator = configurator self._conf_type = conf_type @callback @@ -257,8 +261,9 @@ async def get(self, request): do_setup( self._hass, self._config, self._account, self._account_name, self._conf_type ) - self.configurator.async_request_done(account_data) + self.configurator.async_request_done(self._hass, account_data) + self._log_authenticated() return web_response.Response( headers={"content-type": "text/html"}, text="Success! This window can be closed", @@ -273,13 +278,20 @@ def alt_callback(self, data): ) if not result: self.configurator.notify_errors( + self._hass, self._hass.data[DOMAIN][self._account_name], "Error while authenticating, please see logs for more info.", ) return + account_data = self._hass.data[DOMAIN][self._account_name] do_setup( self._hass, self._config, self._account, self._account_name, self._conf_type ) - self.configurator.async_request_done(account_data) + self.configurator.async_request_done(self._hass, account_data) + + self._log_authenticated() return + + def _log_authenticated(self): + _LOGGER.info("Succesfully authenticated") diff --git a/custom_components/o365/calendar.py b/custom_components/o365/calendar.py index d0280a1..4432e3b 100644 --- a/custom_components/o365/calendar.py +++ b/custom_components/o365/calendar.py @@ -5,13 +5,12 @@ from datetime import datetime, timedelta from operator import attrgetter, itemgetter -from homeassistant.components.calendar import CalendarEventDevice, is_offset_reached - -try: - from homeassistant.components.calendar import calculate_offset -except ImportError: - from homeassistant.components.calendar import extract_offset - +from homeassistant.components.calendar import ( + CalendarEntity, + CalendarEvent, + extract_offset, + is_offset_reached, +) from homeassistant.const import CONF_NAME from homeassistant.helpers.entity import generate_entity_id from homeassistant.util import dt @@ -21,10 +20,6 @@ ATTR_CALENDAR_ID, ATTR_ENTITY_ID, CALENDAR_ENTITY_ID_FORMAT, - CALENDAR_SERVICE_CREATE_SCHEMA, - CALENDAR_SERVICE_MODIFY_SCHEMA, - CALENDAR_SERVICE_REMOVE_SCHEMA, - CALENDAR_SERVICE_RESPOND_SCHEMA, CONF_ACCOUNT, CONF_ACCOUNT_NAME, CONF_CAL_IDS, @@ -44,6 +39,12 @@ PERM_CALENDARS_READWRITE, PERM_MINIMUM_CALENDAR_WRITE, ) +from .schema import ( + CALENDAR_SERVICE_CREATE_SCHEMA, + CALENDAR_SERVICE_MODIFY_SCHEMA, + CALENDAR_SERVICE_REMOVE_SCHEMA, + CALENDAR_SERVICE_RESPOND_SCHEMA, +) from .utils import ( add_call_data_to_event, build_config_file_path, @@ -142,7 +143,7 @@ def _setup_register_services(hass, conf): ) -class O365CalendarEventDevice(CalendarEventDevice): +class O365CalendarEventDevice(CalendarEntity): """O365 Calendar Event Processing.""" def __init__(self, account, calendar_id, entity, entity_id): @@ -163,6 +164,7 @@ def _init_data(self, account, calendar_id, entity): # _LOGGER.debug("Initialising calendar: %s", calendar_id) return O365CalendarData( account, + self.entity_id, calendar_id, search, max_results, @@ -173,7 +175,7 @@ def extra_state_attributes(self): """Device state property.""" if self._event: return { - "all_day": self._event.get("all_day", False) + "all_day": self._event.all_day if self.data.event is not None else False, "offset_reached": self._offset_reached, @@ -203,15 +205,9 @@ async def async_update(self): await self.data.async_update(self.hass) event = deepcopy(self.data.event) if event: - try: - event = calculate_offset(event, DEFAULT_OFFSET) - self._offset_reached = is_offset_reached(event) - except NameError: - event["summary"], offset = extract_offset( - event.get("summary", ""), DEFAULT_OFFSET - ) - start = O365CalendarData.to_datetime(event["start"]) - self._offset_reached = is_offset_reached(start, offset) + event.summary, offset = extract_offset(event.summary, DEFAULT_OFFSET) + start = O365CalendarData.to_datetime(event.start) + self._offset_reached = is_offset_reached(start, offset) results = await self.data.async_o365_get_events( self.hass, dt.utcnow() + timedelta(hours=self._start_offset), @@ -236,6 +232,7 @@ class O365CalendarData: def __init__( self, account, + entity_id, calendar_id, search=None, limit=999, @@ -247,6 +244,7 @@ def __init__( self.calendar = schedule.get_calendar(calendar_id=calendar_id) self._search = search self.event = None + self._entity_id = entity_id async def async_o365_get_events(self, hass, start_date, end_date): """Get the events.""" @@ -276,11 +274,19 @@ async def async_get_events(self, hass, start_date, end_date): vevent_list = list(results) vevent_list.sort(key=attrgetter("start")) event_list = [] - for event in vevent_list: - data = format_event_data(event, self.calendar.calendar_id) - data["start"] = self.get_hass_date(data["start"], event.is_all_day) - data["end"] = self.get_hass_date(data["end"], event.is_all_day) - event_list.append(data) + for vevent in vevent_list: + # data = format_event_data(event, self.calendar.calendar_id) + # data["start"] = self.get_hass_date(data["start"], event.is_all_day) + # data["end"] = self.get_hass_date(data["end"], event.is_all_day) + # event_list.append(data) + event = CalendarEvent( + self.get_hass_date(vevent.start, vevent.is_all_day), + self.get_hass_date(self.get_end_date(vevent), vevent.is_all_day), + vevent.subject, + clean_html(vevent.body), + vevent.location["displayName"], + ) + event_list.append(event) return event_list @@ -304,19 +310,18 @@ async def async_update(self, hass): _LOGGER.debug( "No matching event found in the %d results for %s", len(results), - self.calendar.name, + self._entity_id, ) self.event = None return - self.event = { - "summary": vevent.subject, - "start": self.get_hass_date(vevent.start, vevent.is_all_day), - "end": self.get_hass_date(self.get_end_date(vevent), vevent.is_all_day), - "location": vevent.location, - "description": clean_html(vevent.body), - "all_day": vevent.is_all_day, - } + self.event = CalendarEvent( + self.get_hass_date(vevent.start, vevent.is_all_day), + self.get_hass_date(self.get_end_date(vevent), vevent.is_all_day), + vevent.subject, + clean_html(vevent.body), + vevent.location["displayName"], + ) def _get_root_event(self, results): started_event = None @@ -370,10 +375,7 @@ def is_finished(vevent): @staticmethod def get_hass_date(obj, is_all_day): """Get the date.""" - if isinstance(obj, datetime) and not is_all_day: - return {"dateTime": obj.isoformat()} - - return {"date": obj.date().isoformat()} + return obj if isinstance(obj, datetime) and not is_all_day else obj.date() @staticmethod def to_datetime(obj): diff --git a/custom_components/o365/const.py b/custom_components/o365/const.py index ca556dd..ff02978 100644 --- a/custom_components/o365/const.py +++ b/custom_components/o365/const.py @@ -2,19 +2,6 @@ from datetime import timedelta from enum import Enum -import homeassistant.helpers.config_validation as cv -import voluptuous as vol -from homeassistant.components.notify import ( - ATTR_DATA, - ATTR_MESSAGE, - ATTR_TARGET, - ATTR_TITLE, -) -from homeassistant.const import CONF_NAME -from O365.calendar import AttendeeType # pylint: disable=no-name-in-module -from O365.calendar import EventSensitivity # pylint: disable=no-name-in-module -from O365.calendar import EventShowAs # pylint: disable=no-name-in-module - class EventResponse(Enum): """Event response.""" @@ -149,167 +136,3 @@ class EventResponse(Enum): ] YAML_CALENDARS = "{0}_calendars{1}.yaml" - -EMAIL_SENSOR = vol.Schema( - { - vol.Required(CONF_NAME): cv.string, - vol.Optional(CONF_MAIL_FOLDER): cv.string, - vol.Optional(CONF_MAX_ITEMS, default=5): int, - vol.Optional(CONF_IS_UNREAD): bool, - vol.Optional(CONF_DOWNLOAD_ATTACHMENTS): bool, - } -) -STATUS_SENSOR = vol.Schema( - { - vol.Required(CONF_NAME): cv.string, - } -) -QUERY_SENSOR = vol.Schema( - { - vol.Required(CONF_NAME): cv.string, - vol.Optional(CONF_MAIL_FOLDER): cv.string, - vol.Optional(CONF_MAIL_FROM): cv.string, - vol.Optional(CONF_MAX_ITEMS, default=5): int, - vol.Optional(CONF_HAS_ATTACHMENT): bool, - vol.Optional(CONF_IMPORTANCE): cv.string, - vol.Optional(CONF_IS_UNREAD): bool, - vol.Exclusive(CONF_SUBJECT_CONTAINS, "subject_*"): cv.string, - vol.Exclusive(CONF_SUBJECT_IS, "subject_*"): cv.string, - vol.Optional(CONF_DOWNLOAD_ATTACHMENTS): bool, - } -) - -PRIMARY_DOMAIN_SCHEMA = vol.Schema( - { - vol.Required(CONF_CLIENT_ID): cv.string, - vol.Required(CONF_CLIENT_SECRET): cv.string, - vol.Optional(CONF_TRACK_NEW, default=True): bool, - vol.Optional(CONF_ENABLE_UPDATE, default=True): bool, - vol.Optional(CONF_ALT_CONFIG, default=False): bool, - vol.Optional(CONF_EMAIL_SENSORS): [EMAIL_SENSOR], - vol.Optional(CONF_QUERY_SENSORS): [QUERY_SENSOR], - vol.Optional(CONF_STATUS_SENSORS): [STATUS_SENSOR], - } -) -SECONDARY_DOMAIN_SCHEMA = vol.Schema( - { - CONF_ACCOUNTS: vol.Schema( - [ - { - vol.Required(CONF_CLIENT_ID): cv.string, - vol.Required(CONF_CLIENT_SECRET): cv.string, - vol.Optional(CONF_TRACK_NEW, default=True): bool, - vol.Optional(CONF_ENABLE_UPDATE, default=False): bool, - vol.Required(CONF_ACCOUNT_NAME, ""): cv.string, - vol.Optional(CONF_ALT_CONFIG, default=False): bool, - vol.Optional(CONF_EMAIL_SENSORS): [EMAIL_SENSOR], - vol.Optional(CONF_QUERY_SENSORS): [QUERY_SENSOR], - vol.Optional(CONF_STATUS_SENSORS): [STATUS_SENSOR], - } - ] - ) - } -) - -NOTIFY_DATA_SCHEMA = vol.Schema( - { - vol.Optional(ATTR_MESSAGE_IS_HTML, default=False): bool, - vol.Optional(ATTR_TARGET): cv.string, - vol.Optional(ATTR_ZIP_ATTACHMENTS, default=False): bool, - vol.Optional(ATTR_ZIP_NAME): cv.string, - vol.Optional(ATTR_PHOTOS, default=[]): [cv.string], - vol.Optional(ATTR_ATTACHMENTS, default=[]): [cv.string], - } -) - -NOTIFY_BASE_SCHEMA = vol.Schema( - { - vol.Optional(ATTR_TARGET, default=[]): [cv.string], - vol.Optional(ATTR_TITLE, default=""): cv.string, - vol.Optional(ATTR_DATA): NOTIFY_DATA_SCHEMA, - } -) - -CALENDAR_SERVICE_RESPOND_SCHEMA = vol.Schema( - { - vol.Optional(ATTR_ENTITY_ID): cv.string, - vol.Required(ATTR_EVENT_ID): cv.string, - vol.Required(ATTR_CALENDAR_ID): cv.string, - vol.Optional(ATTR_RESPONSE, None): cv.enum(EventResponse), - vol.Optional(ATTR_SEND_RESPONSE, True): bool, - vol.Optional(ATTR_MESSAGE, None): cv.string, - } -) - -ATTENDEE_SCHEMA = vol.Schema( - { - vol.Required(ATTR_EMAIL): cv.string, - vol.Required(ATTR_TYPE): cv.enum(AttendeeType), - } -) - -CALENDAR_SERVICE_CREATE_SCHEMA = vol.Schema( - { - vol.Optional(ATTR_ENTITY_ID): cv.string, - vol.Required(ATTR_CALENDAR_ID): cv.string, - vol.Required(ATTR_START): cv.datetime, - vol.Required(ATTR_END): cv.datetime, - vol.Required(ATTR_SUBJECT): cv.string, - vol.Optional(ATTR_BODY): cv.string, - vol.Optional(ATTR_LOCATION): cv.string, - vol.Optional(ATTR_CATEGORIES): [cv.string], - vol.Optional(ATTR_SENSITIVITY): cv.enum(EventSensitivity), - vol.Optional(ATTR_SHOW_AS): cv.enum(EventShowAs), - vol.Optional(ATTR_IS_ALL_DAY): bool, - vol.Optional(ATTR_ATTENDEES): [ATTENDEE_SCHEMA], - } -) - -CALENDAR_SERVICE_MODIFY_SCHEMA = vol.Schema( - { - vol.Optional(ATTR_ENTITY_ID): cv.string, - vol.Required(ATTR_EVENT_ID): cv.string, - vol.Required(ATTR_CALENDAR_ID): cv.string, - vol.Optional(ATTR_START): cv.datetime, - vol.Optional(ATTR_END): cv.datetime, - vol.Required(ATTR_SUBJECT): cv.string, - vol.Optional(ATTR_BODY): cv.string, - vol.Optional(ATTR_LOCATION): cv.string, - vol.Optional(ATTR_CATEGORIES): [cv.string], - vol.Optional(ATTR_SENSITIVITY): cv.enum(EventSensitivity), - vol.Optional(ATTR_SHOW_AS): cv.enum(EventShowAs), - vol.Optional(ATTR_IS_ALL_DAY): bool, - vol.Optional(ATTR_ATTENDEES): [ATTENDEE_SCHEMA], - } -) - - -CALENDAR_SERVICE_REMOVE_SCHEMA = vol.Schema( - { - vol.Optional(ATTR_ENTITY_ID): cv.string, - vol.Required(ATTR_EVENT_ID): cv.string, - vol.Required(ATTR_CALENDAR_ID): cv.string, - } -) - -SINGLE_CALSEARCH_CONFIG = vol.Schema( - { - vol.Required(CONF_NAME): cv.string, - vol.Required(CONF_DEVICE_ID): cv.string, - vol.Optional(CONF_HOURS_FORWARD_TO_GET, default=24): int, - vol.Optional(CONF_HOURS_BACKWARD_TO_GET, default=0): int, - vol.Optional(CONF_SEARCH): cv.string, - vol.Optional(CONF_TRACK): cv.boolean, - vol.Optional(CONF_MAX_RESULTS): cv.positive_int, - } -) - -CALENDAR_DEVICE_SCHEMA = vol.Schema( - { - vol.Required(CONF_CAL_ID): cv.string, - vol.Required(CONF_ENTITIES, None): vol.All( - cv.ensure_list, [SINGLE_CALSEARCH_CONFIG] - ), - }, - extra=vol.ALLOW_EXTRA, -) diff --git a/custom_components/o365/notify.py b/custom_components/o365/notify.py index d8decb9..e61a2bd 100644 --- a/custom_components/o365/notify.py +++ b/custom_components/o365/notify.py @@ -2,32 +2,17 @@ import logging import os -from homeassistant.components.notify import BaseNotificationService - -from .const import ( - ATTR_ATTACHMENTS, - ATTR_DATA, - ATTR_MESSAGE_IS_HTML, - ATTR_PHOTOS, - ATTR_TARGET, - ATTR_TITLE, - ATTR_ZIP_ATTACHMENTS, - ATTR_ZIP_NAME, - CONF_ACCOUNT, - CONF_ACCOUNT_NAME, - CONF_CONFIG_TYPE, - DOMAIN, - NOTIFY_BASE_SCHEMA, - PERM_MAIL_SEND, - PERM_MINIMUM_SEND, -) -from .utils import ( - build_token_filename, - get_ha_filepath, - get_permissions, - validate_minimum_permission, - zip_files, -) +from homeassistant.components.notify import (ATTR_DATA, ATTR_TARGET, + ATTR_TITLE, + BaseNotificationService) + +from .const import (ATTR_ATTACHMENTS, ATTR_MESSAGE_IS_HTML, ATTR_PHOTOS, + ATTR_ZIP_ATTACHMENTS, ATTR_ZIP_NAME, CONF_ACCOUNT, + CONF_ACCOUNT_NAME, CONF_CONFIG_TYPE, DOMAIN, + PERM_MAIL_SEND, PERM_MINIMUM_SEND) +from .schema import NOTIFY_BASE_SCHEMA +from .utils import (build_token_filename, get_ha_filepath, get_permissions, + validate_minimum_permission, zip_files) _LOGGER = logging.getLogger(__name__) diff --git a/custom_components/o365/schema.py b/custom_components/o365/schema.py new file mode 100644 index 0000000..cd7f90f --- /dev/null +++ b/custom_components/o365/schema.py @@ -0,0 +1,231 @@ +"""Schema for O365 Integration.""" + +import homeassistant.helpers.config_validation as cv +import voluptuous as vol +from homeassistant.components.notify import ( + ATTR_DATA, + ATTR_MESSAGE, + ATTR_TARGET, + ATTR_TITLE, +) +from homeassistant.const import CONF_NAME +from O365.calendar import AttendeeType # pylint: disable=no-name-in-module +from O365.calendar import EventSensitivity # pylint: disable=no-name-in-module +from O365.calendar import EventShowAs # pylint: disable=no-name-in-module + +from .const import ( + ATTR_ATTACHMENTS, + ATTR_ATTENDEES, + ATTR_BODY, + ATTR_CALENDAR_ID, + ATTR_CATEGORIES, + ATTR_EMAIL, + ATTR_END, + ATTR_ENTITY_ID, + ATTR_EVENT_ID, + ATTR_IS_ALL_DAY, + ATTR_LOCATION, + ATTR_MESSAGE_IS_HTML, + ATTR_PHOTOS, + ATTR_RESPONSE, + ATTR_SEND_RESPONSE, + ATTR_SENSITIVITY, + ATTR_SHOW_AS, + ATTR_START, + ATTR_SUBJECT, + ATTR_TYPE, + ATTR_ZIP_ATTACHMENTS, + ATTR_ZIP_NAME, + CONF_ACCOUNT_NAME, + CONF_ACCOUNTS, + CONF_ALT_CONFIG, + CONF_CAL_ID, + CONF_CLIENT_ID, + CONF_CLIENT_SECRET, + CONF_DEVICE_ID, + CONF_DOWNLOAD_ATTACHMENTS, + CONF_EMAIL_SENSORS, + CONF_ENABLE_UPDATE, + CONF_ENTITIES, + CONF_HAS_ATTACHMENT, + CONF_HOURS_BACKWARD_TO_GET, + CONF_HOURS_FORWARD_TO_GET, + CONF_IMPORTANCE, + CONF_IS_UNREAD, + CONF_MAIL_FOLDER, + CONF_MAIL_FROM, + CONF_MAX_ITEMS, + CONF_MAX_RESULTS, + CONF_QUERY_SENSORS, + CONF_SEARCH, + CONF_STATUS_SENSORS, + CONF_SUBJECT_CONTAINS, + CONF_SUBJECT_IS, + CONF_TRACK, + CONF_TRACK_NEW, + EventResponse, +) + +EMAIL_SENSOR = vol.Schema( + { + vol.Required(CONF_NAME): cv.string, + vol.Optional(CONF_MAIL_FOLDER): cv.string, + vol.Optional(CONF_MAX_ITEMS, default=5): int, + vol.Optional(CONF_IS_UNREAD): bool, + vol.Optional(CONF_DOWNLOAD_ATTACHMENTS): bool, + } +) +STATUS_SENSOR = vol.Schema( + { + vol.Required(CONF_NAME): cv.string, + } +) +QUERY_SENSOR = vol.Schema( + { + vol.Required(CONF_NAME): cv.string, + vol.Optional(CONF_MAIL_FOLDER): cv.string, + vol.Optional(CONF_MAIL_FROM): cv.string, + vol.Optional(CONF_MAX_ITEMS, default=5): int, + vol.Optional(CONF_HAS_ATTACHMENT): bool, + vol.Optional(CONF_IMPORTANCE): cv.string, + vol.Optional(CONF_IS_UNREAD): bool, + vol.Exclusive(CONF_SUBJECT_CONTAINS, "subject_*"): cv.string, + vol.Exclusive(CONF_SUBJECT_IS, "subject_*"): cv.string, + vol.Optional(CONF_DOWNLOAD_ATTACHMENTS): bool, + } +) + +LEGACY_SCHEMA = vol.Schema( + { + vol.Required(CONF_CLIENT_ID): cv.string, + vol.Required(CONF_CLIENT_SECRET): cv.string, + vol.Optional(CONF_TRACK_NEW, default=True): bool, + vol.Optional(CONF_ENABLE_UPDATE, default=True): bool, + vol.Optional(CONF_ALT_CONFIG, default=False): bool, + vol.Optional(CONF_EMAIL_SENSORS): [EMAIL_SENSOR], + vol.Optional(CONF_QUERY_SENSORS): [QUERY_SENSOR], + vol.Optional(CONF_STATUS_SENSORS): [STATUS_SENSOR], + } +) +MULTI_ACCOUNT_SCHEMA = vol.Schema( + { + CONF_ACCOUNTS: vol.Schema( + [ + { + vol.Required(CONF_CLIENT_ID): cv.string, + vol.Required(CONF_CLIENT_SECRET): cv.string, + vol.Optional(CONF_TRACK_NEW, default=True): bool, + vol.Optional(CONF_ENABLE_UPDATE, default=False): bool, + vol.Required(CONF_ACCOUNT_NAME, ""): cv.string, + vol.Optional(CONF_ALT_CONFIG, default=False): bool, + vol.Optional(CONF_EMAIL_SENSORS): [EMAIL_SENSOR], + vol.Optional(CONF_QUERY_SENSORS): [QUERY_SENSOR], + vol.Optional(CONF_STATUS_SENSORS): [STATUS_SENSOR], + } + ] + ) + } +) + +NOTIFY_DATA_SCHEMA = vol.Schema( + { + vol.Optional(ATTR_MESSAGE_IS_HTML, default=False): bool, + vol.Optional(ATTR_TARGET): cv.string, + vol.Optional(ATTR_ZIP_ATTACHMENTS, default=False): bool, + vol.Optional(ATTR_ZIP_NAME): cv.string, + vol.Optional(ATTR_PHOTOS, default=[]): [cv.string], + vol.Optional(ATTR_ATTACHMENTS, default=[]): [cv.string], + } +) + +NOTIFY_BASE_SCHEMA = vol.Schema( + { + vol.Optional(ATTR_TARGET, default=[]): [cv.string], + vol.Optional(ATTR_TITLE, default=""): cv.string, + vol.Optional(ATTR_DATA): NOTIFY_DATA_SCHEMA, + } +) + +CALENDAR_SERVICE_RESPOND_SCHEMA = vol.Schema( + { + vol.Optional(ATTR_ENTITY_ID): cv.string, + vol.Required(ATTR_EVENT_ID): cv.string, + vol.Required(ATTR_CALENDAR_ID): cv.string, + vol.Optional(ATTR_RESPONSE, None): cv.enum(EventResponse), + vol.Optional(ATTR_SEND_RESPONSE, True): bool, + vol.Optional(ATTR_MESSAGE, None): cv.string, + } +) + +ATTENDEE_SCHEMA = vol.Schema( + { + vol.Required(ATTR_EMAIL): cv.string, + vol.Required(ATTR_TYPE): cv.enum(AttendeeType), + } +) + +CALENDAR_SERVICE_CREATE_SCHEMA = vol.Schema( + { + vol.Optional(ATTR_ENTITY_ID): cv.string, + vol.Required(ATTR_CALENDAR_ID): cv.string, + vol.Required(ATTR_START): cv.datetime, + vol.Required(ATTR_END): cv.datetime, + vol.Required(ATTR_SUBJECT): cv.string, + vol.Optional(ATTR_BODY): cv.string, + vol.Optional(ATTR_LOCATION): cv.string, + vol.Optional(ATTR_CATEGORIES): [cv.string], + vol.Optional(ATTR_SENSITIVITY): cv.enum(EventSensitivity), + vol.Optional(ATTR_SHOW_AS): cv.enum(EventShowAs), + vol.Optional(ATTR_IS_ALL_DAY): bool, + vol.Optional(ATTR_ATTENDEES): [ATTENDEE_SCHEMA], + } +) + +CALENDAR_SERVICE_MODIFY_SCHEMA = vol.Schema( + { + vol.Optional(ATTR_ENTITY_ID): cv.string, + vol.Required(ATTR_EVENT_ID): cv.string, + vol.Required(ATTR_CALENDAR_ID): cv.string, + vol.Optional(ATTR_START): cv.datetime, + vol.Optional(ATTR_END): cv.datetime, + vol.Required(ATTR_SUBJECT): cv.string, + vol.Optional(ATTR_BODY): cv.string, + vol.Optional(ATTR_LOCATION): cv.string, + vol.Optional(ATTR_CATEGORIES): [cv.string], + vol.Optional(ATTR_SENSITIVITY): cv.enum(EventSensitivity), + vol.Optional(ATTR_SHOW_AS): cv.enum(EventShowAs), + vol.Optional(ATTR_IS_ALL_DAY): bool, + vol.Optional(ATTR_ATTENDEES): [ATTENDEE_SCHEMA], + } +) + + +CALENDAR_SERVICE_REMOVE_SCHEMA = vol.Schema( + { + vol.Optional(ATTR_ENTITY_ID): cv.string, + vol.Required(ATTR_EVENT_ID): cv.string, + vol.Required(ATTR_CALENDAR_ID): cv.string, + } +) + +SINGLE_CALSEARCH_CONFIG = vol.Schema( + { + vol.Required(CONF_NAME): cv.string, + vol.Required(CONF_DEVICE_ID): cv.string, + vol.Optional(CONF_HOURS_FORWARD_TO_GET, default=24): int, + vol.Optional(CONF_HOURS_BACKWARD_TO_GET, default=0): int, + vol.Optional(CONF_SEARCH): cv.string, + vol.Optional(CONF_TRACK): cv.boolean, + vol.Optional(CONF_MAX_RESULTS): cv.positive_int, + } +) + +CALENDAR_DEVICE_SCHEMA = vol.Schema( + { + vol.Required(CONF_CAL_ID): cv.string, + vol.Required(CONF_ENTITIES, None): vol.All( + cv.ensure_list, [SINGLE_CALSEARCH_CONFIG] + ), + }, + extra=vol.ALLOW_EXTRA, +) diff --git a/custom_components/o365/utils.py b/custom_components/o365/utils.py index b7d8d7e..c0f8fd1 100644 --- a/custom_components/o365/utils.py +++ b/custom_components/o365/utils.py @@ -15,7 +15,6 @@ from voluptuous.error import Error as VoluptuousError from .const import ( - CALENDAR_DEVICE_SCHEMA, CONF_ACCOUNT_NAME, CONF_CAL_ID, CONF_CONFIG_TYPE, @@ -44,6 +43,7 @@ TOKEN_FILENAME, YAML_CALENDARS, ) +from .schema import CALENDAR_DEVICE_SCHEMA _LOGGER = logging.getLogger(__name__) @@ -182,26 +182,23 @@ def get_email_attributes(mail, download_attachments): def format_event_data(event, calendar_id): """Format the event data.""" - data = { + return { "summary": event.subject, + "start": event.start, + "end": event.end, + "all_day": event.is_all_day, "description": clean_html(event.body), "location": event.location["displayName"], "categories": event.categories, "sensitivity": event.sensitivity.name, "show_as": event.show_as.name, - "all_day": event.is_all_day, "attendees": [ {"email": x.address, "type": x.attendee_type.value} for x in event.attendees._Attendees__attendees # pylint: disable=protected-access ], - "start": event.start, - "end": event.end, "uid": event.object_id, "calendar_id": calendar_id, } - data["subject"] = data["summary"] - data["body"] = data["description"] - return data def add_call_data_to_event(event, event_data): diff --git a/hacs.json b/hacs.json index 46aae23..0df5919 100644 --- a/hacs.json +++ b/hacs.json @@ -2,9 +2,9 @@ "name": "Office 365 Integration", "zip_release": true, "filename": "o365.zip", - "homeassistant": "0.110", + "homeassistant": "2022.5.0", "content_in_root": false, - "domains": ["calendar"], + "domains": ["calendar","sensor"], "persistent_directory": ".O365-token-cache", "render_readme": true }