mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 05:07:41 +00:00
2023.8.1 (#97772)
This commit is contained in:
commit
489860a28b
@ -7,7 +7,7 @@
|
|||||||
"documentation": "https://www.home-assistant.io/integrations/apple_tv",
|
"documentation": "https://www.home-assistant.io/integrations/apple_tv",
|
||||||
"iot_class": "local_push",
|
"iot_class": "local_push",
|
||||||
"loggers": ["pyatv", "srptools"],
|
"loggers": ["pyatv", "srptools"],
|
||||||
"requirements": ["pyatv==0.13.2"],
|
"requirements": ["pyatv==0.13.3"],
|
||||||
"zeroconf": [
|
"zeroconf": [
|
||||||
"_mediaremotetv._tcp.local.",
|
"_mediaremotetv._tcp.local.",
|
||||||
"_companion-link._tcp.local.",
|
"_companion-link._tcp.local.",
|
||||||
|
@ -19,6 +19,6 @@
|
|||||||
"bluetooth-adapters==0.16.0",
|
"bluetooth-adapters==0.16.0",
|
||||||
"bluetooth-auto-recovery==1.2.1",
|
"bluetooth-auto-recovery==1.2.1",
|
||||||
"bluetooth-data-tools==1.6.1",
|
"bluetooth-data-tools==1.6.1",
|
||||||
"dbus-fast==1.87.5"
|
"dbus-fast==1.90.1"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -15,9 +15,11 @@ from homeassistant.components.sensor import (
|
|||||||
DOMAIN as SENSOR_DOMAIN,
|
DOMAIN as SENSOR_DOMAIN,
|
||||||
PLATFORM_SCHEMA,
|
PLATFORM_SCHEMA,
|
||||||
STATE_CLASSES_SCHEMA,
|
STATE_CLASSES_SCHEMA,
|
||||||
|
SensorDeviceClass,
|
||||||
SensorEntity,
|
SensorEntity,
|
||||||
SensorStateClass,
|
SensorStateClass,
|
||||||
)
|
)
|
||||||
|
from homeassistant.components.sensor.helpers import async_parse_date_datetime
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_COMMAND,
|
CONF_COMMAND,
|
||||||
CONF_DEVICE_CLASS,
|
CONF_DEVICE_CLASS,
|
||||||
@ -206,14 +208,24 @@ class CommandSensor(ManualTriggerEntity, SensorEntity):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if self._value_template is not None:
|
if self._value_template is not None:
|
||||||
self._attr_native_value = (
|
value = self._value_template.async_render_with_possible_json_value(
|
||||||
self._value_template.async_render_with_possible_json_value(
|
value,
|
||||||
value,
|
None,
|
||||||
None,
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
else:
|
|
||||||
|
if self.device_class not in {
|
||||||
|
SensorDeviceClass.DATE,
|
||||||
|
SensorDeviceClass.TIMESTAMP,
|
||||||
|
}:
|
||||||
self._attr_native_value = value
|
self._attr_native_value = value
|
||||||
|
self._process_manual_data(value)
|
||||||
|
return
|
||||||
|
|
||||||
|
self._attr_native_value = None
|
||||||
|
if value is not None:
|
||||||
|
self._attr_native_value = async_parse_date_datetime(
|
||||||
|
value, self.entity_id, self.device_class
|
||||||
|
)
|
||||||
self._process_manual_data(value)
|
self._process_manual_data(value)
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
@ -7,5 +7,5 @@
|
|||||||
"integration_type": "system",
|
"integration_type": "system",
|
||||||
"iot_class": "local_push",
|
"iot_class": "local_push",
|
||||||
"quality_scale": "internal",
|
"quality_scale": "internal",
|
||||||
"requirements": ["hassil==1.2.5", "home-assistant-intents==2023.7.25"]
|
"requirements": ["hassil==1.2.5", "home-assistant-intents==2023.8.2"]
|
||||||
}
|
}
|
||||||
|
@ -5,5 +5,5 @@
|
|||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/duotecno",
|
"documentation": "https://www.home-assistant.io/integrations/duotecno",
|
||||||
"iot_class": "local_push",
|
"iot_class": "local_push",
|
||||||
"requirements": ["pyduotecno==2023.8.0"]
|
"requirements": ["pyduotecno==2023.8.3"]
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import logging
|
|||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant.components.http import HomeAssistantAccessLogger
|
||||||
from homeassistant.components.network import async_get_source_ip
|
from homeassistant.components.network import async_get_source_ip
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_ENTITIES,
|
CONF_ENTITIES,
|
||||||
@ -100,7 +101,7 @@ async def start_emulated_hue_bridge(
|
|||||||
config.advertise_port or config.listen_port,
|
config.advertise_port or config.listen_port,
|
||||||
)
|
)
|
||||||
|
|
||||||
runner = web.AppRunner(app)
|
runner = web.AppRunner(app, access_log_class=HomeAssistantAccessLogger)
|
||||||
await runner.setup()
|
await runner.setup()
|
||||||
|
|
||||||
site = web.TCPSite(runner, config.host_ip_addr, config.listen_port)
|
site = web.TCPSite(runner, config.host_ip_addr, config.listen_port)
|
||||||
|
@ -18,9 +18,10 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
"""Remove forked-daapd component."""
|
"""Remove forked-daapd component."""
|
||||||
status = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
status = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||||
if status and hass.data.get(DOMAIN) and hass.data[DOMAIN].get(entry.entry_id):
|
if status and hass.data.get(DOMAIN) and hass.data[DOMAIN].get(entry.entry_id):
|
||||||
hass.data[DOMAIN][entry.entry_id][
|
if websocket_handler := hass.data[DOMAIN][entry.entry_id][
|
||||||
HASS_DATA_UPDATER_KEY
|
HASS_DATA_UPDATER_KEY
|
||||||
].websocket_handler.cancel()
|
].websocket_handler:
|
||||||
|
websocket_handler.cancel()
|
||||||
for remove_listener in hass.data[DOMAIN][entry.entry_id][
|
for remove_listener in hass.data[DOMAIN][entry.entry_id][
|
||||||
HASS_DATA_REMOVE_LISTENERS_KEY
|
HASS_DATA_REMOVE_LISTENERS_KEY
|
||||||
]:
|
]:
|
||||||
|
@ -31,6 +31,7 @@ from homeassistant.components.spotify import (
|
|||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT
|
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
|
from homeassistant.exceptions import PlatformNotReady
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
from homeassistant.helpers.dispatcher import (
|
from homeassistant.helpers.dispatcher import (
|
||||||
async_dispatcher_connect,
|
async_dispatcher_connect,
|
||||||
@ -127,10 +128,10 @@ async def async_setup_entry(
|
|||||||
forked_daapd_updater = ForkedDaapdUpdater(
|
forked_daapd_updater = ForkedDaapdUpdater(
|
||||||
hass, forked_daapd_api, config_entry.entry_id
|
hass, forked_daapd_api, config_entry.entry_id
|
||||||
)
|
)
|
||||||
await forked_daapd_updater.async_init()
|
|
||||||
hass.data[DOMAIN][config_entry.entry_id][
|
hass.data[DOMAIN][config_entry.entry_id][
|
||||||
HASS_DATA_UPDATER_KEY
|
HASS_DATA_UPDATER_KEY
|
||||||
] = forked_daapd_updater
|
] = forked_daapd_updater
|
||||||
|
await forked_daapd_updater.async_init()
|
||||||
|
|
||||||
|
|
||||||
async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||||
@ -914,7 +915,8 @@ class ForkedDaapdUpdater:
|
|||||||
|
|
||||||
async def async_init(self):
|
async def async_init(self):
|
||||||
"""Perform async portion of class initialization."""
|
"""Perform async portion of class initialization."""
|
||||||
server_config = await self._api.get_request("config")
|
if not (server_config := await self._api.get_request("config")):
|
||||||
|
raise PlatformNotReady
|
||||||
if websocket_port := server_config.get("websocket_port"):
|
if websocket_port := server_config.get("websocket_port"):
|
||||||
self.websocket_handler = asyncio.create_task(
|
self.websocket_handler = asyncio.create_task(
|
||||||
self._api.start_websocket_handler(
|
self._api.start_websocket_handler(
|
||||||
|
@ -161,10 +161,13 @@ class FreeboxRouter:
|
|||||||
async def _update_raids_sensors(self) -> None:
|
async def _update_raids_sensors(self) -> None:
|
||||||
"""Update Freebox raids."""
|
"""Update Freebox raids."""
|
||||||
# None at first request
|
# None at first request
|
||||||
fbx_raids: list[dict[str, Any]] = await self._api.storage.get_raids() or []
|
try:
|
||||||
|
fbx_raids: list[dict[str, Any]] = await self._api.storage.get_raids() or []
|
||||||
for fbx_raid in fbx_raids:
|
except HttpRequestError:
|
||||||
self.raids[fbx_raid["id"]] = fbx_raid
|
_LOGGER.warning("Unable to enumerate raid disks")
|
||||||
|
else:
|
||||||
|
for fbx_raid in fbx_raids:
|
||||||
|
self.raids[fbx_raid["id"]] = fbx_raid
|
||||||
|
|
||||||
async def update_home_devices(self) -> None:
|
async def update_home_devices(self) -> None:
|
||||||
"""Update Home devices (alarm, light, sensor, switch, remote ...)."""
|
"""Update Home devices (alarm, light, sensor, switch, remote ...)."""
|
||||||
|
@ -469,6 +469,11 @@ class FritzBoxTools(
|
|||||||
if not host.get("MACAddress"):
|
if not host.get("MACAddress"):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if (wan_access := host.get("X_AVM-DE_WANAccess")) is not None:
|
||||||
|
wan_access_result = "granted" in wan_access
|
||||||
|
else:
|
||||||
|
wan_access_result = None
|
||||||
|
|
||||||
hosts[host["MACAddress"]] = Device(
|
hosts[host["MACAddress"]] = Device(
|
||||||
name=host["HostName"],
|
name=host["HostName"],
|
||||||
connected=host["Active"],
|
connected=host["Active"],
|
||||||
@ -476,7 +481,7 @@ class FritzBoxTools(
|
|||||||
connection_type="",
|
connection_type="",
|
||||||
ip_address=host["IPAddress"],
|
ip_address=host["IPAddress"],
|
||||||
ssid=None,
|
ssid=None,
|
||||||
wan_access="granted" in host["X_AVM-DE_WANAccess"],
|
wan_access=wan_access_result,
|
||||||
)
|
)
|
||||||
|
|
||||||
if not self.fritz_status.device_has_mesh_support or (
|
if not self.fritz_status.device_has_mesh_support or (
|
||||||
|
@ -89,6 +89,7 @@ class HueLight(HueBaseEntity, LightEntity):
|
|||||||
self._supported_color_modes.add(ColorMode.BRIGHTNESS)
|
self._supported_color_modes.add(ColorMode.BRIGHTNESS)
|
||||||
# support transition if brightness control
|
# support transition if brightness control
|
||||||
self._attr_supported_features |= LightEntityFeature.TRANSITION
|
self._attr_supported_features |= LightEntityFeature.TRANSITION
|
||||||
|
self._color_temp_active: bool = False
|
||||||
# get list of supported effects (combine effects and timed_effects)
|
# get list of supported effects (combine effects and timed_effects)
|
||||||
self._attr_effect_list = []
|
self._attr_effect_list = []
|
||||||
if effects := resource.effects:
|
if effects := resource.effects:
|
||||||
@ -121,10 +122,8 @@ class HueLight(HueBaseEntity, LightEntity):
|
|||||||
@property
|
@property
|
||||||
def color_mode(self) -> ColorMode:
|
def color_mode(self) -> ColorMode:
|
||||||
"""Return the color mode of the light."""
|
"""Return the color mode of the light."""
|
||||||
if color_temp := self.resource.color_temperature:
|
if self.color_temp_active:
|
||||||
# Hue lights return `mired_valid` to indicate CT is active
|
return ColorMode.COLOR_TEMP
|
||||||
if color_temp.mirek is not None:
|
|
||||||
return ColorMode.COLOR_TEMP
|
|
||||||
if self.resource.supports_color:
|
if self.resource.supports_color:
|
||||||
return ColorMode.XY
|
return ColorMode.XY
|
||||||
if self.resource.supports_dimming:
|
if self.resource.supports_dimming:
|
||||||
@ -132,6 +131,18 @@ class HueLight(HueBaseEntity, LightEntity):
|
|||||||
# fallback to on_off
|
# fallback to on_off
|
||||||
return ColorMode.ONOFF
|
return ColorMode.ONOFF
|
||||||
|
|
||||||
|
@property
|
||||||
|
def color_temp_active(self) -> bool:
|
||||||
|
"""Return if the light is in Color Temperature mode."""
|
||||||
|
color_temp = self.resource.color_temperature
|
||||||
|
if color_temp is None or color_temp.mirek is None:
|
||||||
|
return False
|
||||||
|
# Official Hue lights return `mirek_valid` to indicate CT is active
|
||||||
|
# while non-official lights do not.
|
||||||
|
if self.device.product_data.certified:
|
||||||
|
return self.resource.color_temperature.mirek_valid
|
||||||
|
return self._color_temp_active
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def xy_color(self) -> tuple[float, float] | None:
|
def xy_color(self) -> tuple[float, float] | None:
|
||||||
"""Return the xy color."""
|
"""Return the xy color."""
|
||||||
@ -193,6 +204,7 @@ class HueLight(HueBaseEntity, LightEntity):
|
|||||||
xy_color = kwargs.get(ATTR_XY_COLOR)
|
xy_color = kwargs.get(ATTR_XY_COLOR)
|
||||||
color_temp = normalize_hue_colortemp(kwargs.get(ATTR_COLOR_TEMP))
|
color_temp = normalize_hue_colortemp(kwargs.get(ATTR_COLOR_TEMP))
|
||||||
brightness = normalize_hue_brightness(kwargs.get(ATTR_BRIGHTNESS))
|
brightness = normalize_hue_brightness(kwargs.get(ATTR_BRIGHTNESS))
|
||||||
|
self._color_temp_active = color_temp is not None
|
||||||
flash = kwargs.get(ATTR_FLASH)
|
flash = kwargs.get(ATTR_FLASH)
|
||||||
effect = effect_str = kwargs.get(ATTR_EFFECT)
|
effect = effect_str = kwargs.get(ATTR_EFFECT)
|
||||||
if effect_str in (EFFECT_NONE, EFFECT_NONE.lower()):
|
if effect_str in (EFFECT_NONE, EFFECT_NONE.lower()):
|
||||||
|
@ -138,7 +138,7 @@ class MicroBotConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
await self._client.connect(init=True)
|
await self._client.connect(init=True)
|
||||||
return self.async_show_form(step_id="link")
|
return self.async_show_form(step_id="link")
|
||||||
|
|
||||||
if not self._client.is_connected():
|
if not await self._client.is_connected():
|
||||||
errors["base"] = "linking"
|
errors["base"] = "linking"
|
||||||
else:
|
else:
|
||||||
await self._client.disconnect()
|
await self._client.disconnect()
|
||||||
|
@ -342,10 +342,13 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
unload_ok = await hass.config_entries.async_unload_platforms(
|
unload_ok = await hass.config_entries.async_unload_platforms(
|
||||||
entry,
|
entry,
|
||||||
[
|
[
|
||||||
platform
|
Platform.SENSOR, # always unload system entities (telegram counter, etc.)
|
||||||
for platform in SUPPORTED_PLATFORMS
|
*[
|
||||||
if platform in hass.data[DATA_KNX_CONFIG]
|
platform
|
||||||
and platform is not Platform.NOTIFY
|
for platform in SUPPORTED_PLATFORMS
|
||||||
|
if platform in hass.data[DATA_KNX_CONFIG]
|
||||||
|
and platform not in (Platform.SENSOR, Platform.NOTIFY)
|
||||||
|
],
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
if unload_ok:
|
if unload_ok:
|
||||||
|
@ -111,7 +111,7 @@ class PlenticoreDataSelect(
|
|||||||
self.platform_name = platform_name
|
self.platform_name = platform_name
|
||||||
self.module_id = description.module_id
|
self.module_id = description.module_id
|
||||||
self.data_id = description.key
|
self.data_id = description.key
|
||||||
self._device_info = device_info
|
self._attr_device_info = device_info
|
||||||
self._attr_unique_id = f"{entry_id}_{description.module_id}"
|
self._attr_unique_id = f"{entry_id}_{description.module_id}"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -26,7 +26,6 @@ CONF_STATION = "station"
|
|||||||
ATTRIBUTION = "Data from National Weather Service/NOAA"
|
ATTRIBUTION = "Data from National Weather Service/NOAA"
|
||||||
|
|
||||||
ATTR_FORECAST_DETAILED_DESCRIPTION = "detailed_description"
|
ATTR_FORECAST_DETAILED_DESCRIPTION = "detailed_description"
|
||||||
ATTR_FORECAST_DAYTIME = "daytime"
|
|
||||||
|
|
||||||
CONDITION_CLASSES: dict[str, list[str]] = {
|
CONDITION_CLASSES: dict[str, list[str]] = {
|
||||||
ATTR_CONDITION_EXCEPTIONAL: [
|
ATTR_CONDITION_EXCEPTIONAL: [
|
||||||
|
@ -9,6 +9,7 @@ from homeassistant.components.weather import (
|
|||||||
ATTR_CONDITION_SUNNY,
|
ATTR_CONDITION_SUNNY,
|
||||||
ATTR_FORECAST_CONDITION,
|
ATTR_FORECAST_CONDITION,
|
||||||
ATTR_FORECAST_HUMIDITY,
|
ATTR_FORECAST_HUMIDITY,
|
||||||
|
ATTR_FORECAST_IS_DAYTIME,
|
||||||
ATTR_FORECAST_NATIVE_DEW_POINT,
|
ATTR_FORECAST_NATIVE_DEW_POINT,
|
||||||
ATTR_FORECAST_NATIVE_TEMP,
|
ATTR_FORECAST_NATIVE_TEMP,
|
||||||
ATTR_FORECAST_NATIVE_WIND_SPEED,
|
ATTR_FORECAST_NATIVE_WIND_SPEED,
|
||||||
@ -36,7 +37,6 @@ from homeassistant.util.unit_system import UnitSystem
|
|||||||
|
|
||||||
from . import base_unique_id, device_info
|
from . import base_unique_id, device_info
|
||||||
from .const import (
|
from .const import (
|
||||||
ATTR_FORECAST_DAYTIME,
|
|
||||||
ATTR_FORECAST_DETAILED_DESCRIPTION,
|
ATTR_FORECAST_DETAILED_DESCRIPTION,
|
||||||
ATTRIBUTION,
|
ATTRIBUTION,
|
||||||
CONDITION_CLASSES,
|
CONDITION_CLASSES,
|
||||||
@ -101,7 +101,6 @@ if TYPE_CHECKING:
|
|||||||
"""Forecast with extra fields needed for NWS."""
|
"""Forecast with extra fields needed for NWS."""
|
||||||
|
|
||||||
detailed_description: str | None
|
detailed_description: str | None
|
||||||
daytime: bool | None
|
|
||||||
|
|
||||||
|
|
||||||
class NWSWeather(WeatherEntity):
|
class NWSWeather(WeatherEntity):
|
||||||
@ -268,7 +267,7 @@ class NWSWeather(WeatherEntity):
|
|||||||
data[ATTR_FORECAST_HUMIDITY] = forecast_entry.get("relativeHumidity")
|
data[ATTR_FORECAST_HUMIDITY] = forecast_entry.get("relativeHumidity")
|
||||||
|
|
||||||
if self.mode == DAYNIGHT:
|
if self.mode == DAYNIGHT:
|
||||||
data[ATTR_FORECAST_DAYTIME] = forecast_entry.get("isDaytime")
|
data[ATTR_FORECAST_IS_DAYTIME] = forecast_entry.get("isDaytime")
|
||||||
|
|
||||||
time = forecast_entry.get("iconTime")
|
time = forecast_entry.get("iconTime")
|
||||||
weather = forecast_entry.get("iconWeather")
|
weather = forecast_entry.get("iconWeather")
|
||||||
|
@ -6,5 +6,5 @@
|
|||||||
"dependencies": ["recorder"],
|
"dependencies": ["recorder"],
|
||||||
"documentation": "https://www.home-assistant.io/integrations/opower",
|
"documentation": "https://www.home-assistant.io/integrations/opower",
|
||||||
"iot_class": "cloud_polling",
|
"iot_class": "cloud_polling",
|
||||||
"requirements": ["opower==0.0.18"]
|
"requirements": ["opower==0.0.20"]
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,7 @@ SENSOR_DESCRIPTIONS: list[OverkizSensorDescription] = [
|
|||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
icon="mdi:battery",
|
icon="mdi:battery",
|
||||||
device_class=SensorDeviceClass.ENUM,
|
device_class=SensorDeviceClass.ENUM,
|
||||||
options=["full", "normal", "low", "verylow"],
|
options=["full", "normal", "medium", "low", "verylow"],
|
||||||
translation_key="battery",
|
translation_key="battery",
|
||||||
),
|
),
|
||||||
OverkizSensorDescription(
|
OverkizSensorDescription(
|
||||||
|
@ -77,6 +77,7 @@
|
|||||||
"full": "Full",
|
"full": "Full",
|
||||||
"low": "Low",
|
"low": "Low",
|
||||||
"normal": "Normal",
|
"normal": "Normal",
|
||||||
|
"medium": "Medium",
|
||||||
"verylow": "Very low"
|
"verylow": "Very low"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -6,5 +6,5 @@
|
|||||||
"documentation": "https://www.home-assistant.io/integrations/roborock",
|
"documentation": "https://www.home-assistant.io/integrations/roborock",
|
||||||
"iot_class": "local_polling",
|
"iot_class": "local_polling",
|
||||||
"loggers": ["roborock"],
|
"loggers": ["roborock"],
|
||||||
"requirements": ["python-roborock==0.30.2"]
|
"requirements": ["python-roborock==0.31.1"]
|
||||||
}
|
}
|
||||||
|
@ -218,6 +218,8 @@ async def async_setup_entry(
|
|||||||
class SolarlogSensor(CoordinatorEntity[SolarlogData], SensorEntity):
|
class SolarlogSensor(CoordinatorEntity[SolarlogData], SensorEntity):
|
||||||
"""Representation of a Sensor."""
|
"""Representation of a Sensor."""
|
||||||
|
|
||||||
|
_attr_has_entity_name = True
|
||||||
|
|
||||||
entity_description: SolarLogSensorEntityDescription
|
entity_description: SolarLogSensorEntityDescription
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@ -228,7 +230,6 @@ class SolarlogSensor(CoordinatorEntity[SolarlogData], SensorEntity):
|
|||||||
"""Initialize the sensor."""
|
"""Initialize the sensor."""
|
||||||
super().__init__(coordinator)
|
super().__init__(coordinator)
|
||||||
self.entity_description = description
|
self.entity_description = description
|
||||||
self._attr_name = f"{coordinator.name} {description.name}"
|
|
||||||
self._attr_unique_id = f"{coordinator.unique_id}_{description.key}"
|
self._attr_unique_id = f"{coordinator.unique_id}_{description.key}"
|
||||||
self._attr_device_info = DeviceInfo(
|
self._attr_device_info = DeviceInfo(
|
||||||
identifiers={(DOMAIN, coordinator.unique_id)},
|
identifiers={(DOMAIN, coordinator.unique_id)},
|
||||||
|
@ -57,25 +57,29 @@ def async_discovery_data_from_service(
|
|||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
ext_addr = service.properties.get(b"xa")
|
# Service properties are always bytes if they are set from the network.
|
||||||
ext_pan_id = service.properties.get(b"xp")
|
# For legacy backwards compatibility zeroconf allows properties to be set
|
||||||
network_name = try_decode(service.properties.get(b"nn"))
|
# as strings but we never do that so we can safely cast here.
|
||||||
model_name = try_decode(service.properties.get(b"mn"))
|
service_properties = cast(dict[bytes, bytes | None], service.properties)
|
||||||
|
ext_addr = service_properties.get(b"xa")
|
||||||
|
ext_pan_id = service_properties.get(b"xp")
|
||||||
|
network_name = try_decode(service_properties.get(b"nn"))
|
||||||
|
model_name = try_decode(service_properties.get(b"mn"))
|
||||||
server = service.server
|
server = service.server
|
||||||
vendor_name = try_decode(service.properties.get(b"vn"))
|
vendor_name = try_decode(service_properties.get(b"vn"))
|
||||||
thread_version = try_decode(service.properties.get(b"tv"))
|
thread_version = try_decode(service_properties.get(b"tv"))
|
||||||
unconfigured = None
|
unconfigured = None
|
||||||
brand = KNOWN_BRANDS.get(vendor_name)
|
brand = KNOWN_BRANDS.get(vendor_name)
|
||||||
if brand == "homeassistant":
|
if brand == "homeassistant":
|
||||||
# Attempt to detect incomplete configuration
|
# Attempt to detect incomplete configuration
|
||||||
if (state_bitmap_b := service.properties.get(b"sb")) is not None:
|
if (state_bitmap_b := service_properties.get(b"sb")) is not None:
|
||||||
try:
|
try:
|
||||||
state_bitmap = StateBitmap.from_bytes(state_bitmap_b)
|
state_bitmap = StateBitmap.from_bytes(state_bitmap_b)
|
||||||
if not state_bitmap.is_active:
|
if not state_bitmap.is_active:
|
||||||
unconfigured = True
|
unconfigured = True
|
||||||
except ValueError:
|
except ValueError:
|
||||||
_LOGGER.debug("Failed to decode state bitmap in service %s", service)
|
_LOGGER.debug("Failed to decode state bitmap in service %s", service)
|
||||||
if service.properties.get(b"at") is None:
|
if service_properties.get(b"at") is None:
|
||||||
unconfigured = True
|
unconfigured = True
|
||||||
|
|
||||||
return ThreadRouterDiscoveryData(
|
return ThreadRouterDiscoveryData(
|
||||||
@ -168,10 +172,19 @@ class ThreadRouterDiscovery:
|
|||||||
return
|
return
|
||||||
|
|
||||||
_LOGGER.debug("_add_update_service %s %s", name, service)
|
_LOGGER.debug("_add_update_service %s %s", name, service)
|
||||||
|
# Service properties are always bytes if they are set from the network.
|
||||||
|
# For legacy backwards compatibility zeroconf allows properties to be set
|
||||||
|
# as strings but we never do that so we can safely cast here.
|
||||||
|
service_properties = cast(dict[bytes, bytes | None], service.properties)
|
||||||
|
|
||||||
|
if not (xa := service_properties.get(b"xa")):
|
||||||
|
_LOGGER.debug("_add_update_service failed to find xa in %s", service)
|
||||||
|
return
|
||||||
|
|
||||||
# We use the extended mac address as key, bail out if it's missing
|
# We use the extended mac address as key, bail out if it's missing
|
||||||
try:
|
try:
|
||||||
extended_mac_address = service.properties[b"xa"].hex()
|
extended_mac_address = xa.hex()
|
||||||
except (KeyError, UnicodeDecodeError) as err:
|
except UnicodeDecodeError as err:
|
||||||
_LOGGER.debug("_add_update_service failed to parse service %s", err)
|
_LOGGER.debug("_add_update_service failed to parse service %s", err)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -154,7 +154,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"hostname": "k[lps]*",
|
"hostname": "k[lps]*",
|
||||||
"macaddress": "788C5B*"
|
"macaddress": "788CB5*"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"documentation": "https://www.home-assistant.io/integrations/tplink",
|
"documentation": "https://www.home-assistant.io/integrations/tplink",
|
||||||
|
@ -116,7 +116,7 @@ class SmartPlugSwitchChild(SmartPlugSwitch):
|
|||||||
coordinator: TPLinkDataUpdateCoordinator,
|
coordinator: TPLinkDataUpdateCoordinator,
|
||||||
plug: SmartDevice,
|
plug: SmartDevice,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the switch."""
|
"""Initialize the child switch."""
|
||||||
super().__init__(device, coordinator)
|
super().__init__(device, coordinator)
|
||||||
self._plug = plug
|
self._plug = plug
|
||||||
self._attr_unique_id = legacy_device_id(plug)
|
self._attr_unique_id = legacy_device_id(plug)
|
||||||
@ -124,10 +124,15 @@ class SmartPlugSwitchChild(SmartPlugSwitch):
|
|||||||
|
|
||||||
@async_refresh_after
|
@async_refresh_after
|
||||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||||
"""Turn the switch on."""
|
"""Turn the child switch on."""
|
||||||
await self._plug.turn_on()
|
await self._plug.turn_on()
|
||||||
|
|
||||||
@async_refresh_after
|
@async_refresh_after
|
||||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||||
"""Turn the switch off."""
|
"""Turn the child switch off."""
|
||||||
await self._plug.turn_off()
|
await self._plug.turn_off()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_on(self) -> bool:
|
||||||
|
"""Return true if child switch is on."""
|
||||||
|
return bool(self._plug.is_on)
|
||||||
|
@ -41,7 +41,7 @@ class UnifiImageEntityDescriptionMixin(Generic[HandlerT, ApiItemT]):
|
|||||||
"""Validate and load entities from different UniFi handlers."""
|
"""Validate and load entities from different UniFi handlers."""
|
||||||
|
|
||||||
image_fn: Callable[[UniFiController, ApiItemT], bytes]
|
image_fn: Callable[[UniFiController, ApiItemT], bytes]
|
||||||
value_fn: Callable[[ApiItemT], str]
|
value_fn: Callable[[ApiItemT], str | None]
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@ -99,7 +99,7 @@ class UnifiImageEntity(UnifiEntity[HandlerT, ApiItemT], ImageEntity):
|
|||||||
_attr_content_type = "image/png"
|
_attr_content_type = "image/png"
|
||||||
|
|
||||||
current_image: bytes | None = None
|
current_image: bytes | None = None
|
||||||
previous_value = ""
|
previous_value: str | None = None
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
"iot_class": "local_push",
|
"iot_class": "local_push",
|
||||||
"loggers": ["aiounifi"],
|
"loggers": ["aiounifi"],
|
||||||
"quality_scale": "platinum",
|
"quality_scale": "platinum",
|
||||||
"requirements": ["aiounifi==50"],
|
"requirements": ["aiounifi==51"],
|
||||||
"ssdp": [
|
"ssdp": [
|
||||||
{
|
{
|
||||||
"manufacturer": "Ubiquiti Networks",
|
"manufacturer": "Ubiquiti Networks",
|
||||||
|
@ -86,10 +86,10 @@ def _setup_entities(devices, async_add_entities):
|
|||||||
class VeSyncFanHA(VeSyncDevice, FanEntity):
|
class VeSyncFanHA(VeSyncDevice, FanEntity):
|
||||||
"""Representation of a VeSync fan."""
|
"""Representation of a VeSync fan."""
|
||||||
|
|
||||||
_attr_supported_features = FanEntityFeature.SET_SPEED
|
_attr_supported_features = FanEntityFeature.SET_SPEED | FanEntityFeature.PRESET_MODE
|
||||||
_attr_name = None
|
_attr_name = None
|
||||||
|
|
||||||
def __init__(self, fan):
|
def __init__(self, fan) -> None:
|
||||||
"""Initialize the VeSync fan device."""
|
"""Initialize the VeSync fan device."""
|
||||||
super().__init__(fan)
|
super().__init__(fan)
|
||||||
self.smartfan = fan
|
self.smartfan = fan
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
from contextlib import suppress
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@ -141,8 +142,9 @@ class WaqiSensor(SensorEntity):
|
|||||||
@property
|
@property
|
||||||
def native_value(self):
|
def native_value(self):
|
||||||
"""Return the state of the device."""
|
"""Return the state of the device."""
|
||||||
if self._data is not None:
|
if (value := self._data.get("aqi")) is not None:
|
||||||
return self._data.get("aqi")
|
with suppress(ValueError):
|
||||||
|
return float(value)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -553,11 +553,17 @@ def info_from_service(service: AsyncServiceInfo) -> ZeroconfServiceInfo | None:
|
|||||||
break
|
break
|
||||||
if not host:
|
if not host:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
# Service properties are always bytes if they are set from the network.
|
||||||
|
# For legacy backwards compatibility zeroconf allows properties to be set
|
||||||
|
# as strings but we never do that so we can safely cast here.
|
||||||
|
service_properties = cast(dict[bytes, bytes | None], service.properties)
|
||||||
|
|
||||||
properties: dict[str, Any] = {
|
properties: dict[str, Any] = {
|
||||||
k.decode("ascii", "replace"): None
|
k.decode("ascii", "replace"): None
|
||||||
if v is None
|
if v is None
|
||||||
else v.decode("utf-8", "replace")
|
else v.decode("utf-8", "replace")
|
||||||
for k, v in service.properties.items()
|
for k, v in service_properties.items()
|
||||||
}
|
}
|
||||||
|
|
||||||
assert service.server is not None, "server cannot be none if there are addresses"
|
assert service.server is not None, "server cannot be none if there are addresses"
|
||||||
|
@ -8,5 +8,5 @@
|
|||||||
"iot_class": "local_push",
|
"iot_class": "local_push",
|
||||||
"loggers": ["zeroconf"],
|
"loggers": ["zeroconf"],
|
||||||
"quality_scale": "internal",
|
"quality_scale": "internal",
|
||||||
"requirements": ["zeroconf==0.72.0"]
|
"requirements": ["zeroconf==0.74.0"]
|
||||||
}
|
}
|
||||||
|
@ -329,7 +329,7 @@ class BaseLight(LogMixin, light.LightEntity):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(brightness is not None or transition)
|
(brightness is not None or transition is not None)
|
||||||
and not new_color_provided_while_off
|
and not new_color_provided_while_off
|
||||||
and brightness_supported(self._attr_supported_color_modes)
|
and brightness_supported(self._attr_supported_color_modes)
|
||||||
):
|
):
|
||||||
@ -350,11 +350,11 @@ class BaseLight(LogMixin, light.LightEntity):
|
|||||||
self._attr_brightness = level
|
self._attr_brightness = level
|
||||||
|
|
||||||
if (
|
if (
|
||||||
brightness is None
|
(brightness is None and transition is None)
|
||||||
and not new_color_provided_while_off
|
and not new_color_provided_while_off
|
||||||
or (self._FORCE_ON and brightness)
|
or (self._FORCE_ON and brightness != 0)
|
||||||
):
|
):
|
||||||
# since some lights don't always turn on with move_to_level_with_on_off,
|
# since FORCE_ON lights don't turn on with move_to_level_with_on_off,
|
||||||
# we should call the on command on the on_off cluster
|
# we should call the on command on the on_off cluster
|
||||||
# if brightness is not 0.
|
# if brightness is not 0.
|
||||||
result = await self._on_off_cluster_handler.on()
|
result = await self._on_off_cluster_handler.on()
|
||||||
@ -385,7 +385,7 @@ class BaseLight(LogMixin, light.LightEntity):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if new_color_provided_while_off:
|
if new_color_provided_while_off:
|
||||||
# The light is has the correct color, so we can now transition
|
# The light has the correct color, so we can now transition
|
||||||
# it to the correct brightness level.
|
# it to the correct brightness level.
|
||||||
result = await self._level_cluster_handler.move_to_level(
|
result = await self._level_cluster_handler.move_to_level(
|
||||||
level=level, transition_time=int(10 * duration)
|
level=level, transition_time=int(10 * duration)
|
||||||
@ -1076,7 +1076,7 @@ class HueLight(Light):
|
|||||||
manufacturers={"Jasco", "Quotra-Vision", "eWeLight", "eWeLink"},
|
manufacturers={"Jasco", "Quotra-Vision", "eWeLight", "eWeLink"},
|
||||||
)
|
)
|
||||||
class ForceOnLight(Light):
|
class ForceOnLight(Light):
|
||||||
"""Representation of a light which does not respect move_to_level_with_on_off."""
|
"""Representation of a light which does not respect on/off for move_to_level_with_on_off commands."""
|
||||||
|
|
||||||
_attr_name: str = "Light"
|
_attr_name: str = "Light"
|
||||||
_FORCE_ON = True
|
_FORCE_ON = True
|
||||||
|
@ -25,10 +25,10 @@
|
|||||||
"pyserial-asyncio==0.6",
|
"pyserial-asyncio==0.6",
|
||||||
"zha-quirks==0.0.102",
|
"zha-quirks==0.0.102",
|
||||||
"zigpy-deconz==0.21.0",
|
"zigpy-deconz==0.21.0",
|
||||||
"zigpy==0.56.2",
|
"zigpy==0.56.4",
|
||||||
"zigpy-xbee==0.18.1",
|
"zigpy-xbee==0.18.1",
|
||||||
"zigpy-zigate==0.11.0",
|
"zigpy-zigate==0.11.0",
|
||||||
"zigpy-znp==0.11.3"
|
"zigpy-znp==0.11.4"
|
||||||
],
|
],
|
||||||
"usb": [
|
"usb": [
|
||||||
{
|
{
|
||||||
|
@ -7,7 +7,7 @@ from typing import Final
|
|||||||
APPLICATION_NAME: Final = "HomeAssistant"
|
APPLICATION_NAME: Final = "HomeAssistant"
|
||||||
MAJOR_VERSION: Final = 2023
|
MAJOR_VERSION: Final = 2023
|
||||||
MINOR_VERSION: Final = 8
|
MINOR_VERSION: Final = 8
|
||||||
PATCH_VERSION: Final = "0"
|
PATCH_VERSION: Final = "1"
|
||||||
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
||||||
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
|
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
|
||||||
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 11, 0)
|
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 11, 0)
|
||||||
|
@ -782,7 +782,7 @@ DHCP: list[dict[str, str | bool]] = [
|
|||||||
{
|
{
|
||||||
"domain": "tplink",
|
"domain": "tplink",
|
||||||
"hostname": "k[lps]*",
|
"hostname": "k[lps]*",
|
||||||
"macaddress": "788C5B*",
|
"macaddress": "788CB5*",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"domain": "tuya",
|
"domain": "tuya",
|
||||||
|
@ -15,15 +15,15 @@ bluetooth-auto-recovery==1.2.1
|
|||||||
bluetooth-data-tools==1.6.1
|
bluetooth-data-tools==1.6.1
|
||||||
certifi>=2021.5.30
|
certifi>=2021.5.30
|
||||||
ciso8601==2.3.0
|
ciso8601==2.3.0
|
||||||
cryptography==41.0.2
|
cryptography==41.0.3
|
||||||
dbus-fast==1.87.5
|
dbus-fast==1.90.1
|
||||||
fnv-hash-fast==0.4.0
|
fnv-hash-fast==0.4.0
|
||||||
ha-av==10.1.1
|
ha-av==10.1.1
|
||||||
hass-nabucasa==0.69.0
|
hass-nabucasa==0.69.0
|
||||||
hassil==1.2.5
|
hassil==1.2.5
|
||||||
home-assistant-bluetooth==1.10.2
|
home-assistant-bluetooth==1.10.2
|
||||||
home-assistant-frontend==20230802.0
|
home-assistant-frontend==20230802.0
|
||||||
home-assistant-intents==2023.7.25
|
home-assistant-intents==2023.8.2
|
||||||
httpx==0.24.1
|
httpx==0.24.1
|
||||||
ifaddr==0.2.0
|
ifaddr==0.2.0
|
||||||
janus==1.0.0
|
janus==1.0.0
|
||||||
@ -52,7 +52,7 @@ voluptuous-serialize==2.6.0
|
|||||||
voluptuous==0.13.1
|
voluptuous==0.13.1
|
||||||
webrtcvad==2.0.10
|
webrtcvad==2.0.10
|
||||||
yarl==1.9.2
|
yarl==1.9.2
|
||||||
zeroconf==0.72.0
|
zeroconf==0.74.0
|
||||||
|
|
||||||
# Constrain pycryptodome to avoid vulnerability
|
# Constrain pycryptodome to avoid vulnerability
|
||||||
# see https://github.com/home-assistant/core/pull/16238
|
# see https://github.com/home-assistant/core/pull/16238
|
||||||
|
@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "homeassistant"
|
name = "homeassistant"
|
||||||
version = "2023.8.0"
|
version = "2023.8.1"
|
||||||
license = {text = "Apache-2.0"}
|
license = {text = "Apache-2.0"}
|
||||||
description = "Open-source home automation platform running on Python 3."
|
description = "Open-source home automation platform running on Python 3."
|
||||||
readme = "README.rst"
|
readme = "README.rst"
|
||||||
@ -41,7 +41,7 @@ dependencies = [
|
|||||||
"lru-dict==1.2.0",
|
"lru-dict==1.2.0",
|
||||||
"PyJWT==2.8.0",
|
"PyJWT==2.8.0",
|
||||||
# PyJWT has loose dependency. We want the latest one.
|
# PyJWT has loose dependency. We want the latest one.
|
||||||
"cryptography==41.0.2",
|
"cryptography==41.0.3",
|
||||||
# pyOpenSSL 23.2.0 is required to work with cryptography 41+
|
# pyOpenSSL 23.2.0 is required to work with cryptography 41+
|
||||||
"pyOpenSSL==23.2.0",
|
"pyOpenSSL==23.2.0",
|
||||||
"orjson==3.9.2",
|
"orjson==3.9.2",
|
||||||
|
@ -16,7 +16,7 @@ ifaddr==0.2.0
|
|||||||
Jinja2==3.1.2
|
Jinja2==3.1.2
|
||||||
lru-dict==1.2.0
|
lru-dict==1.2.0
|
||||||
PyJWT==2.8.0
|
PyJWT==2.8.0
|
||||||
cryptography==41.0.2
|
cryptography==41.0.3
|
||||||
pyOpenSSL==23.2.0
|
pyOpenSSL==23.2.0
|
||||||
orjson==3.9.2
|
orjson==3.9.2
|
||||||
pip>=21.3.1
|
pip>=21.3.1
|
||||||
|
@ -360,7 +360,7 @@ aiosyncthing==0.5.1
|
|||||||
aiotractive==0.5.5
|
aiotractive==0.5.5
|
||||||
|
|
||||||
# homeassistant.components.unifi
|
# homeassistant.components.unifi
|
||||||
aiounifi==50
|
aiounifi==51
|
||||||
|
|
||||||
# homeassistant.components.vlc_telnet
|
# homeassistant.components.vlc_telnet
|
||||||
aiovlc==0.1.0
|
aiovlc==0.1.0
|
||||||
@ -632,7 +632,7 @@ datadog==0.15.0
|
|||||||
datapoint==0.9.8
|
datapoint==0.9.8
|
||||||
|
|
||||||
# homeassistant.components.bluetooth
|
# homeassistant.components.bluetooth
|
||||||
dbus-fast==1.87.5
|
dbus-fast==1.90.1
|
||||||
|
|
||||||
# homeassistant.components.debugpy
|
# homeassistant.components.debugpy
|
||||||
debugpy==1.6.7
|
debugpy==1.6.7
|
||||||
@ -991,7 +991,7 @@ holidays==0.28
|
|||||||
home-assistant-frontend==20230802.0
|
home-assistant-frontend==20230802.0
|
||||||
|
|
||||||
# homeassistant.components.conversation
|
# homeassistant.components.conversation
|
||||||
home-assistant-intents==2023.7.25
|
home-assistant-intents==2023.8.2
|
||||||
|
|
||||||
# homeassistant.components.home_connect
|
# homeassistant.components.home_connect
|
||||||
homeconnect==0.7.2
|
homeconnect==0.7.2
|
||||||
@ -1368,7 +1368,7 @@ openwrt-luci-rpc==1.1.16
|
|||||||
openwrt-ubus-rpc==0.0.2
|
openwrt-ubus-rpc==0.0.2
|
||||||
|
|
||||||
# homeassistant.components.opower
|
# homeassistant.components.opower
|
||||||
opower==0.0.18
|
opower==0.0.20
|
||||||
|
|
||||||
# homeassistant.components.oralb
|
# homeassistant.components.oralb
|
||||||
oralb-ble==0.17.6
|
oralb-ble==0.17.6
|
||||||
@ -1572,7 +1572,7 @@ pyatag==0.3.5.3
|
|||||||
pyatmo==7.5.0
|
pyatmo==7.5.0
|
||||||
|
|
||||||
# homeassistant.components.apple_tv
|
# homeassistant.components.apple_tv
|
||||||
pyatv==0.13.2
|
pyatv==0.13.3
|
||||||
|
|
||||||
# homeassistant.components.aussie_broadband
|
# homeassistant.components.aussie_broadband
|
||||||
pyaussiebb==0.0.15
|
pyaussiebb==0.0.15
|
||||||
@ -1650,7 +1650,7 @@ pydrawise==2023.7.1
|
|||||||
pydroid-ipcam==2.0.0
|
pydroid-ipcam==2.0.0
|
||||||
|
|
||||||
# homeassistant.components.duotecno
|
# homeassistant.components.duotecno
|
||||||
pyduotecno==2023.8.0
|
pyduotecno==2023.8.3
|
||||||
|
|
||||||
# homeassistant.components.ebox
|
# homeassistant.components.ebox
|
||||||
pyebox==1.1.4
|
pyebox==1.1.4
|
||||||
@ -2150,7 +2150,7 @@ python-qbittorrent==0.4.3
|
|||||||
python-ripple-api==0.0.3
|
python-ripple-api==0.0.3
|
||||||
|
|
||||||
# homeassistant.components.roborock
|
# homeassistant.components.roborock
|
||||||
python-roborock==0.30.2
|
python-roborock==0.31.1
|
||||||
|
|
||||||
# homeassistant.components.smarttub
|
# homeassistant.components.smarttub
|
||||||
python-smarttub==0.0.33
|
python-smarttub==0.0.33
|
||||||
@ -2749,7 +2749,7 @@ zamg==0.2.4
|
|||||||
zengge==0.2
|
zengge==0.2
|
||||||
|
|
||||||
# homeassistant.components.zeroconf
|
# homeassistant.components.zeroconf
|
||||||
zeroconf==0.72.0
|
zeroconf==0.74.0
|
||||||
|
|
||||||
# homeassistant.components.zeversolar
|
# homeassistant.components.zeversolar
|
||||||
zeversolar==0.3.1
|
zeversolar==0.3.1
|
||||||
@ -2773,10 +2773,10 @@ zigpy-xbee==0.18.1
|
|||||||
zigpy-zigate==0.11.0
|
zigpy-zigate==0.11.0
|
||||||
|
|
||||||
# homeassistant.components.zha
|
# homeassistant.components.zha
|
||||||
zigpy-znp==0.11.3
|
zigpy-znp==0.11.4
|
||||||
|
|
||||||
# homeassistant.components.zha
|
# homeassistant.components.zha
|
||||||
zigpy==0.56.2
|
zigpy==0.56.4
|
||||||
|
|
||||||
# homeassistant.components.zoneminder
|
# homeassistant.components.zoneminder
|
||||||
zm-py==0.5.2
|
zm-py==0.5.2
|
||||||
|
@ -335,7 +335,7 @@ aiosyncthing==0.5.1
|
|||||||
aiotractive==0.5.5
|
aiotractive==0.5.5
|
||||||
|
|
||||||
# homeassistant.components.unifi
|
# homeassistant.components.unifi
|
||||||
aiounifi==50
|
aiounifi==51
|
||||||
|
|
||||||
# homeassistant.components.vlc_telnet
|
# homeassistant.components.vlc_telnet
|
||||||
aiovlc==0.1.0
|
aiovlc==0.1.0
|
||||||
@ -515,7 +515,7 @@ datadog==0.15.0
|
|||||||
datapoint==0.9.8
|
datapoint==0.9.8
|
||||||
|
|
||||||
# homeassistant.components.bluetooth
|
# homeassistant.components.bluetooth
|
||||||
dbus-fast==1.87.5
|
dbus-fast==1.90.1
|
||||||
|
|
||||||
# homeassistant.components.debugpy
|
# homeassistant.components.debugpy
|
||||||
debugpy==1.6.7
|
debugpy==1.6.7
|
||||||
@ -777,7 +777,7 @@ holidays==0.28
|
|||||||
home-assistant-frontend==20230802.0
|
home-assistant-frontend==20230802.0
|
||||||
|
|
||||||
# homeassistant.components.conversation
|
# homeassistant.components.conversation
|
||||||
home-assistant-intents==2023.7.25
|
home-assistant-intents==2023.8.2
|
||||||
|
|
||||||
# homeassistant.components.home_connect
|
# homeassistant.components.home_connect
|
||||||
homeconnect==0.7.2
|
homeconnect==0.7.2
|
||||||
@ -1037,7 +1037,7 @@ openerz-api==0.2.0
|
|||||||
openhomedevice==2.2.0
|
openhomedevice==2.2.0
|
||||||
|
|
||||||
# homeassistant.components.opower
|
# homeassistant.components.opower
|
||||||
opower==0.0.18
|
opower==0.0.20
|
||||||
|
|
||||||
# homeassistant.components.oralb
|
# homeassistant.components.oralb
|
||||||
oralb-ble==0.17.6
|
oralb-ble==0.17.6
|
||||||
@ -1178,7 +1178,7 @@ pyatag==0.3.5.3
|
|||||||
pyatmo==7.5.0
|
pyatmo==7.5.0
|
||||||
|
|
||||||
# homeassistant.components.apple_tv
|
# homeassistant.components.apple_tv
|
||||||
pyatv==0.13.2
|
pyatv==0.13.3
|
||||||
|
|
||||||
# homeassistant.components.aussie_broadband
|
# homeassistant.components.aussie_broadband
|
||||||
pyaussiebb==0.0.15
|
pyaussiebb==0.0.15
|
||||||
@ -1223,7 +1223,7 @@ pydiscovergy==2.0.1
|
|||||||
pydroid-ipcam==2.0.0
|
pydroid-ipcam==2.0.0
|
||||||
|
|
||||||
# homeassistant.components.duotecno
|
# homeassistant.components.duotecno
|
||||||
pyduotecno==2023.8.0
|
pyduotecno==2023.8.3
|
||||||
|
|
||||||
# homeassistant.components.econet
|
# homeassistant.components.econet
|
||||||
pyeconet==0.1.20
|
pyeconet==0.1.20
|
||||||
@ -1579,7 +1579,7 @@ python-picnic-api==1.1.0
|
|||||||
python-qbittorrent==0.4.3
|
python-qbittorrent==0.4.3
|
||||||
|
|
||||||
# homeassistant.components.roborock
|
# homeassistant.components.roborock
|
||||||
python-roborock==0.30.2
|
python-roborock==0.31.1
|
||||||
|
|
||||||
# homeassistant.components.smarttub
|
# homeassistant.components.smarttub
|
||||||
python-smarttub==0.0.33
|
python-smarttub==0.0.33
|
||||||
@ -2022,7 +2022,7 @@ youtubeaio==1.1.5
|
|||||||
zamg==0.2.4
|
zamg==0.2.4
|
||||||
|
|
||||||
# homeassistant.components.zeroconf
|
# homeassistant.components.zeroconf
|
||||||
zeroconf==0.72.0
|
zeroconf==0.74.0
|
||||||
|
|
||||||
# homeassistant.components.zeversolar
|
# homeassistant.components.zeversolar
|
||||||
zeversolar==0.3.1
|
zeversolar==0.3.1
|
||||||
@ -2040,10 +2040,10 @@ zigpy-xbee==0.18.1
|
|||||||
zigpy-zigate==0.11.0
|
zigpy-zigate==0.11.0
|
||||||
|
|
||||||
# homeassistant.components.zha
|
# homeassistant.components.zha
|
||||||
zigpy-znp==0.11.3
|
zigpy-znp==0.11.4
|
||||||
|
|
||||||
# homeassistant.components.zha
|
# homeassistant.components.zha
|
||||||
zigpy==0.56.2
|
zigpy==0.56.4
|
||||||
|
|
||||||
# homeassistant.components.zwave_js
|
# homeassistant.components.zwave_js
|
||||||
zwave-js-server-python==0.49.0
|
zwave-js-server-python==0.49.0
|
||||||
|
@ -61,8 +61,9 @@ def allow_name_translation(integration: Integration) -> bool:
|
|||||||
"""Validate that the translation name is not the same as the integration name."""
|
"""Validate that the translation name is not the same as the integration name."""
|
||||||
# Only enforce for core because custom integrations can't be
|
# Only enforce for core because custom integrations can't be
|
||||||
# added to allow list.
|
# added to allow list.
|
||||||
return integration.core and (
|
return (
|
||||||
integration.domain in ALLOW_NAME_TRANSLATION
|
not integration.core
|
||||||
|
or integration.domain in ALLOW_NAME_TRANSLATION
|
||||||
or integration.quality_scale == "internal"
|
or integration.quality_scale == "internal"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -646,3 +646,54 @@ async def test_updating_manually(
|
|||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert called
|
assert called
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"get_config",
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"command_line": [
|
||||||
|
{
|
||||||
|
"sensor": {
|
||||||
|
"name": "Test",
|
||||||
|
"command": "echo 2022-12-22T13:15:30Z",
|
||||||
|
"device_class": "timestamp",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_scrape_sensor_device_timestamp(
|
||||||
|
hass: HomeAssistant, load_yaml_integration: None
|
||||||
|
) -> None:
|
||||||
|
"""Test Command Line sensor with a device of type TIMESTAMP."""
|
||||||
|
entity_state = hass.states.get("sensor.test")
|
||||||
|
assert entity_state
|
||||||
|
assert entity_state.state == "2022-12-22T13:15:30+00:00"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"get_config",
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"command_line": [
|
||||||
|
{
|
||||||
|
"sensor": {
|
||||||
|
"name": "Test",
|
||||||
|
"command": "echo January 17, 2022",
|
||||||
|
"device_class": "date",
|
||||||
|
"value_template": "{{ strptime(value, '%B %d, %Y').strftime('%Y-%m-%d') }}",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_scrape_sensor_device_date(
|
||||||
|
hass: HomeAssistant, load_yaml_integration: None
|
||||||
|
) -> None:
|
||||||
|
"""Test Command Line sensor with a device of type DATE."""
|
||||||
|
entity_state = hass.states.get("sensor.test")
|
||||||
|
assert entity_state
|
||||||
|
assert entity_state.state == "2022-01-17"
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
'id': 'homeassistant',
|
'id': 'homeassistant',
|
||||||
'name': 'Home Assistant',
|
'name': 'Home Assistant',
|
||||||
'supported_languages': list([
|
'supported_languages': list([
|
||||||
|
'af',
|
||||||
'ar',
|
'ar',
|
||||||
'bg',
|
'bg',
|
||||||
'bn',
|
'bn',
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
"""The config flow tests for the forked_daapd media player platform."""
|
"""The config flow tests for the forked_daapd media player platform."""
|
||||||
from unittest.mock import AsyncMock, patch
|
from unittest.mock import AsyncMock, MagicMock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@ -12,9 +12,11 @@ from homeassistant.components.forked_daapd.const import (
|
|||||||
CONF_TTS_VOLUME,
|
CONF_TTS_VOLUME,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
)
|
)
|
||||||
|
from homeassistant.components.forked_daapd.media_player import async_setup_entry
|
||||||
from homeassistant.config_entries import SOURCE_USER, SOURCE_ZEROCONF
|
from homeassistant.config_entries import SOURCE_USER, SOURCE_ZEROCONF
|
||||||
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT
|
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.exceptions import PlatformNotReady
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
@ -242,3 +244,18 @@ async def test_options_flow(hass: HomeAssistant, config_entry) -> None:
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY
|
assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY
|
||||||
|
|
||||||
|
|
||||||
|
async def test_async_setup_entry_not_ready(hass: HomeAssistant, config_entry) -> None:
|
||||||
|
"""Test that a PlatformNotReady exception is thrown during platform setup."""
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.forked_daapd.media_player.ForkedDaapdAPI",
|
||||||
|
autospec=True,
|
||||||
|
) as mock_api:
|
||||||
|
mock_api.return_value.get_request.return_value = None
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
with pytest.raises(PlatformNotReady):
|
||||||
|
await async_setup_entry(hass, config_entry, MagicMock())
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
mock_api.return_value.get_request.assert_called_once()
|
||||||
|
@ -77,6 +77,6 @@ class MockMicroBotApiClientFail:
|
|||||||
async def disconnect(self):
|
async def disconnect(self):
|
||||||
"""Mock disconnect."""
|
"""Mock disconnect."""
|
||||||
|
|
||||||
def is_connected(self):
|
async def is_connected(self):
|
||||||
"""Mock disconnected."""
|
"""Mock disconnected."""
|
||||||
return False
|
return False
|
||||||
|
@ -180,12 +180,14 @@ def _mocked_strip() -> SmartStrip:
|
|||||||
plug0.alias = "Plug0"
|
plug0.alias = "Plug0"
|
||||||
plug0.device_id = "bb:bb:cc:dd:ee:ff_PLUG0DEVICEID"
|
plug0.device_id = "bb:bb:cc:dd:ee:ff_PLUG0DEVICEID"
|
||||||
plug0.mac = "bb:bb:cc:dd:ee:ff"
|
plug0.mac = "bb:bb:cc:dd:ee:ff"
|
||||||
|
plug0.is_on = True
|
||||||
plug0.protocol = _mock_protocol()
|
plug0.protocol = _mock_protocol()
|
||||||
plug1 = _mocked_plug()
|
plug1 = _mocked_plug()
|
||||||
plug1.device_id = "cc:bb:cc:dd:ee:ff_PLUG1DEVICEID"
|
plug1.device_id = "cc:bb:cc:dd:ee:ff_PLUG1DEVICEID"
|
||||||
plug1.mac = "cc:bb:cc:dd:ee:ff"
|
plug1.mac = "cc:bb:cc:dd:ee:ff"
|
||||||
plug1.alias = "Plug1"
|
plug1.alias = "Plug1"
|
||||||
plug1.protocol = _mock_protocol()
|
plug1.protocol = _mock_protocol()
|
||||||
|
plug1.is_on = False
|
||||||
strip.children = [plug0, plug1]
|
strip.children = [plug0, plug1]
|
||||||
return strip
|
return strip
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ import pytest
|
|||||||
from homeassistant.components import tplink
|
from homeassistant.components import tplink
|
||||||
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
|
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
|
||||||
from homeassistant.components.tplink.const import DOMAIN
|
from homeassistant.components.tplink.const import DOMAIN
|
||||||
from homeassistant.const import ATTR_ENTITY_ID, STATE_ON, STATE_UNAVAILABLE
|
from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF, STATE_ON, STATE_UNAVAILABLE
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers import entity_registry as er
|
from homeassistant.helpers import entity_registry as er
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
@ -146,22 +146,37 @@ async def test_strip(hass: HomeAssistant) -> None:
|
|||||||
# since this is what the previous version did
|
# since this is what the previous version did
|
||||||
assert hass.states.get("switch.my_strip") is None
|
assert hass.states.get("switch.my_strip") is None
|
||||||
|
|
||||||
for plug_id in range(2):
|
entity_id = "switch.my_strip_plug0"
|
||||||
entity_id = f"switch.my_strip_plug{plug_id}"
|
state = hass.states.get(entity_id)
|
||||||
state = hass.states.get(entity_id)
|
assert state.state == STATE_ON
|
||||||
assert state.state == STATE_ON
|
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
SWITCH_DOMAIN, "turn_off", {ATTR_ENTITY_ID: entity_id}, blocking=True
|
SWITCH_DOMAIN, "turn_off", {ATTR_ENTITY_ID: entity_id}, blocking=True
|
||||||
)
|
)
|
||||||
strip.children[plug_id].turn_off.assert_called_once()
|
strip.children[0].turn_off.assert_called_once()
|
||||||
strip.children[plug_id].turn_off.reset_mock()
|
strip.children[0].turn_off.reset_mock()
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
SWITCH_DOMAIN, "turn_on", {ATTR_ENTITY_ID: entity_id}, blocking=True
|
SWITCH_DOMAIN, "turn_on", {ATTR_ENTITY_ID: entity_id}, blocking=True
|
||||||
)
|
)
|
||||||
strip.children[plug_id].turn_on.assert_called_once()
|
strip.children[0].turn_on.assert_called_once()
|
||||||
strip.children[plug_id].turn_on.reset_mock()
|
strip.children[0].turn_on.reset_mock()
|
||||||
|
|
||||||
|
entity_id = "switch.my_strip_plug1"
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state.state == STATE_OFF
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
SWITCH_DOMAIN, "turn_off", {ATTR_ENTITY_ID: entity_id}, blocking=True
|
||||||
|
)
|
||||||
|
strip.children[1].turn_off.assert_called_once()
|
||||||
|
strip.children[1].turn_off.reset_mock()
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
SWITCH_DOMAIN, "turn_on", {ATTR_ENTITY_ID: entity_id}, blocking=True
|
||||||
|
)
|
||||||
|
strip.children[1].turn_on.assert_called_once()
|
||||||
|
strip.children[1].turn_on.reset_mock()
|
||||||
|
|
||||||
|
|
||||||
async def test_strip_unique_ids(hass: HomeAssistant) -> None:
|
async def test_strip_unique_ids(hass: HomeAssistant) -> None:
|
||||||
|
@ -200,7 +200,7 @@
|
|||||||
'auto',
|
'auto',
|
||||||
'sleep',
|
'sleep',
|
||||||
]),
|
]),
|
||||||
'supported_features': 1,
|
'supported_features': 9,
|
||||||
}),
|
}),
|
||||||
'entity_id': 'fan.fan',
|
'entity_id': 'fan.fan',
|
||||||
'last_changed': str,
|
'last_changed': str,
|
||||||
|
@ -58,7 +58,7 @@
|
|||||||
'original_icon': None,
|
'original_icon': None,
|
||||||
'original_name': None,
|
'original_name': None,
|
||||||
'platform': 'vesync',
|
'platform': 'vesync',
|
||||||
'supported_features': <FanEntityFeature: 1>,
|
'supported_features': <FanEntityFeature: 9>,
|
||||||
'translation_key': None,
|
'translation_key': None,
|
||||||
'unique_id': 'air-purifier',
|
'unique_id': 'air-purifier',
|
||||||
'unit_of_measurement': None,
|
'unit_of_measurement': None,
|
||||||
@ -73,7 +73,7 @@
|
|||||||
'auto',
|
'auto',
|
||||||
'sleep',
|
'sleep',
|
||||||
]),
|
]),
|
||||||
'supported_features': <FanEntityFeature: 1>,
|
'supported_features': <FanEntityFeature: 9>,
|
||||||
}),
|
}),
|
||||||
'context': <ANY>,
|
'context': <ANY>,
|
||||||
'entity_id': 'fan.air_purifier_131s',
|
'entity_id': 'fan.air_purifier_131s',
|
||||||
@ -140,7 +140,7 @@
|
|||||||
'original_icon': None,
|
'original_icon': None,
|
||||||
'original_name': None,
|
'original_name': None,
|
||||||
'platform': 'vesync',
|
'platform': 'vesync',
|
||||||
'supported_features': <FanEntityFeature: 1>,
|
'supported_features': <FanEntityFeature: 9>,
|
||||||
'translation_key': None,
|
'translation_key': None,
|
||||||
'unique_id': 'asd_sdfKIHG7IJHGwJGJ7GJ_ag5h3G55',
|
'unique_id': 'asd_sdfKIHG7IJHGwJGJ7GJ_ag5h3G55',
|
||||||
'unit_of_measurement': None,
|
'unit_of_measurement': None,
|
||||||
@ -161,7 +161,7 @@
|
|||||||
'sleep',
|
'sleep',
|
||||||
]),
|
]),
|
||||||
'screen_status': True,
|
'screen_status': True,
|
||||||
'supported_features': <FanEntityFeature: 1>,
|
'supported_features': <FanEntityFeature: 9>,
|
||||||
}),
|
}),
|
||||||
'context': <ANY>,
|
'context': <ANY>,
|
||||||
'entity_id': 'fan.air_purifier_200s',
|
'entity_id': 'fan.air_purifier_200s',
|
||||||
@ -229,7 +229,7 @@
|
|||||||
'original_icon': None,
|
'original_icon': None,
|
||||||
'original_name': None,
|
'original_name': None,
|
||||||
'platform': 'vesync',
|
'platform': 'vesync',
|
||||||
'supported_features': <FanEntityFeature: 1>,
|
'supported_features': <FanEntityFeature: 9>,
|
||||||
'translation_key': None,
|
'translation_key': None,
|
||||||
'unique_id': '400s-purifier',
|
'unique_id': '400s-purifier',
|
||||||
'unit_of_measurement': None,
|
'unit_of_measurement': None,
|
||||||
@ -251,7 +251,7 @@
|
|||||||
'sleep',
|
'sleep',
|
||||||
]),
|
]),
|
||||||
'screen_status': True,
|
'screen_status': True,
|
||||||
'supported_features': <FanEntityFeature: 1>,
|
'supported_features': <FanEntityFeature: 9>,
|
||||||
}),
|
}),
|
||||||
'context': <ANY>,
|
'context': <ANY>,
|
||||||
'entity_id': 'fan.air_purifier_400s',
|
'entity_id': 'fan.air_purifier_400s',
|
||||||
@ -319,7 +319,7 @@
|
|||||||
'original_icon': None,
|
'original_icon': None,
|
||||||
'original_name': None,
|
'original_name': None,
|
||||||
'platform': 'vesync',
|
'platform': 'vesync',
|
||||||
'supported_features': <FanEntityFeature: 1>,
|
'supported_features': <FanEntityFeature: 9>,
|
||||||
'translation_key': None,
|
'translation_key': None,
|
||||||
'unique_id': '600s-purifier',
|
'unique_id': '600s-purifier',
|
||||||
'unit_of_measurement': None,
|
'unit_of_measurement': None,
|
||||||
@ -341,7 +341,7 @@
|
|||||||
'sleep',
|
'sleep',
|
||||||
]),
|
]),
|
||||||
'screen_status': True,
|
'screen_status': True,
|
||||||
'supported_features': <FanEntityFeature: 1>,
|
'supported_features': <FanEntityFeature: 9>,
|
||||||
}),
|
}),
|
||||||
'context': <ANY>,
|
'context': <ANY>,
|
||||||
'entity_id': 'fan.air_purifier_600s',
|
'entity_id': 'fan.air_purifier_600s',
|
||||||
|
@ -530,7 +530,78 @@ async def test_transitions(
|
|||||||
light2_state = hass.states.get(device_2_entity_id)
|
light2_state = hass.states.get(device_2_entity_id)
|
||||||
assert light2_state.state == STATE_OFF
|
assert light2_state.state == STATE_OFF
|
||||||
|
|
||||||
# first test 0 length transition with no color provided
|
# first test 0 length transition with no color and no brightness provided
|
||||||
|
dev1_cluster_on_off.request.reset_mock()
|
||||||
|
dev1_cluster_level.request.reset_mock()
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
"turn_on",
|
||||||
|
{"entity_id": device_1_entity_id, "transition": 0},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
assert dev1_cluster_on_off.request.call_count == 0
|
||||||
|
assert dev1_cluster_on_off.request.await_count == 0
|
||||||
|
assert dev1_cluster_color.request.call_count == 0
|
||||||
|
assert dev1_cluster_color.request.await_count == 0
|
||||||
|
assert dev1_cluster_level.request.call_count == 1
|
||||||
|
assert dev1_cluster_level.request.await_count == 1
|
||||||
|
assert dev1_cluster_level.request.call_args == call(
|
||||||
|
False,
|
||||||
|
dev1_cluster_level.commands_by_name["move_to_level_with_on_off"].id,
|
||||||
|
dev1_cluster_level.commands_by_name["move_to_level_with_on_off"].schema,
|
||||||
|
level=254, # default "full on" brightness
|
||||||
|
transition_time=0,
|
||||||
|
expect_reply=True,
|
||||||
|
manufacturer=None,
|
||||||
|
tsn=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
light1_state = hass.states.get(device_1_entity_id)
|
||||||
|
assert light1_state.state == STATE_ON
|
||||||
|
assert light1_state.attributes["brightness"] == 254
|
||||||
|
|
||||||
|
# test 0 length transition with no color and no brightness provided again, but for "force on" lights
|
||||||
|
eWeLink_cluster_on_off.request.reset_mock()
|
||||||
|
eWeLink_cluster_level.request.reset_mock()
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
"turn_on",
|
||||||
|
{"entity_id": eWeLink_light_entity_id, "transition": 0},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
assert eWeLink_cluster_on_off.request.call_count == 1
|
||||||
|
assert eWeLink_cluster_on_off.request.await_count == 1
|
||||||
|
assert eWeLink_cluster_on_off.request.call_args_list[0] == call(
|
||||||
|
False,
|
||||||
|
eWeLink_cluster_on_off.commands_by_name["on"].id,
|
||||||
|
eWeLink_cluster_on_off.commands_by_name["on"].schema,
|
||||||
|
expect_reply=True,
|
||||||
|
manufacturer=None,
|
||||||
|
tsn=None,
|
||||||
|
)
|
||||||
|
assert eWeLink_cluster_color.request.call_count == 0
|
||||||
|
assert eWeLink_cluster_color.request.await_count == 0
|
||||||
|
assert eWeLink_cluster_level.request.call_count == 1
|
||||||
|
assert eWeLink_cluster_level.request.await_count == 1
|
||||||
|
assert eWeLink_cluster_level.request.call_args == call(
|
||||||
|
False,
|
||||||
|
eWeLink_cluster_level.commands_by_name["move_to_level_with_on_off"].id,
|
||||||
|
eWeLink_cluster_level.commands_by_name["move_to_level_with_on_off"].schema,
|
||||||
|
level=254, # default "full on" brightness
|
||||||
|
transition_time=0,
|
||||||
|
expect_reply=True,
|
||||||
|
manufacturer=None,
|
||||||
|
tsn=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
eWeLink_state = hass.states.get(eWeLink_light_entity_id)
|
||||||
|
assert eWeLink_state.state == STATE_ON
|
||||||
|
assert eWeLink_state.attributes["brightness"] == 254
|
||||||
|
|
||||||
|
eWeLink_cluster_on_off.request.reset_mock()
|
||||||
|
eWeLink_cluster_level.request.reset_mock()
|
||||||
|
|
||||||
|
# test 0 length transition with brightness, but no color provided
|
||||||
dev1_cluster_on_off.request.reset_mock()
|
dev1_cluster_on_off.request.reset_mock()
|
||||||
dev1_cluster_level.request.reset_mock()
|
dev1_cluster_level.request.reset_mock()
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
@ -1423,18 +1494,10 @@ async def async_test_level_on_off_from_hass(
|
|||||||
{"entity_id": entity_id, "transition": 10},
|
{"entity_id": entity_id, "transition": 10},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
assert on_off_cluster.request.call_count == 1
|
assert on_off_cluster.request.call_count == 0
|
||||||
assert on_off_cluster.request.await_count == 1
|
assert on_off_cluster.request.await_count == 0
|
||||||
assert level_cluster.request.call_count == 1
|
assert level_cluster.request.call_count == 1
|
||||||
assert level_cluster.request.await_count == 1
|
assert level_cluster.request.await_count == 1
|
||||||
assert on_off_cluster.request.call_args == call(
|
|
||||||
False,
|
|
||||||
on_off_cluster.commands_by_name["on"].id,
|
|
||||||
on_off_cluster.commands_by_name["on"].schema,
|
|
||||||
expect_reply=True,
|
|
||||||
manufacturer=None,
|
|
||||||
tsn=None,
|
|
||||||
)
|
|
||||||
assert level_cluster.request.call_args == call(
|
assert level_cluster.request.call_args == call(
|
||||||
False,
|
False,
|
||||||
level_cluster.commands_by_name["move_to_level_with_on_off"].id,
|
level_cluster.commands_by_name["move_to_level_with_on_off"].id,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user