Amended config flow, hardcoded support for v3

-Amended config flow, took out second step to select socks
-Added configuration to allow users to change polling interval
-Hardcoded support for only v3 sock, I do not own any other socks to test with
This commit is contained in:
RyanClark123
2023-05-09 16:37:25 +01:00
parent daff165593
commit a5a8336006
8 changed files with 153 additions and 99 deletions
+30 -19
View File
@@ -3,14 +3,24 @@ from __future__ import annotations
import logging
from pyowletapi.owlet import Owlet
from pyowletapi.api import OwletAPI
from pyowletapi.sock import Sock
from pyowletapi.exceptions import OwletAuthenticationError, OwletDevicesError
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import DOMAIN, CONF_OWLET_REGION, CONF_OWLET_USERNAME, CONF_OWLET_PASSWORD
from .const import (
DOMAIN,
CONF_OWLET_REGION,
CONF_OWLET_USERNAME,
CONF_OWLET_PASSWORD,
CONF_OWLET_POLLINTERVAL,
CONF_OWLET_EXPIRY,
CONF_OWLET_TOKEN,
SUPPORTED_VERSIONS,
)
from .coordinator import OwletCoordinator
PLATFORMS: list[Platform] = [Platform.BINARY_SENSOR, Platform.SENSOR]
@@ -22,34 +32,35 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Owlet Smart Sock from a config entry."""
hass.data.setdefault(DOMAIN, {})
owlet = Owlet(
owlet_api = OwletAPI(
entry.data[CONF_OWLET_REGION],
entry.data[CONF_OWLET_USERNAME],
entry.data[CONF_OWLET_PASSWORD],
entry.data[CONF_OWLET_TOKEN],
entry.data[CONF_OWLET_EXPIRY],
async_get_clientsession(hass),
)
try:
await owlet.authenticate()
token = await owlet_api.authenticate()
if token:
entry.data[CONF_OWLET_TOKEN] = token[CONF_OWLET_TOKEN]
entry.data[CONF_OWLET_EXPIRY] = token[CONF_OWLET_EXPIRY]
socks = {
device["device"]["dsn"]: Sock(owlet_api, device["device"])
for device in await owlet_api.get_devices(SUPPORTED_VERSIONS)
}
except OwletAuthenticationError as err:
_LOGGER.error("Login failed %s", err)
return False
existing_socks = entry.data["devices"]
new_socks = []
try:
socks = await owlet.get_devices()
except OwletDevicesError:
pass
[new_socks.append(sock) for sock in socks if sock not in existing_socks]
if new_socks:
hass.config_entries.async_update_entry(
entry, data={**entry.data, **{"devices": socks}}
)
coordinators = [OwletCoordinator(hass, sock) for sock in socks.values()]
coordinators = [
OwletCoordinator(hass, sock, entry.options.get(CONF_OWLET_POLLINTERVAL))
for sock in socks.values()
]
for coordinator in coordinators:
await coordinator.async_config_entry_first_refresh()
+45 -25
View File
@@ -4,7 +4,7 @@ from __future__ import annotations
import logging
from typing import Any
from pyowletapi.owlet import Owlet
from pyowletapi.api import OwletAPI
from pyowletapi.sock import Sock
from pyowletapi.exceptions import (
OwletConnectionError,
@@ -18,22 +18,27 @@ from homeassistant import config_entries
from homeassistant.data_entry_flow import FlowResult
from homeassistant.config_entries import ConfigEntry
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers import config_validation
from homeassistant.core import callback
from .const import (
DOMAIN,
CONF_OWLET_REGION,
CONF_OWLET_USERNAME,
CONF_OWLET_PASSWORD,
CONF_OWLET_POLLINTERVAL,
CONF_OWLET_TOKEN,
CONF_OWLET_EXPIRY,
POLLING_INTERVAL,
SUPPORTED_VERSIONS,
)
_LOGGER = logging.getLogger(__name__)
STEP_USER_DATA_SCHEMA = vol.Schema(
{
vol.Required(CONF_OWLET_REGION): vol.In(["europe", "world"]),
vol.Required(CONF_OWLET_USERNAME): str,
vol.Required(CONF_OWLET_PASSWORD): str,
vol.Required("region"): vol.In(["europe", "world"]),
vol.Required("username"): str,
vol.Required("password"): str,
}
)
@@ -60,7 +65,7 @@ class OwletConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
self._username = user_input[CONF_OWLET_USERNAME]
self._password = user_input[CONF_OWLET_PASSWORD]
owlet = Owlet(
owlet_api = OwletAPI(
self._region,
self._username,
self._password,
@@ -71,10 +76,20 @@ class OwletConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
self._abort_if_unique_id_configured()
try:
await owlet.authenticate()
token = await owlet_api.authenticate()
try:
self._devices = await owlet.get_devices()
return await self.async_step_socks()
await owlet_api.get_devices(SUPPORTED_VERSIONS)
return self.async_create_entry(
title=self._username,
data={
CONF_OWLET_REGION: self._region,
CONF_OWLET_USERNAME: self._username,
CONF_OWLET_PASSWORD: self._password,
CONF_OWLET_TOKEN: token[CONF_OWLET_TOKEN],
CONF_OWLET_EXPIRY: token[CONF_OWLET_EXPIRY],
},
options={CONF_OWLET_POLLINTERVAL: POLLING_INTERVAL},
)
except OwletDevicesError:
errors["base"] = "no_devices"
@@ -90,27 +105,32 @@ class OwletConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors
)
async def async_step_socks(self, user_input=None):
"""Allow the user to choose which devices to configure"""
errors = {}
@staticmethod
@callback
def async_get_options_flow(config_entry):
"""Get the options flow for this handler."""
return OptionsFlowHandler(config_entry)
class OptionsFlowHandler(config_entries.OptionsFlow):
"""Handle a options flow for owlet"""
def __init__(self, config_entry: config_entries.ConfigEntry) -> None:
"""Initialise options flow"""
self.config_entry = config_entry
async def async_step_init(self, user_input=None):
"""Handle options flow"""
if user_input is not None:
return self.async_create_entry(
title="Owlet",
data={
CONF_OWLET_REGION: self._region,
CONF_OWLET_USERNAME: self._username,
CONF_OWLET_PASSWORD: self._password,
"devices": user_input["socks"],
},
)
return self.async_create_entry(title="", data=user_input)
schema = vol.Schema(
{
vol.Required("socks"): config_validation.multi_select(
{sock: sock for sock in list(self._devices.keys())}
),
vol.Required(
CONF_OWLET_POLLINTERVAL,
default=self.config_entry.options.get(CONF_OWLET_POLLINTERVAL),
): vol.All(vol.Coerce(int), vol.Range(min=10)),
}
)
return self.async_show_form(step_id="socks", data_schema=schema, errors=errors)
return self.async_show_form(step_id="init", data_schema=schema)
+5 -1
View File
@@ -5,7 +5,11 @@ DOMAIN = "owlet"
CONF_OWLET_REGION = "region"
CONF_OWLET_USERNAME = "username"
CONF_OWLET_PASSWORD = "password"
CONF_SOCK_SERIAL = "sock_serial"
CONF_OWLET_DEVICES = "devices"
CONF_OWLET_POLLINTERVAL = "pollinterval"
CONF_OWLET_TOKEN = "token"
CONF_OWLET_EXPIRY = "expiry"
SUPPORTED_VERSIONS = [3]
POLLING_INTERVAL = 10
MANUFACTURER = "Owlet Baby Care"
+4 -3
View File
@@ -13,7 +13,6 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda
from .const import (
DOMAIN,
POLLING_INTERVAL,
MANUFACTURER,
)
@@ -23,18 +22,19 @@ _LOGGER = logging.getLogger(__name__)
class OwletCoordinator(DataUpdateCoordinator):
"""Coordinator is responsible for querying the device at a specified route."""
def __init__(self, hass: HomeAssistant, sock: Sock) -> None:
def __init__(self, hass: HomeAssistant, sock: Sock, interval: int) -> None:
"""Initialise a custom coordinator."""
super().__init__(
hass,
_LOGGER,
name=DOMAIN,
update_interval=timedelta(seconds=POLLING_INTERVAL),
update_interval=timedelta(seconds=interval),
)
assert self.config_entry is not None
self._device_unique_id = sock.serial
self._model = sock.model
self._sw_version = sock.sw_version
self._hw_version = sock.version
self.sock = sock
self.device_info = DeviceInfo(
identifiers={(DOMAIN, self._device_unique_id)},
@@ -42,6 +42,7 @@ class OwletCoordinator(DataUpdateCoordinator):
manufacturer=MANUFACTURER,
model=self._model,
sw_version=self._sw_version,
hw_version=self._hw_version,
)
async def _async_update_data(self) -> None:
+2 -2
View File
@@ -10,7 +10,7 @@
"homekit": {},
"iot_class": "cloud_polling",
"requirements": [
"pyowletapi==2023.5.7"
"pyowletapi==2023.5.17"
],
"version":"1.1.0"
"version":"1.2.0"
}
+10 -4
View File
@@ -8,10 +8,6 @@
"username": "Email",
"password": "Password"
}
},
"socks":{
"title": "Configure Socks",
"description":"Select socks to configure"
}
},
"error": {
@@ -22,5 +18,15 @@
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
}
},
"options": {
"step": {
"init":{
"title":"Configure options for Owlet",
"data":{
"pollinterval": "Polling interval in seconds, min 10"
}
}
}
}
}
+10 -4
View File
@@ -8,10 +8,6 @@
"username": "Email",
"password": "Password"
}
},
"socks":{
"title": "Configure Socks",
"description":"Select socks to configure"
}
},
"error": {
@@ -22,5 +18,15 @@
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
}
},
"options": {
"step": {
"init":{
"title":"Configure options for Owlet",
"data":{
"pollinterval": "Polling interval in seconds, min 10"
}
}
}
}
}
+10 -4
View File
@@ -8,10 +8,6 @@
"username": "Email",
"password": "Password"
}
},
"socks":{
"title": "Configure Socks",
"description":"Select socks to configure"
}
},
"error": {
@@ -22,5 +18,15 @@
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
}
},
"options": {
"step": {
"init":{
"title":"Configure options for Owlet",
"data":{
"pollinterval": "Polling interval in seconds, min 10"
}
}
}
}
}