Skip to content

Commit

Permalink
2024.11.3 (#131248)
Browse files Browse the repository at this point in the history
  • Loading branch information
frenck authored Nov 22, 2024
2 parents 847afab + 4ef50ff commit 0644d78
Show file tree
Hide file tree
Showing 50 changed files with 687 additions and 142 deletions.
2 changes: 1 addition & 1 deletion homeassistant/components/airq/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
"integration_type": "hub",
"iot_class": "local_polling",
"loggers": ["aioairq"],
"requirements": ["aioairq==0.3.2"]
"requirements": ["aioairq==0.4.3"]
}
2 changes: 1 addition & 1 deletion homeassistant/components/airzone/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@
"documentation": "https://www.home-assistant.io/integrations/airzone",
"iot_class": "local_polling",
"loggers": ["aioairzone"],
"requirements": ["aioairzone==0.9.5"]
"requirements": ["aioairzone==0.9.7"]
}
1 change: 1 addition & 0 deletions homeassistant/components/apsystems/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ApSystemsConfigEntry) ->
ip_address=entry.data[CONF_IP_ADDRESS],
port=entry.data.get(CONF_PORT, DEFAULT_PORT),
timeout=8,
enable_debounce=True,
)
coordinator = ApSystemsDataCoordinator(hass, api)
await coordinator.async_config_entry_first_refresh()
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/apsystems/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/apsystems",
"integration_type": "device",
"iot_class": "local_polling",
"requirements": ["apsystems-ez1==2.2.1"]
"requirements": ["apsystems-ez1==2.4.0"]
}
3 changes: 2 additions & 1 deletion homeassistant/components/apsystems/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from typing import Any

from aiohttp.client_exceptions import ClientConnectionError
from APsystemsEZ1 import InverterReturnedError

from homeassistant.components.switch import SwitchDeviceClass, SwitchEntity
from homeassistant.core import HomeAssistant
Expand Down Expand Up @@ -40,7 +41,7 @@ async def async_update(self) -> None:
"""Update switch status and availability."""
try:
status = await self._api.get_device_power_status()
except (TimeoutError, ClientConnectionError):
except (TimeoutError, ClientConnectionError, InverterReturnedError):
self._attr_available = False
else:
self._attr_available = True
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/bluetooth/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"requirements": [
"bleak==0.22.3",
"bleak-retry-connector==3.6.0",
"bluetooth-adapters==0.20.0",
"bluetooth-adapters==0.20.2",
"bluetooth-auto-recovery==1.4.2",
"bluetooth-data-tools==1.20.0",
"dbus-fast==2.24.3",
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/cast/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
},
"view_path": {
"name": "View path",
"description": "The path of the dashboard view to show."
"description": "The URL path of the dashboard view to show."
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/elkm1/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
}
},
"alarm_arm_home_instant": {
"name": "Alarm are home instant",
"name": "Alarm arm home instant",
"description": "Arms the ElkM1 in home instant mode.",
"fields": {
"code": {
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/elmax/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"documentation": "https://www.home-assistant.io/integrations/elmax",
"iot_class": "cloud_polling",
"loggers": ["elmax_api"],
"requirements": ["elmax-api==0.0.5"],
"requirements": ["elmax-api==0.0.6.1"],
"zeroconf": [
{
"type": "_elmax-ssl._tcp.local."
Expand Down
3 changes: 3 additions & 0 deletions homeassistant/components/esphome/ffmpeg_proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,9 @@ async def transcode(
# Remove metadata and cover art
command_args.extend(["-map_metadata", "-1", "-vn"])

# disable progress stats on stderr
command_args.append("-nostats")

# Output to stdout
command_args.append("pipe:")

Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/esphome/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@
},
"service_calls_not_allowed": {
"title": "{name} is not permitted to perform Home Assistant actions",
"description": "The ESPHome device attempted to perform a Home Assistant action, but this functionality is not enabled.\n\nIf you trust this device and want to allow it to perfom Home Assistant action, you can enable this functionality in the options flow."
"description": "The ESPHome device attempted to perform a Home Assistant action, but this functionality is not enabled.\n\nIf you trust this device and want to allow it to perform Home Assistant action, you can enable this functionality in the options flow."
}
}
}
3 changes: 2 additions & 1 deletion homeassistant/components/feedreader/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from __future__ import annotations

import html
import logging
from typing import Any
import urllib.error
Expand Down Expand Up @@ -106,7 +107,7 @@ async def async_step_user(
return self.abort_on_import_error(user_input[CONF_URL], "url_error")
return self.show_user_form(user_input, {"base": "url_error"})

feed_title = feed["feed"]["title"]
feed_title = html.unescape(feed["feed"]["title"])

return self.async_create_entry(
title=feed_title,
Expand Down
4 changes: 3 additions & 1 deletion homeassistant/components/feedreader/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from calendar import timegm
from datetime import datetime
import html
from logging import getLogger
from time import gmtime, struct_time
from typing import TYPE_CHECKING
Expand Down Expand Up @@ -102,7 +103,8 @@ async def async_setup(self) -> None:
"""Set up the feed manager."""
feed = await self._async_fetch_feed()
self.logger.debug("Feed data fetched from %s : %s", self.url, feed["feed"])
self.feed_author = feed["feed"].get("author")
if feed_author := feed["feed"].get("author"):
self.feed_author = html.unescape(feed_author)
self.feed_version = feedparser.api.SUPPORTED_VERSIONS.get(feed["version"])
self._feed = feed

Expand Down
12 changes: 10 additions & 2 deletions homeassistant/components/feedreader/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from __future__ import annotations

import html
import logging

from feedparser import FeedParserDict
Expand Down Expand Up @@ -76,15 +77,22 @@ def _async_handle_update(self) -> None:
# so we always take the first entry in list, since we only care about the latest entry
feed_data: FeedParserDict = data[0]

if description := feed_data.get("description"):
description = html.unescape(description)

if title := feed_data.get("title"):
title = html.unescape(title)

if content := feed_data.get("content"):
if isinstance(content, list) and isinstance(content[0], dict):
content = content[0].get("value")
content = html.unescape(content)

self._trigger_event(
EVENT_FEEDREADER,
{
ATTR_DESCRIPTION: feed_data.get("description"),
ATTR_TITLE: feed_data.get("title"),
ATTR_DESCRIPTION: description,
ATTR_TITLE: title,
ATTR_LINK: feed_data.get("link"),
ATTR_CONTENT: content,
},
Expand Down
67 changes: 23 additions & 44 deletions homeassistant/components/fibaro/cover.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,37 +69,29 @@ def _is_open_close_only(self) -> bool:
# so if it is missing we have a device which supports open / close only
return not self.fibaro_device.value.has_value

@property
def current_cover_position(self) -> int | None:
"""Return current position of cover. 0 is closed, 100 is open."""
return self.bound(self.level)

@property
def current_cover_tilt_position(self) -> int | None:
"""Return the current tilt position for venetian blinds."""
return self.bound(self.level2)

@property
def is_opening(self) -> bool | None:
"""Return if the cover is opening or not.
Be aware that this property is only available for some modern devices.
For example the Fibaro Roller Shutter 4 reports this correctly.
"""
if self.fibaro_device.state.has_value:
return self.fibaro_device.state.str_value().lower() == "opening"
return None

@property
def is_closing(self) -> bool | None:
"""Return if the cover is closing or not.
Be aware that this property is only available for some modern devices.
For example the Fibaro Roller Shutter 4 reports this correctly.
"""
if self.fibaro_device.state.has_value:
return self.fibaro_device.state.str_value().lower() == "closing"
return None
def update(self) -> None:
"""Update the state."""
super().update()

self._attr_current_cover_position = self.bound(self.level)
self._attr_current_cover_tilt_position = self.bound(self.level2)

device_state = self.fibaro_device.state

# Be aware that opening and closing is only available for some modern
# devices.
# For example the Fibaro Roller Shutter 4 reports this correctly.
if device_state.has_value:
self._attr_is_opening = device_state.str_value().lower() == "opening"
self._attr_is_closing = device_state.str_value().lower() == "closing"

closed: bool | None = None
if self._is_open_close_only():
if device_state.has_value and device_state.str_value().lower() != "unknown":
closed = device_state.str_value().lower() == "closed"
elif self.current_cover_position is not None:
closed = self.current_cover_position == 0
self._attr_is_closed = closed

def set_cover_position(self, **kwargs: Any) -> None:
"""Move the cover to a specific position."""
Expand All @@ -109,19 +101,6 @@ def set_cover_tilt_position(self, **kwargs: Any) -> None:
"""Move the cover to a specific position."""
self.set_level2(cast(int, kwargs.get(ATTR_TILT_POSITION)))

@property
def is_closed(self) -> bool | None:
"""Return if the cover is closed."""
if self._is_open_close_only():
state = self.fibaro_device.state
if not state.has_value or state.str_value().lower() == "unknown":
return None
return state.str_value().lower() == "closed"

if self.current_cover_position is None:
return None
return self.current_cover_position == 0

def open_cover(self, **kwargs: Any) -> None:
"""Open the cover."""
self.action("open")
Expand Down
12 changes: 6 additions & 6 deletions homeassistant/components/hassio/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@
"fields": {
"addon": {
"name": "Add-on",
"description": "The add-on slug."
"description": "The add-on to start."
}
}
},
Expand All @@ -284,17 +284,17 @@
"fields": {
"addon": {
"name": "[%key:component::hassio::services::addon_start::fields::addon::name%]",
"description": "[%key:component::hassio::services::addon_start::fields::addon::description%]"
"description": "The add-on to restart."
}
}
},
"addon_stdin": {
"name": "Write data to add-on stdin.",
"description": "Writes data to add-on stdin.",
"description": "Writes data to the add-on's standard input.",
"fields": {
"addon": {
"name": "[%key:component::hassio::services::addon_start::fields::addon::name%]",
"description": "[%key:component::hassio::services::addon_start::fields::addon::description%]"
"description": "The add-on to write to."
}
}
},
Expand All @@ -304,7 +304,7 @@
"fields": {
"addon": {
"name": "[%key:component::hassio::services::addon_start::fields::addon::name%]",
"description": "[%key:component::hassio::services::addon_start::fields::addon::description%]"
"description": "The add-on to stop."
}
}
},
Expand All @@ -314,7 +314,7 @@
"fields": {
"addon": {
"name": "[%key:component::hassio::services::addon_start::fields::addon::name%]",
"description": "[%key:component::hassio::services::addon_start::fields::addon::description%]"
"description": "The add-on to update."
}
}
},
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/holiday/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/holiday",
"iot_class": "local_polling",
"requirements": ["holidays==0.60", "babel==2.15.0"]
"requirements": ["holidays==0.61", "babel==2.15.0"]
}
2 changes: 1 addition & 1 deletion homeassistant/components/homematicip_cloud/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
"iot_class": "cloud_push",
"loggers": ["homematicip"],
"quality_scale": "silver",
"requirements": ["homematicip==1.1.2"]
"requirements": ["homematicip==1.1.3"]
}
25 changes: 14 additions & 11 deletions homeassistant/components/mqtt/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
CONF_PROTOCOL,
CONF_USERNAME,
)
from homeassistant.core import callback
from homeassistant.core import HomeAssistant, callback
from homeassistant.data_entry_flow import AbortFlow
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.hassio import is_hassio
Expand Down Expand Up @@ -737,6 +737,16 @@ def _validate(
)


async def _get_uploaded_file(hass: HomeAssistant, id: str) -> str:
"""Get file content from uploaded file."""

def _proces_uploaded_file() -> str:
with process_uploaded_file(hass, id) as file_path:
return file_path.read_text(encoding=DEFAULT_ENCODING)

return await hass.async_add_executor_job(_proces_uploaded_file)


async def async_get_broker_settings(
flow: ConfigFlow | OptionsFlow,
fields: OrderedDict[Any, Any],
Expand Down Expand Up @@ -795,8 +805,7 @@ async def _async_validate_broker_settings(
return False
certificate_id: str | None = user_input.get(CONF_CERTIFICATE)
if certificate_id:
with process_uploaded_file(hass, certificate_id) as certificate_file:
certificate = certificate_file.read_text(encoding=DEFAULT_ENCODING)
certificate = await _get_uploaded_file(hass, certificate_id)

# Return to form for file upload CA cert or client cert and key
if (
Expand All @@ -812,15 +821,9 @@ async def _async_validate_broker_settings(
return False

if client_certificate_id:
with process_uploaded_file(
hass, client_certificate_id
) as client_certificate_file:
client_certificate = client_certificate_file.read_text(
encoding=DEFAULT_ENCODING
)
client_certificate = await _get_uploaded_file(hass, client_certificate_id)
if client_key_id:
with process_uploaded_file(hass, client_key_id) as key_file:
client_key = key_file.read_text(encoding=DEFAULT_ENCODING)
client_key = await _get_uploaded_file(hass, client_key_id)

certificate_data: dict[str, Any] = {}
if certificate:
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/myuplink/strings.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"application_credentials": {
"description": "Follow the [instructions]({more_info_url}) to give Home Assistant access to your myUplink account. You also need to create application credentials linked to your account:\n1. Go to [Applications at myUplink developer site]({create_creds_url}) and get credentials from an existing application or select **Create New Application**.\n1. Set appropriate Application name and Description\n2. Enter `{callback_url}` as Callback Url"
"description": "Follow the [instructions]({more_info_url}) to give Home Assistant access to your myUplink account. You also need to create application credentials linked to your account:\n1. Go to [Applications at myUplink developer site]({create_creds_url}) and get credentials from an existing application or select **Create New Application**.\n1. Set appropriate Application name and Description\n1. Enter `{callback_url}` as Callback URL"
},
"config": {
"step": {
Expand Down
Loading

0 comments on commit 0644d78

Please sign in to comment.