Skip to content

Commit

Permalink
2024.3.2 (#113973)
Browse files Browse the repository at this point in the history
* Streamline Notion config entry updates (refresh token and user ID) (#112832)

* Bump aioautomower to 2024.3.2 (#113162)

* Bump aioautomower to 2024.3.3 (#113430)

* Check for EA release channel for UniFi Protect (#113432)

Co-authored-by: J. Nick Koston <nick@koston.org>

* Bump `pysnmp-lextudio` to version `6.0.11` (#113463)

* Tado fix water heater (#113464)

Co-authored-by: Joostlek <joostlek@outlook.com>

* Bump aiodhcpwatcher to 0.8.2 (#113466)

* Bump axis to v55 (#113479)

* Bump croniter to 2.0.2 (#113494)

* Revert setting communication delay in Risco init (#113497)

* Bump pyrisco to 0.5.10 (#113505)

* Fix missing context when running script from template entity (#113523)

Co-authored-by: J. Nick Koston <nick@koston.org>

* Bump ical to 7.0.3 to fix local-todo persisted with invalid DTSTART values (#113526)

* Fix Airthings BLE illuminance sensor name (#113560)

* Ignore Shelly block update with cfgChanged None (#113587)

* Catch `TimeoutError` in `Brother` config flow (#113593)

* Catch TimeoutError in Brother config flow

* Update tests

* Remove unnecessary parentheses

---------

Co-authored-by: Maciej Bieniek <478555+bieniu@users.noreply.github.com>

* Bump axis to v56 (#113608)

* Bump pyunifiprotect to 5.0.1 (#113630)

* Bump pyunifiprotect to 5.0.2 (#113651)

* Add removal condition to Shelly battery sensor (#113703)

Co-authored-by: Maciej Bieniek <478555+bieniu@users.noreply.github.com>

* Bump aioraven to 0.5.2 (#113714)

* Fix unknown values in onewire (#113731)

* Fix unknown values in onewire

* Update tests

* Bump pymodbus v3.6.6 (#113796)

* Catch API errors in cast media_player service handlers (#113839)

* Catch API errors in cast media_player service handlers

* Remove left over debug code

* Fix wrapping of coroutine function with api_error

* Bump pychromecast to 14.0.1 (#113841)

* Fix startup race in cast (#113843)

* Redact the area of traccar server geofences (#113861)

* Bump pytedee_async to 0.2.17 (#113933)

* Bump axis to v57 (#113952)

* Bump version to 2024.3.2

---------

Co-authored-by: Aaron Bach <bachya1208@gmail.com>
Co-authored-by: Thomas55555 <59625598+Thomas55555@users.noreply.github.com>
Co-authored-by: Christopher Bailey <cbailey@mort.is>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: Lex Li <425130+lextm@users.noreply.github.com>
Co-authored-by: Erwin Douna <e.douna@gmail.com>
Co-authored-by: Joostlek <joostlek@outlook.com>
Co-authored-by: Robert Svensson <Kane610@users.noreply.github.com>
Co-authored-by: Diogo Gomes <diogogomes@gmail.com>
Co-authored-by: On Freund <onfreund@gmail.com>
Co-authored-by: Erik Montnemery <erik@montnemery.com>
Co-authored-by: Allen Porter <allen@thebends.org>
Co-authored-by: Shay Levy <levyshay1@gmail.com>
Co-authored-by: Maciej Bieniek <bieniu@users.noreply.github.com>
Co-authored-by: Maciej Bieniek <478555+bieniu@users.noreply.github.com>
Co-authored-by: Scott K Logan <logans@cottsay.net>
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
Co-authored-by: jan iversen <jancasacondor@gmail.com>
Co-authored-by: Joakim Sørensen <ludeeus@ludeeus.dev>
Co-authored-by: Josef Zweck <24647999+zweckj@users.noreply.github.com>
  • Loading branch information
21 people authored Mar 22, 2024
1 parent f7972ce commit f10d924
Show file tree
Hide file tree
Showing 54 changed files with 289 additions and 197 deletions.
1 change: 1 addition & 0 deletions homeassistant/components/airthings_ble/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@
),
"illuminance": SensorEntityDescription(
key="illuminance",
translation_key="illuminance",
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
),
Expand Down
3 changes: 3 additions & 0 deletions homeassistant/components/airthings_ble/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@
},
"radon_longterm_level": {
"name": "Radon longterm level"
},
"illuminance": {
"name": "[%key:component::sensor::entity_component::illuminance::name%]"
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/axis/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"iot_class": "local_push",
"loggers": ["axis"],
"quality_scale": "platinum",
"requirements": ["axis==54"],
"requirements": ["axis==57"],
"ssdp": [
{
"manufacturer": "AXIS"
Expand Down
4 changes: 2 additions & 2 deletions homeassistant/components/brother/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ async def async_step_user(
return self.async_create_entry(title=title, data=user_input)
except InvalidHost:
errors[CONF_HOST] = "wrong_host"
except ConnectionError:
except (ConnectionError, TimeoutError):
errors["base"] = "cannot_connect"
except SnmpError:
errors["base"] = "snmp_error"
Expand Down Expand Up @@ -88,7 +88,7 @@ async def async_step_zeroconf(
await self.brother.async_update()
except UnsupportedModelError:
return self.async_abort(reason="unsupported_model")
except (ConnectionError, SnmpError):
except (ConnectionError, SnmpError, TimeoutError):
return self.async_abort(reason="cannot_connect")

# Check if already configured
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/cast/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@

async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Cast from a config entry."""
hass.data[DOMAIN] = {"cast_platform": {}, "unknown_models": {}}
await home_assistant_cast.async_setup_ha_cast(hass, entry)
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
hass.data[DOMAIN] = {"cast_platform": {}, "unknown_models": {}}
await async_process_integration_platforms(hass, DOMAIN, _register_cast_platform)
return True

Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/cast/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@
"documentation": "https://www.home-assistant.io/integrations/cast",
"iot_class": "local_polling",
"loggers": ["casttube", "pychromecast"],
"requirements": ["PyChromecast==14.0.0"],
"requirements": ["PyChromecast==14.0.1"],
"zeroconf": ["_googlecast._tcp.local."]
}
68 changes: 61 additions & 7 deletions homeassistant/components/cast/media_player.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
from collections.abc import Callable
from contextlib import suppress
from datetime import datetime
from functools import wraps
import json
import logging
from typing import TYPE_CHECKING, Any
from typing import TYPE_CHECKING, Any, Concatenate, ParamSpec, TypeVar

import pychromecast
from pychromecast.controllers.homeassistant import HomeAssistantController
Expand All @@ -18,6 +19,7 @@
)
from pychromecast.controllers.multizone import MultizoneManager
from pychromecast.controllers.receiver import VOLUME_CONTROL_TYPE_FIXED
from pychromecast.error import PyChromecastError
from pychromecast.quick_play import quick_play
from pychromecast.socket_client import (
CONNECTION_STATUS_CONNECTED,
Expand Down Expand Up @@ -83,6 +85,34 @@
CAST_SPLASH = "https://www.home-assistant.io/images/cast/splash.png"


_CastDeviceT = TypeVar("_CastDeviceT", bound="CastDevice")
_R = TypeVar("_R")
_P = ParamSpec("_P")

_FuncType = Callable[Concatenate[_CastDeviceT, _P], _R]
_ReturnFuncType = Callable[Concatenate[_CastDeviceT, _P], _R]


def api_error(
func: _FuncType[_CastDeviceT, _P, _R],
) -> _ReturnFuncType[_CastDeviceT, _P, _R]:
"""Handle PyChromecastError and reraise a HomeAssistantError."""

@wraps(func)
def wrapper(self: _CastDeviceT, *args: _P.args, **kwargs: _P.kwargs) -> _R:
"""Wrap a CastDevice method."""
try:
return_value = func(self, *args, **kwargs)
except PyChromecastError as err:
raise HomeAssistantError(
f"{self.__class__.__name__}.{func.__name__} Failed: {err}"
) from err

return return_value

return wrapper


@callback
def _async_create_cast_device(hass: HomeAssistant, info: ChromecastInfo):
"""Create a CastDevice entity or dynamic group from the chromecast object.
Expand Down Expand Up @@ -476,6 +506,21 @@ def _media_controller(self):

return media_controller

@api_error
def _quick_play(self, app_name: str, data: dict[str, Any]) -> None:
"""Launch the app `app_name` and start playing media defined by `data`."""
quick_play(self._get_chromecast(), app_name, data)

@api_error
def _quit_app(self) -> None:
"""Quit the currently running app."""
self._get_chromecast().quit_app()

@api_error
def _start_app(self, app_id: str) -> None:
"""Start an app."""
self._get_chromecast().start_app(app_id)

def turn_on(self) -> None:
"""Turn on the cast device."""

Expand All @@ -486,52 +531,61 @@ def turn_on(self) -> None:

if chromecast.app_id is not None:
# Quit the previous app before starting splash screen or media player
chromecast.quit_app()
self._quit_app()

# The only way we can turn the Chromecast is on is by launching an app
if chromecast.cast_type == pychromecast.const.CAST_TYPE_CHROMECAST:
app_data = {"media_id": CAST_SPLASH, "media_type": "image/png"}
quick_play(chromecast, "default_media_receiver", app_data)
self._quick_play("default_media_receiver", app_data)
else:
chromecast.start_app(pychromecast.config.APP_MEDIA_RECEIVER)
self._start_app(pychromecast.config.APP_MEDIA_RECEIVER)

@api_error
def turn_off(self) -> None:
"""Turn off the cast device."""
self._get_chromecast().quit_app()

@api_error
def mute_volume(self, mute: bool) -> None:
"""Mute the volume."""
self._get_chromecast().set_volume_muted(mute)

@api_error
def set_volume_level(self, volume: float) -> None:
"""Set volume level, range 0..1."""
self._get_chromecast().set_volume(volume)

@api_error
def media_play(self) -> None:
"""Send play command."""
media_controller = self._media_controller()
media_controller.play()

@api_error
def media_pause(self) -> None:
"""Send pause command."""
media_controller = self._media_controller()
media_controller.pause()

@api_error
def media_stop(self) -> None:
"""Send stop command."""
media_controller = self._media_controller()
media_controller.stop()

@api_error
def media_previous_track(self) -> None:
"""Send previous track command."""
media_controller = self._media_controller()
media_controller.queue_prev()

@api_error
def media_next_track(self) -> None:
"""Send next track command."""
media_controller = self._media_controller()
media_controller.queue_next()

@api_error
def media_seek(self, position: float) -> None:
"""Seek the media to a specific location."""
media_controller = self._media_controller()
Expand Down Expand Up @@ -644,7 +698,7 @@ async def async_play_media(
if "app_id" in app_data:
app_id = app_data.pop("app_id")
_LOGGER.info("Starting Cast app by ID %s", app_id)
await self.hass.async_add_executor_job(chromecast.start_app, app_id)
await self.hass.async_add_executor_job(self._start_app, app_id)
if app_data:
_LOGGER.warning(
"Extra keys %s were ignored. Please use app_name to cast media",
Expand All @@ -655,7 +709,7 @@ async def async_play_media(
app_name = app_data.pop("app_name")
try:
await self.hass.async_add_executor_job(
quick_play, chromecast, app_name, app_data
self._quick_play, app_name, app_data
)
except NotImplementedError:
_LOGGER.error("App %s not supported", app_name)
Expand Down Expand Up @@ -729,7 +783,7 @@ async def async_play_media(
app_data,
)
await self.hass.async_add_executor_job(
quick_play, chromecast, "default_media_receiver", app_data
self._quick_play, "default_media_receiver", app_data
)

def _media_status(self):
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/dhcp/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
],
"quality_scale": "internal",
"requirements": [
"aiodhcpwatcher==0.8.1",
"aiodhcpwatcher==0.8.2",
"aiodiscover==1.6.1",
"cached_ipaddress==0.3.0"
]
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/google/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
"documentation": "https://www.home-assistant.io/integrations/calendar.google",
"iot_class": "cloud_polling",
"loggers": ["googleapiclient"],
"requirements": ["gcal-sync==6.0.3", "oauth2client==4.1.3", "ical==7.0.1"]
"requirements": ["gcal-sync==6.0.3", "oauth2client==4.1.3", "ical==7.0.3"]
}
2 changes: 1 addition & 1 deletion homeassistant/components/husqvarna_automower/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
"documentation": "https://www.home-assistant.io/integrations/husqvarna_automower",
"iot_class": "cloud_push",
"loggers": ["aioautomower"],
"requirements": ["aioautomower==2024.3.0"]
"requirements": ["aioautomower==2024.3.3"]
}
7 changes: 7 additions & 0 deletions homeassistant/components/husqvarna_automower/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ class AutomowerSensorEntityDescription(SensorEntityDescription):
device_class=SensorDeviceClass.DURATION,
native_unit_of_measurement=UnitOfTime.SECONDS,
suggested_unit_of_measurement=UnitOfTime.HOURS,
exists_fn=lambda data: data.statistics.total_charging_time is not None,
value_fn=lambda data: data.statistics.total_charging_time,
),
AutomowerSensorEntityDescription(
Expand All @@ -79,6 +80,7 @@ class AutomowerSensorEntityDescription(SensorEntityDescription):
device_class=SensorDeviceClass.DURATION,
native_unit_of_measurement=UnitOfTime.SECONDS,
suggested_unit_of_measurement=UnitOfTime.HOURS,
exists_fn=lambda data: data.statistics.total_cutting_time is not None,
value_fn=lambda data: data.statistics.total_cutting_time,
),
AutomowerSensorEntityDescription(
Expand All @@ -89,6 +91,7 @@ class AutomowerSensorEntityDescription(SensorEntityDescription):
device_class=SensorDeviceClass.DURATION,
native_unit_of_measurement=UnitOfTime.SECONDS,
suggested_unit_of_measurement=UnitOfTime.HOURS,
exists_fn=lambda data: data.statistics.total_running_time is not None,
value_fn=lambda data: data.statistics.total_running_time,
),
AutomowerSensorEntityDescription(
Expand All @@ -99,6 +102,7 @@ class AutomowerSensorEntityDescription(SensorEntityDescription):
device_class=SensorDeviceClass.DURATION,
native_unit_of_measurement=UnitOfTime.SECONDS,
suggested_unit_of_measurement=UnitOfTime.HOURS,
exists_fn=lambda data: data.statistics.total_searching_time is not None,
value_fn=lambda data: data.statistics.total_searching_time,
),
AutomowerSensorEntityDescription(
Expand All @@ -107,6 +111,7 @@ class AutomowerSensorEntityDescription(SensorEntityDescription):
icon="mdi:battery-sync-outline",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
exists_fn=lambda data: data.statistics.number_of_charging_cycles is not None,
value_fn=lambda data: data.statistics.number_of_charging_cycles,
),
AutomowerSensorEntityDescription(
Expand All @@ -115,6 +120,7 @@ class AutomowerSensorEntityDescription(SensorEntityDescription):
icon="mdi:counter",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.TOTAL,
exists_fn=lambda data: data.statistics.number_of_collisions is not None,
value_fn=lambda data: data.statistics.number_of_collisions,
),
AutomowerSensorEntityDescription(
Expand All @@ -125,6 +131,7 @@ class AutomowerSensorEntityDescription(SensorEntityDescription):
device_class=SensorDeviceClass.DISTANCE,
native_unit_of_measurement=UnitOfLength.METERS,
suggested_unit_of_measurement=UnitOfLength.KILOMETERS,
exists_fn=lambda data: data.statistics.total_drive_distance is not None,
value_fn=lambda data: data.statistics.total_drive_distance,
),
AutomowerSensorEntityDescription(
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/local_calendar/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/local_calendar",
"iot_class": "local_polling",
"loggers": ["ical"],
"requirements": ["ical==7.0.1"]
"requirements": ["ical==7.0.3"]
}
2 changes: 1 addition & 1 deletion homeassistant/components/local_todo/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/local_todo",
"iot_class": "local_polling",
"requirements": ["ical==7.0.1"]
"requirements": ["ical==7.0.3"]
}
2 changes: 1 addition & 1 deletion homeassistant/components/modbus/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
"iot_class": "local_polling",
"loggers": ["pymodbus"],
"quality_scale": "gold",
"requirements": ["pymodbus==3.6.5"]
"requirements": ["pymodbus==3.6.6"]
}
19 changes: 10 additions & 9 deletions homeassistant/components/notion/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,9 +165,16 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
except NotionError as err:
raise ConfigEntryNotReady("Config entry failed to load") from err

# Always update the config entry with the latest refresh token and user UUID:
entry_updates["data"][CONF_REFRESH_TOKEN] = client.refresh_token
entry_updates["data"][CONF_USER_UUID] = client.user_uuid
# Update the Notion user UUID and refresh token if they've changed:
for key, value in (
(CONF_REFRESH_TOKEN, client.refresh_token),
(CONF_USER_UUID, client.user_uuid),
):
if entry.data[key] == value:
continue
entry_updates["data"][key] = value

hass.config_entries.async_update_entry(entry, **entry_updates)

@callback
def async_save_refresh_token(refresh_token: str) -> None:
Expand All @@ -180,12 +187,6 @@ def async_save_refresh_token(refresh_token: str) -> None:
# Create a callback to save the refresh token when it changes:
entry.async_on_unload(client.add_refresh_token_callback(async_save_refresh_token))

# Save the client's refresh token if it's different than what we already have:
if (token := client.refresh_token) and token != entry.data[CONF_REFRESH_TOKEN]:
async_save_refresh_token(token)

hass.config_entries.async_update_entry(entry, **entry_updates)

async def async_update() -> NotionData:
"""Get the latest data from the Notion API."""
data = NotionData(hass=hass, entry=entry)
Expand Down
4 changes: 3 additions & 1 deletion homeassistant/components/onewire/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ class OneWireBinarySensor(OneWireEntity, BinarySensorEntity):
entity_description: OneWireBinarySensorEntityDescription

@property
def is_on(self) -> bool:
def is_on(self) -> bool | None:
"""Return true if sensor is on."""
if self._state is None:
return None
return bool(self._state)
6 changes: 4 additions & 2 deletions homeassistant/components/onewire/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,8 +204,10 @@ class OneWireSwitch(OneWireEntity, SwitchEntity):
entity_description: OneWireSwitchEntityDescription

@property
def is_on(self) -> bool:
"""Return true if sensor is on."""
def is_on(self) -> bool | None:
"""Return true if switch is on."""
if self._state is None:
return None
return bool(self._state)

def turn_on(self, **kwargs: Any) -> None:
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/rainforest_raven/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"dependencies": ["usb"],
"documentation": "https://www.home-assistant.io/integrations/rainforest_raven",
"iot_class": "local_polling",
"requirements": ["aioraven==0.5.1"],
"requirements": ["aioraven==0.5.2"],
"usb": [
{
"vid": "0403",
Expand Down
Loading

0 comments on commit f10d924

Please sign in to comment.