Skip to content

Commit

Permalink
2024.10.3 (#128654)
Browse files Browse the repository at this point in the history
  • Loading branch information
frenck authored Oct 18, 2024
2 parents 6952d24 + 5157715 commit a301d51
Show file tree
Hide file tree
Showing 38 changed files with 446 additions and 71 deletions.
4 changes: 3 additions & 1 deletion homeassistant/components/airzone/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
OperationMode.HEATING: HVACMode.HEAT,
OperationMode.FAN: HVACMode.FAN_ONLY,
OperationMode.DRY: HVACMode.DRY,
OperationMode.AUX_HEATING: HVACMode.HEAT,
OperationMode.AUTO: HVACMode.HEAT_COOL,
}
HVAC_MODE_HASS_TO_LIB: Final[dict[HVACMode, OperationMode]] = {
Expand Down Expand Up @@ -157,9 +158,10 @@ def __init__(
self._attr_temperature_unit = TEMP_UNIT_LIB_TO_HASS[
self.get_airzone_value(AZD_TEMP_UNIT)
]
self._attr_hvac_modes = [
_attr_hvac_modes = [
HVAC_MODE_LIB_TO_HASS[mode] for mode in self.get_airzone_value(AZD_MODES)
]
self._attr_hvac_modes = list(dict.fromkeys(_attr_hvac_modes))
if (
self.get_airzone_value(AZD_SPEED) is not None
and self.get_airzone_value(AZD_SPEEDS) is not None
Expand Down
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.3"]
"requirements": ["aioairzone==0.9.5"]
}
2 changes: 1 addition & 1 deletion homeassistant/components/bluesound/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/bluesound",
"iot_class": "local_polling",
"requirements": ["pyblu==1.0.3"],
"requirements": ["pyblu==1.0.4"],
"zeroconf": [
{
"type": "_musc._tcp.local."
Expand Down
2 changes: 2 additions & 0 deletions homeassistant/components/bluesound/media_player.py
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,8 @@ def media_position(self) -> int | None:
return None

position = self._status.seconds
if position is None:
return None

if mediastate == MediaPlayerState.PLAYING:
position += (dt_util.utcnow() - self._last_status_update).total_seconds()
Expand Down
4 changes: 4 additions & 0 deletions homeassistant/components/daikin/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ async def _set(self, settings: dict[str, Any]) -> None:

if values:
await self.device.set(values)
await self.coordinator.async_refresh()

@property
def unique_id(self) -> str:
Expand Down Expand Up @@ -261,6 +262,7 @@ async def async_set_preset_mode(self, preset_mode: str) -> None:
await self.device.set_advanced_mode(
HA_PRESET_TO_DAIKIN[PRESET_ECO], ATTR_STATE_OFF
)
await self.coordinator.async_refresh()

@property
def preset_modes(self) -> list[str]:
Expand All @@ -275,9 +277,11 @@ def preset_modes(self) -> list[str]:
async def async_turn_on(self) -> None:
"""Turn device on."""
await self.device.set({})
await self.coordinator.async_refresh()

async def async_turn_off(self) -> None:
"""Turn device off."""
await self.device.set(
{HA_ATTR_TO_DAIKIN[ATTR_HVAC_MODE]: HA_STATE_TO_DAIKIN[HVACMode.OFF]}
)
await self.coordinator.async_refresh()
6 changes: 6 additions & 0 deletions homeassistant/components/daikin/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,12 @@ def is_on(self) -> bool:
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the zone on."""
await self.device.set_zone(self._zone_id, "zone_onoff", "1")
await self.coordinator.async_refresh()

async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the zone off."""
await self.device.set_zone(self._zone_id, "zone_onoff", "0")
await self.coordinator.async_refresh()


class DaikinStreamerSwitch(DaikinEntity, SwitchEntity):
Expand All @@ -88,10 +90,12 @@ def is_on(self) -> bool:
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the zone on."""
await self.device.set_streamer("on")
await self.coordinator.async_refresh()

async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the zone off."""
await self.device.set_streamer("off")
await self.coordinator.async_refresh()


class DaikinToggleSwitch(DaikinEntity, SwitchEntity):
Expand All @@ -112,7 +116,9 @@ def is_on(self) -> bool:
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the zone on."""
await self.device.set({})
await self.coordinator.async_refresh()

async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the zone off."""
await self.device.set({DAIKIN_ATTR_MODE: "off"})
await self.coordinator.async_refresh()
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.1.5", "oauth2client==4.1.3", "ical==8.2.0"]
"requirements": ["gcal-sync==6.1.6", "oauth2client==4.1.3", "ical==8.2.0"]
}
2 changes: 1 addition & 1 deletion homeassistant/components/ipp/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@
"iot_class": "local_polling",
"loggers": ["deepmerge", "pyipp"],
"quality_scale": "platinum",
"requirements": ["pyipp==0.16.0"],
"requirements": ["pyipp==0.17.0"],
"zeroconf": ["_ipps._tcp.local.", "_ipp._tcp.local."]
}
5 changes: 2 additions & 3 deletions homeassistant/components/ipp/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from collections.abc import Callable
from dataclasses import dataclass
from datetime import datetime, timedelta
from datetime import datetime
from typing import Any

from pyipp import Marker, Printer
Expand All @@ -19,7 +19,6 @@
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import StateType
from homeassistant.util.dt import utcnow

from . import IPPConfigEntry
from .const import (
Expand Down Expand Up @@ -80,7 +79,7 @@ def _get_marker_value_fn(
device_class=SensorDeviceClass.TIMESTAMP,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
value_fn=lambda printer: (utcnow() - timedelta(seconds=printer.info.uptime)),
value_fn=lambda printer: printer.booted_at,
),
)

Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/media_extractor/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@
"iot_class": "calculated",
"loggers": ["yt_dlp"],
"quality_scale": "internal",
"requirements": ["yt-dlp==2024.09.27"],
"requirements": ["yt-dlp==2024.10.07"],
"single_config_entry": true
}
2 changes: 1 addition & 1 deletion homeassistant/components/opentherm_gw/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/opentherm_gw",
"iot_class": "local_push",
"loggers": ["pyotgw"],
"requirements": ["pyotgw==2.2.1"]
"requirements": ["pyotgw==2.2.2"]
}
16 changes: 14 additions & 2 deletions homeassistant/components/roku/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from homeassistant.const import CONF_HOST, Platform
from homeassistant.core import HomeAssistant

from .const import DOMAIN
from .const import CONF_PLAY_MEDIA_APP_ID, DEFAULT_PLAY_MEDIA_APP_ID, DOMAIN
from .coordinator import RokuDataUpdateCoordinator

PLATFORMS = [
Expand All @@ -24,14 +24,21 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
device_id = entry.entry_id

coordinator = RokuDataUpdateCoordinator(
hass, host=entry.data[CONF_HOST], device_id=device_id
hass,
host=entry.data[CONF_HOST],
device_id=device_id,
play_media_app_id=entry.options.get(
CONF_PLAY_MEDIA_APP_ID, DEFAULT_PLAY_MEDIA_APP_ID
),
)
await coordinator.async_config_entry_first_refresh()

hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator

await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

entry.async_on_unload(entry.add_update_listener(async_reload_entry))

return True


Expand All @@ -40,3 +47,8 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
hass.data[DOMAIN].pop(entry.entry_id)
return unload_ok


async def async_reload_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Reload the config entry when it changed."""
await hass.config_entries.async_reload(entry.entry_id)
42 changes: 40 additions & 2 deletions homeassistant/components/roku/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,17 @@
import voluptuous as vol

from homeassistant.components import ssdp, zeroconf
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.config_entries import (
ConfigEntry,
ConfigFlow,
ConfigFlowResult,
OptionsFlowWithConfigEntry,
)
from homeassistant.const import CONF_HOST, CONF_NAME
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.aiohttp_client import async_get_clientsession

from .const import DOMAIN
from .const import CONF_PLAY_MEDIA_APP_ID, DEFAULT_PLAY_MEDIA_APP_ID, DOMAIN

DATA_SCHEMA = vol.Schema({vol.Required(CONF_HOST): str})

Expand Down Expand Up @@ -155,3 +160,36 @@ async def async_step_discovery_confirm(
title=self.discovery_info[CONF_NAME],
data=self.discovery_info,
)

@staticmethod
@callback
def async_get_options_flow(
config_entry: ConfigEntry,
) -> OptionsFlowWithConfigEntry:
"""Create the options flow."""
return RokuOptionsFlowHandler(config_entry)


class RokuOptionsFlowHandler(OptionsFlowWithConfigEntry):
"""Handle Roku options."""

async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Manage Roku options."""
if user_input is not None:
return self.async_create_entry(title="", data=user_input)

return self.async_show_form(
step_id="init",
data_schema=vol.Schema(
{
vol.Optional(
CONF_PLAY_MEDIA_APP_ID,
default=self.options.get(
CONF_PLAY_MEDIA_APP_ID, DEFAULT_PLAY_MEDIA_APP_ID
),
): str,
}
),
)
6 changes: 6 additions & 0 deletions homeassistant/components/roku/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,9 @@

# Services
SERVICE_SEARCH = "search"

# Config
CONF_PLAY_MEDIA_APP_ID = "play_media_app_id"

# Defaults
DEFAULT_PLAY_MEDIA_APP_ID = "15985"
7 changes: 2 additions & 5 deletions homeassistant/components/roku/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,12 @@ class RokuDataUpdateCoordinator(DataUpdateCoordinator[Device]):
roku: Roku

def __init__(
self,
hass: HomeAssistant,
*,
host: str,
device_id: str,
self, hass: HomeAssistant, *, host: str, device_id: str, play_media_app_id: str
) -> None:
"""Initialize global Roku data updater."""
self.device_id = device_id
self.roku = Roku(host=host, session=async_get_clientsession(hass))
self.play_media_app_id = play_media_app_id

self.full_update_interval = timedelta(minutes=15)
self.last_full_update = None
Expand Down
14 changes: 11 additions & 3 deletions homeassistant/components/roku/media_player.py
Original file line number Diff line number Diff line change
Expand Up @@ -445,17 +445,25 @@ async def async_play_media(
if attr in extra
}

params = {"t": "a", **params}
params = {"u": media_id, "t": "a", **params}

await self.coordinator.roku.play_on_roku(media_id, params)
await self.coordinator.roku.launch(
self.coordinator.play_media_app_id,
params,
)
elif media_type in {MediaType.URL, MediaType.VIDEO}:
params = {
param: extra[attr]
for (attr, param) in ATTRS_TO_PLAY_ON_ROKU_PARAMS.items()
if attr in extra
}
params["u"] = media_id
params["t"] = "v"

await self.coordinator.roku.play_on_roku(media_id, params)
await self.coordinator.roku.launch(
self.coordinator.play_media_app_id,
params,
)
else:
_LOGGER.error("Media type %s is not supported", original_media_type)
return
Expand Down
12 changes: 12 additions & 0 deletions homeassistant/components/roku/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,18 @@
"unknown": "[%key:common::config_flow::error::unknown%]"
}
},
"options": {
"step": {
"init": {
"data": {
"play_media_app_id": "Play Media Roku Application ID"
},
"data_description": {
"play_media_app_id": "The application ID to use when launching media playback. Must support the PlayOnRoku API."
}
}
}
},
"entity": {
"binary_sensor": {
"headphones_connected": {
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/solarlog/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/solarlog",
"iot_class": "local_polling",
"loggers": ["solarlog_cli"],
"requirements": ["solarlog_cli==0.3.1"]
"requirements": ["solarlog_cli==0.3.2"]
}
4 changes: 3 additions & 1 deletion homeassistant/components/tag/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,9 @@ def _create_entry(
original_name=f"{DEFAULT_NAME} {tag_id}",
suggested_object_id=slugify(name) if name else tag_id,
)
return entity_registry.async_update_entity(entry.entity_id, name=name)
if name:
return entity_registry.async_update_entity(entry.entity_id, name=name)
return entry


class TagStore(Store[collection.SerializedStorageCollection]):
Expand Down
15 changes: 14 additions & 1 deletion homeassistant/components/wmspro/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from __future__ import annotations

import ipaddress
import logging
from typing import Any

Expand Down Expand Up @@ -38,7 +39,19 @@ async def async_step_dhcp(
"""Handle the DHCP discovery step."""
unique_id = format_mac(discovery_info.macaddress)
await self.async_set_unique_id(unique_id)
self._abort_if_unique_id_configured()

entry = self.hass.config_entries.async_entry_for_domain_unique_id(
DOMAIN, unique_id
)
if entry:
try: # Check if current host is a valid IP address
ipaddress.ip_address(entry.data[CONF_HOST])
except ValueError: # Do not touch name-based host
return self.async_abort(reason="already_configured")
else: # Update existing host with new IP address
self._abort_if_unique_id_configured(
updates={CONF_HOST: discovery_info.ip}
)

for entry in self.hass.config_entries.async_entries(DOMAIN):
if not entry.unique_id and entry.data[CONF_HOST] in (
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/zha/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -1198,7 +1198,7 @@ def create_zha_config(hass: HomeAssistant, ha_zha_data: HAZHAData) -> ZHAData:
# deep copy the yaml config to avoid modifying the original and to safely
# pass it to the ZHA library
app_config = copy.deepcopy(ha_zha_data.yaml_config.get(CONF_ZIGPY, {}))
database = app_config.get(
database = ha_zha_data.yaml_config.get(
CONF_DATABASE,
hass.config.path(DEFAULT_DATABASE_NAME),
)
Expand Down
Loading

0 comments on commit a301d51

Please sign in to comment.