Adds integration for Plaato Airlock (#23727)
* Adds integration for Plaato Airlock * Updates codeowners and coveragerc * Fixes lint errors * Fixers lint check error * Removed sv translation file * Adds en translation file * Sets config flow to true in manifest * Moves config flow and domain to seperate files * Fixes lint errors * Runs hassfest to regenerate config_flows.py * Adds should poll property and fixes for loop * Only log a warning when webhook data was broken * Fixes static test failure * Moves state update from async_update to state prop * Unsubscribes the dispatch signal listener * Update sensor.py
This commit is contained in:
parent
f3e4e8dce8
commit
266b3bc714
10 changed files with 327 additions and 0 deletions
|
@ -451,6 +451,7 @@ omit =
|
|||
homeassistant/components/ping/device_tracker.py
|
||||
homeassistant/components/pioneer/media_player.py
|
||||
homeassistant/components/pjlink/media_player.py
|
||||
homeassistant/components/plaato/*
|
||||
homeassistant/components/plex/media_player.py
|
||||
homeassistant/components/plex/sensor.py
|
||||
homeassistant/components/plum_lightpad/*
|
||||
|
|
|
@ -191,6 +191,7 @@ homeassistant/components/panel_iframe/* @home-assistant/frontend
|
|||
homeassistant/components/persistent_notification/* @home-assistant/core
|
||||
homeassistant/components/philips_js/* @elupus
|
||||
homeassistant/components/pi_hole/* @fabaff
|
||||
homeassistant/components/plaato/* @JohNan
|
||||
homeassistant/components/plant/* @ChristianKuehnel
|
||||
homeassistant/components/point/* @fredrike
|
||||
homeassistant/components/ps4/* @ktnrg45
|
||||
|
|
18
homeassistant/components/plaato/.translations/en.json
Normal file
18
homeassistant/components/plaato/.translations/en.json
Normal file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"not_internet_accessible": "Your Home Assistant instance needs to be accessible from the internet to receive messages from Plaato Airlock.",
|
||||
"one_instance_allowed": "Only a single instance is necessary."
|
||||
},
|
||||
"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."
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"description": "Are you sure you want to set up the Plaato Airlock?",
|
||||
"title": "Set up the Plaato Webhook"
|
||||
}
|
||||
},
|
||||
"title": "Plaato Airlock"
|
||||
}
|
||||
}
|
126
homeassistant/components/plaato/__init__.py
Normal file
126
homeassistant/components/plaato/__init__.py
Normal file
|
@ -0,0 +1,126 @@
|
|||
"""Support for Plaato Airlock."""
|
||||
import logging
|
||||
|
||||
from aiohttp import web
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import DOMAIN as SENSOR
|
||||
from homeassistant.const import (
|
||||
CONF_WEBHOOK_ID, HTTP_OK,
|
||||
TEMP_CELSIUS, TEMP_FAHRENHEIT, VOLUME_GALLONS, VOLUME_LITERS)
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||
from .const import DOMAIN
|
||||
|
||||
_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 = '{}_sensor_update'.format(DOMAIN)
|
||||
SENSOR_DATA_KEY = '{}.{}'.format(DOMAIN, SENSOR)
|
||||
|
||||
WEBHOOK_SCHEMA = vol.Schema({
|
||||
vol.Required(ATTR_DEVICE_NAME): cv.string,
|
||||
vol.Required(ATTR_DEVICE_ID): cv.positive_int,
|
||||
vol.Required(ATTR_TEMP_UNIT): vol.Any(TEMP_CELSIUS, TEMP_FAHRENHEIT),
|
||||
vol.Required(ATTR_VOLUME_UNIT): vol.Any(VOLUME_LITERS, VOLUME_GALLONS),
|
||||
vol.Required(ATTR_BPM): cv.positive_int,
|
||||
vol.Required(ATTR_TEMP): vol.Coerce(float),
|
||||
vol.Required(ATTR_SG): vol.Coerce(float),
|
||||
vol.Required(ATTR_OG): vol.Coerce(float),
|
||||
vol.Required(ATTR_ABV): vol.Coerce(float),
|
||||
vol.Required(ATTR_CO2_VOLUME): vol.Coerce(float),
|
||||
vol.Required(ATTR_BATCH_VOLUME): vol.Coerce(float),
|
||||
vol.Required(ATTR_BUBBLES): cv.positive_int,
|
||||
}, extra=vol.ALLOW_EXTRA)
|
||||
|
||||
|
||||
async def async_setup(hass, hass_config):
|
||||
"""Set up the Plaato component."""
|
||||
return True
|
||||
|
||||
|
||||
async def async_setup_entry(hass, entry):
|
||||
"""Configure based on config entry."""
|
||||
if DOMAIN not in hass.data:
|
||||
hass.data[DOMAIN] = {}
|
||||
|
||||
webhook_id = entry.data[CONF_WEBHOOK_ID]
|
||||
hass.components.webhook.async_register(
|
||||
DOMAIN, 'Plaato', webhook_id, handle_webhook)
|
||||
|
||||
hass.async_create_task(
|
||||
hass.config_entries.async_forward_entry_setup(entry, SENSOR)
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass, entry):
|
||||
"""Unload a config entry."""
|
||||
hass.components.webhook.async_unregister(entry.data[CONF_WEBHOOK_ID])
|
||||
hass.data[SENSOR_DATA_KEY]()
|
||||
|
||||
await hass.config_entries.async_forward_entry_unload(entry, SENSOR)
|
||||
return True
|
||||
|
||||
|
||||
async def handle_webhook(hass, webhook_id, request):
|
||||
"""Handle incoming webhook from Plaato."""
|
||||
try:
|
||||
data = WEBHOOK_SCHEMA(await request.json())
|
||||
except vol.MultipleInvalid as error:
|
||||
_LOGGER.warning("An error occurred when parsing webhook data <%s>",
|
||||
error)
|
||||
return
|
||||
|
||||
device_id = _device_id(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)
|
||||
|
||||
return web.Response(
|
||||
text="Saving status for {}".format(device_id), status=HTTP_OK)
|
||||
|
||||
|
||||
def _device_id(data):
|
||||
"""Return name of device sensor."""
|
||||
return "{}_{}".format(data.get(ATTR_DEVICE_NAME), data.get(ATTR_DEVICE_ID))
|
11
homeassistant/components/plaato/config_flow.py
Normal file
11
homeassistant/components/plaato/config_flow.py
Normal file
|
@ -0,0 +1,11 @@
|
|||
"""Config flow for GPSLogger."""
|
||||
from homeassistant.helpers import config_entry_flow
|
||||
from .const import DOMAIN
|
||||
|
||||
config_entry_flow.register_webhook_flow(
|
||||
DOMAIN,
|
||||
'Webhook',
|
||||
{
|
||||
'docs_url': 'https://www.home-assistant.io/components/plaato/'
|
||||
}
|
||||
)
|
3
homeassistant/components/plaato/const.py
Normal file
3
homeassistant/components/plaato/const.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
"""Const for GPSLogger."""
|
||||
|
||||
DOMAIN = 'plaato'
|
9
homeassistant/components/plaato/manifest.json
Normal file
9
homeassistant/components/plaato/manifest.json
Normal file
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"domain": "plaato",
|
||||
"name": "Plaato Airlock",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/components/plaato",
|
||||
"dependencies": ["webhook"],
|
||||
"codeowners": ["@JohNan"],
|
||||
"requirements": []
|
||||
}
|
139
homeassistant/components/plaato/sensor.py
Normal file
139
homeassistant/components/plaato/sensor.py
Normal file
|
@ -0,0 +1,139 @@
|
|||
"""Support for Plaato Airlock sensors."""
|
||||
|
||||
import logging
|
||||
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
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)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_setup_platform(hass, config, async_add_entities,
|
||||
discovery_info=None):
|
||||
"""Set up the Plaato sensor."""
|
||||
|
||||
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
"""Set up Plaato from a config entry."""
|
||||
devices = {}
|
||||
|
||||
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):
|
||||
"""Update/Create the sensors."""
|
||||
if device_id not in devices and get_device(device_id):
|
||||
entities = []
|
||||
sensors = get_device_sensors(device_id)
|
||||
|
||||
for sensor_type in sensors:
|
||||
entities.append(PlaatoSensor(device_id, sensor_type))
|
||||
|
||||
devices[device_id] = entities
|
||||
|
||||
async_add_entities(entities, True)
|
||||
else:
|
||||
for entity in devices[device_id]:
|
||||
entity.async_schedule_update_ha_state()
|
||||
|
||||
hass.data[SENSOR_DATA_KEY] = async_dispatcher_connect(
|
||||
hass, SENSOR_UPDATE, _update_sensor
|
||||
)
|
||||
|
||||
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 = "{} {}".format(device_id, sensor_type)
|
||||
self._attributes = None
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the sensor."""
|
||||
return "{} {}".format(PLAATO_DOMAIN, self._name)
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
"""Return the unique ID of this sensor."""
|
||||
return "{}_{}".format(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, '')
|
||||
|
||||
@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
|
||||
|
||||
@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 '%'
|
||||
|
||||
return ''
|
||||
|
||||
@property
|
||||
def should_poll(self):
|
||||
"""Return the polling state."""
|
||||
return False
|
18
homeassistant/components/plaato/strings.json
Normal file
18
homeassistant/components/plaato/strings.json
Normal file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"config": {
|
||||
"title": "Plaato Airlock",
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Set up the Plaato Webhook",
|
||||
"description": "Are you sure you want to set up the Plaato Airlock?"
|
||||
}
|
||||
},
|
||||
"abort": {
|
||||
"one_instance_allowed": "Only a single instance is necessary.",
|
||||
"not_internet_accessible": "Your Home Assistant instance needs to be accessible from the internet to receive messages from Plaato Airlock."
|
||||
},
|
||||
"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."
|
||||
}
|
||||
}
|
||||
}
|
|
@ -37,6 +37,7 @@ FLOWS = [
|
|||
"nest",
|
||||
"openuv",
|
||||
"owntracks",
|
||||
"plaato",
|
||||
"point",
|
||||
"ps4",
|
||||
"rainmachine",
|
||||
|
|
Loading…
Add table
Reference in a new issue