Bump geniushub client, handle dead devices, handle raise_for_status (#25687)

* Initial commit

* tweak error logging

* bump client

* correct regression

* small coding tweak

* debug logging to one entry

* refactor for self.data['attr']

* bump client

* small tidy-up
This commit is contained in:
David Bonnes 2019-08-04 23:06:36 +01:00 committed by Martin Hjelmare
parent 0d95ad3857
commit b0c79c271d
7 changed files with 50 additions and 79 deletions

View File

@ -2,6 +2,7 @@
from datetime import timedelta from datetime import timedelta
import logging import logging
import aiohttp
import voluptuous as vol import voluptuous as vol
from geniushubclient import GeniusHubClient from geniushubclient import GeniusHubClient
@ -41,32 +42,23 @@ async def async_setup(hass, hass_config):
args = (kwargs.pop(CONF_TOKEN),) args = (kwargs.pop(CONF_TOKEN),)
hass.data[DOMAIN] = {} hass.data[DOMAIN] = {}
data = hass.data[DOMAIN]["data"] = GeniusData(hass, args, kwargs) broker = GeniusBroker(hass, args, kwargs)
try: try:
await data._client.hub.update() # pylint: disable=protected-access await broker._client.hub.update() # pylint: disable=protected-access
except AssertionError: # assert response.status == HTTP_OK except aiohttp.ClientResponseError as err:
_LOGGER.warning("Setup failed, check your configuration.", exc_info=True) _LOGGER.error("Setup failed, check your configuration, %s", err)
return False return False
broker.make_debug_log_entries()
_LOGGER.debug( async_track_time_interval(hass, broker.async_update, SCAN_INTERVAL)
# noqa; pylint: disable=protected-access
"zones_raw = %s",
data._client.hub._zones_raw,
)
_LOGGER.debug(
# noqa; pylint: disable=protected-access
"devices_raw = %s",
data._client.hub._devices_raw,
)
async_track_time_interval(hass, data.async_update, SCAN_INTERVAL)
for platform in ["climate", "water_heater"]: for platform in ["climate", "water_heater"]:
hass.async_create_task( hass.async_create_task(
async_load_platform(hass, platform, DOMAIN, {}, hass_config) async_load_platform(hass, platform, DOMAIN, {}, hass_config)
) )
if data._client.api_version == 3: # pylint: disable=protected-access if broker._client.api_version == 3: # pylint: disable=protected-access
for platform in ["sensor", "binary_sensor"]: for platform in ["sensor", "binary_sensor"]:
hass.async_create_task( hass.async_create_task(
async_load_platform(hass, platform, DOMAIN, {}, hass_config) async_load_platform(hass, platform, DOMAIN, {}, hass_config)
@ -75,7 +67,7 @@ async def async_setup(hass, hass_config):
return True return True
class GeniusData: class GeniusBroker:
"""Container for geniushub client and data.""" """Container for geniushub client and data."""
def __init__(self, hass, args, kwargs): def __init__(self, hass, args, kwargs):
@ -89,19 +81,18 @@ class GeniusData:
"""Update the geniushub client's data.""" """Update the geniushub client's data."""
try: try:
await self._client.hub.update() await self._client.hub.update()
except AssertionError: # assert response.status == HTTP_OK except aiohttp.ClientResponseError as err:
_LOGGER.warning("Update failed.", exc_info=True) _LOGGER.warning("Update failed, %s", err)
return return
self.make_debug_log_entries()
_LOGGER.debug(
# noqa; pylint: disable=protected-access
"zones_raw = %s",
self._client.hub._zones_raw,
)
_LOGGER.debug(
# noqa; pylint: disable=protected-access
"devices_raw = %s",
self._client.hub._devices_raw,
)
async_dispatcher_send(self._hass, DOMAIN) async_dispatcher_send(self._hass, DOMAIN)
def make_debug_log_entries(self):
"""Make any useful debug log entries."""
# pylint: disable=protected-access
_LOGGER.debug(
"Raw JSON: \n\nhub._raw_zones = %s \n\nhub._raw_devices = %s",
self._client.hub._raw_zones,
self._client.hub._raw_devices,
)

View File

@ -1,6 +1,4 @@
"""Support for Genius Hub binary_sensor devices.""" """Support for Genius Hub binary_sensor devices."""
import logging
from homeassistant.components.binary_sensor import BinarySensorDevice from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.core import callback from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.dispatcher import async_dispatcher_connect
@ -8,8 +6,6 @@ from homeassistant.util.dt import utc_from_timestamp
from . import DOMAIN from . import DOMAIN
_LOGGER = logging.getLogger(__name__)
GH_IS_SWITCH = ["Dual Channel Receiver", "Electric Switch", "Smart Plug"] GH_IS_SWITCH = ["Dual Channel Receiver", "Electric Switch", "Smart Plug"]
@ -17,9 +13,10 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
"""Set up the Genius Hub sensor entities.""" """Set up the Genius Hub sensor entities."""
client = hass.data[DOMAIN]["client"] client = hass.data[DOMAIN]["client"]
devices = [d for d in client.hub.device_objs if d.type is not None]
switches = [ switches = [
GeniusBinarySensor(client, d) for d in devices if d.type[:21] in GH_IS_SWITCH GeniusBinarySensor(client, d)
for d in client.hub.device_objs
if d.type[:21] in GH_IS_SWITCH
] ]
async_add_entities(switches) async_add_entities(switches)
@ -59,16 +56,16 @@ class GeniusBinarySensor(BinarySensorDevice):
@property @property
def is_on(self): def is_on(self):
"""Return the status of the sensor.""" """Return the status of the sensor."""
return self._device.state["outputOnOff"] return self._device.data["state"]["outputOnOff"]
@property @property
def device_state_attributes(self): def device_state_attributes(self):
"""Return the device state attributes.""" """Return the device state attributes."""
attrs = {} attrs = {}
attrs["assigned_zone"] = self._device.assignedZones[0]["name"] attrs["assigned_zone"] = self._device.data["assignedZones"][0]["name"]
# noqa; pylint: disable=protected-access # noqa; pylint: disable=protected-access
last_comms = self._device._raw_json["childValues"]["lastComms"]["val"] last_comms = self._device._raw_data["childValues"]["lastComms"]["val"]
if last_comms != 0: if last_comms != 0:
attrs["last_comms"] = utc_from_timestamp(last_comms).isoformat() attrs["last_comms"] = utc_from_timestamp(last_comms).isoformat()

View File

@ -1,5 +1,4 @@
"""Support for Genius Hub climate devices.""" """Support for Genius Hub climate devices."""
import logging
from typing import Any, Awaitable, Dict, Optional, List from typing import Any, Awaitable, Dict, Optional, List
from homeassistant.components.climate import ClimateDevice from homeassistant.components.climate import ClimateDevice
@ -17,8 +16,6 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect
from . import DOMAIN from . import DOMAIN
_LOGGER = logging.getLogger(__name__)
ATTR_DURATION = "duration" ATTR_DURATION = "duration"
GH_ZONES = ["radiator"] GH_ZONES = ["radiator"]
@ -40,13 +37,10 @@ async def async_setup_platform(
"""Set up the Genius Hub climate entities.""" """Set up the Genius Hub climate entities."""
client = hass.data[DOMAIN]["client"] client = hass.data[DOMAIN]["client"]
async_add_entities( entities = [
[ GeniusClimateZone(client, z) for z in client.hub.zone_objs if z.type in GH_ZONES
GeniusClimateZone(client, z)
for z in client.hub.zone_objs
if z.type in GH_ZONES
] ]
) async_add_entities(entities)
class GeniusClimateZone(ClimateDevice): class GeniusClimateZone(ClimateDevice):
@ -78,7 +72,7 @@ class GeniusClimateZone(ClimateDevice):
@property @property
def device_state_attributes(self) -> Dict[str, Any]: def device_state_attributes(self) -> Dict[str, Any]:
"""Return the device state attributes.""" """Return the device state attributes."""
tmp = self._zone.__dict__.items() tmp = self._zone.data.items()
return {"status": {k: v for k, v in tmp if k in GH_STATE_ATTRS}} return {"status": {k: v for k, v in tmp if k in GH_STATE_ATTRS}}
@property @property
@ -94,12 +88,12 @@ class GeniusClimateZone(ClimateDevice):
@property @property
def current_temperature(self) -> Optional[float]: def current_temperature(self) -> Optional[float]:
"""Return the current temperature.""" """Return the current temperature."""
return self._zone.temperature return self._zone.data["temperature"]
@property @property
def target_temperature(self) -> Optional[float]: def target_temperature(self) -> Optional[float]:
"""Return the temperature we try to reach.""" """Return the temperature we try to reach."""
return self._zone.setpoint return self._zone.data["setpoint"]
@property @property
def min_temp(self) -> float: def min_temp(self) -> float:
@ -124,7 +118,7 @@ class GeniusClimateZone(ClimateDevice):
@property @property
def hvac_mode(self) -> str: def hvac_mode(self) -> str:
"""Return hvac operation ie. heat, cool mode.""" """Return hvac operation ie. heat, cool mode."""
return GH_HVAC_TO_HA.get(self._zone.mode, HVAC_MODE_HEAT) return GH_HVAC_TO_HA.get(self._zone.data["mode"], HVAC_MODE_HEAT)
@property @property
def hvac_modes(self) -> List[str]: def hvac_modes(self) -> List[str]:
@ -134,7 +128,7 @@ class GeniusClimateZone(ClimateDevice):
@property @property
def preset_mode(self) -> Optional[str]: def preset_mode(self) -> Optional[str]:
"""Return the current preset mode, e.g., home, away, temp.""" """Return the current preset mode, e.g., home, away, temp."""
return GH_PRESET_TO_HA.get(self._zone.mode) return GH_PRESET_TO_HA.get(self._zone.data["mode"])
@property @property
def preset_modes(self) -> Optional[List[str]]: def preset_modes(self) -> Optional[List[str]]:

View File

@ -3,7 +3,7 @@
"name": "Genius Hub", "name": "Genius Hub",
"documentation": "https://www.home-assistant.io/components/geniushub", "documentation": "https://www.home-assistant.io/components/geniushub",
"requirements": [ "requirements": [
"geniushub-client==0.5.4" "geniushub-client==0.5.8"
], ],
"dependencies": [], "dependencies": [],
"codeowners": ["@zxdavb"] "codeowners": ["@zxdavb"]

View File

@ -1,6 +1,5 @@
"""Support for Genius Hub sensor devices.""" """Support for Genius Hub sensor devices."""
from datetime import timedelta from datetime import timedelta
import logging
from homeassistant.const import DEVICE_CLASS_BATTERY from homeassistant.const import DEVICE_CLASS_BATTERY
from homeassistant.core import callback from homeassistant.core import callback
@ -10,8 +9,6 @@ from homeassistant.util.dt import utc_from_timestamp, utcnow
from . import DOMAIN from . import DOMAIN
_LOGGER = logging.getLogger(__name__)
GH_HAS_BATTERY = ["Room Thermostat", "Genius Valve", "Room Sensor", "Radiator Valve"] GH_HAS_BATTERY = ["Room Thermostat", "Genius Valve", "Room Sensor", "Radiator Valve"]
GH_LEVEL_MAPPING = { GH_LEVEL_MAPPING = {
@ -26,17 +23,16 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
client = hass.data[DOMAIN]["client"] client = hass.data[DOMAIN]["client"]
sensors = [ sensors = [
GeniusDevice(client, d) GeniusBattery(client, d)
for d in client.hub.device_objs for d in client.hub.device_objs
if d.type in GH_HAS_BATTERY if d.type in GH_HAS_BATTERY
] ]
issues = [GeniusIssue(client, i) for i in list(GH_LEVEL_MAPPING)] issues = [GeniusIssue(client, i) for i in list(GH_LEVEL_MAPPING)]
async_add_entities(sensors + issues, update_before_add=True) async_add_entities(sensors + issues, update_before_add=True)
class GeniusDevice(Entity): class GeniusBattery(Entity):
"""Representation of a Genius Hub sensor.""" """Representation of a Genius Hub sensor."""
def __init__(self, client, device): def __init__(self, client, device):
@ -63,7 +59,7 @@ class GeniusDevice(Entity):
def icon(self): def icon(self):
"""Return the icon of the sensor.""" """Return the icon of the sensor."""
# noqa; pylint: disable=protected-access # noqa; pylint: disable=protected-access
values = self._device._raw_json["childValues"] values = self._device._raw_data["childValues"]
last_comms = utc_from_timestamp(values["lastComms"]["val"]) last_comms = utc_from_timestamp(values["lastComms"]["val"])
if "WakeUp_Interval" in values: if "WakeUp_Interval" in values:
@ -74,7 +70,7 @@ class GeniusDevice(Entity):
if last_comms < utcnow() - interval * 3: if last_comms < utcnow() - interval * 3:
return "mdi:battery-unknown" return "mdi:battery-unknown"
battery_level = self._device.state["batteryLevel"] battery_level = self._device.data["state"]["batteryLevel"]
if battery_level == 255: if battery_level == 255:
return "mdi:battery-unknown" return "mdi:battery-unknown"
if battery_level < 40: if battery_level < 40:
@ -104,17 +100,17 @@ class GeniusDevice(Entity):
@property @property
def state(self): def state(self):
"""Return the state of the sensor.""" """Return the state of the sensor."""
level = self._device.state.get("batteryLevel", 255) level = self._device.data["state"].get("batteryLevel", 255)
return level if level != 255 else 0 return level if level != 255 else 0
@property @property
def device_state_attributes(self): def device_state_attributes(self):
"""Return the device state attributes.""" """Return the device state attributes."""
attrs = {} attrs = {}
attrs["assigned_zone"] = self._device.assignedZones[0]["name"] attrs["assigned_zone"] = self._device.data["assignedZones"][0]["name"]
# noqa; pylint: disable=protected-access # noqa; pylint: disable=protected-access
last_comms = self._device._raw_json["childValues"]["lastComms"]["val"] last_comms = self._device._raw_data["childValues"]["lastComms"]["val"]
attrs["last_comms"] = utc_from_timestamp(last_comms).isoformat() attrs["last_comms"] = utc_from_timestamp(last_comms).isoformat()
return {**attrs} return {**attrs}

View File

@ -1,6 +1,4 @@
"""Support for Genius Hub water_heater devices.""" """Support for Genius Hub water_heater devices."""
import logging
from homeassistant.components.water_heater import ( from homeassistant.components.water_heater import (
WaterHeaterDevice, WaterHeaterDevice,
SUPPORT_TARGET_TEMPERATURE, SUPPORT_TARGET_TEMPERATURE,
@ -15,8 +13,6 @@ from . import DOMAIN
STATE_AUTO = "auto" STATE_AUTO = "auto"
STATE_MANUAL = "manual" STATE_MANUAL = "manual"
_LOGGER = logging.getLogger(__name__)
GH_HEATERS = ["hot water temperature"] GH_HEATERS = ["hot water temperature"]
GH_SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE | SUPPORT_OPERATION_MODE GH_SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE | SUPPORT_OPERATION_MODE
@ -82,7 +78,7 @@ class GeniusWaterHeater(WaterHeaterDevice):
@property @property
def device_state_attributes(self): def device_state_attributes(self):
"""Return the device state attributes.""" """Return the device state attributes."""
tmp = self._boiler.__dict__.items() tmp = self._boiler.data.items()
return {"status": {k: v for k, v in tmp if k in GH_STATE_ATTRS}} return {"status": {k: v for k, v in tmp if k in GH_STATE_ATTRS}}
@property @property
@ -93,15 +89,12 @@ class GeniusWaterHeater(WaterHeaterDevice):
@property @property
def current_temperature(self): def current_temperature(self):
"""Return the current temperature.""" """Return the current temperature."""
try: return self._boiler.data.get("temperature")
return self._boiler.temperature
except AttributeError:
return None
@property @property
def target_temperature(self): def target_temperature(self):
"""Return the temperature we try to reach.""" """Return the temperature we try to reach."""
return self._boiler.setpoint return self._boiler.data["setpoint"]
@property @property
def min_temp(self): def min_temp(self):
@ -131,7 +124,7 @@ class GeniusWaterHeater(WaterHeaterDevice):
@property @property
def current_operation(self): def current_operation(self):
"""Return the current operation mode.""" """Return the current operation mode."""
return GH_STATE_TO_HA[self._boiler.mode] return GH_STATE_TO_HA[self._boiler.data["mode"]]
async def async_set_operation_mode(self, operation_mode): async def async_set_operation_mode(self, operation_mode):
"""Set a new operation mode for this boiler.""" """Set a new operation mode for this boiler."""

View File

@ -517,7 +517,7 @@ gearbest_parser==1.0.7
geizhals==0.0.9 geizhals==0.0.9
# homeassistant.components.geniushub # homeassistant.components.geniushub
geniushub-client==0.5.4 geniushub-client==0.5.8
# homeassistant.components.geo_json_events # homeassistant.components.geo_json_events
# homeassistant.components.nsw_rural_fire_service_feed # homeassistant.components.nsw_rural_fire_service_feed