text: """Support for Notion."""
  import asyncio
  from datetime import timedelta
  
  from aionotion import async_get_client
  from aionotion.errors import InvalidCredentialsError, NotionError
  
  from homeassistant.config_entries import ConfigEntry
  from homeassistant.const import ATTR_ATTRIBUTION, CONF_PASSWORD, CONF_USERNAME
  from homeassistant.core import HomeAssistant, callback
  from homeassistant.exceptions import ConfigEntryNotReady
  from homeassistant.helpers import (
      aiohttp_client,
      config_validation as cv,
      device_registry as dr,
  )
  from homeassistant.helpers.update_coordinator import (
      CoordinatorEntity,
      DataUpdateCoordinator,
      UpdateFailed,
  )
  
  from .const import DATA_COORDINATOR, DOMAIN, LOGGER
  
  PLATFORMS = ["binary_sensor", "sensor"]
  
  ATTR_SYSTEM_MODE = "system_mode"
  ATTR_SYSTEM_NAME = "system_name"
  
  DEFAULT_ATTRIBUTION = "Data provided by Notion"
  DEFAULT_SCAN_INTERVAL = timedelta(minutes=1)
  
  CONFIG_SCHEMA = cv.deprecated(DOMAIN, invalidation_version="0.119")
  
  
  async def async_setup(hass: HomeAssistant, config: dict) -> bool:
      """Set up the Notion component."""
      hass.data[DOMAIN] = {DATA_COORDINATOR: {}}
      return True
  
  
  async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
      """Set up Notion as a config entry."""
      if not entry.unique_id:
          hass.config_entries.async_update_entry(
              entry, unique_id=entry.data[CONF_USERNAME]
          )
  
      session = aiohttp_client.async_get_clientsession(hass)
  
      try:
          client = await async_get_client(
              entry.data[CONF_USERNAME], entry.data[CONF_PASSWORD], session
          )
      except InvalidCredentialsError:
          LOGGER.error("Invalid username and/or password")
          return False
      except NotionError as err:
          LOGGER.error("Config entry failed: %s", err)
          raise ConfigEntryNotReady from err
  
      async def async_update():
          """Get the latest data from the Notion API."""
          data = {"bridges": {}, "sensors": {}, "tasks": {}}
          tasks = {
              "bridges": client.bridge.async_all(),
              "sensors": client.sensor.async_all(),
              "tasks": client.task.async_all(),
          }
  
          results = await asyncio.gather(*tasks.values(), return_exceptions=True)
          for attr, result in zip(tasks, results):
              if isinstance(result, NotionError):
                  raise UpdateFailed(
                      f"There was a Notion error while updating {attr}: {result}"
                  )
              if isinstance(result, Exception):
                  raise UpdateFailed(
                      f"There was an unknown error while updating {attr}: {result}"
                  )
  
              for item in result:
                  if attr == "bridges" and item["id"] not in data["bridges"]:
                      # If a new bridge is discovered, register it:
                      hass.async_create_task(async_register_new_bridge(hass, item, entry))
                  data[attr][item["id"]] = item
  
          return data
  
      coordinator = hass.data[DOMAIN][DATA_COORDINATOR][
          entry.entry_id
      ] = DataUpdateCoordinator(
          hass,
          LOGGER,
          name=entry.data[CONF_USERNAME],
          update_interval=DEFAULT_SCAN_INTERVAL,
          update_method=async_update,
      )
  
      await coordinator.async_refresh()
  
      for platform in PLATFORMS:
          hass.async_create_task(
              hass.config_entries.async_forward_entry_setup(entry, platform)
          )
  
      return True
  
  
  async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
      """Unload a Notion config entry."""
      unload_ok = all(
          await asyncio.gather(
              *[
                  hass.config_entries.async_forward_entry_unload(entry, platform)
                  for platform in PLATFORMS
              ]
          )
      )
      if unload_ok:
          hass.data[DOMAIN][DATA_COORDINATOR].pop(entry.entry_id)
  
      return unload_ok
  
  
  async def async_register_new_bridge(
      hass: HomeAssistant, bridge: dict, entry: ConfigEntry
  ):
      """Register a new bridge."""
      device_registry = await dr.async_get_registry(hass)
      device_registry.async_get_or_create(
          config_entry_id=entry.entry_id,
          identifiers={(DOMAIN, bridge["hardware_id"])},
          manufacturer="Silicon Labs",
          model=bridge["hardware_revision"],
          name=bridge["name"] or bridge["id"],
          sw_version=bridge["firmware_version"]["wifi"],
      )
  
  
  class NotionEntity(CoordinatorEntity):
      """Define a base Notion entity."""
  
      def __init__(
          self,
          coordinator: DataUpdateCoordinator,
          task_id: str,
          sensor_id: str,
          bridge_id: str,
          system_id: str,
          name: str,
          device_class: str,
      ):
          """Initialize the entity."""
          super().__init__(coordinator)
          self._attrs = {ATTR_ATTRIBUTION: DEFAULT_ATTRIBUTION}
          self._bridge_id = bridge_id
          self._device_class = device_class
          self._name = name
          self._sensor_id = sensor_id
          self._state = None
          self._system_id = system_id
          self._unique_id = (
              f'{sensor_id}_{self.coordinator.data["tasks"][task_id]["task_type"]}'
          )
          self.task_id = task_id
  
      @property
      def available(self) -> bool:
          """Return True if entity is available."""
          return (
              self.coordinator.last_update_success
              and self.task_id in self.coordinator.data["tasks"]
          )
  
      @property
      def device_class(self) -> str:
          """Return the device class."""
          return self._device_class
  
      @property
      def device_state_attributes(self) -> dict:
          """Return the state attributes."""
          return self._attrs
  
      @property
      def device_info(self) -> dict:
          """Return device registry information for this entity."""
          bridge = self.coordinator.data["bridges"].get(self._bridge_id, {})
          sensor = self.coordinator.data["sensors"][self._sensor_id]
  
          return {
              "identifiers": {(DOMAIN, sensor["hardware_id"])},
              "manufacturer": "Silicon Labs",
              "model": sensor["hardware_revision"],
              "name": sensor["name"],
              "sw_version": sensor["firmware_version"],
              "via_device": (DOMAIN, bridge.get("hardware_id")),
          }
  
      @property
      def name(self) -> str:
          """Return the name of the entity."""
          sensor = self.coordinator.data["sensors"][self._sensor_id]
          return f'{sensor["name"]}: {self._name}'
  
      @property
      def unique_id(self) -> str:
          """Return a unique, unchanging string that represents this entity."""
          return self._unique_id
  
      async def _async_update_bridge_id(self) -> None:
          """Update the entity's bridge ID if it has changed.
  
          Sensors can move to other bridges based on signal strength, etc.
          """
          sensor = self.coordinator.data["sensors"][self._sensor_id]
  
          # If the sensor's bridge ID is the same as what we had before or if it points
          # to a bridge that doesn't exist (which can happen due to a Notion API bug),
          # return immediately:
          if (
              self._bridge_id == sensor["bridge"]["id"]
              or sensor["bridge"]["id"] not in self.coordinator.data["bridges"]
          ):
              return
  
          self._bridge_id = sensor["bridge"]["id"]
  
          device_registry = await dr.async_get_registry(self.hass)
          bridge = self.coordinator.data["bridges"][self._bridge_id]
          bridge_device = device_registry.async_get_device(
              {DOMAIN: bridge["hardware_id"]}, set()
          )
          this_device = device_registry.async_get_device(
              {DOMAIN: sensor["hardware_id"]}, set()
          )
  
          device_registry.async_update_device(
              this_device.id, via_device_id=bridge_device.id
          )
  
      @callback
      def _async_update_from_latest_data(self) -> None:
          """Update the entity from the latest data."""
          raise NotImplementedError
  
      @callback
      def _handle_coordinator_update(self):
          """Respond to a DataUpdateCoordinator update."""
          if self.task_id in self.coordinator.data["tasks"]:
              self.hass.async_create_task(self._async_update_bridge_id())
              self._async_update_from_latest_data()
  
          self.async_write_ha_state()
  
      async def async_added_to_hass(self):
          """Handle entity which will be added."""
          await super().async_added_to_hass()
          self._async_update_from_latest_data()
  