Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
- Loading branch information
Showing
10 changed files
with
327 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Validating CODEOWNERS rules …
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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/' | ||
} | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
"""Const for GPSLogger.""" | ||
|
||
DOMAIN = 'plaato' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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": [] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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." | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -37,6 +37,7 @@ | |
"nest", | ||
"openuv", | ||
"owntracks", | ||
"plaato", | ||
"point", | ||
"ps4", | ||
"rainmachine", | ||
|