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:
@@ -3,14 +3,24 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from pyowletapi.owlet import Owlet
|
from pyowletapi.api import OwletAPI
|
||||||
|
from pyowletapi.sock import Sock
|
||||||
from pyowletapi.exceptions import OwletAuthenticationError, OwletDevicesError
|
from pyowletapi.exceptions import OwletAuthenticationError, OwletDevicesError
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import Platform
|
from homeassistant.const import Platform
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
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
|
from .coordinator import OwletCoordinator
|
||||||
|
|
||||||
PLATFORMS: list[Platform] = [Platform.BINARY_SENSOR, Platform.SENSOR]
|
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."""
|
"""Set up Owlet Smart Sock from a config entry."""
|
||||||
hass.data.setdefault(DOMAIN, {})
|
hass.data.setdefault(DOMAIN, {})
|
||||||
|
|
||||||
owlet = Owlet(
|
owlet_api = OwletAPI(
|
||||||
entry.data[CONF_OWLET_REGION],
|
entry.data[CONF_OWLET_REGION],
|
||||||
entry.data[CONF_OWLET_USERNAME],
|
entry.data[CONF_OWLET_USERNAME],
|
||||||
entry.data[CONF_OWLET_PASSWORD],
|
entry.data[CONF_OWLET_PASSWORD],
|
||||||
|
entry.data[CONF_OWLET_TOKEN],
|
||||||
|
entry.data[CONF_OWLET_EXPIRY],
|
||||||
async_get_clientsession(hass),
|
async_get_clientsession(hass),
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
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:
|
except OwletAuthenticationError as err:
|
||||||
_LOGGER.error("Login failed %s", err)
|
_LOGGER.error("Login failed %s", err)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
existing_socks = entry.data["devices"]
|
coordinators = [
|
||||||
new_socks = []
|
OwletCoordinator(hass, sock, entry.options.get(CONF_OWLET_POLLINTERVAL))
|
||||||
try:
|
for sock in socks.values()
|
||||||
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()]
|
|
||||||
|
|
||||||
for coordinator in coordinators:
|
for coordinator in coordinators:
|
||||||
await coordinator.async_config_entry_first_refresh()
|
await coordinator.async_config_entry_first_refresh()
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from __future__ import annotations
|
|||||||
import logging
|
import logging
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from pyowletapi.owlet import Owlet
|
from pyowletapi.api import OwletAPI
|
||||||
from pyowletapi.sock import Sock
|
from pyowletapi.sock import Sock
|
||||||
from pyowletapi.exceptions import (
|
from pyowletapi.exceptions import (
|
||||||
OwletConnectionError,
|
OwletConnectionError,
|
||||||
@@ -18,22 +18,27 @@ from homeassistant import config_entries
|
|||||||
from homeassistant.data_entry_flow import FlowResult
|
from homeassistant.data_entry_flow import FlowResult
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
from homeassistant.helpers import config_validation
|
from homeassistant.core import callback
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
CONF_OWLET_REGION,
|
CONF_OWLET_REGION,
|
||||||
CONF_OWLET_USERNAME,
|
CONF_OWLET_USERNAME,
|
||||||
CONF_OWLET_PASSWORD,
|
CONF_OWLET_PASSWORD,
|
||||||
|
CONF_OWLET_POLLINTERVAL,
|
||||||
|
CONF_OWLET_TOKEN,
|
||||||
|
CONF_OWLET_EXPIRY,
|
||||||
|
POLLING_INTERVAL,
|
||||||
|
SUPPORTED_VERSIONS,
|
||||||
)
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
STEP_USER_DATA_SCHEMA = vol.Schema(
|
STEP_USER_DATA_SCHEMA = vol.Schema(
|
||||||
{
|
{
|
||||||
vol.Required(CONF_OWLET_REGION): vol.In(["europe", "world"]),
|
vol.Required("region"): vol.In(["europe", "world"]),
|
||||||
vol.Required(CONF_OWLET_USERNAME): str,
|
vol.Required("username"): str,
|
||||||
vol.Required(CONF_OWLET_PASSWORD): str,
|
vol.Required("password"): str,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -60,7 +65,7 @@ class OwletConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
self._username = user_input[CONF_OWLET_USERNAME]
|
self._username = user_input[CONF_OWLET_USERNAME]
|
||||||
self._password = user_input[CONF_OWLET_PASSWORD]
|
self._password = user_input[CONF_OWLET_PASSWORD]
|
||||||
|
|
||||||
owlet = Owlet(
|
owlet_api = OwletAPI(
|
||||||
self._region,
|
self._region,
|
||||||
self._username,
|
self._username,
|
||||||
self._password,
|
self._password,
|
||||||
@@ -71,10 +76,20 @@ class OwletConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
self._abort_if_unique_id_configured()
|
self._abort_if_unique_id_configured()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await owlet.authenticate()
|
token = await owlet_api.authenticate()
|
||||||
try:
|
try:
|
||||||
self._devices = await owlet.get_devices()
|
await owlet_api.get_devices(SUPPORTED_VERSIONS)
|
||||||
return await self.async_step_socks()
|
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:
|
except OwletDevicesError:
|
||||||
errors["base"] = "no_devices"
|
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
|
step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_step_socks(self, user_input=None):
|
@staticmethod
|
||||||
"""Allow the user to choose which devices to configure"""
|
@callback
|
||||||
errors = {}
|
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:
|
if user_input is not None:
|
||||||
return self.async_create_entry(
|
return self.async_create_entry(title="", data=user_input)
|
||||||
title="Owlet",
|
|
||||||
data={
|
|
||||||
CONF_OWLET_REGION: self._region,
|
|
||||||
CONF_OWLET_USERNAME: self._username,
|
|
||||||
CONF_OWLET_PASSWORD: self._password,
|
|
||||||
"devices": user_input["socks"],
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
schema = vol.Schema(
|
schema = vol.Schema(
|
||||||
{
|
{
|
||||||
vol.Required("socks"): config_validation.multi_select(
|
vol.Required(
|
||||||
{sock: sock for sock in list(self._devices.keys())}
|
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,7 +5,11 @@ DOMAIN = "owlet"
|
|||||||
CONF_OWLET_REGION = "region"
|
CONF_OWLET_REGION = "region"
|
||||||
CONF_OWLET_USERNAME = "username"
|
CONF_OWLET_USERNAME = "username"
|
||||||
CONF_OWLET_PASSWORD = "password"
|
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
|
POLLING_INTERVAL = 10
|
||||||
MANUFACTURER = "Owlet Baby Care"
|
MANUFACTURER = "Owlet Baby Care"
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda
|
|||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
POLLING_INTERVAL,
|
|
||||||
MANUFACTURER,
|
MANUFACTURER,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -23,18 +22,19 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
class OwletCoordinator(DataUpdateCoordinator):
|
class OwletCoordinator(DataUpdateCoordinator):
|
||||||
"""Coordinator is responsible for querying the device at a specified route."""
|
"""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."""
|
"""Initialise a custom coordinator."""
|
||||||
super().__init__(
|
super().__init__(
|
||||||
hass,
|
hass,
|
||||||
_LOGGER,
|
_LOGGER,
|
||||||
name=DOMAIN,
|
name=DOMAIN,
|
||||||
update_interval=timedelta(seconds=POLLING_INTERVAL),
|
update_interval=timedelta(seconds=interval),
|
||||||
)
|
)
|
||||||
assert self.config_entry is not None
|
assert self.config_entry is not None
|
||||||
self._device_unique_id = sock.serial
|
self._device_unique_id = sock.serial
|
||||||
self._model = sock.model
|
self._model = sock.model
|
||||||
self._sw_version = sock.sw_version
|
self._sw_version = sock.sw_version
|
||||||
|
self._hw_version = sock.version
|
||||||
self.sock = sock
|
self.sock = sock
|
||||||
self.device_info = DeviceInfo(
|
self.device_info = DeviceInfo(
|
||||||
identifiers={(DOMAIN, self._device_unique_id)},
|
identifiers={(DOMAIN, self._device_unique_id)},
|
||||||
@@ -42,6 +42,7 @@ class OwletCoordinator(DataUpdateCoordinator):
|
|||||||
manufacturer=MANUFACTURER,
|
manufacturer=MANUFACTURER,
|
||||||
model=self._model,
|
model=self._model,
|
||||||
sw_version=self._sw_version,
|
sw_version=self._sw_version,
|
||||||
|
hw_version=self._hw_version,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def _async_update_data(self) -> None:
|
async def _async_update_data(self) -> None:
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
"homekit": {},
|
"homekit": {},
|
||||||
"iot_class": "cloud_polling",
|
"iot_class": "cloud_polling",
|
||||||
"requirements": [
|
"requirements": [
|
||||||
"pyowletapi==2023.5.7"
|
"pyowletapi==2023.5.17"
|
||||||
],
|
],
|
||||||
"version":"1.1.0"
|
"version":"1.2.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,10 +8,6 @@
|
|||||||
"username": "Email",
|
"username": "Email",
|
||||||
"password": "Password"
|
"password": "Password"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"socks":{
|
|
||||||
"title": "Configure Socks",
|
|
||||||
"description":"Select socks to configure"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -22,5 +18,15 @@
|
|||||||
"abort": {
|
"abort": {
|
||||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
"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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,26 +1,32 @@
|
|||||||
{
|
{
|
||||||
"config": {
|
"config": {
|
||||||
"step": {
|
"step": {
|
||||||
"user":{
|
"user":{
|
||||||
"title": "Enter login details",
|
"title": "Enter login details",
|
||||||
"data":{
|
"data":{
|
||||||
"region": "Region",
|
"region": "Region",
|
||||||
"username": "Email",
|
"username": "Email",
|
||||||
"password": "Password"
|
"password": "Password"
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"socks":{
|
},
|
||||||
"title": "Configure Socks",
|
"error": {
|
||||||
"description":"Select socks to configure"
|
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||||
|
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
|
||||||
|
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||||
|
},
|
||||||
|
"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"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
|
||||||
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
|
|
||||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
|
||||||
},
|
|
||||||
"abort": {
|
|
||||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
@@ -1,26 +1,32 @@
|
|||||||
{
|
{
|
||||||
"config": {
|
"config": {
|
||||||
"step": {
|
"step": {
|
||||||
"user":{
|
"user":{
|
||||||
"title": "Enter login details",
|
"title": "Enter login details",
|
||||||
"data":{
|
"data":{
|
||||||
"region": "Region",
|
"region": "Region",
|
||||||
"username": "Email",
|
"username": "Email",
|
||||||
"password": "Password"
|
"password": "Password"
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"socks":{
|
},
|
||||||
"title": "Configure Socks",
|
"error": {
|
||||||
"description":"Select socks to configure"
|
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||||
|
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
|
||||||
|
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||||
|
},
|
||||||
|
"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"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
|
||||||
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
|
|
||||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
|
||||||
},
|
|
||||||
"abort": {
|
|
||||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user