-
-
Notifications
You must be signed in to change notification settings - Fork 38.2k
New venstar climate component #11639
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
Changes from 1 commit
138f08f
b3981fa
8dc5e9f
126b4b2
ad5f55e
340b52f
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,287 @@ | ||
| """ | ||
| Support for Venstar WiFi Thermostats. | ||
|
|
||
| For more details about this platform, please refer to the documentation at | ||
| https://home-assistant.io/components/climate.venstar/ | ||
| """ | ||
| import logging | ||
| import voluptuous as vol | ||
|
|
||
| from homeassistant.components.climate import ( | ||
| STATE_COOL, STATE_HEAT, STATE_IDLE, STATE_AUTO, | ||
| ClimateDevice, PLATFORM_SCHEMA, | ||
| SUPPORT_TARGET_TEMPERATURE, SUPPORT_TARGET_HUMIDITY, | ||
| SUPPORT_FAN_MODE, SUPPORT_OPERATION_MODE, | ||
| SUPPORT_TARGET_TEMPERATURE_HIGH, | ||
| SUPPORT_TARGET_TEMPERATURE_LOW, | ||
| ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW) | ||
|
|
||
| from homeassistant.const import ( | ||
| CONF_HOST, CONF_PASSWORD, CONF_SSL, CONF_USERNAME, CONF_TIMEOUT, | ||
| TEMP_FAHRENHEIT, ATTR_TEMPERATURE, TEMP_CELSIUS, | ||
| PRECISION_WHOLE, STATE_ON) | ||
|
|
||
| import homeassistant.helpers.config_validation as cv | ||
|
|
||
| REQUIREMENTS = ['venstarcolortouch==0.3'] | ||
|
|
||
| _LOGGER = logging.getLogger(__name__) | ||
|
|
||
| DEFAULT_SSL = False | ||
|
|
||
| PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ | ||
| vol.Required(CONF_HOST): cv.string, | ||
| vol.Optional(CONF_USERNAME, default=None): cv.string, | ||
| vol.Optional(CONF_PASSWORD, default=None): cv.string, | ||
| vol.Optional(CONF_SSL, default=DEFAULT_SSL): cv.boolean, | ||
| vol.Optional(CONF_TIMEOUT, default=5): | ||
| vol.All(vol.Coerce(int), vol.Range(min=1)) | ||
| }) | ||
|
|
||
|
|
||
| def setup_platform(hass, config, add_devices, discovery_info=None): | ||
| """Set up the Venstar thermostat.""" | ||
| username = config.get(CONF_USERNAME) | ||
| password = config.get(CONF_PASSWORD) | ||
| host = config.get(CONF_HOST) | ||
| if config.get(CONF_SSL): | ||
| proto = 'https' | ||
| else: | ||
| proto = 'http' | ||
| timeout = config.get(CONF_TIMEOUT) | ||
|
|
||
| import venstarcolortouch | ||
| client = venstarcolortouch.VenstarColorTouch(addr=host, | ||
| timeout=timeout, | ||
| user=username, | ||
| password=password, | ||
| proto=proto) | ||
|
|
||
| add_devices([VenstarThermostat(client)]) | ||
|
Contributor
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. If you call this with |
||
|
|
||
|
|
||
| class VenstarThermostat(ClimateDevice): | ||
| """Representation of a Venstar thermostat.""" | ||
|
|
||
| def __init__(self, client): | ||
| """Initialize the thermostat.""" | ||
| self._client = client | ||
| self._client.update_info() | ||
|
Contributor
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 guess this does I/O? Please don't do that in the constructor. Instead, see above: call |
||
| self._client.update_sensors() | ||
| self._fan_list = [STATE_ON, STATE_AUTO] | ||
| self._operation_list = [STATE_HEAT, STATE_COOL, STATE_IDLE, STATE_AUTO] | ||
|
Contributor
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. For extra cleanliness, you could extract those two lists into a tuple constant somewhere at the start of this file. |
||
|
|
||
| def update(self): | ||
| """Update the data from the thermostat.""" | ||
| _LOGGER.info("Refreshing data from your Venstar Thermostat.") | ||
|
Contributor
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. HA will automatically log whenever the state of your device changes. I'd recommend to remove this logging to reduce the logspam.
Contributor
Author
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. okay- missed that one. Will remove. |
||
| info_success = self._client.update_info() | ||
| sensor_success = self._client.update_sensors() | ||
| if not info_success or not sensor_success: | ||
| _LOGGER.error("Failed to update data from your Thermostat.") | ||
|
|
||
| # Thermostat config | ||
| @property | ||
| def supported_features(self): | ||
| """Return the list of supported features.""" | ||
| if self._client.mode == self._client.MODE_AUTO: | ||
|
Contributor
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. What does this mean? When the device is in
Contributor
Author
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. That's correct, yes - the thermostat physically has 4 modes:
-cool- you set a single temperature, and the thermostat will attempt to cool the house DOWN TO this temp -auto- you set 2 temperatures, and the thermostat will automatically swap between heating and cooling depending upon the temperature of the house. It's essentially the heat/cool mode combined. -off/idle - no hold at all, but reporting on temp I don't expect to use the auto setting, but felt i needed to do something for it in case someone else did. |
||
| return (SUPPORT_TARGET_TEMPERATURE | | ||
| SUPPORT_TARGET_TEMPERATURE_HIGH | | ||
| SUPPORT_TARGET_TEMPERATURE_LOW | | ||
| SUPPORT_TARGET_HUMIDITY | | ||
| SUPPORT_FAN_MODE | | ||
| SUPPORT_OPERATION_MODE) | ||
|
|
||
| else: | ||
| return (SUPPORT_TARGET_TEMPERATURE | | ||
| SUPPORT_TARGET_HUMIDITY | | ||
| SUPPORT_FAN_MODE | | ||
| SUPPORT_OPERATION_MODE) | ||
|
|
||
| @property | ||
| def name(self): | ||
| """Return the name of the thermostat.""" | ||
| return self._client.name | ||
|
|
||
| @property | ||
| def precision(self): | ||
| """Return the precision of the system. | ||
|
|
||
| Venstar temperature values are passed back and forth in the | ||
| API as whole degrees C or F. | ||
| """ | ||
| return PRECISION_WHOLE | ||
|
|
||
| @property | ||
| def temperature_unit(self): | ||
| """Return the unit of measurement, as defined by the API.""" | ||
| if self._client.tempunits == self._client.TEMPUNITS_F: | ||
| return TEMP_FAHRENHEIT | ||
| else: | ||
| return TEMP_CELSIUS | ||
|
|
||
| @property | ||
| def fan_list(self): | ||
| """Return the list of available fan modes.""" | ||
| return self._fan_list | ||
|
|
||
| @property | ||
| def operation_list(self): | ||
| """Return the list of available operation modes.""" | ||
| return self._operation_list | ||
|
|
||
| # Current Values | ||
| @property | ||
| def current_temperature(self): | ||
| """Return the current temperature.""" | ||
| return self._client.get_indoor_temp() | ||
|
Contributor
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. Does this do I/O? Or is the only I/O happening in the
Contributor
Author
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/O here- only on update_sensor/update_info. |
||
|
|
||
| @property | ||
| def current_humidity(self): | ||
| """Return the current humidity.""" | ||
| return self._client.get_indoor_humidity() | ||
|
|
||
| @property | ||
| def current_operation(self): | ||
| """Return current operation ie. heat, cool, idle.""" | ||
| if self._client.mode == self._client.MODE_HEAT: | ||
| return STATE_HEAT | ||
| elif self._client.mode == self._client.MODE_COOL: | ||
| return STATE_COOL | ||
| elif self._client.mode == self._client.MODE_AUTO: | ||
| return STATE_AUTO | ||
| else: | ||
| return STATE_IDLE | ||
|
Contributor
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'm not really sure what
Contributor
Author
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. It's a little confusing because the thermostat is still "on". It's screen is on, it's still monitoring/reporting temperature/humidity, still responding to API calls, etc. But your assumption is correct there- it will not turn on the air conditioner, nor the furnace to attempt to keep a certain temperature. It looks like in the honeywell component they're referring to this state as "idle" (see line 286 from honeywel.py). Looks like nuheat is also.
Contributor
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. Okay… we should probably clean that up at some point, but for now this is probably fine. :)
Contributor
Author
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. Oh- no problem if you'd rather use the "off" state to represent this. I'll go ahead and do that. |
||
|
|
||
| @property | ||
| def current_fan_mode(self): | ||
|
Contributor
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 looks like there is no way of setting the fan to
Contributor
Author
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. Yes that's correct- only AUTO/ON are valid. |
||
| """Return the fan setting.""" | ||
| if self._client.fan == self._client.FAN_AUTO: | ||
| return STATE_AUTO | ||
| else: | ||
| return STATE_ON | ||
|
|
||
| @property | ||
| def state_attributes(self): | ||
|
Contributor
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. Please don't override this. Instead, implement |
||
| """Extend the pre-packaged state attributes for extra info.""" | ||
| orig_data = super(VenstarThermostat, self).state_attributes | ||
| orig_data['fan_state'] = self._client.fanstate | ||
| orig_data['hvac_state'] = self._client.state | ||
| return orig_data | ||
|
|
||
| # Target Values | ||
| @property | ||
| def target_temperature(self): | ||
| """Return the target temperature we try to reach.""" | ||
| if self._client.mode == self._client.MODE_HEAT: | ||
| return self._client.heattemp | ||
| elif self._client.mode == self._client.MODE_COOL: | ||
| return self._client.cooltemp | ||
| else: | ||
| return None | ||
|
|
||
| @property | ||
| def target_temperature_low(self): | ||
| """Return the lower bound temp if auto mode is on.""" | ||
| if self._client.mode == self._client.MODE_AUTO: | ||
| return self._client.heattemp | ||
| else: | ||
| return None | ||
|
|
||
| @property | ||
| def target_temperature_high(self): | ||
| """Return the upper bound temp if auto mode is on.""" | ||
| if self._client.mode == self._client.MODE_AUTO: | ||
| return self._client.cooltemp | ||
| else: | ||
| return None | ||
|
|
||
| @property | ||
| def target_humidity(self): | ||
| """Return the humidity we try to reach.""" | ||
| return self._client.hum_setpoint | ||
|
|
||
| @property | ||
| def min_humidity(self): | ||
| """Return the minimum humidity. Hardcoded to 0 in API.""" | ||
| return 0 | ||
|
|
||
| @property | ||
| def max_humidity(self): | ||
| """Return the maximum humidity. Hardcoded to 60 in API.""" | ||
| return 60 | ||
|
|
||
| # Commands | ||
| def set_temperature(self, **kwargs): | ||
|
Contributor
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 |
||
| """Set a new target temperature.""" | ||
| if self._client.mode == self._client.MODE_AUTO: | ||
| temp_low = kwargs.get(ATTR_TARGET_TEMP_LOW) | ||
| temp_high = kwargs.get(ATTR_TARGET_TEMP_HIGH) | ||
| else: | ||
| temperature = kwargs.get(ATTR_TEMPERATURE) | ||
|
|
||
| if self._client.mode == self._client.MODE_HEAT: | ||
| _LOGGER.info("Currently operating in heat mode. " | ||
|
Contributor
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. That is a lot of logging… HA will automatically print a log line when your entity changes its state. So, I'd suggest removing this. |
||
| "Setting target heat temperature to %s %s. " | ||
| "The cool temp is at: %s", | ||
| temperature, | ||
| self.temperature_unit, | ||
| self._client.cooltemp) | ||
| success = self._client.set_setpoints(temperature, | ||
| self._client.cooltemp) | ||
| elif self._client.mode == self._client.MODE_COOL: | ||
| _LOGGER.info("Currently operating in cool mode. " | ||
| "Setting target cool temperature to %s %s.", | ||
| temperature, self.temperature_unit) | ||
| success = self._client.set_setpoints(self._client.heattemp, | ||
| temperature) | ||
| elif self._client.mode == self._client.MODE_AUTO: | ||
| _LOGGER.info("Current in auto mode. " | ||
| "Setting temp range to %s - %s %s.", | ||
| temp_low, temp_high, self.temperature_unit) | ||
| success = self._client.set_setpoints(temp_low, temp_high) | ||
| else: | ||
| _LOGGER.error("The thermostat is currently not " | ||
| "in a mode that supports target temperature.") | ||
|
|
||
| if not success: | ||
| _LOGGER.error("Failed to change the " | ||
| "temperature of your thermostat.") | ||
|
|
||
| def set_fan_mode(self, fan): | ||
| """Set new target fan mode.""" | ||
| _LOGGER.info("Contacting your thermostat to " | ||
|
Contributor
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. Again, I think that's too much logging. |
||
| "set the fan state to %s.", fan) | ||
| if fan == STATE_ON: | ||
| success = self._client.set_fan(self._client.FAN_ON) | ||
| else: | ||
| success = self._client.set_fan(self._client.FAN_AUTO) | ||
|
|
||
| if not success: | ||
| _LOGGER.error("Failed to change the fan mode of your thermostat.") | ||
|
|
||
| def set_operation_mode(self, operation_mode): | ||
| """Set new target operation mode.""" | ||
| _LOGGER.info("Contacting your thermostat to set " | ||
|
Contributor
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 above. |
||
| "it's operating mode to %s.", operation_mode) | ||
| if operation_mode == STATE_HEAT: | ||
| success = self._client.set_mode(self._client.MODE_HEAT) | ||
| elif operation_mode == STATE_COOL: | ||
| success = self._client.set_mode(self._client.MODE_COOL) | ||
| elif operation_mode == STATE_AUTO: | ||
| success = self._client.set_mode(self._client.MODE_AUTO) | ||
| else: | ||
| success = self._client.set_mode(self._client.MODE_OFF) | ||
|
|
||
| if not success: | ||
| _LOGGER.error("Failed to change the " | ||
| "operation mode of your thermostat.") | ||
|
|
||
| def set_humidity(self, humidity): | ||
| """Set new target humidity.""" | ||
| _LOGGER.info("Contacting your thermostat to set it's " | ||
|
Contributor
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 above. |
||
| "target humidity to %s", humidity) | ||
| success = self._client.set_hum_setpoint(humidity) | ||
|
|
||
| if not success: | ||
| _LOGGER.error("Failed to change the target " | ||
| "humidity level of your thermostat.") | ||
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.
I don't think you need to set
default=None. That's the default's default.