Skip to content
This repository has been archived by the owner on Apr 19, 2022. It is now read-only.

Commit

Permalink
Should fix entities not updating, once and for all (#13)
Browse files Browse the repository at this point in the history
  • Loading branch information
JohNan authored Feb 9, 2021
1 parent 4fdc4e2 commit da4101f
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 71 deletions.
22 changes: 10 additions & 12 deletions custom_components/plaato/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@
VOLUME_GALLONS,
VOLUME_LITERS,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady, InvalidStateError
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
Expand Down Expand Up @@ -94,10 +94,10 @@ async def async_setup(hass: HomeAssistant, config: dict):
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
"""Configure based on config entry."""

use_webhook = entry.data.get(CONF_USE_WEBHOOK, False)
use_webhook = entry.data[CONF_USE_WEBHOOK]

if use_webhook:
setup_webhook(hass, entry)
async_setup_webhook(hass, entry)
else:
await async_setup_coordinator(hass, entry)

Expand All @@ -110,15 +110,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
return True


def setup_webhook(hass: HomeAssistant, entry: ConfigEntry):
@callback
def async_setup_webhook(hass: HomeAssistant, entry: ConfigEntry):
"""Init webhook based on config entry."""
if entry.data[CONF_WEBHOOK_ID] is None:
raise InvalidStateError

webhook_id = entry.data[CONF_WEBHOOK_ID]
device_name = entry.data[CONF_DEVICE_NAME]

__set_entry_data(entry, hass)
_set_entry_data(entry, hass)

hass.components.webhook.async_register(
DOMAIN, f"{DOMAIN}.{device_name}", webhook_id, handle_webhook
Expand All @@ -133,21 +131,21 @@ async def async_setup_coordinator(hass: HomeAssistant, entry: ConfigEntry):
if entry.options.get(CONF_SCAN_INTERVAL):
update_interval = timedelta(minutes=entry.options[CONF_SCAN_INTERVAL])
else:
update_interval = DEFAULT_SCAN_INTERVAL
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)
_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):
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],
Expand Down
8 changes: 3 additions & 5 deletions custom_components/plaato/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up Plaato from a config entry."""

if config_entry.data.get(CONF_USE_WEBHOOK, False):
return False
if config_entry.data[CONF_USE_WEBHOOK]:
return

coordinator = hass.data[DOMAIN][config_entry.entry_id][COORDINATOR]
async_add_entities(
Expand All @@ -29,11 +29,9 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
sensor_type,
coordinator,
)
for sensor_type in coordinator.data.binary_sensors.keys()
for sensor_type in coordinator.data.binary_sensors
)

return True


class PlaatoBinarySensor(PlaatoEntity, BinarySensorEntity):
"""Representation of a Binary Sensor."""
Expand Down
38 changes: 18 additions & 20 deletions custom_components/plaato/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,12 @@
class PlaatoConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handles a Plaato config flow."""

VERSION = 2
VERSION = 1
CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL

def __init__(self):
"""Initialize."""
self._init_info = {}
self._errors = {}

async def async_step_user(self, user_input=None):
"""Handle user step."""
Expand All @@ -63,28 +62,26 @@ async def async_step_user(self, user_input=None):
): vol.In(list(PlaatoDeviceType)),
}
),
errors=self._errors,
)

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 None:
return await self._show_api_method_form(device_type)
if user_input is not None:
token = user_input.get(CONF_TOKEN, None)
use_webhook = user_input.get(CONF_USE_WEBHOOK, False)

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)

if not token and not use_webhook:
self._errors["base"] = PlaatoConfigFlow._get_error(device_type)
return await self._show_api_method_form(device_type)
self._init_info[CONF_USE_WEBHOOK] = use_webhook
self._init_info[CONF_TOKEN] = token
return await self.async_step_webhook()

self._init_info[CONF_USE_WEBHOOK] = use_webhook
self._init_info[CONF_TOKEN] = token
self._errors = {}
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."""
Expand All @@ -98,7 +95,6 @@ async def async_step_webhook(self, user_input=None):

return self.async_show_form(
step_id="webhook",
errors=self._errors,
description_placeholders={
PLACEHOLDER_WEBHOOK_URL: webhook_url,
PLACEHOLDER_DOCS_URL: DOCS_URL,
Expand Down Expand Up @@ -129,18 +125,20 @@ async def _async_create_entry(self):
},
)

async def _show_api_method_form(self, device_type: PlaatoDeviceType):
data_scheme = vol.Schema({vol.Optional(CONF_TOKEN, default=""): str})
async def _show_api_method_form(
self, device_type: PlaatoDeviceType, errors: dict = None
):
data_schema = vol.Schema({vol.Optional(CONF_TOKEN, default=""): str})

if device_type == PlaatoDeviceType.Airlock:
data_scheme = data_scheme.extend(
data_schema = data_schema.extend(
{vol.Optional(CONF_USE_WEBHOOK, default=False): bool}
)

return self.async_show_form(
step_id="api_method",
data_schema=data_scheme,
errors=self._errors,
data_schema=data_schema,
errors=errors,
description_placeholders={PLACEHOLDER_DEVICE_TYPE: device_type.name},
)

Expand Down
12 changes: 11 additions & 1 deletion custom_components/plaato/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
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"
Expand All @@ -22,5 +23,14 @@
DEVICE_TYPE = "device_type"
DEVICE_ID = "device_id"
UNDO_UPDATE_LISTENER = "undo_update_listener"
DEFAULT_SCAN_INTERVAL = timedelta(minutes=5)
DEFAULT_SCAN_INTERVAL = 5
MIN_UPDATE_INTERVAL = timedelta(minutes=1)

DEVICE_STATE_ATTRIBUTES = {
"beer_name": "beer_name",
"keg_date": "keg_date",
"mode": "mode",
"original_gravity": "original_gravity",
"final_gravity": "final_gravity",
"alcohol_by_volume": "alcohol_by_volume",
}
44 changes: 35 additions & 9 deletions custom_components/plaato/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,49 @@

from homeassistant.helpers import entity

from .const import DEVICE, DEVICE_ID, DEVICE_NAME, DEVICE_TYPE, DOMAIN, SENSOR_DATA
from .const import (
DEVICE,
DEVICE_ID,
DEVICE_NAME,
DEVICE_STATE_ATTRIBUTES,
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."""
sensor_data = data[SENSOR_DATA] if coordinator is None else coordinator.data
self._coordinator = coordinator
self._sensor_data: PlaatoDevice = sensor_data
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._name = self._sensor_data.get_sensor_name(sensor_type)
self._attributes = PlaatoEntity._to_snake_case(self._sensor_data.attributes)
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._name}".title()
return f"{DOMAIN} {self._device_type} {self._device_name} {self._sensor_name}".title()

@property
def unique_id(self):
Expand All @@ -50,8 +70,13 @@ def device_info(self):
@property
def device_state_attributes(self):
"""Return the state attributes of the monitored installation."""
if self._attributes is not None:
return self._attributes
if self._attributes:
return {
attr_key: self._attributes[plaato_key]
for attr_key, plaato_key in DEVICE_STATE_ATTRIBUTES.items()
if plaato_key in self._attributes
and self._attributes[plaato_key] is not None
}

@property
def available(self):
Expand All @@ -74,7 +99,8 @@ async def async_added_to_hass(self):
else:
self.async_on_remove(
self.hass.helpers.dispatcher.async_dispatcher_connect(
f"{DOMAIN}_{self.unique_id}", self.async_write_ha_state
SENSOR_SIGNAL % (self._device_id, self._sensor_type),
self.async_write_ha_state,
)
)

Expand Down
51 changes: 27 additions & 24 deletions custom_components/plaato/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,16 @@
)

from . import ATTR_TEMP, SENSOR_UPDATE
from .const import CONF_USE_WEBHOOK, COORDINATOR, DEVICE, DEVICE_ID, DOMAIN, SENSOR_DATA
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__)
Expand All @@ -25,37 +34,34 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=

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]

async def _update_sensor(device_id, sensor_data: PlaatoDevice):
@callback
async def _async_update_from_webhook(device_id, sensor_data: PlaatoDevice):
"""Update/Create the sensors."""
entry_data = hass.data[DOMAIN][entry.entry_id]
entry_data[SENSOR_DATA] = sensor_data

if entry.entry_id not in devices:
if device_id != entry_data[DEVICE][DEVICE_ID]:
entry_data[DEVICE][DEVICE_ID] = device_id

entities = [
PlaatoSensor(hass.data[DOMAIN][entry.entry_id], sensor_type)
for sensor_type in sensor_data.sensors.keys()
]
devices[entry.entry_id] = entities
async_add_entities(entities)
async_add_entities(
[
PlaatoSensor(entry_data, sensor_type)
for sensor_type in sensor_data.sensors
]
)
else:
for entity in devices[entry.entry_id]:
async_dispatcher_send(hass, f"{DOMAIN}_{entity.unique_id}")
for sensor_type in sensor_data.sensors:
async_dispatcher_send(hass, SENSOR_SIGNAL % (device_id, sensor_type))

if entry.data.get(CONF_USE_WEBHOOK, False):
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 = hass.data[DOMAIN][entry.entry_id][COORDINATOR]
coordinator = entry_data[COORDINATOR]
async_add_entities(
PlaatoSensor(hass.data[DOMAIN][entry.entry_id], sensor_type, coordinator)
for sensor_type in coordinator.data.sensors.keys()
PlaatoSensor(entry_data, sensor_type, coordinator)
for sensor_type in coordinator.data.sensors
)

return True


class PlaatoSensor(PlaatoEntity):
"""Representation of a Plaato Sensor."""
Expand All @@ -73,9 +79,6 @@ def device_class(self) -> Optional[str]:
@property
def state(self):
"""Return the state of the sensor."""
if self._coordinator is not None:
return self._coordinator.data.sensors.get(self._sensor_type)

return self._sensor_data.sensors.get(self._sensor_type)

@property
Expand Down

0 comments on commit da4101f

Please sign in to comment.