Bump blebox_uniapi to 2.0.0 and adapt integration (#73834)

pull/73826/head
Michał Huryn 2022-06-29 11:57:55 +02:00 committed by GitHub
parent 078c5cea86
commit b5af96e4bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 140 additions and 45 deletions

View File

@ -129,8 +129,8 @@ build.json @home-assistant/supervisor
/homeassistant/components/binary_sensor/ @home-assistant/core
/tests/components/binary_sensor/ @home-assistant/core
/homeassistant/components/bizkaibus/ @UgaitzEtxebarria
/homeassistant/components/blebox/ @bbx-a @bbx-jp
/tests/components/blebox/ @bbx-a @bbx-jp
/homeassistant/components/blebox/ @bbx-a @bbx-jp @riokuu
/tests/components/blebox/ @bbx-a @bbx-jp @riokuu
/homeassistant/components/blink/ @fronzbot
/tests/components/blink/ @fronzbot
/homeassistant/components/blueprint/ @home-assistant/core

View File

@ -1,8 +1,8 @@
"""The BleBox devices integration."""
import logging
from blebox_uniapi.box import Box
from blebox_uniapi.error import Error
from blebox_uniapi.products import Products
from blebox_uniapi.session import ApiHost
from homeassistant.config_entries import ConfigEntry
@ -30,7 +30,6 @@ PARALLEL_UPDATES = 0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up BleBox devices from a config entry."""
websession = async_get_clientsession(hass)
host = entry.data[CONF_HOST]
@ -40,7 +39,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
api_host = ApiHost(host, port, timeout, websession, hass.loop)
try:
product = await Products.async_from_host(api_host)
product = await Box.async_from_host(api_host)
except Error as ex:
_LOGGER.error("Identify failed at %s:%d (%s)", api_host.host, api_host.port, ex)
raise ConfigEntryNotReady from ex
@ -50,7 +49,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
product = domain_entry.setdefault(PRODUCT, product)
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
return True
@ -71,8 +69,8 @@ def create_blebox_entities(
"""Create entities from a BleBox product's features."""
product = hass.data[DOMAIN][config_entry.entry_id][PRODUCT]
entities = []
if entity_type in product.features:
for feature in product.features[entity_type]:
entities.append(entity_klass(feature))

View File

@ -1,4 +1,6 @@
"""BleBox air quality entity."""
from datetime import timedelta
from homeassistant.components.air_quality import AirQualityEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
@ -6,6 +8,8 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import BleBoxEntity, create_blebox_entities
SCAN_INTERVAL = timedelta(seconds=5)
async def async_setup_entry(
hass: HomeAssistant,

View File

@ -1,4 +1,6 @@
"""BleBox climate entity."""
from datetime import timedelta
from homeassistant.components.climate import ClimateEntity
from homeassistant.components.climate.const import (
ClimateEntityFeature,
@ -12,6 +14,8 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import BleBoxEntity, create_blebox_entities
SCAN_INTERVAL = timedelta(seconds=5)
async def async_setup_entry(
hass: HomeAssistant,

View File

@ -1,8 +1,8 @@
"""Config flow for BleBox devices integration."""
import logging
from blebox_uniapi.box import Box
from blebox_uniapi.error import Error, UnsupportedBoxVersion
from blebox_uniapi.products import Products
from blebox_uniapi.session import ApiHost
import voluptuous as vol
@ -65,7 +65,6 @@ class BleBoxConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
self, step, exception, schema, host, port, message_id, log_fn
):
"""Handle step exceptions."""
log_fn("%s at %s:%d (%s)", LOG_MSG[message_id], host, port, exception)
return self.async_show_form(
@ -101,9 +100,8 @@ class BleBoxConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
websession = async_get_clientsession(hass)
api_host = ApiHost(*addr, DEFAULT_SETUP_TIMEOUT, websession, hass.loop, _LOGGER)
try:
product = await Products.async_from_host(api_host)
product = await Box.async_from_host(api_host)
except UnsupportedBoxVersion as ex:
return self.handle_step_exception(

View File

@ -1,23 +1,34 @@
"""BleBox light entities implementation."""
from __future__ import annotations
from datetime import timedelta
import logging
from blebox_uniapi.error import BadOnValueError
import blebox_uniapi.light
from blebox_uniapi.light import BleboxColorMode
from homeassistant.components.light import (
ATTR_BRIGHTNESS,
ATTR_COLOR_TEMP,
ATTR_EFFECT,
ATTR_RGB_COLOR,
ATTR_RGBW_COLOR,
ATTR_RGBWW_COLOR,
ColorMode,
LightEntity,
LightEntityFeature,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.util.color import color_rgb_to_hex, rgb_hex_to_rgb_list
from . import BleBoxEntity, create_blebox_entities
_LOGGER = logging.getLogger(__name__)
SCAN_INTERVAL = timedelta(seconds=5)
async def async_setup_entry(
hass: HomeAssistant,
@ -31,6 +42,17 @@ async def async_setup_entry(
)
COLOR_MODE_MAP = {
BleboxColorMode.RGBW: ColorMode.RGBW,
BleboxColorMode.RGB: ColorMode.RGB,
BleboxColorMode.MONO: ColorMode.BRIGHTNESS,
BleboxColorMode.RGBorW: ColorMode.RGBW, # white hex is prioritised over RGB channel
BleboxColorMode.CT: ColorMode.COLOR_TEMP,
BleboxColorMode.CTx2: ColorMode.COLOR_TEMP, # two instances
BleboxColorMode.RGBWW: ColorMode.RGBWW,
}
class BleBoxLightEntity(BleBoxEntity, LightEntity):
"""Representation of BleBox lights."""
@ -38,6 +60,7 @@ class BleBoxLightEntity(BleBoxEntity, LightEntity):
"""Initialize a BleBox light."""
super().__init__(feature)
self._attr_supported_color_modes = {self.color_mode}
self._attr_supported_features = LightEntityFeature.EFFECT
@property
def is_on(self) -> bool:
@ -49,46 +72,105 @@ class BleBoxLightEntity(BleBoxEntity, LightEntity):
"""Return the name."""
return self._feature.brightness
@property
def color_temp(self):
"""Return color temperature."""
return self._feature.color_temp
@property
def color_mode(self):
"""Return the color mode."""
if self._feature.supports_white and self._feature.supports_color:
return ColorMode.RGBW
if self._feature.supports_brightness:
return ColorMode.BRIGHTNESS
return ColorMode.ONOFF
"""Return the color mode.
Set values to _attr_ibutes if needed.
"""
color_mode_tmp = COLOR_MODE_MAP.get(self._feature.color_mode, ColorMode.ONOFF)
if color_mode_tmp == ColorMode.COLOR_TEMP:
self._attr_min_mireds = 1
self._attr_max_mireds = 255
return color_mode_tmp
@property
def effect_list(self) -> list[str] | None:
"""Return the list of supported effects."""
return self._feature.effect_list
@property
def effect(self) -> str | None:
"""Return the current effect."""
return self._feature.effect
@property
def rgb_color(self):
"""Return value for rgb."""
if (rgb_hex := self._feature.rgb_hex) is None:
return None
return tuple(
blebox_uniapi.light.Light.normalise_elements_of_rgb(
blebox_uniapi.light.Light.rgb_hex_to_rgb_list(rgb_hex)[0:3]
)
)
@property
def rgbw_color(self):
"""Return the hue and saturation."""
if (rgbw_hex := self._feature.rgbw_hex) is None:
return None
return tuple(blebox_uniapi.light.Light.rgb_hex_to_rgb_list(rgbw_hex)[0:4])
return tuple(rgb_hex_to_rgb_list(rgbw_hex)[0:4])
@property
def rgbww_color(self):
"""Return value for rgbww."""
if (rgbww_hex := self._feature.rgbww_hex) is None:
return None
return tuple(blebox_uniapi.light.Light.rgb_hex_to_rgb_list(rgbww_hex))
async def async_turn_on(self, **kwargs):
"""Turn the light on."""
rgbw = kwargs.get(ATTR_RGBW_COLOR)
brightness = kwargs.get(ATTR_BRIGHTNESS)
effect = kwargs.get(ATTR_EFFECT)
color_temp = kwargs.get(ATTR_COLOR_TEMP)
rgbww = kwargs.get(ATTR_RGBWW_COLOR)
feature = self._feature
value = feature.sensible_on_value
if brightness is not None:
value = feature.apply_brightness(value, brightness)
rgb = kwargs.get(ATTR_RGB_COLOR)
if rgbw is not None:
value = feature.apply_white(value, rgbw[3])
value = feature.apply_color(value, color_rgb_to_hex(*rgbw[0:3]))
try:
await self._feature.async_on(value)
except BadOnValueError as ex:
_LOGGER.error(
"Turning on '%s' failed: Bad value %s (%s)", self.name, value, ex
value = list(rgbw)
if color_temp is not None:
value = feature.return_color_temp_with_brightness(
int(color_temp), self.brightness
)
if rgbww is not None:
value = list(rgbww)
if rgb is not None:
if self.color_mode == ColorMode.RGB and brightness is None:
brightness = self.brightness
value = list(rgb)
if brightness is not None:
if self.color_mode == ATTR_COLOR_TEMP:
value = feature.return_color_temp_with_brightness(
self.color_temp, brightness
)
else:
value = feature.apply_brightness(value, brightness)
if effect is not None:
effect_value = self.effect_list.index(effect)
await self._feature.async_api_command("effect", effect_value)
else:
try:
await self._feature.async_on(value)
except BadOnValueError as ex:
_LOGGER.error(
"Turning on '%s' failed: Bad value %s (%s)", self.name, value, ex
)
async def async_turn_off(self, **kwargs):
"""Turn the light off."""
await self._feature.async_off()

View File

@ -3,8 +3,8 @@
"name": "BleBox devices",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/blebox",
"requirements": ["blebox_uniapi==1.3.3"],
"codeowners": ["@bbx-a", "@bbx-jp"],
"requirements": ["blebox_uniapi==2.0.0"],
"codeowners": ["@bbx-a", "@bbx-jp", "@riokuu"],
"iot_class": "local_polling",
"loggers": ["blebox_uniapi"]
}

View File

@ -1,4 +1,6 @@
"""BleBox switch implementation."""
from datetime import timedelta
from homeassistant.components.switch import SwitchEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
@ -7,6 +9,8 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import BleBoxEntity, create_blebox_entities
from .const import BLEBOX_TO_HASS_DEVICE_CLASSES
SCAN_INTERVAL = timedelta(seconds=5)
async def async_setup_entry(
hass: HomeAssistant,

View File

@ -399,7 +399,7 @@ bimmer_connected==0.9.6
bizkaibus==0.1.1
# homeassistant.components.blebox
blebox_uniapi==1.3.3
blebox_uniapi==2.0.0
# homeassistant.components.blink
blinkpy==0.19.0

View File

@ -311,7 +311,7 @@ bellows==0.31.0
bimmer_connected==0.9.6
# homeassistant.components.blebox
blebox_uniapi==1.3.3
blebox_uniapi==2.0.0
# homeassistant.components.blink
blinkpy==0.19.0

View File

@ -16,12 +16,11 @@ from tests.components.light.conftest import mock_light_profiles # noqa: F401
def patch_product_identify(path=None, **kwargs):
"""Patch the blebox_uniapi Products class."""
if path is None:
path = "homeassistant.components.blebox.Products"
patcher = patch(path, mock.DEFAULT, blebox_uniapi.products.Products, True, True)
products_class = patcher.start()
products_class.async_from_host = AsyncMock(**kwargs)
return products_class
patcher = patch.object(
blebox_uniapi.box.Box, "async_from_host", AsyncMock(**kwargs)
)
patcher.start()
return blebox_uniapi.box.Box
def setup_product_mock(category, feature_mocks, path=None):
@ -84,7 +83,6 @@ async def async_setup_entities(hass, config, entity_ids):
config_entry.add_to_hass(hass)
assert await async_setup_component(hass, DOMAIN, config)
await hass.async_block_till_done()
entity_registry = er.async_get(hass)
return [entity_registry.async_get(entity_id) for entity_id in entity_ids]

View File

@ -77,8 +77,8 @@ async def test_flow_works(hass, valid_feature_mock, flow_feature_mock):
@pytest.fixture(name="product_class_mock")
def product_class_mock_fixture():
"""Return a mocked feature."""
path = "homeassistant.components.blebox.config_flow.Products"
patcher = patch(path, DEFAULT, blebox_uniapi.products.Products, True, True)
path = "homeassistant.components.blebox.config_flow.Box"
patcher = patch(path, DEFAULT, blebox_uniapi.box.Box, True, True)
yield patcher

View File

@ -39,6 +39,8 @@ def dimmer_fixture():
is_on=True,
supports_color=False,
supports_white=False,
color_mode=blebox_uniapi.light.BleboxColorMode.MONO,
effect_list=None,
)
product = feature.product
type(product).name = PropertyMock(return_value="My dimmer")
@ -210,6 +212,8 @@ def wlightboxs_fixture():
is_on=None,
supports_color=False,
supports_white=False,
color_mode=blebox_uniapi.light.BleboxColorMode.MONO,
effect_list=["NONE", "PL", "RELAX"],
)
product = feature.product
type(product).name = PropertyMock(return_value="My wLightBoxS")
@ -310,6 +314,9 @@ def wlightbox_fixture():
supports_white=True,
white_value=None,
rgbw_hex=None,
color_mode=blebox_uniapi.light.BleboxColorMode.RGBW,
effect="NONE",
effect_list=["NONE", "PL", "POLICE"],
)
product = feature.product
type(product).name = PropertyMock(return_value="My wLightBox")
@ -379,7 +386,7 @@ async def test_wlightbox_on_rgbw(wlightbox, hass, config):
def turn_on(value):
feature_mock.is_on = True
assert value == "c1d2f3c7"
assert value == [193, 210, 243, 199]
feature_mock.white_value = 0xC7 # on
feature_mock.rgbw_hex = "c1d2f3c7"