init
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
"""The Govee integration."""
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
from govee_api_laggat import Govee
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_API_KEY
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import PlatformNotReady
|
||||
|
||||
from .const import DOMAIN
|
||||
from .learning_storage import GoveeLearningStorage
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
CONFIG_SCHEMA = vol.Schema({DOMAIN: vol.Schema({})}, extra=vol.ALLOW_EXTRA)
|
||||
|
||||
# supported platforms
|
||||
PLATFORMS = ["light"]
|
||||
|
||||
|
||||
def setup(hass, config):
|
||||
"""This setup does nothing, we use the async setup."""
|
||||
hass.states.set("govee.state", "setup called")
|
||||
return True
|
||||
|
||||
|
||||
async def async_setup(hass: HomeAssistant, config: dict):
|
||||
"""Set up the Govee component."""
|
||||
hass.states.async_set("govee.state", "async_setup called")
|
||||
hass.data[DOMAIN] = {}
|
||||
return True
|
||||
|
||||
|
||||
def is_online(online: bool):
|
||||
"""Log online/offline change."""
|
||||
msg = "API is offline."
|
||||
if online:
|
||||
msg = "API is back online."
|
||||
_LOGGER.warning(msg)
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||
"""Set up Govee from a config entry."""
|
||||
|
||||
# get vars from ConfigFlow/OptionsFlow
|
||||
config = entry.data
|
||||
options = entry.options
|
||||
api_key = options.get(CONF_API_KEY, config.get(CONF_API_KEY, ""))
|
||||
|
||||
# Setup connection with devices/cloud
|
||||
hub = await Govee.create(
|
||||
api_key, learning_storage=GoveeLearningStorage(hass.config.config_dir)
|
||||
)
|
||||
# keep reference for disposing
|
||||
hass.data[DOMAIN] = {}
|
||||
hass.data[DOMAIN]["hub"] = hub
|
||||
|
||||
# inform when api is offline/online
|
||||
hub.events.online += is_online
|
||||
|
||||
# Verify that passed in configuration works
|
||||
_, err = await hub.get_devices()
|
||||
if err:
|
||||
_LOGGER.warning("Could not connect to Govee API: %s", err)
|
||||
await hub.rate_limit_delay()
|
||||
await async_unload_entry(hass, entry)
|
||||
raise PlatformNotReady()
|
||||
|
||||
for component in PLATFORMS:
|
||||
hass.async_create_task(
|
||||
hass.config_entries.async_forward_entry_setup(entry, component)
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||
"""Unload a config entry."""
|
||||
|
||||
unload_ok = all(
|
||||
await asyncio.gather(
|
||||
*[
|
||||
_unload_component_entry(hass, entry, component)
|
||||
for component in PLATFORMS
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
if unload_ok:
|
||||
hub = hass.data[DOMAIN].pop("hub")
|
||||
await hub.close()
|
||||
|
||||
return unload_ok
|
||||
|
||||
|
||||
def _unload_component_entry(
|
||||
hass: HomeAssistant, entry: ConfigEntry, component: str
|
||||
) -> bool:
|
||||
"""Unload an entry for a specific component."""
|
||||
success = False
|
||||
try:
|
||||
success = hass.config_entries.async_forward_entry_unload(entry, component)
|
||||
except ValueError:
|
||||
# probably ValueError: Config entry was never loaded!
|
||||
return success
|
||||
except Exception as ex:
|
||||
_LOGGER.warning(
|
||||
"Continuing on exception when unloading %s component's entry: %s",
|
||||
component,
|
||||
ex,
|
||||
)
|
||||
return success
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,210 @@
|
||||
"""Config flow for Govee integration."""
|
||||
|
||||
import logging
|
||||
|
||||
from govee_api_laggat import Govee, GoveeNoLearningStorage, GoveeError
|
||||
|
||||
from homeassistant import config_entries, core, exceptions
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.const import CONF_API_KEY, CONF_DELAY
|
||||
from homeassistant.core import callback
|
||||
import voluptuous as vol
|
||||
|
||||
from .const import (
|
||||
CONF_DISABLE_ATTRIBUTE_UPDATES,
|
||||
CONF_OFFLINE_IS_OFF,
|
||||
CONF_USE_ASSUMED_STATE,
|
||||
DOMAIN,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def validate_api_key(hass: core.HomeAssistant, user_input):
|
||||
"""Validate the user input allows us to connect.
|
||||
|
||||
Return info that you want to store in the config entry.
|
||||
"""
|
||||
api_key = user_input[CONF_API_KEY]
|
||||
async with Govee(api_key, learning_storage=GoveeNoLearningStorage()) as hub:
|
||||
_, error = await hub.get_devices()
|
||||
if error:
|
||||
raise CannotConnect(error)
|
||||
|
||||
# Return info that you want to store in the config entry.
|
||||
return user_input
|
||||
|
||||
|
||||
async def validate_disabled_attribute_updates(hass: core.HomeAssistant, user_input):
|
||||
"""Validate format of the ignore_device_attributes parameter string
|
||||
|
||||
Return info that you want to store in the config entry.
|
||||
"""
|
||||
disable_str = user_input[CONF_DISABLE_ATTRIBUTE_UPDATES]
|
||||
if disable_str:
|
||||
# we have something to check, connect without API key
|
||||
async with Govee("", learning_storage=GoveeNoLearningStorage()) as hub:
|
||||
# this will throw an GoveeError if something fails
|
||||
hub.ignore_device_attributes(disable_str)
|
||||
|
||||
# Return info that you want to store in the config entry.
|
||||
return user_input
|
||||
|
||||
|
||||
@config_entries.HANDLERS.register(DOMAIN)
|
||||
class GoveeFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
"""Handle a config flow for Govee."""
|
||||
|
||||
VERSION = 1
|
||||
CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL
|
||||
|
||||
async def async_step_user(self, user_input=None):
|
||||
"""Handle the initial step."""
|
||||
errors = {}
|
||||
if user_input is not None:
|
||||
try:
|
||||
user_input = await validate_api_key(self.hass, user_input)
|
||||
|
||||
except CannotConnect as conn_ex:
|
||||
_LOGGER.exception("Cannot connect: %s", conn_ex)
|
||||
errors[CONF_API_KEY] = "cannot_connect"
|
||||
except GoveeError as govee_ex:
|
||||
_LOGGER.exception("Govee library error: %s", govee_ex)
|
||||
errors["base"] = "govee_ex"
|
||||
except Exception as ex: # pylint: disable=broad-except
|
||||
_LOGGER.exception("Unexpected exception: %s", ex)
|
||||
errors["base"] = "unknown"
|
||||
|
||||
if not errors:
|
||||
return self.async_create_entry(title=DOMAIN, data=user_input)
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="user",
|
||||
data_schema=vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_API_KEY): cv.string,
|
||||
vol.Optional(CONF_DELAY, default=10): cv.positive_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."""
|
||||
# get the current value for API key for comparison and default value
|
||||
old_api_key = self.config_entry.options.get(
|
||||
CONF_API_KEY, self.config_entry.data.get(CONF_API_KEY, "")
|
||||
)
|
||||
|
||||
errors = {}
|
||||
if user_input is not None:
|
||||
# check if API Key changed and is valid
|
||||
try:
|
||||
api_key = user_input[CONF_API_KEY]
|
||||
if old_api_key != api_key:
|
||||
user_input = await validate_api_key(self.hass, user_input)
|
||||
|
||||
except CannotConnect as conn_ex:
|
||||
_LOGGER.exception("Cannot connect: %s", conn_ex)
|
||||
errors[CONF_API_KEY] = "cannot_connect"
|
||||
except GoveeError as govee_ex:
|
||||
_LOGGER.exception("Govee library error: %s", govee_ex)
|
||||
errors["base"] = "govee_ex"
|
||||
except Exception as ex: # pylint: disable=broad-except
|
||||
_LOGGER.exception("Unexpected exception: %s", ex)
|
||||
errors["base"] = "unknown"
|
||||
|
||||
# check validate_disabled_attribute_updates
|
||||
try:
|
||||
user_input = await validate_disabled_attribute_updates(
|
||||
self.hass, user_input
|
||||
)
|
||||
|
||||
# apply settings to the running instance
|
||||
if DOMAIN in self.hass.data and "hub" in self.hass.data[DOMAIN]:
|
||||
hub = self.hass.data[DOMAIN]["hub"]
|
||||
if hub:
|
||||
disable_str = user_input[CONF_DISABLE_ATTRIBUTE_UPDATES]
|
||||
hub.ignore_device_attributes(disable_str)
|
||||
except GoveeError as govee_ex:
|
||||
_LOGGER.exception(
|
||||
"Wrong input format for validate_disabled_attribute_updates: %s",
|
||||
govee_ex,
|
||||
)
|
||||
errors[
|
||||
CONF_DISABLE_ATTRIBUTE_UPDATES
|
||||
] = "disabled_attribute_updates_wrong"
|
||||
|
||||
if not errors:
|
||||
# update options flow values
|
||||
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()
|
||||
|
||||
options_schema = vol.Schema(
|
||||
{
|
||||
# to config flow
|
||||
vol.Required(
|
||||
CONF_API_KEY,
|
||||
default=old_api_key,
|
||||
): cv.string,
|
||||
vol.Optional(
|
||||
CONF_DELAY,
|
||||
default=self.config_entry.options.get(
|
||||
CONF_DELAY, self.config_entry.data.get(CONF_DELAY, 10)
|
||||
),
|
||||
): cv.positive_int,
|
||||
# to options flow
|
||||
vol.Required(
|
||||
CONF_USE_ASSUMED_STATE,
|
||||
default=self.config_entry.options.get(CONF_USE_ASSUMED_STATE, True),
|
||||
): cv.boolean,
|
||||
vol.Required(
|
||||
CONF_OFFLINE_IS_OFF,
|
||||
default=self.config_entry.options.get(CONF_OFFLINE_IS_OFF, False),
|
||||
): cv.boolean,
|
||||
# TODO: validator doesn't work, change to list?
|
||||
vol.Optional(
|
||||
CONF_DISABLE_ATTRIBUTE_UPDATES,
|
||||
default=self.config_entry.options.get(
|
||||
CONF_DISABLE_ATTRIBUTE_UPDATES, ""
|
||||
),
|
||||
): cv.string,
|
||||
},
|
||||
)
|
||||
|
||||
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."""
|
||||
@@ -0,0 +1,10 @@
|
||||
"""Constants for the Govee LED strips integration."""
|
||||
|
||||
DOMAIN = "govee"
|
||||
|
||||
CONF_DISABLE_ATTRIBUTE_UPDATES = "disable_attribute_updates"
|
||||
CONF_OFFLINE_IS_OFF = "offline_is_off"
|
||||
CONF_USE_ASSUMED_STATE = "use_assumed_state"
|
||||
|
||||
COLOR_TEMP_KELVIN_MIN = 2000
|
||||
COLOR_TEMP_KELVIN_MAX = 9000
|
||||
@@ -0,0 +1 @@
|
||||
/workspaces/hacs-govee/custom_components/govee
|
||||
@@ -0,0 +1,66 @@
|
||||
"""The Govee learned storage yaml file manager."""
|
||||
|
||||
from dataclasses import asdict
|
||||
import logging
|
||||
|
||||
import dacite
|
||||
from govee_api_laggat import GoveeAbstractLearningStorage, GoveeLearnedInfo
|
||||
import yaml
|
||||
|
||||
from homeassistant.util.yaml import load_yaml, save_yaml
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
LEARNING_STORAGE_YAML = "/govee_learning.yaml"
|
||||
|
||||
|
||||
class GoveeLearningStorage(GoveeAbstractLearningStorage):
|
||||
"""The govee_api_laggat library uses this to store learned information about lights."""
|
||||
|
||||
def __init__(self, config_dir, *args, **kwargs):
|
||||
"""Get the config directory."""
|
||||
super().__init__(*args, **kwargs)
|
||||
self._config_dir = config_dir
|
||||
|
||||
async def read(self):
|
||||
"""Restore from yaml file."""
|
||||
learned_info = {}
|
||||
try:
|
||||
learned_dict = load_yaml(self._config_dir + LEARNING_STORAGE_YAML)
|
||||
learned_info = {
|
||||
device: dacite.from_dict(
|
||||
data_class=GoveeLearnedInfo, data=learned_dict[device]
|
||||
)
|
||||
for device in learned_dict
|
||||
}
|
||||
_LOGGER.info(
|
||||
"Loaded learning information from %s.",
|
||||
self._config_dir + LEARNING_STORAGE_YAML,
|
||||
)
|
||||
except FileNotFoundError:
|
||||
_LOGGER.warning(
|
||||
"There is no %s file containing learned information about your devices. "
|
||||
+ "This is normal for first start of Govee integration.",
|
||||
self._config_dir + LEARNING_STORAGE_YAML,
|
||||
)
|
||||
except (
|
||||
dacite.DaciteError,
|
||||
TypeError,
|
||||
UnicodeDecodeError,
|
||||
yaml.YAMLError,
|
||||
) as ex:
|
||||
_LOGGER.warning(
|
||||
"The %s file containing learned information about your devices is invalid: %s. "
|
||||
+ "Learning starts from scratch.",
|
||||
self._config_dir + LEARNING_STORAGE_YAML,
|
||||
ex,
|
||||
)
|
||||
return learned_info
|
||||
|
||||
async def write(self, learned_info):
|
||||
"""Save to yaml file."""
|
||||
leaned_dict = {device: asdict(learned_info[device]) for device in learned_info}
|
||||
save_yaml(self._config_dir + LEARNING_STORAGE_YAML, leaned_dict)
|
||||
_LOGGER.info(
|
||||
"Stored learning information to %s.",
|
||||
self._config_dir + LEARNING_STORAGE_YAML,
|
||||
)
|
||||
@@ -0,0 +1,325 @@
|
||||
"""Govee platform."""
|
||||
|
||||
from datetime import timedelta, datetime
|
||||
import logging
|
||||
|
||||
from govee_api_laggat import Govee, GoveeDevice, GoveeError
|
||||
from govee_api_laggat.govee_dtos import GoveeSource
|
||||
|
||||
from homeassistant.components.light import (
|
||||
ATTR_BRIGHTNESS,
|
||||
ATTR_COLOR_TEMP,
|
||||
ATTR_HS_COLOR,
|
||||
SUPPORT_BRIGHTNESS,
|
||||
SUPPORT_COLOR,
|
||||
SUPPORT_COLOR_TEMP,
|
||||
LightEntity,
|
||||
)
|
||||
from homeassistant.const import CONF_DELAY
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
from homeassistant.util import color
|
||||
|
||||
from .const import (
|
||||
DOMAIN,
|
||||
CONF_OFFLINE_IS_OFF,
|
||||
CONF_USE_ASSUMED_STATE,
|
||||
COLOR_TEMP_KELVIN_MIN,
|
||||
COLOR_TEMP_KELVIN_MAX,
|
||||
)
|
||||
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_setup_entry(hass, entry, async_add_entities):
|
||||
"""Set up the Govee Light platform."""
|
||||
_LOGGER.debug("Setting up Govee lights")
|
||||
config = entry.data
|
||||
options = entry.options
|
||||
hub = hass.data[DOMAIN]["hub"]
|
||||
|
||||
# refresh
|
||||
update_interval = timedelta(
|
||||
seconds=options.get(CONF_DELAY, config.get(CONF_DELAY, 10))
|
||||
)
|
||||
coordinator = GoveeDataUpdateCoordinator(
|
||||
hass, _LOGGER, update_interval=update_interval, config_entry=entry
|
||||
)
|
||||
# Fetch initial data so we have data when entities subscribe
|
||||
hub.events.new_device += lambda device: add_entity(
|
||||
async_add_entities, hub, entry, coordinator, device
|
||||
)
|
||||
await coordinator.async_refresh()
|
||||
|
||||
# Add devices
|
||||
for device in hub.devices:
|
||||
add_entity(async_add_entities, hub, entry, coordinator, device)
|
||||
# async_add_entities(
|
||||
# [
|
||||
# GoveeLightEntity(hub, entry.title, coordinator, device)
|
||||
# for device in hub.devices
|
||||
# ],
|
||||
# update_before_add=False,
|
||||
# )
|
||||
|
||||
|
||||
def add_entity(async_add_entities, hub, entry, coordinator, device):
|
||||
async_add_entities(
|
||||
[GoveeLightEntity(hub, entry.title, coordinator, device)],
|
||||
update_before_add=False,
|
||||
)
|
||||
|
||||
|
||||
class GoveeDataUpdateCoordinator(DataUpdateCoordinator):
|
||||
"""Device state update handler."""
|
||||
|
||||
def __init__(self, hass, logger, update_interval=None, *, config_entry):
|
||||
"""Initialize global data updater."""
|
||||
self._config_entry = config_entry
|
||||
|
||||
super().__init__(
|
||||
hass,
|
||||
logger,
|
||||
name=DOMAIN,
|
||||
update_interval=update_interval,
|
||||
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")
|
||||
if "govee" not in self.hass.data:
|
||||
raise UpdateFailed("Govee instance not available")
|
||||
try:
|
||||
hub = self.hass.data[DOMAIN]["hub"]
|
||||
|
||||
if not hub.online:
|
||||
# when offline, check connection, this will set hub.online
|
||||
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:
|
||||
if device.error:
|
||||
self.logger.warning(
|
||||
"update failed for %s: %s", device.device, device.error
|
||||
)
|
||||
return device_states
|
||||
except GoveeError as ex:
|
||||
raise UpdateFailed(f"Exception on getting states: {ex}") from ex
|
||||
|
||||
|
||||
class GoveeLightEntity(LightEntity):
|
||||
"""Representation of a stateful light entity."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hub: Govee,
|
||||
title: str,
|
||||
coordinator: GoveeDataUpdateCoordinator,
|
||||
device: GoveeDevice,
|
||||
):
|
||||
"""Init a Govee light strip."""
|
||||
self._hub = hub
|
||||
self._title = title
|
||||
self._coordinator = coordinator
|
||||
self._device = device
|
||||
|
||||
@property
|
||||
def entity_registry_enabled_default(self):
|
||||
"""Return if the entity should be enabled when first added to the entity registry."""
|
||||
return True
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""Connect to dispatcher listening for entity data notifications."""
|
||||
self._coordinator.async_add_listener(self.async_write_ha_state)
|
||||
|
||||
@property
|
||||
def _state(self):
|
||||
"""Lights internal state."""
|
||||
return self._device # self._hub.state(self._device)
|
||||
|
||||
@property
|
||||
def supported_features(self):
|
||||
"""Flag supported features."""
|
||||
support_flags = 0
|
||||
if self._device.support_brightness:
|
||||
support_flags |= SUPPORT_BRIGHTNESS
|
||||
if self._device.support_color:
|
||||
support_flags |= SUPPORT_COLOR
|
||||
if self._device.support_color_tem:
|
||||
support_flags |= SUPPORT_COLOR_TEMP
|
||||
return support_flags
|
||||
|
||||
async def async_turn_on(self, **kwargs):
|
||||
"""Turn device on."""
|
||||
_LOGGER.debug(
|
||||
"async_turn_on for Govee light %s, kwargs: %s", self._device.device, kwargs
|
||||
)
|
||||
err = None
|
||||
|
||||
just_turn_on = True
|
||||
if ATTR_HS_COLOR in kwargs:
|
||||
hs_color = kwargs.pop(ATTR_HS_COLOR)
|
||||
just_turn_on = False
|
||||
col = color.color_hs_to_RGB(hs_color[0], hs_color[1])
|
||||
_, err = await self._hub.set_color(self._device, col)
|
||||
if ATTR_BRIGHTNESS in kwargs:
|
||||
brightness = kwargs.pop(ATTR_BRIGHTNESS)
|
||||
just_turn_on = False
|
||||
bright_set = brightness - 1
|
||||
_, err = await self._hub.set_brightness(self._device, bright_set)
|
||||
if ATTR_COLOR_TEMP in kwargs:
|
||||
color_temp = kwargs.pop(ATTR_COLOR_TEMP)
|
||||
just_turn_on = False
|
||||
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)
|
||||
|
||||
# if there is no known specific command - turn on
|
||||
if just_turn_on:
|
||||
_, err = await self._hub.turn_on(self._device)
|
||||
# debug log unknown commands
|
||||
if kwargs:
|
||||
_LOGGER.debug(
|
||||
"async_turn_on doesnt know how to handle kwargs: %s", repr(kwargs)
|
||||
)
|
||||
# warn on any error
|
||||
if err:
|
||||
_LOGGER.warning(
|
||||
"async_turn_on failed with '%s' for %s, kwargs: %s",
|
||||
err,
|
||||
self._device.device,
|
||||
kwargs,
|
||||
)
|
||||
|
||||
async def async_turn_off(self, **kwargs):
|
||||
"""Turn device off."""
|
||||
_LOGGER.debug("async_turn_off for Govee light %s", self._device.device)
|
||||
await self._hub.turn_off(self._device)
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
"""Return the unique ID."""
|
||||
return f"govee_{self._title}_{self._device.device}"
|
||||
|
||||
@property
|
||||
def device_id(self):
|
||||
"""Return the ID."""
|
||||
return self.unique_id
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name."""
|
||||
return self._device.device_name
|
||||
|
||||
@property
|
||||
def device_info(self):
|
||||
"""Return the device info."""
|
||||
return {
|
||||
"identifiers": {(DOMAIN, self.device_id)},
|
||||
"name": self.name,
|
||||
"manufacturer": "Govee",
|
||||
"model": self._device.model,
|
||||
"via_device": (DOMAIN, "Govee API (cloud)"),
|
||||
}
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
"""Return true if device is on."""
|
||||
return self._device.power_state
|
||||
|
||||
@property
|
||||
def assumed_state(self):
|
||||
"""
|
||||
Return true if the state is assumed.
|
||||
|
||||
This can be disabled in options.
|
||||
"""
|
||||
return (
|
||||
self._coordinator.use_assumed_state
|
||||
and self._device.source == GoveeSource.HISTORY
|
||||
)
|
||||
|
||||
@property
|
||||
def available(self):
|
||||
"""Return if light is available."""
|
||||
return self._device.online
|
||||
|
||||
@property
|
||||
def hs_color(self):
|
||||
"""Return the hs color value."""
|
||||
return color.color_RGB_to_hs(
|
||||
self._device.color[0],
|
||||
self._device.color[1],
|
||||
self._device.color[2],
|
||||
)
|
||||
|
||||
@property
|
||||
def rgb_color(self):
|
||||
"""Return the rgb color value."""
|
||||
return [
|
||||
self._device.color[0],
|
||||
self._device.color[1],
|
||||
self._device.color[2],
|
||||
]
|
||||
|
||||
@property
|
||||
def brightness(self):
|
||||
"""Return the brightness value."""
|
||||
# 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 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 color.color_temperature_kelvin_to_mired(COLOR_TEMP_KELVIN_MIN)
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self):
|
||||
"""Return the device state attributes."""
|
||||
return {
|
||||
# rate limiting information on Govee API
|
||||
"rate_limit_total": self._hub.rate_limit_total,
|
||||
"rate_limit_remaining": self._hub.rate_limit_remaining,
|
||||
"rate_limit_reset_seconds": round(self._hub.rate_limit_reset_seconds, 2),
|
||||
"rate_limit_reset": datetime.fromtimestamp(
|
||||
self._hub.rate_limit_reset
|
||||
).isoformat(),
|
||||
"rate_limit_on": self._hub.rate_limit_on,
|
||||
# general information
|
||||
"manufacturer": "Govee",
|
||||
"model": self._device.model,
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"domain": "govee",
|
||||
"name": "Govee",
|
||||
"codeowners": ["@LaggAt"],
|
||||
"config_flow": true,
|
||||
"dependencies": [],
|
||||
"documentation": "https://github.com/LaggAt/hacs-govee/blob/master/README.md",
|
||||
"homekit": {},
|
||||
"iot_class": "cloud_polling",
|
||||
"issue_tracker": "https://github.com/LaggAt/hacs-govee/issues",
|
||||
"requirements": ["govee-api-laggat==0.2.2", "dacite==1.8.0"],
|
||||
"ssdp": [],
|
||||
"version": "2023.11.1",
|
||||
"zeroconf": []
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"title": "Govee",
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Already configured. Only a single configuration possible."
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Cannot connect. Is the API-Key correct and the internet connection working?",
|
||||
"unknown": "Unknown Error."
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "API Key",
|
||||
"delay": "Poll Interval"
|
||||
},
|
||||
"title": "",
|
||||
"description": "Get your API Key from the Govee Home App. For Details see https://github.com/LaggAt/hacs-govee/blob/master/README.md"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"error": {
|
||||
"cannot_connect": "Cannot connect. Is the API-Key correct and the internet connection working?",
|
||||
"unknown": "Unknown Error.",
|
||||
"disabled_attribute_updates_wrong": "Wrong format, see README above."
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "API Key (requires restart)",
|
||||
"delay": "Poll Interval (requires restart)",
|
||||
"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",
|
||||
"disable_attribute_updates": "DISABLE state updates. Space to disable. Read the README above!"
|
||||
},
|
||||
"title": "Options",
|
||||
"description": "Configure the Govee integration. For Details see https://github.com/LaggAt/hacs-govee/blob/master/README.md"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"title": "Govee",
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Bereits eingerichtet. Es ist nur eine Konfiguration möglich."
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Keine Verbindung möglich. Ist der API-Key richtig und die Internet Verbindung in Ordnung?",
|
||||
"unknown": "Unbekannter Fehler."
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "API Key",
|
||||
"delay": "Abfrage-Intervall"
|
||||
},
|
||||
"description": "Den API Key bekommen Sie in der Govee Home App. Details dazu hier: https://github.com/LaggAt/hacs-govee/blob/master/README.md"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"error": {
|
||||
"cannot_connect": "Keine Verbindung möglich. Ist der API-Key richtig und die Internet Verbindung in Ordnung?",
|
||||
"unknown": "Unbekannter Fehler.",
|
||||
"disabled_attribute_updates_wrong": "Format ist inkorrekt, bitte README lesen."
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "API Key (benötigt Neustart)",
|
||||
"delay": "Abfrage-Intervall (benötigt Neustart)",
|
||||
"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",
|
||||
"disable_attribute_updates": "Status updates verhindern. Leertaste zum ausschalten. Bitte das README oben dazu lesen."
|
||||
},
|
||||
"title": "Einstellungen",
|
||||
"description": "Einstellen der Govee Integration. Details dazu hier: https://github.com/LaggAt/hacs-govee/blob/master/README.md"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"title": "Govee",
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Already configured. Only a single configuration possible."
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Cannot connect. Is the API-Key correct and the internet connection working?",
|
||||
"unknown": "Unknown Error."
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "API Key",
|
||||
"delay": "Poll Interval"
|
||||
},
|
||||
"description": "Get your API Key from the Govee Home App. For Details see https://github.com/LaggAt/hacs-govee/blob/master/README.md"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"error": {
|
||||
"cannot_connect": "Cannot connect. Is the API-Key correct and the internet connection working?",
|
||||
"unknown": "Unknown Error.",
|
||||
"disabled_attribute_updates_wrong": "Wrong format, see README above."
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "API Key (requires restart)",
|
||||
"delay": "Poll Interval (requires restart)",
|
||||
"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",
|
||||
"disable_attribute_updates": "DISABLE state updates. Space to disable. Read the README above!"
|
||||
},
|
||||
"title": "Options",
|
||||
"description": "Configure the Govee integration. For Details see https://github.com/LaggAt/hacs-govee/blob/master/README.md"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"title": "Govee",
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Déjà configuré. Une seule configuration possible."
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Impossible de se connecter. La clé d'API est-elle correcte et la connexion Internet fonctionne-t-elle?",
|
||||
"unknown": "Erreure inconnue."
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "clé d'API",
|
||||
"delay": "Intervalle d'interrogation"
|
||||
},
|
||||
"description": "Obtenez votre clé API à partir de l'application Govee Home. Pour plus de détails, visitez https://github.com/LaggAt/hacs-govee/blob/master/README.md"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"error": {
|
||||
"cannot_connect": "Impossible de se connecter. La clé d'API est-elle correcte et la connexion Internet fonctionne-t-elle?",
|
||||
"unknown": "Erreure inconnue.",
|
||||
"disabled_attribute_updates_wrong": "Format incorrect, voir le 'lisez-moi' ci-dessus."
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "Clé d'API (nécessite un redémarrage)",
|
||||
"delay": "Intervalle d'interrogation (nécessite un redémarrage)",
|
||||
"use_assumed_state": "Utiliser 'état supposé' (deux boutons). Par défaut : Vrai",
|
||||
"offline_is_off": "Lorsqu'une DEL est hors ligne, affichez-la comme éteinte (la valeur par défaut ne change pas d'état). Par défaut : Faux",
|
||||
"disable_attribute_updates": "DÉSACTIVER les mises à jour d'état. Espace pour désactiver. Lisez le 'lisez-moi' ci-dessus !"
|
||||
},
|
||||
"title": "Options",
|
||||
"description": "Configurez l'intégration Govee. Pour plus de détails, visitez https://github.com/LaggAt/hacs-govee/blob/master/README.md"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"title": "Govee",
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Já configurado. Apenas uma única configuração é possível."
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Não pode conectar. A API-Key está correta e a conexão com a Internet está funcionando?",
|
||||
"unknown": "Erro desconhecido."
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "Chave de API",
|
||||
"delay": "Intervalo de escaneamento"
|
||||
},
|
||||
"description": "Obtenha sua chave de API do aplicativo Govee Home. Para detalhes consulte https://github.com/LaggAt/hacs-govee/blob/master/README.md"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"error": {
|
||||
"cannot_connect": "Não pode conectar. A API-Key está correta e a conexão com a Internet está funcionando?",
|
||||
"unknown": "Erro desconhecido.",
|
||||
"disabled_attribute_updates_wrong": "Formato errado, veja README acima."
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "Chave de API (requer reinicialização)",
|
||||
"delay": "Intervalo de escaneamento (requer reinicialização)",
|
||||
"use_assumed_state": "Use 'estado presumido' (dois botões). Padrão: true",
|
||||
"offline_is_off": "Quando um led estiver offline, mostre-o como desligado (o padrão não muda de estado). Padrão: False",
|
||||
"disable_attribute_updates": "DESATIVAR atualizações de estado. Espaço para desativar. Leia o README acima!"
|
||||
},
|
||||
"title": "Opções",
|
||||
"description": "Configure a integração do Govee. Para detalhes consulte https://github.com/LaggAt/hacs-govee/blob/master/README.md"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user