diff --git a/homeassistant/components/lightwave/__init__.py b/homeassistant/components/lightwave/__init__.py index 4a27d4a7f4a..73002908ff3 100644 --- a/homeassistant/components/lightwave/__init__.py +++ b/homeassistant/components/lightwave/__init__.py @@ -2,20 +2,30 @@ from lightwave.lightwave import LWLink import voluptuous as vol +from homeassistant.components.climate import DOMAIN as CLIMATE_DOMAIN +from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN from homeassistant.const import CONF_HOST, CONF_LIGHTS, CONF_NAME, CONF_SWITCHES import homeassistant.helpers.config_validation as cv from homeassistant.helpers.discovery import async_load_platform -LIGHTWAVE_LINK = "lightwave_link" - +CONF_SERIAL = "serial" +CONF_PROXY_IP = "proxy_ip" +CONF_PROXY_PORT = "proxy_port" +CONF_TRV = "trv" +CONF_TRVS = "trvs" +DEFAULT_PROXY_PORT = 7878 +DEFAULT_PROXY_IP = "127.0.0.1" DOMAIN = "lightwave" +LIGHTWAVE_LINK = f"{DOMAIN}_link" +LIGHTWAVE_TRV_PROXY = f"{DOMAIN}_proxy" +LIGHTWAVE_TRV_PROXY_PORT = f"{DOMAIN}_proxy_port" CONFIG_SCHEMA = vol.Schema( { DOMAIN: vol.Schema( vol.All( - cv.has_at_least_one_key(CONF_LIGHTS, CONF_SWITCHES), + cv.has_at_least_one_key(CONF_LIGHTS, CONF_SWITCHES, CONF_TRV), { vol.Required(CONF_HOST): cv.string, vol.Optional(CONF_LIGHTS, default={}): { @@ -24,6 +34,22 @@ CONFIG_SCHEMA = vol.Schema( vol.Optional(CONF_SWITCHES, default={}): { cv.string: vol.Schema({vol.Required(CONF_NAME): cv.string}) }, + vol.Optional(CONF_TRV, default={}): { + vol.Optional( + CONF_PROXY_PORT, default=DEFAULT_PROXY_PORT + ): cv.port, + vol.Optional( + CONF_PROXY_IP, default=DEFAULT_PROXY_IP + ): cv.string, + vol.Required(CONF_TRVS, default={}): { + cv.string: vol.Schema( + { + vol.Required(CONF_NAME): cv.string, + vol.Required(CONF_SERIAL): cv.string, + } + ) + }, + }, }, ) ) @@ -34,9 +60,9 @@ CONFIG_SCHEMA = vol.Schema( async def async_setup(hass, config): """Try to start embedded Lightwave broker.""" - host = config[DOMAIN][CONF_HOST] - hass.data[LIGHTWAVE_LINK] = LWLink(host) + lwlink = LWLink(host) + hass.data[LIGHTWAVE_LINK] = lwlink lights = config[DOMAIN][CONF_LIGHTS] if lights: @@ -50,4 +76,17 @@ async def async_setup(hass, config): async_load_platform(hass, "switch", DOMAIN, switches, config) ) + trv = config[DOMAIN][CONF_TRV] + if trv: + trvs = trv[CONF_TRVS] + proxy_ip = trv[CONF_PROXY_IP] + proxy_port = trv[CONF_PROXY_PORT] + lwlink.set_trv_proxy(proxy_ip, proxy_port) + + platforms = [CLIMATE_DOMAIN, SENSOR_DOMAIN] + for platform in platforms: + hass.async_create_task( + async_load_platform(hass, platform, DOMAIN, trvs, config) + ) + return True diff --git a/homeassistant/components/lightwave/climate.py b/homeassistant/components/lightwave/climate.py new file mode 100644 index 00000000000..8e842624b16 --- /dev/null +++ b/homeassistant/components/lightwave/climate.py @@ -0,0 +1,142 @@ +"""Support for LightwaveRF TRVs.""" +from homeassistant.components.climate import ( + DEFAULT_MAX_TEMP, + DEFAULT_MIN_TEMP, + HVAC_MODE_HEAT, + HVAC_MODE_OFF, + SUPPORT_TARGET_TEMPERATURE, + ClimateDevice, +) +from homeassistant.components.climate.const import CURRENT_HVAC_HEAT, CURRENT_HVAC_OFF +from homeassistant.const import ATTR_TEMPERATURE, CONF_NAME, TEMP_CELSIUS + +from . import CONF_SERIAL, LIGHTWAVE_LINK + + +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): + """Find and return LightWave lights.""" + if discovery_info is None: + return + + entities = [] + lwlink = hass.data[LIGHTWAVE_LINK] + + for device_id, device_config in discovery_info.items(): + name = device_config[CONF_NAME] + serial = device_config[CONF_SERIAL] + entities.append(LightwaveTrv(name, device_id, lwlink, serial)) + + async_add_entities(entities) + + +class LightwaveTrv(ClimateDevice): + """Representation of a LightWaveRF TRV.""" + + def __init__(self, name, device_id, lwlink, serial): + """Initialize LightwaveTrv entity.""" + self._name = name + self._device_id = device_id + self._state = None + self._current_temperature = None + self._target_temperature = None + self._hvac_action = None + self._lwlink = lwlink + self._serial = serial + # inhibit is used to prevent race condition on update. If non zero, skip next update cycle. + self._inhibit = 0 + + @property + def supported_features(self): + """Flag supported features.""" + return SUPPORT_TARGET_TEMPERATURE + + def update(self): + """Communicate with a Lightwave RTF Proxy to get state.""" + (temp, targ, _, trv_output) = self._lwlink.read_trv_status(self._serial) + if temp is not None: + self._current_temperature = temp + if targ is not None: + if self._inhibit == 0: + self._target_temperature = targ + if targ == 0: + # TRV off + self._target_temperature = None + if targ >= 40: + # Call for heat mode, or TRV in a fixed position + self._target_temperature = None + else: + # Done the job - use proxy next iteration + self._inhibit = 0 + if trv_output is not None: + if trv_output > 0: + self._hvac_action = CURRENT_HVAC_HEAT + else: + self._hvac_action = CURRENT_HVAC_OFF + + @property + def name(self): + """Lightwave trv name.""" + return self._name + + @property + def current_temperature(self): + """Property giving the current room temperature.""" + return self._current_temperature + + @property + def target_temperature(self): + """Target room temperature.""" + if self._inhibit > 0: + # If we get an update before the new temp has + # propagated, the target temp is set back to the + # old target on the next poll, showing a false + # reading temporarily. + self._target_temperature = self._inhibit + return self._target_temperature + + @property + def hvac_modes(self): + """HVAC modes.""" + return [HVAC_MODE_HEAT, HVAC_MODE_OFF] + + @property + def hvac_mode(self): + """HVAC mode.""" + return HVAC_MODE_HEAT + + @property + def hvac_action(self): + """HVAC action.""" + return self._hvac_action + + @property + def min_temp(self): + """Min Temp.""" + return DEFAULT_MIN_TEMP + + @property + def max_temp(self): + """Max Temp.""" + return DEFAULT_MAX_TEMP + + @property + def temperature_unit(self): + """Set temperature unit.""" + return TEMP_CELSIUS + + @property + def target_temperature_step(self): + """Set temperature step.""" + return 0.5 + + def set_temperature(self, **kwargs): + """Set TRV target temperature.""" + if ATTR_TEMPERATURE in kwargs: + self._target_temperature = kwargs[ATTR_TEMPERATURE] + self._inhibit = self._target_temperature + self._lwlink.set_temperature( + self._device_id, self._target_temperature, self._name + ) + + async def async_set_hvac_mode(self, hvac_mode): + """Set HVAC Mode for TRV.""" diff --git a/homeassistant/components/lightwave/manifest.json b/homeassistant/components/lightwave/manifest.json index ffda0e96c12..a80136b3fec 100644 --- a/homeassistant/components/lightwave/manifest.json +++ b/homeassistant/components/lightwave/manifest.json @@ -2,6 +2,6 @@ "domain": "lightwave", "name": "Lightwave", "documentation": "https://www.home-assistant.io/integrations/lightwave", - "requirements": ["lightwave==0.17"], + "requirements": ["lightwave==0.18"], "codeowners": [] } diff --git a/homeassistant/components/lightwave/sensor.py b/homeassistant/components/lightwave/sensor.py new file mode 100644 index 00000000000..f018a0c17c7 --- /dev/null +++ b/homeassistant/components/lightwave/sensor.py @@ -0,0 +1,60 @@ +"""Support for LightwaveRF TRV - Associated Battery.""" +from homeassistant.const import CONF_NAME, DEVICE_CLASS_BATTERY, UNIT_PERCENTAGE +from homeassistant.helpers.entity import Entity + +from . import CONF_SERIAL, LIGHTWAVE_LINK + + +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): + """Find and return battery.""" + if discovery_info is None: + return + + batteries = [] + + lwlink = hass.data[LIGHTWAVE_LINK] + + for device_config in discovery_info.values(): + name = device_config[CONF_NAME] + serial = device_config[CONF_SERIAL] + batteries.append(LightwaveBattery(name, lwlink, serial)) + + async_add_entities(batteries) + + +class LightwaveBattery(Entity): + """Lightwave TRV Battery.""" + + def __init__(self, name, lwlink, serial): + """Initialize the Lightwave Trv battery sensor.""" + self._name = name + self._state = None + self._lwlink = lwlink + self._serial = serial + + @property + def device_class(self): + """Return the device class of the sensor.""" + return DEVICE_CLASS_BATTERY + + @property + def name(self): + """Return the name of the sensor.""" + return self._name + + @property + def state(self): + """Return the state of the sensor.""" + return self._state + + @property + def unit_of_measurement(self): + """Return the state of the sensor.""" + return UNIT_PERCENTAGE + + def update(self): + """Communicate with a Lightwave RTF Proxy to get state.""" + (dummy_temp, dummy_targ, battery, dummy_output) = self._lwlink.read_trv_status( + self._serial + ) + self._state = battery diff --git a/requirements_all.txt b/requirements_all.txt index b025253cf77..e992c3164f3 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -818,7 +818,7 @@ liffylights==0.9.4 lightify==1.0.7.2 # homeassistant.components.lightwave -lightwave==0.17 +lightwave==0.18 # homeassistant.components.limitlessled limitlessled==1.1.3