diff --git a/custom_components/o365/calendar.py b/custom_components/o365/calendar.py index a274b0a..370de8b 100644 --- a/custom_components/o365/calendar.py +++ b/custom_components/o365/calendar.py @@ -4,42 +4,22 @@ from datetime import datetime, timedelta from operator import attrgetter, itemgetter -from homeassistant.components.calendar import ( - CalendarEventDevice, - calculate_offset, - is_offset_reached, -) +from homeassistant.components.calendar import (CalendarEventDevice, + calculate_offset, + is_offset_reached) from homeassistant.helpers.entity import generate_entity_id from homeassistant.util import Throttle, dt -from .const import ( - CALENDAR_ENTITY_ID_FORMAT, - CALENDAR_SERVICE_CREATE_SCHEMA, - CALENDAR_SERVICE_MODIFY_SCHEMA, - CALENDAR_SERVICE_REMOVE_SCHEMA, - CALENDAR_SERVICE_RESPOND_SCHEMA, - CONF_DEVICE_ID, - CONF_ENTITIES, - CONF_HOURS_BACKWARD_TO_GET, - CONF_HOURS_FORWARD_TO_GET, - CONF_MAX_RESULTS, - CONF_NAME, - CONF_SEARCH, - CONF_TRACK, - CONF_TRACK_NEW, - DEFAULT_OFFSET, - DOMAIN, - MIN_TIME_BETWEEN_UPDATES, - YAML_CALENDARS, -) -from .utils import ( - add_call_data_to_event, - build_config_file_path, - clean_html, - format_event_data, - load_calendars, - update_calendar_file, -) +from .const import (CALENDAR_ENTITY_ID_FORMAT, CALENDAR_SERVICE_CREATE_SCHEMA, + CALENDAR_SERVICE_MODIFY_SCHEMA, + CALENDAR_SERVICE_REMOVE_SCHEMA, + CALENDAR_SERVICE_RESPOND_SCHEMA, CONF_DEVICE_ID, + CONF_ENTITIES, CONF_HOURS_BACKWARD_TO_GET, + CONF_HOURS_FORWARD_TO_GET, CONF_MAX_RESULTS, CONF_NAME, + CONF_SEARCH, CONF_TRACK, CONF_TRACK_NEW, DEFAULT_OFFSET, + DOMAIN, MIN_TIME_BETWEEN_UPDATES, YAML_CALENDARS) +from .utils import (add_call_data_to_event, build_config_file_path, clean_html, + format_event_data, load_calendars, update_calendar_file) _LOGGER = logging.getLogger(__name__) @@ -52,14 +32,16 @@ def setup_platform( return None account = hass.data[DOMAIN]["account"] - track_new = hass.data[DOMAIN][CONF_TRACK_NEW] - is_authenticated = account.is_authenticated - if not is_authenticated: + if not account.is_authenticated: return False - calendar_services = CalendarServices(account, track_new, hass) - calendar_services.scan_for_calendars(None) + _setup_add_devices(hass, account, add_devices) + _setup_register_services(hass, account) + return True + + +def _setup_add_devices(hass, account, add_devices): calendars = load_calendars(build_config_file_path(hass, YAML_CALENDARS)) devices = [] @@ -74,6 +56,12 @@ def setup_platform( devices.append(cal) add_devices(devices, True) + +def _setup_register_services(hass, account): + track_new = hass.data[DOMAIN][CONF_TRACK_NEW] + calendar_services = CalendarServices(account, track_new, hass) + calendar_services.scan_for_calendars(None) + hass.services.register( DOMAIN, "modify_calendar_event", calendar_services.modify_calendar_event ) @@ -90,8 +78,6 @@ def setup_platform( DOMAIN, "scan_for_calendars", calendar_services.scan_for_calendars ) - return True - class O365CalendarEventDevice(CalendarEventDevice): """O365 Calendar Event Processing.""" @@ -307,7 +293,7 @@ def __init__(self, account, track_new_found_calendars, hass): def modify_calendar_event(self, call): """Modify the event.""" event_data = call.data - CALENDAR_SERVICE_MODIFY_SCHEMA({k: v for k, v in event_data.items()}) + CALENDAR_SERVICE_MODIFY_SCHEMA(dict(event_data.items())) calendar = self.schedule.get_calendar(calendar_id=event_data.get("calendar_id")) event = calendar.get_event(event_data["event_id"]) event = add_call_data_to_event(event, call.data) @@ -316,7 +302,7 @@ def modify_calendar_event(self, call): def create_calendar_event(self, call): """Create the event.""" event_data = call.data - CALENDAR_SERVICE_CREATE_SCHEMA({k: v for k, v in event_data.items()}) + CALENDAR_SERVICE_CREATE_SCHEMA(dict(event_data.items())) calendar = self.schedule.get_calendar(calendar_id=event_data.get("calendar_id")) event = calendar.new_event() event = add_call_data_to_event(event, call.data) @@ -325,7 +311,7 @@ def create_calendar_event(self, call): def remove_calendar_event(self, call): """Remove the event.""" event_data = call.data - CALENDAR_SERVICE_REMOVE_SCHEMA({k: v for k, v in event_data.items()}) + CALENDAR_SERVICE_REMOVE_SCHEMA(dict(event_data.items())) calendar = self.schedule.get_calendar(calendar_id=event_data.get("calendar_id")) event = calendar.get_event(event_data["event_id"]) event.delete() @@ -333,7 +319,7 @@ def remove_calendar_event(self, call): def respond_calendar_event(self, call): """Respond to calendar event.""" event_data = call.data - CALENDAR_SERVICE_RESPOND_SCHEMA({k: v for k, v in event_data.items()}) + CALENDAR_SERVICE_RESPOND_SCHEMA(dict(event_data.items())) calendar = self.schedule.get_calendar(calendar_id=event_data.get("calendar_id")) event = calendar.get_event(event_data["event_id"]) response = event_data.get("response") diff --git a/custom_components/o365/sensor.py b/custom_components/o365/sensor.py index 2102c79..de56821 100644 --- a/custom_components/o365/sensor.py +++ b/custom_components/o365/sensor.py @@ -5,20 +5,10 @@ from homeassistant.helpers.entity import Entity -from .const import ( - CONF_EMAIL_SENSORS, - CONF_HAS_ATTACHMENT, - CONF_IMPORTANCE, - CONF_IS_UNREAD, - CONF_MAIL_FOLDER, - CONF_MAIL_FROM, - CONF_MAX_ITEMS, - CONF_NAME, - CONF_QUERY_SENSORS, - CONF_SUBJECT_CONTAINS, - CONF_SUBJECT_IS, - DOMAIN, -) +from .const import (CONF_EMAIL_SENSORS, CONF_HAS_ATTACHMENT, CONF_IMPORTANCE, + CONF_IS_UNREAD, CONF_MAIL_FOLDER, CONF_MAIL_FROM, + CONF_MAX_ITEMS, CONF_NAME, CONF_QUERY_SENSORS, + CONF_SUBJECT_CONTAINS, CONF_SUBJECT_IS, DOMAIN) from .utils import get_email_attributes _LOGGER = logging.getLogger(__name__) @@ -26,27 +16,34 @@ def setup_platform( hass, config, add_devices, discovery_info=None -): # pylint: disable=unused-argument +): # pylint: disable=unused-argument """O365 platform definition.""" if discovery_info is None: - return + return None account = hass.data[DOMAIN]["account"] is_authenticated = account.is_authenticated if not is_authenticated: return False + _unread_sensors(hass, account, add_devices) + _query_sensors(hass, account, add_devices) + + return True + + +def _unread_sensors(hass, account, add_devices): unread_sensors = hass.data[DOMAIN].get(CONF_EMAIL_SENSORS, []) for conf in unread_sensors: - mail_folder = _get_mail_folder(account, conf, CONF_EMAIL_SENSORS) - if mail_folder: + if mail_folder := _get_mail_folder(account, conf, CONF_EMAIL_SENSORS): sensor = O365InboxSensor(conf, mail_folder) add_devices([sensor], True) + +def _query_sensors(hass, account, add_devices): query_sensors = hass.data[DOMAIN].get(CONF_QUERY_SENSORS, []) for conf in query_sensors: - mail_folder = _get_mail_folder(account, conf, CONF_QUERY_SENSORS) - if mail_folder: + if mail_folder := _get_mail_folder(account, conf, CONF_QUERY_SENSORS): sensor = O365QuerySensor(conf, mail_folder) add_devices([sensor], True) @@ -55,15 +52,8 @@ def _get_mail_folder(account, conf, sensor_type): """Get the configured folder.""" mailbox = account.mailbox() mail_folder = None - mail_folder_conf = conf.get(CONF_MAIL_FOLDER) - if mail_folder_conf: + if mail_folder_conf := conf.get(CONF_MAIL_FOLDER): for i, folder in enumerate(mail_folder_conf.split("/")): - _LOGGER.debug( - "Processing folder - %s - from %s config entry - %s ", - folder, - sensor_type, - mail_folder_conf, - ) if i == 0: mail_folder = mailbox.get_folder(folder_name=folder) else: @@ -77,9 +67,6 @@ def _get_mail_folder(account, conf, sensor_type): mail_folder_conf, ) return None - - # _LOGGER.debug(f"Got folder id - {mail_folder.folder_id}") - else: mail_folder = mailbox.inbox_folder() diff --git a/custom_components/o365/utils.py b/custom_components/o365/utils.py index 555dce1..e0f5049 100644 --- a/custom_components/o365/utils.py +++ b/custom_components/o365/utils.py @@ -10,22 +10,14 @@ from bs4 import BeautifulSoup from homeassistant.util import dt from O365.calendar import Attendee # pylint: disable=no-name-in-module) -from O365.calendar import EventSensitivity # pylint: disable=no-name-in-module) +from O365.calendar import \ + EventSensitivity # pylint: disable=no-name-in-module) from voluptuous.error import Error as VoluptuousError -from .const import ( - CALENDAR_DEVICE_SCHEMA, - CONF_CAL_ID, - CONF_DEVICE_ID, - CONF_ENTITIES, - CONF_NAME, - CONF_TRACK, - CONFIG_BASE_DIR, - DATETIME_FORMAT, - DEFAULT_CACHE_PATH, - MINIMUM_REQUIRED_SCOPES, - TOKEN_FILENAME, -) +from .const import (CALENDAR_DEVICE_SCHEMA, CONF_CAL_ID, CONF_DEVICE_ID, + CONF_ENTITIES, CONF_NAME, CONF_TRACK, CONFIG_BASE_DIR, + DATETIME_FORMAT, DEFAULT_CACHE_PATH, + MINIMUM_REQUIRED_SCOPES, TOKEN_FILENAME) _LOGGER = logging.getLogger(__name__) @@ -33,9 +25,8 @@ def clean_html(html): """Clean the HTML.""" soup = BeautifulSoup(html, features="html.parser") - body = soup.find("body") - if body: - return soup.find("body").get_text(" ", strip=True) + if body := soup.find("body"): + return body.get_text(" ", strip=True) return html @@ -50,7 +41,7 @@ def validate_permissions(hass, token_path=DEFAULT_CACHE_PATH, filename=TOKEN_FIL with open(full_token_path, "r", encoding="UTF-8") as file_handle: raw = file_handle.read() permissions = json.loads(raw)["scope"] - scope = [x for x in MINIMUM_REQUIRED_SCOPES] # noqa: C416 + scope = list(MINIMUM_REQUIRED_SCOPES) all_permissions_granted = all(x in permissions for x in scope) if not all_permissions_granted: _LOGGER.warning("All permissions granted: %s", all_permissions_granted) @@ -110,7 +101,7 @@ def format_event_data(event, calendar_id): "all_day": event.is_all_day, "attendees": [ {"email": x.address, "type": x.attendee_type.value} - for x in event.attendees._Attendees__attendees + for x in event.attendees._Attendees__attendees # pylint: disable=protected-access ], "start": event.start, "end": event.end, @@ -124,28 +115,22 @@ def format_event_data(event, calendar_id): def add_call_data_to_event(event, event_data): """Add the call data.""" - subject = event_data.get("subject") - if subject: + if subject := event_data.get("subject"): event.subject = subject - body = event_data.get("body") - if body: + if body := event_data.get("body"): event.body = body - location = event_data.get("location") - if location: + if location := event_data.get("location"): event.location = location - categories = event_data.get("categories") - if categories: + if categories := event_data.get("categories"): event.categories = categories - show_as = event_data.get("show_as") - if show_as: + if show_as := event_data.get("show_as"): event.show_as = show_as - attendees = event_data.get("attendees") - if attendees: + if attendees := event_data.get("attendees"): event.attendees.clear() event.attendees.add( [ @@ -154,12 +139,10 @@ def add_call_data_to_event(event, event_data): ] ) - start = event_data.get("start") - if start: + if start := event_data.get("start"): event.start = dt.parse_datetime(start) - end = event_data.get("end") - if end: + if end := event_data.get("end"): event.end = dt.parse_datetime(end) is_all_day = event_data.get("is_all_day") @@ -173,8 +156,7 @@ def add_call_data_to_event(event, event_data): event.end.year, event.end.month, event.end.day, 0, 0, 0 ) - sensitivity = event_data.get("sensitivity") - if sensitivity: + if sensitivity := event_data.get("sensitivity"): event.sensitivity = EventSensitivity(sensitivity.lower()) return event @@ -189,9 +171,7 @@ def load_calendars(path): return {} for calendar in data: try: - calendars.update( - {calendar[CONF_CAL_ID]: CALENDAR_DEVICE_SCHEMA(calendar)} - ) + calendars[calendar[CONF_CAL_ID]] = CALENDAR_DEVICE_SCHEMA(calendar) except VoluptuousError as exception: # keep going _LOGGER.warning("Calendar Invalid Data: %s", exception)