diff --git a/custom_components/o365/classes/teamssensor.py b/custom_components/o365/classes/teamssensor.py index 5938216..75fc015 100644 --- a/custom_components/o365/classes/teamssensor.py +++ b/custom_components/o365/classes/teamssensor.py @@ -6,6 +6,8 @@ from homeassistant.const import ATTR_NAME, CONF_EMAIL from homeassistant.exceptions import ServiceValidationError +from O365.teams import PreferredActivity, PreferredAvailability + from ..const import ( ATTR_ACTIVITY, ATTR_AVAILABILITY, @@ -23,6 +25,7 @@ DOMAIN, EVENT_HA_EVENT, EVENT_SEND_CHAT_MESSAGE, + EVENT_UPDATE_USER_PREFERRED_STATUS, EVENT_UPDATE_USER_STATUS, PERM_CHAT_READWRITE, PERM_MINIMUM_CHAT_WRITE, @@ -91,6 +94,34 @@ def update_user_status(self, availability, activity, expiration_duration=None): ) return False + def update_user_preferred_status(self, availability, expiration_duration=None): + """Update the users teams status.""" + if self._email: + raise ServiceValidationError( + translation_domain=DOMAIN, + translation_key="not_possible", + translation_placeholders={ + CONF_EMAIL: self._email, + }, + ) + + if not self._validate_status_permissions(): + return False + + activity = ( + availability + if availability != PreferredAvailability.OFFLINE + else PreferredActivity.OFFWORK + ) + status = self.teams.set_my_user_preferred_presence( + availability, activity, expiration_duration + ) + self._raise_event( + EVENT_UPDATE_USER_PREFERRED_STATUS, + {ATTR_AVAILABILITY: status.availability, ATTR_ACTIVITY: status.activity}, + ) + return False + def _raise_event(self, event_type, status): self.hass.bus.fire( f"{DOMAIN}_{event_type}", diff --git a/custom_components/o365/const.py b/custom_components/o365/const.py index 407e57f..28f7eb7 100644 --- a/custom_components/o365/const.py +++ b/custom_components/o365/const.py @@ -157,6 +157,7 @@ class EventResponse(Enum): EVENT_RESPOND_CALENDAR_EVENT = "respond_calendar_event" EVENT_SEND_CHAT_MESSAGE = "send_chat_message" EVENT_UPDATE_USER_STATUS = "update_user_status" +EVENT_UPDATE_USER_PREFERRED_STATUS = "update_user_preferred_status" LEGACY_ACCOUNT_NAME = "converted" O365_STORAGE = "o365_storage" diff --git a/custom_components/o365/icons.json b/custom_components/o365/icons.json index a5e7dd2..312e3df 100644 --- a/custom_components/o365/icons.json +++ b/custom_components/o365/icons.json @@ -13,7 +13,8 @@ "auto_reply_enable": "mdi:microsoft-outlook", "auto_reply_disable": "mdi:microsoft-outlook", "send_chat_message": "mdi:microsoft-teams", - "update_user_status": "mdi:microsoft-teams" + "update_user_status": "mdi:microsoft-teams", + "update_user_preferred_status": "mdi:microsoft-teams" }, "entity": { "sensor": { diff --git a/custom_components/o365/manifest.json b/custom_components/o365/manifest.json index 83dbdb2..051678c 100644 --- a/custom_components/o365/manifest.json +++ b/custom_components/o365/manifest.json @@ -12,7 +12,7 @@ "iot_class": "cloud_polling", "issue_tracker": "https://github.com/RogerSelwyn/O365-HomeAssistant/issues", "requirements": [ - "O365==2.0.33", + "O365==2.0.34", "BeautifulSoup4>=4.10.0" ], "version": "v4.7.0b1" diff --git a/custom_components/o365/schema.py b/custom_components/o365/schema.py index c55a9dc..07963ba 100644 --- a/custom_components/o365/schema.py +++ b/custom_components/o365/schema.py @@ -9,6 +9,7 @@ ATTR_TITLE, ) from homeassistant.const import CONF_EMAIL, CONF_ENABLED, CONF_NAME + from O365.calendar import ( # pylint: disable=no-name-in-module AttendeeType, EventSensitivity, @@ -17,7 +18,7 @@ from O365.mailbox import ( # pylint: disable=no-name-in-module, import-error ExternalAudience, ) -from O365.teams import Activity, Availability +from O365.teams import Activity, Availability, PreferredAvailability from O365.utils import ImportanceLevel # pylint: disable=no-name-in-module from .const import ( @@ -252,13 +253,17 @@ vol.Required(ATTR_EVENT_ID): cv.string, } - STATUS_SERVICE_UPDATE_USER_STATUS_SCHEMA = { vol.Required(ATTR_AVAILABILITY): vol.Coerce(Availability), vol.Required(ATTR_ACTIVITY): vol.Coerce(Activity), vol.Optional(ATTR_EXPIRATIONDURATION): cv.string, } +STATUS_SERVICE_UPDATE_USER_PERERRED_STATUS_SCHEMA = { + vol.Required(ATTR_AVAILABILITY): vol.Coerce(PreferredAvailability), + vol.Optional(ATTR_EXPIRATIONDURATION): cv.string, +} + TODO_SERVICE_NEW_SCHEMA = { vol.Required(ATTR_SUBJECT): cv.string, vol.Optional(ATTR_DESCRIPTION): cv.string, diff --git a/custom_components/o365/sensor.py b/custom_components/o365/sensor.py index 0e6580e..a072651 100644 --- a/custom_components/o365/sensor.py +++ b/custom_components/o365/sensor.py @@ -34,6 +34,7 @@ AUTO_REPLY_SERVICE_DISABLE_SCHEMA, AUTO_REPLY_SERVICE_ENABLE_SCHEMA, CHAT_SERVICE_SEND_MESSAGE_SCHEMA, + STATUS_SERVICE_UPDATE_USER_PERERRED_STATUS_SCHEMA, STATUS_SERVICE_UPDATE_USER_STATUS_SCHEMA, ) @@ -140,6 +141,11 @@ async def _async_setup_status_services(config, perms): STATUS_SERVICE_UPDATE_USER_STATUS_SCHEMA, "update_user_status", ) + platform.async_register_entity_service( + "update_user_preferred_status", + STATUS_SERVICE_UPDATE_USER_PERERRED_STATUS_SCHEMA, + "update_user_preferred_status", + ) async def _async_setup_chat_services(config, perms): diff --git a/custom_components/o365/services.yaml b/custom_components/o365/services.yaml index 492aa15..0298a51 100644 --- a/custom_components/o365/services.yaml +++ b/custom_components/o365/services.yaml @@ -519,3 +519,42 @@ update_user_status: required: false selector: text: + +update_user_preferred_status: + name: Update user preferred Teams status + description: "Update the user's preferred Teams status" + target: + device: + integration: o365 + entity: + integration: o365 + domain: sensor + fields: + availability: + name: Availability + description: "The base presence information" + example: Busy + required: true + selector: + select: + mode: dropdown + options: + - label: "Available" + value: "Available" + - label: "Busy" + value: "Busy" + - label: "Do Not Disturb" + value: "DoNotDisturb" + - label: "Be Right Back" + value: "BeRightBack" + - label: "Away" + value: "Away" + - label: "Offline" + value: "Offline" + expiration_duration: + name: Expiration Duration + description: "The expiration of the app presence session. The value is represented in ISO 8601 format for durations" + example: PT1H + required: false + selector: + text: diff --git a/docs/services.md b/docs/services.md index 1fa9a01..101d54c 100644 --- a/docs/services.md +++ b/docs/services.md @@ -157,7 +157,7 @@ These services must be targeted at a `status` sensor. They can only target the l ### o365.update_user_status Update Teams status for the logged in client. This will not override a status that is set via the MS Teams client. Allowable pairings of availability and activity are show in the [MS Graph Documentation](https://learn.microsoft.com/en-us/graph/api/presence-setpresence?view=graph-rest-1.0&tabs=http#request-body). The expiration/duration field is also documented on the same page. It defaults to 5 minutes. -#### Example update status service call +#### Example update user status service call ```yaml service: o365.update_user_status @@ -168,6 +168,16 @@ data: target: entity_id: sensor.roger_teams_status ``` +### o365.update_user_preferred_status +Update Teams preferred status for the logged-in user. This is equivalent to setting status within the Teams client. Allowable pairings of availability and activity are show in the [MS Graph Documentation](https://learn.microsoft.com/en-us/graph/api/presence-setuserpreferredpresence?view=graph-rest-1.0&tabs=http#request-body). The expiration/duration field is also documented on the same page. If not provided, a default expiration will be applied: DoNotDisturb or Busy - Expiration in 1 day; All others - Expiration in 7 days +#### Example update user preferred status service call -https://learn.microsoft.com/en-us/graph/api/presence-setpresence?view=graph-rest-1.0&tabs=http#request-body \ No newline at end of file +```yaml +service: o365.update_user_preferred_status +data: + availability: Offline + expiration_duration: PT1H +target: + entity_id: sensor.roger_teams_status +``` \ No newline at end of file