mirror of
https://github.com/home-assistant/core.git
synced 2026-05-04 03:51:12 +02:00
Compare commits
51 Commits
2024.12.0b6
...
2024.12.2
| Author | SHA1 | Date | |
|---|---|---|---|
| 3fe2c14a79 | |||
| 238cf691a4 | |||
| 5a5bb139fa | |||
| 01a9a58327 | |||
| fc34c6181c | |||
| 60e8a38ba3 | |||
| e4765c40fe | |||
| e239871566 | |||
| c8e5a6df5d | |||
| cac4eef795 | |||
| 8fc50c776e | |||
| da344a44e5 | |||
| 1993142e44 | |||
| 382d32c7a7 | |||
| ef89563bad | |||
| 26012ac922 | |||
| a33c69a2a2 | |||
| 0096ffb659 | |||
| db141ce449 | |||
| af5f718a71 | |||
| f1284178ed | |||
| b0005cedff | |||
| 5d01f7db85 | |||
| d6a4a7f052 | |||
| 1f6c5b4d8b | |||
| 4e56f9c014 | |||
| f343dce418 | |||
| cf53a9743f | |||
| 4884891b2c | |||
| 30504fc9bd | |||
| 8827454dbd | |||
| 3b30bbb85e | |||
| df9eb482b5 | |||
| 32aee61441 | |||
| 35873cbe27 | |||
| 6fe492a51c | |||
| b1bc35f1c3 | |||
| 56d10a0a7a | |||
| d091936ac6 | |||
| 1dfd4e80b9 | |||
| d919de6734 | |||
| 3f9f0f8ac2 | |||
| bf20ffae96 | |||
| dad81927cb | |||
| 92392ab3d4 | |||
| a47e5398f0 | |||
| cf6d33635b | |||
| 6a4031a383 | |||
| 2b40844171 | |||
| 9b90df74a6 | |||
| dcdf033fa9 |
@@ -25,5 +25,5 @@
|
||||
"integration_type": "device",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["aioacaia"],
|
||||
"requirements": ["aioacaia==0.1.10"]
|
||||
"requirements": ["aioacaia==0.1.11"]
|
||||
}
|
||||
|
||||
@@ -28,5 +28,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/august",
|
||||
"iot_class": "cloud_push",
|
||||
"loggers": ["pubnub", "yalexs"],
|
||||
"requirements": ["yalexs==8.10.0", "yalexs-ble==2.5.1"]
|
||||
"requirements": ["yalexs==8.10.0", "yalexs-ble==2.5.2"]
|
||||
}
|
||||
|
||||
@@ -8,6 +8,6 @@
|
||||
"integration_type": "system",
|
||||
"iot_class": "cloud_push",
|
||||
"loggers": ["hass_nabucasa"],
|
||||
"requirements": ["hass-nabucasa==0.85.0"],
|
||||
"requirements": ["hass-nabucasa==0.86.0"],
|
||||
"single_config_entry": true
|
||||
}
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/conversation",
|
||||
"integration_type": "system",
|
||||
"quality_scale": "internal",
|
||||
"requirements": ["hassil==2.0.5", "home-assistant-intents==2024.12.2"]
|
||||
"requirements": ["hassil==2.0.5", "home-assistant-intents==2024.12.9"]
|
||||
}
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/cups",
|
||||
"iot_class": "local_polling",
|
||||
"quality_scale": "legacy",
|
||||
"requirements": ["pycups==1.9.73"]
|
||||
"requirements": ["pycups==2.0.4"]
|
||||
}
|
||||
|
||||
@@ -4,8 +4,7 @@ from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from pydeako.deako import Deako, DeviceListTimeout, FindDevicesTimeout
|
||||
from pydeako.discover import DeakoDiscoverer
|
||||
from pydeako import Deako, DeakoDiscoverer, FindDevicesError
|
||||
|
||||
from homeassistant.components import zeroconf
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
@@ -30,12 +29,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: DeakoConfigEntry) -> boo
|
||||
await connection.connect()
|
||||
try:
|
||||
await connection.find_devices()
|
||||
except DeviceListTimeout as exc: # device list never received
|
||||
_LOGGER.warning("Device not responding to device list")
|
||||
await connection.disconnect()
|
||||
raise ConfigEntryNotReady(exc) from exc
|
||||
except FindDevicesTimeout as exc: # total devices expected not received
|
||||
_LOGGER.warning("Device not responding to device requests")
|
||||
except FindDevicesError as exc:
|
||||
_LOGGER.warning("Error finding devices: %s", exc)
|
||||
await connection.disconnect()
|
||||
raise ConfigEntryNotReady(exc) from exc
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Config flow for deako."""
|
||||
|
||||
from pydeako.discover import DeakoDiscoverer, DevicesNotFoundException
|
||||
from pydeako import DeakoDiscoverer, DevicesNotFoundException
|
||||
|
||||
from homeassistant.components import zeroconf
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from typing import Any
|
||||
|
||||
from pydeako.deako import Deako
|
||||
from pydeako import Deako
|
||||
|
||||
from homeassistant.components.light import ATTR_BRIGHTNESS, ColorMode, LightEntity
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/deako",
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["pydeako"],
|
||||
"requirements": ["pydeako==0.5.4"],
|
||||
"requirements": ["pydeako==0.6.0"],
|
||||
"single_config_entry": true,
|
||||
"zeroconf": ["_deako._tcp.local."]
|
||||
}
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
"integration_type": "service",
|
||||
"iot_class": "local_push",
|
||||
"quality_scale": "internal",
|
||||
"requirements": ["debugpy==1.8.6"]
|
||||
"requirements": ["debugpy==1.8.8"]
|
||||
}
|
||||
|
||||
@@ -99,8 +99,8 @@ class EcovacsController:
|
||||
for device_config in devices.not_supported:
|
||||
_LOGGER.warning(
|
||||
(
|
||||
'Device "%s" not supported. Please add support for it to '
|
||||
"https://github.com/DeebotUniverse/client.py: %s"
|
||||
'Device "%s" not supported. More information at '
|
||||
"https://github.com/DeebotUniverse/client.py/issues/612: %s"
|
||||
),
|
||||
device_config["deviceName"],
|
||||
device_config,
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/ecovacs",
|
||||
"iot_class": "cloud_push",
|
||||
"loggers": ["sleekxmppfs", "sucks", "deebot_client"],
|
||||
"requirements": ["py-sucks==0.9.10", "deebot-client==9.1.0"]
|
||||
"requirements": ["py-sucks==0.9.10", "deebot-client==9.3.0"]
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ def check_local_version_supported(api_version: str | None) -> bool:
|
||||
class DirectPanel(PanelEntry):
|
||||
"""Helper class for wrapping a directly accessed Elmax Panel."""
|
||||
|
||||
def __init__(self, panel_uri):
|
||||
def __init__(self, panel_uri) -> None:
|
||||
"""Construct the object."""
|
||||
super().__init__(panel_uri, True, {})
|
||||
|
||||
|
||||
@@ -203,7 +203,7 @@ class ElmaxConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
|
||||
async def async_step_direct(self, user_input: dict[str, Any]) -> ConfigFlowResult:
|
||||
"""Handle the direct setup step."""
|
||||
self._selected_mode = CONF_ELMAX_MODE_CLOUD
|
||||
self._selected_mode = CONF_ELMAX_MODE_DIRECT
|
||||
if user_input is None:
|
||||
return self.async_show_form(
|
||||
step_id=CONF_ELMAX_MODE_DIRECT,
|
||||
|
||||
@@ -121,13 +121,13 @@ class ElmaxCover(ElmaxEntity, CoverEntity):
|
||||
else:
|
||||
_LOGGER.debug("Ignoring stop request as the cover is IDLE")
|
||||
|
||||
async def async_open_cover(self, **kwargs):
|
||||
async def async_open_cover(self, **kwargs: Any) -> None:
|
||||
"""Open the cover."""
|
||||
await self.coordinator.http_client.execute_command(
|
||||
endpoint_id=self._device.endpoint_id, command=CoverCommand.UP
|
||||
)
|
||||
|
||||
async def async_close_cover(self, **kwargs):
|
||||
async def async_close_cover(self, **kwargs: Any) -> None:
|
||||
"""Close the cover."""
|
||||
await self.coordinator.http_client.execute_command(
|
||||
endpoint_id=self._device.endpoint_id, command=CoverCommand.DOWN
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/elmax",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["elmax_api"],
|
||||
"requirements": ["elmax-api==0.0.6.1"],
|
||||
"requirements": ["elmax-api==0.0.6.3"],
|
||||
"zeroconf": [
|
||||
{
|
||||
"type": "_elmax-ssl._tcp.local."
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
"loggers": ["aioesphomeapi", "noiseprotocol", "bleak_esphome"],
|
||||
"mqtt": ["esphome/discover/#"],
|
||||
"requirements": [
|
||||
"aioesphomeapi==27.0.3",
|
||||
"aioesphomeapi==28.0.0",
|
||||
"esphome-dashboard-api==1.2.3",
|
||||
"bleak-esphome==1.1.0"
|
||||
],
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/ezviz",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["paho_mqtt", "pyezviz"],
|
||||
"requirements": ["pyezviz==0.2.2.3"]
|
||||
"requirements": ["pyezviz==0.2.1.2"]
|
||||
}
|
||||
|
||||
@@ -20,5 +20,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/frontend",
|
||||
"integration_type": "system",
|
||||
"quality_scale": "internal",
|
||||
"requirements": ["home-assistant-frontend==20241127.4"]
|
||||
"requirements": ["home-assistant-frontend==20241127.7"]
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import date, datetime, timedelta
|
||||
from datetime import UTC, date, datetime, timedelta
|
||||
from typing import Any, cast
|
||||
|
||||
from homeassistant.components.todo import (
|
||||
@@ -39,8 +39,10 @@ def _convert_todo_item(item: TodoItem) -> dict[str, str | None]:
|
||||
else:
|
||||
result["status"] = TodoItemStatus.NEEDS_ACTION
|
||||
if (due := item.due) is not None:
|
||||
# due API field is a timestamp string, but with only date resolution
|
||||
result["due"] = dt_util.start_of_local_day(due).isoformat()
|
||||
# due API field is a timestamp string, but with only date resolution.
|
||||
# The time portion of the date is always discarded by the API, so we
|
||||
# always set to UTC.
|
||||
result["due"] = dt_util.start_of_local_day(due).replace(tzinfo=UTC).isoformat()
|
||||
else:
|
||||
result["due"] = None
|
||||
result["notes"] = item.description
|
||||
@@ -51,6 +53,8 @@ def _convert_api_item(item: dict[str, str]) -> TodoItem:
|
||||
"""Convert tasks API items into a TodoItem."""
|
||||
due: date | None = None
|
||||
if (due_str := item.get("due")) is not None:
|
||||
# Due dates are returned always in UTC so we only need to
|
||||
# parse the date portion which will be interpreted as a a local date.
|
||||
due = datetime.fromisoformat(due_str).date()
|
||||
return TodoItem(
|
||||
summary=item["title"],
|
||||
|
||||
@@ -4,6 +4,7 @@ from __future__ import annotations
|
||||
|
||||
from collections.abc import Mapping
|
||||
from pathlib import Path
|
||||
import sys
|
||||
from typing import Final
|
||||
|
||||
from aiohttp.hdrs import CACHE_CONTROL, CONTENT_TYPE
|
||||
@@ -17,6 +18,15 @@ CACHE_HEADER = f"public, max-age={CACHE_TIME}"
|
||||
CACHE_HEADERS: Mapping[str, str] = {CACHE_CONTROL: CACHE_HEADER}
|
||||
RESPONSE_CACHE: LRU[tuple[str, Path], tuple[Path, str]] = LRU(512)
|
||||
|
||||
if sys.version_info >= (3, 13):
|
||||
# guess_type is soft-deprecated in 3.13
|
||||
# for paths and should only be used for
|
||||
# URLs. guess_file_type should be used
|
||||
# for paths instead.
|
||||
_GUESSER = CONTENT_TYPES.guess_file_type
|
||||
else:
|
||||
_GUESSER = CONTENT_TYPES.guess_type
|
||||
|
||||
|
||||
class CachingStaticResource(StaticResource):
|
||||
"""Static Resource handler that will add cache headers."""
|
||||
@@ -37,9 +47,7 @@ class CachingStaticResource(StaticResource):
|
||||
# Must be directory index; ignore caching
|
||||
return response
|
||||
file_path = response._path # noqa: SLF001
|
||||
response.content_type = (
|
||||
CONTENT_TYPES.guess_type(file_path)[0] or FALLBACK_CONTENT_TYPE
|
||||
)
|
||||
response.content_type = _GUESSER(file_path)[0] or FALLBACK_CONTENT_TYPE
|
||||
# Cache actual header after setter construction.
|
||||
content_type = response.headers[CONTENT_TYPE]
|
||||
RESPONSE_CACHE[key] = (file_path, content_type)
|
||||
|
||||
@@ -53,10 +53,10 @@ class HusqvarnaConfigFlowHandler(
|
||||
tz = await dt_util.async_get_time_zone(str(dt_util.DEFAULT_TIME_ZONE))
|
||||
automower_api = AutomowerSession(AsyncConfigFlowAuth(websession, token), tz)
|
||||
try:
|
||||
data = await automower_api.get_status()
|
||||
status_data = await automower_api.get_status()
|
||||
except Exception: # noqa: BLE001
|
||||
return self.async_abort(reason="unknown")
|
||||
if data == {}:
|
||||
if status_data == {}:
|
||||
return self.async_abort(reason="no_mower_connected")
|
||||
|
||||
structured_token = structure_token(token[CONF_ACCESS_TOKEN])
|
||||
|
||||
@@ -7,7 +7,7 @@ from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed
|
||||
|
||||
from .const import DOMAIN
|
||||
from .const import APP_ID, DOMAIN
|
||||
from .coordinator import (
|
||||
HydrawiseMainDataUpdateCoordinator,
|
||||
HydrawiseUpdateCoordinators,
|
||||
@@ -30,7 +30,8 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
|
||||
raise ConfigEntryAuthFailed
|
||||
|
||||
hydrawise = client.Hydrawise(
|
||||
auth.Auth(config_entry.data[CONF_USERNAME], config_entry.data[CONF_PASSWORD])
|
||||
auth.Auth(config_entry.data[CONF_USERNAME], config_entry.data[CONF_PASSWORD]),
|
||||
app_id=APP_ID,
|
||||
)
|
||||
|
||||
main_coordinator = HydrawiseMainDataUpdateCoordinator(hass, hydrawise)
|
||||
|
||||
@@ -6,14 +6,14 @@ from collections.abc import Callable, Mapping
|
||||
from typing import Any
|
||||
|
||||
from aiohttp import ClientError
|
||||
from pydrawise import auth, client
|
||||
from pydrawise import auth as pydrawise_auth, client
|
||||
from pydrawise.exceptions import NotAuthorizedError
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import SOURCE_REAUTH, ConfigFlow, ConfigFlowResult
|
||||
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
|
||||
|
||||
from .const import DOMAIN, LOGGER
|
||||
from .const import APP_ID, DOMAIN, LOGGER
|
||||
|
||||
|
||||
class HydrawiseConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
@@ -29,16 +29,21 @@ class HydrawiseConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
on_failure: Callable[[str], ConfigFlowResult],
|
||||
) -> ConfigFlowResult:
|
||||
"""Create the config entry."""
|
||||
|
||||
# Verify that the provided credentials work."""
|
||||
api = client.Hydrawise(auth.Auth(username, password))
|
||||
auth = pydrawise_auth.Auth(username, password)
|
||||
try:
|
||||
# Don't fetch zones because we don't need them yet.
|
||||
user = await api.get_user(fetch_zones=False)
|
||||
await auth.token()
|
||||
except NotAuthorizedError:
|
||||
return on_failure("invalid_auth")
|
||||
except TimeoutError:
|
||||
return on_failure("timeout_connect")
|
||||
|
||||
try:
|
||||
api = client.Hydrawise(auth, app_id=APP_ID)
|
||||
# Don't fetch zones because we don't need them yet.
|
||||
user = await api.get_user(fetch_zones=False)
|
||||
except TimeoutError:
|
||||
return on_failure("timeout_connect")
|
||||
except ClientError as ex:
|
||||
LOGGER.error("Unable to connect to Hydrawise cloud service: %s", ex)
|
||||
return on_failure("cannot_connect")
|
||||
|
||||
@@ -3,8 +3,12 @@
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
|
||||
from homeassistant.const import __version__ as HA_VERSION
|
||||
|
||||
LOGGER = logging.getLogger(__package__)
|
||||
|
||||
APP_ID = f"homeassistant-{HA_VERSION}"
|
||||
|
||||
DOMAIN = "hydrawise"
|
||||
DEFAULT_WATERING_TIME = timedelta(minutes=15)
|
||||
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/hydrawise",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["pydrawise"],
|
||||
"requirements": ["pydrawise==2024.9.0"]
|
||||
"requirements": ["pydrawise==2024.12.0"]
|
||||
}
|
||||
|
||||
@@ -8,6 +8,6 @@
|
||||
"iot_class": "calculated",
|
||||
"loggers": ["yt_dlp"],
|
||||
"quality_scale": "internal",
|
||||
"requirements": ["yt-dlp[default]==2024.12.03"],
|
||||
"requirements": ["yt-dlp[default]==2024.12.06"],
|
||||
"single_config_entry": true
|
||||
}
|
||||
|
||||
@@ -95,11 +95,17 @@ PARAMETER_ID_TO_EXCLUDE_F730 = (
|
||||
)
|
||||
|
||||
PARAMETER_ID_TO_INCLUDE_SMO20 = (
|
||||
"40013",
|
||||
"40033",
|
||||
"40940",
|
||||
"44069",
|
||||
"44071",
|
||||
"44073",
|
||||
"47011",
|
||||
"47015",
|
||||
"47028",
|
||||
"47032",
|
||||
"47398",
|
||||
"50004",
|
||||
)
|
||||
|
||||
|
||||
@@ -27,7 +27,9 @@ from .entity import NordpoolBaseEntity
|
||||
PARALLEL_UPDATES = 0
|
||||
|
||||
|
||||
def get_prices(data: DeliveryPeriodData) -> dict[str, tuple[float, float, float]]:
|
||||
def get_prices(
|
||||
data: DeliveryPeriodData,
|
||||
) -> dict[str, tuple[float | None, float, float | None]]:
|
||||
"""Return previous, current and next prices.
|
||||
|
||||
Output: {"SE3": (10.0, 10.5, 12.1)}
|
||||
@@ -39,6 +41,7 @@ def get_prices(data: DeliveryPeriodData) -> dict[str, tuple[float, float, float]
|
||||
previous_time = current_time - timedelta(hours=1)
|
||||
next_time = current_time + timedelta(hours=1)
|
||||
price_data = data.entries
|
||||
LOGGER.debug("Price data: %s", price_data)
|
||||
for entry in price_data:
|
||||
if entry.start <= current_time <= entry.end:
|
||||
current_price_entries = entry.entry
|
||||
@@ -46,10 +49,20 @@ def get_prices(data: DeliveryPeriodData) -> dict[str, tuple[float, float, float]
|
||||
last_price_entries = entry.entry
|
||||
if entry.start <= next_time <= entry.end:
|
||||
next_price_entries = entry.entry
|
||||
LOGGER.debug(
|
||||
"Last price %s, current price %s, next price %s",
|
||||
last_price_entries,
|
||||
current_price_entries,
|
||||
next_price_entries,
|
||||
)
|
||||
|
||||
result = {}
|
||||
for area, price in current_price_entries.items():
|
||||
result[area] = (last_price_entries[area], price, next_price_entries[area])
|
||||
result[area] = (
|
||||
last_price_entries.get(area),
|
||||
price,
|
||||
next_price_entries.get(area),
|
||||
)
|
||||
LOGGER.debug("Prices: %s", result)
|
||||
return result
|
||||
|
||||
@@ -90,7 +103,7 @@ class NordpoolDefaultSensorEntityDescription(SensorEntityDescription):
|
||||
class NordpoolPricesSensorEntityDescription(SensorEntityDescription):
|
||||
"""Describes Nord Pool prices sensor entity."""
|
||||
|
||||
value_fn: Callable[[tuple[float, float, float]], float | None]
|
||||
value_fn: Callable[[tuple[float | None, float, float | None]], float | None]
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
@@ -136,13 +149,13 @@ PRICES_SENSOR_TYPES: tuple[NordpoolPricesSensorEntityDescription, ...] = (
|
||||
NordpoolPricesSensorEntityDescription(
|
||||
key="last_price",
|
||||
translation_key="last_price",
|
||||
value_fn=lambda data: data[0] / 1000,
|
||||
value_fn=lambda data: data[0] / 1000 if data[0] else None,
|
||||
suggested_display_precision=2,
|
||||
),
|
||||
NordpoolPricesSensorEntityDescription(
|
||||
key="next_price",
|
||||
translation_key="next_price",
|
||||
value_fn=lambda data: data[2] / 1000,
|
||||
value_fn=lambda data: data[2] / 1000 if data[2] else None,
|
||||
suggested_display_precision=2,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -480,7 +480,13 @@ DEVICE_CLASS_UNITS: dict[NumberDeviceClass, set[type[StrEnum] | str | None]] = {
|
||||
NumberDeviceClass.PM10: {CONCENTRATION_MICROGRAMS_PER_CUBIC_METER},
|
||||
NumberDeviceClass.PM25: {CONCENTRATION_MICROGRAMS_PER_CUBIC_METER},
|
||||
NumberDeviceClass.POWER_FACTOR: {PERCENTAGE, None},
|
||||
NumberDeviceClass.POWER: {UnitOfPower.WATT, UnitOfPower.KILO_WATT},
|
||||
NumberDeviceClass.POWER: {
|
||||
UnitOfPower.WATT,
|
||||
UnitOfPower.KILO_WATT,
|
||||
UnitOfPower.MEGA_WATT,
|
||||
UnitOfPower.GIGA_WATT,
|
||||
UnitOfPower.TERA_WATT,
|
||||
},
|
||||
NumberDeviceClass.PRECIPITATION: set(UnitOfPrecipitationDepth),
|
||||
NumberDeviceClass.PRECIPITATION_INTENSITY: set(UnitOfVolumetricFlux),
|
||||
NumberDeviceClass.PRESSURE: set(UnitOfPressure),
|
||||
|
||||
@@ -191,17 +191,8 @@ class PlugwiseClimateEntity(PlugwiseEntity, ClimateEntity):
|
||||
self._previous_action_mode(self.coordinator)
|
||||
|
||||
# Adam provides the hvac_action for each thermostat
|
||||
if self._gateway["smile_name"] == "Adam":
|
||||
if (control_state := self.device.get("control_state")) == "cooling":
|
||||
return HVACAction.COOLING
|
||||
if control_state == "heating":
|
||||
return HVACAction.HEATING
|
||||
if control_state == "preheating":
|
||||
return HVACAction.PREHEATING
|
||||
if control_state == "off":
|
||||
return HVACAction.IDLE
|
||||
|
||||
return HVACAction.IDLE
|
||||
if (action := self.device.get("control_state")) is not None:
|
||||
return HVACAction(action)
|
||||
|
||||
# Anna
|
||||
heater: str = self._gateway["heater_id"]
|
||||
|
||||
@@ -7,6 +7,6 @@
|
||||
"integration_type": "hub",
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["plugwise"],
|
||||
"requirements": ["plugwise==1.6.0"],
|
||||
"requirements": ["plugwise==1.6.3"],
|
||||
"zeroconf": ["_plugwise._tcp.local."]
|
||||
}
|
||||
|
||||
@@ -18,5 +18,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/reolink",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["reolink_aio"],
|
||||
"requirements": ["reolink-aio==0.11.4"]
|
||||
"requirements": ["reolink-aio==0.11.5"]
|
||||
}
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/risco",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["pyrisco"],
|
||||
"requirements": ["pyrisco==0.6.4"]
|
||||
"requirements": ["pyrisco==0.6.5"]
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
"requirements": [
|
||||
"getmac==0.9.4",
|
||||
"samsungctl[websocket]==0.7.1",
|
||||
"samsungtvws[async,encrypted]==2.7.1",
|
||||
"samsungtvws[async,encrypted]==2.7.2",
|
||||
"wakeonlan==2.1.0",
|
||||
"async-upnp-client==0.41.0"
|
||||
],
|
||||
|
||||
@@ -579,7 +579,13 @@ DEVICE_CLASS_UNITS: dict[SensorDeviceClass, set[type[StrEnum] | str | None]] = {
|
||||
SensorDeviceClass.PM10: {CONCENTRATION_MICROGRAMS_PER_CUBIC_METER},
|
||||
SensorDeviceClass.PM25: {CONCENTRATION_MICROGRAMS_PER_CUBIC_METER},
|
||||
SensorDeviceClass.POWER_FACTOR: {PERCENTAGE, None},
|
||||
SensorDeviceClass.POWER: {UnitOfPower.WATT, UnitOfPower.KILO_WATT},
|
||||
SensorDeviceClass.POWER: {
|
||||
UnitOfPower.WATT,
|
||||
UnitOfPower.KILO_WATT,
|
||||
UnitOfPower.MEGA_WATT,
|
||||
UnitOfPower.GIGA_WATT,
|
||||
UnitOfPower.TERA_WATT,
|
||||
},
|
||||
SensorDeviceClass.PRECIPITATION: set(UnitOfPrecipitationDepth),
|
||||
SensorDeviceClass.PRECIPITATION_INTENSITY: set(UnitOfVolumetricFlux),
|
||||
SensorDeviceClass.PRESSURE: set(UnitOfPressure),
|
||||
|
||||
@@ -79,6 +79,8 @@ class SwitchBotCloudAirConditioner(SwitchBotCloudEntity, ClimateEntity):
|
||||
_attr_hvac_mode = HVACMode.FAN_ONLY
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_attr_target_temperature = 21
|
||||
_attr_target_temperature_step = 1
|
||||
_attr_precision = 1
|
||||
_attr_name = None
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
@@ -97,7 +99,7 @@ class SwitchBotCloudAirConditioner(SwitchBotCloudEntity, ClimateEntity):
|
||||
)
|
||||
await self.send_api_command(
|
||||
AirConditionerCommands.SET_ALL,
|
||||
parameters=f"{new_temperature},{new_mode},{new_fan_speed},on",
|
||||
parameters=f"{int(new_temperature)},{new_mode},{new_fan_speed},on",
|
||||
)
|
||||
|
||||
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
|
||||
|
||||
@@ -14,5 +14,5 @@
|
||||
},
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["PyTado"],
|
||||
"requirements": ["python-tado==0.17.7"]
|
||||
"requirements": ["python-tado==0.17.6"]
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ SCOPES = [
|
||||
Scope.OPENID,
|
||||
Scope.OFFLINE_ACCESS,
|
||||
Scope.VEHICLE_DEVICE_DATA,
|
||||
Scope.VEHICLE_LOCATION,
|
||||
Scope.VEHICLE_CMDS,
|
||||
Scope.VEHICLE_CHARGING_CMDS,
|
||||
Scope.ENERGY_DEVICE_DATA,
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/tesla_fleet",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["tesla-fleet-api"],
|
||||
"requirements": ["tesla-fleet-api==0.8.4"]
|
||||
"requirements": ["tesla-fleet-api==0.8.5"]
|
||||
}
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/teslemetry",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["tesla-fleet-api"],
|
||||
"requirements": ["tesla-fleet-api==0.8.4", "teslemetry-stream==0.4.2"]
|
||||
"requirements": ["tesla-fleet-api==0.8.5", "teslemetry-stream==0.4.2"]
|
||||
}
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/tessie",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["tessie", "tesla-fleet-api"],
|
||||
"requirements": ["tessie-api==0.1.1", "tesla-fleet-api==0.8.4"]
|
||||
"requirements": ["tessie-api==0.1.1", "tesla-fleet-api==0.8.5"]
|
||||
}
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/totalconnect",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["total_connect_client"],
|
||||
"requirements": ["total-connect-client==2024.5"]
|
||||
"requirements": ["total-connect-client==2024.12"]
|
||||
}
|
||||
|
||||
@@ -300,5 +300,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/tplink",
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["kasa"],
|
||||
"requirements": ["python-kasa[speedups]==0.8.0"]
|
||||
"requirements": ["python-kasa[speedups]==0.8.1"]
|
||||
}
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
"integration_type": "service",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["twentemilieu"],
|
||||
"requirements": ["twentemilieu==2.1.0"]
|
||||
"requirements": ["twentemilieu==2.2.0"]
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"integration_type": "hub",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["aiounifi"],
|
||||
"requirements": ["aiounifi==80"],
|
||||
"requirements": ["aiounifi==81"],
|
||||
"ssdp": [
|
||||
{
|
||||
"manufacturer": "Ubiquiti Networks",
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/upb",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["upb_lib"],
|
||||
"requirements": ["upb-lib==0.5.8"]
|
||||
"requirements": ["upb-lib==0.5.9"]
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
"""Support for tracking consumption over given periods of time."""
|
||||
|
||||
from datetime import timedelta
|
||||
from datetime import datetime, timedelta
|
||||
import logging
|
||||
|
||||
from croniter import croniter
|
||||
from cronsim import CronSim, CronSimError
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.select import DOMAIN as SELECT_DOMAIN
|
||||
@@ -47,9 +47,12 @@ DEFAULT_OFFSET = timedelta(hours=0)
|
||||
|
||||
def validate_cron_pattern(pattern):
|
||||
"""Check that the pattern is well-formed."""
|
||||
if croniter.is_valid(pattern):
|
||||
return pattern
|
||||
raise vol.Invalid("Invalid pattern")
|
||||
try:
|
||||
CronSim(pattern, datetime(2020, 1, 1)) # any date will do
|
||||
except CronSimError as err:
|
||||
_LOGGER.error("Invalid cron pattern %s: %s", pattern, err)
|
||||
raise vol.Invalid("Invalid pattern") from err
|
||||
return pattern
|
||||
|
||||
|
||||
def period_or_cron(config):
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/utility_meter",
|
||||
"integration_type": "helper",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["croniter"],
|
||||
"quality_scale": "internal",
|
||||
"requirements": ["cronsim==2.6"]
|
||||
}
|
||||
|
||||
@@ -13,5 +13,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/yale",
|
||||
"iot_class": "cloud_push",
|
||||
"loggers": ["socketio", "engineio", "yalexs"],
|
||||
"requirements": ["yalexs==8.10.0", "yalexs-ble==2.5.1"]
|
||||
"requirements": ["yalexs==8.10.0", "yalexs-ble==2.5.2"]
|
||||
}
|
||||
|
||||
@@ -12,5 +12,5 @@
|
||||
"dependencies": ["bluetooth_adapters"],
|
||||
"documentation": "https://www.home-assistant.io/integrations/yalexs_ble",
|
||||
"iot_class": "local_push",
|
||||
"requirements": ["yalexs-ble==2.5.1"]
|
||||
"requirements": ["yalexs-ble==2.5.2"]
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"zha",
|
||||
"universal_silabs_flasher"
|
||||
],
|
||||
"requirements": ["universal-silabs-flasher==0.0.25", "zha==0.0.41"],
|
||||
"requirements": ["universal-silabs-flasher==0.0.25", "zha==0.0.42"],
|
||||
"usb": [
|
||||
{
|
||||
"vid": "10C4",
|
||||
|
||||
@@ -25,7 +25,7 @@ if TYPE_CHECKING:
|
||||
APPLICATION_NAME: Final = "HomeAssistant"
|
||||
MAJOR_VERSION: Final = 2024
|
||||
MINOR_VERSION: Final = 12
|
||||
PATCH_VERSION: Final = "0b6"
|
||||
PATCH_VERSION: Final = "2"
|
||||
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
||||
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
|
||||
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 12, 0)
|
||||
|
||||
@@ -71,7 +71,10 @@ async def async_get_system_info(hass: HomeAssistant) -> dict[str, Any]:
|
||||
|
||||
try:
|
||||
info_object["user"] = cached_get_user()
|
||||
except KeyError:
|
||||
except (KeyError, OSError):
|
||||
# OSError on python >= 3.13, KeyError on python < 3.13
|
||||
# KeyError can be removed when 3.12 support is dropped
|
||||
# see https://docs.python.org/3/whatsnew/3.13.html
|
||||
info_object["user"] = None
|
||||
|
||||
if platform.system() == "Darwin":
|
||||
|
||||
@@ -5,7 +5,7 @@ aiodiscover==2.1.0
|
||||
aiodns==3.2.0
|
||||
aiohasupervisor==0.2.1
|
||||
aiohttp-fast-zlib==0.2.0
|
||||
aiohttp==3.11.9
|
||||
aiohttp==3.11.10
|
||||
aiohttp_cors==0.7.0
|
||||
aiozoneinfo==0.2.1
|
||||
astral==2.2
|
||||
@@ -31,11 +31,11 @@ fnv-hash-fast==1.0.2
|
||||
go2rtc-client==0.1.1
|
||||
ha-ffmpeg==3.2.2
|
||||
habluetooth==3.6.0
|
||||
hass-nabucasa==0.85.0
|
||||
hass-nabucasa==0.86.0
|
||||
hassil==2.0.5
|
||||
home-assistant-bluetooth==1.13.0
|
||||
home-assistant-frontend==20241127.4
|
||||
home-assistant-intents==2024.12.2
|
||||
home-assistant-frontend==20241127.7
|
||||
home-assistant-intents==2024.12.9
|
||||
httpx==0.27.2
|
||||
ifaddr==0.2.0
|
||||
Jinja2==3.1.4
|
||||
|
||||
+3
-3
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "homeassistant"
|
||||
version = "2024.12.0b6"
|
||||
version = "2024.12.2"
|
||||
license = {text = "Apache-2.0"}
|
||||
description = "Open-source home automation platform running on Python 3."
|
||||
readme = "README.rst"
|
||||
@@ -29,7 +29,7 @@ dependencies = [
|
||||
# change behavior based on presence of supervisor. Deprecated with #127228
|
||||
# Lib can be removed with 2025.11
|
||||
"aiohasupervisor==0.2.1",
|
||||
"aiohttp==3.11.9",
|
||||
"aiohttp==3.11.10",
|
||||
"aiohttp_cors==0.7.0",
|
||||
"aiohttp-fast-zlib==0.2.0",
|
||||
"aiozoneinfo==0.2.1",
|
||||
@@ -45,7 +45,7 @@ dependencies = [
|
||||
"fnv-hash-fast==1.0.2",
|
||||
# hass-nabucasa is imported by helpers which don't depend on the cloud
|
||||
# integration
|
||||
"hass-nabucasa==0.85.0",
|
||||
"hass-nabucasa==0.86.0",
|
||||
# When bumping httpx, please check the version pins of
|
||||
# httpcore, anyio, and h11 in gen_requirements_all
|
||||
"httpx==0.27.2",
|
||||
|
||||
+2
-2
@@ -5,7 +5,7 @@
|
||||
# Home Assistant Core
|
||||
aiodns==3.2.0
|
||||
aiohasupervisor==0.2.1
|
||||
aiohttp==3.11.9
|
||||
aiohttp==3.11.10
|
||||
aiohttp_cors==0.7.0
|
||||
aiohttp-fast-zlib==0.2.0
|
||||
aiozoneinfo==0.2.1
|
||||
@@ -19,7 +19,7 @@ bcrypt==4.2.0
|
||||
certifi>=2021.5.30
|
||||
ciso8601==2.3.1
|
||||
fnv-hash-fast==1.0.2
|
||||
hass-nabucasa==0.85.0
|
||||
hass-nabucasa==0.86.0
|
||||
httpx==0.27.2
|
||||
home-assistant-bluetooth==1.13.0
|
||||
ifaddr==0.2.0
|
||||
|
||||
+26
-26
@@ -173,7 +173,7 @@ aio-geojson-usgs-earthquakes==0.3
|
||||
aio-georss-gdacs==0.10
|
||||
|
||||
# homeassistant.components.acaia
|
||||
aioacaia==0.1.10
|
||||
aioacaia==0.1.11
|
||||
|
||||
# homeassistant.components.airq
|
||||
aioairq==0.4.3
|
||||
@@ -243,7 +243,7 @@ aioelectricitymaps==0.4.0
|
||||
aioemonitor==1.0.5
|
||||
|
||||
# homeassistant.components.esphome
|
||||
aioesphomeapi==27.0.3
|
||||
aioesphomeapi==28.0.0
|
||||
|
||||
# homeassistant.components.flo
|
||||
aioflo==2021.11.0
|
||||
@@ -402,7 +402,7 @@ aiotedee==0.2.20
|
||||
aiotractive==0.6.0
|
||||
|
||||
# homeassistant.components.unifi
|
||||
aiounifi==80
|
||||
aiounifi==81
|
||||
|
||||
# homeassistant.components.vlc_telnet
|
||||
aiovlc==0.5.1
|
||||
@@ -729,7 +729,7 @@ datapoint==0.9.9
|
||||
dbus-fast==2.24.3
|
||||
|
||||
# homeassistant.components.debugpy
|
||||
debugpy==1.8.6
|
||||
debugpy==1.8.8
|
||||
|
||||
# homeassistant.components.decora_wifi
|
||||
# decora-wifi==1.4
|
||||
@@ -738,7 +738,7 @@ debugpy==1.8.6
|
||||
# decora==0.6
|
||||
|
||||
# homeassistant.components.ecovacs
|
||||
deebot-client==9.1.0
|
||||
deebot-client==9.3.0
|
||||
|
||||
# homeassistant.components.ihc
|
||||
# homeassistant.components.namecheapdns
|
||||
@@ -824,7 +824,7 @@ eliqonline==1.2.2
|
||||
elkm1-lib==2.2.10
|
||||
|
||||
# homeassistant.components.elmax
|
||||
elmax-api==0.0.6.1
|
||||
elmax-api==0.0.6.3
|
||||
|
||||
# homeassistant.components.elvia
|
||||
elvia==0.1.0
|
||||
@@ -1090,7 +1090,7 @@ habitipy==0.3.3
|
||||
habluetooth==3.6.0
|
||||
|
||||
# homeassistant.components.cloud
|
||||
hass-nabucasa==0.85.0
|
||||
hass-nabucasa==0.86.0
|
||||
|
||||
# homeassistant.components.splunk
|
||||
hass-splunk==0.1.1
|
||||
@@ -1130,10 +1130,10 @@ hole==0.8.0
|
||||
holidays==0.62
|
||||
|
||||
# homeassistant.components.frontend
|
||||
home-assistant-frontend==20241127.4
|
||||
home-assistant-frontend==20241127.7
|
||||
|
||||
# homeassistant.components.conversation
|
||||
home-assistant-intents==2024.12.2
|
||||
home-assistant-intents==2024.12.9
|
||||
|
||||
# homeassistant.components.home_connect
|
||||
homeconnect==0.8.0
|
||||
@@ -1622,7 +1622,7 @@ plexauth==0.0.6
|
||||
plexwebsocket==0.0.14
|
||||
|
||||
# homeassistant.components.plugwise
|
||||
plugwise==1.6.0
|
||||
plugwise==1.6.3
|
||||
|
||||
# homeassistant.components.plum_lightpad
|
||||
plumlightpad==0.0.11
|
||||
@@ -1832,7 +1832,7 @@ pycountry==24.6.1
|
||||
pycsspeechtts==1.0.8
|
||||
|
||||
# homeassistant.components.cups
|
||||
# pycups==1.9.73
|
||||
# pycups==2.0.4
|
||||
|
||||
# homeassistant.components.daikin
|
||||
pydaikin==2.13.7
|
||||
@@ -1841,7 +1841,7 @@ pydaikin==2.13.7
|
||||
pydanfossair==0.1.0
|
||||
|
||||
# homeassistant.components.deako
|
||||
pydeako==0.5.4
|
||||
pydeako==0.6.0
|
||||
|
||||
# homeassistant.components.deconz
|
||||
pydeconz==118
|
||||
@@ -1859,7 +1859,7 @@ pydiscovergy==3.0.2
|
||||
pydoods==1.0.2
|
||||
|
||||
# homeassistant.components.hydrawise
|
||||
pydrawise==2024.9.0
|
||||
pydrawise==2024.12.0
|
||||
|
||||
# homeassistant.components.android_ip_webcam
|
||||
pydroid-ipcam==2.0.0
|
||||
@@ -1907,7 +1907,7 @@ pyeverlights==0.1.0
|
||||
pyevilgenius==2.0.0
|
||||
|
||||
# homeassistant.components.ezviz
|
||||
pyezviz==0.2.2.3
|
||||
pyezviz==0.2.1.2
|
||||
|
||||
# homeassistant.components.fibaro
|
||||
pyfibaro==0.8.0
|
||||
@@ -2203,7 +2203,7 @@ pyrecswitch==1.0.2
|
||||
pyrepetierng==0.1.0
|
||||
|
||||
# homeassistant.components.risco
|
||||
pyrisco==0.6.4
|
||||
pyrisco==0.6.5
|
||||
|
||||
# homeassistant.components.rituals_perfume_genie
|
||||
pyrituals==0.0.6
|
||||
@@ -2362,7 +2362,7 @@ python-join-api==0.0.9
|
||||
python-juicenet==1.1.0
|
||||
|
||||
# homeassistant.components.tplink
|
||||
python-kasa[speedups]==0.8.0
|
||||
python-kasa[speedups]==0.8.1
|
||||
|
||||
# homeassistant.components.linkplay
|
||||
python-linkplay==0.0.20
|
||||
@@ -2411,7 +2411,7 @@ python-smarttub==0.0.38
|
||||
python-songpal==0.16.2
|
||||
|
||||
# homeassistant.components.tado
|
||||
python-tado==0.17.7
|
||||
python-tado==0.17.6
|
||||
|
||||
# homeassistant.components.technove
|
||||
python-technove==1.3.1
|
||||
@@ -2556,7 +2556,7 @@ renault-api==0.2.7
|
||||
renson-endura-delta==1.7.1
|
||||
|
||||
# homeassistant.components.reolink
|
||||
reolink-aio==0.11.4
|
||||
reolink-aio==0.11.5
|
||||
|
||||
# homeassistant.components.idteck_prox
|
||||
rfk101py==0.0.1
|
||||
@@ -2610,7 +2610,7 @@ rxv==0.7.0
|
||||
samsungctl[websocket]==0.7.1
|
||||
|
||||
# homeassistant.components.samsungtv
|
||||
samsungtvws[async,encrypted]==2.7.1
|
||||
samsungtvws[async,encrypted]==2.7.2
|
||||
|
||||
# homeassistant.components.sanix
|
||||
sanix==1.0.6
|
||||
@@ -2810,7 +2810,7 @@ temperusb==1.6.1
|
||||
# homeassistant.components.tesla_fleet
|
||||
# homeassistant.components.teslemetry
|
||||
# homeassistant.components.tessie
|
||||
tesla-fleet-api==0.8.4
|
||||
tesla-fleet-api==0.8.5
|
||||
|
||||
# homeassistant.components.powerwall
|
||||
tesla-powerwall==0.5.2
|
||||
@@ -2858,7 +2858,7 @@ tololib==1.1.0
|
||||
toonapi==0.3.0
|
||||
|
||||
# homeassistant.components.totalconnect
|
||||
total-connect-client==2024.5
|
||||
total-connect-client==2024.12
|
||||
|
||||
# homeassistant.components.tplink_lte
|
||||
tp-connected==0.0.4
|
||||
@@ -2882,7 +2882,7 @@ ttn_client==1.2.0
|
||||
tuya-device-sharing-sdk==0.2.1
|
||||
|
||||
# homeassistant.components.twentemilieu
|
||||
twentemilieu==2.1.0
|
||||
twentemilieu==2.2.0
|
||||
|
||||
# homeassistant.components.twilio
|
||||
twilio==6.32.0
|
||||
@@ -2915,7 +2915,7 @@ unifiled==0.11
|
||||
universal-silabs-flasher==0.0.25
|
||||
|
||||
# homeassistant.components.upb
|
||||
upb-lib==0.5.8
|
||||
upb-lib==0.5.9
|
||||
|
||||
# homeassistant.components.upcloud
|
||||
upcloud-api==2.6.0
|
||||
@@ -3044,7 +3044,7 @@ yalesmartalarmclient==0.4.3
|
||||
# homeassistant.components.august
|
||||
# homeassistant.components.yale
|
||||
# homeassistant.components.yalexs_ble
|
||||
yalexs-ble==2.5.1
|
||||
yalexs-ble==2.5.2
|
||||
|
||||
# homeassistant.components.august
|
||||
# homeassistant.components.yale
|
||||
@@ -3066,7 +3066,7 @@ youless-api==2.1.2
|
||||
youtubeaio==1.1.5
|
||||
|
||||
# homeassistant.components.media_extractor
|
||||
yt-dlp[default]==2024.12.03
|
||||
yt-dlp[default]==2024.12.06
|
||||
|
||||
# homeassistant.components.zamg
|
||||
zamg==0.3.6
|
||||
@@ -3081,7 +3081,7 @@ zeroconf==0.136.2
|
||||
zeversolar==0.3.2
|
||||
|
||||
# homeassistant.components.zha
|
||||
zha==0.0.41
|
||||
zha==0.0.42
|
||||
|
||||
# homeassistant.components.zhong_hong
|
||||
zhong-hong-hvac==1.0.13
|
||||
|
||||
+25
-25
@@ -161,7 +161,7 @@ aio-geojson-usgs-earthquakes==0.3
|
||||
aio-georss-gdacs==0.10
|
||||
|
||||
# homeassistant.components.acaia
|
||||
aioacaia==0.1.10
|
||||
aioacaia==0.1.11
|
||||
|
||||
# homeassistant.components.airq
|
||||
aioairq==0.4.3
|
||||
@@ -231,7 +231,7 @@ aioelectricitymaps==0.4.0
|
||||
aioemonitor==1.0.5
|
||||
|
||||
# homeassistant.components.esphome
|
||||
aioesphomeapi==27.0.3
|
||||
aioesphomeapi==28.0.0
|
||||
|
||||
# homeassistant.components.flo
|
||||
aioflo==2021.11.0
|
||||
@@ -384,7 +384,7 @@ aiotedee==0.2.20
|
||||
aiotractive==0.6.0
|
||||
|
||||
# homeassistant.components.unifi
|
||||
aiounifi==80
|
||||
aiounifi==81
|
||||
|
||||
# homeassistant.components.vlc_telnet
|
||||
aiovlc==0.5.1
|
||||
@@ -625,10 +625,10 @@ datapoint==0.9.9
|
||||
dbus-fast==2.24.3
|
||||
|
||||
# homeassistant.components.debugpy
|
||||
debugpy==1.8.6
|
||||
debugpy==1.8.8
|
||||
|
||||
# homeassistant.components.ecovacs
|
||||
deebot-client==9.1.0
|
||||
deebot-client==9.3.0
|
||||
|
||||
# homeassistant.components.ihc
|
||||
# homeassistant.components.namecheapdns
|
||||
@@ -699,7 +699,7 @@ elgato==5.1.2
|
||||
elkm1-lib==2.2.10
|
||||
|
||||
# homeassistant.components.elmax
|
||||
elmax-api==0.0.6.1
|
||||
elmax-api==0.0.6.3
|
||||
|
||||
# homeassistant.components.elvia
|
||||
elvia==0.1.0
|
||||
@@ -928,7 +928,7 @@ habitipy==0.3.3
|
||||
habluetooth==3.6.0
|
||||
|
||||
# homeassistant.components.cloud
|
||||
hass-nabucasa==0.85.0
|
||||
hass-nabucasa==0.86.0
|
||||
|
||||
# homeassistant.components.conversation
|
||||
hassil==2.0.5
|
||||
@@ -956,10 +956,10 @@ hole==0.8.0
|
||||
holidays==0.62
|
||||
|
||||
# homeassistant.components.frontend
|
||||
home-assistant-frontend==20241127.4
|
||||
home-assistant-frontend==20241127.7
|
||||
|
||||
# homeassistant.components.conversation
|
||||
home-assistant-intents==2024.12.2
|
||||
home-assistant-intents==2024.12.9
|
||||
|
||||
# homeassistant.components.home_connect
|
||||
homeconnect==0.8.0
|
||||
@@ -1329,7 +1329,7 @@ plexauth==0.0.6
|
||||
plexwebsocket==0.0.14
|
||||
|
||||
# homeassistant.components.plugwise
|
||||
plugwise==1.6.0
|
||||
plugwise==1.6.3
|
||||
|
||||
# homeassistant.components.plum_lightpad
|
||||
plumlightpad==0.0.11
|
||||
@@ -1488,7 +1488,7 @@ pycsspeechtts==1.0.8
|
||||
pydaikin==2.13.7
|
||||
|
||||
# homeassistant.components.deako
|
||||
pydeako==0.5.4
|
||||
pydeako==0.6.0
|
||||
|
||||
# homeassistant.components.deconz
|
||||
pydeconz==118
|
||||
@@ -1500,7 +1500,7 @@ pydexcom==0.2.3
|
||||
pydiscovergy==3.0.2
|
||||
|
||||
# homeassistant.components.hydrawise
|
||||
pydrawise==2024.9.0
|
||||
pydrawise==2024.12.0
|
||||
|
||||
# homeassistant.components.android_ip_webcam
|
||||
pydroid-ipcam==2.0.0
|
||||
@@ -1536,7 +1536,7 @@ pyeverlights==0.1.0
|
||||
pyevilgenius==2.0.0
|
||||
|
||||
# homeassistant.components.ezviz
|
||||
pyezviz==0.2.2.3
|
||||
pyezviz==0.2.1.2
|
||||
|
||||
# homeassistant.components.fibaro
|
||||
pyfibaro==0.8.0
|
||||
@@ -1775,7 +1775,7 @@ pyqwikswitch==0.93
|
||||
pyrainbird==6.0.1
|
||||
|
||||
# homeassistant.components.risco
|
||||
pyrisco==0.6.4
|
||||
pyrisco==0.6.5
|
||||
|
||||
# homeassistant.components.rituals_perfume_genie
|
||||
pyrituals==0.0.6
|
||||
@@ -1889,7 +1889,7 @@ python-izone==1.2.9
|
||||
python-juicenet==1.1.0
|
||||
|
||||
# homeassistant.components.tplink
|
||||
python-kasa[speedups]==0.8.0
|
||||
python-kasa[speedups]==0.8.1
|
||||
|
||||
# homeassistant.components.linkplay
|
||||
python-linkplay==0.0.20
|
||||
@@ -1932,7 +1932,7 @@ python-smarttub==0.0.38
|
||||
python-songpal==0.16.2
|
||||
|
||||
# homeassistant.components.tado
|
||||
python-tado==0.17.7
|
||||
python-tado==0.17.6
|
||||
|
||||
# homeassistant.components.technove
|
||||
python-technove==1.3.1
|
||||
@@ -2047,7 +2047,7 @@ renault-api==0.2.7
|
||||
renson-endura-delta==1.7.1
|
||||
|
||||
# homeassistant.components.reolink
|
||||
reolink-aio==0.11.4
|
||||
reolink-aio==0.11.5
|
||||
|
||||
# homeassistant.components.rflink
|
||||
rflink==0.0.66
|
||||
@@ -2086,7 +2086,7 @@ rxv==0.7.0
|
||||
samsungctl[websocket]==0.7.1
|
||||
|
||||
# homeassistant.components.samsungtv
|
||||
samsungtvws[async,encrypted]==2.7.1
|
||||
samsungtvws[async,encrypted]==2.7.2
|
||||
|
||||
# homeassistant.components.sanix
|
||||
sanix==1.0.6
|
||||
@@ -2238,7 +2238,7 @@ temperusb==1.6.1
|
||||
# homeassistant.components.tesla_fleet
|
||||
# homeassistant.components.teslemetry
|
||||
# homeassistant.components.tessie
|
||||
tesla-fleet-api==0.8.4
|
||||
tesla-fleet-api==0.8.5
|
||||
|
||||
# homeassistant.components.powerwall
|
||||
tesla-powerwall==0.5.2
|
||||
@@ -2274,7 +2274,7 @@ tololib==1.1.0
|
||||
toonapi==0.3.0
|
||||
|
||||
# homeassistant.components.totalconnect
|
||||
total-connect-client==2024.5
|
||||
total-connect-client==2024.12
|
||||
|
||||
# homeassistant.components.tplink_omada
|
||||
tplink-omada-client==1.4.3
|
||||
@@ -2295,7 +2295,7 @@ ttn_client==1.2.0
|
||||
tuya-device-sharing-sdk==0.2.1
|
||||
|
||||
# homeassistant.components.twentemilieu
|
||||
twentemilieu==2.1.0
|
||||
twentemilieu==2.2.0
|
||||
|
||||
# homeassistant.components.twilio
|
||||
twilio==6.32.0
|
||||
@@ -2322,7 +2322,7 @@ unifi-discovery==1.2.0
|
||||
universal-silabs-flasher==0.0.25
|
||||
|
||||
# homeassistant.components.upb
|
||||
upb-lib==0.5.8
|
||||
upb-lib==0.5.9
|
||||
|
||||
# homeassistant.components.upcloud
|
||||
upcloud-api==2.6.0
|
||||
@@ -2433,7 +2433,7 @@ yalesmartalarmclient==0.4.3
|
||||
# homeassistant.components.august
|
||||
# homeassistant.components.yale
|
||||
# homeassistant.components.yalexs_ble
|
||||
yalexs-ble==2.5.1
|
||||
yalexs-ble==2.5.2
|
||||
|
||||
# homeassistant.components.august
|
||||
# homeassistant.components.yale
|
||||
@@ -2452,7 +2452,7 @@ youless-api==2.1.2
|
||||
youtubeaio==1.1.5
|
||||
|
||||
# homeassistant.components.media_extractor
|
||||
yt-dlp[default]==2024.12.03
|
||||
yt-dlp[default]==2024.12.06
|
||||
|
||||
# homeassistant.components.zamg
|
||||
zamg==0.3.6
|
||||
@@ -2464,7 +2464,7 @@ zeroconf==0.136.2
|
||||
zeversolar==0.3.2
|
||||
|
||||
# homeassistant.components.zha
|
||||
zha==0.0.41
|
||||
zha==0.0.42
|
||||
|
||||
# homeassistant.components.zwave_js
|
||||
zwave-js-server-python==0.59.1
|
||||
|
||||
@@ -23,7 +23,7 @@ RUN --mount=from=ghcr.io/astral-sh/uv:0.5.4,source=/uv,target=/bin/uv \
|
||||
-c /usr/src/homeassistant/homeassistant/package_constraints.txt \
|
||||
-r /usr/src/homeassistant/requirements.txt \
|
||||
stdlib-list==0.10.0 pipdeptree==2.23.4 tqdm==4.66.5 ruff==0.8.0 \
|
||||
PyTurboJPEG==1.7.5 go2rtc-client==0.1.1 ha-ffmpeg==3.2.2 hassil==2.0.5 home-assistant-intents==2024.12.2 mutagen==1.47.0 pymicro-vad==1.0.1 pyspeex-noise==1.0.2
|
||||
PyTurboJPEG==1.7.5 go2rtc-client==0.1.1 ha-ffmpeg==3.2.2 hassil==2.0.5 home-assistant-intents==2024.12.9 mutagen==1.47.0 pymicro-vad==1.0.1 pyspeex-noise==1.0.2
|
||||
|
||||
LABEL "name"="hassfest"
|
||||
LABEL "maintainer"="Home Assistant <hello@home-assistant.io>"
|
||||
|
||||
@@ -571,7 +571,7 @@
|
||||
'name': 'HassGetState',
|
||||
}),
|
||||
'match': True,
|
||||
'sentence_template': '[tell me] how many {on_off_domains:domain} (is|are) {on_off_states:state} [in <area>]',
|
||||
'sentence_template': '[tell me] how many {on_off_domains:domain} (is|are) {on_off_states:state} [<in_area_floor>]',
|
||||
'slots': dict({
|
||||
'area': 'kitchen',
|
||||
'domain': 'lights',
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from pydeako.deako import DeviceListTimeout, FindDevicesTimeout
|
||||
from pydeako import FindDevicesError
|
||||
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.core import HomeAssistant
|
||||
@@ -37,7 +37,7 @@ async def test_deako_async_setup_entry(
|
||||
assert mock_config_entry.runtime_data == pydeako_deako_mock.return_value
|
||||
|
||||
|
||||
async def test_deako_async_setup_entry_device_list_timeout(
|
||||
async def test_deako_async_setup_entry_devices_error(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
pydeako_deako_mock: MagicMock,
|
||||
@@ -47,32 +47,7 @@ async def test_deako_async_setup_entry_device_list_timeout(
|
||||
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
|
||||
pydeako_deako_mock.return_value.find_devices.side_effect = DeviceListTimeout()
|
||||
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
pydeako_deako_mock.assert_called_once_with(
|
||||
pydeako_discoverer_mock.return_value.get_address
|
||||
)
|
||||
pydeako_deako_mock.return_value.connect.assert_called_once()
|
||||
pydeako_deako_mock.return_value.find_devices.assert_called_once()
|
||||
pydeako_deako_mock.return_value.disconnect.assert_called_once()
|
||||
|
||||
assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY
|
||||
|
||||
|
||||
async def test_deako_async_setup_entry_find_devices_timeout(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
pydeako_deako_mock: MagicMock,
|
||||
pydeako_discoverer_mock: MagicMock,
|
||||
) -> None:
|
||||
"""Test async_setup_entry raises ConfigEntryNotReady when pydeako raises FindDevicesTimeout."""
|
||||
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
|
||||
pydeako_deako_mock.return_value.find_devices.side_effect = FindDevicesTimeout()
|
||||
pydeako_deako_mock.return_value.find_devices.side_effect = FindDevicesError()
|
||||
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Configuration for Elmax tests."""
|
||||
|
||||
from collections.abc import Generator
|
||||
from datetime import datetime, timedelta
|
||||
import json
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
@@ -11,6 +12,7 @@ from elmax_api.constants import (
|
||||
ENDPOINT_LOGIN,
|
||||
)
|
||||
from httpx import Response
|
||||
import jwt
|
||||
import pytest
|
||||
import respx
|
||||
|
||||
@@ -64,9 +66,20 @@ def httpx_mock_direct_fixture() -> Generator[respx.MockRouter]:
|
||||
) as respx_mock:
|
||||
# Mock Login POST.
|
||||
login_route = respx_mock.post(f"/api/v2/{ENDPOINT_LOGIN}", name="login")
|
||||
login_route.return_value = Response(
|
||||
200, json=json.loads(load_fixture("direct/login.json", "elmax"))
|
||||
|
||||
login_json = json.loads(load_fixture("direct/login.json", "elmax"))
|
||||
decoded_jwt = jwt.decode_complete(
|
||||
login_json["token"].split(" ")[1],
|
||||
algorithms="HS256",
|
||||
options={"verify_signature": False},
|
||||
)
|
||||
expiration = datetime.now() + timedelta(hours=1)
|
||||
decoded_jwt["payload"]["exp"] = int(expiration.timestamp())
|
||||
jws_string = jwt.encode(
|
||||
payload=decoded_jwt["payload"], algorithm="HS256", key=""
|
||||
)
|
||||
login_json["token"] = f"JWT {jws_string}"
|
||||
login_route.return_value = Response(200, json=login_json)
|
||||
|
||||
# Mock Device list GET.
|
||||
list_devices_route = respx_mock.get(
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
)
|
||||
# ---
|
||||
# name: test_create_todo_list_item[due].1
|
||||
'{"title": "Soda", "status": "needsAction", "due": "2023-11-18T00:00:00-08:00", "notes": null}'
|
||||
'{"title": "Soda", "status": "needsAction", "due": "2023-11-18T00:00:00+00:00", "notes": null}'
|
||||
# ---
|
||||
# name: test_create_todo_list_item[summary]
|
||||
tuple(
|
||||
@@ -137,7 +137,7 @@
|
||||
)
|
||||
# ---
|
||||
# name: test_partial_update[due_date].1
|
||||
'{"title": "Water", "status": "needsAction", "due": "2023-11-18T00:00:00-08:00", "notes": null}'
|
||||
'{"title": "Water", "status": "needsAction", "due": "2023-11-18T00:00:00+00:00", "notes": null}'
|
||||
# ---
|
||||
# name: test_partial_update[empty_description]
|
||||
tuple(
|
||||
@@ -166,6 +166,33 @@
|
||||
# name: test_partial_update_status[api_responses0].1
|
||||
'{"title": "Water", "status": "needsAction", "due": null, "notes": null}'
|
||||
# ---
|
||||
# name: test_update_due_date[api_responses0-America/Regina]
|
||||
tuple(
|
||||
'https://tasks.googleapis.com/tasks/v1/lists/task-list-id-1/tasks/some-task-id?alt=json',
|
||||
'PATCH',
|
||||
)
|
||||
# ---
|
||||
# name: test_update_due_date[api_responses0-America/Regina].1
|
||||
'{"title": "Water", "status": "needsAction", "due": "2024-12-05T00:00:00+00:00", "notes": null}'
|
||||
# ---
|
||||
# name: test_update_due_date[api_responses0-Asia/Tokyo]
|
||||
tuple(
|
||||
'https://tasks.googleapis.com/tasks/v1/lists/task-list-id-1/tasks/some-task-id?alt=json',
|
||||
'PATCH',
|
||||
)
|
||||
# ---
|
||||
# name: test_update_due_date[api_responses0-Asia/Tokyo].1
|
||||
'{"title": "Water", "status": "needsAction", "due": "2024-12-05T00:00:00+00:00", "notes": null}'
|
||||
# ---
|
||||
# name: test_update_due_date[api_responses0-UTC]
|
||||
tuple(
|
||||
'https://tasks.googleapis.com/tasks/v1/lists/task-list-id-1/tasks/some-task-id?alt=json',
|
||||
'PATCH',
|
||||
)
|
||||
# ---
|
||||
# name: test_update_due_date[api_responses0-UTC].1
|
||||
'{"title": "Water", "status": "needsAction", "due": "2024-12-05T00:00:00+00:00", "notes": null}'
|
||||
# ---
|
||||
# name: test_update_todo_list_item[api_responses0]
|
||||
tuple(
|
||||
'https://tasks.googleapis.com/tasks/v1/lists/task-list-id-1/tasks/some-task-id?alt=json',
|
||||
|
||||
@@ -239,6 +239,7 @@ def mock_http_response(response_handler: list | Callable) -> Mock:
|
||||
yield mock_response
|
||||
|
||||
|
||||
@pytest.mark.parametrize("timezone", ["America/Regina", "UTC", "Asia/Tokyo"])
|
||||
@pytest.mark.parametrize(
|
||||
"api_responses",
|
||||
[
|
||||
@@ -251,7 +252,7 @@ def mock_http_response(response_handler: list | Callable) -> Mock:
|
||||
"title": "Task 1",
|
||||
"status": "needsAction",
|
||||
"position": "0000000000000001",
|
||||
"due": "2023-11-18T00:00:00+00:00",
|
||||
"due": "2023-11-18T00:00:00Z",
|
||||
},
|
||||
{
|
||||
"id": "task-2",
|
||||
@@ -271,8 +272,10 @@ async def test_get_items(
|
||||
integration_setup: Callable[[], Awaitable[bool]],
|
||||
hass_ws_client: WebSocketGenerator,
|
||||
ws_get_items: Callable[[], Awaitable[dict[str, str]]],
|
||||
timezone: str,
|
||||
) -> None:
|
||||
"""Test getting todo list items."""
|
||||
await hass.config.async_set_time_zone(timezone)
|
||||
|
||||
assert await integration_setup()
|
||||
|
||||
@@ -484,6 +487,39 @@ async def test_update_todo_list_item(
|
||||
assert call.kwargs.get("body") == snapshot
|
||||
|
||||
|
||||
@pytest.mark.parametrize("timezone", ["America/Regina", "UTC", "Asia/Tokyo"])
|
||||
@pytest.mark.parametrize("api_responses", [UPDATE_API_RESPONSES])
|
||||
async def test_update_due_date(
|
||||
hass: HomeAssistant,
|
||||
setup_credentials: None,
|
||||
integration_setup: Callable[[], Awaitable[bool]],
|
||||
mock_http_response: Any,
|
||||
snapshot: SnapshotAssertion,
|
||||
timezone: str,
|
||||
) -> None:
|
||||
"""Test for updating the due date of a To-do item and timezone."""
|
||||
await hass.config.async_set_time_zone(timezone)
|
||||
|
||||
assert await integration_setup()
|
||||
|
||||
state = hass.states.get("todo.my_tasks")
|
||||
assert state
|
||||
assert state.state == "1"
|
||||
|
||||
await hass.services.async_call(
|
||||
TODO_DOMAIN,
|
||||
TodoServices.UPDATE_ITEM,
|
||||
{ATTR_ITEM: "some-task-id", ATTR_DUE_DATE: "2024-12-5"},
|
||||
target={ATTR_ENTITY_ID: "todo.my_tasks"},
|
||||
blocking=True,
|
||||
)
|
||||
assert len(mock_http_response.call_args_list) == 4
|
||||
call = mock_http_response.call_args_list[2]
|
||||
assert call
|
||||
assert call.args == snapshot
|
||||
assert call.kwargs.get("body") == snapshot
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"api_responses",
|
||||
[
|
||||
|
||||
@@ -56,7 +56,6 @@ def mock_legacy_pydrawise(
|
||||
|
||||
@pytest.fixture
|
||||
def mock_pydrawise(
|
||||
mock_auth: AsyncMock,
|
||||
user: User,
|
||||
controller: Controller,
|
||||
zones: list[Zone],
|
||||
|
||||
@@ -21,6 +21,7 @@ pytestmark = pytest.mark.usefixtures("mock_setup_entry")
|
||||
async def test_form(
|
||||
hass: HomeAssistant,
|
||||
mock_setup_entry: AsyncMock,
|
||||
mock_auth: AsyncMock,
|
||||
mock_pydrawise: AsyncMock,
|
||||
user: User,
|
||||
) -> None:
|
||||
@@ -46,11 +47,12 @@ async def test_form(
|
||||
CONF_PASSWORD: "__password__",
|
||||
}
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
mock_pydrawise.get_user.assert_called_once_with(fetch_zones=False)
|
||||
mock_auth.token.assert_awaited_once_with()
|
||||
mock_pydrawise.get_user.assert_awaited_once_with(fetch_zones=False)
|
||||
|
||||
|
||||
async def test_form_api_error(
|
||||
hass: HomeAssistant, mock_pydrawise: AsyncMock, user: User
|
||||
hass: HomeAssistant, mock_auth: AsyncMock, mock_pydrawise: AsyncMock, user: User
|
||||
) -> None:
|
||||
"""Test we handle API errors."""
|
||||
mock_pydrawise.get_user.side_effect = ClientError("XXX")
|
||||
@@ -71,8 +73,29 @@ async def test_form_api_error(
|
||||
assert result2["type"] is FlowResultType.CREATE_ENTRY
|
||||
|
||||
|
||||
async def test_form_connect_timeout(
|
||||
hass: HomeAssistant, mock_pydrawise: AsyncMock, user: User
|
||||
async def test_form_auth_connect_timeout(
|
||||
hass: HomeAssistant, mock_auth: AsyncMock, mock_pydrawise: AsyncMock
|
||||
) -> None:
|
||||
"""Test we handle API errors."""
|
||||
mock_auth.token.side_effect = TimeoutError
|
||||
init_result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
data = {CONF_USERNAME: "asdf@asdf.com", CONF_PASSWORD: "__password__"}
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
init_result["flow_id"], data
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["errors"] == {"base": "timeout_connect"}
|
||||
|
||||
mock_auth.token.reset_mock(side_effect=True)
|
||||
result2 = await hass.config_entries.flow.async_configure(result["flow_id"], data)
|
||||
assert result2["type"] is FlowResultType.CREATE_ENTRY
|
||||
|
||||
|
||||
async def test_form_client_connect_timeout(
|
||||
hass: HomeAssistant, mock_auth: AsyncMock, mock_pydrawise: AsyncMock, user: User
|
||||
) -> None:
|
||||
"""Test we handle API errors."""
|
||||
mock_pydrawise.get_user.side_effect = TimeoutError
|
||||
@@ -94,10 +117,10 @@ async def test_form_connect_timeout(
|
||||
|
||||
|
||||
async def test_form_not_authorized_error(
|
||||
hass: HomeAssistant, mock_pydrawise: AsyncMock, user: User
|
||||
hass: HomeAssistant, mock_auth: AsyncMock, mock_pydrawise: AsyncMock
|
||||
) -> None:
|
||||
"""Test we handle API errors."""
|
||||
mock_pydrawise.get_user.side_effect = NotAuthorizedError
|
||||
mock_auth.token.side_effect = NotAuthorizedError
|
||||
|
||||
init_result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
@@ -109,8 +132,7 @@ async def test_form_not_authorized_error(
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["errors"] == {"base": "invalid_auth"}
|
||||
|
||||
mock_pydrawise.get_user.reset_mock(side_effect=True)
|
||||
mock_pydrawise.get_user.return_value = user
|
||||
mock_auth.token.reset_mock(side_effect=True)
|
||||
result2 = await hass.config_entries.flow.async_configure(result["flow_id"], data)
|
||||
assert result2["type"] is FlowResultType.CREATE_ENTRY
|
||||
|
||||
@@ -118,6 +140,7 @@ async def test_form_not_authorized_error(
|
||||
async def test_reauth(
|
||||
hass: HomeAssistant,
|
||||
user: User,
|
||||
mock_auth: AsyncMock,
|
||||
mock_pydrawise: AsyncMock,
|
||||
) -> None:
|
||||
"""Test that re-authorization works."""
|
||||
|
||||
@@ -176,7 +176,7 @@
|
||||
"off"
|
||||
],
|
||||
"climate_mode": "auto",
|
||||
"control_state": "off",
|
||||
"control_state": "idle",
|
||||
"dev_class": "climate",
|
||||
"model": "ThermoZone",
|
||||
"name": "Bathroom",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"06aecb3d00354375924f50c47af36bd2": {
|
||||
"active_preset": "no_frost",
|
||||
"climate_mode": "off",
|
||||
"control_state": "off",
|
||||
"control_state": "idle",
|
||||
"dev_class": "climate",
|
||||
"model": "ThermoZone",
|
||||
"name": "Slaapkamer",
|
||||
@@ -26,7 +26,7 @@
|
||||
"13228dab8ce04617af318a2888b3c548": {
|
||||
"active_preset": "home",
|
||||
"climate_mode": "heat",
|
||||
"control_state": "off",
|
||||
"control_state": "idle",
|
||||
"dev_class": "climate",
|
||||
"model": "ThermoZone",
|
||||
"name": "Woonkamer",
|
||||
@@ -238,7 +238,7 @@
|
||||
"d27aede973b54be484f6842d1b2802ad": {
|
||||
"active_preset": "home",
|
||||
"climate_mode": "heat",
|
||||
"control_state": "off",
|
||||
"control_state": "idle",
|
||||
"dev_class": "climate",
|
||||
"model": "ThermoZone",
|
||||
"name": "Kinderkamer",
|
||||
@@ -285,7 +285,7 @@
|
||||
"d58fec52899f4f1c92e4f8fad6d8c48c": {
|
||||
"active_preset": "home",
|
||||
"climate_mode": "heat",
|
||||
"control_state": "off",
|
||||
"control_state": "idle",
|
||||
"dev_class": "climate",
|
||||
"model": "ThermoZone",
|
||||
"name": "Logeerkamer",
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
"off"
|
||||
],
|
||||
"climate_mode": "auto",
|
||||
"control_state": "idle",
|
||||
"dev_class": "climate",
|
||||
"model": "ThermoZone",
|
||||
"name": "Badkamer",
|
||||
@@ -66,6 +67,7 @@
|
||||
"off"
|
||||
],
|
||||
"climate_mode": "heat",
|
||||
"control_state": "idle",
|
||||
"dev_class": "climate",
|
||||
"model": "ThermoZone",
|
||||
"name": "Bios",
|
||||
@@ -112,6 +114,7 @@
|
||||
"446ac08dd04d4eff8ac57489757b7314": {
|
||||
"active_preset": "no_frost",
|
||||
"climate_mode": "heat",
|
||||
"control_state": "idle",
|
||||
"dev_class": "climate",
|
||||
"model": "ThermoZone",
|
||||
"name": "Garage",
|
||||
@@ -258,6 +261,7 @@
|
||||
"off"
|
||||
],
|
||||
"climate_mode": "auto",
|
||||
"control_state": "idle",
|
||||
"dev_class": "climate",
|
||||
"model": "ThermoZone",
|
||||
"name": "Jessie",
|
||||
@@ -402,6 +406,7 @@
|
||||
"off"
|
||||
],
|
||||
"climate_mode": "auto",
|
||||
"control_state": "heating",
|
||||
"dev_class": "climate",
|
||||
"model": "ThermoZone",
|
||||
"name": "Woonkamer",
|
||||
@@ -577,7 +582,7 @@
|
||||
"cooling_present": false,
|
||||
"gateway_id": "fe799307f1624099878210aa0b9f1475",
|
||||
"heater_id": "90986d591dcd426cae3ec3e8111ff730",
|
||||
"item_count": 364,
|
||||
"item_count": 369,
|
||||
"notifications": {
|
||||
"af82e4ccf9c548528166d38e560662a4": {
|
||||
"warning": "Node Plug (with MAC address 000D6F000D13CB01, in room 'n.a.') has been unreachable since 23:03 2020-01-18. Please check the connection and restart the device."
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
'off',
|
||||
]),
|
||||
'climate_mode': 'auto',
|
||||
'control_state': 'idle',
|
||||
'dev_class': 'climate',
|
||||
'model': 'ThermoZone',
|
||||
'name': 'Badkamer',
|
||||
@@ -75,6 +76,7 @@
|
||||
'off',
|
||||
]),
|
||||
'climate_mode': 'heat',
|
||||
'control_state': 'idle',
|
||||
'dev_class': 'climate',
|
||||
'model': 'ThermoZone',
|
||||
'name': 'Bios',
|
||||
@@ -131,6 +133,7 @@
|
||||
'446ac08dd04d4eff8ac57489757b7314': dict({
|
||||
'active_preset': 'no_frost',
|
||||
'climate_mode': 'heat',
|
||||
'control_state': 'idle',
|
||||
'dev_class': 'climate',
|
||||
'model': 'ThermoZone',
|
||||
'name': 'Garage',
|
||||
@@ -286,6 +289,7 @@
|
||||
'off',
|
||||
]),
|
||||
'climate_mode': 'auto',
|
||||
'control_state': 'idle',
|
||||
'dev_class': 'climate',
|
||||
'model': 'ThermoZone',
|
||||
'name': 'Jessie',
|
||||
@@ -440,6 +444,7 @@
|
||||
'off',
|
||||
]),
|
||||
'climate_mode': 'auto',
|
||||
'control_state': 'heating',
|
||||
'dev_class': 'climate',
|
||||
'model': 'ThermoZone',
|
||||
'name': 'Woonkamer',
|
||||
@@ -625,7 +630,7 @@
|
||||
'cooling_present': False,
|
||||
'gateway_id': 'fe799307f1624099878210aa0b9f1475',
|
||||
'heater_id': '90986d591dcd426cae3ec3e8111ff730',
|
||||
'item_count': 364,
|
||||
'item_count': 369,
|
||||
'notifications': dict({
|
||||
'af82e4ccf9c548528166d38e560662a4': dict({
|
||||
'warning': "Node Plug (with MAC address 000D6F000D13CB01, in room 'n.a.') has been unreachable since 23:03 2020-01-18. Please check the connection and restart the device.",
|
||||
|
||||
@@ -31,15 +31,13 @@ async def test_adam_climate_entity_attributes(
|
||||
state = hass.states.get("climate.woonkamer")
|
||||
assert state
|
||||
assert state.state == HVACMode.AUTO
|
||||
assert state.attributes["hvac_action"] == "heating"
|
||||
assert state.attributes["hvac_modes"] == [HVACMode.AUTO, HVACMode.HEAT]
|
||||
# hvac_action is not asserted as the fixture is not in line with recent firmware functionality
|
||||
|
||||
assert "preset_modes" in state.attributes
|
||||
assert "no_frost" in state.attributes["preset_modes"]
|
||||
assert "home" in state.attributes["preset_modes"]
|
||||
|
||||
assert state.attributes["current_temperature"] == 20.9
|
||||
assert state.attributes["preset_mode"] == "home"
|
||||
assert state.attributes["current_temperature"] == 20.9
|
||||
assert state.attributes["supported_features"] == 17
|
||||
assert state.attributes["temperature"] == 21.5
|
||||
assert state.attributes["min_temp"] == 0.0
|
||||
@@ -49,15 +47,13 @@ async def test_adam_climate_entity_attributes(
|
||||
state = hass.states.get("climate.jessie")
|
||||
assert state
|
||||
assert state.state == HVACMode.AUTO
|
||||
assert state.attributes["hvac_action"] == "idle"
|
||||
assert state.attributes["hvac_modes"] == [HVACMode.AUTO, HVACMode.HEAT]
|
||||
# hvac_action is not asserted as the fixture is not in line with recent firmware functionality
|
||||
|
||||
assert "preset_modes" in state.attributes
|
||||
assert "no_frost" in state.attributes["preset_modes"]
|
||||
assert "home" in state.attributes["preset_modes"]
|
||||
|
||||
assert state.attributes["current_temperature"] == 17.2
|
||||
assert state.attributes["preset_mode"] == "asleep"
|
||||
assert state.attributes["current_temperature"] == 17.2
|
||||
assert state.attributes["temperature"] == 15.0
|
||||
assert state.attributes["min_temp"] == 0.0
|
||||
assert state.attributes["max_temp"] == 35.0
|
||||
|
||||
@@ -165,6 +165,7 @@
|
||||
'openid',
|
||||
'offline_access',
|
||||
'vehicle_device_data',
|
||||
'vehicle_location',
|
||||
'vehicle_cmds',
|
||||
'vehicle_charging_cmds',
|
||||
'energy_device_data',
|
||||
|
||||
@@ -93,10 +93,9 @@ async def test_container_installationtype(hass: HomeAssistant) -> None:
|
||||
assert info["installation_type"] == "Unsupported Third Party Container"
|
||||
|
||||
|
||||
async def test_getuser_keyerror(hass: HomeAssistant) -> None:
|
||||
"""Test getuser keyerror."""
|
||||
with patch(
|
||||
"homeassistant.helpers.system_info.cached_get_user", side_effect=KeyError
|
||||
):
|
||||
@pytest.mark.parametrize("error", [KeyError, OSError])
|
||||
async def test_getuser_oserror(hass: HomeAssistant, error: Exception) -> None:
|
||||
"""Test getuser oserror."""
|
||||
with patch("homeassistant.helpers.system_info.cached_get_user", side_effect=error):
|
||||
info = await async_get_system_info(hass)
|
||||
assert info["user"] is None
|
||||
|
||||
Reference in New Issue
Block a user