diff --git a/custom_components/o365/__init__.py b/custom_components/o365/__init__.py index 5391c9e..e5479e2 100644 --- a/custom_components/o365/__init__.py +++ b/custom_components/o365/__init__.py @@ -25,6 +25,7 @@ CONF_ACCOUNT_NAME, CONF_ACCOUNTS, CONF_ALT_AUTH_METHOD, + CONF_AUTO_REPLY_SENSORS, CONF_CHAT_SENSORS, CONF_CLIENT_ID, CONF_CLIENT_SECRET, @@ -196,6 +197,7 @@ def do_setup(hass, config, account, account_name, conf_type): status_sensors = config.get(CONF_STATUS_SENSORS, []) chat_sensors = config.get(CONF_CHAT_SENSORS, []) todo_sensors = config.get(CONF_TODO_SENSORS, []) + auto_reply_sensors = config.get(CONF_AUTO_REPLY_SENSORS, []) enable_update = config.get(CONF_ENABLE_UPDATE, True) account_config = { @@ -205,6 +207,7 @@ def do_setup(hass, config, account, account_name, conf_type): CONF_STATUS_SENSORS: status_sensors, CONF_CHAT_SENSORS: chat_sensors, CONF_TODO_SENSORS: todo_sensors, + CONF_AUTO_REPLY_SENSORS: auto_reply_sensors, CONF_ENABLE_UPDATE: enable_update, CONF_TRACK_NEW_CALENDAR: config.get(CONF_TRACK_NEW_CALENDAR, True), CONF_ACCOUNT_NAME: config.get(CONF_ACCOUNT_NAME, ""), diff --git a/custom_components/o365/classes/mailsensor.py b/custom_components/o365/classes/mailsensor.py index b51f953..30bb895 100644 --- a/custom_components/o365/classes/mailsensor.py +++ b/custom_components/o365/classes/mailsensor.py @@ -4,6 +4,8 @@ import voluptuous as vol from homeassistant.components.sensor import SensorEntity +from O365.mailbox import ExternalAudience # pylint: disable=no-name-in-module + from ..const import ( ATTR_ATTRIBUTES, CONF_ACCOUNT, @@ -19,6 +21,7 @@ CONF_SUBJECT_IS, PERM_MAILBOX_SETTINGS, PERM_MINIMUM_MAILBOX_SETTINGS, + SENSOR_AUTO_REPLY, SENSOR_MAIL, ) from ..utils import build_token_filename, get_permissions, validate_minimum_permission @@ -49,48 +52,6 @@ def extra_state_attributes(self): """Device state attributes.""" return self.coordinator.data[self.entity_key][ATTR_ATTRIBUTES] - def auto_reply_enable( - self, - external_reply, - internal_reply, - start=None, - end=None, - external_audience=None, - ): - """Enable out of office autoreply.""" - if not self._validate_permissions(): - return - - account = self._config[CONF_ACCOUNT] - mailbox = account.mailbox() - mailbox.set_automatic_reply( - internal_reply, external_reply, start, end, external_audience - ) - - def auto_reply_disable(self): - """Disable out of office autoreply.""" - if not self._validate_permissions(): - return - - account = self._config[CONF_ACCOUNT] - mailbox = account.mailbox() - mailbox.set_disable_reply() - - def _validate_permissions(self): - permissions = get_permissions( - self.hass, - filename=build_token_filename( - self._config, self._config.get(CONF_CONFIG_TYPE) - ), - ) - if not validate_minimum_permission(PERM_MINIMUM_MAILBOX_SETTINGS, permissions): - raise vol.Invalid( - "Not authorisied to update auto reply - requires permission: " - + f"{PERM_MAILBOX_SETTINGS}" - ) - - return True - class O365QuerySensor(O365MailSensor, SensorEntity): """O365 Query sensor processing.""" @@ -167,3 +128,64 @@ def __init__( if is_unread is not None: self.query = self.mail_folder.new_query() self.query.chain("and").on_attribute("IsRead").equals(not is_unread) + + +class O365AutoReplySensor(O365Sensor, SensorEntity): + """O365 Tasks sensor processing.""" + + def __init__(self, coordinator, name, entity_id, config, unqique_id): + """Initialise the Tasks Sensor.""" + super().__init__(coordinator, name, entity_id, SENSOR_AUTO_REPLY, unqique_id) + self._config = config + + @property + def state(self): + """Sensor state.""" + return "TBC" + + @property + def icon(self): + """Entity icon.""" + return "mdi:reply-all" + + def auto_reply_enable( + self, + external_reply, + internal_reply, + start=None, + end=None, + external_audience=ExternalAudience.ALL, + ): + """Enable out of office autoreply.""" + if not self._validate_permissions(): + return + + account = self._config[CONF_ACCOUNT] + mailbox = account.mailbox() + mailbox.set_automatic_reply( + internal_reply, external_reply, start, end, external_audience + ) + + def auto_reply_disable(self): + """Disable out of office autoreply.""" + if not self._validate_permissions(): + return + + account = self._config[CONF_ACCOUNT] + mailbox = account.mailbox() + mailbox.set_disable_reply() + + def _validate_permissions(self): + permissions = get_permissions( + self.hass, + filename=build_token_filename( + self._config, self._config.get(CONF_CONFIG_TYPE) + ), + ) + if not validate_minimum_permission(PERM_MINIMUM_MAILBOX_SETTINGS, permissions): + raise vol.Invalid( + "Not authorisied to update auto reply - requires permission: " + + f"{PERM_MAILBOX_SETTINGS}" + ) + + return True diff --git a/custom_components/o365/classes/taskssensor.py b/custom_components/o365/classes/taskssensor.py index 9a514cd..25873fe 100644 --- a/custom_components/o365/classes/taskssensor.py +++ b/custom_components/o365/classes/taskssensor.py @@ -24,9 +24,9 @@ class O365TasksSensor(O365Sensor, SensorEntity): """O365 Tasks sensor processing.""" - def __init__(self, coordinator, todo, name, entity_id, config, unqique_id): + def __init__(self, coordinator, todo, name, entity_id, config, unique_id): """Initialise the Tasks Sensor.""" - super().__init__(coordinator, name, entity_id, SENSOR_TODO, unqique_id) + super().__init__(coordinator, name, entity_id, SENSOR_TODO, unique_id) self.todo = todo self.query = self.todo.new_query("status").unequal("completed") self._config = config diff --git a/custom_components/o365/const.py b/custom_components/o365/const.py index 8f86d0a..df7c79b 100644 --- a/custom_components/o365/const.py +++ b/custom_components/o365/const.py @@ -64,6 +64,7 @@ class EventResponse(Enum): CONF_ACCOUNT_NAME = "account_name" CONF_ALIASES = "aliases" CONF_ALT_AUTH_METHOD = "alt_auth_method" +CONF_AUTO_REPLY_SENSORS = "auto_reply_sensors" CONF_BODY_CONTAINS = "body_contains" CONF_CACHE_PATH = "cache_path" CONF_CALENDAR_NAME = "calendar_name" @@ -175,6 +176,7 @@ class EventResponse(Enum): [PERM_MAIL_SEND_SHARED], ] +SENSOR_AUTO_REPLY = "auto_reply" SENSOR_DOMAIN = "sensor" SENSOR_ENTITY_ID_FORMAT = SENSOR_DOMAIN + ".{}" SENSOR_MAIL = "inbox" diff --git a/custom_components/o365/schema.py b/custom_components/o365/schema.py index 521dcb4..a0cffd4 100644 --- a/custom_components/o365/schema.py +++ b/custom_components/o365/schema.py @@ -9,6 +9,7 @@ ATTR_TITLE, ) from homeassistant.const import CONF_ENABLED, 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 @@ -46,6 +47,7 @@ CONF_ACCOUNT_NAME, CONF_ACCOUNTS, CONF_ALT_AUTH_METHOD, + CONF_AUTO_REPLY_SENSORS, CONF_BODY_CONTAINS, CONF_CAL_ID, CONF_CHAT_SENSORS, @@ -98,6 +100,11 @@ vol.Required(CONF_NAME): cv.string, } ) +AUTO_REPLY_SENSOR = vol.Schema( + { + vol.Required(CONF_NAME): cv.string, + } +) QUERY_SENSOR = vol.Schema( { vol.Required(CONF_NAME): cv.string, @@ -131,6 +138,7 @@ vol.Optional(CONF_QUERY_SENSORS): [QUERY_SENSOR], vol.Optional(CONF_STATUS_SENSORS): [STATUS_SENSOR], vol.Optional(CONF_CHAT_SENSORS): [CHAT_SENSOR], + vol.Optional(CONF_AUTO_REPLY_SENSORS): [AUTO_REPLY_SENSOR], } ) MULTI_ACCOUNT_SCHEMA = vol.Schema( @@ -150,6 +158,7 @@ vol.Optional(CONF_STATUS_SENSORS): [STATUS_SENSOR], vol.Optional(CONF_CHAT_SENSORS): [CHAT_SENSOR], vol.Optional(CONF_TODO_SENSORS): TODO_SENSOR, + vol.Optional(CONF_AUTO_REPLY_SENSORS): [AUTO_REPLY_SENSOR], } ] ) diff --git a/custom_components/o365/sensor.py b/custom_components/o365/sensor.py index 2bb5455..cb4a96a 100644 --- a/custom_components/o365/sensor.py +++ b/custom_components/o365/sensor.py @@ -12,7 +12,7 @@ ) # UpdateFailed, from requests.exceptions import HTTPError -from .classes.mailsensor import O365EmailSensor, O365QuerySensor +from .classes.mailsensor import O365AutoReplySensor, O365EmailSensor, O365QuerySensor from .classes.taskssensor import O365TasksSensor from .classes.teamssensor import O365TeamsChatSensor, O365TeamsStatusSensor from .const import ( @@ -28,6 +28,7 @@ ATTR_TASKS, CONF_ACCOUNT, CONF_ACCOUNT_NAME, + CONF_AUTO_REPLY_SENSORS, CONF_CHAT_SENSORS, CONF_CONFIG_TYPE, CONF_EMAIL_SENSORS, @@ -122,12 +123,14 @@ async def async_setup_entries(self): status_entities = self._status_sensors() chat_entities = self._chat_sensors() todo_entities = await self._async_todo_sensors() + auto_reply_entities = await self._async_auto_reply_sensors() self._entities = ( email_entities + query_entities + status_entities + chat_entities + todo_entities + + auto_reply_entities ) return self._entities @@ -280,6 +283,23 @@ async def _async_todo_entities(self, task_lists, config): ) return entities + async def _async_auto_reply_sensors(self): + auto_reply_sensors = self._config.get(CONF_AUTO_REPLY_SENSORS, []) + entities = [] + for sensor_conf in auto_reply_sensors: + name = sensor_conf.get(CONF_NAME) + entity_id = async_generate_entity_id( + SENSOR_ENTITY_ID_FORMAT, + name, + hass=self.hass, + ) + unique_id = f"{name}_{self._account_name}" + auto_reply_sensor = O365AutoReplySensor( + self, name, entity_id, self._config, unique_id + ) + entities.append(auto_reply_sensor) + return entities + async def _async_get_mail_folder(self, sensor_conf, sensor_type): """Get the configured folder.""" mailbox = self._account.mailbox() diff --git a/custom_components/o365/utils.py b/custom_components/o365/utils.py index f490fd0..91ecee4 100644 --- a/custom_components/o365/utils.py +++ b/custom_components/o365/utils.py @@ -10,11 +10,13 @@ import yaml from bs4 import BeautifulSoup from homeassistant.const import CONF_ENABLED, CONF_NAME -from O365.calendar import Attendee # pylint: disable=no-name-in-module) from voluptuous.error import Error as VoluptuousError +from O365.calendar import Attendee # pylint: disable=no-name-in-module) + from .const import ( CONF_ACCOUNT_NAME, + CONF_AUTO_REPLY_SENSORS, CONF_CAL_ID, CONF_CHAT_SENSORS, CONF_CONFIG_TYPE, @@ -46,6 +48,7 @@ PERM_MINIMUM_CHAT, PERM_MINIMUM_GROUP, PERM_MINIMUM_MAIL, + PERM_MINIMUM_MAILBOX_SETTINGS, PERM_MINIMUM_PRESENCE, PERM_MINIMUM_TASKS, PERM_MINIMUM_USER, @@ -87,6 +90,7 @@ def build_minimum_permissions(hass, config, conf_type): status_sensors = config.get(CONF_STATUS_SENSORS, []) chat_sensors = config.get(CONF_CHAT_SENSORS, []) todo_sensors = config.get(CONF_TODO_SENSORS, []) + auto_reply_sensors = config.get(CONF_AUTO_REPLY_SENSORS, []) minimum_permissions = [PERM_MINIMUM_USER, PERM_MINIMUM_CALENDAR] if len(email_sensors) > 0 or len(query_sensors) > 0: minimum_permissions.append(PERM_MINIMUM_MAIL) @@ -96,6 +100,8 @@ def build_minimum_permissions(hass, config, conf_type): minimum_permissions.append(PERM_MINIMUM_CHAT) if len(todo_sensors) > 0 and todo_sensors.get(CONF_ENABLED, False): minimum_permissions.append(PERM_MINIMUM_TASKS) + if len(auto_reply_sensors) > 0: + minimum_permissions.append(PERM_MINIMUM_MAILBOX_SETTINGS) if group_permissions_required(hass, config, conf_type): minimum_permissions.append(PERM_MINIMUM_GROUP) @@ -112,6 +118,7 @@ def build_requested_permissions(config): todo_sensors = config.get(CONF_TODO_SENSORS, []) enable_update = config.get(CONF_ENABLE_UPDATE, True) groups = config.get(CONF_GROUPS, False) + auto_reply_sensors = config.get(CONF_AUTO_REPLY_SENSORS, []) scope = [PERM_OFFLINE_ACCESS, PERM_USER_READ] if enable_update: scope.extend((PERM_MAIL_SEND, PERM_CALENDARS_READWRITE)) @@ -124,8 +131,8 @@ def build_requested_permissions(config): scope.append(PERM_GROUP_READ_ALL) if len(email_sensors) > 0 or len(query_sensors) > 0: scope.append(PERM_MAIL_READ) - if enable_update: - scope.append(PERM_MAILBOX_SETTINGS) + if len(auto_reply_sensors) > 0: + scope.append(PERM_MAILBOX_SETTINGS) if len(status_sensors) > 0: scope.append(PERM_PRESENCE_READ) if len(chat_sensors) > 0: