forked from home-assistant/core
Compare commits
74 Commits
2024.2.0b3
...
2024.2.0b4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef6fed5067 | ||
|
|
0a627aed6d | ||
|
|
cb03a6e29b | ||
|
|
e630027455 | ||
|
|
280d7ef4ec | ||
|
|
93b7ffa807 | ||
|
|
cc8e9ac141 | ||
|
|
2724b115da | ||
|
|
150fb151fa | ||
|
|
4b8cb35ba0 | ||
|
|
5cce878b85 | ||
|
|
92ebc5b436 | ||
|
|
463320c8ee | ||
|
|
3bf5fa9302 | ||
|
|
650ab70444 | ||
|
|
bb8a74a3f4 | ||
|
|
49445c46a0 | ||
|
|
490101fa92 | ||
|
|
2ac4bb8e9f | ||
|
|
1c0a6970e2 | ||
|
|
f3f69a8107 | ||
|
|
a77bcccbbb | ||
|
|
e98da8596a | ||
|
|
5843c93371 | ||
|
|
5991b06574 | ||
|
|
fecdfbfb9f | ||
|
|
8cfe3821da | ||
|
|
cba67d1525 | ||
|
|
39df394414 | ||
|
|
db91a40b55 | ||
|
|
3181358484 | ||
|
|
776e2da4e6 | ||
|
|
72ffdf4f4b | ||
|
|
36bba95fd0 | ||
|
|
13fc69d8a8 | ||
|
|
94464f220c | ||
|
|
13decb9b10 | ||
|
|
b6226acd2b | ||
|
|
d04282a41c | ||
|
|
a671d0bc6c | ||
|
|
6c84e8dff0 | ||
|
|
57279b1c7b | ||
|
|
a5646e0df2 | ||
|
|
63ad3ebdf4 | ||
|
|
f2a9ef6591 | ||
|
|
2a5bb66c06 | ||
|
|
0c0be6d6a1 | ||
|
|
e99a58ad53 | ||
|
|
6c4b773bc1 | ||
|
|
c571f36c6c | ||
|
|
f1b041afbe | ||
|
|
183af92658 | ||
|
|
d71dd12263 | ||
|
|
18a7aa20b4 | ||
|
|
f8fde71ef3 | ||
|
|
966798b588 | ||
|
|
66b6f81996 | ||
|
|
4a1a5b9e87 | ||
|
|
f01f033b3f | ||
|
|
cd884de79e | ||
|
|
14be7e4a72 | ||
|
|
2be71d53c5 | ||
|
|
1c960d300d | ||
|
|
87a1482e4d | ||
|
|
24b8e60978 | ||
|
|
404e30911b | ||
|
|
9c599f7513 | ||
|
|
f3e8360949 | ||
|
|
43f8731f8a | ||
|
|
4a60d36216 | ||
|
|
07a1ee0baa | ||
|
|
a57c30be88 | ||
|
|
b1bf69689e | ||
|
|
4735aadaa8 |
@@ -83,6 +83,7 @@ class AdvantageAirAC(AdvantageAirAcEntity, ClimateEntity):
|
||||
_attr_max_temp = 32
|
||||
_attr_min_temp = 16
|
||||
_attr_name = None
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(self, instance: AdvantageAirData, ac_key: str) -> None:
|
||||
"""Initialize an AdvantageAir AC unit."""
|
||||
@@ -202,11 +203,16 @@ class AdvantageAirZone(AdvantageAirZoneEntity, ClimateEntity):
|
||||
"""AdvantageAir MyTemp Zone control."""
|
||||
|
||||
_attr_hvac_modes = [HVACMode.OFF, HVACMode.HEAT_COOL]
|
||||
_attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
_attr_supported_features = (
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
| ClimateEntityFeature.TURN_OFF
|
||||
| ClimateEntityFeature.TURN_ON
|
||||
)
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_attr_target_temperature_step = PRECISION_WHOLE
|
||||
_attr_max_temp = 32
|
||||
_attr_min_temp = 16
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(self, instance: AdvantageAirData, ac_key: str, zone_key: str) -> None:
|
||||
"""Initialize an AdvantageAir Zone control."""
|
||||
|
||||
@@ -117,6 +117,7 @@ class AirzoneClimate(AirzoneZoneEntity, ClimateEntity):
|
||||
_attr_name = None
|
||||
_speeds: dict[int, str] = {}
|
||||
_speeds_reverse: dict[str, int] = {}
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -129,7 +130,11 @@ class AirzoneClimate(AirzoneZoneEntity, ClimateEntity):
|
||||
super().__init__(coordinator, entry, system_zone_id, zone_data)
|
||||
|
||||
self._attr_unique_id = f"{self._attr_unique_id}_{system_zone_id}"
|
||||
self._attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
self._attr_supported_features = (
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
| ClimateEntityFeature.TURN_OFF
|
||||
| ClimateEntityFeature.TURN_ON
|
||||
)
|
||||
self._attr_target_temperature_step = API_TEMPERATURE_STEP
|
||||
self._attr_temperature_unit = TEMP_UNIT_LIB_TO_HASS[
|
||||
self.get_airzone_value(AZD_TEMP_UNIT)
|
||||
|
||||
@@ -144,8 +144,13 @@ class AirzoneClimate(AirzoneEntity, ClimateEntity):
|
||||
"""Define an Airzone Cloud climate."""
|
||||
|
||||
_attr_name = None
|
||||
_attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
_attr_supported_features = (
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
| ClimateEntityFeature.TURN_OFF
|
||||
| ClimateEntityFeature.TURN_ON
|
||||
)
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
@callback
|
||||
def _handle_coordinator_update(self) -> None:
|
||||
|
||||
@@ -153,10 +153,15 @@ class AmbiclimateEntity(ClimateEntity):
|
||||
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_attr_target_temperature_step = 1
|
||||
_attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
_attr_supported_features = (
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
| ClimateEntityFeature.TURN_OFF
|
||||
| ClimateEntityFeature.TURN_ON
|
||||
)
|
||||
_attr_hvac_modes = [HVACMode.HEAT, HVACMode.OFF]
|
||||
_attr_has_entity_name = True
|
||||
_attr_name = None
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(self, heater: AmbiclimateDevice, store: Store[dict[str, Any]]) -> None:
|
||||
"""Initialize the thermostat."""
|
||||
|
||||
@@ -33,10 +33,15 @@ async def async_setup_entry(
|
||||
class BAFAutoComfort(BAFEntity, ClimateEntity):
|
||||
"""BAF climate auto comfort."""
|
||||
|
||||
_attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
_attr_supported_features = (
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
| ClimateEntityFeature.TURN_OFF
|
||||
| ClimateEntityFeature.TURN_ON
|
||||
)
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_attr_hvac_modes = [HVACMode.OFF, HVACMode.FAN_ONLY]
|
||||
_attr_translation_key = "auto_comfort"
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
@callback
|
||||
def _async_update_attrs(self) -> None:
|
||||
|
||||
@@ -63,6 +63,7 @@ class BalboaClimateEntity(BalboaEntity, ClimateEntity):
|
||||
)
|
||||
_attr_translation_key = DOMAIN
|
||||
_attr_name = None
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(self, client: SpaClient) -> None:
|
||||
"""Initialize the climate entity."""
|
||||
|
||||
@@ -53,8 +53,13 @@ async def async_setup_entry(
|
||||
class BleBoxClimateEntity(BleBoxEntity[blebox_uniapi.climate.Climate], ClimateEntity):
|
||||
"""Representation of a BleBox climate feature (saunaBox)."""
|
||||
|
||||
_attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
_attr_supported_features = (
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
| ClimateEntityFeature.TURN_OFF
|
||||
| ClimateEntityFeature.TURN_ON
|
||||
)
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
@property
|
||||
def hvac_modes(self):
|
||||
|
||||
@@ -35,9 +35,14 @@ class BroadlinkThermostat(ClimateEntity, BroadlinkEntity):
|
||||
|
||||
_attr_has_entity_name = True
|
||||
_attr_hvac_modes = [HVACMode.HEAT, HVACMode.OFF, HVACMode.AUTO]
|
||||
_attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
_attr_supported_features = (
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
| ClimateEntityFeature.TURN_OFF
|
||||
| ClimateEntityFeature.TURN_ON
|
||||
)
|
||||
_attr_target_temperature_step = PRECISION_HALVES
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(self, device: BroadlinkDevice) -> None:
|
||||
"""Initialize the climate entity."""
|
||||
|
||||
@@ -73,12 +73,16 @@ class BSBLANClimate(
|
||||
_attr_name = None
|
||||
# Determine preset modes
|
||||
_attr_supported_features = (
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
| ClimateEntityFeature.PRESET_MODE
|
||||
| ClimateEntityFeature.TURN_OFF
|
||||
| ClimateEntityFeature.TURN_ON
|
||||
)
|
||||
_attr_preset_modes = PRESET_MODES
|
||||
|
||||
# Determine hvac modes
|
||||
_attr_hvac_modes = HVAC_MODES
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
||||
@@ -4,8 +4,11 @@ from __future__ import annotations
|
||||
from collections.abc import Mapping
|
||||
from typing import Any
|
||||
|
||||
from aioelectricitymaps import ElectricityMaps
|
||||
from aioelectricitymaps.exceptions import ElectricityMapsError, InvalidToken
|
||||
from aioelectricitymaps import (
|
||||
ElectricityMaps,
|
||||
ElectricityMapsError,
|
||||
ElectricityMapsInvalidTokenError,
|
||||
)
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries
|
||||
@@ -146,7 +149,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
|
||||
try:
|
||||
await fetch_latest_carbon_intensity(self.hass, em, data)
|
||||
except InvalidToken:
|
||||
except ElectricityMapsInvalidTokenError:
|
||||
errors["base"] = "invalid_auth"
|
||||
except ElectricityMapsError:
|
||||
errors["base"] = "unknown"
|
||||
|
||||
@@ -4,9 +4,12 @@ from __future__ import annotations
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
|
||||
from aioelectricitymaps import ElectricityMaps
|
||||
from aioelectricitymaps.exceptions import ElectricityMapsError, InvalidToken
|
||||
from aioelectricitymaps.models import CarbonIntensityResponse
|
||||
from aioelectricitymaps import (
|
||||
CarbonIntensityResponse,
|
||||
ElectricityMaps,
|
||||
ElectricityMapsError,
|
||||
ElectricityMapsInvalidTokenError,
|
||||
)
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
@@ -43,7 +46,7 @@ class CO2SignalCoordinator(DataUpdateCoordinator[CarbonIntensityResponse]):
|
||||
return await fetch_latest_carbon_intensity(
|
||||
self.hass, self.client, self.config_entry.data
|
||||
)
|
||||
except InvalidToken as err:
|
||||
except ElectricityMapsInvalidTokenError as err:
|
||||
raise ConfigEntryAuthFailed from err
|
||||
except ElectricityMapsError as err:
|
||||
raise UpdateFailed(str(err)) from err
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
"integration_type": "service",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["aioelectricitymaps"],
|
||||
"requirements": ["aioelectricitymaps==0.2.0"]
|
||||
"requirements": ["aioelectricitymaps==0.3.0"]
|
||||
}
|
||||
|
||||
@@ -349,7 +349,7 @@ async def websocket_hass_agent_debug(
|
||||
},
|
||||
# Slot values that would be received by the intent
|
||||
"slots": { # direct access to values
|
||||
entity_key: entity.value
|
||||
entity_key: entity.text or entity.value
|
||||
for entity_key, entity in result.entities.items()
|
||||
},
|
||||
# Extra slot details, such as the originally matched text
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"""Standard conversation implementation for Home Assistant."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
@@ -264,9 +265,11 @@ class DefaultAgent(AbstractConversationAgent):
|
||||
_LOGGER.debug(
|
||||
"Recognized intent '%s' for template '%s' but had unmatched: %s",
|
||||
result.intent.name,
|
||||
result.intent_sentence.text
|
||||
if result.intent_sentence is not None
|
||||
else "",
|
||||
(
|
||||
result.intent_sentence.text
|
||||
if result.intent_sentence is not None
|
||||
else ""
|
||||
),
|
||||
result.unmatched_entities_list,
|
||||
)
|
||||
error_response_type, error_response_args = _get_unmatched_response(result)
|
||||
@@ -285,7 +288,8 @@ class DefaultAgent(AbstractConversationAgent):
|
||||
|
||||
# Slot values to pass to the intent
|
||||
slots = {
|
||||
entity.name: {"value": entity.value} for entity in result.entities_list
|
||||
entity.name: {"value": entity.value, "text": entity.text or entity.value}
|
||||
for entity in result.entities_list
|
||||
}
|
||||
|
||||
try:
|
||||
@@ -474,9 +478,11 @@ class DefaultAgent(AbstractConversationAgent):
|
||||
for entity_name, entity_value in recognize_result.entities.items()
|
||||
},
|
||||
# First matched or unmatched state
|
||||
"state": template.TemplateState(self.hass, state1)
|
||||
if state1 is not None
|
||||
else None,
|
||||
"state": (
|
||||
template.TemplateState(self.hass, state1)
|
||||
if state1 is not None
|
||||
else None
|
||||
),
|
||||
"query": {
|
||||
# Entity states that matched the query (e.g, "on")
|
||||
"matched": [
|
||||
@@ -734,7 +740,7 @@ class DefaultAgent(AbstractConversationAgent):
|
||||
|
||||
if not entity:
|
||||
# Default name
|
||||
entity_names.append((state.name, state.name, context))
|
||||
entity_names.append((state.name, state.entity_id, context))
|
||||
continue
|
||||
|
||||
if entity.aliases:
|
||||
@@ -742,10 +748,10 @@ class DefaultAgent(AbstractConversationAgent):
|
||||
if not alias.strip():
|
||||
continue
|
||||
|
||||
entity_names.append((alias, alias, context))
|
||||
entity_names.append((alias, state.entity_id, context))
|
||||
|
||||
# Default name
|
||||
entity_names.append((state.name, state.name, context))
|
||||
entity_names.append((state.name, state.entity_id, context))
|
||||
|
||||
# Expose all areas
|
||||
areas = ar.async_get(self.hass)
|
||||
@@ -785,7 +791,7 @@ class DefaultAgent(AbstractConversationAgent):
|
||||
if device_area is None:
|
||||
return None
|
||||
|
||||
return {"area": device_area.id}
|
||||
return {"area": {"value": device_area.id, "text": device_area.name}}
|
||||
|
||||
def _get_error_text(
|
||||
self,
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
"integration_type": "system",
|
||||
"iot_class": "local_push",
|
||||
"quality_scale": "internal",
|
||||
"requirements": ["hassil==1.6.0", "home-assistant-intents==2024.2.1"]
|
||||
"requirements": ["hassil==1.6.1", "home-assistant-intents==2024.2.2"]
|
||||
}
|
||||
|
||||
@@ -100,6 +100,7 @@ class DeconzThermostat(DeconzDevice[Thermostat], ClimateEntity):
|
||||
TYPE = DOMAIN
|
||||
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(self, device: Thermostat, gateway: DeconzGateway) -> None:
|
||||
"""Set up thermostat device."""
|
||||
@@ -119,7 +120,11 @@ class DeconzThermostat(DeconzDevice[Thermostat], ClimateEntity):
|
||||
HVAC_MODE_TO_DECONZ[item]: item for item in self._attr_hvac_modes
|
||||
}
|
||||
|
||||
self._attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
self._attr_supported_features = (
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
| ClimateEntityFeature.TURN_OFF
|
||||
| ClimateEntityFeature.TURN_ON
|
||||
)
|
||||
|
||||
if device.fan_mode:
|
||||
self._attr_supported_features |= ClimateEntityFeature.FAN_MODE
|
||||
|
||||
@@ -97,6 +97,7 @@ class DemoClimate(ClimateEntity):
|
||||
_attr_name = None
|
||||
_attr_should_poll = False
|
||||
_attr_translation_key = "ubercool"
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -137,6 +138,9 @@ class DemoClimate(ClimateEntity):
|
||||
self._attr_supported_features |= (
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
|
||||
)
|
||||
self._attr_supported_features |= (
|
||||
ClimateEntityFeature.TURN_OFF | ClimateEntityFeature.TURN_ON
|
||||
)
|
||||
self._target_temperature = target_temperature
|
||||
self._target_humidity = target_humidity
|
||||
self._unit_of_measurement = unit_of_measurement
|
||||
|
||||
@@ -7,6 +7,6 @@
|
||||
"integration_type": "device",
|
||||
"iot_class": "local_polling",
|
||||
"quality_scale": "platinum",
|
||||
"requirements": ["elgato==5.1.1"],
|
||||
"requirements": ["elgato==5.1.2"],
|
||||
"zeroconf": ["_elg._tcp.local."]
|
||||
}
|
||||
|
||||
@@ -35,8 +35,10 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
self._api_token = api_token = user_input[CONF_API_TOKEN]
|
||||
client = Elvia(meter_value_token=api_token).meter_value()
|
||||
try:
|
||||
end_time = dt_util.utcnow()
|
||||
results = await client.get_meter_values(
|
||||
start_time=(dt_util.now() - timedelta(hours=1)).isoformat()
|
||||
start_time=(end_time - timedelta(hours=1)).isoformat(),
|
||||
end_time=end_time.isoformat(),
|
||||
)
|
||||
|
||||
except ElviaError.AuthError as exception:
|
||||
|
||||
@@ -4,7 +4,7 @@ from __future__ import annotations
|
||||
from datetime import datetime, timedelta
|
||||
from typing import TYPE_CHECKING, cast
|
||||
|
||||
from elvia import Elvia
|
||||
from elvia import Elvia, error as ElviaError
|
||||
|
||||
from homeassistant.components.recorder.models import StatisticData, StatisticMetaData
|
||||
from homeassistant.components.recorder.statistics import (
|
||||
@@ -68,21 +68,37 @@ class ElviaImporter:
|
||||
)
|
||||
|
||||
if not last_stats:
|
||||
# First time we insert 1 years of data (if available)
|
||||
# First time we insert 3 years of data (if available)
|
||||
hourly_data: list[MeterValueTimeSeries] = []
|
||||
until = dt_util.utcnow()
|
||||
hourly_data = await self._fetch_hourly_data(
|
||||
since=until - timedelta(days=365),
|
||||
until=until,
|
||||
)
|
||||
for year in (3, 2, 1):
|
||||
try:
|
||||
year_hours = await self._fetch_hourly_data(
|
||||
since=until - timedelta(days=365 * year),
|
||||
until=until - timedelta(days=365 * (year - 1)),
|
||||
)
|
||||
except ElviaError.ElviaException:
|
||||
# This will raise if the contract have no data for the
|
||||
# year, we can safely ignore this
|
||||
continue
|
||||
hourly_data.extend(year_hours)
|
||||
|
||||
if hourly_data is None or len(hourly_data) == 0:
|
||||
LOGGER.error("No data available for the metering point")
|
||||
return
|
||||
last_stats_time = None
|
||||
_sum = 0.0
|
||||
else:
|
||||
hourly_data = await self._fetch_hourly_data(
|
||||
since=dt_util.utc_from_timestamp(last_stats[statistic_id][0]["end"]),
|
||||
until=dt_util.utcnow(),
|
||||
)
|
||||
try:
|
||||
hourly_data = await self._fetch_hourly_data(
|
||||
since=dt_util.utc_from_timestamp(
|
||||
last_stats[statistic_id][0]["end"]
|
||||
),
|
||||
until=dt_util.utcnow(),
|
||||
)
|
||||
except ElviaError.ElviaException as err:
|
||||
LOGGER.error("Error fetching data: %s", err)
|
||||
return
|
||||
|
||||
if (
|
||||
hourly_data is None
|
||||
|
||||
@@ -352,7 +352,6 @@ class ESPHomeManager:
|
||||
if self.voice_assistant_udp_server is not None:
|
||||
_LOGGER.warning("Voice assistant UDP server was not stopped")
|
||||
self.voice_assistant_udp_server.stop()
|
||||
self.voice_assistant_udp_server.close()
|
||||
self.voice_assistant_udp_server = None
|
||||
|
||||
hass = self.hass
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"""ESPHome voice assistant support."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
@@ -67,7 +68,7 @@ class VoiceAssistantUDPServer(asyncio.DatagramProtocol):
|
||||
"""Receive UDP packets and forward them to the voice assistant."""
|
||||
|
||||
started = False
|
||||
stopped = False
|
||||
stop_requested = False
|
||||
transport: asyncio.DatagramTransport | None = None
|
||||
remote_addr: tuple[str, int] | None = None
|
||||
|
||||
@@ -92,6 +93,11 @@ class VoiceAssistantUDPServer(asyncio.DatagramProtocol):
|
||||
self._tts_done = asyncio.Event()
|
||||
self._tts_task: asyncio.Task | None = None
|
||||
|
||||
@property
|
||||
def is_running(self) -> bool:
|
||||
"""True if the the UDP server is started and hasn't been asked to stop."""
|
||||
return self.started and (not self.stop_requested)
|
||||
|
||||
async def start_server(self) -> int:
|
||||
"""Start accepting connections."""
|
||||
|
||||
@@ -99,7 +105,7 @@ class VoiceAssistantUDPServer(asyncio.DatagramProtocol):
|
||||
"""Accept connection."""
|
||||
if self.started:
|
||||
raise RuntimeError("Can only start once")
|
||||
if self.stopped:
|
||||
if self.stop_requested:
|
||||
raise RuntimeError("No longer accepting connections")
|
||||
|
||||
self.started = True
|
||||
@@ -124,7 +130,7 @@ class VoiceAssistantUDPServer(asyncio.DatagramProtocol):
|
||||
@callback
|
||||
def datagram_received(self, data: bytes, addr: tuple[str, int]) -> None:
|
||||
"""Handle incoming UDP packet."""
|
||||
if not self.started or self.stopped:
|
||||
if not self.is_running:
|
||||
return
|
||||
if self.remote_addr is None:
|
||||
self.remote_addr = addr
|
||||
@@ -142,19 +148,19 @@ class VoiceAssistantUDPServer(asyncio.DatagramProtocol):
|
||||
def stop(self) -> None:
|
||||
"""Stop the receiver."""
|
||||
self.queue.put_nowait(b"")
|
||||
self.started = False
|
||||
self.stopped = True
|
||||
self.close()
|
||||
|
||||
def close(self) -> None:
|
||||
"""Close the receiver."""
|
||||
self.started = False
|
||||
self.stopped = True
|
||||
self.stop_requested = True
|
||||
|
||||
if self.transport is not None:
|
||||
self.transport.close()
|
||||
|
||||
async def _iterate_packets(self) -> AsyncIterable[bytes]:
|
||||
"""Iterate over incoming packets."""
|
||||
if not self.started or self.stopped:
|
||||
if not self.is_running:
|
||||
raise RuntimeError("Not running")
|
||||
|
||||
while data := await self.queue.get():
|
||||
@@ -303,8 +309,11 @@ class VoiceAssistantUDPServer(asyncio.DatagramProtocol):
|
||||
|
||||
async def _send_tts(self, media_id: str) -> None:
|
||||
"""Send TTS audio to device via UDP."""
|
||||
# Always send stream start/end events
|
||||
self.handle_event(VoiceAssistantEventType.VOICE_ASSISTANT_TTS_STREAM_START, {})
|
||||
|
||||
try:
|
||||
if self.transport is None:
|
||||
if (not self.is_running) or (self.transport is None):
|
||||
return
|
||||
|
||||
extension, data = await tts.async_get_media_source_audio(
|
||||
@@ -337,15 +346,11 @@ class VoiceAssistantUDPServer(asyncio.DatagramProtocol):
|
||||
|
||||
_LOGGER.debug("Sending %d bytes of audio", audio_bytes_size)
|
||||
|
||||
self.handle_event(
|
||||
VoiceAssistantEventType.VOICE_ASSISTANT_TTS_STREAM_START, {}
|
||||
)
|
||||
|
||||
bytes_per_sample = stt.AudioBitRates.BITRATE_16 // 8
|
||||
sample_offset = 0
|
||||
samples_left = audio_bytes_size // bytes_per_sample
|
||||
|
||||
while samples_left > 0:
|
||||
while (samples_left > 0) and self.is_running:
|
||||
bytes_offset = sample_offset * bytes_per_sample
|
||||
chunk: bytes = audio_bytes[bytes_offset : bytes_offset + 1024]
|
||||
samples_in_chunk = len(chunk) // bytes_per_sample
|
||||
|
||||
@@ -156,6 +156,7 @@ class EvoClimateEntity(EvoDevice, ClimateEntity):
|
||||
"""Base for an evohome Climate device."""
|
||||
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
@property
|
||||
def hvac_modes(self) -> list[HVACMode]:
|
||||
@@ -190,7 +191,10 @@ class EvoZone(EvoChild, EvoClimateEntity):
|
||||
]
|
||||
|
||||
self._attr_supported_features = (
|
||||
ClimateEntityFeature.PRESET_MODE | ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
ClimateEntityFeature.PRESET_MODE
|
||||
| ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
| ClimateEntityFeature.TURN_OFF
|
||||
| ClimateEntityFeature.TURN_ON
|
||||
)
|
||||
|
||||
async def async_zone_svc_request(self, service: str, data: dict[str, Any]) -> None:
|
||||
@@ -372,6 +376,9 @@ class EvoController(EvoClimateEntity):
|
||||
]
|
||||
if self._attr_preset_modes:
|
||||
self._attr_supported_features = ClimateEntityFeature.PRESET_MODE
|
||||
self._attr_supported_features |= (
|
||||
ClimateEntityFeature.TURN_OFF | ClimateEntityFeature.TURN_ON
|
||||
)
|
||||
|
||||
async def async_tcs_svc_request(self, service: str, data: dict[str, Any]) -> None:
|
||||
"""Process a service request (system mode) for a controller.
|
||||
|
||||
@@ -69,6 +69,7 @@ class Flexit(ClimateEntity):
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.FAN_MODE
|
||||
)
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(
|
||||
self, hub: ModbusHub, modbus_slave: int | None, name: str | None
|
||||
|
||||
@@ -62,13 +62,17 @@ class FlexitClimateEntity(FlexitEntity, ClimateEntity):
|
||||
]
|
||||
|
||||
_attr_supported_features = (
|
||||
ClimateEntityFeature.PRESET_MODE | ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
ClimateEntityFeature.PRESET_MODE
|
||||
| ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
| ClimateEntityFeature.TURN_OFF
|
||||
| ClimateEntityFeature.TURN_ON
|
||||
)
|
||||
|
||||
_attr_target_temperature_step = PRECISION_HALVES
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_attr_max_temp = MAX_TEMP
|
||||
_attr_min_temp = MIN_TEMP
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(self, coordinator: FlexitCoordinator) -> None:
|
||||
"""Initialize the Flexit unit."""
|
||||
|
||||
@@ -139,6 +139,7 @@ class HomeKitBaseClimateEntity(HomeKitEntity, ClimateEntity):
|
||||
"""The base HomeKit Controller climate entity."""
|
||||
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
@callback
|
||||
def _async_reconfigure(self) -> None:
|
||||
|
||||
@@ -14,6 +14,6 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/homekit_controller",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["aiohomekit", "commentjson"],
|
||||
"requirements": ["aiohomekit==3.1.3"],
|
||||
"requirements": ["aiohomekit==3.1.4"],
|
||||
"zeroconf": ["_hap._tcp.local.", "_hap._udp.local."]
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ async def async_setup_entry(
|
||||
lights = []
|
||||
|
||||
for area_name, device in entry_data.lights:
|
||||
if device.type == "CEILING_FAN_TYPE2":
|
||||
if device.type == "CEILING_FAN_TYPE":
|
||||
# If this is a fan, check to see if this entity already exists.
|
||||
# If not, do not create a new one.
|
||||
entity_id = ent_reg.async_get_entity_id(
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/matrix",
|
||||
"iot_class": "cloud_push",
|
||||
"loggers": ["matrix_client"],
|
||||
"requirements": ["matrix-nio==0.22.1", "Pillow==10.2.0"]
|
||||
"requirements": ["matrix-nio==0.24.0", "Pillow==10.2.0"]
|
||||
}
|
||||
|
||||
@@ -68,8 +68,12 @@ class MaxCubeClimate(ClimateEntity):
|
||||
|
||||
_attr_hvac_modes = [HVACMode.OFF, HVACMode.AUTO, HVACMode.HEAT]
|
||||
_attr_supported_features = (
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
| ClimateEntityFeature.PRESET_MODE
|
||||
| ClimateEntityFeature.TURN_OFF
|
||||
| ClimateEntityFeature.TURN_ON
|
||||
)
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(self, handler, device):
|
||||
"""Initialize MAX! Cube ClimateEntity."""
|
||||
|
||||
@@ -114,6 +114,7 @@ class MelCloudClimate(ClimateEntity):
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_attr_has_entity_name = True
|
||||
_attr_name = None
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(self, device: MelCloudDevice) -> None:
|
||||
"""Initialize the climate."""
|
||||
@@ -137,6 +138,8 @@ class AtaDeviceClimate(MelCloudClimate):
|
||||
ClimateEntityFeature.FAN_MODE
|
||||
| ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
| ClimateEntityFeature.SWING_MODE
|
||||
| ClimateEntityFeature.TURN_OFF
|
||||
| ClimateEntityFeature.TURN_ON
|
||||
)
|
||||
|
||||
def __init__(self, device: MelCloudDevice, ata_device: AtaDevice) -> None:
|
||||
|
||||
@@ -57,9 +57,13 @@ class MelissaClimate(ClimateEntity):
|
||||
|
||||
_attr_hvac_modes = OP_MODES
|
||||
_attr_supported_features = (
|
||||
ClimateEntityFeature.FAN_MODE | ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
ClimateEntityFeature.FAN_MODE
|
||||
| ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
| ClimateEntityFeature.TURN_OFF
|
||||
| ClimateEntityFeature.TURN_ON
|
||||
)
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(self, api, serial_number, init_data):
|
||||
"""Initialize the climate device."""
|
||||
|
||||
@@ -99,6 +99,7 @@ class MillHeater(CoordinatorEntity[MillDataUpdateCoordinator], ClimateEntity):
|
||||
)
|
||||
_attr_target_temperature_step = PRECISION_TENTHS
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(
|
||||
self, coordinator: MillDataUpdateCoordinator, heater: mill.Heater
|
||||
|
||||
@@ -46,6 +46,7 @@ class Alpha2Climate(CoordinatorEntity[Alpha2BaseCoordinator], ClimateEntity):
|
||||
_attr_hvac_modes = [HVACMode.HEAT, HVACMode.COOL]
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_attr_preset_modes = [PRESET_AUTO, PRESET_DAY, PRESET_NIGHT]
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(self, coordinator: Alpha2BaseCoordinator, heat_area_id: str) -> None:
|
||||
"""Initialize Alpha2 ClimateEntity."""
|
||||
|
||||
@@ -610,6 +610,7 @@ class MqttClimate(MqttTemperatureControlEntity, ClimateEntity):
|
||||
_attributes_extra_blocked = MQTT_CLIMATE_ATTRIBUTES_BLOCKED
|
||||
_attr_target_temperature_low: float | None = None
|
||||
_attr_target_temperature_high: float | None = None
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
@staticmethod
|
||||
def config_schema() -> vol.Schema:
|
||||
|
||||
@@ -70,11 +70,12 @@ class MySensorsHVAC(mysensors.device.MySensorsChildEntity, ClimateEntity):
|
||||
"""Representation of a MySensors HVAC."""
|
||||
|
||||
_attr_hvac_modes = OPERATION_LIST
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
@property
|
||||
def supported_features(self) -> ClimateEntityFeature:
|
||||
"""Return the list of supported features."""
|
||||
features = ClimateEntityFeature(0)
|
||||
features = ClimateEntityFeature.TURN_OFF | ClimateEntityFeature.TURN_ON
|
||||
set_req = self.gateway.const.SetReq
|
||||
if set_req.V_HVAC_SPEED in self._values:
|
||||
features = features | ClimateEntityFeature.FAN_MODE
|
||||
|
||||
@@ -100,6 +100,7 @@ class ThermostatEntity(ClimateEntity):
|
||||
_attr_has_entity_name = True
|
||||
_attr_should_poll = False
|
||||
_attr_name = None
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(self, device: Device) -> None:
|
||||
"""Initialize ThermostatEntity."""
|
||||
@@ -246,7 +247,7 @@ class ThermostatEntity(ClimateEntity):
|
||||
|
||||
def _get_supported_features(self) -> ClimateEntityFeature:
|
||||
"""Compute the bitmap of supported features from the current state."""
|
||||
features = ClimateEntityFeature(0)
|
||||
features = ClimateEntityFeature.TURN_OFF | ClimateEntityFeature.TURN_ON
|
||||
if HVACMode.HEAT_COOL in self.hvac_modes:
|
||||
features |= ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
|
||||
if HVACMode.HEAT in self.hvac_modes or HVACMode.COOL in self.hvac_modes:
|
||||
|
||||
@@ -190,6 +190,7 @@ class NetatmoThermostat(NetatmoBaseEntity, ClimateEntity):
|
||||
_attr_supported_features = SUPPORT_FLAGS
|
||||
_attr_target_temperature_step = PRECISION_HALVES
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(self, netatmo_device: NetatmoRoom) -> None:
|
||||
"""Initialize the sensor."""
|
||||
|
||||
@@ -153,6 +153,7 @@ class NexiaZone(NexiaThermostatZoneEntity, ClimateEntity):
|
||||
"""Provides Nexia Climate support."""
|
||||
|
||||
_attr_name = None
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(
|
||||
self, coordinator: NexiaDataUpdateCoordinator, zone: NexiaThermostatZone
|
||||
|
||||
@@ -81,6 +81,7 @@ class NoboZone(ClimateEntity):
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_attr_target_temperature_step = 1
|
||||
# Need to poll to get preset change when in HVACMode.AUTO, so can't set _attr_should_poll = False
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(self, zone_id, hub: nobo, override_type) -> None:
|
||||
"""Initialize the climate device."""
|
||||
|
||||
@@ -78,6 +78,7 @@ class NuHeatThermostat(CoordinatorEntity, ClimateEntity):
|
||||
_attr_has_entity_name = True
|
||||
_attr_name = None
|
||||
_attr_preset_modes = PRESET_MODES
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(self, coordinator, thermostat, temperature_unit):
|
||||
"""Initialize the thermostat."""
|
||||
|
||||
@@ -66,8 +66,13 @@ class ThermostatDevice(ClimateEntity):
|
||||
"""Interface class for the oemthermostat module."""
|
||||
|
||||
_attr_hvac_modes = SUPPORT_HVAC
|
||||
_attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
_attr_supported_features = (
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
| ClimateEntityFeature.TURN_OFF
|
||||
| ClimateEntityFeature.TURN_ON
|
||||
)
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(self, thermostat, name):
|
||||
"""Initialize the device."""
|
||||
|
||||
@@ -84,6 +84,7 @@ class OpenThermClimate(ClimateEntity):
|
||||
_away_state_a = False
|
||||
_away_state_b = False
|
||||
_current_operation: HVACAction | None = None
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(self, gw_dev, options):
|
||||
"""Initialize the device."""
|
||||
|
||||
@@ -53,6 +53,7 @@ class AtlanticElectricalHeater(OverkizEntity, ClimateEntity):
|
||||
)
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_attr_translation_key = DOMAIN
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
@property
|
||||
def hvac_mode(self) -> HVACMode:
|
||||
|
||||
@@ -75,6 +75,7 @@ class AtlanticElectricalHeaterWithAdjustableTemperatureSetpoint(
|
||||
| ClimateEntityFeature.TURN_ON
|
||||
)
|
||||
_attr_translation_key = DOMAIN
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(
|
||||
self, device_url: str, coordinator: OverkizDataUpdateCoordinator
|
||||
|
||||
@@ -45,6 +45,7 @@ class AtlanticElectricalTowelDryer(OverkizEntity, ClimateEntity):
|
||||
_attr_preset_modes = [*PRESET_MODE_TO_OVERKIZ]
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_attr_translation_key = DOMAIN
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(
|
||||
self, device_url: str, coordinator: OverkizDataUpdateCoordinator
|
||||
|
||||
@@ -54,6 +54,7 @@ class AtlanticHeatRecoveryVentilation(OverkizEntity, ClimateEntity):
|
||||
| ClimateEntityFeature.TURN_ON
|
||||
)
|
||||
_attr_translation_key = DOMAIN
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(
|
||||
self, device_url: str, coordinator: OverkizDataUpdateCoordinator
|
||||
|
||||
@@ -83,6 +83,7 @@ class AtlanticPassAPCHeatingZone(OverkizEntity, ClimateEntity):
|
||||
)
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_attr_translation_key = DOMAIN
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(
|
||||
self, device_url: str, coordinator: OverkizDataUpdateCoordinator
|
||||
|
||||
@@ -30,6 +30,7 @@ class AtlanticPassAPCZoneControl(OverkizEntity, ClimateEntity):
|
||||
_attr_supported_features = (
|
||||
ClimateEntityFeature.TURN_OFF | ClimateEntityFeature.TURN_ON
|
||||
)
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
@property
|
||||
def hvac_mode(self) -> HVACMode:
|
||||
|
||||
@@ -90,6 +90,7 @@ class HitachiAirToAirHeatPumpHLRRWIFI(OverkizEntity, ClimateEntity):
|
||||
_attr_target_temperature_step = 1.0
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_attr_translation_key = DOMAIN
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(
|
||||
self, device_url: str, coordinator: OverkizDataUpdateCoordinator
|
||||
|
||||
@@ -81,6 +81,7 @@ class SomfyHeatingTemperatureInterface(OverkizEntity, ClimateEntity):
|
||||
# Both min and max temp values have been retrieved from the Somfy Application.
|
||||
_attr_min_temp = 15.0
|
||||
_attr_max_temp = 26.0
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(
|
||||
self, device_url: str, coordinator: OverkizDataUpdateCoordinator
|
||||
|
||||
@@ -64,6 +64,7 @@ class SomfyThermostat(OverkizEntity, ClimateEntity):
|
||||
_attr_hvac_modes = [*HVAC_MODES_TO_OVERKIZ]
|
||||
_attr_preset_modes = [*PRESET_MODES_TO_OVERKIZ]
|
||||
_attr_translation_key = DOMAIN
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
# Both min and max temp values have been retrieved from the Somfy Application.
|
||||
_attr_min_temp = 15.0
|
||||
|
||||
@@ -58,6 +58,7 @@ class ValveHeatingTemperatureInterface(OverkizEntity, ClimateEntity):
|
||||
)
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_attr_translation_key = DOMAIN
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(
|
||||
self, device_url: str, coordinator: OverkizDataUpdateCoordinator
|
||||
|
||||
@@ -60,6 +60,7 @@ class ProliphixThermostat(ClimateEntity):
|
||||
_attr_precision = PRECISION_TENTHS
|
||||
_attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
_attr_temperature_unit = UnitOfTemperature.FAHRENHEIT
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(self, pdp):
|
||||
"""Initialize the thermostat."""
|
||||
|
||||
49
homeassistant/components/proximity/diagnostics.py
Normal file
49
homeassistant/components/proximity/diagnostics.py
Normal file
@@ -0,0 +1,49 @@
|
||||
"""Diagnostics support for Proximity."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.device_tracker import ATTR_GPS, ATTR_IP, ATTR_MAC
|
||||
from homeassistant.components.diagnostics import async_redact_data
|
||||
from homeassistant.components.person import ATTR_USER_ID
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import ATTR_LATITUDE, ATTR_LONGITUDE
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import ProximityDataUpdateCoordinator
|
||||
|
||||
TO_REDACT = {
|
||||
ATTR_GPS,
|
||||
ATTR_IP,
|
||||
ATTR_LATITUDE,
|
||||
ATTR_LONGITUDE,
|
||||
ATTR_MAC,
|
||||
ATTR_USER_ID,
|
||||
"context",
|
||||
}
|
||||
|
||||
|
||||
async def async_get_config_entry_diagnostics(
|
||||
hass: HomeAssistant, entry: ConfigEntry
|
||||
) -> dict[str, Any]:
|
||||
"""Return diagnostics for a config entry."""
|
||||
coordinator: ProximityDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
diag_data = {
|
||||
"entry": entry.as_dict(),
|
||||
}
|
||||
|
||||
tracked_states: dict[str, dict] = {}
|
||||
for tracked_entity_id in coordinator.tracked_entities:
|
||||
if (state := hass.states.get(tracked_entity_id)) is None:
|
||||
continue
|
||||
tracked_states[tracked_entity_id] = state.as_dict()
|
||||
|
||||
diag_data["data"] = {
|
||||
"proximity": coordinator.data.proximity,
|
||||
"entities": coordinator.data.entities,
|
||||
"entity_mapping": coordinator.entity_mapping,
|
||||
"tracked_states": async_redact_data(tracked_states, TO_REDACT),
|
||||
}
|
||||
return diag_data
|
||||
@@ -106,6 +106,7 @@ class RadioThermostat(RadioThermostatEntity, ClimateEntity):
|
||||
_attr_temperature_unit = UnitOfTemperature.FAHRENHEIT
|
||||
_attr_precision = PRECISION_HALVES
|
||||
_attr_name = None
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(self, coordinator: RadioThermUpdateCoordinator) -> None:
|
||||
"""Initialize the thermostat."""
|
||||
@@ -113,7 +114,10 @@ class RadioThermostat(RadioThermostatEntity, ClimateEntity):
|
||||
self._attr_unique_id = self.init_data.mac
|
||||
self._attr_fan_modes = CT30_FAN_OPERATION_LIST
|
||||
self._attr_supported_features = (
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.FAN_MODE
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
| ClimateEntityFeature.FAN_MODE
|
||||
| ClimateEntityFeature.TURN_OFF
|
||||
| ClimateEntityFeature.TURN_ON
|
||||
)
|
||||
if not isinstance(self.device, radiotherm.thermostat.CT80):
|
||||
return
|
||||
|
||||
@@ -81,6 +81,7 @@ class SchluterThermostat(CoordinatorEntity, ClimateEntity):
|
||||
_attr_hvac_modes = [HVACMode.HEAT]
|
||||
_attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(self, coordinator, serial_number, api, session_id):
|
||||
"""Initialize the thermostat."""
|
||||
|
||||
@@ -81,8 +81,12 @@ class ScreenLogicClimate(ScreenLogicPushEntity, ClimateEntity, RestoreEntity):
|
||||
entity_description: ScreenLogicClimateDescription
|
||||
_attr_hvac_modes = SUPPORTED_MODES
|
||||
_attr_supported_features = (
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
| ClimateEntityFeature.PRESET_MODE
|
||||
| ClimateEntityFeature.TURN_OFF
|
||||
| ClimateEntityFeature.TURN_ON
|
||||
)
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(self, coordinator, entity_description) -> None:
|
||||
"""Initialize a ScreenLogic climate entity."""
|
||||
|
||||
@@ -45,6 +45,7 @@ class SENZClimate(CoordinatorEntity, ClimateEntity):
|
||||
_attr_min_temp = 5
|
||||
_attr_has_entity_name = True
|
||||
_attr_name = None
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
||||
@@ -167,6 +167,7 @@ class BlockSleepingClimate(
|
||||
)
|
||||
_attr_target_temperature_step = SHTRV_01_TEMPERATURE_SETTINGS["step"]
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -448,6 +449,7 @@ class RpcClimate(ShellyRpcEntity, ClimateEntity):
|
||||
)
|
||||
_attr_target_temperature_step = RPC_THERMOSTAT_SETTINGS["step"]
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(self, coordinator: ShellyRpcCoordinator, id_: int) -> None:
|
||||
"""Initialize."""
|
||||
|
||||
@@ -162,6 +162,8 @@ def get_capabilities(capabilities: Sequence[str]) -> Sequence[str] | None:
|
||||
class SmartThingsThermostat(SmartThingsEntity, ClimateEntity):
|
||||
"""Define a SmartThings climate entities."""
|
||||
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(self, device):
|
||||
"""Init the class."""
|
||||
super().__init__(device)
|
||||
@@ -343,6 +345,7 @@ class SmartThingsAirConditioner(SmartThingsEntity, ClimateEntity):
|
||||
"""Define a SmartThings Air Conditioner."""
|
||||
|
||||
_hvac_modes: list[HVACMode]
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(self, device) -> None:
|
||||
"""Init the class."""
|
||||
|
||||
@@ -67,6 +67,7 @@ class SmartTubThermostat(SmartTubEntity, ClimateEntity):
|
||||
)
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_attr_preset_modes = list(PRESET_MODES.values())
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(self, coordinator, spa):
|
||||
"""Initialize the entity."""
|
||||
|
||||
@@ -76,6 +76,10 @@ class SonosEntity(Entity):
|
||||
@property
|
||||
def device_info(self) -> DeviceInfo:
|
||||
"""Return information about the device."""
|
||||
suggested_area: str | None = None
|
||||
if not self.speaker.battery_info:
|
||||
# Only set suggested area for non-portable devices
|
||||
suggested_area = self.speaker.zone_name
|
||||
return DeviceInfo(
|
||||
identifiers={(DOMAIN, self.soco.uid)},
|
||||
name=self.speaker.zone_name,
|
||||
@@ -86,7 +90,7 @@ class SonosEntity(Entity):
|
||||
(dr.CONNECTION_UPNP, f"uuid:{self.speaker.uid}"),
|
||||
},
|
||||
manufacturer="Sonos",
|
||||
suggested_area=self.speaker.zone_name,
|
||||
suggested_area=suggested_area,
|
||||
configuration_url=f"http://{self.soco.ip_address}:1400/support/review",
|
||||
)
|
||||
|
||||
|
||||
@@ -73,9 +73,13 @@ class StiebelEltron(ClimateEntity):
|
||||
|
||||
_attr_hvac_modes = SUPPORT_HVAC
|
||||
_attr_supported_features = (
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
| ClimateEntityFeature.PRESET_MODE
|
||||
| ClimateEntityFeature.TURN_OFF
|
||||
| ClimateEntityFeature.TURN_ON
|
||||
)
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(self, name, ste_data):
|
||||
"""Initialize the unit."""
|
||||
|
||||
@@ -131,7 +131,10 @@ def create_climate_entity(tado, name: str, zone_id: int, device_info: dict):
|
||||
|
||||
zone_type = capabilities["type"]
|
||||
support_flags = (
|
||||
ClimateEntityFeature.PRESET_MODE | ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
ClimateEntityFeature.PRESET_MODE
|
||||
| ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
| ClimateEntityFeature.TURN_OFF
|
||||
| ClimateEntityFeature.TURN_ON
|
||||
)
|
||||
supported_hvac_modes = [
|
||||
TADO_TO_HA_HVAC_MODE_MAP[CONST_MODE_OFF],
|
||||
@@ -221,6 +224,7 @@ class TadoClimate(TadoZoneEntity, ClimateEntity):
|
||||
_attr_name = None
|
||||
_attr_translation_key = DOMAIN
|
||||
_available = False
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Diagnostics support for Tankerkoenig."""
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import asdict
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.diagnostics import async_redact_data
|
||||
@@ -27,6 +28,9 @@ async def async_get_config_entry_diagnostics(
|
||||
|
||||
diag_data = {
|
||||
"entry": async_redact_data(entry.as_dict(), TO_REDACT),
|
||||
"data": coordinator.data,
|
||||
"data": {
|
||||
station_id: asdict(price_info)
|
||||
for station_id, price_info in coordinator.data.items()
|
||||
},
|
||||
}
|
||||
return diag_data
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/tankerkoenig",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["aiotankerkoenig"],
|
||||
"requirements": ["aiotankerkoenig==0.2.0"]
|
||||
"requirements": ["aiotankerkoenig==0.3.0"]
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ class TeslemetryClimateEntity(TeslemetryVehicleEntity, ClimateEntity):
|
||||
| ClimateEntityFeature.PRESET_MODE
|
||||
)
|
||||
_attr_preset_modes = ["off", "keep", "dog", "camp"]
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
@property
|
||||
def hvac_mode(self) -> HVACMode | None:
|
||||
|
||||
@@ -56,6 +56,7 @@ class TessieClimateEntity(TessieEntity, ClimateEntity):
|
||||
TessieClimateKeeper.DOG,
|
||||
TessieClimateKeeper.CAMP,
|
||||
]
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
||||
@@ -83,8 +83,11 @@ class TfiacClimate(ClimateEntity):
|
||||
ClimateEntityFeature.FAN_MODE
|
||||
| ClimateEntityFeature.SWING_MODE
|
||||
| ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
| ClimateEntityFeature.TURN_OFF
|
||||
| ClimateEntityFeature.TURN_ON
|
||||
)
|
||||
_attr_temperature_unit = UnitOfTemperature.FAHRENHEIT
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(self, hass, client):
|
||||
"""Init class."""
|
||||
|
||||
@@ -58,6 +58,7 @@ class SaunaClimate(ToloSaunaCoordinatorEntity, ClimateEntity):
|
||||
)
|
||||
_attr_target_temperature_step = 1
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(
|
||||
self, coordinator: ToloSaunaUpdateCoordinator, entry: ConfigEntry
|
||||
|
||||
@@ -51,6 +51,7 @@ class ToonThermostatDevice(ToonDisplayDeviceEntity, ClimateEntity):
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE
|
||||
)
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
||||
@@ -69,6 +69,7 @@ class Touchline(ClimateEntity):
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE
|
||||
)
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(self, touchline_thermostat):
|
||||
"""Initialize the Touchline device."""
|
||||
|
||||
@@ -156,6 +156,10 @@
|
||||
"hostname": "k[lps]*",
|
||||
"macaddress": "54AF97*"
|
||||
},
|
||||
{
|
||||
"hostname": "l[59]*",
|
||||
"macaddress": "54AF97*"
|
||||
},
|
||||
{
|
||||
"hostname": "k[lps]*",
|
||||
"macaddress": "AC15A2*"
|
||||
@@ -177,21 +181,41 @@
|
||||
"macaddress": "5CE931*"
|
||||
},
|
||||
{
|
||||
"hostname": "l5*",
|
||||
"hostname": "l[59]*",
|
||||
"macaddress": "3C52A1*"
|
||||
},
|
||||
{
|
||||
"hostname": "l5*",
|
||||
"macaddress": "5C628B*"
|
||||
},
|
||||
{
|
||||
"hostname": "tp*",
|
||||
"macaddress": "5C628B*"
|
||||
},
|
||||
{
|
||||
"hostname": "p1*",
|
||||
"macaddress": "482254*"
|
||||
},
|
||||
{
|
||||
"hostname": "s5*",
|
||||
"macaddress": "482254*"
|
||||
},
|
||||
{
|
||||
"hostname": "p1*",
|
||||
"macaddress": "30DE4B*"
|
||||
},
|
||||
{
|
||||
"hostname": "p1*",
|
||||
"macaddress": "3C52A1*"
|
||||
},
|
||||
{
|
||||
"hostname": "tp*",
|
||||
"macaddress": "3C52A1*"
|
||||
},
|
||||
{
|
||||
"hostname": "s5*",
|
||||
"macaddress": "3C52A1*"
|
||||
},
|
||||
{
|
||||
"hostname": "l9*",
|
||||
"macaddress": "A842A1*"
|
||||
@@ -199,11 +223,51 @@
|
||||
{
|
||||
"hostname": "l9*",
|
||||
"macaddress": "3460F9*"
|
||||
},
|
||||
{
|
||||
"hostname": "hs*",
|
||||
"macaddress": "704F57*"
|
||||
},
|
||||
{
|
||||
"hostname": "k[lps]*",
|
||||
"macaddress": "74DA88*"
|
||||
},
|
||||
{
|
||||
"hostname": "p3*",
|
||||
"macaddress": "788CB5*"
|
||||
},
|
||||
{
|
||||
"hostname": "p1*",
|
||||
"macaddress": "CC32E5*"
|
||||
},
|
||||
{
|
||||
"hostname": "k[lps]*",
|
||||
"macaddress": "CC32E5*"
|
||||
},
|
||||
{
|
||||
"hostname": "hs*",
|
||||
"macaddress": "CC32E5*"
|
||||
},
|
||||
{
|
||||
"hostname": "k[lps]*",
|
||||
"macaddress": "D80D17*"
|
||||
},
|
||||
{
|
||||
"hostname": "k[lps]*",
|
||||
"macaddress": "D84732*"
|
||||
},
|
||||
{
|
||||
"hostname": "p1*",
|
||||
"macaddress": "F0A731*"
|
||||
},
|
||||
{
|
||||
"hostname": "l9*",
|
||||
"macaddress": "F0A731*"
|
||||
}
|
||||
],
|
||||
"documentation": "https://www.home-assistant.io/integrations/tplink",
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["kasa"],
|
||||
"quality_scale": "platinum",
|
||||
"requirements": ["python-kasa[speedups]==0.6.2"]
|
||||
"requirements": ["python-kasa[speedups]==0.6.2.1"]
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"config_flow": true,
|
||||
"dependencies": ["webhook"],
|
||||
"documentation": "https://www.home-assistant.io/integrations/traccar",
|
||||
"iot_class": "local_polling",
|
||||
"iot_class": "cloud_push",
|
||||
"loggers": ["pytraccar"],
|
||||
"requirements": ["pytraccar==2.0.0", "stringcase==1.2.0"]
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ class VelbusClimate(VelbusEntity, ClimateEntity):
|
||||
_attr_hvac_mode = HVACMode.HEAT
|
||||
_attr_hvac_modes = [HVACMode.HEAT]
|
||||
_attr_preset_modes = list(PRESET_MODES)
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
@property
|
||||
def target_temperature(self) -> float | None:
|
||||
|
||||
@@ -108,6 +108,7 @@ class VenstarThermostat(VenstarEntity, ClimateEntity):
|
||||
_attr_hvac_modes = [HVACMode.HEAT, HVACMode.COOL, HVACMode.OFF, HVACMode.AUTO]
|
||||
_attr_precision = PRECISION_HALVES
|
||||
_attr_name = None
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -130,6 +131,8 @@ class VenstarThermostat(VenstarEntity, ClimateEntity):
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
| ClimateEntityFeature.FAN_MODE
|
||||
| ClimateEntityFeature.PRESET_MODE
|
||||
| ClimateEntityFeature.TURN_OFF
|
||||
| ClimateEntityFeature.TURN_ON
|
||||
)
|
||||
|
||||
if self._client.mode == self._client.MODE_AUTO:
|
||||
|
||||
@@ -53,6 +53,7 @@ class VeraThermostat(VeraDevice[veraApi.VeraThermostat], ClimateEntity):
|
||||
| ClimateEntityFeature.TURN_OFF
|
||||
| ClimateEntityFeature.TURN_ON
|
||||
)
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(
|
||||
self, vera_device: veraApi.VeraThermostat, controller_data: ControllerData
|
||||
|
||||
@@ -10,6 +10,7 @@ from typing import Any
|
||||
|
||||
from PyViCare.PyViCare import PyViCare
|
||||
from PyViCare.PyViCareDevice import Device
|
||||
from PyViCare.PyViCareDeviceConfig import PyViCareDeviceConfig
|
||||
from PyViCare.PyViCareUtils import (
|
||||
PyViCareInvalidConfigurationError,
|
||||
PyViCareInvalidCredentialsError,
|
||||
@@ -85,15 +86,16 @@ def setup_vicare_api(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
"""Set up PyVicare API."""
|
||||
vicare_api = vicare_login(hass, entry.data)
|
||||
|
||||
for device in vicare_api.devices:
|
||||
_LOGGER.info(
|
||||
device_config_list = get_supported_devices(vicare_api.devices)
|
||||
|
||||
for device in device_config_list:
|
||||
_LOGGER.debug(
|
||||
"Found device: %s (online: %s)", device.getModel(), str(device.isOnline())
|
||||
)
|
||||
|
||||
# Currently we only support a single device
|
||||
device_list = vicare_api.devices
|
||||
device = device_list[0]
|
||||
hass.data[DOMAIN][entry.entry_id][VICARE_DEVICE_CONFIG_LIST] = device_list
|
||||
device = device_config_list[0]
|
||||
hass.data[DOMAIN][entry.entry_id][VICARE_DEVICE_CONFIG_LIST] = device_config_list
|
||||
hass.data[DOMAIN][entry.entry_id][VICARE_DEVICE_CONFIG] = device
|
||||
hass.data[DOMAIN][entry.entry_id][VICARE_API] = getattr(
|
||||
device,
|
||||
@@ -113,3 +115,14 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
)
|
||||
|
||||
return unload_ok
|
||||
|
||||
|
||||
def get_supported_devices(
|
||||
devices: list[PyViCareDeviceConfig],
|
||||
) -> list[PyViCareDeviceConfig]:
|
||||
"""Remove unsupported devices from the list."""
|
||||
return [
|
||||
device_config
|
||||
for device_config in devices
|
||||
if device_config.getModel() not in ["Heatbox1", "Heatbox2_SRC"]
|
||||
]
|
||||
|
||||
@@ -157,6 +157,7 @@ class ViCareClimate(ViCareEntity, ClimateEntity):
|
||||
_attr_preset_modes = list(HA_TO_VICARE_PRESET_HEATING)
|
||||
_current_action: bool | None = None
|
||||
_current_mode: str | None = None
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
||||
@@ -118,7 +118,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
async def async_step_dhcp(self, discovery_info: dhcp.DhcpServiceInfo) -> FlowResult:
|
||||
"""Invoke when a Viessmann MAC address is discovered on the network."""
|
||||
formatted_mac = format_mac(discovery_info.macaddress)
|
||||
_LOGGER.info("Found device with mac %s", formatted_mac)
|
||||
_LOGGER.debug("Found device with mac %s", formatted_mac)
|
||||
|
||||
await self.async_set_unique_id(formatted_mac)
|
||||
self._abort_if_unique_id_configured()
|
||||
|
||||
@@ -658,41 +658,6 @@ COMPRESSOR_SENSORS: tuple[ViCareSensorEntityDescription, ...] = (
|
||||
)
|
||||
|
||||
|
||||
def _build_entity(
|
||||
vicare_api,
|
||||
device_config: PyViCareDeviceConfig,
|
||||
entity_description: ViCareSensorEntityDescription,
|
||||
):
|
||||
"""Create a ViCare sensor entity."""
|
||||
if is_supported(entity_description.key, entity_description, vicare_api):
|
||||
return ViCareSensor(
|
||||
vicare_api,
|
||||
device_config,
|
||||
entity_description,
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
async def _entities_from_descriptions(
|
||||
hass: HomeAssistant,
|
||||
entities: list[ViCareSensor],
|
||||
sensor_descriptions: tuple[ViCareSensorEntityDescription, ...],
|
||||
iterables,
|
||||
config_entry: ConfigEntry,
|
||||
) -> None:
|
||||
"""Create entities from descriptions and list of burners/circuits."""
|
||||
for description in sensor_descriptions:
|
||||
for current in iterables:
|
||||
entity = await hass.async_add_executor_job(
|
||||
_build_entity,
|
||||
current,
|
||||
hass.data[DOMAIN][config_entry.entry_id][VICARE_DEVICE_CONFIG],
|
||||
description,
|
||||
)
|
||||
if entity:
|
||||
entities.append(entity)
|
||||
|
||||
|
||||
def _build_entities(
|
||||
device: PyViCareDevice,
|
||||
device_config: PyViCareDeviceConfig,
|
||||
|
||||
@@ -103,10 +103,13 @@ class AirConEntity(ClimateEntity):
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
| ClimateEntityFeature.FAN_MODE
|
||||
| ClimateEntityFeature.SWING_MODE
|
||||
| ClimateEntityFeature.TURN_OFF
|
||||
| ClimateEntityFeature.TURN_ON
|
||||
)
|
||||
_attr_swing_modes = SUPPORTED_SWING_MODES
|
||||
_attr_target_temperature_step = SUPPORTED_TARGET_TEMPERATURE_STEP
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
||||
@@ -54,6 +54,7 @@ class XS1ThermostatEntity(XS1DeviceEntity, ClimateEntity):
|
||||
_attr_hvac_mode = HVACMode.HEAT
|
||||
_attr_hvac_modes = [HVACMode.HEAT]
|
||||
_attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(self, device, sensor):
|
||||
"""Initialize the actuator."""
|
||||
|
||||
@@ -62,6 +62,7 @@ class YoLinkClimateEntity(YoLinkEntity, ClimateEntity):
|
||||
"""YoLink Climate Entity."""
|
||||
|
||||
_attr_name = None
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -86,6 +87,8 @@ class YoLinkClimateEntity(YoLinkEntity, ClimateEntity):
|
||||
ClimateEntityFeature.FAN_MODE
|
||||
| ClimateEntityFeature.PRESET_MODE
|
||||
| ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
|
||||
| ClimateEntityFeature.TURN_OFF
|
||||
| ClimateEntityFeature.TURN_ON
|
||||
)
|
||||
|
||||
@callback
|
||||
|
||||
@@ -141,6 +141,7 @@ class Thermostat(ZhaEntity, ClimateEntity):
|
||||
_attr_precision = PRECISION_TENTHS
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_attr_translation_key: str = "thermostat"
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(self, unique_id, zha_device, cluster_handlers, **kwargs):
|
||||
"""Initialize ZHA Thermostat instance."""
|
||||
|
||||
@@ -128,9 +128,13 @@ class ZhongHongClimate(ClimateEntity):
|
||||
]
|
||||
_attr_should_poll = False
|
||||
_attr_supported_features = (
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.FAN_MODE
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
| ClimateEntityFeature.FAN_MODE
|
||||
| ClimateEntityFeature.TURN_OFF
|
||||
| ClimateEntityFeature.TURN_ON
|
||||
)
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(self, hub, addr_out, addr_in):
|
||||
"""Set up the ZhongHong climate devices."""
|
||||
|
||||
@@ -56,6 +56,7 @@ class ZWaveMeClimate(ZWaveMeEntity, ClimateEntity):
|
||||
_attr_hvac_mode = HVACMode.HEAT
|
||||
_attr_hvac_modes = [HVACMode.HEAT]
|
||||
_attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def set_temperature(self, **kwargs: Any) -> None:
|
||||
"""Set new target temperature."""
|
||||
|
||||
@@ -16,7 +16,7 @@ from .helpers.deprecation import (
|
||||
APPLICATION_NAME: Final = "HomeAssistant"
|
||||
MAJOR_VERSION: Final = 2024
|
||||
MINOR_VERSION: Final = 2
|
||||
PATCH_VERSION: Final = "0b3"
|
||||
PATCH_VERSION: Final = "0b4"
|
||||
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
||||
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
|
||||
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 11, 0)
|
||||
|
||||
@@ -788,6 +788,11 @@ DHCP: list[dict[str, str | bool]] = [
|
||||
"hostname": "k[lps]*",
|
||||
"macaddress": "54AF97*",
|
||||
},
|
||||
{
|
||||
"domain": "tplink",
|
||||
"hostname": "l[59]*",
|
||||
"macaddress": "54AF97*",
|
||||
},
|
||||
{
|
||||
"domain": "tplink",
|
||||
"hostname": "k[lps]*",
|
||||
@@ -815,7 +820,7 @@ DHCP: list[dict[str, str | bool]] = [
|
||||
},
|
||||
{
|
||||
"domain": "tplink",
|
||||
"hostname": "l5*",
|
||||
"hostname": "l[59]*",
|
||||
"macaddress": "3C52A1*",
|
||||
},
|
||||
{
|
||||
@@ -823,16 +828,41 @@ DHCP: list[dict[str, str | bool]] = [
|
||||
"hostname": "l5*",
|
||||
"macaddress": "5C628B*",
|
||||
},
|
||||
{
|
||||
"domain": "tplink",
|
||||
"hostname": "tp*",
|
||||
"macaddress": "5C628B*",
|
||||
},
|
||||
{
|
||||
"domain": "tplink",
|
||||
"hostname": "p1*",
|
||||
"macaddress": "482254*",
|
||||
},
|
||||
{
|
||||
"domain": "tplink",
|
||||
"hostname": "s5*",
|
||||
"macaddress": "482254*",
|
||||
},
|
||||
{
|
||||
"domain": "tplink",
|
||||
"hostname": "p1*",
|
||||
"macaddress": "30DE4B*",
|
||||
},
|
||||
{
|
||||
"domain": "tplink",
|
||||
"hostname": "p1*",
|
||||
"macaddress": "3C52A1*",
|
||||
},
|
||||
{
|
||||
"domain": "tplink",
|
||||
"hostname": "tp*",
|
||||
"macaddress": "3C52A1*",
|
||||
},
|
||||
{
|
||||
"domain": "tplink",
|
||||
"hostname": "s5*",
|
||||
"macaddress": "3C52A1*",
|
||||
},
|
||||
{
|
||||
"domain": "tplink",
|
||||
"hostname": "l9*",
|
||||
@@ -843,6 +873,56 @@ DHCP: list[dict[str, str | bool]] = [
|
||||
"hostname": "l9*",
|
||||
"macaddress": "3460F9*",
|
||||
},
|
||||
{
|
||||
"domain": "tplink",
|
||||
"hostname": "hs*",
|
||||
"macaddress": "704F57*",
|
||||
},
|
||||
{
|
||||
"domain": "tplink",
|
||||
"hostname": "k[lps]*",
|
||||
"macaddress": "74DA88*",
|
||||
},
|
||||
{
|
||||
"domain": "tplink",
|
||||
"hostname": "p3*",
|
||||
"macaddress": "788CB5*",
|
||||
},
|
||||
{
|
||||
"domain": "tplink",
|
||||
"hostname": "p1*",
|
||||
"macaddress": "CC32E5*",
|
||||
},
|
||||
{
|
||||
"domain": "tplink",
|
||||
"hostname": "k[lps]*",
|
||||
"macaddress": "CC32E5*",
|
||||
},
|
||||
{
|
||||
"domain": "tplink",
|
||||
"hostname": "hs*",
|
||||
"macaddress": "CC32E5*",
|
||||
},
|
||||
{
|
||||
"domain": "tplink",
|
||||
"hostname": "k[lps]*",
|
||||
"macaddress": "D80D17*",
|
||||
},
|
||||
{
|
||||
"domain": "tplink",
|
||||
"hostname": "k[lps]*",
|
||||
"macaddress": "D84732*",
|
||||
},
|
||||
{
|
||||
"domain": "tplink",
|
||||
"hostname": "p1*",
|
||||
"macaddress": "F0A731*",
|
||||
},
|
||||
{
|
||||
"domain": "tplink",
|
||||
"hostname": "l9*",
|
||||
"macaddress": "F0A731*",
|
||||
},
|
||||
{
|
||||
"domain": "tuya",
|
||||
"macaddress": "105A17*",
|
||||
|
||||
@@ -6168,7 +6168,7 @@
|
||||
"traccar": {
|
||||
"integration_type": "hub",
|
||||
"config_flow": true,
|
||||
"iot_class": "local_polling",
|
||||
"iot_class": "cloud_push",
|
||||
"name": "Traccar Client"
|
||||
},
|
||||
"traccar_server": {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"""Module to coordinate user intentions."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
@@ -401,17 +402,21 @@ class ServiceIntentHandler(IntentHandler):
|
||||
hass = intent_obj.hass
|
||||
slots = self.async_validate_slots(intent_obj.slots)
|
||||
|
||||
name: str | None = slots.get("name", {}).get("value")
|
||||
if name == "all":
|
||||
name_slot = slots.get("name", {})
|
||||
entity_id: str | None = name_slot.get("value")
|
||||
entity_name: str | None = name_slot.get("text")
|
||||
if entity_id == "all":
|
||||
# Don't match on name if targeting all entities
|
||||
name = None
|
||||
entity_id = None
|
||||
|
||||
# Look up area first to fail early
|
||||
area_name = slots.get("area", {}).get("value")
|
||||
area_slot = slots.get("area", {})
|
||||
area_id = area_slot.get("value")
|
||||
area_name = area_slot.get("text")
|
||||
area: area_registry.AreaEntry | None = None
|
||||
if area_name is not None:
|
||||
if area_id is not None:
|
||||
areas = area_registry.async_get(hass)
|
||||
area = areas.async_get_area(area_name) or areas.async_get_area_by_name(
|
||||
area = areas.async_get_area(area_id) or areas.async_get_area_by_name(
|
||||
area_name
|
||||
)
|
||||
if area is None:
|
||||
@@ -431,7 +436,7 @@ class ServiceIntentHandler(IntentHandler):
|
||||
states = list(
|
||||
async_match_states(
|
||||
hass,
|
||||
name=name,
|
||||
name=entity_id,
|
||||
area=area,
|
||||
domains=domains,
|
||||
device_classes=device_classes,
|
||||
@@ -442,8 +447,8 @@ class ServiceIntentHandler(IntentHandler):
|
||||
if not states:
|
||||
# No states matched constraints
|
||||
raise NoStatesMatchedError(
|
||||
name=name,
|
||||
area=area_name,
|
||||
name=entity_name or entity_id,
|
||||
area=area_name or area_id,
|
||||
domains=domains,
|
||||
device_classes=device_classes,
|
||||
)
|
||||
|
||||
@@ -26,10 +26,10 @@ ha-av==10.1.1
|
||||
ha-ffmpeg==3.1.0
|
||||
habluetooth==2.4.0
|
||||
hass-nabucasa==0.76.0
|
||||
hassil==1.6.0
|
||||
hassil==1.6.1
|
||||
home-assistant-bluetooth==1.12.0
|
||||
home-assistant-frontend==20240202.0
|
||||
home-assistant-intents==2024.2.1
|
||||
home-assistant-intents==2024.2.2
|
||||
httpx==0.26.0
|
||||
ifaddr==0.2.0
|
||||
janus==1.0.0
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "homeassistant"
|
||||
version = "2024.2.0b3"
|
||||
version = "2024.2.0b4"
|
||||
license = {text = "Apache-2.0"}
|
||||
description = "Open-source home automation platform running on Python 3."
|
||||
readme = "README.rst"
|
||||
|
||||
@@ -233,7 +233,7 @@ aioeagle==1.1.0
|
||||
aioecowitt==2023.5.0
|
||||
|
||||
# homeassistant.components.co2signal
|
||||
aioelectricitymaps==0.2.0
|
||||
aioelectricitymaps==0.3.0
|
||||
|
||||
# homeassistant.components.emonitor
|
||||
aioemonitor==1.0.5
|
||||
@@ -257,7 +257,7 @@ aioguardian==2022.07.0
|
||||
aioharmony==0.2.10
|
||||
|
||||
# homeassistant.components.homekit_controller
|
||||
aiohomekit==3.1.3
|
||||
aiohomekit==3.1.4
|
||||
|
||||
# homeassistant.components.http
|
||||
aiohttp-fast-url-dispatcher==0.3.0
|
||||
@@ -377,7 +377,7 @@ aioswitcher==3.4.1
|
||||
aiosyncthing==0.5.1
|
||||
|
||||
# homeassistant.components.tankerkoenig
|
||||
aiotankerkoenig==0.2.0
|
||||
aiotankerkoenig==0.3.0
|
||||
|
||||
# homeassistant.components.tractive
|
||||
aiotractive==0.5.6
|
||||
@@ -758,7 +758,7 @@ ecoaliface==0.4.0
|
||||
electrickiwi-api==0.8.5
|
||||
|
||||
# homeassistant.components.elgato
|
||||
elgato==5.1.1
|
||||
elgato==5.1.2
|
||||
|
||||
# homeassistant.components.eliqonline
|
||||
eliqonline==1.2.2
|
||||
@@ -1025,7 +1025,7 @@ hass-nabucasa==0.76.0
|
||||
hass-splunk==0.1.1
|
||||
|
||||
# homeassistant.components.conversation
|
||||
hassil==1.6.0
|
||||
hassil==1.6.1
|
||||
|
||||
# homeassistant.components.jewish_calendar
|
||||
hdate==0.10.4
|
||||
@@ -1062,7 +1062,7 @@ holidays==0.41
|
||||
home-assistant-frontend==20240202.0
|
||||
|
||||
# homeassistant.components.conversation
|
||||
home-assistant-intents==2024.2.1
|
||||
home-assistant-intents==2024.2.2
|
||||
|
||||
# homeassistant.components.home_connect
|
||||
homeconnect==0.7.2
|
||||
@@ -1256,7 +1256,7 @@ lxml==5.1.0
|
||||
mac-vendor-lookup==0.1.12
|
||||
|
||||
# homeassistant.components.matrix
|
||||
matrix-nio==0.22.1
|
||||
matrix-nio==0.24.0
|
||||
|
||||
# homeassistant.components.maxcube
|
||||
maxcube-api==0.4.3
|
||||
@@ -2232,7 +2232,7 @@ python-join-api==0.0.9
|
||||
python-juicenet==1.1.0
|
||||
|
||||
# homeassistant.components.tplink
|
||||
python-kasa[speedups]==0.6.2
|
||||
python-kasa[speedups]==0.6.2.1
|
||||
|
||||
# homeassistant.components.lirc
|
||||
# python-lirc==1.2.3
|
||||
|
||||
@@ -212,7 +212,7 @@ aioeagle==1.1.0
|
||||
aioecowitt==2023.5.0
|
||||
|
||||
# homeassistant.components.co2signal
|
||||
aioelectricitymaps==0.2.0
|
||||
aioelectricitymaps==0.3.0
|
||||
|
||||
# homeassistant.components.emonitor
|
||||
aioemonitor==1.0.5
|
||||
@@ -233,7 +233,7 @@ aioguardian==2022.07.0
|
||||
aioharmony==0.2.10
|
||||
|
||||
# homeassistant.components.homekit_controller
|
||||
aiohomekit==3.1.3
|
||||
aiohomekit==3.1.4
|
||||
|
||||
# homeassistant.components.http
|
||||
aiohttp-fast-url-dispatcher==0.3.0
|
||||
@@ -350,7 +350,7 @@ aioswitcher==3.4.1
|
||||
aiosyncthing==0.5.1
|
||||
|
||||
# homeassistant.components.tankerkoenig
|
||||
aiotankerkoenig==0.2.0
|
||||
aiotankerkoenig==0.3.0
|
||||
|
||||
# homeassistant.components.tractive
|
||||
aiotractive==0.5.6
|
||||
@@ -618,7 +618,7 @@ easyenergy==2.1.0
|
||||
electrickiwi-api==0.8.5
|
||||
|
||||
# homeassistant.components.elgato
|
||||
elgato==5.1.1
|
||||
elgato==5.1.2
|
||||
|
||||
# homeassistant.components.elkm1
|
||||
elkm1-lib==2.2.6
|
||||
@@ -830,7 +830,7 @@ habluetooth==2.4.0
|
||||
hass-nabucasa==0.76.0
|
||||
|
||||
# homeassistant.components.conversation
|
||||
hassil==1.6.0
|
||||
hassil==1.6.1
|
||||
|
||||
# homeassistant.components.jewish_calendar
|
||||
hdate==0.10.4
|
||||
@@ -858,7 +858,7 @@ holidays==0.41
|
||||
home-assistant-frontend==20240202.0
|
||||
|
||||
# homeassistant.components.conversation
|
||||
home-assistant-intents==2024.2.1
|
||||
home-assistant-intents==2024.2.2
|
||||
|
||||
# homeassistant.components.home_connect
|
||||
homeconnect==0.7.2
|
||||
@@ -998,7 +998,7 @@ lxml==5.1.0
|
||||
mac-vendor-lookup==0.1.12
|
||||
|
||||
# homeassistant.components.matrix
|
||||
matrix-nio==0.22.1
|
||||
matrix-nio==0.24.0
|
||||
|
||||
# homeassistant.components.maxcube
|
||||
maxcube-api==0.4.3
|
||||
@@ -1708,7 +1708,7 @@ python-izone==1.2.9
|
||||
python-juicenet==1.1.0
|
||||
|
||||
# homeassistant.components.tplink
|
||||
python-kasa[speedups]==0.6.2
|
||||
python-kasa[speedups]==0.6.2.1
|
||||
|
||||
# homeassistant.components.matter
|
||||
python-matter-server==5.4.0
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
"""Test the CO2 Signal config flow."""
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
from aioelectricitymaps.exceptions import (
|
||||
ElectricityMapsDecodeError,
|
||||
from aioelectricitymaps import (
|
||||
ElectricityMapsConnectionError,
|
||||
ElectricityMapsError,
|
||||
InvalidToken,
|
||||
ElectricityMapsInvalidTokenError,
|
||||
)
|
||||
import pytest
|
||||
|
||||
@@ -134,11 +134,11 @@ async def test_form_country(hass: HomeAssistant) -> None:
|
||||
("side_effect", "err_code"),
|
||||
[
|
||||
(
|
||||
InvalidToken,
|
||||
ElectricityMapsInvalidTokenError,
|
||||
"invalid_auth",
|
||||
),
|
||||
(ElectricityMapsError("Something else"), "unknown"),
|
||||
(ElectricityMapsDecodeError("Boom"), "unknown"),
|
||||
(ElectricityMapsConnectionError("Boom"), "unknown"),
|
||||
],
|
||||
ids=[
|
||||
"invalid auth",
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
from datetime import timedelta
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
from aioelectricitymaps.exceptions import (
|
||||
ElectricityMapsDecodeError,
|
||||
from aioelectricitymaps import (
|
||||
ElectricityMapsConnectionError,
|
||||
ElectricityMapsConnectionTimeoutError,
|
||||
ElectricityMapsError,
|
||||
InvalidToken,
|
||||
ElectricityMapsInvalidTokenError,
|
||||
)
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
import pytest
|
||||
@@ -42,7 +43,8 @@ async def test_sensor(
|
||||
@pytest.mark.parametrize(
|
||||
"error",
|
||||
[
|
||||
ElectricityMapsDecodeError,
|
||||
ElectricityMapsConnectionTimeoutError,
|
||||
ElectricityMapsConnectionError,
|
||||
ElectricityMapsError,
|
||||
Exception,
|
||||
],
|
||||
@@ -93,8 +95,12 @@ async def test_sensor_reauth_triggered(
|
||||
assert (state := hass.states.get("sensor.electricity_maps_co2_intensity"))
|
||||
assert state.state == "45.9862319009581"
|
||||
|
||||
electricity_maps.latest_carbon_intensity_by_coordinates.side_effect = InvalidToken
|
||||
electricity_maps.latest_carbon_intensity_by_country_code.side_effect = InvalidToken
|
||||
electricity_maps.latest_carbon_intensity_by_coordinates.side_effect = (
|
||||
ElectricityMapsInvalidTokenError
|
||||
)
|
||||
electricity_maps.latest_carbon_intensity_by_country_code.side_effect = (
|
||||
ElectricityMapsInvalidTokenError
|
||||
)
|
||||
|
||||
freezer.tick(timedelta(minutes=20))
|
||||
async_fire_time_changed(hass)
|
||||
|
||||
@@ -1397,7 +1397,7 @@
|
||||
'name': dict({
|
||||
'name': 'name',
|
||||
'text': 'my cool light',
|
||||
'value': 'my cool light',
|
||||
'value': 'light.kitchen',
|
||||
}),
|
||||
}),
|
||||
'intent': dict({
|
||||
@@ -1422,7 +1422,7 @@
|
||||
'name': dict({
|
||||
'name': 'name',
|
||||
'text': 'my cool light',
|
||||
'value': 'my cool light',
|
||||
'value': 'light.kitchen',
|
||||
}),
|
||||
}),
|
||||
'intent': dict({
|
||||
@@ -1498,7 +1498,7 @@
|
||||
'sentence_template': '[tell me] how many {on_off_domains:domain} (is|are) {on_off_states:state} [in <area>]',
|
||||
'slots': dict({
|
||||
'area': 'kitchen',
|
||||
'domain': 'light',
|
||||
'domain': 'lights',
|
||||
'state': 'on',
|
||||
}),
|
||||
'source': 'builtin',
|
||||
@@ -1572,7 +1572,7 @@
|
||||
'name': dict({
|
||||
'name': 'name',
|
||||
'text': 'test light',
|
||||
'value': 'test light',
|
||||
'value': 'light.demo_1234',
|
||||
}),
|
||||
}),
|
||||
'intent': dict({
|
||||
@@ -1581,7 +1581,7 @@
|
||||
'match': True,
|
||||
'sentence_template': '[<numeric_value_set>] <name> brightness [to] <brightness>',
|
||||
'slots': dict({
|
||||
'brightness': 100,
|
||||
'brightness': '100%',
|
||||
'name': 'test light',
|
||||
}),
|
||||
'source': 'builtin',
|
||||
@@ -1604,7 +1604,7 @@
|
||||
'name': dict({
|
||||
'name': 'name',
|
||||
'text': 'test light',
|
||||
'value': 'test light',
|
||||
'value': 'light.demo_1234',
|
||||
}),
|
||||
}),
|
||||
'intent': dict({
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"""Test for the default agent."""
|
||||
|
||||
from collections import defaultdict
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
@@ -85,8 +86,10 @@ async def test_exposed_areas(
|
||||
entity_registry: er.EntityRegistry,
|
||||
) -> None:
|
||||
"""Test that all areas are exposed."""
|
||||
area_kitchen = area_registry.async_get_or_create("kitchen")
|
||||
area_bedroom = area_registry.async_get_or_create("bedroom")
|
||||
area_kitchen = area_registry.async_get_or_create("kitchen_id")
|
||||
area_kitchen = area_registry.async_update(area_kitchen.id, name="kitchen")
|
||||
area_bedroom = area_registry.async_get_or_create("bedroom_id")
|
||||
area_bedroom = area_registry.async_update(area_bedroom.id, name="bedroom")
|
||||
|
||||
entry = MockConfigEntry()
|
||||
entry.add_to_hass(hass)
|
||||
@@ -122,6 +125,9 @@ async def test_exposed_areas(
|
||||
|
||||
# All is well for the exposed kitchen light
|
||||
assert result.response.response_type == intent.IntentResponseType.ACTION_DONE
|
||||
assert result.response.intent is not None
|
||||
assert result.response.intent.slots["area"]["value"] == area_kitchen.id
|
||||
assert result.response.intent.slots["area"]["text"] == area_kitchen.normalized_name
|
||||
|
||||
# Bedroom has no exposed entities
|
||||
result = await conversation.async_converse(
|
||||
@@ -195,7 +201,8 @@ async def test_unexposed_entities_skipped(
|
||||
entity_registry: er.EntityRegistry,
|
||||
) -> None:
|
||||
"""Test that unexposed entities are skipped in exposed areas."""
|
||||
area_kitchen = area_registry.async_get_or_create("kitchen")
|
||||
area_kitchen = area_registry.async_get_or_create("kitchen_id")
|
||||
area_kitchen = area_registry.async_update(area_kitchen.id, name="kitchen")
|
||||
|
||||
# Both lights are in the kitchen
|
||||
exposed_light = entity_registry.async_get_or_create("light", "demo", "1234")
|
||||
@@ -224,6 +231,9 @@ async def test_unexposed_entities_skipped(
|
||||
|
||||
assert len(calls) == 1
|
||||
assert result.response.response_type == intent.IntentResponseType.ACTION_DONE
|
||||
assert result.response.intent is not None
|
||||
assert result.response.intent.slots["area"]["value"] == area_kitchen.id
|
||||
assert result.response.intent.slots["area"]["text"] == area_kitchen.normalized_name
|
||||
|
||||
# Only one light should be returned
|
||||
hass.states.async_set(exposed_light.entity_id, "on")
|
||||
@@ -314,8 +324,10 @@ async def test_device_area_context(
|
||||
turn_on_calls = async_mock_service(hass, "light", "turn_on")
|
||||
turn_off_calls = async_mock_service(hass, "light", "turn_off")
|
||||
|
||||
area_kitchen = area_registry.async_get_or_create("Kitchen")
|
||||
area_bedroom = area_registry.async_get_or_create("Bedroom")
|
||||
area_kitchen = area_registry.async_get_or_create("kitchen_id")
|
||||
area_kitchen = area_registry.async_update(area_kitchen.id, name="kitchen")
|
||||
area_bedroom = area_registry.async_get_or_create("bedroom_id")
|
||||
area_bedroom = area_registry.async_update(area_bedroom.id, name="bedroom")
|
||||
|
||||
# Create 2 lights in each area
|
||||
area_lights = defaultdict(list)
|
||||
@@ -363,13 +375,14 @@ async def test_device_area_context(
|
||||
assert result.response.response_type == intent.IntentResponseType.ACTION_DONE
|
||||
assert result.response.intent is not None
|
||||
assert result.response.intent.slots["area"]["value"] == area_kitchen.id
|
||||
assert result.response.intent.slots["area"]["text"] == area_kitchen.normalized_name
|
||||
|
||||
# Verify only kitchen lights were targeted
|
||||
assert {s.entity_id for s in result.response.matched_states} == {
|
||||
e.entity_id for e in area_lights["kitchen"]
|
||||
e.entity_id for e in area_lights[area_kitchen.id]
|
||||
}
|
||||
assert {c.data["entity_id"][0] for c in turn_on_calls} == {
|
||||
e.entity_id for e in area_lights["kitchen"]
|
||||
e.entity_id for e in area_lights[area_kitchen.id]
|
||||
}
|
||||
turn_on_calls.clear()
|
||||
|
||||
@@ -386,13 +399,14 @@ async def test_device_area_context(
|
||||
assert result.response.response_type == intent.IntentResponseType.ACTION_DONE
|
||||
assert result.response.intent is not None
|
||||
assert result.response.intent.slots["area"]["value"] == area_bedroom.id
|
||||
assert result.response.intent.slots["area"]["text"] == area_bedroom.normalized_name
|
||||
|
||||
# Verify only bedroom lights were targeted
|
||||
assert {s.entity_id for s in result.response.matched_states} == {
|
||||
e.entity_id for e in area_lights["bedroom"]
|
||||
e.entity_id for e in area_lights[area_bedroom.id]
|
||||
}
|
||||
assert {c.data["entity_id"][0] for c in turn_on_calls} == {
|
||||
e.entity_id for e in area_lights["bedroom"]
|
||||
e.entity_id for e in area_lights[area_bedroom.id]
|
||||
}
|
||||
turn_on_calls.clear()
|
||||
|
||||
@@ -409,13 +423,14 @@ async def test_device_area_context(
|
||||
assert result.response.response_type == intent.IntentResponseType.ACTION_DONE
|
||||
assert result.response.intent is not None
|
||||
assert result.response.intent.slots["area"]["value"] == area_bedroom.id
|
||||
assert result.response.intent.slots["area"]["text"] == area_bedroom.normalized_name
|
||||
|
||||
# Verify only bedroom lights were targeted
|
||||
assert {s.entity_id for s in result.response.matched_states} == {
|
||||
e.entity_id for e in area_lights["bedroom"]
|
||||
e.entity_id for e in area_lights[area_bedroom.id]
|
||||
}
|
||||
assert {c.data["entity_id"][0] for c in turn_off_calls} == {
|
||||
e.entity_id for e in area_lights["bedroom"]
|
||||
e.entity_id for e in area_lights[area_bedroom.id]
|
||||
}
|
||||
turn_off_calls.clear()
|
||||
|
||||
@@ -463,7 +478,8 @@ async def test_error_no_device_in_area(
|
||||
hass: HomeAssistant, init_components, area_registry: ar.AreaRegistry
|
||||
) -> None:
|
||||
"""Test error message when area is missing a device/entity."""
|
||||
area_registry.async_get_or_create("kitchen")
|
||||
area_kitchen = area_registry.async_get_or_create("kitchen_id")
|
||||
area_kitchen = area_registry.async_update(area_kitchen.id, name="kitchen")
|
||||
result = await conversation.async_converse(
|
||||
hass, "turn on missing entity in the kitchen", None, Context(), None
|
||||
)
|
||||
@@ -482,7 +498,7 @@ async def test_error_no_domain(
|
||||
"""Test error message when no devices/entities exist for a domain."""
|
||||
|
||||
# We don't have a sentence for turning on all fans
|
||||
fan_domain = MatchEntity(name="domain", value="fan", text="")
|
||||
fan_domain = MatchEntity(name="domain", value="fan", text="fans")
|
||||
recognize_result = RecognizeResult(
|
||||
intent=Intent("HassTurnOn"),
|
||||
intent_data=IntentData([]),
|
||||
@@ -513,7 +529,8 @@ async def test_error_no_domain_in_area(
|
||||
hass: HomeAssistant, init_components, area_registry: ar.AreaRegistry
|
||||
) -> None:
|
||||
"""Test error message when no devices/entities for a domain exist in an area."""
|
||||
area_registry.async_get_or_create("kitchen")
|
||||
area_kitchen = area_registry.async_get_or_create("kitchen_id")
|
||||
area_kitchen = area_registry.async_update(area_kitchen.id, name="kitchen")
|
||||
result = await conversation.async_converse(
|
||||
hass, "turn on the lights in the kitchen", None, Context(), None
|
||||
)
|
||||
@@ -526,13 +543,11 @@ async def test_error_no_domain_in_area(
|
||||
)
|
||||
|
||||
|
||||
async def test_error_no_device_class(
|
||||
hass: HomeAssistant, init_components, area_registry: ar.AreaRegistry
|
||||
) -> None:
|
||||
async def test_error_no_device_class(hass: HomeAssistant, init_components) -> None:
|
||||
"""Test error message when no entities of a device class exist."""
|
||||
|
||||
# We don't have a sentence for opening all windows
|
||||
window_class = MatchEntity(name="device_class", value="window", text="")
|
||||
window_class = MatchEntity(name="device_class", value="window", text="windows")
|
||||
recognize_result = RecognizeResult(
|
||||
intent=Intent("HassTurnOn"),
|
||||
intent_data=IntentData([]),
|
||||
@@ -563,7 +578,8 @@ async def test_error_no_device_class_in_area(
|
||||
hass: HomeAssistant, init_components, area_registry: ar.AreaRegistry
|
||||
) -> None:
|
||||
"""Test error message when no entities of a device class exist in an area."""
|
||||
area_registry.async_get_or_create("bedroom")
|
||||
area_bedroom = area_registry.async_get_or_create("bedroom_id")
|
||||
area_bedroom = area_registry.async_update(area_bedroom.id, name="bedroom")
|
||||
result = await conversation.async_converse(
|
||||
hass, "open bedroom windows", None, Context(), None
|
||||
)
|
||||
@@ -600,7 +616,8 @@ async def test_no_states_matched_default_error(
|
||||
hass: HomeAssistant, init_components, area_registry: ar.AreaRegistry
|
||||
) -> None:
|
||||
"""Test default response when no states match and slots are missing."""
|
||||
area_registry.async_get_or_create("kitchen")
|
||||
area_kitchen = area_registry.async_get_or_create("kitchen_id")
|
||||
area_kitchen = area_registry.async_update(area_kitchen.id, name="kitchen")
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.conversation.default_agent.intent.async_handle",
|
||||
@@ -629,9 +646,9 @@ async def test_empty_aliases(
|
||||
entity_registry: er.EntityRegistry,
|
||||
) -> None:
|
||||
"""Test that empty aliases are not added to slot lists."""
|
||||
area_kitchen = area_registry.async_get_or_create("kitchen")
|
||||
assert area_kitchen.id is not None
|
||||
area_registry.async_update(area_kitchen.id, aliases={" "})
|
||||
area_kitchen = area_registry.async_get_or_create("kitchen_id")
|
||||
area_kitchen = area_registry.async_update(area_kitchen.id, name="kitchen")
|
||||
area_kitchen = area_registry.async_update(area_kitchen.id, aliases={" "})
|
||||
|
||||
entry = MockConfigEntry()
|
||||
entry.add_to_hass(hass)
|
||||
@@ -643,11 +660,16 @@ async def test_empty_aliases(
|
||||
device_registry.async_update_device(kitchen_device.id, area_id=area_kitchen.id)
|
||||
|
||||
kitchen_light = entity_registry.async_get_or_create("light", "demo", "1234")
|
||||
entity_registry.async_update_entity(
|
||||
kitchen_light.entity_id, device_id=kitchen_device.id, aliases={" "}
|
||||
kitchen_light = entity_registry.async_update_entity(
|
||||
kitchen_light.entity_id,
|
||||
device_id=kitchen_device.id,
|
||||
name="kitchen light",
|
||||
aliases={" "},
|
||||
)
|
||||
hass.states.async_set(
|
||||
kitchen_light.entity_id, "on", attributes={ATTR_FRIENDLY_NAME: "kitchen light"}
|
||||
kitchen_light.entity_id,
|
||||
"on",
|
||||
attributes={ATTR_FRIENDLY_NAME: kitchen_light.name},
|
||||
)
|
||||
|
||||
with patch(
|
||||
@@ -665,16 +687,16 @@ async def test_empty_aliases(
|
||||
assert slot_lists.keys() == {"area", "name"}
|
||||
areas = slot_lists["area"]
|
||||
assert len(areas.values) == 1
|
||||
assert areas.values[0].value_out == "kitchen"
|
||||
assert areas.values[0].value_out == area_kitchen.id
|
||||
assert areas.values[0].text_in.text == area_kitchen.normalized_name
|
||||
|
||||
names = slot_lists["name"]
|
||||
assert len(names.values) == 1
|
||||
assert names.values[0].value_out == "kitchen light"
|
||||
assert names.values[0].value_out == kitchen_light.entity_id
|
||||
assert names.values[0].text_in.text == kitchen_light.name
|
||||
|
||||
|
||||
async def test_all_domains_loaded(
|
||||
hass: HomeAssistant, init_components, area_registry: ar.AreaRegistry
|
||||
) -> None:
|
||||
async def test_all_domains_loaded(hass: HomeAssistant, init_components) -> None:
|
||||
"""Test that sentences for all domains are always loaded."""
|
||||
|
||||
# light domain is not loaded
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user