Fix types for WLED (#50001)
This commit is contained in:
parent
2ca0eb61dc
commit
4e4042a869
8 changed files with 37 additions and 45 deletions
|
@ -10,7 +10,14 @@ from homeassistant.components.light import DOMAIN as LIGHT_DOMAIN
|
||||||
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
||||||
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
|
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import ATTR_NAME, CONF_HOST
|
from homeassistant.const import (
|
||||||
|
ATTR_IDENTIFIERS,
|
||||||
|
ATTR_MANUFACTURER,
|
||||||
|
ATTR_MODEL,
|
||||||
|
ATTR_NAME,
|
||||||
|
ATTR_SW_VERSION,
|
||||||
|
CONF_HOST,
|
||||||
|
)
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
from homeassistant.helpers.entity import DeviceInfo
|
from homeassistant.helpers.entity import DeviceInfo
|
||||||
|
@ -20,13 +27,7 @@ from homeassistant.helpers.update_coordinator import (
|
||||||
UpdateFailed,
|
UpdateFailed,
|
||||||
)
|
)
|
||||||
|
|
||||||
from .const import (
|
from .const import DOMAIN
|
||||||
ATTR_IDENTIFIERS,
|
|
||||||
ATTR_MANUFACTURER,
|
|
||||||
ATTR_MODEL,
|
|
||||||
ATTR_SOFTWARE_VERSION,
|
|
||||||
DOMAIN,
|
|
||||||
)
|
|
||||||
|
|
||||||
SCAN_INTERVAL = timedelta(seconds=5)
|
SCAN_INTERVAL = timedelta(seconds=5)
|
||||||
PLATFORMS = (LIGHT_DOMAIN, SENSOR_DOMAIN, SWITCH_DOMAIN)
|
PLATFORMS = (LIGHT_DOMAIN, SENSOR_DOMAIN, SWITCH_DOMAIN)
|
||||||
|
@ -128,6 +129,8 @@ class WLEDDataUpdateCoordinator(DataUpdateCoordinator[WLEDDevice]):
|
||||||
class WLEDEntity(CoordinatorEntity):
|
class WLEDEntity(CoordinatorEntity):
|
||||||
"""Defines a base WLED entity."""
|
"""Defines a base WLED entity."""
|
||||||
|
|
||||||
|
coordinator: WLEDDataUpdateCoordinator
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
|
@ -172,5 +175,5 @@ class WLEDDeviceEntity(WLEDEntity):
|
||||||
ATTR_NAME: self.coordinator.data.info.name,
|
ATTR_NAME: self.coordinator.data.info.name,
|
||||||
ATTR_MANUFACTURER: self.coordinator.data.info.brand,
|
ATTR_MANUFACTURER: self.coordinator.data.info.brand,
|
||||||
ATTR_MODEL: self.coordinator.data.info.product,
|
ATTR_MODEL: self.coordinator.data.info.product,
|
||||||
ATTR_SOFTWARE_VERSION: self.coordinator.data.info.version,
|
ATTR_SW_VERSION: self.coordinator.data.info.version,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
"""Config flow to configure the WLED integration."""
|
"""Config flow to configure the WLED integration."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
from wled import WLED, WLEDConnectionError
|
from wled import WLED, WLEDConnectionError
|
||||||
|
|
||||||
|
@ -8,7 +10,7 @@ from homeassistant.config_entries import SOURCE_ZEROCONF, ConfigFlow
|
||||||
from homeassistant.const import CONF_HOST, CONF_MAC, CONF_NAME
|
from homeassistant.const import CONF_HOST, CONF_MAC, CONF_NAME
|
||||||
from homeassistant.data_entry_flow import FlowResult
|
from homeassistant.data_entry_flow import FlowResult
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
from homeassistant.helpers.typing import ConfigType
|
from homeassistant.helpers.typing import DiscoveryInfoType
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
|
|
||||||
|
@ -18,16 +20,16 @@ class WLEDFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||||
|
|
||||||
VERSION = 1
|
VERSION = 1
|
||||||
|
|
||||||
async def async_step_user(self, user_input: ConfigType | None = None) -> FlowResult:
|
async def async_step_user(
|
||||||
|
self, user_input: dict[str, Any] | None = None
|
||||||
|
) -> FlowResult:
|
||||||
"""Handle a flow initiated by the user."""
|
"""Handle a flow initiated by the user."""
|
||||||
return await self._handle_config_flow(user_input)
|
return await self._handle_config_flow(user_input)
|
||||||
|
|
||||||
async def async_step_zeroconf(
|
async def async_step_zeroconf(
|
||||||
self, discovery_info: ConfigType | None = None
|
self, discovery_info: DiscoveryInfoType
|
||||||
) -> FlowResult:
|
) -> FlowResult:
|
||||||
"""Handle zeroconf discovery."""
|
"""Handle zeroconf discovery."""
|
||||||
if discovery_info is None:
|
|
||||||
return self.async_abort(reason="cannot_connect")
|
|
||||||
|
|
||||||
# Hostname is format: wled-livingroom.local.
|
# Hostname is format: wled-livingroom.local.
|
||||||
host = discovery_info["hostname"].rstrip(".")
|
host = discovery_info["hostname"].rstrip(".")
|
||||||
|
@ -46,13 +48,13 @@ class WLEDFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||||
return await self._handle_config_flow(discovery_info, True)
|
return await self._handle_config_flow(discovery_info, True)
|
||||||
|
|
||||||
async def async_step_zeroconf_confirm(
|
async def async_step_zeroconf_confirm(
|
||||||
self, user_input: ConfigType = None
|
self, user_input: dict[str, Any] | None = None
|
||||||
) -> FlowResult:
|
) -> FlowResult:
|
||||||
"""Handle a flow initiated by zeroconf."""
|
"""Handle a flow initiated by zeroconf."""
|
||||||
return await self._handle_config_flow(user_input)
|
return await self._handle_config_flow(user_input)
|
||||||
|
|
||||||
async def _handle_config_flow(
|
async def _handle_config_flow(
|
||||||
self, user_input: ConfigType | None = None, prepare: bool = False
|
self, user_input: dict[str, Any] | None = None, prepare: bool = False
|
||||||
) -> FlowResult:
|
) -> FlowResult:
|
||||||
"""Config flow handler for WLED."""
|
"""Config flow handler for WLED."""
|
||||||
source = self.context.get("source")
|
source = self.context.get("source")
|
||||||
|
@ -63,6 +65,9 @@ class WLEDFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||||
return self._show_confirm_dialog()
|
return self._show_confirm_dialog()
|
||||||
return self._show_setup_form()
|
return self._show_setup_form()
|
||||||
|
|
||||||
|
# if prepare is True, user_input can not be None.
|
||||||
|
assert user_input is not None
|
||||||
|
|
||||||
if source == SOURCE_ZEROCONF:
|
if source == SOURCE_ZEROCONF:
|
||||||
user_input[CONF_HOST] = self.context.get(CONF_HOST)
|
user_input[CONF_HOST] = self.context.get(CONF_HOST)
|
||||||
user_input[CONF_MAC] = self.context.get(CONF_MAC)
|
user_input[CONF_MAC] = self.context.get(CONF_MAC)
|
||||||
|
|
|
@ -7,12 +7,9 @@ DOMAIN = "wled"
|
||||||
ATTR_COLOR_PRIMARY = "color_primary"
|
ATTR_COLOR_PRIMARY = "color_primary"
|
||||||
ATTR_DURATION = "duration"
|
ATTR_DURATION = "duration"
|
||||||
ATTR_FADE = "fade"
|
ATTR_FADE = "fade"
|
||||||
ATTR_IDENTIFIERS = "identifiers"
|
|
||||||
ATTR_INTENSITY = "intensity"
|
ATTR_INTENSITY = "intensity"
|
||||||
ATTR_LED_COUNT = "led_count"
|
ATTR_LED_COUNT = "led_count"
|
||||||
ATTR_MANUFACTURER = "manufacturer"
|
|
||||||
ATTR_MAX_POWER = "max_power"
|
ATTR_MAX_POWER = "max_power"
|
||||||
ATTR_MODEL = "model"
|
|
||||||
ATTR_ON = "on"
|
ATTR_ON = "on"
|
||||||
ATTR_PALETTE = "palette"
|
ATTR_PALETTE = "palette"
|
||||||
ATTR_PLAYLIST = "playlist"
|
ATTR_PLAYLIST = "playlist"
|
||||||
|
|
|
@ -58,6 +58,7 @@ async def async_setup_entry(
|
||||||
coordinator: WLEDDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
coordinator: WLEDDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||||
|
|
||||||
platform = entity_platform.async_get_current_platform()
|
platform = entity_platform.async_get_current_platform()
|
||||||
|
|
||||||
platform.async_register_entity_service(
|
platform.async_register_entity_service(
|
||||||
SERVICE_EFFECT,
|
SERVICE_EFFECT,
|
||||||
{
|
{
|
||||||
|
@ -127,7 +128,7 @@ class WLEDMasterLight(LightEntity, WLEDDeviceEntity):
|
||||||
@wled_exception_handler
|
@wled_exception_handler
|
||||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||||
"""Turn off the light."""
|
"""Turn off the light."""
|
||||||
data = {ATTR_ON: False}
|
data: dict[str, bool | int] = {ATTR_ON: False}
|
||||||
|
|
||||||
if ATTR_TRANSITION in kwargs:
|
if ATTR_TRANSITION in kwargs:
|
||||||
# WLED uses 100ms per unit, so 10 = 1 second.
|
# WLED uses 100ms per unit, so 10 = 1 second.
|
||||||
|
@ -138,7 +139,7 @@ class WLEDMasterLight(LightEntity, WLEDDeviceEntity):
|
||||||
@wled_exception_handler
|
@wled_exception_handler
|
||||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||||
"""Turn on the light."""
|
"""Turn on the light."""
|
||||||
data = {ATTR_ON: True}
|
data: dict[str, bool | int] = {ATTR_ON: True}
|
||||||
|
|
||||||
if ATTR_TRANSITION in kwargs:
|
if ATTR_TRANSITION in kwargs:
|
||||||
# WLED uses 100ms per unit, so 10 = 1 second.
|
# WLED uses 100ms per unit, so 10 = 1 second.
|
||||||
|
@ -230,7 +231,7 @@ class WLEDSegmentLight(LightEntity, WLEDDeviceEntity):
|
||||||
}
|
}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def hs_color(self) -> tuple[float, float] | None:
|
def hs_color(self) -> tuple[float, float]:
|
||||||
"""Return the hue and saturation color value [float, float]."""
|
"""Return the hue and saturation color value [float, float]."""
|
||||||
color = self.coordinator.data.state.segments[self._segment].color_primary
|
color = self.coordinator.data.state.segments[self._segment].color_primary
|
||||||
return color_util.color_RGB_to_hs(*color[:3])
|
return color_util.color_RGB_to_hs(*color[:3])
|
||||||
|
@ -295,7 +296,7 @@ class WLEDSegmentLight(LightEntity, WLEDDeviceEntity):
|
||||||
@wled_exception_handler
|
@wled_exception_handler
|
||||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||||
"""Turn off the light."""
|
"""Turn off the light."""
|
||||||
data = {ATTR_ON: False}
|
data: dict[str, bool | int] = {ATTR_ON: False}
|
||||||
|
|
||||||
if ATTR_TRANSITION in kwargs:
|
if ATTR_TRANSITION in kwargs:
|
||||||
# WLED uses 100ms per unit, so 10 = 1 second.
|
# WLED uses 100ms per unit, so 10 = 1 second.
|
||||||
|
@ -312,7 +313,10 @@ class WLEDSegmentLight(LightEntity, WLEDDeviceEntity):
|
||||||
@wled_exception_handler
|
@wled_exception_handler
|
||||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||||
"""Turn on the light."""
|
"""Turn on the light."""
|
||||||
data = {ATTR_ON: True, ATTR_SEGMENT_ID: self._segment}
|
data: dict[str, Any] = {
|
||||||
|
ATTR_ON: True,
|
||||||
|
ATTR_SEGMENT_ID: self._segment,
|
||||||
|
}
|
||||||
|
|
||||||
if ATTR_COLOR_TEMP in kwargs:
|
if ATTR_COLOR_TEMP in kwargs:
|
||||||
mireds = color_util.color_temperature_kelvin_to_mired(
|
mireds = color_util.color_temperature_kelvin_to_mired(
|
||||||
|
@ -385,7 +389,7 @@ class WLEDSegmentLight(LightEntity, WLEDDeviceEntity):
|
||||||
speed: int | None = None,
|
speed: int | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set the effect of a WLED light."""
|
"""Set the effect of a WLED light."""
|
||||||
data = {ATTR_SEGMENT_ID: self._segment}
|
data: dict[str, bool | int | str | None] = {ATTR_SEGMENT_ID: self._segment}
|
||||||
|
|
||||||
if effect is not None:
|
if effect is not None:
|
||||||
data[ATTR_EFFECT] = effect
|
data[ATTR_EFFECT] = effect
|
||||||
|
@ -419,7 +423,7 @@ class WLEDSegmentLight(LightEntity, WLEDDeviceEntity):
|
||||||
def async_update_segments(
|
def async_update_segments(
|
||||||
entry: ConfigEntry,
|
entry: ConfigEntry,
|
||||||
coordinator: WLEDDataUpdateCoordinator,
|
coordinator: WLEDDataUpdateCoordinator,
|
||||||
current: dict[int, WLEDSegmentLight],
|
current: dict[int, WLEDSegmentLight | WLEDMasterLight],
|
||||||
async_add_entities,
|
async_add_entities,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Update segments."""
|
"""Update segments."""
|
||||||
|
@ -459,7 +463,7 @@ def async_update_segments(
|
||||||
async def async_remove_entity(
|
async def async_remove_entity(
|
||||||
index: int,
|
index: int,
|
||||||
coordinator: WLEDDataUpdateCoordinator,
|
coordinator: WLEDDataUpdateCoordinator,
|
||||||
current: dict[int, WLEDSegmentLight],
|
current: dict[int, WLEDSegmentLight | WLEDMasterLight],
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Remove WLED segment light from Home Assistant."""
|
"""Remove WLED segment light from Home Assistant."""
|
||||||
entity = current[index]
|
entity = current[index]
|
||||||
|
|
|
@ -74,7 +74,7 @@ class WLEDSensor(WLEDDeviceEntity, SensorEntity):
|
||||||
return f"{self.coordinator.data.info.mac_address}_{self._key}"
|
return f"{self.coordinator.data.info.mac_address}_{self._key}"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def unit_of_measurement(self) -> str:
|
def unit_of_measurement(self) -> str | None:
|
||||||
"""Return the unit this state is expressed in."""
|
"""Return the unit this state is expressed in."""
|
||||||
return self._unit_of_measurement
|
return self._unit_of_measurement
|
||||||
|
|
||||||
|
|
3
mypy.ini
3
mypy.ini
|
@ -1325,9 +1325,6 @@ ignore_errors = true
|
||||||
[mypy-homeassistant.components.withings.*]
|
[mypy-homeassistant.components.withings.*]
|
||||||
ignore_errors = true
|
ignore_errors = true
|
||||||
|
|
||||||
[mypy-homeassistant.components.wled.*]
|
|
||||||
ignore_errors = true
|
|
||||||
|
|
||||||
[mypy-homeassistant.components.wunderground.*]
|
[mypy-homeassistant.components.wunderground.*]
|
||||||
ignore_errors = true
|
ignore_errors = true
|
||||||
|
|
||||||
|
|
|
@ -236,7 +236,6 @@ IGNORED_MODULES: Final[list[str]] = [
|
||||||
"homeassistant.components.wemo.*",
|
"homeassistant.components.wemo.*",
|
||||||
"homeassistant.components.wink.*",
|
"homeassistant.components.wink.*",
|
||||||
"homeassistant.components.withings.*",
|
"homeassistant.components.withings.*",
|
||||||
"homeassistant.components.wled.*",
|
|
||||||
"homeassistant.components.wunderground.*",
|
"homeassistant.components.wunderground.*",
|
||||||
"homeassistant.components.xbox.*",
|
"homeassistant.components.xbox.*",
|
||||||
"homeassistant.components.xiaomi_aqara.*",
|
"homeassistant.components.xiaomi_aqara.*",
|
||||||
|
|
|
@ -119,19 +119,6 @@ async def test_zeroconf_confirm_connection_error(
|
||||||
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
||||||
|
|
||||||
|
|
||||||
@patch("homeassistant.components.wled.WLED.update", side_effect=WLEDConnectionError)
|
|
||||||
async def test_zeroconf_no_data(
|
|
||||||
update_mock: MagicMock, hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
|
|
||||||
) -> None:
|
|
||||||
"""Test we abort if zeroconf provides no data."""
|
|
||||||
flow = config_flow.WLEDFlowHandler()
|
|
||||||
flow.hass = hass
|
|
||||||
result = await flow.async_step_zeroconf()
|
|
||||||
|
|
||||||
assert result["reason"] == "cannot_connect"
|
|
||||||
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
|
||||||
|
|
||||||
|
|
||||||
async def test_user_device_exists_abort(
|
async def test_user_device_exists_abort(
|
||||||
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
|
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue