-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for GPCV and GECO series heaters. (#28)
Add support for GPCV and GECO series heaters. Based on changes in KiLLeRRat/homeassistant-goldair-climate and the manual for GECO/GPEH heaters. Co-authored-by: Jason Rumney <jasonrumney@clarion.com.my>
- Loading branch information
1 parent
6bb70b7
commit fefa995
Showing
15 changed files
with
571 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
151 changes: 151 additions & 0 deletions
151
custom_components/goldair_climate/geco_heater/climate.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
""" | ||
Goldair GECO WiFi Heater device. | ||
""" | ||
from homeassistant.components.climate import ClimateDevice | ||
from homeassistant.components.climate.const import ( | ||
ATTR_HVAC_MODE, | ||
HVAC_MODE_HEAT, | ||
SUPPORT_TARGET_TEMPERATURE, | ||
) | ||
from homeassistant.const import ATTR_TEMPERATURE, STATE_UNAVAILABLE | ||
|
||
from ..device import GoldairTuyaDevice | ||
from .const import ( | ||
ATTR_ERROR, | ||
ATTR_TARGET_TEMPERATURE, | ||
HVAC_MODE_TO_DPS_MODE, | ||
PROPERTY_TO_DPS_ID, | ||
) | ||
|
||
SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE | ||
|
||
|
||
class GoldairGECOHeater(ClimateDevice): | ||
"""Representation of a Goldair GECO WiFi heater.""" | ||
|
||
def __init__(self, device): | ||
"""Initialize the heater. | ||
Args: | ||
device (GoldairTuyaDevice): The device API instance.""" | ||
self._device = device | ||
|
||
self._support_flags = SUPPORT_FLAGS | ||
|
||
self._TEMPERATURE_STEP = 1 | ||
self._TEMPERATURE_LIMITS = {"min": 15, "max": 35} | ||
|
||
@property | ||
def supported_features(self): | ||
"""Return the list of supported features.""" | ||
return self._support_flags | ||
|
||
@property | ||
def should_poll(self): | ||
"""Return the polling state.""" | ||
return True | ||
|
||
@property | ||
def name(self): | ||
"""Return the name of the climate device.""" | ||
return self._device.name | ||
|
||
@property | ||
def unique_id(self): | ||
"""Return the unique id for this heater.""" | ||
return self._device.unique_id | ||
|
||
@property | ||
def device_info(self): | ||
"""Return device information about this heater.""" | ||
return self._device.device_info | ||
|
||
@property | ||
def icon(self): | ||
"""Return the icon to use in the frontend for this device.""" | ||
hvac_mode = self.hvac_mode | ||
|
||
if hvac_mode == HVAC_MODE_HEAT: | ||
return "mdi:radiator" | ||
else: | ||
return "mdi:radiator-disabled" | ||
|
||
@property | ||
def temperature_unit(self): | ||
"""Return the unit of measurement.""" | ||
return self._device.temperature_unit | ||
|
||
@property | ||
def target_temperature(self): | ||
"""Return the temperature we try to reach.""" | ||
return self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_TARGET_TEMPERATURE]) | ||
|
||
@property | ||
def target_temperature_step(self): | ||
"""Return the supported step of target temperature.""" | ||
return self._TEMPERATURE_STEP | ||
|
||
@property | ||
def min_temp(self): | ||
"""Return the minimum temperature.""" | ||
return self._TEMPERATURE_LIMITS["min"] | ||
|
||
@property | ||
def max_temp(self): | ||
"""Return the maximum temperature.""" | ||
return self._TEMPERATURE_LIMITS["max"] | ||
|
||
async def async_set_temperature(self, **kwargs): | ||
"""Set new target temperatures.""" | ||
if kwargs.get(ATTR_TEMPERATURE) is not None: | ||
await self.async_set_target_temperature(kwargs.get(ATTR_TEMPERATURE)) | ||
|
||
async def async_set_target_temperature(self, target_temperature): | ||
target_temperature = int(round(target_temperature)) | ||
|
||
limits = self._TEMPERATURE_LIMITS | ||
if not limits["min"] <= target_temperature <= limits["max"]: | ||
raise ValueError( | ||
f"Target temperature ({target_temperature}) must be between " | ||
f'{limits["min"]} and {limits["max"]}' | ||
) | ||
|
||
await self._device.async_set_property( | ||
PROPERTY_TO_DPS_ID[ATTR_TARGET_TEMPERATURE], target_temperature | ||
) | ||
|
||
@property | ||
def current_temperature(self): | ||
"""Return the current temperature.""" | ||
return self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_TEMPERATURE]) | ||
|
||
@property | ||
def hvac_mode(self): | ||
"""Return current HVAC mode, ie Heat or Off.""" | ||
dps_mode = self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_HVAC_MODE]) | ||
|
||
if dps_mode is not None: | ||
return GoldairTuyaDevice.get_key_for_value(HVAC_MODE_TO_DPS_MODE, dps_mode) | ||
else: | ||
return STATE_UNAVAILABLE | ||
|
||
@property | ||
def hvac_modes(self): | ||
"""Return the list of available HVAC modes.""" | ||
return list(HVAC_MODE_TO_DPS_MODE.keys()) | ||
|
||
async def async_set_hvac_mode(self, hvac_mode): | ||
"""Set new HVAC mode.""" | ||
dps_mode = HVAC_MODE_TO_DPS_MODE[hvac_mode] | ||
await self._device.async_set_property( | ||
PROPERTY_TO_DPS_ID[ATTR_HVAC_MODE], dps_mode | ||
) | ||
|
||
@property | ||
def device_state_attributes(self): | ||
"""Get additional attributes that HA doesn't naturally support.""" | ||
error = self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_ERROR]) | ||
|
||
return {ATTR_ERROR: error or None} | ||
|
||
async def async_update(self): | ||
await self._device.async_refresh() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
from homeassistant.components.climate.const import ( | ||
ATTR_HVAC_MODE, | ||
ATTR_PRESET_MODE, | ||
HVAC_MODE_HEAT, | ||
HVAC_MODE_OFF, | ||
) | ||
from homeassistant.const import ATTR_TEMPERATURE | ||
|
||
ATTR_TARGET_TEMPERATURE = "target_temperature" | ||
ATTR_CHILD_LOCK = "child_lock" | ||
ATTR_ERROR = "error" | ||
|
||
PROPERTY_TO_DPS_ID = { | ||
ATTR_HVAC_MODE: "1", | ||
ATTR_CHILD_LOCK: "2", | ||
ATTR_TARGET_TEMPERATURE: "3", | ||
ATTR_TEMPERATURE: "4", | ||
ATTR_ERROR: "6", | ||
} | ||
|
||
HVAC_MODE_TO_DPS_MODE = {HVAC_MODE_OFF: False, HVAC_MODE_HEAT: True} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
""" | ||
Platform to control the child lock on Goldair GECO WiFi-connected heaters and panels. | ||
""" | ||
from homeassistant.components.lock import STATE_LOCKED, STATE_UNLOCKED, LockDevice | ||
from homeassistant.const import STATE_UNAVAILABLE | ||
|
||
from ..device import GoldairTuyaDevice | ||
from .const import ATTR_CHILD_LOCK, PROPERTY_TO_DPS_ID | ||
|
||
|
||
class GoldairGECOHeaterChildLock(LockDevice): | ||
"""Representation of a Goldair GECO WiFi-connected heater child lock.""" | ||
|
||
def __init__(self, device): | ||
"""Initialize the lock. | ||
Args: | ||
device (GoldairTuyaDevice): The device API instance.""" | ||
self._device = device | ||
|
||
@property | ||
def should_poll(self): | ||
"""Return the polling state.""" | ||
return True | ||
|
||
@property | ||
def name(self): | ||
"""Return the name of the lock.""" | ||
return self._device.name | ||
|
||
@property | ||
def unique_id(self): | ||
"""Return the unique id for this heater child lock.""" | ||
return self._device.unique_id | ||
|
||
@property | ||
def device_info(self): | ||
"""Return device information about this heater child lock.""" | ||
return self._device.device_info | ||
|
||
@property | ||
def state(self): | ||
"""Return the current state.""" | ||
if self.is_locked is None: | ||
return STATE_UNAVAILABLE | ||
else: | ||
return STATE_LOCKED if self.is_locked else STATE_UNLOCKED | ||
|
||
@property | ||
def is_locked(self): | ||
"""Return the a boolean representing whether the child lock is on or not.""" | ||
return self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_CHILD_LOCK]) | ||
|
||
async def async_lock(self, **kwargs): | ||
"""Turn on the child lock.""" | ||
await self._device.async_set_property(PROPERTY_TO_DPS_ID[ATTR_CHILD_LOCK], True) | ||
|
||
async def async_unlock(self, **kwargs): | ||
"""Turn off the child lock.""" | ||
await self._device.async_set_property( | ||
PROPERTY_TO_DPS_ID[ATTR_CHILD_LOCK], False | ||
) | ||
|
||
async def async_update(self): | ||
await self._device.async_refresh() |
Empty file.
Oops, something went wrong.