Add Awair Local API support (#75535)
This commit is contained in:
parent
078a4974e1
commit
ebbff7b60e
15 changed files with 603 additions and 143 deletions
|
@ -4,12 +4,14 @@ from __future__ import annotations
|
|||
from collections.abc import Mapping
|
||||
from typing import Any
|
||||
|
||||
from python_awair import Awair
|
||||
from aiohttp.client_exceptions import ClientConnectorError
|
||||
from python_awair import Awair, AwairLocal, AwairLocalDevice
|
||||
from python_awair.exceptions import AuthError, AwairError
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components import zeroconf
|
||||
from homeassistant.config_entries import ConfigFlow
|
||||
from homeassistant.const import CONF_ACCESS_TOKEN
|
||||
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_HOST
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
|
||||
|
@ -21,20 +23,76 @@ class AwairFlowHandler(ConfigFlow, domain=DOMAIN):
|
|||
|
||||
VERSION = 1
|
||||
|
||||
_device: AwairLocalDevice
|
||||
|
||||
async def async_step_zeroconf(
|
||||
self, discovery_info: zeroconf.ZeroconfServiceInfo
|
||||
) -> FlowResult:
|
||||
"""Handle zeroconf discovery."""
|
||||
|
||||
host = discovery_info.host
|
||||
LOGGER.debug("Discovered device: %s", host)
|
||||
|
||||
self._device, _ = await self._check_local_connection(host)
|
||||
|
||||
if self._device is not None:
|
||||
await self.async_set_unique_id(self._device.mac_address)
|
||||
self._abort_if_unique_id_configured(error="already_configured_device")
|
||||
self.context.update(
|
||||
{
|
||||
"title_placeholders": {
|
||||
"model": self._device.model,
|
||||
"device_id": self._device.device_id,
|
||||
}
|
||||
}
|
||||
)
|
||||
else:
|
||||
return self.async_abort(reason="unreachable")
|
||||
return await self.async_step_discovery_confirm()
|
||||
|
||||
async def async_step_discovery_confirm(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> FlowResult:
|
||||
"""Confirm discovery."""
|
||||
if user_input is not None:
|
||||
title = f"{self._device.model} ({self._device.device_id})"
|
||||
return self.async_create_entry(
|
||||
title=title,
|
||||
data={CONF_HOST: self._device.device_addr},
|
||||
)
|
||||
|
||||
self._set_confirm_only()
|
||||
placeholders = {
|
||||
"model": self._device.model,
|
||||
"device_id": self._device.device_id,
|
||||
}
|
||||
return self.async_show_form(
|
||||
step_id="discovery_confirm",
|
||||
description_placeholders=placeholders,
|
||||
)
|
||||
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, str] | None = None
|
||||
) -> FlowResult:
|
||||
"""Handle a flow initialized by the user."""
|
||||
|
||||
return self.async_show_menu(step_id="user", menu_options=["local", "cloud"])
|
||||
|
||||
async def async_step_cloud(self, user_input: Mapping[str, Any]) -> FlowResult:
|
||||
"""Handle collecting and verifying Awair Cloud API credentials."""
|
||||
|
||||
errors = {}
|
||||
|
||||
if user_input is not None:
|
||||
user, error = await self._check_connection(user_input[CONF_ACCESS_TOKEN])
|
||||
user, error = await self._check_cloud_connection(
|
||||
user_input[CONF_ACCESS_TOKEN]
|
||||
)
|
||||
|
||||
if user is not None:
|
||||
await self.async_set_unique_id(user.email)
|
||||
self._abort_if_unique_id_configured()
|
||||
self._abort_if_unique_id_configured(error="already_configured_account")
|
||||
|
||||
title = f"{user.email} ({user.user_id})"
|
||||
title = user.email
|
||||
return self.async_create_entry(title=title, data=user_input)
|
||||
|
||||
if error != "invalid_access_token":
|
||||
|
@ -43,8 +101,39 @@ class AwairFlowHandler(ConfigFlow, domain=DOMAIN):
|
|||
errors = {CONF_ACCESS_TOKEN: "invalid_access_token"}
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="user",
|
||||
data_schema=vol.Schema({vol.Required(CONF_ACCESS_TOKEN): str}),
|
||||
step_id="cloud",
|
||||
data_schema=vol.Schema({vol.Optional(CONF_ACCESS_TOKEN): str}),
|
||||
description_placeholders={
|
||||
"url": "https://developer.getawair.com/onboard/login"
|
||||
},
|
||||
errors=errors,
|
||||
)
|
||||
|
||||
async def async_step_local(self, user_input: Mapping[str, Any]) -> FlowResult:
|
||||
"""Handle collecting and verifying Awair Local API hosts."""
|
||||
|
||||
errors = {}
|
||||
|
||||
if user_input is not None:
|
||||
self._device, error = await self._check_local_connection(
|
||||
user_input[CONF_HOST]
|
||||
)
|
||||
|
||||
if self._device is not None:
|
||||
await self.async_set_unique_id(self._device.mac_address)
|
||||
self._abort_if_unique_id_configured(error="already_configured_device")
|
||||
title = f"{self._device.model} ({self._device.device_id})"
|
||||
return self.async_create_entry(title=title, data=user_input)
|
||||
|
||||
if error is not None:
|
||||
errors = {CONF_HOST: error}
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="local",
|
||||
data_schema=vol.Schema({vol.Required(CONF_HOST): str}),
|
||||
description_placeholders={
|
||||
"url": "https://support.getawair.com/hc/en-us/articles/360049221014-Awair-Element-Local-API-Feature"
|
||||
},
|
||||
errors=errors,
|
||||
)
|
||||
|
||||
|
@ -60,7 +149,7 @@ class AwairFlowHandler(ConfigFlow, domain=DOMAIN):
|
|||
|
||||
if user_input is not None:
|
||||
access_token = user_input[CONF_ACCESS_TOKEN]
|
||||
_, error = await self._check_connection(access_token)
|
||||
_, error = await self._check_cloud_connection(access_token)
|
||||
|
||||
if error is None:
|
||||
entry = await self.async_set_unique_id(self.unique_id)
|
||||
|
@ -79,7 +168,24 @@ class AwairFlowHandler(ConfigFlow, domain=DOMAIN):
|
|||
errors=errors,
|
||||
)
|
||||
|
||||
async def _check_connection(self, access_token: str):
|
||||
async def _check_local_connection(self, device_address: str):
|
||||
"""Check the access token is valid."""
|
||||
session = async_get_clientsession(self.hass)
|
||||
awair = AwairLocal(session=session, device_addrs=[device_address])
|
||||
|
||||
try:
|
||||
devices = await awair.devices()
|
||||
return (devices[0], None)
|
||||
|
||||
except ClientConnectorError as err:
|
||||
LOGGER.error("Unable to connect error: %s", err)
|
||||
return (None, "unreachable")
|
||||
|
||||
except AwairError as err:
|
||||
LOGGER.error("Unexpected API error: %s", err)
|
||||
return (None, "unknown")
|
||||
|
||||
async def _check_cloud_connection(self, access_token: str):
|
||||
"""Check the access token is valid."""
|
||||
session = async_get_clientsession(self.hass)
|
||||
awair = Awair(access_token=access_token, session=session)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue