WiZ Cleanups part 3 (#65819)
* WiZ Cleanups part 3 - Sockets are now using the switch platform * tweaks * remove rgb colorcheck * tweaks * tweaks * cover * coverpull/65830/head
parent
f171ec4676
commit
5621e20963
|
@ -1317,7 +1317,9 @@ omit =
|
|||
homeassistant/components/wiz/__init__.py
|
||||
homeassistant/components/wiz/const.py
|
||||
homeassistant/components/wiz/discovery.py
|
||||
homeassistant/components/wiz/entity.py
|
||||
homeassistant/components/wiz/light.py
|
||||
homeassistant/components/wiz/switch.py
|
||||
homeassistant/components/wolflink/__init__.py
|
||||
homeassistant/components/wolflink/sensor.py
|
||||
homeassistant/components/wolflink/const.py
|
||||
|
|
|
@ -22,7 +22,7 @@ from .models import WizData
|
|||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
PLATFORMS = [Platform.LIGHT]
|
||||
PLATFORMS = [Platform.LIGHT, Platform.SWITCH]
|
||||
|
||||
REQUEST_REFRESH_DELAY = 0.35
|
||||
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
"""WiZ integration entities."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from pywizlight.bulblibrary import BulbType
|
||||
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
|
||||
from homeassistant.helpers.entity import DeviceInfo, ToggleEntity
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .models import WizData
|
||||
|
||||
|
||||
class WizToggleEntity(CoordinatorEntity, ToggleEntity):
|
||||
"""Representation of WiZ toggle entity."""
|
||||
|
||||
def __init__(self, wiz_data: WizData, name: str) -> None:
|
||||
"""Initialize an WiZ device."""
|
||||
super().__init__(wiz_data.coordinator)
|
||||
self._device = wiz_data.bulb
|
||||
bulb_type: BulbType = self._device.bulbtype
|
||||
self._attr_unique_id = self._device.mac
|
||||
self._attr_name = name
|
||||
self._attr_device_info = DeviceInfo(
|
||||
connections={(CONNECTION_NETWORK_MAC, self._device.mac)},
|
||||
name=name,
|
||||
manufacturer="WiZ",
|
||||
model=bulb_type.name,
|
||||
)
|
||||
|
||||
@callback
|
||||
def _handle_coordinator_update(self) -> None:
|
||||
"""Handle updated data from the coordinator."""
|
||||
self._attr_is_on = self._device.status
|
||||
super()._handle_coordinator_update()
|
||||
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Instruct the device to turn off."""
|
||||
await self._device.turn_off()
|
||||
await self.coordinator.async_request_refresh()
|
|
@ -1,4 +1,4 @@
|
|||
"""WiZ integration."""
|
||||
"""WiZ integration light platform."""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
@ -14,7 +14,6 @@ from homeassistant.components.light import (
|
|||
ATTR_COLOR_TEMP,
|
||||
ATTR_EFFECT,
|
||||
ATTR_HS_COLOR,
|
||||
ATTR_RGB_COLOR,
|
||||
COLOR_MODE_BRIGHTNESS,
|
||||
COLOR_MODE_COLOR_TEMP,
|
||||
COLOR_MODE_HS,
|
||||
|
@ -22,14 +21,15 @@ from homeassistant.components.light import (
|
|||
LightEntity,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
|
||||
from homeassistant.helpers.entity import DeviceInfo
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
import homeassistant.util.color as color_utils
|
||||
from homeassistant.util.color import (
|
||||
color_temperature_kelvin_to_mired,
|
||||
color_temperature_mired_to_kelvin,
|
||||
)
|
||||
|
||||
from .const import DOMAIN
|
||||
from .const import DOMAIN, SOCKET_DEVICE_STR
|
||||
from .entity import WizToggleEntity
|
||||
from .models import WizData
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
@ -52,22 +52,15 @@ def get_supported_color_modes(bulb_type: BulbType) -> set[str]:
|
|||
return color_modes
|
||||
|
||||
|
||||
def supports_effects(bulb_type: BulbType) -> bool:
|
||||
"""Check if a bulb supports effects."""
|
||||
return bool(bulb_type.features.effect)
|
||||
|
||||
|
||||
def get_min_max_mireds(bulb_type: BulbType) -> tuple[int, int]:
|
||||
"""Return the coldest and warmest color_temp that this light supports."""
|
||||
if bulb_type is None:
|
||||
return DEFAULT_MIN_MIREDS, DEFAULT_MAX_MIREDS
|
||||
# DW bulbs have no kelvin
|
||||
if bulb_type.bulb_type == BulbClass.DW:
|
||||
return 0, 0
|
||||
# If bulbtype is TW or RGB then return the kelvin value
|
||||
return color_utils.color_temperature_kelvin_to_mired(
|
||||
return color_temperature_kelvin_to_mired(
|
||||
bulb_type.kelvin_range.max
|
||||
), color_utils.color_temperature_kelvin_to_mired(bulb_type.kelvin_range.min)
|
||||
), color_temperature_kelvin_to_mired(bulb_type.kelvin_range.min)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
|
@ -77,162 +70,79 @@ async def async_setup_entry(
|
|||
) -> None:
|
||||
"""Set up the WiZ Platform from config_flow."""
|
||||
wiz_data: WizData = hass.data[DOMAIN][entry.entry_id]
|
||||
async_add_entities([WizBulbEntity(wiz_data, entry.title)])
|
||||
if SOCKET_DEVICE_STR not in wiz_data.bulb.bulbtype.name:
|
||||
async_add_entities([WizBulbEntity(wiz_data, entry.title)])
|
||||
|
||||
|
||||
class WizBulbEntity(CoordinatorEntity, LightEntity):
|
||||
class WizBulbEntity(WizToggleEntity, LightEntity):
|
||||
"""Representation of WiZ Light bulb."""
|
||||
|
||||
def __init__(self, wiz_data: WizData, name: str) -> None:
|
||||
"""Initialize an WiZLight."""
|
||||
super().__init__(wiz_data.coordinator)
|
||||
self._light = wiz_data.bulb
|
||||
bulb_type: BulbType = self._light.bulbtype
|
||||
self._attr_unique_id = self._light.mac
|
||||
self._attr_name = name
|
||||
super().__init__(wiz_data, name)
|
||||
bulb_type: BulbType = self._device.bulbtype
|
||||
self._attr_effect_list = wiz_data.scenes
|
||||
self._attr_min_mireds, self._attr_max_mireds = get_min_max_mireds(bulb_type)
|
||||
self._attr_supported_color_modes = get_supported_color_modes(bulb_type)
|
||||
if supports_effects(bulb_type):
|
||||
if bulb_type.features.effect:
|
||||
self._attr_supported_features = SUPPORT_EFFECT
|
||||
self._attr_device_info = DeviceInfo(
|
||||
connections={(CONNECTION_NETWORK_MAC, self._light.mac)},
|
||||
name=name,
|
||||
manufacturer="WiZ",
|
||||
model=bulb_type.name,
|
||||
)
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool | None:
|
||||
"""Return true if light is on."""
|
||||
is_on: bool | None = self._light.status
|
||||
return is_on
|
||||
|
||||
@property
|
||||
def brightness(self) -> int | None:
|
||||
"""Return the brightness of the light."""
|
||||
if (brightness := self._light.state.get_brightness()) is None:
|
||||
return None
|
||||
if 0 <= int(brightness) <= 255:
|
||||
return int(brightness)
|
||||
_LOGGER.error("Received invalid brightness : %s. Expected: 0-255", brightness)
|
||||
return None
|
||||
|
||||
@property
|
||||
def color_mode(self) -> str:
|
||||
"""Return the current color mode."""
|
||||
@callback
|
||||
def _handle_coordinator_update(self) -> None:
|
||||
"""Handle updated data from the coordinator."""
|
||||
state = self._device.state
|
||||
if (brightness := state.get_brightness()) is not None:
|
||||
self._attr_brightness = max(0, min(255, brightness))
|
||||
color_modes = self.supported_color_modes
|
||||
assert color_modes is not None
|
||||
if (
|
||||
COLOR_MODE_COLOR_TEMP in color_modes
|
||||
and self._light.state.get_colortemp() is not None
|
||||
):
|
||||
return COLOR_MODE_COLOR_TEMP
|
||||
if (
|
||||
if COLOR_MODE_COLOR_TEMP in color_modes and state.get_colortemp() is not None:
|
||||
self._attr_color_mode = COLOR_MODE_COLOR_TEMP
|
||||
if color_temp := state.get_colortemp():
|
||||
self._attr_color_temp = color_temperature_kelvin_to_mired(color_temp)
|
||||
elif (
|
||||
COLOR_MODE_HS in color_modes
|
||||
and (rgb := self._light.state.get_rgb()) is not None
|
||||
and (rgb := state.get_rgb()) is not None
|
||||
and rgb[0] is not None
|
||||
):
|
||||
return COLOR_MODE_HS
|
||||
return COLOR_MODE_BRIGHTNESS
|
||||
if (warm_white := state.get_warm_white()) is not None:
|
||||
self._attr_hs_color = convertHSfromRGBCW(rgb, warm_white)
|
||||
self._attr_color_mode = COLOR_MODE_HS
|
||||
else:
|
||||
self._attr_color_mode = COLOR_MODE_BRIGHTNESS
|
||||
self._attr_effect = state.get_scene()
|
||||
super()._handle_coordinator_update()
|
||||
|
||||
@property
|
||||
def hs_color(self) -> tuple[float, float] | None:
|
||||
"""Return the hs color value."""
|
||||
colortemp = self._light.state.get_colortemp()
|
||||
if colortemp is not None and colortemp != 0:
|
||||
return None
|
||||
if (rgb := self._light.state.get_rgb()) is None:
|
||||
return None
|
||||
if rgb[0] is None:
|
||||
# this is the case if the temperature was changed
|
||||
# do nothing until the RGB color was changed
|
||||
return None
|
||||
if (warmwhite := self._light.state.get_warm_white()) is None:
|
||||
return None
|
||||
hue_sat = convertHSfromRGBCW(rgb, warmwhite)
|
||||
hue: float = hue_sat[0]
|
||||
sat: float = hue_sat[1]
|
||||
return hue, sat
|
||||
|
||||
@property
|
||||
def color_temp(self) -> int | None:
|
||||
"""Return the CT color value in mireds."""
|
||||
colortemp = self._light.state.get_colortemp()
|
||||
if colortemp is None or colortemp == 0:
|
||||
return None
|
||||
_LOGGER.debug(
|
||||
"[wizlight %s] kelvin from the bulb: %s", self._light.ip, colortemp
|
||||
)
|
||||
return color_utils.color_temperature_kelvin_to_mired(colortemp)
|
||||
|
||||
@property
|
||||
def effect(self) -> str | None:
|
||||
"""Return the current effect."""
|
||||
effect: str | None = self._light.state.get_scene()
|
||||
return effect
|
||||
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Instruct the light to turn on."""
|
||||
brightness = None
|
||||
|
||||
if ATTR_BRIGHTNESS in kwargs:
|
||||
brightness = kwargs.get(ATTR_BRIGHTNESS)
|
||||
|
||||
if ATTR_RGB_COLOR in kwargs:
|
||||
pilot = PilotBuilder(rgb=kwargs.get(ATTR_RGB_COLOR), brightness=brightness)
|
||||
@callback
|
||||
def _async_pilot_builder(self, **kwargs: Any) -> PilotBuilder:
|
||||
"""Create the PilotBuilder for turn on."""
|
||||
brightness = kwargs.get(ATTR_BRIGHTNESS)
|
||||
|
||||
if ATTR_HS_COLOR in kwargs:
|
||||
pilot = PilotBuilder(
|
||||
return PilotBuilder(
|
||||
hucolor=(kwargs[ATTR_HS_COLOR][0], kwargs[ATTR_HS_COLOR][1]),
|
||||
brightness=brightness,
|
||||
)
|
||||
else:
|
||||
colortemp = None
|
||||
if ATTR_COLOR_TEMP in kwargs:
|
||||
kelvin = color_utils.color_temperature_mired_to_kelvin(
|
||||
kwargs[ATTR_COLOR_TEMP]
|
||||
)
|
||||
colortemp = kelvin
|
||||
_LOGGER.debug(
|
||||
"[wizlight %s] kelvin changed and send to bulb: %s",
|
||||
self._light.ip,
|
||||
colortemp,
|
||||
)
|
||||
|
||||
sceneid = None
|
||||
if ATTR_EFFECT in kwargs:
|
||||
sceneid = get_id_from_scene_name(kwargs[ATTR_EFFECT])
|
||||
color_temp = None
|
||||
if ATTR_COLOR_TEMP in kwargs:
|
||||
color_temp = color_temperature_mired_to_kelvin(kwargs[ATTR_COLOR_TEMP])
|
||||
|
||||
if sceneid == 1000: # rhythm
|
||||
pilot = PilotBuilder()
|
||||
else:
|
||||
pilot = PilotBuilder(
|
||||
brightness=brightness, colortemp=colortemp, scene=sceneid
|
||||
)
|
||||
_LOGGER.debug(
|
||||
"[wizlight %s] Pilot will be send with brightness=%s, colortemp=%s, scene=%s",
|
||||
self._light.ip,
|
||||
brightness,
|
||||
colortemp,
|
||||
sceneid,
|
||||
)
|
||||
scene_id = None
|
||||
if ATTR_EFFECT in kwargs:
|
||||
scene_id = get_id_from_scene_name(kwargs[ATTR_EFFECT])
|
||||
if scene_id == 1000: # rhythm
|
||||
return PilotBuilder()
|
||||
|
||||
sceneid = None
|
||||
if ATTR_EFFECT in kwargs:
|
||||
sceneid = get_id_from_scene_name(kwargs[ATTR_EFFECT])
|
||||
_LOGGER.debug(
|
||||
"[wizlight %s] Pilot will be sent with brightness=%s, color_temp=%s, scene_id=%s",
|
||||
self._device.ip,
|
||||
brightness,
|
||||
color_temp,
|
||||
scene_id,
|
||||
)
|
||||
return PilotBuilder(brightness=brightness, colortemp=color_temp, scene=scene_id)
|
||||
|
||||
if sceneid == 1000: # rhythm
|
||||
pilot = PilotBuilder()
|
||||
else:
|
||||
pilot = PilotBuilder(
|
||||
brightness=brightness, colortemp=colortemp, scene=sceneid
|
||||
)
|
||||
|
||||
await self._light.turn_on(pilot)
|
||||
await self.coordinator.async_request_refresh()
|
||||
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Instruct the light to turn off."""
|
||||
await self._light.turn_off()
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Instruct the light to turn on."""
|
||||
await self._device.turn_on(self._async_pilot_builder(**kwargs))
|
||||
await self.coordinator.async_request_refresh()
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
"no_wiz_light": "The bulb can not be connected via WiZ Platform integration."
|
||||
},
|
||||
"abort": {
|
||||
"no_devices_found": "[%key:common::config_flow::abort::no_devices_found%]",
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
"""WiZ integration switch platform."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from pywizlight import PilotBuilder
|
||||
|
||||
from homeassistant.components.switch import SwitchEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import DOMAIN, SOCKET_DEVICE_STR
|
||||
from .entity import WizToggleEntity
|
||||
from .models import WizData
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the WiZ switch platform."""
|
||||
wiz_data: WizData = hass.data[DOMAIN][entry.entry_id]
|
||||
if SOCKET_DEVICE_STR in wiz_data.bulb.bulbtype.name:
|
||||
async_add_entities([WizSocketEntity(wiz_data, entry.title)])
|
||||
|
||||
|
||||
class WizSocketEntity(WizToggleEntity, SwitchEntity):
|
||||
"""Representation of a WiZ socket."""
|
||||
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Instruct the socket to turn on."""
|
||||
await self._device.turn_on(PilotBuilder())
|
||||
await self.coordinator.async_request_refresh()
|
|
@ -1,8 +1,7 @@
|
|||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Device is already configured",
|
||||
"no_devices_found": "No devices found on the network"
|
||||
"already_configured": "Device is already configured"
|
||||
},
|
||||
"error": {
|
||||
"bulb_time_out": "Can not connect to the bulb. Maybe the bulb is offline or a wrong IP/host was entered. Please turn on the light and try again!",
|
||||
|
|
|
@ -152,6 +152,7 @@ async def test_form_updates_unique_id(hass):
|
|||
|
||||
assert result2["type"] == "abort"
|
||||
assert result2["reason"] == "already_configured"
|
||||
assert entry.data[CONF_HOST] == FAKE_IP
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
|
Loading…
Reference in New Issue