-
-
Notifications
You must be signed in to change notification settings - Fork 32k
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
New Hive Component / Platforms #9804
Changes from 32 commits
9c3d11d
74e10f3
b38e659
1e9a9c0
36e313e
b6e2df3
a16df0e
863f569
1f81751
6e302a7
dca60ef
0457a01
5e7d872
63549cf
1595301
6dc23c3
0cc6339
7b54dbd
2f4c801
b2ccfc3
0860845
098112a
6aed8d5
03d925c
c69912d
d4ed607
fe4cbe3
a63fccf
fff1397
30f5666
21c3dc8
a80dd4b
b5d7053
510f777
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
""" | ||
Support for the Hive devices. | ||
|
||
For more details about this platform, please refer to the documentation at | ||
https://home-assistant.io/components/binary_sensor.hive/ | ||
""" | ||
import logging | ||
|
||
from homeassistant.components.binary_sensor import BinarySensorDevice | ||
from homeassistant.components.hive import DATA_HIVE | ||
|
||
DEPENDENCIES = ['hive'] | ||
|
||
DEVICETYPE_DEVICE_CLASS = {'motionsensor': 'motion', | ||
'contactsensor': 'opening'} | ||
|
||
|
||
def setup_platform(hass, config, add_devices, discovery_info=None): | ||
"""Set up Hive sensor devices.""" | ||
if discovery_info is None: | ||
return | ||
session = hass.data.get(DATA_HIVE) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would add a check here before continuing with the setup, to make sure if discovery_info is None:
return
session = hass.data.get(DATA_HIVE)
... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
|
||
add_devices([HiveBinarySensorEntity(session, discovery_info)]) | ||
|
||
|
||
class HiveBinarySensorEntity(BinarySensorDevice): | ||
"""Representation of a Hive binary sensor.""" | ||
|
||
def __init__(self, hivesession, hivedevice): | ||
"""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.data_updatesource = '{}.{}'.format(self.device_type, | ||
self.node_id) | ||
|
||
self.session.entities.append(self) | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This entity does not add itself to the list of entities that the component stores. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you are good, thanks |
||
def handle_update(self, updatesource): | ||
"""Handle the new update request.""" | ||
if '{}.{}'.format(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.""" | ||
return DEVICETYPE_DEVICE_CLASS.get(self.node_device_type) | ||
|
||
@property | ||
def name(self): | ||
"""Return the name of the binary sensor.""" | ||
return self.node_name | ||
|
||
@property | ||
def is_on(self): | ||
"""Return true if the binary sensor is on.""" | ||
return self.session.sensor.get_state(self.node_id, | ||
self.node_device_type) | ||
|
||
def update(self): | ||
"""Update all Node data frome Hive.""" | ||
if self.session.core.update_data(self.node_id): | ||
for entity in self.session.entities: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You shouldn't have this here. If we assume all platforms will update at about the same scan interval, with this here, there'll be five state updates per entity instead of just one, when only one is needed. This is only good to have when the user induces a device update from one entity via a service call, and we want to leverage the new state info for all entities. That is not the case here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is there so that if an update is requested and that then resulted in new hive data it would trigger the update of all the other entities. |
||
entity.handle_update(self.data_updatesource) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
""" | ||
Support for the Hive devices. | ||
|
||
For more details about this platform, please refer to the documentation at | ||
https://home-assistant.io/components/climate.hive/ | ||
""" | ||
import logging | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 'logging' imported but unused There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed Mr Hound! |
||
|
||
from homeassistant.components.climate import (ClimateDevice, | ||
STATE_AUTO, STATE_HEAT, | ||
STATE_OFF, STATE_ON) | ||
from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS | ||
from homeassistant.components.hive import DATA_HIVE | ||
|
||
DEPENDENCIES = ['hive'] | ||
HIVE_TO_HASS_STATE = {'SCHEDULE': STATE_AUTO, 'MANUAL': STATE_HEAT, | ||
'ON': STATE_ON, 'OFF': STATE_OFF} | ||
HASS_TO_HIVE_STATE = {STATE_AUTO: 'SCHEDULE', STATE_HEAT: 'MANUAL', | ||
STATE_ON: 'ON', STATE_OFF: 'OFF'} | ||
|
||
|
||
def setup_platform(hass, config, add_devices, discovery_info=None): | ||
"""Set up Hive climate devices.""" | ||
if discovery_info is None: | ||
return | ||
session = hass.data.get(DATA_HIVE) | ||
|
||
add_devices([HiveClimateEntity(session, discovery_info)]) | ||
|
||
|
||
class HiveClimateEntity(ClimateDevice): | ||
"""Hive Climate Device.""" | ||
|
||
def __init__(self, hivesession, hivedevice): | ||
"""Initialize the Climate device.""" | ||
self.node_id = hivedevice["Hive_NodeID"] | ||
self.node_name = hivedevice["Hive_NodeName"] | ||
self.device_type = hivedevice["HA_DeviceType"] | ||
self.session = hivesession | ||
self.data_updatesource = '{}.{}'.format(self.device_type, | ||
self.node_id) | ||
|
||
if self.device_type == "Heating": | ||
self.modes = [STATE_AUTO, STATE_HEAT, STATE_OFF] | ||
elif self.device_type == "HotWater": | ||
self.modes = [STATE_AUTO, STATE_ON, STATE_OFF] | ||
|
||
self.session.entities.append(self) | ||
|
||
def handle_update(self, updatesource): | ||
"""Handle the new update request.""" | ||
if '{}.{}'.format(self.device_type, self.node_id) not in updatesource: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't understand what's happening here. Why should all hive entities except the one where the update originated from be scheduled for a state update? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The entity that is not updated in this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But why should a change in state of one entity update the state of the other entities? Why should there be that connection? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. because, when you set a new temperature for example, the Hive api returns the latest data for all your Hive devices, so to me it makes sense to utilise as soon as possible that latest data by getting all the HA entities to refresh. They wont go out to the Hive api as it has only just been retrieved to HA cache. It is the same for when you set temperature, or turn on a light, each time you do anything with the Hive API it will return the latest data for all devices. It there any reason why it is not allowed to do this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, I hadn't read all the previous discussions. As said in those, the dispatcher could be used for this case. But I think it's ok like this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok, great, thanks. |
||
self.schedule_update_ha_state() | ||
|
||
@property | ||
def name(self): | ||
"""Return the name of the Climate device.""" | ||
friendly_name = "Climate Device" | ||
if self.device_type == "Heating": | ||
friendly_name = "Heating" | ||
if self.node_name is not None: | ||
friendly_name = '{} {}'.format(self.node_name, friendly_name) | ||
elif self.device_type == "HotWater": | ||
friendly_name = "Hot Water" | ||
return friendly_name | ||
|
||
@property | ||
def temperature_unit(self): | ||
"""Return the unit of measurement.""" | ||
return TEMP_CELSIUS | ||
|
||
@property | ||
def current_temperature(self): | ||
"""Return the current temperature.""" | ||
if self.device_type == "Heating": | ||
return self.session.heating.current_temperature(self.node_id) | ||
|
||
@property | ||
def target_temperature(self): | ||
"""Return the target temperature.""" | ||
if self.device_type == "Heating": | ||
return self.session.heating.get_target_temperature(self.node_id) | ||
|
||
@property | ||
def min_temp(self): | ||
"""Return minimum temperature.""" | ||
if self.device_type == "Heating": | ||
return self.session.heating.min_temperature(self.node_id) | ||
|
||
@property | ||
def max_temp(self): | ||
"""Return the maximum temperature.""" | ||
if self.device_type == "Heating": | ||
return self.session.heating.max_temperature(self.node_id) | ||
|
||
@property | ||
def operation_list(self): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should translate the values returned by the library into the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, I don't understand. Please could you give an example of an existing climate component which could help show me what you mean? The value "schedule" is used as this is what is used in the official Hive application and website. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, have a look at the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Great, thanks, will take a detailed look. So (after a quick look) I need / can to use the following mapping? from the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have updated as above. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks good. If you want to use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. great, thanks. |
||
"""List of the operation modes.""" | ||
return self.modes | ||
|
||
@property | ||
def current_operation(self): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The values used in the operation list in climate are taken directly from the Hive api and is what is displayed and used by the end user in the official Hive application and website. This ensures consistency between the official Hive interfaces and the Home Assistant interface. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see the benefit of that. However, since home assistant tries to provide a standardized interface for lots of devices from lots of manufacturers, we should try to standardize across home assistant first, and between home assistant and the manufacturers second. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok understood There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. as above. |
||
"""Return current mode.""" | ||
if self.device_type == "Heating": | ||
currentmode = self.session.heating.get_mode(self.node_id) | ||
elif self.device_type == "HotWater": | ||
currentmode = self.session.hotwater.get_mode(self.node_id) | ||
return HIVE_TO_HASS_STATE.get(currentmode) | ||
|
||
def set_operation_mode(self, operation_mode): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. my comments as above in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. as above. |
||
"""Set new Heating mode.""" | ||
new_mode = HASS_TO_HIVE_STATE.get(operation_mode) | ||
if self.device_type == "Heating": | ||
self.session.heating.set_mode(self.node_id, new_mode) | ||
elif self.device_type == "HotWater": | ||
self.session.hotwater.set_mode(self.node_id, new_mode) | ||
|
||
for entity in self.session.entities: | ||
entity.handle_update(self.data_updatesource) | ||
|
||
def set_temperature(self, **kwargs): | ||
"""Set new target temperature.""" | ||
new_temperature = kwargs.get(ATTR_TEMPERATURE) | ||
if new_temperature is not None: | ||
if self.device_type == "Heating": | ||
self.session.heating.set_target_temperature(self.node_id, | ||
new_temperature) | ||
|
||
for entity in self.session.entities: | ||
entity.handle_update(self.data_updatesource) | ||
|
||
def update(self): | ||
"""Update all Node data frome Hive.""" | ||
if self.session.core.update_data(self.node_id): | ||
for entity in self.session.entities: | ||
entity.handle_update(self.data_updatesource) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
""" | ||
Support for the Hive devices. | ||
|
||
For more details about this platform, please refer to the documentation at | ||
https://home-assistant.io/components/hive/ | ||
""" | ||
import logging | ||
import voluptuous as vol | ||
|
||
from homeassistant.const import (CONF_PASSWORD, CONF_SCAN_INTERVAL, | ||
CONF_USERNAME) | ||
import homeassistant.helpers.config_validation as cv | ||
from homeassistant.helpers.discovery import load_platform | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Import must be ordered according PEP8. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Imports on all files re-ordered using the recommended isort |
||
|
||
REQUIREMENTS = ['pyhiveapi==0.2.5'] | ||
|
||
_LOGGER = logging.getLogger(__name__) | ||
DOMAIN = 'hive' | ||
DATA_HIVE = 'data_hive' | ||
DEVICETYPES = { | ||
'binary_sensor': 'device_list_binary_sensor', | ||
'climate': 'device_list_climate', | ||
'light': 'device_list_light', | ||
'switch': 'device_list_plug', | ||
'sensor': 'device_list_sensor', | ||
} | ||
|
||
CONFIG_SCHEMA = vol.Schema({ | ||
DOMAIN: vol.Schema({ | ||
vol.Required(CONF_PASSWORD): cv.string, | ||
vol.Required(CONF_USERNAME): cv.string, | ||
vol.Optional(CONF_SCAN_INTERVAL, default=2): cv.positive_int, | ||
}) | ||
}, extra=vol.ALLOW_EXTRA) | ||
|
||
|
||
class HiveSession: | ||
"""Initiate Hive Session Class.""" | ||
|
||
entities = [] | ||
core = None | ||
heating = None | ||
hotwater = None | ||
light = None | ||
sensor = None | ||
switch = None | ||
|
||
|
||
def setup(hass, config): | ||
"""Set up the Hive Component.""" | ||
from pyhiveapi import Pyhiveapi | ||
|
||
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) | ||
|
||
if devicelist is None: | ||
_LOGGER.error("Hive API initialization failed") | ||
return False | ||
|
||
session.sensor = Pyhiveapi.Sensor() | ||
session.heating = Pyhiveapi.Heating() | ||
session.hotwater = Pyhiveapi.Hotwater() | ||
session.light = Pyhiveapi.Light() | ||
session.switch = Pyhiveapi.Switch() | ||
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) | ||
return True |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'logging' imported but unused
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed Mr Hound!