WiZ cleanups part 1 (#65746)

Co-authored-by: Franck Nijhof <frenck@frenck.nl>
This commit is contained in:
J. Nick Koston 2022-02-05 09:23:19 -06:00 committed by GitHub
parent f3c5f9c972
commit 342f5182b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 312 additions and 337 deletions

View File

@ -203,6 +203,7 @@ homeassistant.components.webostv.*
homeassistant.components.websocket_api.*
homeassistant.components.wemo.*
homeassistant.components.whois.*
homeassistant.components.wiz.*
homeassistant.components.zodiac.*
homeassistant.components.zeroconf.*
homeassistant.components.zone.*

View File

@ -1,38 +1,66 @@
"""WiZ Platform integration."""
from dataclasses import dataclass
from datetime import timedelta
import logging
from pywizlight import wizlight
from pywizlight.exceptions import WizLightConnectionError, WizLightTimeOutError
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST
from homeassistant.const import CONF_HOST, Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.debounce import Debouncer
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import DOMAIN
from .const import DOMAIN, WIZ_EXCEPTIONS
from .models import WizData
_LOGGER = logging.getLogger(__name__)
PLATFORMS = ["light"]
PLATFORMS = [Platform.LIGHT]
REQUEST_REFRESH_DELAY = 0.35
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up the wiz integration from a config entry."""
ip_address = entry.data.get(CONF_HOST)
ip_address = entry.data[CONF_HOST]
_LOGGER.debug("Get bulb with IP: %s", ip_address)
bulb = wizlight(ip_address)
try:
bulb = wizlight(ip_address)
scenes = await bulb.getSupportedScenes()
await bulb.getMac()
except (
WizLightTimeOutError,
WizLightConnectionError,
ConnectionRefusedError,
) as err:
scenes = await bulb.getSupportedScenes()
# ValueError gets thrown if the bulb type
# cannot be determined on the first try.
# This is likely because way the library
# processes responses and can be cleaned up
# in the future.
except (ValueError, *WIZ_EXCEPTIONS) as err:
raise ConfigEntryNotReady from err
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = WizData(bulb=bulb, scenes=scenes)
async def _async_update() -> None:
"""Update the WiZ device."""
try:
await bulb.updateState()
except WIZ_EXCEPTIONS as ex:
raise UpdateFailed(f"Failed to update device at {ip_address}: {ex}") from ex
coordinator = DataUpdateCoordinator(
hass=hass,
logger=_LOGGER,
name=entry.title,
update_interval=timedelta(seconds=15),
update_method=_async_update,
# We don't want an immediate refresh since the device
# takes a moment to reflect the state change
request_refresh_debouncer=Debouncer(
hass, _LOGGER, cooldown=REQUEST_REFRESH_DELAY, immediate=False
),
)
await coordinator.async_config_entry_first_refresh()
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = WizData(
coordinator=coordinator, bulb=bulb, scenes=scenes
)
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
return True
@ -42,11 +70,3 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
hass.data[DOMAIN].pop(entry.entry_id)
return unload_ok
@dataclass
class WizData:
"""Data for the wiz integration."""
bulb: wizlight
scenes: list

View File

@ -1,37 +1,38 @@
"""Config flow for WiZ Platform."""
from __future__ import annotations
import logging
from typing import Any
from pywizlight import wizlight
from pywizlight.exceptions import WizLightConnectionError, WizLightTimeOutError
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.const import CONF_HOST, CONF_NAME
from homeassistant.const import CONF_HOST
from homeassistant.data_entry_flow import FlowResult
from .const import DEFAULT_NAME, DOMAIN
from .utils import _short_mac
_LOGGER = logging.getLogger(__name__)
STEP_USER_DATA_SCHEMA = vol.Schema(
{
vol.Required(CONF_HOST): str,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): str,
}
)
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a config flow for WiZ."""
VERSION = 1
async def async_step_user(self, user_input=None):
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Handle a flow initialized by the user."""
errors = {}
if user_input is not None:
bulb = wizlight(user_input[CONF_HOST])
try:
mac = await bulb.getMac()
bulbtype = await bulb.get_bulbtype()
except WizLightTimeOutError:
errors["base"] = "bulb_time_out"
except ConnectionRefusedError:
@ -43,10 +44,18 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
errors["base"] = "unknown"
else:
await self.async_set_unique_id(mac)
self._abort_if_unique_id_configured()
return self.async_create_entry(
title=user_input[CONF_NAME], data=user_input
self._abort_if_unique_id_configured(
updates={CONF_HOST: user_input[CONF_HOST]}
)
bulb_type = bulbtype.bulb_type.value if bulbtype else "Unknown"
name = f"{DEFAULT_NAME} {bulb_type} {_short_mac(mac)}"
return self.async_create_entry(
title=name,
data=user_input,
)
return self.async_show_form(
step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors
step_id="user",
data_schema=vol.Schema({vol.Required(CONF_HOST): str}),
errors=errors,
)

View File

@ -1,4 +1,13 @@
"""Constants for the WiZ Platform integration."""
from pywizlight.exceptions import WizLightConnectionError, WizLightTimeOutError
DOMAIN = "wiz"
DEFAULT_NAME = "WiZ"
WIZ_EXCEPTIONS = (
OSError,
WizLightTimeOutError,
TimeoutError,
WizLightConnectionError,
ConnectionRefusedError,
)

View File

@ -1,12 +1,13 @@
"""WiZ integration."""
from __future__ import annotations
from datetime import timedelta
import contextlib
import logging
from typing import Any
from pywizlight import PilotBuilder, wizlight
from pywizlight import PilotBuilder
from pywizlight.bulblibrary import BulbClass, BulbType
from pywizlight.exceptions import WizLightNotKnownBulb, WizLightTimeOutError
from pywizlight.exceptions import WizLightNotKnownBulb
from pywizlight.rgbcw import convertHSfromRGBCW
from pywizlight.scenes import get_id_from_scene_name
@ -16,83 +17,177 @@ from homeassistant.components.light import (
ATTR_EFFECT,
ATTR_HS_COLOR,
ATTR_RGB_COLOR,
SUPPORT_BRIGHTNESS,
SUPPORT_COLOR,
SUPPORT_COLOR_TEMP,
COLOR_MODE_BRIGHTNESS,
COLOR_MODE_COLOR_TEMP,
COLOR_MODE_HS,
SUPPORT_EFFECT,
LightEntity,
)
from homeassistant.const import CONF_NAME
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.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
import homeassistant.util.color as color_utils
from .const import DOMAIN
from .models import WizData
_LOGGER = logging.getLogger(__name__)
SUPPORT_FEATURES_RGB = (
SUPPORT_BRIGHTNESS | SUPPORT_COLOR | SUPPORT_COLOR_TEMP | SUPPORT_EFFECT
)
DEFAULT_COLOR_MODES = {COLOR_MODE_HS, COLOR_MODE_COLOR_TEMP}
DEFAULT_MIN_MIREDS = 153
DEFAULT_MAX_MIREDS = 454
# set poll interval to 15 sec because of changes from external to the bulb
SCAN_INTERVAL = timedelta(seconds=15)
def get_supported_color_modes(bulb_type: BulbType) -> set[str]:
"""Flag supported features."""
if not bulb_type:
# fallback
return DEFAULT_COLOR_MODES
color_modes = set()
try:
features = bulb_type.features
if features.color:
color_modes.add(COLOR_MODE_HS)
if features.color_tmp:
color_modes.add(COLOR_MODE_COLOR_TEMP)
if not color_modes and features.brightness:
color_modes.add(COLOR_MODE_BRIGHTNESS)
return color_modes
except WizLightNotKnownBulb:
_LOGGER.warning("Bulb is not present in the library. Fallback to full feature")
return DEFAULT_COLOR_MODES
async def async_setup_entry(hass, entry, async_add_entities):
def supports_effects(bulb_type: BulbType) -> bool:
"""Check if a bulb supports effects."""
with contextlib.suppress(WizLightNotKnownBulb):
return bool(bulb_type.features.effect)
return True # default is true
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
try:
return color_utils.color_temperature_kelvin_to_mired(
bulb_type.kelvin_range.max
), color_utils.color_temperature_kelvin_to_mired(bulb_type.kelvin_range.min)
except WizLightNotKnownBulb:
_LOGGER.debug("Kelvin is not present in the library. Fallback to 6500")
return DEFAULT_MIN_MIREDS, DEFAULT_MAX_MIREDS
async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the WiZ Platform from config_flow."""
# Assign configuration variables.
wiz_data = hass.data[DOMAIN][entry.entry_id]
wizbulb = WizBulbEntity(wiz_data.bulb, entry.data.get(CONF_NAME), wiz_data.scenes)
# Add devices with defined name
async_add_entities([wizbulb], update_before_add=True)
return True
wiz_data: WizData = hass.data[DOMAIN][entry.entry_id]
async_add_entities([WizBulbEntity(wiz_data, entry.title)])
class WizBulbEntity(LightEntity):
class WizBulbEntity(CoordinatorEntity, LightEntity):
"""Representation of WiZ Light bulb."""
def __init__(self, light: wizlight, name, scenes):
def __init__(self, wiz_data: WizData, name: str) -> None:
"""Initialize an WiZLight."""
self._light = light
self._state = None
self._brightness = None
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
self._rgb_color = None
self._temperature = None
self._hscolor = None
self._available = None
self._effect = None
self._scenes: list[str] = scenes
self._bulbtype: BulbType = light.bulbtype
self._mac = light.mac
self._attr_unique_id = light.mac
# new init states
self._attr_min_mireds = self.get_min_mireds()
self._attr_max_mireds = self.get_max_mireds()
self._attr_supported_features = self.get_supported_features()
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):
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 brightness(self):
"""Return the brightness of the light."""
return self._brightness
@property
def rgb_color(self):
"""Return the color property."""
return self._rgb_color
@property
def hs_color(self):
"""Return the hs color value."""
return self._hscolor
@property
def is_on(self):
def is_on(self) -> bool | None:
"""Return true if light is on."""
return self._state
is_on: bool | None = self._light.status
return is_on
async def async_turn_on(self, **kwargs):
@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."""
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 (
COLOR_MODE_HS in color_modes
and (rgb := self._light.state.get_rgb()) is not None
and rgb[0] is not None
):
return COLOR_MODE_HS
return COLOR_MODE_BRIGHTNESS
@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
@ -150,199 +245,9 @@ class WizBulbEntity(LightEntity):
)
await self._light.turn_on(pilot)
await self.coordinator.async_request_refresh()
async def async_turn_off(self, **kwargs):
async def async_turn_off(self, **kwargs: Any) -> None:
"""Instruct the light to turn off."""
await self._light.turn_off()
@property
def color_temp(self):
"""Return the CT color value in mireds."""
return self._temperature
def get_min_mireds(self) -> int:
"""Return the coldest color_temp that this light supports."""
if self._bulbtype is None:
return color_utils.color_temperature_kelvin_to_mired(6500)
# DW bulbs have no kelvin
if self._bulbtype.bulb_type == BulbClass.DW:
return 0
# If bulbtype is TW or RGB then return the kelvin value
try:
return color_utils.color_temperature_kelvin_to_mired(
self._bulbtype.kelvin_range.max
)
except WizLightNotKnownBulb:
_LOGGER.debug("Kelvin is not present in the library. Fallback to 6500")
return color_utils.color_temperature_kelvin_to_mired(6500)
def get_max_mireds(self) -> int:
"""Return the warmest color_temp that this light supports."""
if self._bulbtype is None:
return color_utils.color_temperature_kelvin_to_mired(2200)
# DW bulbs have no kelvin
if self._bulbtype.bulb_type == BulbClass.DW:
return 0
# If bulbtype is TW or RGB then return the kelvin value
try:
return color_utils.color_temperature_kelvin_to_mired(
self._bulbtype.kelvin_range.min
)
except WizLightNotKnownBulb:
_LOGGER.debug("Kelvin is not present in the library. Fallback to 2200")
return color_utils.color_temperature_kelvin_to_mired(2200)
def get_supported_features(self) -> int:
"""Flag supported features."""
if not self._bulbtype:
# fallback
return SUPPORT_FEATURES_RGB
features = 0
try:
# Map features for better reading
if self._bulbtype.features.brightness:
features = features | SUPPORT_BRIGHTNESS
if self._bulbtype.features.color:
features = features | SUPPORT_COLOR
if self._bulbtype.features.effect:
features = features | SUPPORT_EFFECT
if self._bulbtype.features.color_tmp:
features = features | SUPPORT_COLOR_TEMP
return features
except WizLightNotKnownBulb:
_LOGGER.warning(
"Bulb is not present in the library. Fallback to full feature"
)
return SUPPORT_FEATURES_RGB
@property
def effect(self):
"""Return the current effect."""
return self._effect
@property
def effect_list(self):
"""Return the list of supported effects.
URL: https://docs.pro.wizconnected.com/#light-modes
"""
return self._scenes
@property
def available(self):
"""Return if light is available."""
return self._available
async def async_update(self):
"""Fetch new state data for this light."""
await self.update_state()
if self._state is not None and self._state is not False:
self.update_brightness()
self.update_temperature()
self.update_color()
self.update_effect()
@property
def device_info(self):
"""Get device specific attributes."""
return {
"connections": {(CONNECTION_NETWORK_MAC, self._mac)},
"name": self._attr_name,
"manufacturer": "WiZ Light Platform",
"model": self._bulbtype.name,
}
def update_state_available(self):
"""Update the state if bulb is available."""
self._state = self._light.status
self._available = True
def update_state_unavailable(self):
"""Update the state if bulb is unavailable."""
self._state = False
self._available = False
async def update_state(self):
"""Update the state."""
try:
await self._light.updateState()
except (ConnectionRefusedError, TimeoutError, WizLightTimeOutError) as ex:
_LOGGER.debug(ex)
self.update_state_unavailable()
else:
if self._light.state is None:
self.update_state_unavailable()
else:
self.update_state_available()
_LOGGER.debug(
"[wizlight %s] updated state: %s and available: %s",
self._light.ip,
self._state,
self._available,
)
def update_brightness(self):
"""Update the brightness."""
if self._light.state.get_brightness() is None:
return
brightness = self._light.state.get_brightness()
if 0 <= int(brightness) <= 255:
self._brightness = int(brightness)
else:
_LOGGER.error(
"Received invalid brightness : %s. Expected: 0-255", brightness
)
self._brightness = None
def update_temperature(self):
"""Update the temperature."""
colortemp = self._light.state.get_colortemp()
if colortemp is None or colortemp == 0:
self._temperature = None
return
_LOGGER.debug(
"[wizlight %s] kelvin from the bulb: %s", self._light.ip, colortemp
)
temperature = color_utils.color_temperature_kelvin_to_mired(colortemp)
self._temperature = temperature
def update_color(self):
"""Update the hs color."""
colortemp = self._light.state.get_colortemp()
if colortemp is not None and colortemp != 0:
self._hscolor = None
return
if self._light.state.get_rgb() is None:
return
rgb = self._light.state.get_rgb()
if rgb[0] is None:
# this is the case if the temperature was changed
# do nothing until the RGB color was changed
return
warmwhite = self._light.state.get_warm_white()
if warmwhite is None:
return
self._hscolor = convertHSfromRGBCW(rgb, warmwhite)
def update_effect(self):
"""Update the bulb scene."""
self._effect = self._light.state.get_scene()
async def get_bulb_type(self):
"""Get the bulb type."""
if self._bulbtype is not None:
return self._bulbtype
try:
self._bulbtype = await self._light.get_bulbtype()
_LOGGER.info(
"[wizlight %s] Initiate the WiZ bulb as %s",
self._light.ip,
self._bulbtype.name,
)
except WizLightTimeOutError:
_LOGGER.debug(
"[wizlight %s] Bulbtype update failed - Timeout", self._light.ip
)
await self.coordinator.async_request_refresh()

View File

@ -3,11 +3,7 @@
"name": "WiZ",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/wiz",
"requirements": [
"pywizlight==0.4.15"
],
"requirements": ["pywizlight==0.4.16"],
"iot_class": "local_polling",
"codeowners": [
"@sbidy"
]
"codeowners": ["@sbidy"]
}

View File

@ -0,0 +1,15 @@
"""WiZ integration models."""
from dataclasses import dataclass
from pywizlight import wizlight
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
@dataclass
class WizData:
"""Data for the wiz integration."""
coordinator: DataUpdateCoordinator
bulb: wizlight
scenes: list

View File

@ -3,13 +3,9 @@
"step": {
"user": {
"data": {
"host": "[%key:common::config_flow::data::host%]",
"name": "[%key:common::config_flow::data::name%]"
"host": "[%key:common::config_flow::data::host%]"
},
"description": "Please enter a hostname or IP address and name to add a new bulb:"
},
"confirm": {
"description": "[%key:common::config_flow::description::confirm_setup%]"
"description": "Enter the IP address of the device."
}
},
"error": {

View File

@ -11,15 +11,11 @@
"unknown": "Unexpected error"
},
"step": {
"confirm": {
"description": "Do you want to add a new Bulb?"
},
"user": {
"data": {
"host": "Hostname or IP",
"name": "Name"
"host": "Host"
},
"description": "Please enter a hostname or IP address and name to add a new bulb:"
"description": "Enter the IP address of the device."
}
}
}

View File

@ -0,0 +1,7 @@
"""WiZ utils."""
from __future__ import annotations
def _short_mac(mac: str) -> str:
"""Get the short mac address from the full mac."""
return mac.replace(":", "").upper()[-6:]

View File

@ -2050,6 +2050,17 @@ no_implicit_optional = true
warn_return_any = true
warn_unreachable = true
[mypy-homeassistant.components.wiz.*]
check_untyped_defs = true
disallow_incomplete_defs = true
disallow_subclassing_any = true
disallow_untyped_calls = true
disallow_untyped_decorators = true
disallow_untyped_defs = true
no_implicit_optional = true
warn_return_any = true
warn_unreachable = true
[mypy-homeassistant.components.zodiac.*]
check_untyped_defs = true
disallow_incomplete_defs = true

View File

@ -2051,7 +2051,7 @@ pywemo==0.7.0
pywilight==0.0.70
# homeassistant.components.wiz
pywizlight==0.4.15
pywizlight==0.4.16
# homeassistant.components.xeoma
pyxeoma==1.4.1

View File

@ -1276,7 +1276,7 @@ pywemo==0.7.0
pywilight==0.0.70
# homeassistant.components.wiz
pywizlight==0.4.15
pywizlight==0.4.16
# homeassistant.components.zerproc
pyzerproc==0.4.8

View File

@ -1,4 +1,5 @@
"""Test the WiZ Platform config flow."""
from contextlib import contextmanager
from unittest.mock import patch
import pytest
@ -9,27 +10,49 @@ from homeassistant.components.wiz.config_flow import (
WizLightTimeOutError,
)
from homeassistant.components.wiz.const import DOMAIN
from homeassistant.const import CONF_HOST, CONF_NAME
from homeassistant.const import CONF_HOST
from tests.common import MockConfigEntry
FAKE_BULB_CONFIG = '{"method":"getSystemConfig","env":"pro","result":\
{"mac":"ABCABCABCABC",\
"homeId":653906,\
"roomId":989983,\
"moduleName":"ESP_0711_STR",\
"fwVersion":"1.21.0",\
"groupId":0,"drvConf":[20,2],\
"ewf":[255,0,255,255,0,0,0],\
"ewfHex":"ff00ffff000000",\
"ping":0}}'
TEST_SYSTEM_INFO = {"id": "ABCABCABCABC", "name": "Test Bulb"}
FAKE_MAC = "ABCABCABCABC"
FAKE_BULB_CONFIG = {
"method": "getSystemConfig",
"env": "pro",
"result": {
"mac": FAKE_MAC,
"homeId": 653906,
"roomId": 989983,
"moduleName": "ESP_0711_STR",
"fwVersion": "1.21.0",
"groupId": 0,
"drvConf": [20, 2],
"ewf": [255, 0, 255, 255, 0, 0, 0],
"ewfHex": "ff00ffff000000",
"ping": 0,
},
}
FAKE_EXTENDED_WHITE_RANGE = [2200, 2700, 6500, 6500]
TEST_SYSTEM_INFO = {"id": FAKE_MAC, "name": "Test Bulb"}
TEST_CONNECTION = {CONF_HOST: "1.1.1.1"}
TEST_NO_IP = {CONF_HOST: "this is no IP input"}
TEST_CONNECTION = {CONF_HOST: "1.1.1.1", CONF_NAME: "Test Bulb"}
def _patch_wizlight():
@contextmanager
def _patcher():
with patch(
"homeassistant.components.wiz.wizlight.getBulbConfig",
return_value=FAKE_BULB_CONFIG,
), patch(
"homeassistant.components.wiz.wizlight.getExtendedWhiteRange",
return_value=FAKE_EXTENDED_WHITE_RANGE,
), patch(
"homeassistant.components.wiz.wizlight.getMac",
return_value=FAKE_MAC,
):
yield
TEST_NO_IP = {CONF_HOST: "this is no IP input", CONF_NAME: "Test Bulb"}
return _patcher()
async def test_form(hass):
@ -40,13 +63,7 @@ async def test_form(hass):
assert result["type"] == "form"
assert result["errors"] == {}
# Patch functions
with patch(
"homeassistant.components.wiz.wizlight.getBulbConfig",
return_value=FAKE_BULB_CONFIG,
), patch(
"homeassistant.components.wiz.wizlight.getMac",
return_value="ABCABCABCABC",
) as mock_setup, patch(
with _patch_wizlight(), patch(
"homeassistant.components.wiz.async_setup_entry",
return_value=True,
) as mock_setup_entry:
@ -57,9 +74,10 @@ async def test_form(hass):
await hass.async_block_till_done()
assert result2["type"] == "create_entry"
assert result2["title"] == "Test Bulb"
assert result2["data"] == TEST_CONNECTION
assert len(mock_setup.mock_calls) == 1
assert result2["title"] == "WiZ Dimmable White ABCABC"
assert result2["data"] == {
CONF_HOST: "1.1.1.1",
}
assert len(mock_setup_entry.mock_calls) == 1
@ -98,8 +116,6 @@ async def test_form_updates_unique_id(hass):
unique_id=TEST_SYSTEM_INFO["id"],
data={
CONF_HOST: "dummy",
CONF_NAME: TEST_SYSTEM_INFO["name"],
"id": TEST_SYSTEM_INFO["id"],
},
)
@ -108,13 +124,7 @@ async def test_form_updates_unique_id(hass):
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
with patch(
"homeassistant.components.wiz.wizlight.getBulbConfig",
return_value=FAKE_BULB_CONFIG,
), patch(
"homeassistant.components.wiz.wizlight.getMac",
return_value="ABCABCABCABC",
), patch(
with _patch_wizlight(), patch(
"homeassistant.components.wiz.async_setup_entry",
return_value=True,
):