mirror of
https://github.com/home-assistant/core.git
synced 2025-07-24 21:57:51 +00:00
Support for EcoNet water heaters (#11260)
* Support for EcoNet water heaters. * Fixed requested changes. * Added logging when temp or operation mode are None * More fixes from PR review. * Updated pyeconet version to fix natural gas water heater error. Last PR review fix.
This commit is contained in:
parent
49bc95549b
commit
3fd620198e
@ -296,6 +296,7 @@ omit =
|
|||||||
homeassistant/components/camera/rpi_camera.py
|
homeassistant/components/camera/rpi_camera.py
|
||||||
homeassistant/components/camera/synology.py
|
homeassistant/components/camera/synology.py
|
||||||
homeassistant/components/camera/yi.py
|
homeassistant/components/camera/yi.py
|
||||||
|
homeassistant/components/climate/econet.py
|
||||||
homeassistant/components/climate/ephember.py
|
homeassistant/components/climate/ephember.py
|
||||||
homeassistant/components/climate/eq3btsmart.py
|
homeassistant/components/climate/eq3btsmart.py
|
||||||
homeassistant/components/climate/flexit.py
|
homeassistant/components/climate/flexit.py
|
||||||
|
235
homeassistant/components/climate/econet.py
Normal file
235
homeassistant/components/climate/econet.py
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
"""
|
||||||
|
Support for Rheem EcoNet water heaters.
|
||||||
|
|
||||||
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/climate.econet/
|
||||||
|
"""
|
||||||
|
import datetime
|
||||||
|
import logging
|
||||||
|
from os import path
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant.components.climate import (
|
||||||
|
DOMAIN,
|
||||||
|
PLATFORM_SCHEMA,
|
||||||
|
STATE_ECO, STATE_GAS, STATE_ELECTRIC,
|
||||||
|
STATE_HEAT_PUMP, STATE_HIGH_DEMAND,
|
||||||
|
STATE_OFF, SUPPORT_TARGET_TEMPERATURE,
|
||||||
|
SUPPORT_OPERATION_MODE,
|
||||||
|
ClimateDevice)
|
||||||
|
from homeassistant.config import load_yaml_config_file
|
||||||
|
from homeassistant.const import (ATTR_ENTITY_ID,
|
||||||
|
CONF_PASSWORD, CONF_USERNAME, TEMP_FAHRENHEIT,
|
||||||
|
ATTR_TEMPERATURE)
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
|
||||||
|
REQUIREMENTS = ['pyeconet==0.0.4']
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
ATTR_VACATION_START = 'next_vacation_start_date'
|
||||||
|
ATTR_VACATION_END = 'next_vacation_end_date'
|
||||||
|
ATTR_ON_VACATION = 'on_vacation'
|
||||||
|
ATTR_TODAYS_ENERGY_USAGE = 'todays_energy_usage'
|
||||||
|
ATTR_IN_USE = 'in_use'
|
||||||
|
|
||||||
|
ATTR_START_DATE = 'start_date'
|
||||||
|
ATTR_END_DATE = 'end_date'
|
||||||
|
|
||||||
|
SUPPORT_FLAGS_HEATER = (SUPPORT_TARGET_TEMPERATURE | SUPPORT_OPERATION_MODE)
|
||||||
|
|
||||||
|
SERVICE_ADD_VACATION = 'econet_add_vacation'
|
||||||
|
SERVICE_DELETE_VACATION = 'econet_delete_vacation'
|
||||||
|
|
||||||
|
ADD_VACATION_SCHEMA = vol.Schema({
|
||||||
|
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
|
||||||
|
vol.Optional(ATTR_START_DATE): cv.positive_int,
|
||||||
|
vol.Required(ATTR_END_DATE): cv.positive_int,
|
||||||
|
})
|
||||||
|
|
||||||
|
DELETE_VACATION_SCHEMA = vol.Schema({
|
||||||
|
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
|
||||||
|
})
|
||||||
|
|
||||||
|
ECONET_DATA = 'econet'
|
||||||
|
|
||||||
|
HA_STATE_TO_ECONET = {
|
||||||
|
STATE_ECO: 'Energy Saver',
|
||||||
|
STATE_ELECTRIC: 'Electric',
|
||||||
|
STATE_HEAT_PUMP: 'Heat Pump',
|
||||||
|
STATE_GAS: 'gas',
|
||||||
|
STATE_HIGH_DEMAND: 'High Demand',
|
||||||
|
STATE_OFF: 'Off',
|
||||||
|
}
|
||||||
|
|
||||||
|
ECONET_STATE_TO_HA = {value: key for key, value in HA_STATE_TO_ECONET.items()}
|
||||||
|
|
||||||
|
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||||
|
vol.Required(CONF_USERNAME): cv.string,
|
||||||
|
vol.Required(CONF_PASSWORD): cv.string,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
|
"""Set up the EcoNet water heaters."""
|
||||||
|
from pyeconet.api import PyEcoNet
|
||||||
|
|
||||||
|
hass.data[ECONET_DATA] = {}
|
||||||
|
hass.data[ECONET_DATA]['water_heaters'] = []
|
||||||
|
|
||||||
|
username = config.get(CONF_USERNAME)
|
||||||
|
password = config.get(CONF_PASSWORD)
|
||||||
|
|
||||||
|
econet = PyEcoNet(username, password)
|
||||||
|
water_heaters = econet.get_water_heaters()
|
||||||
|
hass_water_heaters = [
|
||||||
|
EcoNetWaterHeater(water_heater) for water_heater in water_heaters]
|
||||||
|
add_devices(hass_water_heaters)
|
||||||
|
hass.data[ECONET_DATA]['water_heaters'].extend(hass_water_heaters)
|
||||||
|
|
||||||
|
def service_handle(service):
|
||||||
|
"""Handler for services."""
|
||||||
|
entity_ids = service.data.get('entity_id')
|
||||||
|
all_heaters = hass.data[ECONET_DATA]['water_heaters']
|
||||||
|
_heaters = [
|
||||||
|
x for x in all_heaters
|
||||||
|
if not entity_ids or x.entity_id in entity_ids]
|
||||||
|
|
||||||
|
for _water_heater in _heaters:
|
||||||
|
if service.service == SERVICE_ADD_VACATION:
|
||||||
|
start = service.data.get(ATTR_START_DATE)
|
||||||
|
end = service.data.get(ATTR_END_DATE)
|
||||||
|
_water_heater.add_vacation(start, end)
|
||||||
|
if service.service == SERVICE_DELETE_VACATION:
|
||||||
|
for vacation in _water_heater.water_heater.vacations:
|
||||||
|
vacation.delete()
|
||||||
|
|
||||||
|
_water_heater.schedule_update_ha_state(True)
|
||||||
|
|
||||||
|
descriptions = load_yaml_config_file(
|
||||||
|
path.join(path.dirname(__file__), 'services.yaml'))
|
||||||
|
|
||||||
|
hass.services.register(DOMAIN, SERVICE_ADD_VACATION,
|
||||||
|
service_handle,
|
||||||
|
descriptions.get(SERVICE_ADD_VACATION),
|
||||||
|
schema=ADD_VACATION_SCHEMA)
|
||||||
|
|
||||||
|
hass.services.register(DOMAIN, SERVICE_DELETE_VACATION,
|
||||||
|
service_handle,
|
||||||
|
descriptions.get(SERVICE_DELETE_VACATION),
|
||||||
|
schema=DELETE_VACATION_SCHEMA)
|
||||||
|
|
||||||
|
|
||||||
|
class EcoNetWaterHeater(ClimateDevice):
|
||||||
|
"""Representation of an EcoNet water heater."""
|
||||||
|
|
||||||
|
def __init__(self, water_heater):
|
||||||
|
"""Initialize the water heater."""
|
||||||
|
self.water_heater = water_heater
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
"""Return the device name."""
|
||||||
|
return self.water_heater.name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def available(self):
|
||||||
|
"""Return if the the device is online or not."""
|
||||||
|
return self.water_heater.is_connected
|
||||||
|
|
||||||
|
@property
|
||||||
|
def temperature_unit(self):
|
||||||
|
"""Return the unit of measurement."""
|
||||||
|
return TEMP_FAHRENHEIT
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device_state_attributes(self):
|
||||||
|
"""Return the optional state attributes."""
|
||||||
|
data = {}
|
||||||
|
vacations = self.water_heater.get_vacations()
|
||||||
|
if vacations:
|
||||||
|
data[ATTR_VACATION_START] = vacations[0].start_date
|
||||||
|
data[ATTR_VACATION_END] = vacations[0].end_date
|
||||||
|
data[ATTR_ON_VACATION] = self.water_heater.is_on_vacation
|
||||||
|
todays_usage = self.water_heater.total_usage_for_today
|
||||||
|
if todays_usage:
|
||||||
|
data[ATTR_TODAYS_ENERGY_USAGE] = todays_usage
|
||||||
|
data[ATTR_IN_USE] = self.water_heater.in_use
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
@property
|
||||||
|
def current_operation(self):
|
||||||
|
"""
|
||||||
|
Return current operation as one of the following.
|
||||||
|
|
||||||
|
["eco", "heat_pump",
|
||||||
|
"high_demand", "electric_only"]
|
||||||
|
"""
|
||||||
|
current_op = ECONET_STATE_TO_HA.get(self.water_heater.mode)
|
||||||
|
return current_op
|
||||||
|
|
||||||
|
@property
|
||||||
|
def operation_list(self):
|
||||||
|
"""List of available operation modes."""
|
||||||
|
op_list = []
|
||||||
|
modes = self.water_heater.supported_modes
|
||||||
|
for mode in modes:
|
||||||
|
ha_mode = ECONET_STATE_TO_HA.get(mode)
|
||||||
|
if ha_mode is not None:
|
||||||
|
op_list.append(ha_mode)
|
||||||
|
else:
|
||||||
|
error = "Invalid operation mode mapping. " + mode + \
|
||||||
|
" doesn't map. Please report this."
|
||||||
|
_LOGGER.error(error)
|
||||||
|
return op_list
|
||||||
|
|
||||||
|
@property
|
||||||
|
def supported_features(self):
|
||||||
|
"""Return the list of supported features."""
|
||||||
|
return SUPPORT_FLAGS_HEATER
|
||||||
|
|
||||||
|
def set_temperature(self, **kwargs):
|
||||||
|
"""Set new target temperature."""
|
||||||
|
target_temp = kwargs.get(ATTR_TEMPERATURE)
|
||||||
|
if target_temp is not None:
|
||||||
|
self.water_heater.set_target_set_point(target_temp)
|
||||||
|
else:
|
||||||
|
_LOGGER.error("A target temperature must be provided.")
|
||||||
|
|
||||||
|
def set_operation_mode(self, operation_mode):
|
||||||
|
"""Set operation mode."""
|
||||||
|
op_mode_to_set = HA_STATE_TO_ECONET.get(operation_mode)
|
||||||
|
if op_mode_to_set is not None:
|
||||||
|
self.water_heater.set_mode(op_mode_to_set)
|
||||||
|
else:
|
||||||
|
_LOGGER.error("An operation mode must be provided.")
|
||||||
|
|
||||||
|
def add_vacation(self, start, end):
|
||||||
|
"""Add a vacation to this water heater."""
|
||||||
|
if not start:
|
||||||
|
start = datetime.datetime.now()
|
||||||
|
else:
|
||||||
|
start = datetime.datetime.fromtimestamp(start)
|
||||||
|
end = datetime.datetime.fromtimestamp(end)
|
||||||
|
self.water_heater.set_vacation_mode(start, end)
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
"""Get the latest date."""
|
||||||
|
self.water_heater.update_state()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def target_temperature(self):
|
||||||
|
"""Return the temperature we try to reach."""
|
||||||
|
return self.water_heater.set_point
|
||||||
|
|
||||||
|
@property
|
||||||
|
def min_temp(self):
|
||||||
|
"""Return the minimum temperature."""
|
||||||
|
return self.water_heater.min_set_point
|
||||||
|
|
||||||
|
@property
|
||||||
|
def max_temp(self):
|
||||||
|
"""Return the maximum temperature."""
|
||||||
|
return self.water_heater.max_set_point
|
@ -107,3 +107,23 @@ nuheat_resume_program:
|
|||||||
entity_id:
|
entity_id:
|
||||||
description: Name(s) of entities to change.
|
description: Name(s) of entities to change.
|
||||||
example: 'climate.kitchen'
|
example: 'climate.kitchen'
|
||||||
|
|
||||||
|
econet_add_vacation:
|
||||||
|
description: Add a vacation to your water heater.
|
||||||
|
fields:
|
||||||
|
entity_id:
|
||||||
|
description: Name(s) of entities to change.
|
||||||
|
example: 'climate.water_heater'
|
||||||
|
start_date:
|
||||||
|
description: The timestamp of when the vacation should start. (Optional, defaults to now)
|
||||||
|
example: 1513186320
|
||||||
|
end_date:
|
||||||
|
description: The timestamp of when the vacation should end.
|
||||||
|
example: 1513445520
|
||||||
|
|
||||||
|
econet_delete_vacation:
|
||||||
|
description: Delete your existing vacation from your water heater.
|
||||||
|
fields:
|
||||||
|
entity_id:
|
||||||
|
description: Name(s) of entities to change.
|
||||||
|
example: 'climate.water_heater'
|
||||||
|
@ -673,6 +673,9 @@ pydroid-ipcam==0.8
|
|||||||
# homeassistant.components.sensor.ebox
|
# homeassistant.components.sensor.ebox
|
||||||
pyebox==0.1.0
|
pyebox==0.1.0
|
||||||
|
|
||||||
|
# homeassistant.components.climate.econet
|
||||||
|
pyeconet==0.0.4
|
||||||
|
|
||||||
# homeassistant.components.eight_sleep
|
# homeassistant.components.eight_sleep
|
||||||
pyeight==0.0.7
|
pyeight==0.0.7
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user