Bump blebox_uniapi to 2.0.0 and adapt integration (#73834)
parent
078c5cea86
commit
b5af96e4bb
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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"]
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
Loading…
Reference in New Issue