mirror of
https://github.com/home-assistant/core.git
synced 2025-04-24 09:17:53 +00:00
Fix types for WLED (#50001)
This commit is contained in:
parent
2ca0eb61dc
commit
4e4042a869
@ -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.switch import DOMAIN as SWITCH_DOMAIN
|
||||
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.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.entity import DeviceInfo
|
||||
@ -20,13 +27,7 @@ from homeassistant.helpers.update_coordinator import (
|
||||
UpdateFailed,
|
||||
)
|
||||
|
||||
from .const import (
|
||||
ATTR_IDENTIFIERS,
|
||||
ATTR_MANUFACTURER,
|
||||
ATTR_MODEL,
|
||||
ATTR_SOFTWARE_VERSION,
|
||||
DOMAIN,
|
||||
)
|
||||
from .const import DOMAIN
|
||||
|
||||
SCAN_INTERVAL = timedelta(seconds=5)
|
||||
PLATFORMS = (LIGHT_DOMAIN, SENSOR_DOMAIN, SWITCH_DOMAIN)
|
||||
@ -128,6 +129,8 @@ class WLEDDataUpdateCoordinator(DataUpdateCoordinator[WLEDDevice]):
|
||||
class WLEDEntity(CoordinatorEntity):
|
||||
"""Defines a base WLED entity."""
|
||||
|
||||
coordinator: WLEDDataUpdateCoordinator
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
@ -172,5 +175,5 @@ class WLEDDeviceEntity(WLEDEntity):
|
||||
ATTR_NAME: self.coordinator.data.info.name,
|
||||
ATTR_MANUFACTURER: self.coordinator.data.info.brand,
|
||||
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."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
import voluptuous as vol
|
||||
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.data_entry_flow import FlowResult
|
||||
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
|
||||
|
||||
@ -18,16 +20,16 @@ class WLEDFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
|
||||
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."""
|
||||
return await self._handle_config_flow(user_input)
|
||||
|
||||
async def async_step_zeroconf(
|
||||
self, discovery_info: ConfigType | None = None
|
||||
self, discovery_info: DiscoveryInfoType
|
||||
) -> FlowResult:
|
||||
"""Handle zeroconf discovery."""
|
||||
if discovery_info is None:
|
||||
return self.async_abort(reason="cannot_connect")
|
||||
|
||||
# Hostname is format: wled-livingroom.local.
|
||||
host = discovery_info["hostname"].rstrip(".")
|
||||
@ -46,13 +48,13 @@ class WLEDFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
return await self._handle_config_flow(discovery_info, True)
|
||||
|
||||
async def async_step_zeroconf_confirm(
|
||||
self, user_input: ConfigType = None
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> FlowResult:
|
||||
"""Handle a flow initiated by zeroconf."""
|
||||
return await self._handle_config_flow(user_input)
|
||||
|
||||
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:
|
||||
"""Config flow handler for WLED."""
|
||||
source = self.context.get("source")
|
||||
@ -63,6 +65,9 @@ class WLEDFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
return self._show_confirm_dialog()
|
||||
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:
|
||||
user_input[CONF_HOST] = self.context.get(CONF_HOST)
|
||||
user_input[CONF_MAC] = self.context.get(CONF_MAC)
|
||||
|
@ -7,12 +7,9 @@ DOMAIN = "wled"
|
||||
ATTR_COLOR_PRIMARY = "color_primary"
|
||||
ATTR_DURATION = "duration"
|
||||
ATTR_FADE = "fade"
|
||||
ATTR_IDENTIFIERS = "identifiers"
|
||||
ATTR_INTENSITY = "intensity"
|
||||
ATTR_LED_COUNT = "led_count"
|
||||
ATTR_MANUFACTURER = "manufacturer"
|
||||
ATTR_MAX_POWER = "max_power"
|
||||
ATTR_MODEL = "model"
|
||||
ATTR_ON = "on"
|
||||
ATTR_PALETTE = "palette"
|
||||
ATTR_PLAYLIST = "playlist"
|
||||
|
@ -58,6 +58,7 @@ async def async_setup_entry(
|
||||
coordinator: WLEDDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
platform = entity_platform.async_get_current_platform()
|
||||
|
||||
platform.async_register_entity_service(
|
||||
SERVICE_EFFECT,
|
||||
{
|
||||
@ -127,7 +128,7 @@ class WLEDMasterLight(LightEntity, WLEDDeviceEntity):
|
||||
@wled_exception_handler
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn off the light."""
|
||||
data = {ATTR_ON: False}
|
||||
data: dict[str, bool | int] = {ATTR_ON: False}
|
||||
|
||||
if ATTR_TRANSITION in kwargs:
|
||||
# WLED uses 100ms per unit, so 10 = 1 second.
|
||||
@ -138,7 +139,7 @@ class WLEDMasterLight(LightEntity, WLEDDeviceEntity):
|
||||
@wled_exception_handler
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn on the light."""
|
||||
data = {ATTR_ON: True}
|
||||
data: dict[str, bool | int] = {ATTR_ON: True}
|
||||
|
||||
if ATTR_TRANSITION in kwargs:
|
||||
# WLED uses 100ms per unit, so 10 = 1 second.
|
||||
@ -230,7 +231,7 @@ class WLEDSegmentLight(LightEntity, WLEDDeviceEntity):
|
||||
}
|
||||
|
||||
@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]."""
|
||||
color = self.coordinator.data.state.segments[self._segment].color_primary
|
||||
return color_util.color_RGB_to_hs(*color[:3])
|
||||
@ -295,7 +296,7 @@ class WLEDSegmentLight(LightEntity, WLEDDeviceEntity):
|
||||
@wled_exception_handler
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn off the light."""
|
||||
data = {ATTR_ON: False}
|
||||
data: dict[str, bool | int] = {ATTR_ON: False}
|
||||
|
||||
if ATTR_TRANSITION in kwargs:
|
||||
# WLED uses 100ms per unit, so 10 = 1 second.
|
||||
@ -312,7 +313,10 @@ class WLEDSegmentLight(LightEntity, WLEDDeviceEntity):
|
||||
@wled_exception_handler
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""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:
|
||||
mireds = color_util.color_temperature_kelvin_to_mired(
|
||||
@ -385,7 +389,7 @@ class WLEDSegmentLight(LightEntity, WLEDDeviceEntity):
|
||||
speed: int | None = None,
|
||||
) -> None:
|
||||
"""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:
|
||||
data[ATTR_EFFECT] = effect
|
||||
@ -419,7 +423,7 @@ class WLEDSegmentLight(LightEntity, WLEDDeviceEntity):
|
||||
def async_update_segments(
|
||||
entry: ConfigEntry,
|
||||
coordinator: WLEDDataUpdateCoordinator,
|
||||
current: dict[int, WLEDSegmentLight],
|
||||
current: dict[int, WLEDSegmentLight | WLEDMasterLight],
|
||||
async_add_entities,
|
||||
) -> None:
|
||||
"""Update segments."""
|
||||
@ -459,7 +463,7 @@ def async_update_segments(
|
||||
async def async_remove_entity(
|
||||
index: int,
|
||||
coordinator: WLEDDataUpdateCoordinator,
|
||||
current: dict[int, WLEDSegmentLight],
|
||||
current: dict[int, WLEDSegmentLight | WLEDMasterLight],
|
||||
) -> None:
|
||||
"""Remove WLED segment light from Home Assistant."""
|
||||
entity = current[index]
|
||||
|
@ -74,7 +74,7 @@ class WLEDSensor(WLEDDeviceEntity, SensorEntity):
|
||||
return f"{self.coordinator.data.info.mac_address}_{self._key}"
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self) -> str:
|
||||
def unit_of_measurement(self) -> str | None:
|
||||
"""Return the unit this state is expressed in."""
|
||||
return self._unit_of_measurement
|
||||
|
||||
|
3
mypy.ini
3
mypy.ini
@ -1325,9 +1325,6 @@ ignore_errors = true
|
||||
[mypy-homeassistant.components.withings.*]
|
||||
ignore_errors = true
|
||||
|
||||
[mypy-homeassistant.components.wled.*]
|
||||
ignore_errors = true
|
||||
|
||||
[mypy-homeassistant.components.wunderground.*]
|
||||
ignore_errors = true
|
||||
|
||||
|
@ -236,7 +236,6 @@ IGNORED_MODULES: Final[list[str]] = [
|
||||
"homeassistant.components.wemo.*",
|
||||
"homeassistant.components.wink.*",
|
||||
"homeassistant.components.withings.*",
|
||||
"homeassistant.components.wled.*",
|
||||
"homeassistant.components.wunderground.*",
|
||||
"homeassistant.components.xbox.*",
|
||||
"homeassistant.components.xiaomi_aqara.*",
|
||||
|
@ -119,19 +119,6 @@ async def test_zeroconf_confirm_connection_error(
|
||||
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(
|
||||
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
|
||||
) -> None:
|
||||
|
Loading…
x
Reference in New Issue
Block a user