mirror of
https://github.com/home-assistant/core.git
synced 2025-07-28 15:47:12 +00:00
Merge pull request #50627 from home-assistant/rc
This commit is contained in:
commit
7a0f245f92
@ -3,7 +3,7 @@
|
|||||||
"name": "ASUSWRT",
|
"name": "ASUSWRT",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/asuswrt",
|
"documentation": "https://www.home-assistant.io/integrations/asuswrt",
|
||||||
"requirements": ["aioasuswrt==1.3.1"],
|
"requirements": ["aioasuswrt==1.3.4"],
|
||||||
"codeowners": ["@kennedyshead", "@ollo69"],
|
"codeowners": ["@kennedyshead", "@ollo69"],
|
||||||
"iot_class": "local_polling"
|
"iot_class": "local_polling"
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ from pyiqvia.errors import IQVIAError
|
|||||||
from homeassistant.components.sensor import SensorEntity
|
from homeassistant.components.sensor import SensorEntity
|
||||||
from homeassistant.const import ATTR_ATTRIBUTION
|
from homeassistant.const import ATTR_ATTRIBUTION
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
|
from homeassistant.exceptions import ConfigEntryNotReady
|
||||||
from homeassistant.helpers import aiohttp_client
|
from homeassistant.helpers import aiohttp_client
|
||||||
from homeassistant.helpers.update_coordinator import (
|
from homeassistant.helpers.update_coordinator import (
|
||||||
CoordinatorEntity,
|
CoordinatorEntity,
|
||||||
@ -74,9 +75,14 @@ async def async_setup_entry(hass, entry):
|
|||||||
update_interval=DEFAULT_SCAN_INTERVAL,
|
update_interval=DEFAULT_SCAN_INTERVAL,
|
||||||
update_method=partial(async_get_data_from_api, api_coro),
|
update_method=partial(async_get_data_from_api, api_coro),
|
||||||
)
|
)
|
||||||
init_data_update_tasks.append(coordinator.async_config_entry_first_refresh())
|
init_data_update_tasks.append(coordinator.async_refresh())
|
||||||
|
|
||||||
await asyncio.gather(*init_data_update_tasks)
|
results = await asyncio.gather(*init_data_update_tasks, return_exceptions=True)
|
||||||
|
if all(isinstance(result, Exception) for result in results):
|
||||||
|
# The IQVIA API can be selectively flaky, meaning that any number of the setup
|
||||||
|
# API calls could fail. We only retry integration setup if *all* of the initial
|
||||||
|
# API calls fail:
|
||||||
|
raise ConfigEntryNotReady()
|
||||||
|
|
||||||
hass.data[DOMAIN].setdefault(DATA_COORDINATOR, {})[entry.entry_id] = coordinators
|
hass.data[DOMAIN].setdefault(DATA_COORDINATOR, {})[entry.entry_id] = coordinators
|
||||||
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
|
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
|
||||||
|
@ -104,9 +104,12 @@ class ForecastSensor(IQVIAEntity):
|
|||||||
@callback
|
@callback
|
||||||
def update_from_latest_data(self):
|
def update_from_latest_data(self):
|
||||||
"""Update the sensor."""
|
"""Update the sensor."""
|
||||||
data = self.coordinator.data.get("Location")
|
if not self.coordinator.data:
|
||||||
|
return
|
||||||
|
|
||||||
if not data or not data.get("periods"):
|
data = self.coordinator.data.get("Location", {})
|
||||||
|
|
||||||
|
if not data.get("periods"):
|
||||||
return
|
return
|
||||||
|
|
||||||
indices = [p["Index"] for p in data["periods"]]
|
indices = [p["Index"] for p in data["periods"]]
|
||||||
|
@ -95,21 +95,21 @@ def valid_supported_color_modes(color_modes: Iterable[str]) -> set[str]:
|
|||||||
return color_modes
|
return color_modes
|
||||||
|
|
||||||
|
|
||||||
def brightness_supported(color_modes: Iterable[str]) -> bool:
|
def brightness_supported(color_modes: Iterable[str] | None) -> bool:
|
||||||
"""Test if brightness is supported."""
|
"""Test if brightness is supported."""
|
||||||
if not color_modes:
|
if not color_modes:
|
||||||
return False
|
return False
|
||||||
return any(mode in COLOR_MODES_BRIGHTNESS for mode in color_modes)
|
return any(mode in COLOR_MODES_BRIGHTNESS for mode in color_modes)
|
||||||
|
|
||||||
|
|
||||||
def color_supported(color_modes: Iterable[str]) -> bool:
|
def color_supported(color_modes: Iterable[str] | None) -> bool:
|
||||||
"""Test if color is supported."""
|
"""Test if color is supported."""
|
||||||
if not color_modes:
|
if not color_modes:
|
||||||
return False
|
return False
|
||||||
return any(mode in COLOR_MODES_COLOR for mode in color_modes)
|
return any(mode in COLOR_MODES_COLOR for mode in color_modes)
|
||||||
|
|
||||||
|
|
||||||
def color_temp_supported(color_modes: Iterable[str]) -> bool:
|
def color_temp_supported(color_modes: Iterable[str] | None) -> bool:
|
||||||
"""Test if color temperature is supported."""
|
"""Test if color temperature is supported."""
|
||||||
if not color_modes:
|
if not color_modes:
|
||||||
return False
|
return False
|
||||||
|
@ -13,11 +13,17 @@ from homeassistant.components.light import (
|
|||||||
)
|
)
|
||||||
from homeassistant.const import ATTR_ENTITY_ID, CONF_DOMAIN, CONF_TYPE, SERVICE_TURN_ON
|
from homeassistant.const import ATTR_ENTITY_ID, CONF_DOMAIN, CONF_TYPE, SERVICE_TURN_ON
|
||||||
from homeassistant.core import Context, HomeAssistant, HomeAssistantError
|
from homeassistant.core import Context, HomeAssistant, HomeAssistantError
|
||||||
from homeassistant.helpers import config_validation as cv, entity_registry
|
from homeassistant.helpers import config_validation as cv, entity_registry as er
|
||||||
from homeassistant.helpers.entity import get_supported_features
|
from homeassistant.helpers.entity import get_supported_features
|
||||||
from homeassistant.helpers.typing import ConfigType, TemplateVarsType
|
from homeassistant.helpers.typing import ConfigType, TemplateVarsType
|
||||||
|
|
||||||
from . import ATTR_BRIGHTNESS_PCT, ATTR_BRIGHTNESS_STEP_PCT, DOMAIN, SUPPORT_BRIGHTNESS
|
from . import (
|
||||||
|
ATTR_BRIGHTNESS_PCT,
|
||||||
|
ATTR_BRIGHTNESS_STEP_PCT,
|
||||||
|
ATTR_SUPPORTED_COLOR_MODES,
|
||||||
|
DOMAIN,
|
||||||
|
brightness_supported,
|
||||||
|
)
|
||||||
|
|
||||||
TYPE_BRIGHTNESS_INCREASE = "brightness_increase"
|
TYPE_BRIGHTNESS_INCREASE = "brightness_increase"
|
||||||
TYPE_BRIGHTNESS_DECREASE = "brightness_decrease"
|
TYPE_BRIGHTNESS_DECREASE = "brightness_decrease"
|
||||||
@ -37,6 +43,25 @@ ACTION_SCHEMA = cv.DEVICE_ACTION_BASE_SCHEMA.extend(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_supported_color_modes(hass: HomeAssistant, entity_id: str) -> set | None:
|
||||||
|
"""Get supported color modes for a light entity.
|
||||||
|
|
||||||
|
First try the statemachine, then entity registry.
|
||||||
|
"""
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
if state:
|
||||||
|
return state.attributes.get(ATTR_SUPPORTED_COLOR_MODES)
|
||||||
|
|
||||||
|
entity_registry = er.async_get(hass)
|
||||||
|
entry = entity_registry.async_get(entity_id)
|
||||||
|
if not entry:
|
||||||
|
raise HomeAssistantError(f"Unknown entity {entity_id}")
|
||||||
|
if not entry.capabilities:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return entry.capabilities.get(ATTR_SUPPORTED_COLOR_MODES)
|
||||||
|
|
||||||
|
|
||||||
async def async_call_action_from_config(
|
async def async_call_action_from_config(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config: ConfigType,
|
config: ConfigType,
|
||||||
@ -77,15 +102,16 @@ async def async_get_actions(hass: HomeAssistant, device_id: str) -> list[dict]:
|
|||||||
"""List device actions."""
|
"""List device actions."""
|
||||||
actions = await toggle_entity.async_get_actions(hass, device_id, DOMAIN)
|
actions = await toggle_entity.async_get_actions(hass, device_id, DOMAIN)
|
||||||
|
|
||||||
registry = await entity_registry.async_get_registry(hass)
|
entity_registry = er.async_get(hass)
|
||||||
|
|
||||||
for entry in entity_registry.async_entries_for_device(registry, device_id):
|
for entry in er.async_entries_for_device(entity_registry, device_id):
|
||||||
if entry.domain != DOMAIN:
|
if entry.domain != DOMAIN:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
supported_color_modes = get_supported_color_modes(hass, entry.entity_id)
|
||||||
supported_features = get_supported_features(hass, entry.entity_id)
|
supported_features = get_supported_features(hass, entry.entity_id)
|
||||||
|
|
||||||
if supported_features & SUPPORT_BRIGHTNESS:
|
if brightness_supported(supported_color_modes):
|
||||||
actions.extend(
|
actions.extend(
|
||||||
(
|
(
|
||||||
{
|
{
|
||||||
@ -123,6 +149,11 @@ async def async_get_action_capabilities(hass: HomeAssistant, config: dict) -> di
|
|||||||
if config[CONF_TYPE] != toggle_entity.CONF_TURN_ON:
|
if config[CONF_TYPE] != toggle_entity.CONF_TURN_ON:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
try:
|
||||||
|
supported_color_modes = get_supported_color_modes(hass, config[ATTR_ENTITY_ID])
|
||||||
|
except HomeAssistantError:
|
||||||
|
supported_color_modes = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
supported_features = get_supported_features(hass, config[ATTR_ENTITY_ID])
|
supported_features = get_supported_features(hass, config[ATTR_ENTITY_ID])
|
||||||
except HomeAssistantError:
|
except HomeAssistantError:
|
||||||
@ -130,7 +161,7 @@ async def async_get_action_capabilities(hass: HomeAssistant, config: dict) -> di
|
|||||||
|
|
||||||
extra_fields = {}
|
extra_fields = {}
|
||||||
|
|
||||||
if supported_features & SUPPORT_BRIGHTNESS:
|
if brightness_supported(supported_color_modes):
|
||||||
extra_fields[vol.Optional(ATTR_BRIGHTNESS_PCT)] = VALID_BRIGHTNESS_PCT
|
extra_fields[vol.Optional(ATTR_BRIGHTNESS_PCT)] = VALID_BRIGHTNESS_PCT
|
||||||
|
|
||||||
if supported_features & SUPPORT_FLASH:
|
if supported_features & SUPPORT_FLASH:
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.const import ATTR_ENTITY_ID
|
from homeassistant.const import ATTR_ENTITY_ID
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant, State
|
||||||
from homeassistant.helpers import intent
|
from homeassistant.helpers import intent
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
import homeassistant.util.color as color_util
|
import homeassistant.util.color as color_util
|
||||||
@ -10,10 +10,11 @@ import homeassistant.util.color as color_util
|
|||||||
from . import (
|
from . import (
|
||||||
ATTR_BRIGHTNESS_PCT,
|
ATTR_BRIGHTNESS_PCT,
|
||||||
ATTR_RGB_COLOR,
|
ATTR_RGB_COLOR,
|
||||||
|
ATTR_SUPPORTED_COLOR_MODES,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
SERVICE_TURN_ON,
|
SERVICE_TURN_ON,
|
||||||
SUPPORT_BRIGHTNESS,
|
brightness_supported,
|
||||||
SUPPORT_COLOR,
|
color_supported,
|
||||||
)
|
)
|
||||||
|
|
||||||
INTENT_SET = "HassLightSet"
|
INTENT_SET = "HassLightSet"
|
||||||
@ -24,6 +25,24 @@ async def async_setup_intents(hass: HomeAssistant) -> None:
|
|||||||
hass.helpers.intent.async_register(SetIntentHandler())
|
hass.helpers.intent.async_register(SetIntentHandler())
|
||||||
|
|
||||||
|
|
||||||
|
def _test_supports_color(state: State) -> None:
|
||||||
|
"""Test if state supports colors."""
|
||||||
|
supported_color_modes = state.attributes.get(ATTR_SUPPORTED_COLOR_MODES)
|
||||||
|
if not color_supported(supported_color_modes):
|
||||||
|
raise intent.IntentHandleError(
|
||||||
|
f"Entity {state.name} does not support changing colors"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _test_supports_brightness(state: State) -> None:
|
||||||
|
"""Test if state supports brightness."""
|
||||||
|
supported_color_modes = state.attributes.get(ATTR_SUPPORTED_COLOR_MODES)
|
||||||
|
if not brightness_supported(supported_color_modes):
|
||||||
|
raise intent.IntentHandleError(
|
||||||
|
f"Entity {state.name} does not support changing brightness"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class SetIntentHandler(intent.IntentHandler):
|
class SetIntentHandler(intent.IntentHandler):
|
||||||
"""Handle set color intents."""
|
"""Handle set color intents."""
|
||||||
|
|
||||||
@ -46,14 +65,14 @@ class SetIntentHandler(intent.IntentHandler):
|
|||||||
speech_parts = []
|
speech_parts = []
|
||||||
|
|
||||||
if "color" in slots:
|
if "color" in slots:
|
||||||
intent.async_test_feature(state, SUPPORT_COLOR, "changing colors")
|
_test_supports_color(state)
|
||||||
service_data[ATTR_RGB_COLOR] = slots["color"]["value"]
|
service_data[ATTR_RGB_COLOR] = slots["color"]["value"]
|
||||||
# Use original passed in value of the color because we don't have
|
# Use original passed in value of the color because we don't have
|
||||||
# human readable names for that internally.
|
# human readable names for that internally.
|
||||||
speech_parts.append(f"the color {intent_obj.slots['color']['value']}")
|
speech_parts.append(f"the color {intent_obj.slots['color']['value']}")
|
||||||
|
|
||||||
if "brightness" in slots:
|
if "brightness" in slots:
|
||||||
intent.async_test_feature(state, SUPPORT_BRIGHTNESS, "changing brightness")
|
_test_supports_brightness(state)
|
||||||
service_data[ATTR_BRIGHTNESS_PCT] = slots["brightness"]["value"]
|
service_data[ATTR_BRIGHTNESS_PCT] = slots["brightness"]["value"]
|
||||||
speech_parts.append(f"{slots['brightness']['value']}% brightness")
|
speech_parts.append(f"{slots['brightness']['value']}% brightness")
|
||||||
|
|
||||||
|
@ -506,7 +506,7 @@ class NetatmoThermostat(NetatmoBase, ClimateEntity):
|
|||||||
self._target_temperature = roomstatus["target_temperature"]
|
self._target_temperature = roomstatus["target_temperature"]
|
||||||
self._preset = NETATMO_MAP_PRESET[roomstatus["setpoint_mode"]]
|
self._preset = NETATMO_MAP_PRESET[roomstatus["setpoint_mode"]]
|
||||||
self._hvac_mode = HVAC_MAP_NETATMO[self._preset]
|
self._hvac_mode = HVAC_MAP_NETATMO[self._preset]
|
||||||
self._battery_level = roomstatus.get("battery_level")
|
self._battery_level = roomstatus.get("battery_state")
|
||||||
self._connected = True
|
self._connected = True
|
||||||
|
|
||||||
self._away = self._hvac_mode == HVAC_MAP_NETATMO[STATE_NETATMO_AWAY]
|
self._away = self._hvac_mode == HVAC_MAP_NETATMO[STATE_NETATMO_AWAY]
|
||||||
@ -546,7 +546,7 @@ class NetatmoThermostat(NetatmoBase, ClimateEntity):
|
|||||||
roomstatus["heating_status"] = self._boilerstatus
|
roomstatus["heating_status"] = self._boilerstatus
|
||||||
batterylevel = self._home_status.thermostats[
|
batterylevel = self._home_status.thermostats[
|
||||||
roomstatus["module_id"]
|
roomstatus["module_id"]
|
||||||
].get("battery_level")
|
].get("battery_state")
|
||||||
elif roomstatus["module_type"] == NA_VALVE:
|
elif roomstatus["module_type"] == NA_VALVE:
|
||||||
roomstatus["heating_power_request"] = self._room_status[
|
roomstatus["heating_power_request"] = self._room_status[
|
||||||
"heating_power_request"
|
"heating_power_request"
|
||||||
@ -557,16 +557,11 @@ class NetatmoThermostat(NetatmoBase, ClimateEntity):
|
|||||||
self._boilerstatus and roomstatus["heating_status"]
|
self._boilerstatus and roomstatus["heating_status"]
|
||||||
)
|
)
|
||||||
batterylevel = self._home_status.valves[roomstatus["module_id"]].get(
|
batterylevel = self._home_status.valves[roomstatus["module_id"]].get(
|
||||||
"battery_level"
|
"battery_state"
|
||||||
)
|
)
|
||||||
|
|
||||||
if batterylevel:
|
if batterylevel:
|
||||||
batterypct = interpolate(batterylevel, roomstatus["module_type"])
|
roomstatus["battery_state"] = batterylevel
|
||||||
if (
|
|
||||||
not roomstatus.get("battery_level")
|
|
||||||
or batterypct < roomstatus["battery_level"]
|
|
||||||
):
|
|
||||||
roomstatus["battery_level"] = batterypct
|
|
||||||
|
|
||||||
return roomstatus
|
return roomstatus
|
||||||
|
|
||||||
@ -602,48 +597,6 @@ class NetatmoThermostat(NetatmoBase, ClimateEntity):
|
|||||||
return {**super().device_info, "suggested_area": self._room_data["name"]}
|
return {**super().device_info, "suggested_area": self._room_data["name"]}
|
||||||
|
|
||||||
|
|
||||||
def interpolate(batterylevel: int, module_type: str) -> int:
|
|
||||||
"""Interpolate battery level depending on device type."""
|
|
||||||
na_battery_levels = {
|
|
||||||
NA_THERM: {
|
|
||||||
"full": 4100,
|
|
||||||
"high": 3600,
|
|
||||||
"medium": 3300,
|
|
||||||
"low": 3000,
|
|
||||||
"empty": 2800,
|
|
||||||
},
|
|
||||||
NA_VALVE: {
|
|
||||||
"full": 3200,
|
|
||||||
"high": 2700,
|
|
||||||
"medium": 2400,
|
|
||||||
"low": 2200,
|
|
||||||
"empty": 2200,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
levels = sorted(na_battery_levels[module_type].values())
|
|
||||||
steps = [20, 50, 80, 100]
|
|
||||||
|
|
||||||
na_battery_level = na_battery_levels[module_type]
|
|
||||||
if batterylevel >= na_battery_level["full"]:
|
|
||||||
return 100
|
|
||||||
if batterylevel >= na_battery_level["high"]:
|
|
||||||
i = 3
|
|
||||||
elif batterylevel >= na_battery_level["medium"]:
|
|
||||||
i = 2
|
|
||||||
elif batterylevel >= na_battery_level["low"]:
|
|
||||||
i = 1
|
|
||||||
else:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
pct = steps[i - 1] + (
|
|
||||||
(steps[i] - steps[i - 1])
|
|
||||||
* (batterylevel - levels[i])
|
|
||||||
/ (levels[i + 1] - levels[i])
|
|
||||||
)
|
|
||||||
return int(pct)
|
|
||||||
|
|
||||||
|
|
||||||
def get_all_home_ids(home_data: pyatmo.HomeData) -> list[str]:
|
def get_all_home_ids(home_data: pyatmo.HomeData) -> list[str]:
|
||||||
"""Get all the home ids returned by NetAtmo API."""
|
"""Get all the home ids returned by NetAtmo API."""
|
||||||
if home_data is None:
|
if home_data is None:
|
||||||
|
@ -440,6 +440,11 @@ class SonosMediaPlayerEntity(SonosEntity, MediaPlayerEntity):
|
|||||||
self._media_position = None
|
self._media_position = None
|
||||||
self._media_position_updated_at = None
|
self._media_position_updated_at = None
|
||||||
|
|
||||||
|
async def async_set_favorites(self) -> None:
|
||||||
|
"""Wrap favorites update method with an async lock."""
|
||||||
|
async with self.data.topology_condition:
|
||||||
|
await self.hass.async_add_executor_job(self._set_favorites)
|
||||||
|
|
||||||
def _set_favorites(self) -> None:
|
def _set_favorites(self) -> None:
|
||||||
"""Set available favorites."""
|
"""Set available favorites."""
|
||||||
self._favorites = []
|
self._favorites = []
|
||||||
@ -741,7 +746,7 @@ class SonosMediaPlayerEntity(SonosEntity, MediaPlayerEntity):
|
|||||||
def async_update_content(self, event: SonosEvent | None = None) -> None:
|
def async_update_content(self, event: SonosEvent | None = None) -> None:
|
||||||
"""Update information about available content."""
|
"""Update information about available content."""
|
||||||
if event and "favorites_update_id" in event.variables:
|
if event and "favorites_update_id" in event.variables:
|
||||||
self.hass.async_add_job(self._set_favorites)
|
self.hass.async_add_job(self.async_set_favorites)
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -96,7 +96,7 @@ class SonosSpeaker:
|
|||||||
self.hass, f"{SONOS_SEEN}-{self.soco.uid}", self.async_seen
|
self.hass, f"{SONOS_SEEN}-{self.soco.uid}", self.async_seen
|
||||||
)
|
)
|
||||||
|
|
||||||
if (battery_info := fetch_battery_info_or_none(self.soco)) is not None:
|
if battery_info := fetch_battery_info_or_none(self.soco):
|
||||||
# Battery events can be infrequent, polling is still necessary
|
# Battery events can be infrequent, polling is still necessary
|
||||||
self.battery_info = battery_info
|
self.battery_info = battery_info
|
||||||
self._battery_poll_timer = self.hass.helpers.event.track_time_interval(
|
self._battery_poll_timer = self.hass.helpers.event.track_time_interval(
|
||||||
|
@ -2,8 +2,13 @@
|
|||||||
"domain": "version",
|
"domain": "version",
|
||||||
"name": "Version",
|
"name": "Version",
|
||||||
"documentation": "https://www.home-assistant.io/integrations/version",
|
"documentation": "https://www.home-assistant.io/integrations/version",
|
||||||
"requirements": ["pyhaversion==21.3.0"],
|
"requirements": [
|
||||||
"codeowners": ["@fabaff", "@ludeeus"],
|
"pyhaversion==21.5.0"
|
||||||
|
],
|
||||||
|
"codeowners": [
|
||||||
|
"@fabaff",
|
||||||
|
"@ludeeus"
|
||||||
|
],
|
||||||
"quality_scale": "internal",
|
"quality_scale": "internal",
|
||||||
"iot_class": "local_push"
|
"iot_class": "local_push"
|
||||||
}
|
}
|
@ -1,7 +1,7 @@
|
|||||||
"""Constants used by Home Assistant components."""
|
"""Constants used by Home Assistant components."""
|
||||||
MAJOR_VERSION = 2021
|
MAJOR_VERSION = 2021
|
||||||
MINOR_VERSION = 5
|
MINOR_VERSION = 5
|
||||||
PATCH_VERSION = "3"
|
PATCH_VERSION = "4"
|
||||||
__short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
__short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
||||||
__version__ = f"{__short_version__}.{PATCH_VERSION}"
|
__version__ = f"{__short_version__}.{PATCH_VERSION}"
|
||||||
REQUIRED_PYTHON_VER = (3, 8, 0)
|
REQUIRED_PYTHON_VER = (3, 8, 0)
|
||||||
|
@ -119,7 +119,7 @@ def async_match_state(
|
|||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_test_feature(state: State, feature: int, feature_name: str) -> None:
|
def async_test_feature(state: State, feature: int, feature_name: str) -> None:
|
||||||
"""Test is state supports a feature."""
|
"""Test if state supports a feature."""
|
||||||
if state.attributes.get(ATTR_SUPPORTED_FEATURES, 0) & feature == 0:
|
if state.attributes.get(ATTR_SUPPORTED_FEATURES, 0) & feature == 0:
|
||||||
raise IntentHandleError(f"Entity {state.name} does not support {feature_name}")
|
raise IntentHandleError(f"Entity {state.name} does not support {feature_name}")
|
||||||
|
|
||||||
|
@ -138,7 +138,7 @@ aio_georss_gdacs==0.4
|
|||||||
aioambient==1.2.4
|
aioambient==1.2.4
|
||||||
|
|
||||||
# homeassistant.components.asuswrt
|
# homeassistant.components.asuswrt
|
||||||
aioasuswrt==1.3.1
|
aioasuswrt==1.3.4
|
||||||
|
|
||||||
# homeassistant.components.azure_devops
|
# homeassistant.components.azure_devops
|
||||||
aioazuredevops==1.3.5
|
aioazuredevops==1.3.5
|
||||||
@ -1437,7 +1437,7 @@ pygtfs==0.1.5
|
|||||||
pygti==0.9.2
|
pygti==0.9.2
|
||||||
|
|
||||||
# homeassistant.components.version
|
# homeassistant.components.version
|
||||||
pyhaversion==21.3.0
|
pyhaversion==21.5.0
|
||||||
|
|
||||||
# homeassistant.components.heos
|
# homeassistant.components.heos
|
||||||
pyheos==0.7.2
|
pyheos==0.7.2
|
||||||
|
@ -78,7 +78,7 @@ aio_georss_gdacs==0.4
|
|||||||
aioambient==1.2.4
|
aioambient==1.2.4
|
||||||
|
|
||||||
# homeassistant.components.asuswrt
|
# homeassistant.components.asuswrt
|
||||||
aioasuswrt==1.3.1
|
aioasuswrt==1.3.4
|
||||||
|
|
||||||
# homeassistant.components.azure_devops
|
# homeassistant.components.azure_devops
|
||||||
aioazuredevops==1.3.5
|
aioazuredevops==1.3.5
|
||||||
@ -778,7 +778,7 @@ pygatt[GATTTOOL]==4.0.5
|
|||||||
pygti==0.9.2
|
pygti==0.9.2
|
||||||
|
|
||||||
# homeassistant.components.version
|
# homeassistant.components.version
|
||||||
pyhaversion==21.3.0
|
pyhaversion==21.5.0
|
||||||
|
|
||||||
# homeassistant.components.heos
|
# homeassistant.components.heos
|
||||||
pyheos==0.7.2
|
pyheos==0.7.2
|
||||||
|
@ -3,10 +3,11 @@ import pytest
|
|||||||
|
|
||||||
import homeassistant.components.automation as automation
|
import homeassistant.components.automation as automation
|
||||||
from homeassistant.components.light import (
|
from homeassistant.components.light import (
|
||||||
|
ATTR_SUPPORTED_COLOR_MODES,
|
||||||
|
COLOR_MODE_BRIGHTNESS,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
FLASH_LONG,
|
FLASH_LONG,
|
||||||
FLASH_SHORT,
|
FLASH_SHORT,
|
||||||
SUPPORT_BRIGHTNESS,
|
|
||||||
SUPPORT_FLASH,
|
SUPPORT_FLASH,
|
||||||
)
|
)
|
||||||
from homeassistant.const import CONF_PLATFORM, STATE_OFF, STATE_ON
|
from homeassistant.const import CONF_PLATFORM, STATE_OFF, STATE_ON
|
||||||
@ -55,7 +56,8 @@ async def test_get_actions(hass, device_reg, entity_reg):
|
|||||||
"test",
|
"test",
|
||||||
"5678",
|
"5678",
|
||||||
device_id=device_entry.id,
|
device_id=device_entry.id,
|
||||||
supported_features=SUPPORT_BRIGHTNESS | SUPPORT_FLASH,
|
supported_features=SUPPORT_FLASH,
|
||||||
|
capabilities={"supported_color_modes": ["brightness"]},
|
||||||
)
|
)
|
||||||
expected_actions = [
|
expected_actions = [
|
||||||
{
|
{
|
||||||
@ -132,13 +134,15 @@ async def test_get_action_capabilities(hass, device_reg, entity_reg):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"set_state,num_actions,supported_features_reg,supported_features_state,expected_capabilities",
|
"set_state,num_actions,supported_features_reg,supported_features_state,capabilities_reg,attributes_state,expected_capabilities",
|
||||||
[
|
[
|
||||||
(
|
(
|
||||||
False,
|
False,
|
||||||
5,
|
5,
|
||||||
SUPPORT_BRIGHTNESS,
|
|
||||||
0,
|
0,
|
||||||
|
0,
|
||||||
|
{ATTR_SUPPORTED_COLOR_MODES: [COLOR_MODE_BRIGHTNESS]},
|
||||||
|
{},
|
||||||
{
|
{
|
||||||
"turn_on": [
|
"turn_on": [
|
||||||
{
|
{
|
||||||
@ -155,7 +159,9 @@ async def test_get_action_capabilities(hass, device_reg, entity_reg):
|
|||||||
True,
|
True,
|
||||||
5,
|
5,
|
||||||
0,
|
0,
|
||||||
SUPPORT_BRIGHTNESS,
|
0,
|
||||||
|
None,
|
||||||
|
{ATTR_SUPPORTED_COLOR_MODES: [COLOR_MODE_BRIGHTNESS]},
|
||||||
{
|
{
|
||||||
"turn_on": [
|
"turn_on": [
|
||||||
{
|
{
|
||||||
@ -173,6 +179,8 @@ async def test_get_action_capabilities(hass, device_reg, entity_reg):
|
|||||||
4,
|
4,
|
||||||
SUPPORT_FLASH,
|
SUPPORT_FLASH,
|
||||||
0,
|
0,
|
||||||
|
None,
|
||||||
|
{},
|
||||||
{
|
{
|
||||||
"turn_on": [
|
"turn_on": [
|
||||||
{
|
{
|
||||||
@ -189,6 +197,8 @@ async def test_get_action_capabilities(hass, device_reg, entity_reg):
|
|||||||
4,
|
4,
|
||||||
0,
|
0,
|
||||||
SUPPORT_FLASH,
|
SUPPORT_FLASH,
|
||||||
|
None,
|
||||||
|
{},
|
||||||
{
|
{
|
||||||
"turn_on": [
|
"turn_on": [
|
||||||
{
|
{
|
||||||
@ -210,6 +220,8 @@ async def test_get_action_capabilities_features(
|
|||||||
num_actions,
|
num_actions,
|
||||||
supported_features_reg,
|
supported_features_reg,
|
||||||
supported_features_state,
|
supported_features_state,
|
||||||
|
capabilities_reg,
|
||||||
|
attributes_state,
|
||||||
expected_capabilities,
|
expected_capabilities,
|
||||||
):
|
):
|
||||||
"""Test we get the expected capabilities from a light action."""
|
"""Test we get the expected capabilities from a light action."""
|
||||||
@ -225,10 +237,13 @@ async def test_get_action_capabilities_features(
|
|||||||
"5678",
|
"5678",
|
||||||
device_id=device_entry.id,
|
device_id=device_entry.id,
|
||||||
supported_features=supported_features_reg,
|
supported_features=supported_features_reg,
|
||||||
|
capabilities=capabilities_reg,
|
||||||
).entity_id
|
).entity_id
|
||||||
if set_state:
|
if set_state:
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
entity_id, None, {"supported_features": supported_features_state}
|
entity_id,
|
||||||
|
None,
|
||||||
|
{"supported_features": supported_features_state, **attributes_state},
|
||||||
)
|
)
|
||||||
|
|
||||||
actions = await async_get_device_automations(hass, "action", device_entry.id)
|
actions = await async_get_device_automations(hass, "action", device_entry.id)
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
"""Tests for the light intents."""
|
"""Tests for the light intents."""
|
||||||
from homeassistant.components import light
|
from homeassistant.components import light
|
||||||
from homeassistant.components.light import intent
|
from homeassistant.components.light import (
|
||||||
from homeassistant.const import ATTR_ENTITY_ID, ATTR_SUPPORTED_FEATURES, SERVICE_TURN_ON
|
ATTR_SUPPORTED_COLOR_MODES,
|
||||||
|
COLOR_MODE_HS,
|
||||||
|
intent,
|
||||||
|
)
|
||||||
|
from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_ON
|
||||||
from homeassistant.helpers.intent import IntentHandleError
|
from homeassistant.helpers.intent import IntentHandleError
|
||||||
|
|
||||||
from tests.common import async_mock_service
|
from tests.common import async_mock_service
|
||||||
@ -10,7 +14,7 @@ from tests.common import async_mock_service
|
|||||||
async def test_intent_set_color(hass):
|
async def test_intent_set_color(hass):
|
||||||
"""Test the set color intent."""
|
"""Test the set color intent."""
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
"light.hello_2", "off", {ATTR_SUPPORTED_FEATURES: light.SUPPORT_COLOR}
|
"light.hello_2", "off", {ATTR_SUPPORTED_COLOR_MODES: [COLOR_MODE_HS]}
|
||||||
)
|
)
|
||||||
hass.states.async_set("switch.hello", "off")
|
hass.states.async_set("switch.hello", "off")
|
||||||
calls = async_mock_service(hass, light.DOMAIN, light.SERVICE_TURN_ON)
|
calls = async_mock_service(hass, light.DOMAIN, light.SERVICE_TURN_ON)
|
||||||
@ -55,9 +59,7 @@ async def test_intent_set_color_tests_feature(hass):
|
|||||||
async def test_intent_set_color_and_brightness(hass):
|
async def test_intent_set_color_and_brightness(hass):
|
||||||
"""Test the set color intent."""
|
"""Test the set color intent."""
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
"light.hello_2",
|
"light.hello_2", "off", {ATTR_SUPPORTED_COLOR_MODES: [COLOR_MODE_HS]}
|
||||||
"off",
|
|
||||||
{ATTR_SUPPORTED_FEATURES: (light.SUPPORT_COLOR | light.SUPPORT_BRIGHTNESS)},
|
|
||||||
)
|
)
|
||||||
hass.states.async_set("switch.hello", "off")
|
hass.states.async_set("switch.hello", "off")
|
||||||
calls = async_mock_service(hass, light.DOMAIN, light.SERVICE_TURN_ON)
|
calls = async_mock_service(hass, light.DOMAIN, light.SERVICE_TURN_ON)
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
"""The tests for the Netatmo climate platform."""
|
"""The tests for the Netatmo climate platform."""
|
||||||
from unittest.mock import Mock, patch
|
from unittest.mock import Mock, patch
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from homeassistant.components.climate import (
|
from homeassistant.components.climate import (
|
||||||
DOMAIN as CLIMATE_DOMAIN,
|
DOMAIN as CLIMATE_DOMAIN,
|
||||||
SERVICE_SET_HVAC_MODE,
|
SERVICE_SET_HVAC_MODE,
|
||||||
@ -21,12 +19,7 @@ from homeassistant.components.climate.const import (
|
|||||||
PRESET_BOOST,
|
PRESET_BOOST,
|
||||||
)
|
)
|
||||||
from homeassistant.components.netatmo import climate
|
from homeassistant.components.netatmo import climate
|
||||||
from homeassistant.components.netatmo.climate import (
|
from homeassistant.components.netatmo.climate import PRESET_FROST_GUARD, PRESET_SCHEDULE
|
||||||
NA_THERM,
|
|
||||||
NA_VALVE,
|
|
||||||
PRESET_FROST_GUARD,
|
|
||||||
PRESET_SCHEDULE,
|
|
||||||
)
|
|
||||||
from homeassistant.components.netatmo.const import (
|
from homeassistant.components.netatmo.const import (
|
||||||
ATTR_SCHEDULE_NAME,
|
ATTR_SCHEDULE_NAME,
|
||||||
SERVICE_SET_SCHEDULE,
|
SERVICE_SET_SCHEDULE,
|
||||||
@ -653,28 +646,6 @@ async def test_valves_service_turn_on(hass, climate_entry):
|
|||||||
assert hass.states.get(climate_entity_entrada).state == "auto"
|
assert hass.states.get(climate_entity_entrada).state == "auto"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"batterylevel, module_type, expected",
|
|
||||||
[
|
|
||||||
(4101, NA_THERM, 100),
|
|
||||||
(3601, NA_THERM, 80),
|
|
||||||
(3450, NA_THERM, 65),
|
|
||||||
(3301, NA_THERM, 50),
|
|
||||||
(3001, NA_THERM, 20),
|
|
||||||
(2799, NA_THERM, 0),
|
|
||||||
(3201, NA_VALVE, 100),
|
|
||||||
(2701, NA_VALVE, 80),
|
|
||||||
(2550, NA_VALVE, 65),
|
|
||||||
(2401, NA_VALVE, 50),
|
|
||||||
(2201, NA_VALVE, 20),
|
|
||||||
(2001, NA_VALVE, 0),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
async def test_interpolate(batterylevel, module_type, expected):
|
|
||||||
"""Test interpolation of battery levels depending on device type."""
|
|
||||||
assert climate.interpolate(batterylevel, module_type) == expected
|
|
||||||
|
|
||||||
|
|
||||||
async def test_get_all_home_ids():
|
async def test_get_all_home_ids():
|
||||||
"""Test extracting all home ids returned by NetAtmo API."""
|
"""Test extracting all home ids returned by NetAtmo API."""
|
||||||
# Test with backend returning no data
|
# Test with backend returning no data
|
||||||
|
Loading…
x
Reference in New Issue
Block a user