async def _async_update_data(self) -> dict[str, Device]: """Fetch Overkiz data via event listener.""" try: events = await self.client.fetch_events() except BadCredentialsException as exception: raise ConfigEntryAuthFailed("Invalid authentication.") from exception except TooManyConcurrentRequestsException as exception: raise UpdateFailed("Too many concurrent requests.") from exception except TooManyRequestsException as exception: raise UpdateFailed("Too many requests, try again later.") from exception except MaintenanceException as exception: raise UpdateFailed("Server is down for maintenance.") from exception except InvalidEventListenerIdException as exception: raise UpdateFailed(exception) from exception except TimeoutError as exception: raise UpdateFailed("Failed to connect.") from exception except (ServerDisconnectedError, NotAuthenticatedException): self.executions = {} # During the relogin, similar exceptions can be thrown. try: await self.client.login() self.devices = await self._get_devices() except BadCredentialsException as exception: raise ConfigEntryAuthFailed("Invalid authentication.") from exception except TooManyRequestsException as exception: raise UpdateFailed("Too many requests, try again later.") from exception return self.devices for event in events: LOGGER.debug(event) if event_handler := EVENT_HANDLERS.get(event.name): await event_handler(self, event)
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up upon config entry in user interface.""" conf = entry.data username = conf[CONF_USERNAME] password = conf[CONF_PASSWORD] if CONF_USERCODES not in conf: # should only happen for those who used UI before we added usercodes raise ConfigEntryAuthFailed( "No usercodes in TotalConnect configuration") temp_codes = conf[CONF_USERCODES] usercodes = {int(code): temp_codes[code] for code in temp_codes} try: client = await hass.async_add_executor_job(TotalConnectClient, username, password, usercodes) except AuthenticationError as exception: raise ConfigEntryAuthFailed( "TotalConnect authentication failed during setup") from exception coordinator = TotalConnectDataUpdateCoordinator(hass, client) await coordinator.async_config_entry_first_refresh() hass.data.setdefault(DOMAIN, {}) hass.data[DOMAIN][entry.entry_id] = coordinator hass.config_entries.async_setup_platforms(entry, PLATFORMS) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up upon config entry in user interface.""" conf = entry.data username = conf[CONF_USERNAME] password = conf[CONF_PASSWORD] if CONF_USERCODES not in conf: # should only happen for those who used UI before we added usercodes raise ConfigEntryAuthFailed( "No usercodes in TotalConnect configuration") temp_codes = conf[CONF_USERCODES] usercodes = {int(code): temp_codes[code] for code in temp_codes} client = await hass.async_add_executor_job( TotalConnectClient.TotalConnectClient, username, password, usercodes) if not client.is_valid_credentials(): raise ConfigEntryAuthFailed("TotalConnect authentication failed") hass.data.setdefault(DOMAIN, {}) hass.data[DOMAIN][entry.entry_id] = client hass.config_entries.async_setup_platforms(entry, PLATFORMS) return True
def connect_gateway(self): """Connect the gateway in a way that can called by async_add_executor_job.""" try: self._gateway_device = gateway.Gateway(self._host, self._token) # get the gateway info self._gateway_info = self._gateway_device.info() except DeviceException as error: if isinstance(error.__cause__, ChecksumError): raise ConfigEntryAuthFailed(error) from error _LOGGER.error( "DeviceException during setup of xiaomi gateway with host %s: %s", self._host, error, ) return False # get the connected sub devices use_cloud = self._use_cloud or self._gateway_info.model == GATEWAY_MODEL_EU if not use_cloud: # use local query (not supported by all gateway types) try: self._gateway_device.discover_devices() except DeviceException as error: _LOGGER.info( "DeviceException during getting subdevices of xiaomi gateway" " with host %s, trying cloud to obtain subdevices: %s", self._host, error, ) use_cloud = True if use_cloud: # use miio-cloud if (self._cloud_username is None or self._cloud_password is None or self._cloud_country is None): raise ConfigEntryAuthFailed( "Missing cloud credentials in Xiaomi Miio configuration") try: miio_cloud = MiCloud(self._cloud_username, self._cloud_password) if not miio_cloud.login(): raise ConfigEntryAuthFailed( "Could not login to Xiaomi Miio Cloud, check the credentials" ) devices_raw = miio_cloud.get_devices(self._cloud_country) self._gateway_device.get_devices_from_dict(devices_raw) except DeviceException as error: _LOGGER.error( "DeviceException during setup of xiaomi gateway with host %s: %s", self._host, error, ) return False return True
async def _async_update_data(self): try: async with async_timeout.timeout(DEFAULT_TIMEOUT): # Retrieve the panel online status first panels = await self._client.list_control_panels() panel = next( (panel for panel in panels if panel.hash == self._panel_id), None) # If the panel is no more available within the given. Raise config error as the user must # reconfigure it in order to make it work again if not panel: raise ConfigEntryAuthFailed( f"Panel ID {self._panel_id} is no more linked to this user account" ) self._panel_entry = panel # If the panel is online, proceed with fetching its state # and return it right away if panel.online: status = await self._client.get_panel_status( control_panel_id=panel.hash, pin=self._panel_pin) # type: PanelStatus # Store a dictionary for fast endpoint state access self._state_by_endpoint = { k.endpoint_id: k for k in status.all_endpoints } return status # Otherwise, return None. Listeners will know that this means the device is offline return None except ElmaxBadPinError as err: raise ConfigEntryAuthFailed( "Control panel pin was refused") from err except ElmaxBadLoginError as err: raise ConfigEntryAuthFailed("Refused username/password") from err except ElmaxApiError as err: raise UpdateFailed( f"Error communicating with ELMAX API: {err}") from err except ElmaxNetworkError as err: raise UpdateFailed( "A network error occurred while communicating with Elmax cloud." ) from err
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up IntelliFire from a config entry.""" LOGGER.debug("Setting up config entry: %s", entry.unique_id) if CONF_USERNAME not in entry.data: LOGGER.debug("Old config entry format detected: %s", entry.unique_id) raise ConfigEntryAuthFailed # Define the API Objects read_object = IntellifireAsync(entry.data[CONF_HOST]) ift_control = IntellifireControlAsync(fireplace_ip=entry.data[CONF_HOST], ) try: await ift_control.login( username=entry.data[CONF_USERNAME], password=entry.data[CONF_PASSWORD], ) except (ConnectionError, ClientConnectionError) as err: raise ConfigEntryNotReady from err except LoginException as err: raise ConfigEntryAuthFailed(err) from err finally: await ift_control.close() # Define the update coordinator coordinator = IntellifireDataUpdateCoordinator(hass=hass, read_api=read_object, control_api=ift_control) await coordinator.async_config_entry_first_refresh() hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator hass.config_entries.async_setup_platforms(entry, PLATFORMS) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Deluge from a config entry.""" host = entry.data[CONF_HOST] port = entry.data[CONF_PORT] username = entry.data[CONF_USERNAME] password = entry.data[CONF_PASSWORD] api = await hass.async_add_executor_job( DelugeRPCClient, host, port, username, password ) api.web_port = entry.data[CONF_WEB_PORT] try: await hass.async_add_executor_job(api.connect) except ( ConnectionRefusedError, socket.timeout, SSLError, ) as ex: raise ConfigEntryNotReady("Connection to Deluge Daemon failed") from ex except Exception as ex: # pylint:disable=broad-except if type(ex).__name__ == "BadLoginError": raise ConfigEntryAuthFailed( "Credentials for Deluge client are not valid" ) from ex _LOGGER.error("Unknown error connecting to Deluge: %s", ex) coordinator = DelugeDataUpdateCoordinator(hass, api, entry) await coordinator.async_config_entry_first_refresh() hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator hass.config_entries.async_setup_platforms(entry, PLATFORMS) return True
async def async_update_data(): """Fetch data from Mazda API.""" try: vehicles = await with_timeout(mazda_client.get_vehicles()) # The Mazda API can throw an error when multiple simultaneous requests are # made for the same account, so we can only make one request at a time here for vehicle in vehicles: vehicle["status"] = await with_timeout( mazda_client.get_vehicle_status(vehicle["id"])) # If vehicle is electric, get additional EV-specific status info if vehicle["isElectric"]: vehicle["evStatus"] = await with_timeout( mazda_client.get_ev_vehicle_status(vehicle["id"])) hass.data[DOMAIN][entry.entry_id][DATA_VEHICLES] = vehicles return vehicles except MazdaAuthenticationException as ex: raise ConfigEntryAuthFailed( "Not authenticated with Mazda API") from ex except Exception as ex: _LOGGER.exception( "Unknown error occurred during Mazda update request: %s", ex) raise UpdateFailed(ex) from ex
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Uonet+ Vulcan integration.""" hass.data.setdefault(DOMAIN, {}) try: keystore = Keystore.load(entry.data["keystore"]) account = Account.load(entry.data["account"]) client = Vulcan(keystore, account, async_get_clientsession(hass)) await client.select_student() students = await client.get_students() for student in students: if str(student.pupil.id) == str(entry.data["student_id"]): client.student = student break except UnauthorizedCertificateException as err: raise ConfigEntryAuthFailed( "The certificate is not authorized.") from err except ClientConnectorError as err: raise ConfigEntryNotReady( f"Connection error - please check your internet connection: {err}" ) from err hass.data[DOMAIN][entry.entry_id] = client await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) return True
async def _async_update_data(self) -> None: """Get the latest data from Radarr.""" start = dt_util.as_utc(dt_util.start_of_local_day().replace(microsecond=0)) end = start + timedelta(days=self.config_entry.options[CONF_UPCOMING_DAYS]) try: [ self.system_status, self.rootfolder, self.calendar, self.commands, ] = await asyncio.gather( *[ self.api_client.async_get_system_status(), self.api_client.async_get_root_folders(), self.api_client.async_get_calendar(start_date=start, end_date=end), self.api_client.async_get_commands(), ] ) # Diskspace can timeout with large systems with remote mapped storage # We attempt to get total disk capacity first if not self.disk_space and not self.movies_count_enabled: self.disk_space = await self.api_client.async_get_diskspace() else: # Wait to get movie count after disk capacity is determined self.movies_count_enabled = True self.movies = cast(list, await self.api_client.async_get_movies()) except exceptions.ArrConnectionException as ex: raise UpdateFailed(ex) from ex except exceptions.ArrAuthenticationException as ex: raise ConfigEntryAuthFailed( "API Key is no longer valid. Please reauthenticate" ) from ex
async def _async_update_data(self) -> list[Sensor]: """Get the data for LaCrosse View.""" now = datetime.utcnow() if self.last_update < now - timedelta(minutes=59): # Get new token self.last_update = now try: await self.api.login(self.username, self.password) except LoginError as error: raise ConfigEntryAuthFailed from error # Get the timestamp for yesterday at 6 PM (this is what is used in the app, i noticed it when proxying the request) yesterday = now - timedelta(days=1) yesterday = yesterday.replace(hour=18, minute=0, second=0, microsecond=0) yesterday_timestamp = datetime.timestamp(yesterday) try: sensors = await self.api.get_sensors( location=Location(id=self.id, name=self.name), tz=self.hass.config.time_zone, start=str(int(yesterday_timestamp)), end=str(int(datetime.timestamp(now))), ) except HTTPError as error: raise ConfigEntryNotReady from error # Verify that we have permission to read the sensors for sensor in sensors: if not sensor.permissions.get("read", False): raise ConfigEntryAuthFailed( f"This account does not have permission to read {sensor.name}" ) return sensors
async def _async_update_data(self) -> list[UptimeRobotMonitor] | None: """Update data.""" try: response = await self.api.async_get_monitors() except UptimeRobotAuthenticationException as exception: raise ConfigEntryAuthFailed(exception) from exception except UptimeRobotException as exception: raise UpdateFailed(exception) from exception else: if response.status != API_ATTR_OK: raise UpdateFailed(response.error.message) monitors: list[UptimeRobotMonitor] = response.data current_monitors = { list(device.identifiers)[0][1] for device in dr.async_entries_for_config_entry( self._device_registry, self._config_entry_id) } new_monitors = {str(monitor.id) for monitor in monitors} if stale_monitors := current_monitors - new_monitors: for monitor_id in stale_monitors: if device := self._device_registry.async_get_device({ (DOMAIN, monitor_id) }): self._device_registry.async_remove_device(device.id)
async def async_setup_entry(hass, entry): """Set up a config entry for Apple TV.""" if entry.options.get(CONF_RECONFIGURE, False): hass.config_entries.async_update_entry( entry, options={**entry.options, CONF_RECONFIGURE: False} ) raise ConfigEntryAuthFailed("reconfiguration was requested") manager = AppleTVManager(hass, entry) hass.data.setdefault(DOMAIN, {})[entry.unique_id] = manager async def on_hass_stop(event): """Stop push updates when hass stops.""" await manager.disconnect() entry.async_on_unload( hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, on_hass_stop) ) async def setup_platforms(): """Set up platforms and initiate connection.""" await asyncio.gather( *( hass.config_entries.async_forward_entry_setup(entry, platform) for platform in PLATFORMS ) ) await manager.init() hass.async_create_task(setup_platforms()) entry.async_on_unload(entry.add_update_listener(async_config_entry_changed)) return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Abode integration from a config entry.""" username = entry.data[CONF_USERNAME] password = entry.data[CONF_PASSWORD] polling = entry.data[CONF_POLLING] cache = hass.config.path(DEFAULT_CACHEDB) # For previous config entries where unique_id is None if entry.unique_id is None: hass.config_entries.async_update_entry( entry, unique_id=entry.data[CONF_USERNAME] ) try: abode = await hass.async_add_executor_job( Abode, username, password, True, True, True, cache ) except AbodeAuthenticationException as ex: raise ConfigEntryAuthFailed(f"Invalid credentials: {ex}") from ex except (AbodeException, ConnectTimeout, HTTPError) as ex: raise ConfigEntryNotReady(f"Unable to connect to Abode: {ex}") from ex hass.data[DOMAIN] = AbodeSystem(abode, polling) await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) await setup_hass_events(hass) await hass.async_add_executor_job(setup_hass_services, hass) await hass.async_add_executor_job(setup_abode_events, hass) return True
async def async_update(self) -> None: """Get updated data from SimpliSafe.""" async def async_update_system(system: SystemV2 | SystemV3) -> None: """Update a system.""" await system.async_update(cached=system.version != 3) self._async_process_new_notifications(system) tasks = [ async_update_system(system) for system in self.systems.values() ] results = await asyncio.gather(*tasks, return_exceptions=True) for result in results: if isinstance(result, InvalidCredentialsError): raise ConfigEntryAuthFailed("Invalid credentials") from result if isinstance(result, EndpointUnavailableError): # In case the user attempts an action not allowed in their current plan, # we merely log that message at INFO level (so the user is aware, # but not spammed with ERROR messages that they cannot change): LOGGER.info(result) if isinstance(result, SimplipyError): raise UpdateFailed( f"SimpliSafe error while updating: {result}")
async def async_setup_entry(hass, entry, async_add_entities): try: api = await hass.async_add_executor_job(WavinSentio, entry.data[CONF_USERNAME], entry.data[CONF_PASSWORD]) except UnauthorizedException as err: raise ConfigEntryAuthFailed(err) from err rooms = await hass.async_add_executor_job(api.get_rooms, entry.data[CONF_LOCATION_ID]) dataservice = WavinSentioClimateDataService(hass, api, entry.data[CONF_LOCATION_ID], rooms) dataservice.async_setup() await dataservice.coordinator.async_refresh() entities = [] for room in rooms: ws = WavinSentioEntity(hass, room, dataservice) entities.append(ws) async_add_entities(entities) hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, "sensor"))
async def _async_update_data(self) -> dict[Platform, dict[str, int | str]]: """Get the latest data from Deluge and updates the state.""" data = {} try: data[Platform.SENSOR] = await self.hass.async_add_executor_job( self.api.call, "core.get_session_status", [ "upload_rate", "download_rate", "dht_upload_rate", "dht_download_rate", ], ) data[Platform.SWITCH] = await self.hass.async_add_executor_job( self.api.call, "core.get_torrents_status", {}, ["paused"]) except ( ConnectionRefusedError, socket.timeout, # pylint:disable=no-member SSLError, FailedToReconnectException, ) as ex: raise UpdateFailed( f"Connection to Deluge Daemon Lost: {ex}") from ex except Exception as ex: # pylint:disable=broad-except if type(ex).__name__ == "BadLoginError": raise ConfigEntryAuthFailed( "Credentials for Deluge client are not valid") from ex LOGGER.error("Unknown error connecting to Deluge: %s", ex) raise ex return data
async def async_get_events(self, hass, start_date, end_date) -> list[CalendarEvent]: """Get all events in a specific time frame.""" try: events = await get_lessons( self.client, date_from=start_date, date_to=end_date, ) except UnauthorizedCertificateException as err: raise ConfigEntryAuthFailed( "The certificate is not authorized, please authorize integration again" ) from err except ClientConnectorError as err: if self.available: _LOGGER.warning( "Connection error - please check your internet connection: %s", err) events = [] event_list = [] for item in events: event = CalendarEvent( start=datetime.combine(item["date"], item["time"].from_), end=datetime.combine(item["date"], item["time"].to), summary=item["lesson"], location=item["room"], description=item["teacher"], ) event_list.append(event) return event_list
async def _async_update_trend(): """Update the trend data.""" try: await gateway.update_trend_data() except (SenseAuthenticationException, SenseMFARequiredException) as err: _LOGGER.warning("Sense authentication expired") raise ConfigEntryAuthFailed(err) from err
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Discord from a config entry.""" nextcord.VoiceClient.warn_nacl = False discord_bot = nextcord.Client() try: await discord_bot.login(entry.data[CONF_API_TOKEN]) except nextcord.LoginFailure as ex: raise ConfigEntryAuthFailed("Invalid token given") from ex except (ClientConnectorError, nextcord.HTTPException, nextcord.NotFound) as ex: raise ConfigEntryNotReady("Failed to connect") from ex finally: await discord_bot.close() hass.data.setdefault(DOMAIN, {})[entry.entry_id] = entry.data hass.async_create_task( discovery.async_load_platform( hass, Platform.NOTIFY, DOMAIN, hass.data[DOMAIN][entry.entry_id], hass.data[DOMAIN], )) return True
async def _setup_websocket(self) -> None: """Use WebSocket for updates.""" try: self.logger.debug( "Connecting to ws://%s:%s", self.host, self.bridge.information.websocketPort, ) await self.bridge.async_connect_websocket( self.host, self.bridge.information.websocketPort ) except BridgeAuthenticationException as exception: if self.unsub: self.unsub() self.unsub = None raise ConfigEntryAuthFailed() from exception except (*BRIDGE_CONNECTION_ERRORS, ConnectionRefusedError) as exception: if self.unsub: self.unsub() self.unsub = None raise UpdateFailed( f"Could not connect to {self.title} ({self.host})." ) from exception asyncio.create_task(self._listen_for_events()) async def close_websocket(_) -> None: """Close WebSocket connection.""" await self.bridge.async_close_websocket() # Clean disconnect WebSocket on Home Assistant shutdown self.unsub = self.hass.bus.async_listen_once( EVENT_HOMEASSISTANT_STOP, close_websocket )
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up laundrify from a config entry.""" session = async_get_clientsession(hass) api_client = LaundrifyAPI(entry.data[CONF_ACCESS_TOKEN], session) try: await api_client.validate_token() except UnauthorizedException as err: raise ConfigEntryAuthFailed("Invalid authentication") from err except ApiConnectionException as err: raise ConfigEntryNotReady("Cannot reach laundrify API") from err coordinator = LaundrifyUpdateCoordinator(hass, api_client, DEFAULT_POLL_INTERVAL) await coordinator.async_config_entry_first_refresh() hass.data.setdefault(DOMAIN, {})[entry.entry_id] = { "api": api_client, "coordinator": coordinator, } hass.config_entries.async_setup_platforms(entry, PLATFORMS) return True
def setup(self) -> bool: """Set up the tankerkoenig API.""" for station_id in self._selected_stations: try: station_data = pytankerkoenig.getStationData( self._api_key, station_id) except pytankerkoenig.customException as err: if any(x in str(err).lower() for x in ("api-key", "apikey")): raise ConfigEntryAuthFailed(err) from err station_data = { "ok": False, "message": err, "exception": True, } if not station_data["ok"]: _LOGGER.error( "Error when adding station %s:\n %s", station_id, station_data["message"], ) continue self.add_station(station_data["station"]) if len(self.stations) > 10: _LOGGER.warning( "Found more than 10 stations to check. " "This might invalidate your api-key on the long run. " "Try using a smaller radius") return True
async def async_update() -> dict[str, dict[str, Any]]: """Get the latest data from the Notion API.""" data: dict[str, dict[str, Any]] = { "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, InvalidCredentialsError): raise ConfigEntryAuthFailed( "Invalid username and/or password") from result if isinstance(result, NotionError): raise UpdateFailed( f"There was a Notion error while updating {attr}: {result}" ) from result if isinstance(result, Exception): raise UpdateFailed( f"There was an unknown error while updating {attr}: {result}" ) from 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
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up VLC media player Telnet from a config entry.""" config = entry.data host = config[CONF_HOST] port = config[CONF_PORT] password = config[CONF_PASSWORD] vlc = Client(password=password, host=host, port=port) available = True try: await vlc.connect() except ConnectError as err: LOGGER.warning("Failed to connect to VLC: %s. Trying again", err) available = False if available: try: await vlc.login() except AuthError as err: await disconnect_vlc(vlc) raise ConfigEntryAuthFailed() from err domain_data = hass.data.setdefault(DOMAIN, {}) domain_data[entry.entry_id] = {DATA_VLC: vlc, DATA_AVAILABLE: available} await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) return True
async def _async_update_data(self) -> dict[Platform, dict[str, int | str]]: """Get the latest data from Deluge and updates the state.""" data = {} try: _data = await self.hass.async_add_executor_job( self.api.call, "core.get_session_status", DATA_KEYS, ) data[Platform.SENSOR] = {k.decode(): v for k, v in _data.items()} data[Platform.SWITCH] = await self.hass.async_add_executor_job( self.api.call, "core.get_torrents_status", {}, ["paused"] ) except ( ConnectionRefusedError, socket.timeout, SSLError, FailedToReconnectException, ) as ex: raise UpdateFailed(f"Connection to Deluge Daemon Lost: {ex}") from ex except Exception as ex: if type(ex).__name__ == "BadLoginError": raise ConfigEntryAuthFailed( "Credentials for Deluge client are not valid" ) from ex LOGGER.error("Unknown error connecting to Deluge: %s", ex) raise ex return data
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up pushover from a config entry.""" pushover_api = PushoverAPI(entry.data[CONF_API_KEY]) try: await hass.async_add_executor_job(pushover_api.validate, entry.data[CONF_USER_KEY]) except BadAPIRequestError as err: if "application token is invalid" in str(err): raise ConfigEntryAuthFailed(err) from err raise ConfigEntryNotReady(err) from err hass.data.setdefault(DOMAIN, {})[entry.entry_id] = pushover_api hass.async_create_task( discovery.async_load_platform( hass, Platform.NOTIFY, DOMAIN, { CONF_NAME: entry.data[CONF_NAME], CONF_USER_KEY: entry.data[CONF_USER_KEY], "entry_id": entry.entry_id, }, hass.data[DATA_HASS_CONFIG], )) return True
async def async_connect_device(self, host, token): """Connect to the Xiaomi Device.""" _LOGGER.debug("Initializing with host %s (token %s...)", host, token[:5]) try: self._device = Device(host, token) # get the device info self._device_info = await self._hass.async_add_executor_job( self._device.info ) except DeviceException as error: if isinstance(error.__cause__, ChecksumError): raise ConfigEntryAuthFailed(error) from error _LOGGER.error( "DeviceException during setup of xiaomi device with host %s: %s", host, error, ) return False _LOGGER.debug( "%s %s %s detected", self._device_info.model, self._device_info.firmware_version, self._device_info.hardware_version, ) return True
def _setup(self, hass): """Rachio device setup.""" rachio = self.rachio response = rachio.person.info() if is_invalid_auth_code(int(response[0][KEY_STATUS])): raise ConfigEntryAuthFailed(f"API key error: {response}") if int(response[0][KEY_STATUS]) != HTTPStatus.OK: raise ConfigEntryNotReady(f"API Error: {response}") self._id = response[1][KEY_ID] # Use user ID to get user data data = rachio.person.get(self._id) if is_invalid_auth_code(int(data[0][KEY_STATUS])): raise ConfigEntryAuthFailed(f"User ID error: {data}") if int(data[0][KEY_STATUS]) != HTTPStatus.OK: raise ConfigEntryNotReady(f"API Error: {data}") self.username = data[1][KEY_USERNAME] devices = data[1][KEY_DEVICES] for controller in devices: webhooks = rachio.notification.get_device_webhook( controller[KEY_ID])[1] # The API does not provide a way to tell if a controller is shared # or if they are the owner. To work around this problem we fetch the webhooks # before we setup the device so we can skip it instead of failing. # webhooks are normally a list, however if there is an error # rachio hands us back a dict if isinstance(webhooks, dict): if webhooks.get("code") == PERMISSION_ERROR: _LOGGER.info( "Not adding controller '%s', only controllers owned by '%s' may be added", controller[KEY_NAME], self.username, ) else: _LOGGER.error( "Failed to add rachio controller '%s' because of an error: %s", controller[KEY_NAME], webhooks.get("error", "Unknown Error"), ) continue rachio_iro = RachioIro(hass, rachio, controller, webhooks) rachio_iro.setup() self._controllers.append(rachio_iro) _LOGGER.info('Using Rachio API as user "%s"', self.username)
async def refresh_tokens(hass: HomeAssistant, entry: ConfigEntry): """Store updated authentication and director tokens in hass.data, and schedule next token refresh.""" config = entry.data verify_ssl_session = aiohttp_client.async_get_clientsession(hass) account = C4Account(config[CONF_USERNAME], config[CONF_PASSWORD], verify_ssl_session) try: await account.getAccountBearerToken() except client_exceptions.ClientError as exception: raise ConfigEntryNotReady(exception) from exception except BadCredentials as exception: raise ConfigEntryAuthFailed(exception) from exception controller_unique_id = config[CONF_CONTROLLER_UNIQUE_ID] director_token_dict = await account.getDirectorBearerToken( controller_unique_id) no_verify_ssl_session = aiohttp_client.async_get_clientsession( hass, verify_ssl=False) director = C4Director(config[CONF_HOST], director_token_dict[CONF_TOKEN], no_verify_ssl_session) _LOGGER.debug("Saving new account and director tokens in hass data") entry_data = hass.data[DOMAIN][entry.entry_id] entry_data[CONF_ACCOUNT] = account entry_data[CONF_DIRECTOR] = director if not (CONF_WEBSOCKET in entry_data and isinstance(entry_data[CONF_WEBSOCKET], C4Websocket)): _LOGGER.debug("First time setup, creating new C4Websocket object") connection_tracker = C4WebsocketConnectionTracker(hass, entry) websocket = C4Websocket( config[CONF_HOST], no_verify_ssl_session, connection_tracker.connect_callback, connection_tracker.disconnect_callback, ) entry_data[CONF_WEBSOCKET] = websocket # Silence C4Websocket related loggers, that would otherwise spam INFO logs with debugging messages logging.getLogger("socketio.client").setLevel(logging.WARNING) logging.getLogger("engineio.client").setLevel(logging.WARNING) logging.getLogger("charset_normalizer").setLevel(logging.ERROR) _LOGGER.debug("Starting new WebSocket connection") await entry_data[CONF_WEBSOCKET].sio_connect(director.director_bearer_token ) _LOGGER.debug( "Registering next token refresh in %s seconds", director_token_dict["validSeconds"], ) obj = RefreshTokensObject(hass, entry) entry_data[CONF_CANCEL_TOKEN_REFRESH_CALLBACK] = async_call_later( hass=hass, delay=director_token_dict["validSeconds"], action=obj.refresh_tokens, )