Tweak geniushub and bump client to v0.6.26 (#26640)

* use state attribute rather than type

* HA style tweaks

* small tweak

* bump client

* add more device_state_attributes

* bump client

* small tweak

* bump client for concurrent IO

* force snake_case, and refactor (consolidate) Devices/Zones

* force snake_case, and refactor (consolidate) Devices/Zones 2

* force snake_case, and refactor (consolidate) Devices/Zones 3

* refactor last_comms / wakeup_interval check

* movement sensor is dynamic, and tweaking

* tweak

* bump client to v0.6.20

* dummy

* dummy 2

* bump client to handle another edge case

* use entity_id fro zones

* small tweak

* bump client to 0.6.22

* add recursive snake_case converter

* fix regression

* fix regression 2

* fix regression 3

* remove Awaitables

* don't dynamically create function every scan_interval

* log kast_comms as localtime, delint dt_util

* add sensors fro v1 API

* tweak entity_id

* bump client

* bump client to v0.6.24

* bump client to v0.6.25

* explicit device attrs, dt as UTC

* add unique_id, remove entity_id

* Bump client to 0.6.26 - add Hub UID

* remove convert_dict()

* add mac_address (uid) for v1 API

* tweak var names

* add UID.upper() to avoid unwanted unique_id changes

* Update homeassistant/components/geniushub/__init__.py

Co-Authored-By: Martin Hjelmare <marhje52@kth.se>

* Update homeassistant/components/geniushub/__init__.py

Co-Authored-By: Martin Hjelmare <marhje52@kth.se>

* remove underscores

* refactor for broker

* ready now

* validate UID (MAC address)

* move uid to broker

* use existing constant

* pass client to broker
This commit is contained in:
David Bonnes 2019-10-02 17:27:13 +01:00 committed by GitHub
parent c7da781efc
commit c78b3a4439
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 283 additions and 255 deletions

View file

@ -1,14 +1,22 @@
"""Support for a Genius Hub system."""
from datetime import timedelta
import logging
from typing import Awaitable
from typing import Any, Dict, Optional
import aiohttp
import voluptuous as vol
from geniushubclient import GeniusHub
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_TOKEN, CONF_USERNAME
from homeassistant.const import (
ATTR_TEMPERATURE,
CONF_HOST,
CONF_MAC,
CONF_PASSWORD,
CONF_TOKEN,
CONF_USERNAME,
TEMP_CELSIUS,
)
from homeassistant.core import callback
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.aiohttp_client import async_get_clientsession
@ -19,39 +27,66 @@ from homeassistant.helpers.dispatcher import (
)
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.event import async_track_time_interval
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
import homeassistant.util.dt as dt_util
ATTR_DURATION = "duration"
_LOGGER = logging.getLogger(__name__)
DOMAIN = "geniushub"
# temperature is repeated here, as it gives access to high-precision temps
GH_ZONE_ATTRS = ["mode", "temperature", "type", "occupied", "override"]
GH_DEVICE_ATTRS = {
"luminance": "luminance",
"measuredTemperature": "measured_temperature",
"occupancyTrigger": "occupancy_trigger",
"setback": "setback",
"setTemperature": "set_temperature",
"wakeupInterval": "wakeup_interval",
}
SCAN_INTERVAL = timedelta(seconds=60)
_V1_API_SCHEMA = vol.Schema({vol.Required(CONF_TOKEN): cv.string})
_V3_API_SCHEMA = vol.Schema(
MAC_ADDRESS_REGEXP = r"^([0-9A-F]{2}:){5}([0-9A-F]{2})$"
V1_API_SCHEMA = vol.Schema(
{
vol.Required(CONF_TOKEN): cv.string,
vol.Required(CONF_MAC): vol.Match(MAC_ADDRESS_REGEXP),
}
)
V3_API_SCHEMA = vol.Schema(
{
vol.Required(CONF_HOST): cv.string,
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Optional(CONF_MAC): vol.Match(MAC_ADDRESS_REGEXP),
}
)
CONFIG_SCHEMA = vol.Schema(
{DOMAIN: vol.Any(_V3_API_SCHEMA, _V1_API_SCHEMA)}, extra=vol.ALLOW_EXTRA
{DOMAIN: vol.Any(V3_API_SCHEMA, V1_API_SCHEMA)}, extra=vol.ALLOW_EXTRA
)
async def async_setup(hass, hass_config):
async def async_setup(hass: HomeAssistantType, config: ConfigType) -> bool:
"""Create a Genius Hub system."""
kwargs = dict(hass_config[DOMAIN])
hass.data[DOMAIN] = {}
kwargs = dict(config[DOMAIN])
if CONF_HOST in kwargs:
args = (kwargs.pop(CONF_HOST),)
else:
args = (kwargs.pop(CONF_TOKEN),)
hub_uid = kwargs.pop(CONF_MAC, None)
hass.data[DOMAIN] = {}
broker = GeniusBroker(hass, args, kwargs)
client = GeniusHub(*args, **kwargs, session=async_get_clientsession(hass))
broker = hass.data[DOMAIN]["broker"] = GeniusBroker(hass, client, hub_uid)
try:
await broker.client.update()
await client.update()
except aiohttp.ClientResponseError as err:
_LOGGER.error("Setup failed, check your configuration, %s", err)
return False
@ -59,16 +94,8 @@ async def async_setup(hass, hass_config):
async_track_time_interval(hass, broker.async_update, SCAN_INTERVAL)
for platform in ["climate", "water_heater"]:
hass.async_create_task(
async_load_platform(hass, platform, DOMAIN, {}, hass_config)
)
if broker.client.api_version == 3: # pylint: disable=no-member
for platform in ["sensor", "binary_sensor"]:
hass.async_create_task(
async_load_platform(hass, platform, DOMAIN, {}, hass_config)
)
for platform in ["climate", "water_heater", "sensor", "binary_sensor"]:
hass.async_create_task(async_load_platform(hass, platform, DOMAIN, {}, config))
return True
@ -76,25 +103,30 @@ async def async_setup(hass, hass_config):
class GeniusBroker:
"""Container for geniushub client and data."""
def __init__(self, hass, args, kwargs):
def __init__(self, hass, client, hub_uid) -> None:
"""Initialize the geniushub client."""
self.hass = hass
self.client = hass.data[DOMAIN]["client"] = GeniusHub(
*args, **kwargs, session=async_get_clientsession(hass)
)
self.client = client
self._hub_uid = hub_uid
async def async_update(self, now, **kwargs):
@property
def hub_uid(self) -> int:
"""Return the Hub UID (MAC address)."""
# pylint: disable=no-member
return self._hub_uid if self._hub_uid is not None else self.client.uid
async def async_update(self, now, **kwargs) -> None:
"""Update the geniushub client's data."""
try:
await self.client.update()
except aiohttp.ClientResponseError as err:
_LOGGER.warning("Update failed, %s", err)
_LOGGER.warning("Update failed, message is: %s", err)
return
self.make_debug_log_entries()
async_dispatcher_send(self.hass, DOMAIN)
def make_debug_log_entries(self):
def make_debug_log_entries(self) -> None:
"""Make any useful debug log entries."""
# pylint: disable=protected-access
_LOGGER.debug(
@ -105,13 +137,13 @@ class GeniusBroker:
class GeniusEntity(Entity):
"""Base for all Genius Hub endtities."""
"""Base for all Genius Hub entities."""
def __init__(self):
def __init__(self) -> None:
"""Initialize the entity."""
self._name = None
self._unique_id = self._name = None
async def async_added_to_hass(self) -> Awaitable[None]:
async def async_added_to_hass(self) -> None:
"""Set up a listener when this entity is added to HA."""
async_dispatcher_connect(self.hass, DOMAIN, self._refresh)
@ -119,6 +151,11 @@ class GeniusEntity(Entity):
def _refresh(self) -> None:
self.async_schedule_update_ha_state(force_refresh=True)
@property
def unique_id(self) -> Optional[str]:
"""Return a unique ID."""
return self._unique_id
@property
def name(self) -> str:
"""Return the name of the geniushub entity."""
@ -128,3 +165,102 @@ class GeniusEntity(Entity):
def should_poll(self) -> bool:
"""Return False as geniushub entities should not be polled."""
return False
class GeniusDevice(GeniusEntity):
"""Base for all Genius Hub devices."""
def __init__(self, broker, device) -> None:
"""Initialize the Device."""
super().__init__()
self._device = device
self._unique_id = f"{broker.hub_uid}_device_{device.id}"
self._last_comms = self._state_attr = None
@property
def device_state_attributes(self) -> Dict[str, Any]:
"""Return the device state attributes."""
attrs = {}
attrs["assigned_zone"] = self._device.data["assignedZones"][0]["name"]
if self._last_comms:
attrs["last_comms"] = self._last_comms.isoformat()
state = dict(self._device.data["state"])
if "_state" in self._device.data: # only for v3 API
state.update(self._device.data["_state"])
attrs["state"] = {
GH_DEVICE_ATTRS[k]: v for k, v in state.items() if k in GH_DEVICE_ATTRS
}
return attrs
async def async_update(self) -> None:
"""Update an entity's state data."""
if "_state" in self._device.data: # only for v3 API
self._last_comms = dt_util.utc_from_timestamp(
self._device.data["_state"]["lastComms"]
)
class GeniusZone(GeniusEntity):
"""Base for all Genius Hub zones."""
def __init__(self, broker, zone) -> None:
"""Initialize the Zone."""
super().__init__()
self._zone = zone
self._unique_id = f"{broker.hub_uid}_device_{zone.id}"
self._max_temp = self._min_temp = self._supported_features = None
@property
def name(self) -> str:
"""Return the name of the climate device."""
return self._zone.name
@property
def device_state_attributes(self) -> Dict[str, Any]:
"""Return the device state attributes."""
status = {k: v for k, v in self._zone.data.items() if k in GH_ZONE_ATTRS}
return {"status": status}
@property
def current_temperature(self) -> Optional[float]:
"""Return the current temperature."""
return self._zone.data.get("temperature")
@property
def target_temperature(self) -> float:
"""Return the temperature we try to reach."""
return self._zone.data["setpoint"]
@property
def min_temp(self) -> float:
"""Return max valid temperature that can be set."""
return self._min_temp
@property
def max_temp(self) -> float:
"""Return max valid temperature that can be set."""
return self._max_temp
@property
def temperature_unit(self) -> str:
"""Return the unit of measurement."""
return TEMP_CELSIUS
@property
def supported_features(self) -> int:
"""Return the bitmask of supported features."""
return self._supported_features
async def async_set_temperature(self, **kwargs) -> None:
"""Set a new target temperature for this zone."""
await self._zone.set_override(
kwargs[ATTR_TEMPERATURE], kwargs.get(ATTR_DURATION, 3600)
)

View file

@ -1,52 +1,45 @@
"""Support for Genius Hub binary_sensor devices."""
from typing import Any, Dict
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.util.dt import utc_from_timestamp
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
from . import DOMAIN, GeniusEntity
from . import DOMAIN, GeniusDevice
GH_IS_SWITCH = ["Dual Channel Receiver", "Electric Switch", "Smart Plug"]
GH_STATE_ATTR = "outputOnOff"
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
async def async_setup_platform(
hass: HomeAssistantType, config: ConfigType, async_add_entities, discovery_info=None
) -> None:
"""Set up the Genius Hub sensor entities."""
client = hass.data[DOMAIN]["client"]
if discovery_info is None:
return
broker = hass.data[DOMAIN]["broker"]
switches = [
GeniusBinarySensor(d) for d in client.device_objs if d.type[:21] in GH_IS_SWITCH
GeniusBinarySensor(broker, d, GH_STATE_ATTR)
for d in broker.client.device_objs
if GH_STATE_ATTR in d.data["state"]
]
async_add_entities(switches)
async_add_entities(switches, update_before_add=True)
class GeniusBinarySensor(GeniusEntity, BinarySensorDevice):
class GeniusBinarySensor(GeniusDevice, BinarySensorDevice):
"""Representation of a Genius Hub binary_sensor."""
def __init__(self, device) -> None:
def __init__(self, broker, device, state_attr) -> None:
"""Initialize the binary sensor."""
super().__init__()
super().__init__(broker, device)
self._state_attr = state_attr
self._device = device
if device.type[:21] == "Dual Channel Receiver":
self._name = f"Dual Channel Receiver {device.id}"
self._name = f"{device.type[:21]} {device.id}"
else:
self._name = f"{device.type} {device.id}"
@property
def is_on(self) -> bool:
"""Return the status of the sensor."""
return self._device.data["state"]["outputOnOff"]
@property
def device_state_attributes(self) -> Dict[str, Any]:
"""Return the device state attributes."""
attrs = {}
attrs["assigned_zone"] = self._device.data["assignedZones"][0]["name"]
# pylint: disable=protected-access
last_comms = self._device._raw["childValues"]["lastComms"]["val"]
if last_comms != 0:
attrs["last_comms"] = utc_from_timestamp(last_comms).isoformat()
return {**attrs}
return self._device.data["state"][self._state_attr]

View file

@ -1,5 +1,5 @@
"""Support for Genius Hub climate devices."""
from typing import Any, Awaitable, Dict, Optional, List
from typing import Optional, List
from homeassistant.components.climate import ClimateDevice
from homeassistant.components.climate.const import (
@ -10,16 +10,9 @@ from homeassistant.components.climate.const import (
SUPPORT_TARGET_TEMPERATURE,
SUPPORT_PRESET_MODE,
)
from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
from . import DOMAIN, GeniusEntity
ATTR_DURATION = "duration"
GH_ZONES = ["radiator"]
# temperature is repeated here, as it gives access to high-precision temps
GH_STATE_ATTRS = ["mode", "temperature", "type", "occupied", "override"]
from . import DOMAIN, GeniusZone
# GeniusHub Zones support: Off, Timer, Override/Boost, Footprint & Linked modes
HA_HVAC_TO_GH = {HVAC_MODE_OFF: "off", HVAC_MODE_HEAT: "timer"}
@ -28,78 +21,43 @@ GH_HVAC_TO_HA = {v: k for k, v in HA_HVAC_TO_GH.items()}
HA_PRESET_TO_GH = {PRESET_ACTIVITY: "footprint", PRESET_BOOST: "override"}
GH_PRESET_TO_HA = {v: k for k, v in HA_PRESET_TO_GH.items()}
GH_ZONES = ["radiator", "wet underfloor"]
async def async_setup_platform(
hass, hass_config, async_add_entities, discovery_info=None
):
hass: HomeAssistantType, config: ConfigType, async_add_entities, discovery_info=None
) -> None:
"""Set up the Genius Hub climate entities."""
client = hass.data[DOMAIN]["client"]
if discovery_info is None:
return
entities = [
GeniusClimateZone(z) for z in client.zone_objs if z.data["type"] in GH_ZONES
broker = hass.data[DOMAIN]["broker"]
async_add_entities(
[
GeniusClimateZone(broker, z)
for z in broker.client.zone_objs
if z.data["type"] in GH_ZONES
]
async_add_entities(entities)
)
class GeniusClimateZone(GeniusEntity, ClimateDevice):
class GeniusClimateZone(GeniusZone, ClimateDevice):
"""Representation of a Genius Hub climate device."""
def __init__(self, zone) -> None:
def __init__(self, broker, zone) -> None:
"""Initialize the climate device."""
super().__init__()
super().__init__(broker, zone)
self._zone = zone
if hasattr(self._zone, "occupied"): # has a movement sensor
self._preset_modes = list(HA_PRESET_TO_GH)
else:
self._preset_modes = [PRESET_BOOST]
@property
def name(self) -> str:
"""Return the name of the climate device."""
return self._zone.name
@property
def device_state_attributes(self) -> Dict[str, Any]:
"""Return the device state attributes."""
tmp = self._zone.data.items()
return {"status": {k: v for k, v in tmp if k in GH_STATE_ATTRS}}
self._max_temp = 28.0
self._min_temp = 4.0
self._supported_features = SUPPORT_TARGET_TEMPERATURE | SUPPORT_PRESET_MODE
@property
def icon(self) -> str:
"""Return the icon to use in the frontend UI."""
return "mdi:radiator"
@property
def current_temperature(self) -> Optional[float]:
"""Return the current temperature."""
return self._zone.data["temperature"]
@property
def target_temperature(self) -> float:
"""Return the temperature we try to reach."""
return self._zone.data["setpoint"]
@property
def min_temp(self) -> float:
"""Return max valid temperature that can be set."""
return 4.0
@property
def max_temp(self) -> float:
"""Return max valid temperature that can be set."""
return 28.0
@property
def temperature_unit(self) -> str:
"""Return the unit of measurement."""
return TEMP_CELSIUS
@property
def supported_features(self) -> int:
"""Return the list of supported features."""
return SUPPORT_TARGET_TEMPERATURE | SUPPORT_PRESET_MODE
@property
def hvac_mode(self) -> str:
"""Return hvac operation ie. heat, cool mode."""
@ -118,18 +76,14 @@ class GeniusClimateZone(GeniusEntity, ClimateDevice):
@property
def preset_modes(self) -> Optional[List[str]]:
"""Return a list of available preset modes."""
return self._preset_modes
if "occupied" in self._zone.data: # if has a movement sensor
return [PRESET_ACTIVITY, PRESET_BOOST]
return [PRESET_BOOST]
async def async_set_temperature(self, **kwargs) -> Awaitable[None]:
"""Set a new target temperature for this zone."""
await self._zone.set_override(
kwargs[ATTR_TEMPERATURE], kwargs.get(ATTR_DURATION, 3600)
)
async def async_set_hvac_mode(self, hvac_mode: str) -> Awaitable[None]:
async def async_set_hvac_mode(self, hvac_mode: str) -> None:
"""Set a new hvac mode."""
await self._zone.set_mode(HA_HVAC_TO_GH.get(hvac_mode))
async def async_set_preset_mode(self, preset_mode: str) -> Awaitable[None]:
async def async_set_preset_mode(self, preset_mode: str) -> None:
"""Set a new preset mode."""
await self._zone.set_mode(HA_PRESET_TO_GH.get(preset_mode, "timer"))

View file

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

View file

@ -1,13 +1,14 @@
"""Support for Genius Hub sensor devices."""
from datetime import timedelta
from typing import Any, Awaitable, Dict
from typing import Any, Dict
from homeassistant.const import DEVICE_CLASS_BATTERY
from homeassistant.util.dt import utc_from_timestamp, utcnow
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
import homeassistant.util.dt as dt_util
from . import DOMAIN, GeniusEntity
from . import DOMAIN, GeniusDevice, GeniusEntity
GH_HAS_BATTERY = ["Room Thermostat", "Genius Valve", "Room Sensor", "Radiator Valve"]
GH_STATE_ATTR = "batteryLevel"
GH_LEVEL_MAPPING = {
"error": "Errors",
@ -16,42 +17,47 @@ GH_LEVEL_MAPPING = {
}
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
async def async_setup_platform(
hass: HomeAssistantType, config: ConfigType, async_add_entities, discovery_info=None
) -> None:
"""Set up the Genius Hub sensor entities."""
client = hass.data[DOMAIN]["client"]
if discovery_info is None:
return
sensors = [GeniusBattery(d) for d in client.device_objs if d.type in GH_HAS_BATTERY]
issues = [GeniusIssue(client, i) for i in list(GH_LEVEL_MAPPING)]
broker = hass.data[DOMAIN]["broker"]
sensors = [
GeniusBattery(broker, d, GH_STATE_ATTR)
for d in broker.client.device_objs
if GH_STATE_ATTR in d.data["state"]
]
issues = [GeniusIssue(broker, i) for i in list(GH_LEVEL_MAPPING)]
async_add_entities(sensors + issues, update_before_add=True)
class GeniusBattery(GeniusEntity):
class GeniusBattery(GeniusDevice):
"""Representation of a Genius Hub sensor."""
def __init__(self, device) -> None:
def __init__(self, broker, device, state_attr) -> None:
"""Initialize the sensor."""
super().__init__()
super().__init__(broker, device)
self._state_attr = state_attr
self._device = device
self._name = f"{device.type} {device.id}"
@property
def icon(self) -> str:
"""Return the icon of the sensor."""
values = self._device._raw["childValues"] # pylint: disable=protected-access
last_comms = utc_from_timestamp(values["lastComms"]["val"])
if "WakeUp_Interval" in values:
interval = timedelta(seconds=values["WakeUp_Interval"]["val"])
else:
interval = timedelta(minutes=20)
if last_comms < utcnow() - interval * 3:
if "_state" in self._device.data: # only for v3 API
interval = timedelta(
seconds=self._device.data["_state"].get("wakeupInterval", 30 * 60)
)
if self._last_comms < dt_util.utcnow() - interval * 3:
return "mdi:battery-unknown"
battery_level = self._device.data["state"]["batteryLevel"]
battery_level = self._device.data["state"][self._state_attr]
if battery_level == 255:
return "mdi:battery-unknown"
if battery_level < 40:
@ -76,31 +82,19 @@ class GeniusBattery(GeniusEntity):
@property
def state(self) -> str:
"""Return the state of the sensor."""
level = self._device.data["state"].get("batteryLevel", 255)
level = self._device.data["state"][self._state_attr]
return level if level != 255 else 0
@property
def device_state_attributes(self) -> Dict[str, Any]:
"""Return the device state attributes."""
attrs = {}
attrs["assigned_zone"] = self._device.data["assignedZones"][0]["name"]
# pylint: disable=protected-access
last_comms = self._device._raw["childValues"]["lastComms"]["val"]
attrs["last_comms"] = utc_from_timestamp(last_comms).isoformat()
return {**attrs}
class GeniusIssue(GeniusEntity):
"""Representation of a Genius Hub sensor."""
def __init__(self, hub, level) -> None:
def __init__(self, broker, level) -> None:
"""Initialize the sensor."""
super().__init__()
self._hub = hub
self._name = GH_LEVEL_MAPPING[level]
self._hub = broker.client
self._name = f"GeniusHub {GH_LEVEL_MAPPING[level]}"
self._level = level
self._issues = []
@ -114,7 +108,7 @@ class GeniusIssue(GeniusEntity):
"""Return the device state attributes."""
return {f"{self._level}_list": self._issues}
async def async_update(self) -> Awaitable[None]:
async def async_update(self) -> None:
"""Process the sensor's state data."""
self._issues = [
i["description"] for i in self._hub.issues if i["level"] == self._level

View file

@ -1,27 +1,20 @@
"""Support for Genius Hub water_heater devices."""
from typing import Any, Awaitable, Dict, Optional, List
from typing import List
from homeassistant.components.water_heater import (
WaterHeaterDevice,
SUPPORT_TARGET_TEMPERATURE,
SUPPORT_OPERATION_MODE,
)
from homeassistant.const import ATTR_TEMPERATURE, STATE_OFF, TEMP_CELSIUS
from homeassistant.const import STATE_OFF
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
from . import DOMAIN, GeniusEntity
from . import DOMAIN, GeniusZone
STATE_AUTO = "auto"
STATE_MANUAL = "manual"
GH_HEATERS = ["hot water temperature"]
GH_SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE | SUPPORT_OPERATION_MODE
# HA does not have SUPPORT_ON_OFF for water_heater
GH_MAX_TEMP = 80.0
GH_MIN_TEMP = 30.0
# Genius Hub HW supports only Off, Override/Boost & Timer modes
# Genius Hub HW zones support only Off, Override/Boost & Timer modes
HA_OPMODE_TO_GH = {STATE_OFF: "off", STATE_AUTO: "timer", STATE_MANUAL: "override"}
GH_STATE_TO_HA = {
"off": STATE_OFF,
@ -34,91 +27,49 @@ GH_STATE_TO_HA = {
"linked": None,
"other": None,
}
GH_STATE_ATTRS = ["type", "override"]
GH_HEATERS = ["hot water temperature"]
async def async_setup_platform(
hass, hass_config, async_add_entities, discovery_info=None
):
hass: HomeAssistantType, config: ConfigType, async_add_entities, discovery_info=None
) -> None:
"""Set up the Genius Hub water_heater entities."""
client = hass.data[DOMAIN]["client"]
if discovery_info is None:
return
entities = [
GeniusWaterHeater(z) for z in client.zone_objs if z.data["type"] in GH_HEATERS
broker = hass.data[DOMAIN]["broker"]
async_add_entities(
[
GeniusWaterHeater(broker, z)
for z in broker.client.zone_objs
if z.data["type"] in GH_HEATERS
]
async_add_entities(entities)
)
class GeniusWaterHeater(GeniusEntity, WaterHeaterDevice):
class GeniusWaterHeater(GeniusZone, WaterHeaterDevice):
"""Representation of a Genius Hub water_heater device."""
def __init__(self, boiler) -> None:
def __init__(self, broker, zone) -> None:
"""Initialize the water_heater device."""
super().__init__()
super().__init__(broker, zone)
self._boiler = boiler
self._operation_list = list(HA_OPMODE_TO_GH)
@property
def name(self) -> str:
"""Return the name of the water_heater device."""
return self._boiler.name
@property
def device_state_attributes(self) -> Dict[str, Any]:
"""Return the device state attributes."""
return {
"status": {
k: v for k, v in self._boiler.data.items() if k in GH_STATE_ATTRS
}
}
@property
def current_temperature(self) -> Optional[float]:
"""Return the current temperature."""
return self._boiler.data.get("temperature")
@property
def target_temperature(self) -> float:
"""Return the temperature we try to reach."""
return self._boiler.data["setpoint"]
@property
def min_temp(self) -> float:
"""Return max valid temperature that can be set."""
return GH_MIN_TEMP
@property
def max_temp(self) -> float:
"""Return max valid temperature that can be set."""
return GH_MAX_TEMP
@property
def temperature_unit(self) -> str:
"""Return the unit of measurement."""
return TEMP_CELSIUS
@property
def supported_features(self) -> int:
"""Return the list of supported features."""
return GH_SUPPORT_FLAGS
self._max_temp = 80.0
self._min_temp = 30.0
self._supported_features = SUPPORT_TARGET_TEMPERATURE | SUPPORT_OPERATION_MODE
@property
def operation_list(self) -> List[str]:
"""Return the list of available operation modes."""
return self._operation_list
return list(HA_OPMODE_TO_GH)
@property
def current_operation(self) -> str:
"""Return the current operation mode."""
return GH_STATE_TO_HA[self._boiler.data["mode"]]
return GH_STATE_TO_HA[self._zone.data["mode"]]
async def async_set_operation_mode(self, operation_mode) -> Awaitable[None]:
async def async_set_operation_mode(self, operation_mode) -> None:
"""Set a new operation mode for this boiler."""
await self._boiler.set_mode(HA_OPMODE_TO_GH[operation_mode])
async def async_set_temperature(self, **kwargs) -> Awaitable[None]:
"""Set a new target temperature for this boiler."""
temperature = kwargs[ATTR_TEMPERATURE]
await self._boiler.set_override(temperature, 3600) # 1 hour
await self._zone.set_mode(HA_OPMODE_TO_GH[operation_mode])

View file

@ -525,7 +525,7 @@ gearbest_parser==1.0.7
geizhals==0.0.9
# homeassistant.components.geniushub
geniushub-client==0.6.13
geniushub-client==0.6.26
# homeassistant.components.geo_json_events
# homeassistant.components.nsw_rural_fire_service_feed