Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add hive boost to climate and water_heater #26789

Merged
merged 33 commits into from Sep 27, 2019
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
3da4e2a
Start the Boost work
Sep 4, 2019
40546d0
Add services.yaml
Sep 4, 2019
55f1c07
Added Services #2
Sep 8, 2019
9ecbc49
Start the Boost work
Sep 4, 2019
a6ec9df
Add services.yaml
Sep 4, 2019
8d1d373
Added Services #2
Sep 8, 2019
4a0433a
Merge branch 'feature/add_boost' of github.com:KJonline/home-assistan…
Sep 8, 2019
a1eb496
Working Services
Sep 8, 2019
917d290
Merge branch 'dev' into feature/add_boost
Sep 11, 2019
2f37b6c
pyhiveapi to 0.2.19
Rendili Sep 12, 2019
43199ff
Update Libary to 0.2.19
Sep 14, 2019
2fddd8c
Merge branch 'feature/add_boost' of github.com:KJonline/home-assistan…
Sep 15, 2019
5b37b27
Merge branch 'dev' into feature/add_boost
Sep 15, 2019
6488112
Update Water_heater boost
Rendili Sep 17, 2019
17f0e26
Added Async hass add function
Sep 20, 2019
9576209
Update Services
Sep 21, 2019
ca26b29
Merge branch 'dev' into feature/add_boost
Sep 21, 2019
3c37392
MERGED
Sep 21, 2019
6faac31
Reviewed Changes
Sep 22, 2019
b7c5d85
Fixed Refresh System
Sep 22, 2019
aad3aad
Review 2
Sep 22, 2019
d4db766
Moved device iteration to the platform
Sep 23, 2019
aa92c41
update
Sep 23, 2019
adcd69c
Updates #2
Sep 24, 2019
4922ae4
Review#3 New Base Class
Sep 24, 2019
f2eb368
Review #5
Sep 26, 2019
b3d91a2
Merge branch 'dev' into feature/add_boost
Sep 26, 2019
36b8516
Update homeassistant/components/hive/__init__.py
Sep 26, 2019
c95027a
Update homeassistant/components/hive/__init__.py
Sep 26, 2019
5084044
Update homeassistant/components/hive/__init__.py
Sep 26, 2019
73da60c
Review 6
Sep 26, 2019
7276045
Review 7
Sep 26, 2019
1ed828e
Removed Child classes to inhertit from the parent
Sep 27, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
134 changes: 124 additions & 10 deletions homeassistant/components/hive/__init__.py
@@ -1,17 +1,32 @@
"""Support for the Hive devices."""
"""Support for the Hive devices and services."""
from functools import wraps
import logging

from pyhiveapi import Pyhiveapi
import voluptuous as vol

from homeassistant.const import CONF_PASSWORD, CONF_SCAN_INTERVAL, CONF_USERNAME
from homeassistant.const import (
ATTR_ENTITY_ID,
ATTR_TEMPERATURE,
CONF_PASSWORD,
CONF_SCAN_INTERVAL,
CONF_USERNAME,
)
from homeassistant.core import callback
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.discovery import load_platform
from homeassistant.helpers.dispatcher import async_dispatcher_connect, dispatcher_send
from homeassistant.helpers.entity import Entity

_LOGGER = logging.getLogger(__name__)

DOMAIN = "hive"
DATA_HIVE = "data_hive"
SERVICES = ["Heating", "HotWater"]
SERVICE_BOOST_HOTWATER = "boost_hotwater"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noticed during the review of the documentation: hotwater is not one word. So I expected it to be hot_water ?

SERVICE_BOOST_HEATING = "boost_heating"
ATTR_TIME_PERIOD = "time_period"
ATTR_MODE = "on_off"
DEVICETYPES = {
"binary_sensor": "device_list_binary_sensor",
"climate": "device_list_climate",
Expand All @@ -34,11 +49,31 @@
extra=vol.ALLOW_EXTRA,
)

BOOST_HEATING_SCHEMA = vol.Schema(
{
vol.Required(ATTR_ENTITY_ID): cv.entity_id,
vol.Required(ATTR_TIME_PERIOD): vol.All(
cv.time_period, cv.positive_timedelta, lambda td: td.total_seconds() // 60
),
vol.Optional(ATTR_TEMPERATURE, default="25.0"): vol.Coerce(float),
}
)

BOOST_HOTWATER_SCHEMA = vol.Schema(
{
vol.Required(ATTR_ENTITY_ID): cv.entity_id,
vol.Optional(ATTR_TIME_PERIOD, default="00:30:00"): vol.All(
cv.time_period, cv.positive_timedelta, lambda td: td.total_seconds() // 60
),
vol.Required(ATTR_MODE): cv.string,
}
)


class HiveSession:
"""Initiate Hive Session Class."""

entities = []
entity_lookup = {}
core = None
heating = None
hotwater = None
Expand All @@ -51,16 +86,45 @@ class HiveSession:

def setup(hass, config):
"""Set up the Hive Component."""

def heating_boost(service):
"""Handle the service call."""
node_id = HiveSession.entity_lookup.get(service.data[ATTR_ENTITY_ID])
if not node_id:
# log or raise error
_LOGGER.error("Cannot boost entity id entered")
return

minutes = service.data[ATTR_TIME_PERIOD]
temperature = service.data[ATTR_TEMPERATURE]

session.heating.turn_boost_on(node_id, minutes, temperature)

def hotwater_boost(service):
"""Handle the service call."""
node_id = HiveSession.entity_lookup.get(service.data[ATTR_ENTITY_ID])
if not node_id:
# log or raise error
_LOGGER.error("Cannot boost entity id entered")
return
minutes = service.data[ATTR_TIME_PERIOD]
mode = service.data[ATTR_MODE]

if mode == "on":
session.hotwater.turn_boost_on(node_id, minutes)
elif mode == "off":
session.hotwater.turn_boost_off(node_id)

session = HiveSession()
session.core = Pyhiveapi()

username = config[DOMAIN][CONF_USERNAME]
password = config[DOMAIN][CONF_PASSWORD]
update_interval = config[DOMAIN][CONF_SCAN_INTERVAL]

devicelist = session.core.initialise_api(username, password, update_interval)
devices = session.core.initialise_api(username, password, update_interval)

if devicelist is None:
if devices is None:
_LOGGER.error("Hive API initialization failed")
return False

Expand All @@ -73,9 +137,59 @@ def setup(hass, config):
session.attributes = Pyhiveapi.Attributes()
hass.data[DATA_HIVE] = session

for ha_type, hive_type in DEVICETYPES.items():
for key, devices in devicelist.items():
if key == hive_type:
for hivedevice in devices:
load_platform(hass, ha_type, DOMAIN, hivedevice, config)
for ha_type in DEVICETYPES:
devicelist = devices.get(DEVICETYPES[ha_type])
if devicelist:
load_platform(hass, ha_type, DOMAIN, devicelist, config)
if ha_type == "climate":
hass.services.register(
DOMAIN,
SERVICE_BOOST_HEATING,
heating_boost,
schema=BOOST_HEATING_SCHEMA,
)
if ha_type == "water_heater":
hass.services.register(
DOMAIN,
SERVICE_BOOST_HEATING,
hotwater_boost,
schema=BOOST_HOTWATER_SCHEMA,
)

return True


def refresh_system(func):
"""Force update all entities after state change."""

@wraps(func)
def wrapper(self, *args, **kwargs):
func(self, *args, **kwargs)
dispatcher_send(self.hass, DOMAIN)

return wrapper


class HiveEntity(Entity):
"""Initiate Hive Base Class."""

def __init__(self, session, hive_device):
"""Initialize the instance."""
self.node_id = hive_device["Hive_NodeID"]
self.node_name = hive_device["Hive_NodeName"]
self.device_type = hive_device["HA_DeviceType"]
self.node_device_type = hive_device["Hive_DeviceType"]
MartinHjelmare marked this conversation as resolved.
Show resolved Hide resolved
self.session = session
self.attributes = {}
self._unique_id = f"{self.node_id}-{self.device_type}"

async def async_added_to_hass(self):
MartinHjelmare marked this conversation as resolved.
Show resolved Hide resolved
"""When entity is added to Home Assistant."""
async_dispatcher_connect(self.hass, DOMAIN, self._update_callback)
if self.device_type in SERVICES:
self.session.entity_lookup[self.entity_id] = self.node_id
MartinHjelmare marked this conversation as resolved.
Show resolved Hide resolved

@callback
def _update_callback(self):
"""Call update method."""
self.async_schedule_update_ha_state()
28 changes: 9 additions & 19 deletions homeassistant/components/hive/binary_sensor.py
@@ -1,7 +1,7 @@
"""Support for the Hive binary sensors."""
from homeassistant.components.binary_sensor import BinarySensorDevice

from . import DATA_HIVE, DOMAIN
from . import DOMAIN, DATA_HIVE, HiveEntity

DEVICETYPE_DEVICE_CLASS = {"motionsensor": "motion", "contactsensor": "opening"}

Expand All @@ -10,25 +10,20 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up Hive sensor devices."""
if discovery_info is None:
return
session = hass.data.get(DATA_HIVE)

add_entities([HiveBinarySensorEntity(session, discovery_info)])
session = hass.data.get(DATA_HIVE)
devs = []
for dev in discovery_info:
devs.append(HiveBinarySensorEntity(session, dev))
add_entities(devs)


class HiveBinarySensorEntity(BinarySensorDevice):
class HiveBinarySensorEntity(HiveEntity, BinarySensorDevice):
"""Representation of a Hive binary sensor."""

def __init__(self, hivesession, hivedevice):
def __init__(self, hive_session, hive_device):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this.

"""Initialize the hive sensor."""
self.node_id = hivedevice["Hive_NodeID"]
self.node_name = hivedevice["Hive_NodeName"]
self.device_type = hivedevice["HA_DeviceType"]
self.node_device_type = hivedevice["Hive_DeviceType"]
self.session = hivesession
self.attributes = {}
self.data_updatesource = f"{self.device_type}.{self.node_id}"
self._unique_id = f"{self.node_id}-{self.device_type}"
self.session.entities.append(self)
super().__init__(hive_session, hive_device)

@property
def unique_id(self):
Expand All @@ -40,11 +35,6 @@ def device_info(self):
"""Return device information."""
return {"identifiers": {(DOMAIN, self.unique_id)}, "name": self.name}

def handle_update(self, updatesource):
"""Handle the new update request."""
if f"{self.device_type}.{self.node_id}" not in updatesource:
self.schedule_update_ha_state()

@property
def device_class(self):
"""Return the class of this sensor."""
Expand Down
59 changes: 17 additions & 42 deletions homeassistant/components/hive/climate.py
Expand Up @@ -5,13 +5,14 @@
HVAC_MODE_HEAT,
HVAC_MODE_OFF,
PRESET_BOOST,
PRESET_NONE,
SUPPORT_PRESET_MODE,
SUPPORT_TARGET_TEMPERATURE,
PRESET_NONE,
)
from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS

from . import DATA_HIVE, DOMAIN

from . import DOMAIN, DATA_HIVE, HiveEntity, refresh_system

HIVE_TO_HASS_STATE = {
"SCHEDULE": HVAC_MODE_AUTO,
Expand All @@ -34,28 +35,21 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up Hive climate devices."""
if discovery_info is None:
return
if discovery_info["HA_DeviceType"] != "Heating":
return

session = hass.data.get(DATA_HIVE)
climate = HiveClimateEntity(session, discovery_info)

add_entities([climate])
devs = []
for dev in discovery_info:
devs.append(HiveClimateEntity(session, dev))
add_entities(devs)


class HiveClimateEntity(ClimateDevice):
class HiveClimateEntity(HiveEntity, ClimateDevice):
"""Hive Climate Device."""

def __init__(self, hivesession, hivedevice):
def __init__(self, hive_session, hive_device):
"""Initialize the Climate device."""
self.node_id = hivedevice["Hive_NodeID"]
self.node_name = hivedevice["Hive_NodeName"]
self.device_type = hivedevice["HA_DeviceType"]
self.thermostat_node_id = hivedevice["Thermostat_NodeID"]
self.session = hivesession
self.attributes = {}
self.data_updatesource = f"{self.device_type}.{self.node_id}"
self._unique_id = f"{self.node_id}-{self.device_type}"
super().__init__(hive_session, hive_device)
self.thermostat_node_id = hive_device["Thermostat_NodeID"]

@property
def unique_id(self):
Expand All @@ -72,11 +66,6 @@ def supported_features(self):
"""Return the list of supported features."""
return SUPPORT_FLAGS

def handle_update(self, updatesource):
"""Handle the new update request."""
if f"{self.device_type}.{self.node_id}" not in updatesource:
self.schedule_update_ha_state()

@property
def name(self):
"""Return the name of the Climate device."""
Expand All @@ -99,7 +88,7 @@ def hvac_modes(self):
return SUPPORT_HVAC

@property
def hvac_mode(self) -> str:
def hvac_mode(self):
"""Return hvac operation ie. heat, cool mode.

Need to be one of HVAC_MODE_*.
Expand Down Expand Up @@ -143,43 +132,29 @@ def preset_modes(self):
"""Return a list of available preset modes."""
return SUPPORT_PRESET

async def async_added_to_hass(self):
"""When entity is added to Home Assistant."""
await super().async_added_to_hass()
self.session.entities.append(self)

@refresh_system
def set_hvac_mode(self, hvac_mode):
"""Set new target hvac mode."""
new_mode = HASS_TO_HIVE_STATE[hvac_mode]
self.session.heating.set_mode(self.node_id, new_mode)

for entity in self.session.entities:
entity.handle_update(self.data_updatesource)

@refresh_system
def set_temperature(self, **kwargs):
"""Set new target temperature."""
new_temperature = kwargs.get(ATTR_TEMPERATURE)
if new_temperature is not None:
self.session.heating.set_target_temperature(self.node_id, new_temperature)

for entity in self.session.entities:
entity.handle_update(self.data_updatesource)

def set_preset_mode(self, preset_mode) -> None:
@refresh_system
def set_preset_mode(self, preset_mode):
"""Set new preset mode."""
if preset_mode == PRESET_NONE and self.preset_mode == PRESET_BOOST:
self.session.heating.turn_boost_off(self.node_id)

elif preset_mode == PRESET_BOOST:
curtemp = self.session.heating.current_temperature(self.node_id)
curtemp = round(curtemp * 2) / 2
curtemp = round(self.current_temperature * 2) / 2
temperature = curtemp + 0.5

self.session.heating.turn_boost_on(self.node_id, 30, temperature)

for entity in self.session.entities:
entity.handle_update(self.data_updatesource)

def update(self):
"""Update all Node data from Hive."""
self.session.core.update_data(self.node_id)
Expand Down