diff --git a/README.md b/README.md index 0f96eee..11f43cb 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ Under "Api Permissions" click Add a permission and add the following delegated p * Mail.Send - *Send mail as a user* * Mail.Send.Shared - *Send mail on behalf of others* * Users.Read - *Sign in and read user profile* -* +* Presence.Read - *Read user's presence information* (Required for Teams Presence Sensor) ## Adding to Home Assistant ### Using Home Assistant Community Store (HACS) @@ -71,6 +71,8 @@ o365: has_attachment: True max_items: 2 is_unread: True + status_sensors: + - name: "User Teams Status" ``` ## notify.o365_email service data @@ -125,6 +127,11 @@ Key | Type | Required | Description `subject_contains` | `string` | `False` | Only get emails where the subject contains this string (Mutually exclusive with `subject_is`) `subject_is` | `string` | `False` | Only get emails where the subject equals exactly this string (Mutually exclusive with `subject_contains`) +### status_sensors +Key | Type | Required | Description +-- | -- | -- | -- +`name` | `string` | `True` | The name of the sensor. + ## Calendar configuration This component has changed to now using an external o365_calendars.yaml file, this is done to align this component more with the official Google Calendar Event integration. ### example o365_calendar.yaml: diff --git a/custom_components/o365/__init__.py b/custom_components/o365/__init__.py index 6639a00..474653f 100644 --- a/custom_components/o365/__init__.py +++ b/custom_components/o365/__init__.py @@ -10,27 +10,14 @@ from homeassistant.helpers.network import get_url from O365 import Account, FileSystemTokenBackend -from .const import ( - AUTH_CALLBACK_NAME, - AUTH_CALLBACK_PATH, - AUTH_CALLBACK_PATH_ALT, - CONF_ALT_CONFIG, - CONF_CALENDARS, - CONF_CLIENT_ID, - CONF_CLIENT_SECRET, - CONF_EMAIL_SENSORS, - CONF_QUERY_SENSORS, - CONF_TRACK_NEW, - CONFIG_SCHEMA, - CONFIGURATOR_DESCRIPTION, - CONFIGURATOR_LINK_NAME, - CONFIGURATOR_SUBMIT_CAPTION, - DEFAULT_CACHE_PATH, - DEFAULT_NAME, - DOMAIN, - SCOPE, - TOKEN_FILENAME, -) +from .const import (AUTH_CALLBACK_NAME, AUTH_CALLBACK_PATH, + AUTH_CALLBACK_PATH_ALT, CONF_ALT_CONFIG, CONF_CALENDARS, + CONF_CLIENT_ID, CONF_CLIENT_SECRET, CONF_EMAIL_SENSORS, + CONF_STATUS_SENSORS, CONF_QUERY_SENSORS, CONF_TRACK_NEW, + CONFIG_SCHEMA, CONFIGURATOR_DESCRIPTION, + CONFIGURATOR_LINK_NAME, CONFIGURATOR_SUBMIT_CAPTION, + DEFAULT_CACHE_PATH, DEFAULT_NAME, DOMAIN, SCOPE, + TOKEN_FILENAME) from .utils import build_config_file_path, validate_permissions _LOGGER = logging.getLogger(__name__) @@ -92,6 +79,7 @@ def do_setup(hass, config, account): "account": account, CONF_EMAIL_SENSORS: config.get(CONF_EMAIL_SENSORS, []), CONF_QUERY_SENSORS: config.get(CONF_QUERY_SENSORS, []), + CONF_STATUS_SENSORS: config.get(CONF_STATUS_SENSORS, []), CONF_TRACK_NEW: config.get(CONF_TRACK_NEW, True), } hass.async_create_task( diff --git a/custom_components/o365/calendar.py b/custom_components/o365/calendar.py index 370de8b..ed4acfe 100644 --- a/custom_components/o365/calendar.py +++ b/custom_components/o365/calendar.py @@ -115,10 +115,10 @@ def extra_state_attributes(self): "offset_reached": self._offset_reached, "data": self._data_attribute, } - else: - return { - "data": self._data_attribute, - } + + return { + "data": self._data_attribute, + } @property def event(self): diff --git a/custom_components/o365/const.py b/custom_components/o365/const.py index eb2de3e..757a96b 100644 --- a/custom_components/o365/const.py +++ b/custom_components/o365/const.py @@ -4,12 +4,8 @@ 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.components.notify import (ATTR_DATA, ATTR_MESSAGE, + ATTR_TARGET, ATTR_TITLE) from homeassistant.config import get_default_config_dir from homeassistant.const import CONF_NAME from O365.calendar import AttendeeType # pylint: disable=no-name-in-module @@ -76,6 +72,7 @@ class EventResponse(Enum): CONF_MAIL_FOLDER = "folder" CONF_MAIL_FROM = "from" CONF_MAX_ITEMS = "max_items" +CONF_STATUS_SENSORS = "status_sensors" CONF_QUERY_SENSORS = "query_sensors" CONF_SUBJECT_CONTAINS = "subject_contains" CONF_SUBJECT_IS = "subject_is" @@ -130,6 +127,11 @@ class EventResponse(Enum): vol.Optional(CONF_IS_UNREAD): bool, } ) +STATUS_SENSOR = vol.Schema( + { + vol.Required(CONF_NAME): cv.string, + } +) QUERY_SENSOR = vol.Schema( { vol.Required(CONF_NAME): cv.string, @@ -154,6 +156,7 @@ class EventResponse(Enum): vol.Optional(CONF_CALENDARS, default=[]): [CALENDAR_SCHEMA], vol.Optional(CONF_EMAIL_SENSORS): [EMAIL_SENSOR], vol.Optional(CONF_QUERY_SENSORS): [QUERY_SENSOR], + vol.Optional(CONF_STATUS_SENSORS): [STATUS_SENSOR], }, ) }, diff --git a/custom_components/o365/sensor.py b/custom_components/o365/sensor.py index de56821..a809f66 100644 --- a/custom_components/o365/sensor.py +++ b/custom_components/o365/sensor.py @@ -8,7 +8,8 @@ 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) + CONF_STATUS_SENSORS, CONF_SUBJECT_CONTAINS, + CONF_SUBJECT_IS, DOMAIN) from .utils import get_email_attributes _LOGGER = logging.getLogger(__name__) @@ -28,6 +29,7 @@ def setup_platform( _unread_sensors(hass, account, add_devices) _query_sensors(hass, account, add_devices) + _status_sensors(hass, account, add_devices) return True @@ -48,6 +50,13 @@ def _query_sensors(hass, account, add_devices): add_devices([sensor], True) +def _status_sensors(hass, account, add_devices): + status_sensors = hass.data[DOMAIN].get(CONF_STATUS_SENSORS, []) + for conf in status_sensors: + teams_status_sensor = O365TeamsStatusSensor(account, conf) + add_devices([teams_status_sensor], True) + + def _get_mail_folder(account, conf, sensor_type): """Get the configured folder.""" mailbox = account.mailbox() @@ -73,7 +82,7 @@ def _get_mail_folder(account, conf, sensor_type): return mail_folder -class O365Sensor: +class O365MailSensor: """O365 generic Sensor class.""" def __init__(self, conf, mail_folder): @@ -114,7 +123,7 @@ def update(self): self._attributes = {"data": attrs} -class O365QuerySensor(O365Sensor, Entity): +class O365QuerySensor(O365MailSensor, Entity): """O365 Query sensor processing.""" def __init__(self, conf, mail_folder): @@ -164,8 +173,8 @@ def _add_to_query(self, qtype, attribute_name, attribute_value, check_value=True self.query.chain("and").on_attribute(attribute_name).equals(attribute_value) -class O365InboxSensor(O365Sensor, Entity): - """O365 Inboox processing.""" +class O365InboxSensor(O365MailSensor, Entity): + """O365 Inbox processing.""" def __init__(self, conf, mail_folder): """Initialise the O365 Inbox.""" @@ -177,3 +186,27 @@ def __init__(self, conf, mail_folder): if self.is_unread is not None: self.query = self.mail_folder.new_query() self.query.chain("and").on_attribute("IsRead").equals(not self.is_unread) + + +class O365TeamsStatusSensor(Entity): + """O365 Teams sensor processing.""" + + def __init__(self, account, conf): + """Initialise the Teams Sensor.""" + self.teams = account.teams() + self._name = conf.get(CONF_NAME) + self._state = None + + @property + def name(self): + """Sensor name.""" + return self._name + + @property + def state(self): + """Sensor state.""" + return self._state + + def update(self): + """Update state.""" + self._state = self.teams.get_my_presence().activity