Compare commits

...

51 Commits

Author SHA1 Message Date
Franck Nijhof 3fe2c14a79 2024.12.2 (#132846) 2024-12-10 21:45:06 +01:00
Franck Nijhof 238cf691a4 Bump version to 2024.12.2 2024-12-10 15:07:18 +01:00
Josef Zweck 5a5bb139fa Bump aioacaia to 0.1.11 (#132838) 2024-12-10 14:59:48 +01:00
Robert Resch 01a9a58327 Bump deebot-client to 9.3.0 (#132834) 2024-12-10 14:59:45 +01:00
David Knowles fc34c6181c Pass an application identifier to the Hydrawise API (#132779) 2024-12-10 14:59:42 +01:00
David Knowles 60e8a38ba3 Catch Hydrawise authorization errors in the correct place (#132727) 2024-12-10 14:59:37 +01:00
starkillerOG e4765c40fe Bump reolink-aio to 0.11.5 (#132757) 2024-12-10 14:55:48 +01:00
Bram Kragten e239871566 Update frontend to 20241127.7 (#132729)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2024-12-10 14:55:43 +01:00
Michael Hansen c8e5a6df5d Bump intents to 2024.12.9 (#132726) 2024-12-10 14:54:48 +01:00
Simone Rescio cac4eef795 Revert "Bump pyezviz to 0.2.2.3" (#132715) 2024-12-10 14:51:04 +01:00
Joost Lekkerkerker 8fc50c776e Bump yt-dlp to 2024.12.06 (#132684) 2024-12-10 14:51:00 +01:00
Bouwe Westerdijk da344a44e5 Bump plugwise to v1.6.3 (#132673) 2024-12-10 14:50:57 +01:00
puddly 1993142e44 Bump ZHA dependencies (#132630) 2024-12-10 14:50:54 +01:00
Thomas55555 382d32c7a7 Fix config flow in Husqvarna Automower (#132615) 2024-12-10 14:50:50 +01:00
Bouwe Westerdijk ef89563bad Bump plugwise to v1.6.2 and adapt (#132608) 2024-12-10 14:50:46 +01:00
Bouwe Westerdijk 26012ac922 Bump plugwise to v1.6.1 (#131950) 2024-12-10 14:50:42 +01:00
J. Nick Koston a33c69a2a2 Bump yalexs-ble to 2.5.2 (#132560) 2024-12-10 14:45:34 +01:00
Franck Nijhof 0096ffb659 Update twentemilieu to 2.2.0 (#132554) 2024-12-10 14:45:31 +01:00
Robert Svensson db141ce449 Bump aiounifi to v81 to fix partitioned cookies on python 3.13 (#132540) 2024-12-10 14:45:26 +01:00
Austin Mroczek af5f718a71 bump total_connect_client to 2023.12 (#132531) 2024-12-10 14:43:52 +01:00
Franck Nijhof f1284178ed Update debugpy to 1.8.8 (#132519) 2024-12-10 14:41:15 +01:00
J. Nick Koston b0005cedff Bump pycups to 2.0.4 (#132514) 2024-12-10 14:41:11 +01:00
Erwin Douna 5d01f7db85 Fix PyTado dependency (#132510) 2024-12-10 14:41:08 +01:00
Alex d6a4a7f052 Update pyrisco to 0.6.5 (#132493) 2024-12-10 14:41:02 +01:00
Ravaka Razafimanantsoa 1f6c5b4d8b Fix API change for AC not supporting floats in SwitchBot Cloud (#132231) 2024-12-10 14:36:09 +01:00
David Knowles 4e56f9c014 Bump pydrawise to 2024.12.0 (#132015) 2024-12-10 14:36:06 +01:00
Åke Strandberg f343dce418 Enable additional entities on myUplink model SMO20 (#131688)
* Add a couple of entities to SMO 20

* Enable additional entities on SMO20
2024-12-10 14:35:58 +01:00
Franck Nijhof cf53a9743f 2024.12.1 (#132509) 2024-12-06 20:21:31 +01:00
Franck Nijhof 4884891b2c Bump version to 2024.12.1 2024-12-06 18:54:13 +01:00
Allen Porter 30504fc9bd Fix google tasks due date timezone handling (#132498) 2024-12-06 18:53:42 +01:00
Bram Kragten 8827454dbd Update frontend to 20241127.6 (#132494) 2024-12-06 18:53:39 +01:00
Bram Kragten 3b30bbb85e Update frontend to 20241127.5 (#132475) 2024-12-06 18:53:35 +01:00
epenet df9eb482b5 Bump samsungtvws to 2.7.2 (#132474) 2024-12-06 18:53:32 +01:00
Steven B. 32aee61441 Bump tplink python-kasa dependency to 0.8.1 (#132472) 2024-12-06 18:53:29 +01:00
Robert Resch 35873cbe27 Point to the Ecovacs issue in the library for unspoorted devices (#132470)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2024-12-06 18:53:26 +01:00
Robert Resch 6fe492a51c Bump deebot-client to 9.2.0 (#132467) 2024-12-06 18:53:22 +01:00
G Johansson b1bc35f1c3 Fix nordpool dont have previous or next price (#132457) 2024-12-06 18:53:19 +01:00
Joakim Sørensen 56d10a0a7a Bump hass-nabucasa from 0.85.0 to 0.86.0 (#132456)
Bump hass-nabucasa fro 0.85.0 to 0.86.0
2024-12-06 18:53:16 +01:00
Allen Porter d091936ac6 Update exception handling for python3.13 for getpass.getuser() (#132449)
* Update exception handling for python3.13 for getpass.getuser()

* Add comment

Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>

* Cleanup trailing space

---------

Co-authored-by: Franck Nijhof <frenck@frenck.nl>
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2024-12-06 18:53:12 +01:00
J. Nick Koston 1dfd4e80b9 Bump aioesphomeapi to 28.0.0 (#132447) 2024-12-06 18:53:09 +01:00
J. Nick Koston d919de6734 Bump aiohttp to 3.11.10 (#132441) 2024-12-06 18:53:06 +01:00
Blake Bryant 3f9f0f8ac2 Bump pydeako to 0.6.0 (#132432)
feat: update deako integration to use improved version of pydeako

Some things of note:
- simplified errors
- pydeako has introduced some connection improvements

See here: https://github.com/DeakoLights/pydeako/releases/tag/0.6.0
2024-12-06 18:53:03 +01:00
Glenn Waters bf20ffae96 Bump upb-lib to 0.5.9 (#132411) 2024-12-06 18:53:00 +01:00
Diogo Gomes dad81927cb Removes references to croniter from utility_meter (#132364)
remove croniter
2024-12-06 18:52:56 +01:00
robinostlund 92392ab3d4 Add missing UnitOfPower to sensor (#132352)
* Add missing UnitOfPower to sensor

* Update homeassistant/components/sensor/const.py

Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>

* adding to number

---------

Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2024-12-06 18:52:53 +01:00
Brett Adams a47e5398f0 Bump tesla-fleet-api to 0.8.5 (#132339) 2024-12-06 18:52:50 +01:00
J. Nick Koston cf6d33635b Fix deprecated call to mimetypes.guess_type in CachingStaticResource (#132299) 2024-12-06 18:52:47 +01:00
Alberto Geniola 6a4031a383 Bump elmax-api to 0.0.6.3 (#131876) 2024-12-06 18:52:39 +01:00
Franck Nijhof 2b40844171 2024.12.0 (#132195) 2024-12-04 19:58:02 +01:00
Franck Nijhof 9b90df74a6 Bump version to 2024.12.0 2024-12-04 19:18:48 +01:00
Michael Hansen dcdf033fa9 Bump intents to 2024.12.4 (#132274) 2024-12-04 19:03:26 +01:00
74 changed files with 339 additions and 213 deletions
+1 -1
View File
@@ -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"]
}
+1 -1
View File
@@ -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"]
}
+1 -1
View File
@@ -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"]
}
+3 -8
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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"]
}
+1 -1
View File
@@ -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,
+2 -2
View File
@@ -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
+1 -1
View File
@@ -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"
],
+1 -1
View File
@@ -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"],
+11 -3
View File
@@ -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",
)
+18 -5
View File
@@ -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,
),
)
+7 -1
View File
@@ -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),
+2 -11
View File
@@ -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"]
}
+1 -1
View File
@@ -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"
],
+7 -1
View File
@@ -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:
+1 -1
View File
@@ -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"]
}
+1 -1
View File
@@ -7,7 +7,7 @@
"integration_type": "hub",
"iot_class": "local_push",
"loggers": ["aiounifi"],
"requirements": ["aiounifi==80"],
"requirements": ["aiounifi==81"],
"ssdp": [
{
"manufacturer": "Ubiquiti Networks",
+1 -1
View File
@@ -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"]
}
+1 -1
View File
@@ -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"]
}
+1 -1
View File
@@ -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",
+1 -1
View File
@@ -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)
+4 -1
View File
@@ -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":
+4 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -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',
+3 -28
View File
@@ -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()
+15 -2
View File
@@ -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',
+37 -1
View File
@@ -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",
[
-1
View File
@@ -56,7 +56,6 @@ def mock_legacy_pydrawise(
@pytest.fixture
def mock_pydrawise(
mock_auth: AsyncMock,
user: User,
controller: Controller,
zones: list[Zone],
+31 -8
View File
@@ -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.",
+4 -8
View File
@@ -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',
+4 -5
View File
@@ -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