diff --git a/.github/workflows/cron.yaml b/.github/workflows/cron.yaml index db1f11e..97bc65f 100644 --- a/.github/workflows/cron.yaml +++ b/.github/workflows/cron.yaml @@ -12,7 +12,7 @@ jobs: - uses: "actions/checkout@v2" - name: HACS validation - uses: "hacs/integration/action@master" + uses: "hacs/integration/action@main" with: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CATEGORY: "integration" diff --git a/.github/workflows/pull.yml b/.github/workflows/pull.yml index 44b81a5..bea9662 100644 --- a/.github/workflows/pull.yml +++ b/.github/workflows/pull.yml @@ -11,7 +11,7 @@ jobs: - uses: "actions/checkout@v2" - name: HACS validation - uses: "hacs/integration/action@master" + uses: "hacs/integration/action@main" with: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CATEGORY: "integration" diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index f136d4c..4123136 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -14,7 +14,7 @@ jobs: - uses: "actions/checkout@v2" - name: HACS validation - uses: "hacs/integration/action@master" + uses: "hacs/integration/action@main" with: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CATEGORY: "integration" diff --git a/custom_components/govee/config_flow.py b/custom_components/govee/config_flow.py index a15a3f1..2b3f3e2 100644 --- a/custom_components/govee/config_flow.py +++ b/custom_components/govee/config_flow.py @@ -1,12 +1,16 @@ """Config flow for Govee LED strips integration.""" import logging +from logging import error from govee_api_laggat import Govee from homeassistant import config_entries, core, exceptions +from homeassistant.const import CONF_API_KEY, CONF_DELAY +from homeassistant.core import callback +import voluptuous as vol -from .const import CONF_API_KEY, DATA_SCHEMA, DOMAIN +from .const import DOMAIN, CONF_USE_ASSUMED_STATE, CONF_OFFLINE_IS_OFF _LOGGER = logging.getLogger(__name__) @@ -49,9 +53,71 @@ async def async_step_user(self, user_input=None): errors["base"] = "unknown" return self.async_show_form( - step_id="user", data_schema=DATA_SCHEMA, errors=errors + step_id="user", + data_schema=vol.Schema( + { + vol.Required(CONF_API_KEY): str, + vol.Optional(CONF_DELAY, default=10): int, + } + ), + errors=errors, ) + @staticmethod + @callback + def async_get_options_flow(config_entry): + """Get the options flow.""" + return GoveeOptionsFlowHandler(config_entry) + + +class GoveeOptionsFlowHandler(config_entries.OptionsFlow): + """Handle options.""" + + VERSION = 1 + + def __init__(self, config_entry): + """Initialize options flow.""" + self.config_entry = config_entry + self.options = dict(config_entry.options) + + async def async_step_init(self, user_input=None): + """Manage the options.""" + return await self.async_step_user() + + async def async_step_user(self, user_input=None): + """Manage the options.""" + errors = {} + if user_input is not None: + self.options.update(user_input) + return await self._update_options() + # for later - extend with options you don't want in config but option flow + # return await self.async_step_options_2() + + # TODO: check input for errors + + options_schema = vol.Schema( + { + vol.Required( + CONF_USE_ASSUMED_STATE, + default=self.config_entry.options.get(CONF_USE_ASSUMED_STATE, True), + ): bool, + vol.Required( + CONF_OFFLINE_IS_OFF, + default=self.config_entry.options.get(CONF_OFFLINE_IS_OFF, False), + ): bool + }, + ) + + return self.async_show_form( + step_id="user", + data_schema=options_schema, + errors=errors, + ) + + async def _update_options(self): + """Update config entry options.""" + return self.async_create_entry(title=DOMAIN, data=self.options) + class CannotConnect(exceptions.HomeAssistantError): """Error to indicate we cannot connect.""" diff --git a/custom_components/govee/const.py b/custom_components/govee/const.py index f37a66a..0b32581 100644 --- a/custom_components/govee/const.py +++ b/custom_components/govee/const.py @@ -1,11 +1,9 @@ """Constants for the Govee LED strips integration.""" -import voluptuous as vol - -from homeassistant.const import CONF_API_KEY, CONF_DELAY - DOMAIN = "govee" -DATA_SCHEMA = vol.Schema( - {vol.Required(CONF_API_KEY): str, vol.Optional(CONF_DELAY, default=10): int} -) +CONF_USE_ASSUMED_STATE = "use_assumed_state" +CONF_OFFLINE_IS_OFF = "offline_is_off" + +COLOR_TEMP_KELVIN_MIN = 2000 +COLOR_TEMP_KELVIN_MAX = 9000 diff --git a/custom_components/govee/light.py b/custom_components/govee/light.py index f095494..c68ea0a 100644 --- a/custom_components/govee/light.py +++ b/custom_components/govee/light.py @@ -18,7 +18,14 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from homeassistant.util import color -from .const import DOMAIN +from .const import ( + CONF_OFFLINE_IS_OFF, + CONF_USE_ASSUMED_STATE, + DOMAIN, + COLOR_TEMP_KELVIN_MIN, + COLOR_TEMP_KELVIN_MAX, +) + _LOGGER = logging.getLogger(__name__) @@ -32,7 +39,7 @@ async def async_setup_entry(hass, entry, async_add_entities): # refresh update_interval = timedelta(seconds=config[CONF_DELAY]) coordinator = GoveeDataUpdateCoordinator( - hass, _LOGGER, update_interval=update_interval + hass, _LOGGER, update_interval=update_interval, config_entry=entry ) # Fetch initial data so we have data when entities subscribe await coordinator.async_refresh() @@ -50,13 +57,10 @@ async def async_setup_entry(hass, entry, async_add_entities): class GoveeDataUpdateCoordinator(DataUpdateCoordinator): """Device state update handler.""" - def __init__( - self, - hass, - logger, - update_interval=None, - ): + def __init__(self, hass, logger, update_interval=None, *, config_entry): """Initialize global data updater.""" + self._config_entry = config_entry + super().__init__( hass, logger, @@ -65,6 +69,16 @@ def __init__( update_method=self._async_update, ) + @property + def use_assumed_state(self): + """Use assumed states.""" + return self._config_entry.options.get(CONF_USE_ASSUMED_STATE, True) + + @property + def config_offline_is_off(self): + """Interpret offline led's as off (global config).""" + return self._config_entry.options.get(CONF_OFFLINE_IS_OFF, False) + async def _async_update(self): """Fetch data.""" self.logger.debug("_async_update") @@ -78,6 +92,12 @@ async def _async_update(self): await hub.check_connection() if hub.online: + # set global options to library + if self.config_offline_is_off: + hub.config_offline_is_off = True + else: + hub.config_offline_is_off = None # allow override in learning info + # govee will change this to a single request in 2021 device_states = await hub.get_states() for device in device_states: @@ -149,8 +169,12 @@ async def async_turn_on(self, **kwargs): _, err = await self._hub.set_brightness(self._device, bright_set) elif ATTR_COLOR_TEMP in kwargs: color_temp = kwargs[ATTR_COLOR_TEMP] - _, err = await self._hub.set_color_temp(self._device, color_temp) - # color_temp is not in state + color_temp_kelvin = color.color_temperature_mired_to_kelvin(color_temp) + if color_temp_kelvin > COLOR_TEMP_KELVIN_MAX: + color_temp_kelvin = COLOR_TEMP_KELVIN_MAX + elif color_temp_kelvin < COLOR_TEMP_KELVIN_MIN: + color_temp_kelvin = COLOR_TEMP_KELVIN_MIN + _, err = await self._hub.set_color_temp(self._device, color_temp_kelvin) else: _, err = await self._hub.turn_on(self._device) # warn on any error @@ -200,8 +224,12 @@ def is_on(self): @property def assumed_state(self): - """Return true if the state is assumed.""" - return self._device.source == "history" + """ + Return true if the state is assumed. + + This can be disabled in options. + """ + return self._coordinator.use_assumed_state and self._device.source == "history" @property def available(self): @@ -232,15 +260,22 @@ def brightness(self): # govee is reporting 0 to 254 - home assistant uses 1 to 255 return self._device.brightness + 1 + @property + def color_temp(self): + """Return the color_temp of the light.""" + if not self._device.color_temp: + return None + return color.color_temperature_kelvin_to_mired(self._device.color_temp) + @property def min_mireds(self): """Return the coldest color_temp that this light supports.""" - return 2000 + return color.color_temperature_kelvin_to_mired(COLOR_TEMP_KELVIN_MAX) @property def max_mireds(self): """Return the warmest color_temp that this light supports.""" - return 9000 + return color.color_temperature_kelvin_to_mired(COLOR_TEMP_KELVIN_MIN) @property def device_state_attributes(self): diff --git a/custom_components/govee/manifest.json b/custom_components/govee/manifest.json index 019743f..61d7087 100644 --- a/custom_components/govee/manifest.json +++ b/custom_components/govee/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "documentation": "https://github.com/LaggAt/hacs-govee/blob/master/README.md", "issue_tracker": "https://github.com/LaggAt/hacs-govee/issues", - "requirements": ["govee-api-laggat==0.1.30", "dacite==1.5.1"], + "requirements": ["govee-api-laggat==0.1.32", "dacite==1.5.1"], "ssdp": [], "zeroconf": [], "homekit": {}, diff --git a/custom_components/govee/strings.json b/custom_components/govee/strings.json index dc6f9b5..13cad55 100644 --- a/custom_components/govee/strings.json +++ b/custom_components/govee/strings.json @@ -1,4 +1,5 @@ { + "title": "Govee LED strips", "config": { "abort": { "already_configured": "Already configured. Only a single configuration possible." @@ -13,9 +14,21 @@ "api_key": "API Key", "delay": "Poll Interval" }, + "title": "Govee LED strips", "description": "Get your API Key from the Govee Home App. For Details see https://github.com/LaggAt/hacs-govee/blob/master/README.md" } } }, - "title": "Govee LED strips" + "options": { + "step": { + "user": { + "data": { + "use_assumed_state": "Use 'assumed state' (two buttons). Default: True", + "offline_is_off": "When a led is offline, show it as off (default doesn't change state). Default: False" + }, + "title": "Govee LED strips - Options", + "description": "Configure the Govee integration. For Details see https://github.com/LaggAt/hacs-govee/blob/master/README.md" + } + } + } } diff --git a/custom_components/govee/translations/de.json b/custom_components/govee/translations/de.json index 8c8136b..4753b95 100644 --- a/custom_components/govee/translations/de.json +++ b/custom_components/govee/translations/de.json @@ -1,4 +1,5 @@ { + "title": "Govee LED Strips", "config": { "abort": { "already_configured": "Bereits eingerichtet. Es ist nur eine Konfiguration möglich." @@ -13,9 +14,21 @@ "api_key": "API Key", "delay": "Abfrage-Intervall" }, + "title": "Govee LED Strips", "description": "Den API Key bekommen Sie in der Govee Home App. Details dazu hier: https://github.com/LaggAt/hacs-govee/blob/master/README.md" } } }, - "title": "Govee LED Strips" + "options": { + "step": { + "user": { + "data": { + "use_assumed_state": "Verwende 'angenommenen Zustand' (zwei Buttons). Standard: True", + "offline_is_off": "Wenn eine LED offline ist, zeige sie als Aus (Standard ändert den Status nicht). Standard: False" + }, + "title": "Govee LED Strips - Einstellungen", + "description": "Einstellen der Govee Integration. Details dazu hier: https://github.com/LaggAt/hacs-govee/blob/master/README.md" + } + } + } } diff --git a/custom_components/govee/translations/en.json b/custom_components/govee/translations/en.json index dc6f9b5..13cad55 100644 --- a/custom_components/govee/translations/en.json +++ b/custom_components/govee/translations/en.json @@ -1,4 +1,5 @@ { + "title": "Govee LED strips", "config": { "abort": { "already_configured": "Already configured. Only a single configuration possible." @@ -13,9 +14,21 @@ "api_key": "API Key", "delay": "Poll Interval" }, + "title": "Govee LED strips", "description": "Get your API Key from the Govee Home App. For Details see https://github.com/LaggAt/hacs-govee/blob/master/README.md" } } }, - "title": "Govee LED strips" + "options": { + "step": { + "user": { + "data": { + "use_assumed_state": "Use 'assumed state' (two buttons). Default: True", + "offline_is_off": "When a led is offline, show it as off (default doesn't change state). Default: False" + }, + "title": "Govee LED strips - Options", + "description": "Configure the Govee integration. For Details see https://github.com/LaggAt/hacs-govee/blob/master/README.md" + } + } + } }