From 4b11b0535b498c9654e864e8023db21ff23efa2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Szcz=C4=99=C5=9Bniak-Szlagowski?= Date: Thu, 11 Mar 2021 02:14:47 +0900 Subject: [PATCH 1/2] Add initial light support Tested on Nitori ceiling light only --- __init__.py | 1 + light.py | 146 ++++++++++++++++++++++++++++++++++++++++++++++++++ services.yaml | 41 ++++++++++++++ 3 files changed, 188 insertions(+) create mode 100644 light.py create mode 100644 services.yaml diff --git a/__init__.py b/__init__.py index 3a97a7c..a64235a 100644 --- a/__init__.py +++ b/__init__.py @@ -60,6 +60,7 @@ async def async_setup(hass, config): await discovery.async_load_platform(hass, "sensor", DOMAIN, {}, config) await discovery.async_load_platform(hass, "climate", DOMAIN, {}, config) + await discovery.async_load_platform(hass, "light", DOMAIN, {}, config) return True diff --git a/light.py b/light.py new file mode 100644 index 0000000..007abad --- /dev/null +++ b/light.py @@ -0,0 +1,146 @@ +"""Support for Nature Remo Light.""" +import logging +from enum import Enum + +import voluptuous as vol +from homeassistant.components.light import ( + LightEntity, + SUPPORT_BRIGHTNESS, + SUPPORT_COLOR_TEMP, +) +from homeassistant.helpers import config_validation as cv, entity_platform +from . import DOMAIN, NatureRemoBase + +_LOGGER = logging.getLogger(__name__) + +SERVICE_PRESS_LIGHT_BUTTON = "press_light_button" +SERVICE_PRESS_CUSTOM_BUTTON = "press_custom_button" + +ATTR_IS_NIGHT = "is_night" + + +class LightButton(Enum): + on = "on" + max = "on-100" + favorite = "on-favorite" + on_off = "onoff" + night = "night" + bright_up = "bright-up" + bright_down = "bright-down" + color_temp_up = "colortemp-up" + color_temp_down = "colortemp-down" + + +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): + """Set up the Nature Remo Light.""" + if discovery_info is None: + return + _LOGGER.debug("Setting up light platform.") + coordinator = hass.data[DOMAIN]["coordinator"] + api = hass.data[DOMAIN]["api"] + config = hass.data[DOMAIN]["config"] + appliances = coordinator.data["appliances"] + async_add_entities( + [ + NatureRemoLight(coordinator, api, appliance, config) + for appliance in appliances.values() + if appliance["type"] == "LIGHT" + ] + ) + platform = entity_platform.current_platform.get() + _LOGGER.debug("Registering light entity services.") + platform.async_register_entity_service( + SERVICE_PRESS_LIGHT_BUTTON, + {vol.Required("button_name"): cv.enum(LightButton)}, + NatureRemoLight.async_press_light_button, + ) + platform.async_register_entity_service( + SERVICE_PRESS_CUSTOM_BUTTON, + {vol.Required("button_name"): cv.string}, + NatureRemoLight.async_press_custom_button, + ) + + +class NatureRemoLight(NatureRemoBase, LightEntity): + """Implementation of a Nature Remo Light component.""" + + def __init__(self, coordinator, api, appliance, config): + super().__init__(coordinator, appliance) + self._api = api + self._buttons = [b["name"] for b in appliance["light"]["buttons"]] + self._signals = {s["name"]: s["id"] for s in appliance["signals"]} + self._is_on = False + self._is_night = False + + # Entity methods + + @property + def assumed_state(self): + """Return True if unable to access real state of the entity.""" + # Remo does return light.state however it doesn't seem to be correct + # in my experience. + return True + + # ToggleEntity methods + + @property + def is_on(self): + """Return True if entity is on.""" + return self._is_on + + async def async_turn_on(self, **kwargs): + """Turn device on.""" + await self._post({"button": "on"}) + self._set_on(True) + + async def async_turn_off(self, **kwargs): + """Turn device off.""" + await self._post({"button": "onoff"}) + self._set_on(False) + + # LightEntity methods + + @property + def supported_features(self): + """Flag supported features.""" + # Even though the IR remote of the light may support adjusting + # the brightness and color temperature, it's only ever by 1 grade + # and we don't know how many grades there are, so we can't even + # map the grades into anything useful. + return 0 + + @property + def state_attributes(self): + """Return state attributes.""" + if not self.is_on: + return None + + return {ATTR_IS_NIGHT: self._is_night} + + # own methods + + async def _post(self, data): + await self._api.post(f"/appliances/{self._appliance_id}/light", data) + + def _set_on(self, is_on, is_night = False): + self._is_on = is_on + self._is_night = is_night + self.async_write_ha_state() + + async def async_press_light_button(self, service_call): + button = LightButton(service_call.data["button_name"]) + await self._post({"button": button.value}) + if button == LightButton.on_off and self._is_on \ + or button == LightButton.night and self._is_night: + self._set_on(False) + else: + self._set_on(True, button == LightButton.night) + + async def async_press_custom_button(self, service_call): + signal_name = service_call.data["button_name"] + signal_id = self._signals.get(signal_name) + if signal_id is None: + _LOGGER.error(f"Invalid signal name: {signal_name}") + return + await self._api.post(f"/signals/{signal_id}/send", None) + self._set_on(True) diff --git a/services.yaml b/services.yaml new file mode 100644 index 0000000..33905d7 --- /dev/null +++ b/services.yaml @@ -0,0 +1,41 @@ +press_light_button: + name: Press light button + description: Sends the signal of a button that Remo automatically recognized. + target: + entity: + domain: light + fields: + button_name: + name: Button + description: The button that you want to press + required: true + advanced: false + example: 'on_off' + selector: + select: + options: + - 'on' + - 'max' + - 'favorite' + - 'on_off' + - 'night' + - 'bright_up' + - 'bright_down' + - 'color_temp_up' + - 'color_temp_down' + +press_custom_button: + name: Press custom button + description: Sends the signal of a button that you had to manually register. + target: + entity: + domain: light + fields: + button_name: + name: Button name + description: The name you gave the custom button in the Remo app + required: true + advanced: false + example: 'Eco' + selector: + text: From 756571d04af9b3f9d1d700f8903001b1c16c4124 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Szcz=C4=99=C5=9Bniak-Szlagowski?= Date: Thu, 1 Apr 2021 23:21:02 +0900 Subject: [PATCH 2/2] Mark light support in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b054954..f9c77de 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Yet another [Home Assistant](https://www.home-assistant.io) component for [Natur - [x] Energy Sensor (Nature Remo E/E Lite) - [x] Fetch current power usage - [ ] Switch -- [ ] Light +- [x] Light - [ ] TV - [ ] Others - [ ] Fetch sensor data