diff --git a/.coveragerc b/.coveragerc index 6322dc0f7b3..b2b23757014 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1138,6 +1138,7 @@ omit = homeassistant/components/skybell/switch.py homeassistant/components/slack/__init__.py homeassistant/components/slack/notify.py + homeassistant/components/slack/sensor.py homeassistant/components/slide/* homeassistant/components/slimproto/__init__.py homeassistant/components/slimproto/media_player.py diff --git a/homeassistant/components/slack/__init__.py b/homeassistant/components/slack/__init__.py index a89f645e9b6..36b457b75b7 100644 --- a/homeassistant/components/slack/__init__.py +++ b/homeassistant/components/slack/__init__.py @@ -1,4 +1,6 @@ """The slack integration.""" +from __future__ import annotations + import logging from aiohttp.client_exceptions import ClientError @@ -10,13 +12,23 @@ from homeassistant.const import CONF_API_KEY, CONF_PLATFORM, Platform from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers import aiohttp_client, discovery +from homeassistant.helpers.device_registry import DeviceEntryType +from homeassistant.helpers.entity import DeviceInfo, Entity, EntityDescription from homeassistant.helpers.typing import ConfigType -from .const import DATA_CLIENT, DATA_HASS_CONFIG, DOMAIN +from .const import ( + ATTR_URL, + ATTR_USER_ID, + DATA_CLIENT, + DATA_HASS_CONFIG, + DEFAULT_NAME, + DOMAIN, + SLACK_DATA, +) _LOGGER = logging.getLogger(__name__) -PLATFORMS = [Platform.NOTIFY] +PLATFORMS = [Platform.NOTIFY, Platform.SENSOR] async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: @@ -42,14 +54,18 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: slack = WebClient(token=entry.data[CONF_API_KEY], run_async=True, session=session) try: - await slack.auth_test() + res = await slack.auth_test() except (SlackApiError, ClientError) as ex: if isinstance(ex, SlackApiError) and ex.response["error"] == "invalid_auth": _LOGGER.error("Invalid API key") return False raise ConfigEntryNotReady("Error while setting up integration") from ex - - hass.data.setdefault(DOMAIN, {})[entry.entry_id] = entry.data | {DATA_CLIENT: slack} + data = { + DATA_CLIENT: slack, + ATTR_URL: res[ATTR_URL], + ATTR_USER_ID: res[ATTR_USER_ID], + } + hass.data.setdefault(DOMAIN, {})[entry.entry_id] = entry.data | {SLACK_DATA: data} hass.async_create_task( discovery.async_load_platform( @@ -61,4 +77,33 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: ) ) + await hass.config_entries.async_forward_entry_setups( + entry, [platform for platform in PLATFORMS if platform != Platform.NOTIFY] + ) + return True + + +class SlackEntity(Entity): + """Representation of a Slack entity.""" + + _attr_attribution = "Data provided by Slack" + _attr_has_entity_name = True + + def __init__( + self, + data: dict[str, str | WebClient], + description: EntityDescription, + entry: ConfigEntry, + ) -> None: + """Initialize a Slack entity.""" + self._client = data[DATA_CLIENT] + self.entity_description = description + self._attr_unique_id = f"{data[ATTR_USER_ID]}_{description.key}" + self._attr_device_info = DeviceInfo( + configuration_url=data[ATTR_URL], + entry_type=DeviceEntryType.SERVICE, + identifiers={(DOMAIN, entry.entry_id)}, + manufacturer=DEFAULT_NAME, + name=entry.title, + ) diff --git a/homeassistant/components/slack/const.py b/homeassistant/components/slack/const.py index 83937f4a43e..ec0993e290b 100644 --- a/homeassistant/components/slack/const.py +++ b/homeassistant/components/slack/const.py @@ -6,13 +6,17 @@ ATTR_BLOCKS_TEMPLATE = "blocks_template" ATTR_FILE = "file" ATTR_PASSWORD = "password" ATTR_PATH = "path" +ATTR_SNOOZE = "snooze_endtime" ATTR_URL = "url" ATTR_USERNAME = "username" +ATTR_USER_ID = "user_id" CONF_DEFAULT_CHANNEL = "default_channel" DATA_CLIENT = "client" +DEFAULT_NAME = "Slack" DEFAULT_TIMEOUT_SECONDS = 15 DOMAIN: Final = "slack" +SLACK_DATA = "data" DATA_HASS_CONFIG = "slack_hass_config" diff --git a/homeassistant/components/slack/notify.py b/homeassistant/components/slack/notify.py index bfcf79ef676..d587f960704 100644 --- a/homeassistant/components/slack/notify.py +++ b/homeassistant/components/slack/notify.py @@ -41,6 +41,7 @@ from .const import ( ATTR_USERNAME, CONF_DEFAULT_CHANNEL, DATA_CLIENT, + SLACK_DATA, ) _LOGGER = logging.getLogger(__name__) @@ -121,7 +122,7 @@ async def async_get_service( return SlackNotificationService( hass, - discovery_info.pop(DATA_CLIENT), + discovery_info[SLACK_DATA][DATA_CLIENT], discovery_info, ) diff --git a/homeassistant/components/slack/sensor.py b/homeassistant/components/slack/sensor.py new file mode 100644 index 00000000000..b190e6151ed --- /dev/null +++ b/homeassistant/components/slack/sensor.py @@ -0,0 +1,53 @@ +"""Slack platform for sensor component.""" +from __future__ import annotations + +from slack import WebClient + +from homeassistant.components.sensor import ( + SensorDeviceClass, + SensorEntity, + SensorEntityDescription, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback +import homeassistant.util.dt as dt_util + +from . import SlackEntity +from .const import ATTR_SNOOZE, DOMAIN, SLACK_DATA + + +async def async_setup_entry( + hass: HomeAssistant, + entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up the Slack select.""" + async_add_entities( + [ + SlackSensorEntity( + hass.data[DOMAIN][entry.entry_id][SLACK_DATA], + SensorEntityDescription( + key="do_not_disturb_until", + name="Do not disturb until", + icon="mdi:clock", + device_class=SensorDeviceClass.TIMESTAMP, + ), + entry, + ) + ], + True, + ) + + +class SlackSensorEntity(SlackEntity, SensorEntity): + """Representation of a Slack sensor.""" + + _client: WebClient + + async def async_update(self) -> None: + """Get the latest status.""" + if _time := (await self._client.dnd_info()).get(ATTR_SNOOZE): + self._attr_native_value = dt_util.utc_from_timestamp(_time) + else: + self._attr_native_value = None