mirror of
https://github.com/home-assistant/core.git
synced 2025-06-25 01:21:51 +02:00
Add support for Keg and Airlock to Plaato using polling API (#34760)
Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
@ -702,7 +702,11 @@ omit =
|
||||
homeassistant/components/ping/device_tracker.py
|
||||
homeassistant/components/pioneer/media_player.py
|
||||
homeassistant/components/pjlink/media_player.py
|
||||
homeassistant/components/plaato/*
|
||||
homeassistant/components/plaato/__init__.py
|
||||
homeassistant/components/plaato/binary_sensor.py
|
||||
homeassistant/components/plaato/const.py
|
||||
homeassistant/components/plaato/entity.py
|
||||
homeassistant/components/plaato/sensor.py
|
||||
homeassistant/components/plex/media_player.py
|
||||
homeassistant/components/plum_lightpad/light.py
|
||||
homeassistant/components/pocketcasts/sensor.py
|
||||
|
@ -1,11 +1,34 @@
|
||||
"""Support for Plaato Airlock."""
|
||||
"""Support for Plaato devices."""
|
||||
|
||||
import asyncio
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
|
||||
from aiohttp import web
|
||||
from pyplaato.models.airlock import PlaatoAirlock
|
||||
from pyplaato.plaato import (
|
||||
ATTR_ABV,
|
||||
ATTR_BATCH_VOLUME,
|
||||
ATTR_BPM,
|
||||
ATTR_BUBBLES,
|
||||
ATTR_CO2_VOLUME,
|
||||
ATTR_DEVICE_ID,
|
||||
ATTR_DEVICE_NAME,
|
||||
ATTR_OG,
|
||||
ATTR_SG,
|
||||
ATTR_TEMP,
|
||||
ATTR_TEMP_UNIT,
|
||||
ATTR_VOLUME_UNIT,
|
||||
Plaato,
|
||||
PlaatoDeviceType,
|
||||
)
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import DOMAIN as SENSOR
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
CONF_SCAN_INTERVAL,
|
||||
CONF_TOKEN,
|
||||
CONF_WEBHOOK_ID,
|
||||
HTTP_OK,
|
||||
TEMP_CELSIUS,
|
||||
@ -13,31 +36,33 @@ from homeassistant.const import (
|
||||
VOLUME_GALLONS,
|
||||
VOLUME_LITERS,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
from homeassistant.helpers import aiohttp_client
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||
|
||||
from .const import DOMAIN
|
||||
from .const import (
|
||||
CONF_DEVICE_NAME,
|
||||
CONF_DEVICE_TYPE,
|
||||
CONF_USE_WEBHOOK,
|
||||
COORDINATOR,
|
||||
DEFAULT_SCAN_INTERVAL,
|
||||
DEVICE,
|
||||
DEVICE_ID,
|
||||
DEVICE_NAME,
|
||||
DEVICE_TYPE,
|
||||
DOMAIN,
|
||||
PLATFORMS,
|
||||
SENSOR_DATA,
|
||||
UNDO_UPDATE_LISTENER,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEPENDENCIES = ["webhook"]
|
||||
|
||||
PLAATO_DEVICE_SENSORS = "sensors"
|
||||
PLAATO_DEVICE_ATTRS = "attrs"
|
||||
|
||||
ATTR_DEVICE_ID = "device_id"
|
||||
ATTR_DEVICE_NAME = "device_name"
|
||||
ATTR_TEMP_UNIT = "temp_unit"
|
||||
ATTR_VOLUME_UNIT = "volume_unit"
|
||||
ATTR_BPM = "bpm"
|
||||
ATTR_TEMP = "temp"
|
||||
ATTR_SG = "sg"
|
||||
ATTR_OG = "og"
|
||||
ATTR_BUBBLES = "bubbles"
|
||||
ATTR_ABV = "abv"
|
||||
ATTR_CO2_VOLUME = "co2_volume"
|
||||
ATTR_BATCH_VOLUME = "batch_volume"
|
||||
|
||||
SENSOR_UPDATE = f"{DOMAIN}_sensor_update"
|
||||
SENSOR_DATA_KEY = f"{DOMAIN}.{SENSOR}"
|
||||
|
||||
@ -60,31 +85,124 @@ WEBHOOK_SCHEMA = vol.Schema(
|
||||
)
|
||||
|
||||
|
||||
async def async_setup(hass, hass_config):
|
||||
async def async_setup(hass: HomeAssistant, config: dict):
|
||||
"""Set up the Plaato component."""
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
return True
|
||||
|
||||
|
||||
async def async_setup_entry(hass, entry):
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||
"""Configure based on config entry."""
|
||||
if DOMAIN not in hass.data:
|
||||
hass.data[DOMAIN] = {}
|
||||
|
||||
use_webhook = entry.data[CONF_USE_WEBHOOK]
|
||||
|
||||
if use_webhook:
|
||||
async_setup_webhook(hass, entry)
|
||||
else:
|
||||
await async_setup_coordinator(hass, entry)
|
||||
|
||||
for platform in PLATFORMS:
|
||||
if entry.options.get(platform, True):
|
||||
hass.async_create_task(
|
||||
hass.config_entries.async_forward_entry_setup(entry, platform)
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@callback
|
||||
def async_setup_webhook(hass: HomeAssistant, entry: ConfigEntry):
|
||||
"""Init webhook based on config entry."""
|
||||
webhook_id = entry.data[CONF_WEBHOOK_ID]
|
||||
hass.components.webhook.async_register(DOMAIN, "Plaato", webhook_id, handle_webhook)
|
||||
device_name = entry.data[CONF_DEVICE_NAME]
|
||||
|
||||
hass.async_create_task(hass.config_entries.async_forward_entry_setup(entry, SENSOR))
|
||||
_set_entry_data(entry, hass)
|
||||
|
||||
return True
|
||||
hass.components.webhook.async_register(
|
||||
DOMAIN, f"{DOMAIN}.{device_name}", webhook_id, handle_webhook
|
||||
)
|
||||
|
||||
|
||||
async def async_unload_entry(hass, entry):
|
||||
async def async_setup_coordinator(hass: HomeAssistant, entry: ConfigEntry):
|
||||
"""Init auth token based on config entry."""
|
||||
auth_token = entry.data[CONF_TOKEN]
|
||||
device_type = entry.data[CONF_DEVICE_TYPE]
|
||||
|
||||
if entry.options.get(CONF_SCAN_INTERVAL):
|
||||
update_interval = timedelta(minutes=entry.options[CONF_SCAN_INTERVAL])
|
||||
else:
|
||||
update_interval = timedelta(minutes=DEFAULT_SCAN_INTERVAL)
|
||||
|
||||
coordinator = PlaatoCoordinator(hass, auth_token, device_type, update_interval)
|
||||
await coordinator.async_refresh()
|
||||
if not coordinator.last_update_success:
|
||||
raise ConfigEntryNotReady
|
||||
|
||||
_set_entry_data(entry, hass, coordinator, auth_token)
|
||||
|
||||
for platform in PLATFORMS:
|
||||
if entry.options.get(platform, True):
|
||||
coordinator.platforms.append(platform)
|
||||
|
||||
|
||||
def _set_entry_data(entry, hass, coordinator=None, device_id=None):
|
||||
device = {
|
||||
DEVICE_NAME: entry.data[CONF_DEVICE_NAME],
|
||||
DEVICE_TYPE: entry.data[CONF_DEVICE_TYPE],
|
||||
DEVICE_ID: device_id,
|
||||
}
|
||||
|
||||
hass.data[DOMAIN][entry.entry_id] = {
|
||||
COORDINATOR: coordinator,
|
||||
DEVICE: device,
|
||||
SENSOR_DATA: None,
|
||||
UNDO_UPDATE_LISTENER: entry.add_update_listener(_async_update_listener),
|
||||
}
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||
"""Unload a config entry."""
|
||||
hass.components.webhook.async_unregister(entry.data[CONF_WEBHOOK_ID])
|
||||
hass.data[SENSOR_DATA_KEY]()
|
||||
use_webhook = entry.data[CONF_USE_WEBHOOK]
|
||||
hass.data[DOMAIN][entry.entry_id][UNDO_UPDATE_LISTENER]()
|
||||
|
||||
await hass.config_entries.async_forward_entry_unload(entry, SENSOR)
|
||||
return True
|
||||
if use_webhook:
|
||||
return await async_unload_webhook(hass, entry)
|
||||
|
||||
return await async_unload_coordinator(hass, entry)
|
||||
|
||||
|
||||
async def async_unload_webhook(hass: HomeAssistant, entry: ConfigEntry):
|
||||
"""Unload webhook based entry."""
|
||||
if entry.data[CONF_WEBHOOK_ID] is not None:
|
||||
hass.components.webhook.async_unregister(entry.data[CONF_WEBHOOK_ID])
|
||||
return await async_unload_platforms(hass, entry, PLATFORMS)
|
||||
|
||||
|
||||
async def async_unload_coordinator(hass: HomeAssistant, entry: ConfigEntry):
|
||||
"""Unload auth token based entry."""
|
||||
coordinator = hass.data[DOMAIN][entry.entry_id][COORDINATOR]
|
||||
return await async_unload_platforms(hass, entry, coordinator.platforms)
|
||||
|
||||
|
||||
async def async_unload_platforms(hass: HomeAssistant, entry: ConfigEntry, platforms):
|
||||
"""Unload platforms."""
|
||||
unloaded = all(
|
||||
await asyncio.gather(
|
||||
*[
|
||||
hass.config_entries.async_forward_entry_unload(entry, platform)
|
||||
for platform in platforms
|
||||
]
|
||||
)
|
||||
)
|
||||
if unloaded:
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unloaded
|
||||
|
||||
|
||||
async def _async_update_listener(hass: HomeAssistant, entry: ConfigEntry):
|
||||
"""Handle options update."""
|
||||
await hass.config_entries.async_reload(entry.entry_id)
|
||||
|
||||
|
||||
async def handle_webhook(hass, webhook_id, request):
|
||||
@ -96,31 +214,9 @@ async def handle_webhook(hass, webhook_id, request):
|
||||
return
|
||||
|
||||
device_id = _device_id(data)
|
||||
sensor_data = PlaatoAirlock.from_web_hook(data)
|
||||
|
||||
attrs = {
|
||||
ATTR_DEVICE_NAME: data.get(ATTR_DEVICE_NAME),
|
||||
ATTR_DEVICE_ID: data.get(ATTR_DEVICE_ID),
|
||||
ATTR_TEMP_UNIT: data.get(ATTR_TEMP_UNIT),
|
||||
ATTR_VOLUME_UNIT: data.get(ATTR_VOLUME_UNIT),
|
||||
}
|
||||
|
||||
sensors = {
|
||||
ATTR_TEMP: data.get(ATTR_TEMP),
|
||||
ATTR_BPM: data.get(ATTR_BPM),
|
||||
ATTR_SG: data.get(ATTR_SG),
|
||||
ATTR_OG: data.get(ATTR_OG),
|
||||
ATTR_ABV: data.get(ATTR_ABV),
|
||||
ATTR_CO2_VOLUME: data.get(ATTR_CO2_VOLUME),
|
||||
ATTR_BATCH_VOLUME: data.get(ATTR_BATCH_VOLUME),
|
||||
ATTR_BUBBLES: data.get(ATTR_BUBBLES),
|
||||
}
|
||||
|
||||
hass.data[DOMAIN][device_id] = {
|
||||
PLAATO_DEVICE_ATTRS: attrs,
|
||||
PLAATO_DEVICE_SENSORS: sensors,
|
||||
}
|
||||
|
||||
async_dispatcher_send(hass, SENSOR_UPDATE, device_id)
|
||||
async_dispatcher_send(hass, SENSOR_UPDATE, *(device_id, sensor_data))
|
||||
|
||||
return web.Response(text=f"Saving status for {device_id}", status=HTTP_OK)
|
||||
|
||||
@ -128,3 +224,35 @@ async def handle_webhook(hass, webhook_id, request):
|
||||
def _device_id(data):
|
||||
"""Return name of device sensor."""
|
||||
return f"{data.get(ATTR_DEVICE_NAME)}_{data.get(ATTR_DEVICE_ID)}"
|
||||
|
||||
|
||||
class PlaatoCoordinator(DataUpdateCoordinator):
|
||||
"""Class to manage fetching data from the API."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass,
|
||||
auth_token,
|
||||
device_type: PlaatoDeviceType,
|
||||
update_interval: timedelta,
|
||||
):
|
||||
"""Initialize."""
|
||||
self.api = Plaato(auth_token=auth_token)
|
||||
self.hass = hass
|
||||
self.device_type = device_type
|
||||
self.platforms = []
|
||||
|
||||
super().__init__(
|
||||
hass,
|
||||
_LOGGER,
|
||||
name=DOMAIN,
|
||||
update_interval=update_interval,
|
||||
)
|
||||
|
||||
async def _async_update_data(self):
|
||||
"""Update data via library."""
|
||||
data = await self.api.get_data(
|
||||
session=aiohttp_client.async_get_clientsession(self.hass),
|
||||
device_type=self.device_type,
|
||||
)
|
||||
return data
|
||||
|
56
homeassistant/components/plaato/binary_sensor.py
Normal file
56
homeassistant/components/plaato/binary_sensor.py
Normal file
@ -0,0 +1,56 @@
|
||||
"""Support for Plaato Airlock sensors."""
|
||||
|
||||
import logging
|
||||
|
||||
from pyplaato.plaato import PlaatoKeg
|
||||
|
||||
from homeassistant.components.binary_sensor import (
|
||||
DEVICE_CLASS_OPENING,
|
||||
DEVICE_CLASS_PROBLEM,
|
||||
BinarySensorEntity,
|
||||
)
|
||||
|
||||
from .const import CONF_USE_WEBHOOK, COORDINATOR, DOMAIN
|
||||
from .entity import PlaatoEntity
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
"""Set up Plaato from a config entry."""
|
||||
|
||||
if config_entry.data[CONF_USE_WEBHOOK]:
|
||||
return False
|
||||
|
||||
coordinator = hass.data[DOMAIN][config_entry.entry_id][COORDINATOR]
|
||||
async_add_entities(
|
||||
PlaatoBinarySensor(
|
||||
hass.data[DOMAIN][config_entry.entry_id],
|
||||
sensor_type,
|
||||
coordinator,
|
||||
)
|
||||
for sensor_type in coordinator.data.binary_sensors.keys()
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
class PlaatoBinarySensor(PlaatoEntity, BinarySensorEntity):
|
||||
"""Representation of a Binary Sensor."""
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
"""Return true if the binary sensor is on."""
|
||||
if self._coordinator is not None:
|
||||
return self._coordinator.data.binary_sensors.get(self._sensor_type)
|
||||
return False
|
||||
|
||||
@property
|
||||
def device_class(self):
|
||||
"""Return the class of this device, from component DEVICE_CLASSES."""
|
||||
if self._coordinator is None:
|
||||
return None
|
||||
if self._sensor_type is PlaatoKeg.Pins.LEAK_DETECTION:
|
||||
return DEVICE_CLASS_PROBLEM
|
||||
if self._sensor_type is PlaatoKeg.Pins.POURING:
|
||||
return DEVICE_CLASS_OPENING
|
@ -1,10 +1,223 @@
|
||||
"""Config flow for GPSLogger."""
|
||||
from homeassistant.helpers import config_entry_flow
|
||||
"""Config flow for Plaato."""
|
||||
import logging
|
||||
|
||||
from .const import DOMAIN
|
||||
from pyplaato.plaato import PlaatoDeviceType
|
||||
import voluptuous as vol
|
||||
|
||||
config_entry_flow.register_webhook_flow(
|
||||
DOMAIN,
|
||||
"Webhook",
|
||||
{"docs_url": "https://www.home-assistant.io/integrations/plaato/"},
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_SCAN_INTERVAL, CONF_TOKEN, CONF_WEBHOOK_ID
|
||||
from homeassistant.core import callback
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
from .const import (
|
||||
CONF_CLOUDHOOK,
|
||||
CONF_DEVICE_NAME,
|
||||
CONF_DEVICE_TYPE,
|
||||
CONF_USE_WEBHOOK,
|
||||
DEFAULT_SCAN_INTERVAL,
|
||||
DOCS_URL,
|
||||
PLACEHOLDER_DEVICE_NAME,
|
||||
PLACEHOLDER_DEVICE_TYPE,
|
||||
PLACEHOLDER_DOCS_URL,
|
||||
PLACEHOLDER_WEBHOOK_URL,
|
||||
)
|
||||
from .const import DOMAIN # pylint:disable=unused-import
|
||||
|
||||
_LOGGER = logging.getLogger(__package__)
|
||||
|
||||
|
||||
class PlaatoConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
"""Handles a Plaato config flow."""
|
||||
|
||||
VERSION = 2
|
||||
CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize."""
|
||||
self._init_info = {}
|
||||
|
||||
async def async_step_user(self, user_input=None):
|
||||
"""Handle user step."""
|
||||
|
||||
if user_input is not None:
|
||||
self._init_info[CONF_DEVICE_TYPE] = PlaatoDeviceType(
|
||||
user_input[CONF_DEVICE_TYPE]
|
||||
)
|
||||
self._init_info[CONF_DEVICE_NAME] = user_input[CONF_DEVICE_NAME]
|
||||
|
||||
return await self.async_step_api_method()
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="user",
|
||||
data_schema=vol.Schema(
|
||||
{
|
||||
vol.Required(
|
||||
CONF_DEVICE_NAME,
|
||||
default=self._init_info.get(CONF_DEVICE_NAME, None),
|
||||
): str,
|
||||
vol.Required(
|
||||
CONF_DEVICE_TYPE,
|
||||
default=self._init_info.get(CONF_DEVICE_TYPE, None),
|
||||
): vol.In(list(PlaatoDeviceType)),
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
async def async_step_api_method(self, user_input=None):
|
||||
"""Handle device type step."""
|
||||
|
||||
device_type = self._init_info[CONF_DEVICE_TYPE]
|
||||
|
||||
if user_input is not None:
|
||||
token = user_input.get(CONF_TOKEN, None)
|
||||
use_webhook = user_input.get(CONF_USE_WEBHOOK, False)
|
||||
|
||||
if not token and not use_webhook:
|
||||
errors = {"base": PlaatoConfigFlow._get_error(device_type)}
|
||||
return await self._show_api_method_form(device_type, errors)
|
||||
|
||||
self._init_info[CONF_USE_WEBHOOK] = use_webhook
|
||||
self._init_info[CONF_TOKEN] = token
|
||||
return await self.async_step_webhook()
|
||||
|
||||
return await self._show_api_method_form(device_type)
|
||||
|
||||
async def async_step_webhook(self, user_input=None):
|
||||
"""Validate config step."""
|
||||
|
||||
use_webhook = self._init_info[CONF_USE_WEBHOOK]
|
||||
|
||||
if use_webhook and user_input is None:
|
||||
webhook_id, webhook_url, cloudhook = await self._get_webhook_id()
|
||||
self._init_info[CONF_WEBHOOK_ID] = webhook_id
|
||||
self._init_info[CONF_CLOUDHOOK] = cloudhook
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="webhook",
|
||||
description_placeholders={
|
||||
PLACEHOLDER_WEBHOOK_URL: webhook_url,
|
||||
PLACEHOLDER_DOCS_URL: DOCS_URL,
|
||||
},
|
||||
)
|
||||
|
||||
return await self._async_create_entry()
|
||||
|
||||
async def _async_create_entry(self):
|
||||
"""Create the entry step."""
|
||||
|
||||
webhook_id = self._init_info.get(CONF_WEBHOOK_ID, None)
|
||||
auth_token = self._init_info[CONF_TOKEN]
|
||||
device_name = self._init_info[CONF_DEVICE_NAME]
|
||||
device_type = self._init_info[CONF_DEVICE_TYPE]
|
||||
|
||||
unique_id = auth_token if auth_token else webhook_id
|
||||
|
||||
await self.async_set_unique_id(unique_id)
|
||||
self._abort_if_unique_id_configured()
|
||||
|
||||
return self.async_create_entry(
|
||||
title=device_type.name,
|
||||
data=self._init_info,
|
||||
description_placeholders={
|
||||
PLACEHOLDER_DEVICE_TYPE: device_type.name,
|
||||
PLACEHOLDER_DEVICE_NAME: device_name,
|
||||
},
|
||||
)
|
||||
|
||||
async def _show_api_method_form(
|
||||
self, device_type: PlaatoDeviceType, errors: dict = None
|
||||
):
|
||||
data_scheme = vol.Schema({vol.Optional(CONF_TOKEN, default=""): str})
|
||||
|
||||
if device_type == PlaatoDeviceType.Airlock:
|
||||
data_scheme = data_scheme.extend(
|
||||
{vol.Optional(CONF_USE_WEBHOOK, default=False): bool}
|
||||
)
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="api_method",
|
||||
data_schema=data_scheme,
|
||||
errors=errors,
|
||||
description_placeholders={PLACEHOLDER_DEVICE_TYPE: device_type.name},
|
||||
)
|
||||
|
||||
async def _get_webhook_id(self):
|
||||
"""Generate webhook ID."""
|
||||
webhook_id = self.hass.components.webhook.async_generate_id()
|
||||
if self.hass.components.cloud.async_active_subscription():
|
||||
webhook_url = await self.hass.components.cloud.async_create_cloudhook(
|
||||
webhook_id
|
||||
)
|
||||
cloudhook = True
|
||||
else:
|
||||
webhook_url = self.hass.components.webhook.async_generate_url(webhook_id)
|
||||
cloudhook = False
|
||||
|
||||
return webhook_id, webhook_url, cloudhook
|
||||
|
||||
@staticmethod
|
||||
def _get_error(device_type: PlaatoDeviceType):
|
||||
if device_type == PlaatoDeviceType.Airlock:
|
||||
return "no_api_method"
|
||||
return "no_auth_token"
|
||||
|
||||
@staticmethod
|
||||
@callback
|
||||
def async_get_options_flow(config_entry):
|
||||
"""Get the options flow for this handler."""
|
||||
return PlaatoOptionsFlowHandler(config_entry)
|
||||
|
||||
|
||||
class PlaatoOptionsFlowHandler(config_entries.OptionsFlow):
|
||||
"""Handle Plaato options."""
|
||||
|
||||
def __init__(self, config_entry: ConfigEntry):
|
||||
"""Initialize domain options flow."""
|
||||
super().__init__()
|
||||
|
||||
self._config_entry = config_entry
|
||||
|
||||
async def async_step_init(self, user_input=None):
|
||||
"""Manage the options."""
|
||||
use_webhook = self._config_entry.data.get(CONF_USE_WEBHOOK, False)
|
||||
if use_webhook:
|
||||
return await self.async_step_webhook()
|
||||
|
||||
return await self.async_step_user()
|
||||
|
||||
async def async_step_user(self, user_input=None):
|
||||
"""Manage the options."""
|
||||
if user_input is not None:
|
||||
return self.async_create_entry(title="", data=user_input)
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="user",
|
||||
data_schema=vol.Schema(
|
||||
{
|
||||
vol.Optional(
|
||||
CONF_SCAN_INTERVAL,
|
||||
default=self._config_entry.options.get(
|
||||
CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL
|
||||
),
|
||||
): cv.positive_int
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
async def async_step_webhook(self, user_input=None):
|
||||
"""Manage the options for webhook device."""
|
||||
if user_input is not None:
|
||||
return self.async_create_entry(title="", data=user_input)
|
||||
|
||||
webhook_id = self._config_entry.data.get(CONF_WEBHOOK_ID, None)
|
||||
webhook_url = (
|
||||
""
|
||||
if webhook_id is None
|
||||
else self.hass.components.webhook.async_generate_url(webhook_id)
|
||||
)
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="webhook",
|
||||
description_placeholders={PLACEHOLDER_WEBHOOK_URL: webhook_url},
|
||||
)
|
||||
|
@ -1,3 +1,27 @@
|
||||
"""Const for GPSLogger."""
|
||||
"""Const for Plaato."""
|
||||
from datetime import timedelta
|
||||
|
||||
DOMAIN = "plaato"
|
||||
PLAATO_DEVICE_SENSORS = "sensors"
|
||||
PLAATO_DEVICE_ATTRS = "attrs"
|
||||
SENSOR_SIGNAL = f"{DOMAIN}_%s_%s"
|
||||
|
||||
CONF_USE_WEBHOOK = "use_webhook"
|
||||
CONF_DEVICE_TYPE = "device_type"
|
||||
CONF_DEVICE_NAME = "device_name"
|
||||
CONF_CLOUDHOOK = "cloudhook"
|
||||
PLACEHOLDER_WEBHOOK_URL = "webhook_url"
|
||||
PLACEHOLDER_DOCS_URL = "docs_url"
|
||||
PLACEHOLDER_DEVICE_TYPE = "device_type"
|
||||
PLACEHOLDER_DEVICE_NAME = "device_name"
|
||||
DOCS_URL = "https://www.home-assistant.io/integrations/plaato/"
|
||||
PLATFORMS = ["sensor", "binary_sensor"]
|
||||
SENSOR_DATA = "sensor_data"
|
||||
COORDINATOR = "coordinator"
|
||||
DEVICE = "device"
|
||||
DEVICE_NAME = "device_name"
|
||||
DEVICE_TYPE = "device_type"
|
||||
DEVICE_ID = "device_id"
|
||||
UNDO_UPDATE_LISTENER = "undo_update_listener"
|
||||
DEFAULT_SCAN_INTERVAL = 5
|
||||
MIN_UPDATE_INTERVAL = timedelta(minutes=1)
|
||||
|
103
homeassistant/components/plaato/entity.py
Normal file
103
homeassistant/components/plaato/entity.py
Normal file
@ -0,0 +1,103 @@
|
||||
"""PlaatoEntity class."""
|
||||
from pyplaato.models.device import PlaatoDevice
|
||||
|
||||
from homeassistant.helpers import entity
|
||||
|
||||
from .const import (
|
||||
DEVICE,
|
||||
DEVICE_ID,
|
||||
DEVICE_NAME,
|
||||
DEVICE_TYPE,
|
||||
DOMAIN,
|
||||
SENSOR_DATA,
|
||||
SENSOR_SIGNAL,
|
||||
)
|
||||
|
||||
|
||||
class PlaatoEntity(entity.Entity):
|
||||
"""Representation of a Plaato Entity."""
|
||||
|
||||
def __init__(self, data, sensor_type, coordinator=None):
|
||||
"""Initialize the sensor."""
|
||||
self._coordinator = coordinator
|
||||
self._entry_data = data
|
||||
self._sensor_type = sensor_type
|
||||
self._device_id = data[DEVICE][DEVICE_ID]
|
||||
self._device_type = data[DEVICE][DEVICE_TYPE]
|
||||
self._device_name = data[DEVICE][DEVICE_NAME]
|
||||
self._state = 0
|
||||
|
||||
@property
|
||||
def _attributes(self) -> dict:
|
||||
return PlaatoEntity._to_snake_case(self._sensor_data.attributes)
|
||||
|
||||
@property
|
||||
def _sensor_name(self) -> str:
|
||||
return self._sensor_data.get_sensor_name(self._sensor_type)
|
||||
|
||||
@property
|
||||
def _sensor_data(self) -> PlaatoDevice:
|
||||
if self._coordinator:
|
||||
return self._coordinator.data
|
||||
return self._entry_data[SENSOR_DATA]
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the sensor."""
|
||||
return f"{DOMAIN} {self._device_type} {self._device_name} {self._sensor_name}".title()
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
"""Return the unique ID of this sensor."""
|
||||
return f"{self._device_id}_{self._sensor_type}"
|
||||
|
||||
@property
|
||||
def device_info(self):
|
||||
"""Get device info."""
|
||||
device_info = {
|
||||
"identifiers": {(DOMAIN, self._device_id)},
|
||||
"name": self._device_name,
|
||||
"manufacturer": "Plaato",
|
||||
"model": self._device_type,
|
||||
}
|
||||
|
||||
if self._sensor_data.firmware_version != "":
|
||||
device_info["sw_version"] = self._sensor_data.firmware_version
|
||||
|
||||
return device_info
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
"""Return the state attributes of the monitored installation."""
|
||||
if self._attributes is not None:
|
||||
return self._attributes
|
||||
|
||||
@property
|
||||
def available(self):
|
||||
"""Return if sensor is available."""
|
||||
if self._coordinator is not None:
|
||||
return self._coordinator.last_update_success
|
||||
return True
|
||||
|
||||
@property
|
||||
def should_poll(self):
|
||||
"""Return the polling state."""
|
||||
return False
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""When entity is added to hass."""
|
||||
if self._coordinator is not None:
|
||||
self.async_on_remove(
|
||||
self._coordinator.async_add_listener(self.async_write_ha_state)
|
||||
)
|
||||
else:
|
||||
self.async_on_remove(
|
||||
self.hass.helpers.dispatcher.async_dispatcher_connect(
|
||||
SENSOR_SIGNAL % (self._device_id, self._sensor_type),
|
||||
self.async_write_ha_state,
|
||||
)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _to_snake_case(dictionary: dict):
|
||||
return {k.lower().replace(" ", "_"): v for k, v in dictionary.items()}
|
@ -1,8 +1,10 @@
|
||||
{
|
||||
"domain": "plaato",
|
||||
"name": "Plaato Airlock",
|
||||
"name": "Plaato",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/plaato",
|
||||
"dependencies": ["webhook"],
|
||||
"codeowners": ["@JohNan"]
|
||||
"after_dependencies": ["cloud"],
|
||||
"codeowners": ["@JohNan"],
|
||||
"requirements": ["pyplaato==0.0.15"]
|
||||
}
|
||||
|
@ -1,28 +1,29 @@
|
||||
"""Support for Plaato Airlock sensors."""
|
||||
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
from homeassistant.const import PERCENTAGE
|
||||
from pyplaato.models.device import PlaatoDevice
|
||||
from pyplaato.plaato import PlaatoKeg
|
||||
|
||||
from homeassistant.components.sensor import DEVICE_CLASS_TEMPERATURE
|
||||
from homeassistant.helpers.dispatcher import (
|
||||
async_dispatcher_connect,
|
||||
async_dispatcher_send,
|
||||
)
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
from . import (
|
||||
ATTR_ABV,
|
||||
ATTR_BATCH_VOLUME,
|
||||
ATTR_BPM,
|
||||
ATTR_CO2_VOLUME,
|
||||
ATTR_TEMP,
|
||||
ATTR_TEMP_UNIT,
|
||||
ATTR_VOLUME_UNIT,
|
||||
DOMAIN as PLAATO_DOMAIN,
|
||||
PLAATO_DEVICE_ATTRS,
|
||||
PLAATO_DEVICE_SENSORS,
|
||||
SENSOR_DATA_KEY,
|
||||
SENSOR_UPDATE,
|
||||
from . import ATTR_TEMP, SENSOR_UPDATE
|
||||
from ...core import callback
|
||||
from .const import (
|
||||
CONF_USE_WEBHOOK,
|
||||
COORDINATOR,
|
||||
DEVICE,
|
||||
DEVICE_ID,
|
||||
DOMAIN,
|
||||
SENSOR_DATA,
|
||||
SENSOR_SIGNAL,
|
||||
)
|
||||
from .entity import PlaatoEntity
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@ -31,134 +32,58 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
|
||||
"""Set up the Plaato sensor."""
|
||||
|
||||
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
async def async_setup_entry(hass, entry, async_add_entities):
|
||||
"""Set up Plaato from a config entry."""
|
||||
devices = {}
|
||||
entry_data = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
def get_device(device_id):
|
||||
"""Get a device."""
|
||||
return hass.data[PLAATO_DOMAIN].get(device_id, False)
|
||||
|
||||
def get_device_sensors(device_id):
|
||||
"""Get device sensors."""
|
||||
return hass.data[PLAATO_DOMAIN].get(device_id).get(PLAATO_DEVICE_SENSORS)
|
||||
|
||||
async def _update_sensor(device_id):
|
||||
@callback
|
||||
async def _async_update_from_webhook(device_id, sensor_data: PlaatoDevice):
|
||||
"""Update/Create the sensors."""
|
||||
if device_id not in devices and get_device(device_id):
|
||||
entities = []
|
||||
sensors = get_device_sensors(device_id)
|
||||
entry_data[SENSOR_DATA] = sensor_data
|
||||
|
||||
for sensor_type in sensors:
|
||||
entities.append(PlaatoSensor(device_id, sensor_type))
|
||||
|
||||
devices[device_id] = entities
|
||||
|
||||
async_add_entities(entities, True)
|
||||
if device_id != entry_data[DEVICE][DEVICE_ID]:
|
||||
entry_data[DEVICE][DEVICE_ID] = device_id
|
||||
async_add_entities(
|
||||
[
|
||||
PlaatoSensor(entry_data, sensor_type)
|
||||
for sensor_type in sensor_data.sensors
|
||||
]
|
||||
)
|
||||
else:
|
||||
for entity in devices[device_id]:
|
||||
async_dispatcher_send(hass, f"{PLAATO_DOMAIN}_{entity.unique_id}")
|
||||
for sensor_type in sensor_data.sensors:
|
||||
async_dispatcher_send(hass, SENSOR_SIGNAL % (device_id, sensor_type))
|
||||
|
||||
hass.data[SENSOR_DATA_KEY] = async_dispatcher_connect(
|
||||
hass, SENSOR_UPDATE, _update_sensor
|
||||
)
|
||||
if entry.data[CONF_USE_WEBHOOK]:
|
||||
async_dispatcher_connect(hass, SENSOR_UPDATE, _async_update_from_webhook)
|
||||
else:
|
||||
coordinator = entry_data[COORDINATOR]
|
||||
async_add_entities(
|
||||
PlaatoSensor(entry_data, sensor_type, coordinator)
|
||||
for sensor_type in coordinator.data.sensors.keys()
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
class PlaatoSensor(Entity):
|
||||
"""Representation of a Sensor."""
|
||||
|
||||
def __init__(self, device_id, sensor_type):
|
||||
"""Initialize the sensor."""
|
||||
self._device_id = device_id
|
||||
self._type = sensor_type
|
||||
self._state = 0
|
||||
self._name = f"{device_id} {sensor_type}"
|
||||
self._attributes = None
|
||||
class PlaatoSensor(PlaatoEntity):
|
||||
"""Representation of a Plaato Sensor."""
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the sensor."""
|
||||
return f"{PLAATO_DOMAIN} {self._name}"
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
"""Return the unique ID of this sensor."""
|
||||
return f"{self._device_id}_{self._type}"
|
||||
|
||||
@property
|
||||
def device_info(self):
|
||||
"""Get device info."""
|
||||
return {
|
||||
"identifiers": {(PLAATO_DOMAIN, self._device_id)},
|
||||
"name": self._device_id,
|
||||
"manufacturer": "Plaato",
|
||||
"model": "Airlock",
|
||||
}
|
||||
|
||||
def get_sensors(self):
|
||||
"""Get device sensors."""
|
||||
return (
|
||||
self.hass.data[PLAATO_DOMAIN]
|
||||
.get(self._device_id)
|
||||
.get(PLAATO_DEVICE_SENSORS, False)
|
||||
)
|
||||
|
||||
def get_sensors_unit_of_measurement(self, sensor_type):
|
||||
"""Get unit of measurement for sensor of type."""
|
||||
return (
|
||||
self.hass.data[PLAATO_DOMAIN]
|
||||
.get(self._device_id)
|
||||
.get(PLAATO_DEVICE_ATTRS, [])
|
||||
.get(sensor_type, "")
|
||||
)
|
||||
def device_class(self) -> Optional[str]:
|
||||
"""Return the class of this device, from component DEVICE_CLASSES."""
|
||||
if self._coordinator is not None:
|
||||
if self._sensor_type == PlaatoKeg.Pins.TEMPERATURE:
|
||||
return DEVICE_CLASS_TEMPERATURE
|
||||
if self._sensor_type == ATTR_TEMP:
|
||||
return DEVICE_CLASS_TEMPERATURE
|
||||
return None
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
"""Return the state of the sensor."""
|
||||
sensors = self.get_sensors()
|
||||
if sensors is False:
|
||||
_LOGGER.debug("Device with name %s has no sensors", self.name)
|
||||
return 0
|
||||
|
||||
if self._type == ATTR_ABV:
|
||||
return round(sensors.get(self._type), 2)
|
||||
if self._type == ATTR_TEMP:
|
||||
return round(sensors.get(self._type), 1)
|
||||
if self._type == ATTR_CO2_VOLUME:
|
||||
return round(sensors.get(self._type), 2)
|
||||
return sensors.get(self._type)
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
"""Return the state attributes of the monitored installation."""
|
||||
if self._attributes is not None:
|
||||
return self._attributes
|
||||
return self._sensor_data.sensors.get(self._sensor_type)
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
"""Return the unit of measurement."""
|
||||
if self._type == ATTR_TEMP:
|
||||
return self.get_sensors_unit_of_measurement(ATTR_TEMP_UNIT)
|
||||
if self._type == ATTR_BATCH_VOLUME or self._type == ATTR_CO2_VOLUME:
|
||||
return self.get_sensors_unit_of_measurement(ATTR_VOLUME_UNIT)
|
||||
if self._type == ATTR_BPM:
|
||||
return "bpm"
|
||||
if self._type == ATTR_ABV:
|
||||
return PERCENTAGE
|
||||
|
||||
return ""
|
||||
|
||||
@property
|
||||
def should_poll(self):
|
||||
"""Return the polling state."""
|
||||
return False
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""Register callbacks."""
|
||||
self.async_on_remove(
|
||||
self.hass.helpers.dispatcher.async_dispatcher_connect(
|
||||
f"{PLAATO_DOMAIN}_{self.unique_id}", self.async_write_ha_state
|
||||
)
|
||||
)
|
||||
return self._sensor_data.get_unit_of_measurement(self._sensor_type)
|
||||
|
@ -2,16 +2,53 @@
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Set up the Plaato Webhook",
|
||||
"description": "[%key:common::config_flow::description::confirm_setup%]"
|
||||
"title": "Set up the Plaato devices",
|
||||
"description": "[%key:common::config_flow::description::confirm_setup%]",
|
||||
"data": {
|
||||
"device_name": "Name your device",
|
||||
"device_type": "Type of Plaato device"
|
||||
}
|
||||
},
|
||||
"api_method": {
|
||||
"title": "Select API method",
|
||||
"description": "To be able to query the API an `auth_token` is required which can be obtained by following [these](https://plaato.zendesk.com/hc/en-us/articles/360003234717-Auth-token) instructions\n\n Selected device: **{device_type}** \n\nIf you rather use the built in webhook method (Airlock only) please check the box below and leave Auth Token blank",
|
||||
"data": {
|
||||
"use_webhook": "Use webhook",
|
||||
"token": "Paste Auth Token here"
|
||||
}
|
||||
},
|
||||
"webhook": {
|
||||
"title": "Webhook to use",
|
||||
"description": "To send events to Home Assistant, you will need to setup the webhook feature in Plaato Airlock.\n\nFill in the following info:\n\n- URL: `{webhook_url}`\n- Method: POST\n\nSee [the documentation]({docs_url}) for further details."
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"invalid_webhook_device": "You have selected a device that doesn't not support sending data to a webhook. It is only available for the Airlock",
|
||||
"no_auth_token": "You need to add an auth token",
|
||||
"no_api_method": "You need to add an auth token or select webhook"
|
||||
},
|
||||
"abort": {
|
||||
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]",
|
||||
"webhook_not_internet_accessible": "[%key:common::config_flow::abort::webhook_not_internet_accessible%]"
|
||||
"webhook_not_internet_accessible": "[%key:common::config_flow::abort::webhook_not_internet_accessible%]",
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]"
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "To send events to Home Assistant, you will need to setup the webhook feature in Plaato Airlock.\n\nFill in the following info:\n\n- URL: `{webhook_url}`\n- Method: POST\n\nSee [the documentation]({docs_url}) for further details."
|
||||
"default": "Your Plaato {device_type} with name **{device_name}** was successfully setup!"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"webhook": {
|
||||
"title": "Options for Plaato Airlock",
|
||||
"description": "Webhook info:\n\n- URL: `{webhook_url}`\n- Method: POST\n\n"
|
||||
},
|
||||
"user": {
|
||||
"title": "Options for Plaato",
|
||||
"description": "Set the update interval (minutes)",
|
||||
"data": {
|
||||
"update_interval": "Update interval (minutes)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,53 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Account is already configured",
|
||||
"single_instance_allowed": "Already configured. Only a single configuration possible.",
|
||||
"webhook_not_internet_accessible": "Your Home Assistant instance needs to be accessible from the internet to receive webhook messages."
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "To send events to Home Assistant, you will need to setup the webhook feature in Plaato Airlock.\n\nFill in the following info:\n\n- URL: `{webhook_url}`\n- Method: POST\n\nSee [the documentation]({docs_url}) for further details."
|
||||
"default": "Your Plaato {device_type} with name **{device_name}** was successfully setup!"
|
||||
},
|
||||
"error": {
|
||||
"invalid_webhook_device": "You have selected a device that doesn't not support sending data to a webhook. It is only available for the Airlock",
|
||||
"no_api_method": "You need to add an auth token or select webhook",
|
||||
"no_auth_token": "You need to add an auth token"
|
||||
},
|
||||
"step": {
|
||||
"api_method": {
|
||||
"data": {
|
||||
"token": "Paste Auth Token here",
|
||||
"use_webhook": "Use webhook"
|
||||
},
|
||||
"description": "To be able to query the API an `auth_token` is required which can be obtained by following [these](https://plaato.zendesk.com/hc/en-us/articles/360003234717-Auth-token) instructions\n\n Selected device: **{device_type}** \n\nIf you rather use the built in webhook method (Airlock only) please check the box below and leave Auth Token blank",
|
||||
"title": "Select API method"
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"device_name": "Name your device",
|
||||
"device_type": "Type of Plaato device"
|
||||
},
|
||||
"description": "Do you want to start set up?",
|
||||
"title": "Set up the Plaato Webhook"
|
||||
"title": "Set up the Plaato devices"
|
||||
},
|
||||
"webhook": {
|
||||
"description": "To send events to Home Assistant, you will need to setup the webhook feature in Plaato Airlock.\n\nFill in the following info:\n\n- URL: `{webhook_url}`\n- Method: POST\n\nSee [the documentation]({docs_url}) for further details.",
|
||||
"title": "Webhook to use"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"update_interval": "Update interval (minutes)"
|
||||
},
|
||||
"description": "Set the update interval (minutes)",
|
||||
"title": "Options for Plaato"
|
||||
},
|
||||
"webhook": {
|
||||
"description": "Webhook info:\n\n- URL: `{webhook_url}`\n- Method: POST\n\n",
|
||||
"title": "Options for Plaato Airlock"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1615,6 +1615,9 @@ pypck==0.7.9
|
||||
# homeassistant.components.pjlink
|
||||
pypjlink2==1.2.1
|
||||
|
||||
# homeassistant.components.plaato
|
||||
pyplaato==0.0.15
|
||||
|
||||
# homeassistant.components.point
|
||||
pypoint==2.0.0
|
||||
|
||||
|
@ -842,6 +842,9 @@ pyowm==3.1.1
|
||||
# homeassistant.components.onewire
|
||||
pyownet==0.10.0.post1
|
||||
|
||||
# homeassistant.components.plaato
|
||||
pyplaato==0.0.15
|
||||
|
||||
# homeassistant.components.point
|
||||
pypoint==2.0.0
|
||||
|
||||
|
1
tests/components/plaato/__init__.py
Normal file
1
tests/components/plaato/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
"""Tests for the Plaato integration."""
|
286
tests/components/plaato/test_config_flow.py
Normal file
286
tests/components/plaato/test_config_flow.py
Normal file
@ -0,0 +1,286 @@
|
||||
"""Test the Plaato config flow."""
|
||||
from unittest.mock import patch
|
||||
|
||||
from pyplaato.models.device import PlaatoDeviceType
|
||||
import pytest
|
||||
|
||||
from homeassistant import config_entries, data_entry_flow, setup
|
||||
from homeassistant.components.plaato.const import (
|
||||
CONF_DEVICE_NAME,
|
||||
CONF_DEVICE_TYPE,
|
||||
CONF_USE_WEBHOOK,
|
||||
DOMAIN,
|
||||
)
|
||||
from homeassistant.const import CONF_SCAN_INTERVAL, CONF_TOKEN, CONF_WEBHOOK_ID
|
||||
from homeassistant.data_entry_flow import RESULT_TYPE_CREATE_ENTRY, RESULT_TYPE_FORM
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
BASE_URL = "http://example.com"
|
||||
WEBHOOK_ID = "webhook_id"
|
||||
UNIQUE_ID = "plaato_unique_id"
|
||||
|
||||
|
||||
@pytest.fixture(name="webhook_id")
|
||||
def mock_webhook_id():
|
||||
"""Mock webhook_id."""
|
||||
with patch(
|
||||
"homeassistant.components.webhook.async_generate_id", return_value=WEBHOOK_ID
|
||||
), patch(
|
||||
"homeassistant.components.webhook.async_generate_url", return_value="hook_id"
|
||||
):
|
||||
yield
|
||||
|
||||
|
||||
async def test_show_config_form(hass):
|
||||
"""Test show configuration form."""
|
||||
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
assert result["type"] == "form"
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
|
||||
async def test_show_config_form_device_type_airlock(hass):
|
||||
"""Test show configuration form."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_USER},
|
||||
data={
|
||||
CONF_DEVICE_TYPE: PlaatoDeviceType.Airlock,
|
||||
CONF_DEVICE_NAME: "device_name",
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "api_method"
|
||||
assert result["data_schema"].schema.get(CONF_TOKEN) == str
|
||||
assert result["data_schema"].schema.get(CONF_USE_WEBHOOK) == bool
|
||||
|
||||
|
||||
async def test_show_config_form_device_type_keg(hass):
|
||||
"""Test show configuration form."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_USER},
|
||||
data={CONF_DEVICE_TYPE: PlaatoDeviceType.Keg, CONF_DEVICE_NAME: "device_name"},
|
||||
)
|
||||
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "api_method"
|
||||
assert result["data_schema"].schema.get(CONF_TOKEN) == str
|
||||
assert result["data_schema"].schema.get(CONF_USE_WEBHOOK) is None
|
||||
|
||||
|
||||
async def test_show_config_form_validate_webhook(hass, webhook_id):
|
||||
"""Test show configuration form."""
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
CONF_DEVICE_TYPE: PlaatoDeviceType.Airlock,
|
||||
CONF_DEVICE_NAME: "device_name",
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "api_method"
|
||||
|
||||
async def return_async_value(val):
|
||||
return val
|
||||
|
||||
hass.config.components.add("cloud")
|
||||
with patch(
|
||||
"homeassistant.components.cloud.async_active_subscription", return_value=True
|
||||
), patch(
|
||||
"homeassistant.components.cloud.async_create_cloudhook",
|
||||
return_value=return_async_value("https://hooks.nabu.casa/ABCD"),
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
CONF_TOKEN: "",
|
||||
CONF_USE_WEBHOOK: True,
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "webhook"
|
||||
|
||||
|
||||
async def test_show_config_form_validate_token(hass):
|
||||
"""Test show configuration form."""
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
CONF_DEVICE_TYPE: PlaatoDeviceType.Keg,
|
||||
CONF_DEVICE_NAME: "device_name",
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "api_method"
|
||||
|
||||
with patch("homeassistant.components.plaato.async_setup_entry", return_value=True):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={CONF_TOKEN: "valid_token"}
|
||||
)
|
||||
|
||||
assert result["type"] == RESULT_TYPE_CREATE_ENTRY
|
||||
assert result["title"] == PlaatoDeviceType.Keg.name
|
||||
assert result["data"] == {
|
||||
CONF_USE_WEBHOOK: False,
|
||||
CONF_TOKEN: "valid_token",
|
||||
CONF_DEVICE_TYPE: PlaatoDeviceType.Keg,
|
||||
CONF_DEVICE_NAME: "device_name",
|
||||
}
|
||||
|
||||
|
||||
async def test_show_config_form_no_cloud_webhook(hass, webhook_id):
|
||||
"""Test show configuration form."""
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
CONF_DEVICE_TYPE: PlaatoDeviceType.Airlock,
|
||||
CONF_DEVICE_NAME: "device_name",
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "api_method"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
CONF_USE_WEBHOOK: True,
|
||||
CONF_TOKEN: "",
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "webhook"
|
||||
assert result["errors"] is None
|
||||
|
||||
|
||||
async def test_show_config_form_api_method_no_auth_token(hass, webhook_id):
|
||||
"""Test show configuration form."""
|
||||
|
||||
# Using Keg
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
CONF_DEVICE_TYPE: PlaatoDeviceType.Keg,
|
||||
CONF_DEVICE_NAME: "device_name",
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "api_method"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={CONF_TOKEN: ""}
|
||||
)
|
||||
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "api_method"
|
||||
assert len(result["errors"]) == 1
|
||||
assert result["errors"]["base"] == "no_auth_token"
|
||||
|
||||
# Using Airlock
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
CONF_DEVICE_TYPE: PlaatoDeviceType.Airlock,
|
||||
CONF_DEVICE_NAME: "device_name",
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "api_method"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={CONF_TOKEN: ""}
|
||||
)
|
||||
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "api_method"
|
||||
assert len(result["errors"]) == 1
|
||||
assert result["errors"]["base"] == "no_api_method"
|
||||
|
||||
|
||||
async def test_options(hass):
|
||||
"""Test updating options."""
|
||||
config_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
title="NAME",
|
||||
data={},
|
||||
options={CONF_SCAN_INTERVAL: 5},
|
||||
)
|
||||
|
||||
config_entry.add_to_hass(hass)
|
||||
with patch("homeassistant.components.plaato.async_setup_entry", return_value=True):
|
||||
result = await hass.config_entries.options.async_init(config_entry.entry_id)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={CONF_SCAN_INTERVAL: 10},
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
assert result["data"][CONF_SCAN_INTERVAL] == 10
|
||||
|
||||
|
||||
async def test_options_webhook(hass, webhook_id):
|
||||
"""Test updating options."""
|
||||
config_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
title="NAME",
|
||||
data={CONF_USE_WEBHOOK: True, CONF_WEBHOOK_ID: None},
|
||||
options={CONF_SCAN_INTERVAL: 5},
|
||||
)
|
||||
|
||||
config_entry.add_to_hass(hass)
|
||||
with patch("homeassistant.components.plaato.async_setup_entry", return_value=True):
|
||||
result = await hass.config_entries.options.async_init(config_entry.entry_id)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "webhook"
|
||||
assert result["description_placeholders"] == {"webhook_url": ""}
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={CONF_WEBHOOK_ID: WEBHOOK_ID},
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
assert result["data"][CONF_WEBHOOK_ID] == CONF_WEBHOOK_ID
|
Reference in New Issue
Block a user