Skip to content

Commit

Permalink
2024.7.2 (#121671)
Browse files Browse the repository at this point in the history
  • Loading branch information
frenck committed Jul 10, 2024
2 parents 1cf6291 + 7137075 commit 058b012
Show file tree
Hide file tree
Showing 57 changed files with 462 additions and 217 deletions.
2 changes: 1 addition & 1 deletion homeassistant/components/abode/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@
},
"iot_class": "cloud_push",
"loggers": ["jaraco.abode", "lomond"],
"requirements": ["jaraco.abode==5.1.2"]
"requirements": ["jaraco.abode==5.2.1"]
}
2 changes: 1 addition & 1 deletion homeassistant/components/anova/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/anova",
"iot_class": "cloud_push",
"loggers": ["anova_wifi"],
"requirements": ["anova-wifi==0.15.0"]
"requirements": ["anova-wifi==0.17.0"]
}
2 changes: 1 addition & 1 deletion homeassistant/components/august/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,5 @@
"documentation": "https://www.home-assistant.io/integrations/august",
"iot_class": "cloud_push",
"loggers": ["pubnub", "yalexs"],
"requirements": ["yalexs==6.4.1", "yalexs-ble==2.4.3"]
"requirements": ["yalexs==6.4.2", "yalexs-ble==2.4.3"]
}
8 changes: 8 additions & 0 deletions homeassistant/components/climate/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,14 @@ def _report_turn_on_off(feature: str, method: str) -> None:
# Return if integration has migrated already
return

supported_features = self.supported_features
if supported_features & (
ClimateEntityFeature.TURN_ON | ClimateEntityFeature.TURN_OFF
):
# The entity supports both turn_on and turn_off, the backwards compatibility
# checks are not needed
return

supported_features = self.supported_features
if not supported_features & ClimateEntityFeature.TURN_OFF and (
type(self).async_turn_off is not ClimateEntity.async_turn_off
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/enphase_envoy/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/enphase_envoy",
"iot_class": "local_polling",
"loggers": ["pyenphase"],
"requirements": ["pyenphase==1.20.3"],
"requirements": ["pyenphase==1.20.6"],
"zeroconf": [
{
"type": "_enphase-envoy._tcp.local."
Expand Down
14 changes: 0 additions & 14 deletions homeassistant/components/feedreader/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,13 +107,6 @@ 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"})

if not feed.entries:
if self.context["source"] == SOURCE_IMPORT:
return self.abort_on_import_error(
user_input[CONF_URL], "no_feed_entries"
)
return self.show_user_form(user_input, {"base": "no_feed_entries"})

feed_title = feed["feed"]["title"]

return self.async_create_entry(
Expand Down Expand Up @@ -161,13 +154,6 @@ async def async_step_reconfigure_confirm(
step_id="reconfigure_confirm",
errors={"base": "url_error"},
)
if not feed.entries:
return self.show_user_form(
user_input=user_input,
description_placeholders={"name": self._config_entry.title},
step_id="reconfigure_confirm",
errors={"base": "no_feed_entries"},
)

self.hass.config_entries.async_update_entry(self._config_entry, data=user_input)
return self.async_abort(reason="reconfigure_successful")
Expand Down
7 changes: 1 addition & 6 deletions homeassistant/components/feedreader/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]"
},
"error": {
"url_error": "The URL could not be opened.",
"no_feed_entries": "The URL seems not to serve any feed entries."
"url_error": "The URL could not be opened."
}
},
"options": {
Expand All @@ -38,10 +37,6 @@
"import_yaml_error_url_error": {
"title": "The Feedreader YAML configuration import failed",
"description": "Configuring the Feedreader using YAML is being removed but there was a connection error when trying to import the YAML configuration for `{url}`.\n\nPlease verify that url is reachable and accessable for Home Assistant and restart Home Assistant to try again or remove the Feedreader YAML configuration from your configuration.yaml file and continue to set up the integration manually."
},
"import_yaml_error_no_feed_entries": {
"title": "[%key:component::feedreader::issues::import_yaml_error_url_error::title%]",
"description": "Configuring the Feedreader using YAML is being removed but when trying to import the YAML configuration for `{url}` no feed entries were found.\n\nPlease verify that url serves any feed entries and restart Home Assistant to try again or remove the Feedreader YAML configuration from your configuration.yaml file and continue to set up the integration manually."
}
}
}
2 changes: 1 addition & 1 deletion homeassistant/components/frontend/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@
"documentation": "https://www.home-assistant.io/integrations/frontend",
"integration_type": "system",
"quality_scale": "internal",
"requirements": ["home-assistant-frontend==20240705.0"]
"requirements": ["home-assistant-frontend==20240710.0"]
}
11 changes: 9 additions & 2 deletions homeassistant/components/fully_kiosk/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@

from __future__ import annotations

from fullykiosk import FullyKioskError

from homeassistant.components.camera import Camera, CameraEntityFeature
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from .const import DOMAIN
Expand Down Expand Up @@ -36,8 +39,12 @@ async def async_camera_image(
self, width: int | None = None, height: int | None = None
) -> bytes | None:
"""Return bytes of camera image."""
image_bytes: bytes = await self.coordinator.fully.getCamshot()
return image_bytes
try:
image_bytes: bytes = await self.coordinator.fully.getCamshot()
except FullyKioskError as err:
raise HomeAssistantError(err) from err
else:
return image_bytes

async def async_turn_on(self) -> None:
"""Turn on camera."""
Expand Down
1 change: 0 additions & 1 deletion homeassistant/components/homematic/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,6 @@
key="GAS_POWER",
native_unit_of_measurement=UnitOfVolume.CUBIC_METERS,
device_class=SensorDeviceClass.GAS,
state_class=SensorStateClass.MEASUREMENT,
),
"GAS_ENERGY_COUNTER": SensorEntityDescription(
key="GAS_ENERGY_COUNTER",
Expand Down
4 changes: 2 additions & 2 deletions homeassistant/components/icloud/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@
"description": "Enter your credentials",
"data": {
"username": "[%key:common::config_flow::data::email%]",
"password": "[%key:common::config_flow::data::password%]",
"password": "App-specific password",
"with_family": "With family"
}
},
"reauth_confirm": {
"title": "[%key:common::config_flow::title::reauth%]",
"description": "Your previously entered password for {username} is no longer working. Update your password to keep using this integration.",
"data": {
"password": "[%key:common::config_flow::data::password%]"
"password": "App-specific password"
}
},
"trusted_device": {
Expand Down
10 changes: 4 additions & 6 deletions homeassistant/components/kodi/media_player.py
Original file line number Diff line number Diff line change
Expand Up @@ -641,12 +641,10 @@ def extra_state_attributes(self) -> dict[str, str | None]:
if self.state == MediaPlayerState.OFF:
return state_attr

hdr_type = (
self._item.get("streamdetails", {}).get("video", [{}])[0].get("hdrtype")
)
if hdr_type == "":
state_attr["dynamic_range"] = "sdr"
else:
state_attr["dynamic_range"] = "sdr"
if (video_details := self._item.get("streamdetails", {}).get("video")) and (
hdr_type := video_details[0].get("hdrtype")
):
state_attr["dynamic_range"] = hdr_type

return state_attr
Expand Down
16 changes: 16 additions & 0 deletions homeassistant/components/matter/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,4 +145,20 @@ def _update_from_device(self) -> None:
required_attributes=(clusters.BooleanState.Attributes.StateValue,),
device_type=(device_types.RainSensor,),
),
MatterDiscoverySchema(
platform=Platform.BINARY_SENSOR,
entity_description=MatterBinarySensorEntityDescription(
key="LockDoorStateSensor",
device_class=BinarySensorDeviceClass.DOOR,
# pylint: disable=unnecessary-lambda
measurement_to_ha=lambda x: {
clusters.DoorLock.Enums.DoorStateEnum.kDoorOpen: True,
clusters.DoorLock.Enums.DoorStateEnum.kDoorJammed: True,
clusters.DoorLock.Enums.DoorStateEnum.kDoorForcedOpen: True,
clusters.DoorLock.Enums.DoorStateEnum.kDoorClosed: False,
}.get(x),
),
entity_class=MatterBinarySensor,
required_attributes=(clusters.DoorLock.Attributes.DoorState,),
),
]
73 changes: 46 additions & 27 deletions homeassistant/components/matter/lock.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from __future__ import annotations

import asyncio
from typing import Any

from chip.clusters import Objects as clusters
Expand Down Expand Up @@ -38,6 +39,7 @@ class MatterLock(MatterEntity, LockEntity):
"""Representation of a Matter lock."""

features: int | None = None
_optimistic_timer: asyncio.TimerHandle | None = None

@property
def code_format(self) -> str | None:
Expand Down Expand Up @@ -90,9 +92,15 @@ async def send_device_command(

async def async_lock(self, **kwargs: Any) -> None:
"""Lock the lock with pin if needed."""
# optimistically signal locking to state machine
self._attr_is_locking = True
self.async_write_ha_state()
if not self._attr_is_locked:
# optimistically signal locking to state machine
self._attr_is_locking = True
self.async_write_ha_state()
# the lock should acknowledge the command with an attribute update
# but bad things may happen, so guard against it with a timer.
self._optimistic_timer = self.hass.loop.call_later(
30, self._reset_optimistic_state
)
code: str | None = kwargs.get(ATTR_CODE)
code_bytes = code.encode() if code else None
await self.send_device_command(
Expand All @@ -101,9 +109,15 @@ async def async_lock(self, **kwargs: Any) -> None:

async def async_unlock(self, **kwargs: Any) -> None:
"""Unlock the lock with pin if needed."""
# optimistically signal unlocking to state machine
self._attr_is_unlocking = True
self.async_write_ha_state()
if self._attr_is_locked:
# optimistically signal unlocking to state machine
self._attr_is_unlocking = True
self.async_write_ha_state()
# the lock should acknowledge the command with an attribute update
# but bad things may happen, so guard against it with a timer.
self._optimistic_timer = self.hass.loop.call_later(
30, self._reset_optimistic_state
)
code: str | None = kwargs.get(ATTR_CODE)
code_bytes = code.encode() if code else None
if self.supports_unbolt:
Expand All @@ -120,9 +134,14 @@ async def async_unlock(self, **kwargs: Any) -> None:

async def async_open(self, **kwargs: Any) -> None:
"""Open the door latch."""
# optimistically signal unlocking to state machine
self._attr_is_unlocking = True
# optimistically signal opening to state machine
self._attr_is_opening = True
self.async_write_ha_state()
# the lock should acknowledge the command with an attribute update
# but bad things may happen, so guard against it with a timer.
self._optimistic_timer = self.hass.loop.call_later(
30 if self._attr_is_locked else 5, self._reset_optimistic_state
)
code: str | None = kwargs.get(ATTR_CODE)
code_bytes = code.encode() if code else None
await self.send_device_command(
Expand All @@ -145,38 +164,38 @@ def _update_from_device(self) -> None:
)

# always reset the optimisically (un)locking state on state update
self._attr_is_locking = False
self._attr_is_unlocking = False
self._reset_optimistic_state(write_state=False)

LOGGER.debug("Lock state: %s for %s", lock_state, self.entity_id)

if lock_state is clusters.DoorLock.Enums.DlLockState.kUnlatched:
self._attr_is_locked = False
self._attr_is_open = True
if lock_state is clusters.DoorLock.Enums.DlLockState.kLocked:
self._attr_is_locked = True
self._attr_is_open = False
elif lock_state in (
clusters.DoorLock.Enums.DlLockState.kUnlocked,
clusters.DoorLock.Enums.DlLockState.kUnlatched,
clusters.DoorLock.Enums.DlLockState.kNotFullyLocked,
):
self._attr_is_locked = False
self._attr_is_open = False
else:
# According to the matter docs a null state can happen during device startup.
# Treat any other state as unknown.
# NOTE: A null state can happen during device startup.
self._attr_is_locked = None
self._attr_is_open = None

if self.supports_door_position_sensor:
door_state = self.get_matter_attribute_value(
clusters.DoorLock.Attributes.DoorState
)

assert door_state is not None

LOGGER.debug("Door state: %s for %s", door_state, self.entity_id)

self._attr_is_jammed = (
door_state is clusters.DoorLock.Enums.DoorStateEnum.kDoorJammed
)
self._attr_is_open = (
door_state is clusters.DoorLock.Enums.DoorStateEnum.kDoorOpen
)
@callback
def _reset_optimistic_state(self, write_state: bool = True) -> None:
if self._optimistic_timer and not self._optimistic_timer.cancelled():
self._optimistic_timer.cancel()
self._optimistic_timer = None
self._attr_is_locking = False
self._attr_is_unlocking = False
self._attr_is_opening = False
if write_state:
self.async_write_ha_state()


DISCOVERY_SCHEMAS = [
Expand Down
3 changes: 2 additions & 1 deletion homeassistant/components/mealie/calendar.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ def event(self) -> CalendarEvent | None:
mealplans = self.coordinator.data[self._entry_type]
if not mealplans:
return None
return _get_event_from_mealplan(mealplans[0])
sorted_mealplans = sorted(mealplans, key=lambda x: x.mealplan_date)
return _get_event_from_mealplan(sorted_mealplans[0])

async def async_get_events(
self, hass: HomeAssistant, start_date: datetime, end_date: datetime
Expand Down
5 changes: 4 additions & 1 deletion homeassistant/components/mealie/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
"step": {
"user": {
"data": {
"host": "[%key:common::config_flow::data::host%]",
"host": "[%key:common::config_flow::data::url%]",
"api_token": "[%key:common::config_flow::data::api_token%]"
},
"data_description": {
"host": "The URL of your Mealie instance."
}
}
},
Expand Down
5 changes: 5 additions & 0 deletions homeassistant/components/mobile_app/webhook.py
Original file line number Diff line number Diff line change
Expand Up @@ -721,10 +721,15 @@ async def webhook_get_config(
"""Handle a get config webhook."""
hass_config = hass.config.as_dict()

device: dr.DeviceEntry = hass.data[DOMAIN][DATA_DEVICES][
config_entry.data[CONF_WEBHOOK_ID]
]

resp = {
"latitude": hass_config["latitude"],
"longitude": hass_config["longitude"],
"elevation": hass_config["elevation"],
"hass_device_id": device.id,
"unit_system": hass_config["unit_system"],
"location_name": hass_config["location_name"],
"time_zone": hass_config["time_zone"],
Expand Down
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": "platinum",
"requirements": ["pymodbus==3.6.8"]
"requirements": ["pymodbus==3.6.9"]
}
1 change: 1 addition & 0 deletions homeassistant/components/mpd/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"domain": "mpd",
"name": "Music Player Daemon (MPD)",
"codeowners": [],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/mpd",
"iot_class": "local_polling",
"loggers": ["mpd"],
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/ombi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def urlbase(value) -> str:
vol.Optional(CONF_SSL, default=DEFAULT_SSL): cv.boolean,
}
),
cv.has_at_least_one_key("auth"),
cv.has_at_least_one_key(CONF_API_KEY, CONF_PASSWORD),
)
},
extra=vol.ALLOW_EXTRA,
Expand Down
4 changes: 2 additions & 2 deletions homeassistant/components/philips_js/light.py
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,6 @@ def available(self) -> bool:
"""Return true if entity is available."""
if not super().available:
return False
if not self.coordinator.api.on:
if not self._tv.on:
return False
return self.coordinator.api.powerstate == "On"
return True
Loading

0 comments on commit 058b012

Please sign in to comment.