Skip to content

Commit

Permalink
2024.11.2 (home-assistant#130713)
Browse files Browse the repository at this point in the history
  • Loading branch information
frenck authored Nov 15, 2024
2 parents ab05562 + ac270e1 commit 847afab
Show file tree
Hide file tree
Showing 118 changed files with 1,030 additions and 741 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ repos:
pass_filenames: false
language: script
types: [text]
files: ^(script/hassfest/metadata\.py|homeassistant/const\.py$|pyproject\.toml)$
files: ^(script/hassfest/metadata\.py|homeassistant/const\.py$|pyproject\.toml|homeassistant/components/go2rtc/const\.py)$
- id: hassfest-mypy-config
name: hassfest-mypy-config
entry: script/run-in-env.sh python3 -m script.hassfest -p mypy_config
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ RUN \
"armv7") go2rtc_suffix='arm' ;; \
*) go2rtc_suffix=${BUILD_ARCH} ;; \
esac \
&& curl -L https://github.com/AlexxIT/go2rtc/releases/download/v1.9.6/go2rtc_linux_${go2rtc_suffix} --output /bin/go2rtc \
&& curl -L https://github.com/AlexxIT/go2rtc/releases/download/v1.9.7/go2rtc_linux_${go2rtc_suffix} --output /bin/go2rtc \
&& chmod +x /bin/go2rtc \
# Verify go2rtc can be executed
&& go2rtc --version
Expand Down
14 changes: 10 additions & 4 deletions homeassistant/components/alarm_control_panel/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from datetime import timedelta
from functools import partial
import logging
from typing import Any, Final, final
from typing import TYPE_CHECKING, Any, Final, final

from propcache import cached_property
import voluptuous as vol
Expand Down Expand Up @@ -221,9 +221,15 @@ def _report_deprecated_alarm_state_handling(self) -> None:
@property
def state(self) -> str | None:
"""Return the current state."""
if (alarm_state := self.alarm_state) is None:
return None
return alarm_state
if (alarm_state := self.alarm_state) is not None:
return alarm_state
if self._attr_state is not None:
# Backwards compatibility for integrations that set state directly
# Should be removed in 2025.11
if TYPE_CHECKING:
assert isinstance(self._attr_state, str)
return self._attr_state
return None

@cached_property
def alarm_state(self) -> AlarmControlPanelState | None:
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/cambridge_audio/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@
"integration_type": "device",
"iot_class": "local_push",
"loggers": ["aiostreammagic"],
"requirements": ["aiostreammagic==2.8.4"],
"requirements": ["aiostreammagic==2.8.5"],
"zeroconf": ["_stream-magic._tcp.local.", "_smoip._tcp.local."]
}
7 changes: 6 additions & 1 deletion homeassistant/components/cambridge_audio/select.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,13 @@ def _audio_output_value_fn(client: StreamMagicClient) -> str | None:
CambridgeAudioSelectEntityDescription(
key="display_brightness",
translation_key="display_brightness",
options=[x.value for x in DisplayBrightness],
options=[
DisplayBrightness.BRIGHT.value,
DisplayBrightness.DIM.value,
DisplayBrightness.OFF.value,
],
entity_category=EntityCategory.CONFIG,
load_fn=lambda client: client.display.brightness != DisplayBrightness.NONE,
value_fn=lambda client: client.display.brightness,
set_value_fn=lambda client, value: client.set_display_brightness(
DisplayBrightness(value)
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/co2signal/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ async def _validate_and_create(
)

return self.async_create_entry(
title=get_extra_name(data) or "CO2 Signal",
title=get_extra_name(data) or "Electricity Maps",
data=data,
)

Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/conversation/default_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ async def async_process(self, user_input: ConversationInput) -> ConversationResu
self.hass, language, DOMAIN, [DOMAIN]
)
response_text = translations.get(
f"component.{DOMAIN}.agent.done", "Done"
f"component.{DOMAIN}.conversation.agent.done", "Done"
)

response.async_set_speech(response_text)
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/emulated_kasa/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
"iot_class": "local_push",
"loggers": ["sense_energy"],
"quality_scale": "internal",
"requirements": ["sense-energy==0.13.3"]
"requirements": ["sense-energy==0.13.4"]
}
2 changes: 1 addition & 1 deletion homeassistant/components/file/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
},
"data_description": {
"file_path": "The local file path to retrieve the sensor value from",
"value_template": "A template to render the the sensors value based on the file content",
"value_template": "A template to render the sensors value based on the file content",
"unit_of_measurement": "Unit of measurement for the sensor"
}
},
Expand Down
6 changes: 5 additions & 1 deletion homeassistant/components/generic/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ async def async_test_stream(
return {CONF_STREAM_SOURCE: "timeout"}
await stream.stop()
except StreamWorkerError as err:
return {CONF_STREAM_SOURCE: str(err)}
return {CONF_STREAM_SOURCE: "unknown_with_details", "error_details": str(err)}
except PermissionError:
return {CONF_STREAM_SOURCE: "stream_not_permitted"}
except OSError as err:
Expand Down Expand Up @@ -339,6 +339,7 @@ async def async_step_user(
) -> ConfigFlowResult:
"""Handle the start of the config flow."""
errors = {}
description_placeholders = {}
hass = self.hass
if user_input:
# Secondary validation because serialised vol can't seem to handle this complexity:
Expand Down Expand Up @@ -372,13 +373,16 @@ async def async_step_user(
# temporary preview for user to check the image
self.preview_cam = user_input
return await self.async_step_user_confirm_still()
if "error_details" in errors:
description_placeholders["error"] = errors.pop("error_details")
elif self.user_input:
user_input = self.user_input
else:
user_input = DEFAULT_DATA.copy()
return self.async_show_form(
step_id="user",
data_schema=build_schema(user_input),
description_placeholders=description_placeholders,
errors=errors,
)

Expand Down
1 change: 1 addition & 0 deletions homeassistant/components/generic/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"config": {
"error": {
"unknown": "[%key:common::config_flow::error::unknown%]",
"unknown_with_details": "An unknown error occurred: {error}",
"already_exists": "A camera with these URL settings already exists.",
"unable_still_load": "Unable to load valid image from still image URL (e.g. invalid host, URL or authentication failure). Review log for more info.",
"unable_still_load_auth": "Unable to load valid image from still image URL: The camera may require a user name and password, or they are not correct.",
Expand Down
6 changes: 3 additions & 3 deletions homeassistant/components/generic_thermostat/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"config": {
"step": {
"user": {
"title": "Add generic thermostat helper",
"title": "Add generic thermostat",
"description": "Create a climate entity that controls the temperature via a switch and sensor.",
"data": {
"ac_mode": "Cooling mode",
Expand All @@ -17,8 +17,8 @@
"data_description": {
"ac_mode": "Set the actuator specified to be treated as a cooling device instead of a heating device.",
"heater": "Switch entity used to cool or heat depending on A/C mode.",
"target_sensor": "Temperature sensor that reflect the current temperature.",
"min_cycle_duration": "Set a minimum amount of time that the switch specified must be in its current state prior to being switched either off or on. This option will be ignored if the keep alive option is set.",
"target_sensor": "Temperature sensor that reflects the current temperature.",
"min_cycle_duration": "Set a minimum amount of time that the switch specified must be in its current state prior to being switched either off or on.",
"cold_tolerance": "Minimum amount of difference between the temperature read by the temperature sensor the target temperature that must change prior to being switched on. For example, if the target temperature is 25 and the tolerance is 0.5 the heater will start when the sensor equals or goes below 24.5.",
"hot_tolerance": "Minimum amount of difference between the temperature read by the temperature sensor the target temperature that must change prior to being switched off. For example, if the target temperature is 25 and the tolerance is 0.5 the heater will stop when the sensor equals or goes above 25.5."
}
Expand Down
100 changes: 43 additions & 57 deletions homeassistant/components/go2rtc/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
"""The go2rtc component."""

from __future__ import annotations

from dataclasses import dataclass
import logging
import shutil

from aiohttp.client_exceptions import ClientConnectionError, ServerConnectionError
from awesomeversion import AwesomeVersion
from go2rtc_client import Go2RtcRestClient
from go2rtc_client.exceptions import Go2RtcClientError, Go2RtcVersionError
from go2rtc_client.ws import (
Expand Down Expand Up @@ -35,7 +33,11 @@
from homeassistant.const import CONF_URL, EVENT_HOMEASSISTANT_STOP
from homeassistant.core import Event, HomeAssistant, callback
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import config_validation as cv, discovery_flow
from homeassistant.helpers import (
config_validation as cv,
discovery_flow,
issue_registry as ir,
)
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.typing import ConfigType
from homeassistant.util.hass_dict import HassKey
Expand All @@ -45,8 +47,8 @@
CONF_DEBUG_UI,
DEBUG_UI_URL_MESSAGE,
DOMAIN,
HA_MANAGED_RTSP_PORT,
HA_MANAGED_URL,
RECOMMENDED_VERSION,
)
from .server import Server

Expand Down Expand Up @@ -94,22 +96,13 @@
extra=vol.ALLOW_EXTRA,
)

_DATA_GO2RTC: HassKey[Go2RtcData] = HassKey(DOMAIN)
_DATA_GO2RTC: HassKey[str] = HassKey(DOMAIN)
_RETRYABLE_ERRORS = (ClientConnectionError, ServerConnectionError)


@dataclass(frozen=True)
class Go2RtcData:
"""Data for go2rtc."""

url: str
managed: bool


async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up WebRTC."""
url: str | None = None
managed = False
if DOMAIN not in config and DEFAULT_CONFIG_DOMAIN not in config:
await _remove_go2rtc_entries(hass)
return True
Expand Down Expand Up @@ -144,9 +137,8 @@ async def on_stop(event: Event) -> None:
hass.bus.async_listen(EVENT_HOMEASSISTANT_STOP, on_stop)

url = HA_MANAGED_URL
managed = True

hass.data[_DATA_GO2RTC] = Go2RtcData(url, managed)
hass.data[_DATA_GO2RTC] = url
discovery_flow.async_create_flow(
hass, DOMAIN, context={"source": SOURCE_SYSTEM}, data={}
)
Expand All @@ -161,32 +153,42 @@ async def _remove_go2rtc_entries(hass: HomeAssistant) -> None:

async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up go2rtc from a config entry."""
data = hass.data[_DATA_GO2RTC]
url = hass.data[_DATA_GO2RTC]

# Validate the server URL
try:
client = Go2RtcRestClient(async_get_clientsession(hass), data.url)
await client.validate_server_version()
client = Go2RtcRestClient(async_get_clientsession(hass), url)
version = await client.validate_server_version()
if version < AwesomeVersion(RECOMMENDED_VERSION):
ir.async_create_issue(
hass,
DOMAIN,
"recommended_version",
is_fixable=False,
is_persistent=False,
severity=ir.IssueSeverity.WARNING,
translation_key="recommended_version",
translation_placeholders={
"recommended_version": RECOMMENDED_VERSION,
"current_version": str(version),
},
)
except Go2RtcClientError as err:
if isinstance(err.__cause__, _RETRYABLE_ERRORS):
raise ConfigEntryNotReady(
f"Could not connect to go2rtc instance on {data.url}"
f"Could not connect to go2rtc instance on {url}"
) from err
_LOGGER.warning(
"Could not connect to go2rtc instance on %s (%s)", data.url, err
)
_LOGGER.warning("Could not connect to go2rtc instance on %s (%s)", url, err)
return False
except Go2RtcVersionError as err:
raise ConfigEntryNotReady(
f"The go2rtc server version is not supported, {err}"
) from err
except Exception as err: # noqa: BLE001
_LOGGER.warning(
"Could not connect to go2rtc instance on %s (%s)", data.url, err
)
_LOGGER.warning("Could not connect to go2rtc instance on %s (%s)", url, err)
return False

provider = WebRTCProvider(hass, data)
provider = WebRTCProvider(hass, url)
async_register_webrtc_provider(hass, provider)
return True

Expand All @@ -204,12 +206,12 @@ async def _get_binary(hass: HomeAssistant) -> str | None:
class WebRTCProvider(CameraWebRTCProvider):
"""WebRTC provider."""

def __init__(self, hass: HomeAssistant, data: Go2RtcData) -> None:
def __init__(self, hass: HomeAssistant, url: str) -> None:
"""Initialize the WebRTC provider."""
self._hass = hass
self._data = data
self._url = url
self._session = async_get_clientsession(hass)
self._rest_client = Go2RtcRestClient(self._session, data.url)
self._rest_client = Go2RtcRestClient(self._session, url)
self._sessions: dict[str, Go2RtcWsClient] = {}

@property
Expand All @@ -231,7 +233,7 @@ async def async_handle_async_webrtc_offer(
) -> None:
"""Handle the WebRTC offer and return the answer via the provided callback."""
self._sessions[session_id] = ws_client = Go2RtcWsClient(
self._session, self._data.url, source=camera.entity_id
self._session, self._url, source=camera.entity_id
)

if not (stream_source := await camera.stream_source()):
Expand All @@ -242,34 +244,18 @@ async def async_handle_async_webrtc_offer(

streams = await self._rest_client.streams.list()

if self._data.managed:
# HA manages the go2rtc instance
stream_org_name = camera.entity_id + "_orginal"
stream_redirect_sources = [
f"rtsp://127.0.0.1:{HA_MANAGED_RTSP_PORT}/{stream_org_name}",
f"ffmpeg:{stream_org_name}#audio=opus",
]

if (
(stream_org := streams.get(stream_org_name)) is None
or not any(
stream_source == producer.url for producer in stream_org.producers
)
or (stream_redirect := streams.get(camera.entity_id)) is None
or stream_redirect_sources != [p.url for p in stream_redirect.producers]
):
await self._rest_client.streams.add(stream_org_name, stream_source)
await self._rest_client.streams.add(
camera.entity_id, stream_redirect_sources
)

# go2rtc instance is managed outside HA
elif (stream_org := streams.get(camera.entity_id)) is None or not any(
stream_source == producer.url for producer in stream_org.producers
if (stream := streams.get(camera.entity_id)) is None or not any(
stream_source == producer.url for producer in stream.producers
):
await self._rest_client.streams.add(
camera.entity_id,
[stream_source, f"ffmpeg:{camera.entity_id}#audio=opus"],
[
stream_source,
# We are setting any ffmpeg rtsp related logs to debug
# Connection problems to the camera will be logged by the first stream
# Therefore setting it to debug will not hide any important logs
f"ffmpeg:{camera.entity_id}#audio=opus#query=log_level=debug",
],
)

@callback
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/go2rtc/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@
DEBUG_UI_URL_MESSAGE = "Url and debug_ui cannot be set at the same time."
HA_MANAGED_API_PORT = 11984
HA_MANAGED_URL = f"http://localhost:{HA_MANAGED_API_PORT}/"
HA_MANAGED_RTSP_PORT = 18554
RECOMMENDED_VERSION = "1.9.7"
2 changes: 1 addition & 1 deletion homeassistant/components/go2rtc/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@
"documentation": "https://www.home-assistant.io/integrations/go2rtc",
"integration_type": "system",
"iot_class": "local_polling",
"requirements": ["go2rtc-client==0.1.0"],
"requirements": ["go2rtc-client==0.1.1"],
"single_config_entry": true
}
Loading

0 comments on commit 847afab

Please sign in to comment.