Skip to content

Commit

Permalink
Bump blebox_uniapi to 2.0.0 and adapt integration (home-assistant#73834)
Browse files Browse the repository at this point in the history
  • Loading branch information
riokuu authored Jun 29, 2022
1 parent 078c5ce commit b5af96e
Show file tree
Hide file tree
Showing 13 changed files with 140 additions and 45 deletions.
4 changes: 2 additions & 2 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 3 additions & 5 deletions homeassistant/components/blebox/__init__.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -30,7 +30,6 @@

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]
Expand All @@ -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
Expand All @@ -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


Expand All @@ -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))
Expand Down
4 changes: 4 additions & 0 deletions homeassistant/components/blebox/air_quality.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
"""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
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,
Expand Down
4 changes: 4 additions & 0 deletions homeassistant/components/blebox/climate.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
"""BleBox climate entity."""
from datetime import timedelta

from homeassistant.components.climate import ClimateEntity
from homeassistant.components.climate.const import (
ClimateEntityFeature,
Expand All @@ -12,6 +14,8 @@

from . import BleBoxEntity, create_blebox_entities

SCAN_INTERVAL = timedelta(seconds=5)


async def async_setup_entry(
hass: HomeAssistant,
Expand Down
6 changes: 2 additions & 4 deletions homeassistant/components/blebox/config_flow.py
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -65,7 +65,6 @@ def handle_step_exception(
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(
Expand Down Expand Up @@ -101,9 +100,8 @@ async def async_step_user(self, user_input=None):

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(
Expand Down
122 changes: 102 additions & 20 deletions homeassistant/components/blebox/light.py
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -31,13 +42,25 @@ 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."""

def __init__(self, feature):
"""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:
Expand All @@ -49,46 +72,105 @@ def brightness(self):
"""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()
4 changes: 2 additions & 2 deletions homeassistant/components/blebox/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
}
4 changes: 4 additions & 0 deletions homeassistant/components/blebox/switch.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -7,6 +9,8 @@
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,
Expand Down
2 changes: 1 addition & 1 deletion requirements_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion requirements_test_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 5 additions & 7 deletions tests/components/blebox/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,11 @@

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):
Expand Down Expand Up @@ -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]

Expand Down
4 changes: 2 additions & 2 deletions tests/components/blebox/test_config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down
Loading

0 comments on commit b5af96e

Please sign in to comment.