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 humidity control and 2-stage cooling support #8

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
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
94 changes: 81 additions & 13 deletions custom_components/control4/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,21 @@
from homeassistant.components.climate.const import (
CURRENT_HVAC_COOL,
CURRENT_HVAC_HEAT,
CURRENT_HVAC_DRY,
CURRENT_HVAC_IDLE,
HVAC_MODE_OFF,
HVAC_MODE_HEAT,
HVAC_MODE_COOL,
HVAC_MODE_HEAT_COOL,
HVAC_MODE_DRY,
ATTR_TARGET_TEMP_HIGH,
ATTR_TARGET_TEMP_LOW,
ATTR_CURRENT_HUMIDITY,
ATTR_CURRENT_TEMPERATURE,
ATTR_HUMIDITY,
SUPPORT_TARGET_TEMPERATURE,
SUPPORT_TARGET_TEMPERATURE_RANGE
SUPPORT_TARGET_TEMPERATURE_RANGE,
SUPPORT_TARGET_HUMIDITY
)
from homeassistant.const import (
CONF_NAME,
Expand All @@ -45,18 +50,23 @@
CONF_PROXY_ID = 'proxy_id'
CONF_WEB_TWO_WAY_PORT = 'web_two_way_port'
CONF_WEB_EVENT_PORT = 'web_event_port'
CONF_DEHUMIDIFY = 'dehumidify'

DEFAULT_NAME = 'Control4 Thermostat'
DEFAULT_WEB_TWO_WAY_PORT = 9000
DEFAULT_WEB_EVENT_PORT = 8080
DEFAULT_TIMEOUT = 10

STATE_VARIABLE_ID = '1107'
HUMIDITY_STATE_VARIABLE_ID = '1141'
MODE_VARIABLE_ID = '1104'
HUMIDITY_MODE_VARIABLE_ID = '1139'
CURRENT_TEMP_VARIABLE_ID = '1130'
CURRENT_HUMIDITY_VARIABLE_ID = '1138'
#UNIT_VARIABLE_ID = '1100'
TARGET_TEMP_HIGH_VARIABLE_ID = '1134'
TARGET_TEMP_LOW_VARIABLE_ID = '1132'
TARGET_HUMIDITY_VARIABLE_ID = '1143'
SUPPORT_FLAGS = (SUPPORT_TARGET_TEMPERATURE_RANGE | SUPPORT_TARGET_TEMPERATURE)

MODE_MAPPING = {
Expand All @@ -66,13 +76,25 @@
"Auto": HVAC_MODE_HEAT_COOL
}

HUMIDITY_MODE_MAPPING = {
"Off": HVAC_MODE_OFF,
"Dehumidify": HVAC_MODE_DRY
}

STATE_MAPPING = {
"Off": CURRENT_HVAC_IDLE,
"" : CURRENT_HVAC_IDLE,
"Cool": CURRENT_HVAC_COOL,
"Stage 1 Cool": CURRENT_HVAC_COOL,
"Stage 1 and 2 Cool": CURRENT_HVAC_COOL,
"Heat": CURRENT_HVAC_HEAT
}

HUMIDITY_STATE_MAPPING = {
"Off": CURRENT_HVAC_IDLE,
"Dehumidifying Equipment Wait" : CURRENT_HVAC_DRY
}

UNIT_MAPPING = {
"FAHRENHEIT": TEMP_FAHRENHEIT,
"CELSIUS": TEMP_CELSIUS
Expand All @@ -84,7 +106,8 @@
vol.Optional(CONF_WEB_TWO_WAY_PORT, default=DEFAULT_WEB_TWO_WAY_PORT): cv.positive_int,
vol.Optional(CONF_WEB_EVENT_PORT, default=DEFAULT_WEB_EVENT_PORT): cv.positive_int,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int
vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int,
vol.Optional(CONF_DEHUMIDIFY, default=False): cv.boolean
})

_LOGGER = logging.getLogger(__name__)
Expand All @@ -97,26 +120,37 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
event_url = config.get(CONF_BASE_URL) + ':' + str(config.get(CONF_WEB_EVENT_PORT)) + '/'
proxy_id = config.get(CONF_PROXY_ID)
timeout = config.get(CONF_TIMEOUT)
dehumidify = config.get(CONF_DEHUMIDIFY)

async_add_devices([C4ClimateDevice(hass, name, base_url, proxy_id, timeout, event_url)])
async_add_devices([C4ClimateDevice(hass, name, base_url, proxy_id, timeout,
event_url, dehumidify)])

class C4ClimateDevice(ClimateEntity):

def __init__(self, hass, name, base_url, proxy_id, timeout, event_url):
def __init__(self, hass, name, base_url, proxy_id, timeout, event_url,
dehumidify):
self._state = CURRENT_HVAC_IDLE
self._humidity_state = CURRENT_HVAC_IDLE
self._hvac_mode = HVAC_MODE_OFF
self._humidity_hvac_mode = HVAC_MODE_OFF
self.hass = hass
self._name = name
self._base_url = base_url;
self._event_url = event_url;
self._proxy_id = proxy_id;
self._timeout = timeout
self._current_temp = 0
self._current_humidity = 0
self._target_temp_high = 0
self._target_temp_low = 0
self._target_temp = 0
self._target_humidity = 0
self._unit = TEMP_FAHRENHEIT
self._hvac_modes = [HVAC_MODE_OFF, HVAC_MODE_HEAT, HVAC_MODE_COOL, HVAC_MODE_HEAT_COOL]
self._dehumidify = dehumidify
self._hvac_modes = [HVAC_MODE_OFF, HVAC_MODE_HEAT, HVAC_MODE_COOL,
HVAC_MODE_HEAT_COOL]
if self._dehumidify:
self._hvac_modes.append(HVAC_MODE_DRY)

@property
def name(self):
Expand All @@ -125,12 +159,16 @@ def name(self):
@property
def supported_features(self):
"""Return the list of supported features."""
if self._hvac_mode == HVAC_MODE_HEAT or self._hvac_mode == HVAC_MODE_COOL:
return SUPPORT_TARGET_TEMPERATURE
support_flags = 0
if self._hvac_mode == HVAC_MODE_HEAT or self._hvac_mode == HVAC_MODE_COOL or self._humidity_hvac_mode == HVAC_MODE_DRY:
support_flags = support_flags | SUPPORT_TARGET_TEMPERATURE
elif self._hvac_mode == HVAC_MODE_HEAT_COOL:
return SUPPORT_TARGET_TEMPERATURE_RANGE
support_flags = support_flags | SUPPORT_TARGET_TEMPERATURE_RANGE
else:
return SUPPORT_FLAGS
support_flags = support_flags | SUPPORT_TARGET_TEMPERATURE_RANGE
if self._dehumidify:
support_flags = support_flags | SUPPORT_TARGET_HUMIDITY
return support_flags

@property
def hvac_modes(self):
Expand All @@ -155,6 +193,8 @@ def target_temperature(self):

@property
def hvac_mode(self):
if self._dehumidify and self._hvac_mode == HVAC_MODE_OFF:
return self._humidity_hvac_mode
return self._hvac_mode

@property
Expand All @@ -163,6 +203,8 @@ def precision(self):

@property
def hvac_action(self):
if self._dehumidify and self._state == CURRENT_HVAC_IDLE:
return self._humidity_state
return self._state

@property
Expand All @@ -173,11 +215,19 @@ def target_temperature_high(self):
def target_temperature_low(self):
return self._target_temp_low

@property
def current_humidity(self):
return self._current_humidity

@property
def target_humidity(self):
return self._target_humidity

def set_temperature(self, **kwargs):
temp_low = kwargs.get(ATTR_TARGET_TEMP_LOW)
temp_high = kwargs.get(ATTR_TARGET_TEMP_HIGH)
single_temp = kwargs.get(ATTR_TEMPERATURE)

if single_temp is not None:
_LOGGER.debug('Single Temp Update Mode: ' + str(single_temp))
if self._hvac_mode == HVAC_MODE_HEAT:
Expand All @@ -196,6 +246,11 @@ def set_temperature(self, **kwargs):
_LOGGER.warning('Invalid Temperature Values Passed to Update Method.')
return

def set_humidity(self, humidity):
_LOGGER.debug('Humidity Update Mode: ' + str(humidity))
asyncio.run_coroutine_threadsafe(self.update_state(TARGET_HUMIDITY_VARIABLE_ID, int(humidity)), self.hass.loop).result()
self._target_humidity = humidity

# The following method is broken with the 2 way web driver, I've implemented the web event
# driver, and programming in COntrol4 to work around this. The 2-way web driver cannot update
# the HVAC mode in control4, it appears to be an underlying issue with LUA
Expand Down Expand Up @@ -277,9 +332,11 @@ def async_update(self):
params = {
'command': 'get',
'proxyID': self._proxy_id,
'variableID': ','.join([STATE_VARIABLE_ID, MODE_VARIABLE_ID,
CURRENT_TEMP_VARIABLE_ID, TARGET_TEMP_HIGH_VARIABLE_ID,
TARGET_TEMP_LOW_VARIABLE_ID])
'variableID': ','.join([STATE_VARIABLE_ID,
HUMIDITY_STATE_VARIABLE_ID, MODE_VARIABLE_ID,
HUMIDITY_MODE_VARIABLE_ID, CURRENT_TEMP_VARIABLE_ID,
CURRENT_HUMIDITY_VARIABLE_ID, TARGET_TEMP_HIGH_VARIABLE_ID,
TARGET_TEMP_LOW_VARIABLE_ID, TARGET_HUMIDITY_VARIABLE_ID])
}
url = self.get_url(self._base_url, params)

Expand All @@ -300,8 +357,11 @@ def async_update(self):

try:
self._state = STATE_MAPPING[json_text[STATE_VARIABLE_ID]]
self._humidity_state = HUMIDITY_STATE_MAPPING[json_text[HUMIDITY_STATE_VARIABLE_ID]]
self._hvac_mode = MODE_MAPPING[json_text[MODE_VARIABLE_ID]]
self._humidity_hvac_mode = HUMIDITY_MODE_MAPPING[json_text[HUMIDITY_MODE_VARIABLE_ID]]
self._current_temp = int(json_text[CURRENT_TEMP_VARIABLE_ID])
self._current_humidity = int(json_text[CURRENT_HUMIDITY_VARIABLE_ID])
self._target_temp_high = int(json_text[TARGET_TEMP_HIGH_VARIABLE_ID])
self._target_temp_low = int(json_text[TARGET_TEMP_LOW_VARIABLE_ID])
# self._unit = UNIT_MAPPING[json_text[UNIT_VARIABLE_ID]]
Expand All @@ -310,7 +370,15 @@ def async_update(self):
self._target_temp = int(json_text[TARGET_TEMP_LOW_VARIABLE_ID])
elif self._hvac_mode == HVAC_MODE_COOL:
self._target_temp = int(json_text[TARGET_TEMP_HIGH_VARIABLE_ID])
elif self._humidity_hvac_mode == HVAC_MODE_DRY:
self._target_temp = int(json_text[TARGET_TEMP_HIGH_VARIABLE_ID])
else:
self._target_temp = 0

if self._humidity_hvac_mode == HVAC_MODE_DRY:
self._target_humidity = int(json_text[TARGET_HUMIDITY_VARIABLE_ID])
else:
self._target_humidity = 0

except ValueError:
_LOGGER.warning('Invalid value received')