Skip to content

Commit

Permalink
2024.10.1 (#127566)
Browse files Browse the repository at this point in the history
  • Loading branch information
frenck authored Oct 4, 2024
2 parents 5db4a73 + 2cbf53a commit 2182bc3
Show file tree
Hide file tree
Showing 21 changed files with 162 additions and 34 deletions.
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.2"],
"requirements": ["PyChromecast==14.0.1"],
"zeroconf": ["_googlecast._tcp.local."]
}
2 changes: 1 addition & 1 deletion homeassistant/components/matrix/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
"documentation": "https://www.home-assistant.io/integrations/matrix",
"iot_class": "cloud_push",
"loggers": ["matrix_client"],
"requirements": ["matrix-nio==0.25.1", "Pillow==10.4.0"]
"requirements": ["matrix-nio==0.25.2", "Pillow==10.4.0"]
}
2 changes: 1 addition & 1 deletion homeassistant/components/mealie/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/mealie",
"integration_type": "service",
"iot_class": "local_polling",
"requirements": ["aiomealie==0.9.2"]
"requirements": ["aiomealie==0.9.3"]
}
4 changes: 2 additions & 2 deletions homeassistant/components/nyt_games/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_TOKEN, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.aiohttp_client import async_create_clientsession

from .coordinator import NYTGamesCoordinator

Expand All @@ -23,7 +23,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: NYTGamesConfigEntry) ->
"""Set up NYTGames from a config entry."""

client = NYTGamesClient(
entry.data[CONF_TOKEN], session=async_get_clientsession(hass)
entry.data[CONF_TOKEN], session=async_create_clientsession(hass)
)

coordinator = NYTGamesCoordinator(hass, client)
Expand Down
11 changes: 7 additions & 4 deletions homeassistant/components/nyt_games/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_TOKEN
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.aiohttp_client import async_create_clientsession

from .const import DOMAIN, LOGGER

Expand All @@ -21,8 +21,9 @@ async def async_step_user(
"""Handle a flow initialized by the user."""
errors: dict[str, str] = {}
if user_input:
session = async_get_clientsession(self.hass)
client = NYTGamesClient(user_input[CONF_TOKEN], session=session)
session = async_create_clientsession(self.hass)
token = user_input[CONF_TOKEN].strip()
client = NYTGamesClient(token, session=session)
try:
user_id = await client.get_user_id()
except NYTGamesAuthenticationError:
Expand All @@ -35,7 +36,9 @@ async def async_step_user(
else:
await self.async_set_unique_id(str(user_id))
self._abort_if_unique_id_configured()
return self.async_create_entry(title="NYT Games", data=user_input)
return self.async_create_entry(
title="NYT Games", data={CONF_TOKEN: token}
)
return self.async_show_form(
step_id="user",
data_schema=vol.Schema({vol.Required(CONF_TOKEN): str}),
Expand Down
9 changes: 7 additions & 2 deletions homeassistant/components/rituals_perfume_genie/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.aiohttp_client import async_get_clientsession

from .const import ACCOUNT_HASH, DOMAIN
from .const import ACCOUNT_HASH, DOMAIN, UPDATE_INTERVAL
from .coordinator import RitualsDataUpdateCoordinator

PLATFORMS = [
Expand All @@ -37,9 +37,14 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
# Migrate old unique_ids to the new format
async_migrate_entities_unique_ids(hass, entry, account_devices)

# The API provided by Rituals is currently rate limited to 30 requests
# per hour per IP address. To avoid hitting this limit, we will adjust
# the polling interval based on the number of diffusers one has.
update_interval = UPDATE_INTERVAL * len(account_devices)

# Create a coordinator for each diffuser
coordinators = {
diffuser.hublot: RitualsDataUpdateCoordinator(hass, diffuser)
diffuser.hublot: RitualsDataUpdateCoordinator(hass, diffuser, update_interval)
for diffuser in account_devices
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ async def async_step_user(
try:
await account.authenticate()
except ClientResponseError:
_LOGGER.exception("Unexpected response")
errors["base"] = "cannot_connect"
except AuthenticationException:
errors["base"] = "invalid_auth"
Expand Down
6 changes: 5 additions & 1 deletion homeassistant/components/rituals_perfume_genie/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,8 @@

ACCOUNT_HASH = "account_hash"

UPDATE_INTERVAL = timedelta(minutes=2)
# The API provided by Rituals is currently rate limited to 30 requests
# per hour per IP address. To avoid hitting this limit, the polling
# interval is set to 3 minutes. This also gives a little room for
# Home Assistant restarts.
UPDATE_INTERVAL = timedelta(minutes=3)
12 changes: 9 additions & 3 deletions homeassistant/components/rituals_perfume_genie/coordinator.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,34 @@
"""The Rituals Perfume Genie data update coordinator."""

from datetime import timedelta
import logging

from pyrituals import Diffuser

from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator

from .const import DOMAIN, UPDATE_INTERVAL
from .const import DOMAIN

_LOGGER = logging.getLogger(__name__)


class RitualsDataUpdateCoordinator(DataUpdateCoordinator[None]):
"""Class to manage fetching Rituals Perfume Genie device data from single endpoint."""

def __init__(self, hass: HomeAssistant, diffuser: Diffuser) -> None:
def __init__(
self,
hass: HomeAssistant,
diffuser: Diffuser,
update_interval: timedelta,
) -> None:
"""Initialize global Rituals Perfume Genie data updater."""
self.diffuser = diffuser
super().__init__(
hass,
_LOGGER,
name=f"{DOMAIN}-{diffuser.hublot}",
update_interval=UPDATE_INTERVAL,
update_interval=update_interval,
)

async def _async_update_data(self) -> None:
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/smlight/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/smlight",
"integration_type": "device",
"iot_class": "local_push",
"requirements": ["pysmlight==0.1.1"],
"requirements": ["pysmlight==0.1.2"],
"zeroconf": [
{
"type": "_slzb-06._tcp.local."
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/tellduslive/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,4 +194,4 @@ def native_value(self):
@property
def unique_id(self) -> str:
"""Return a unique ID."""
return "-".join(self._id)
return "-".join(map(str, self._id))
6 changes: 5 additions & 1 deletion homeassistant/components/template/alarm_control_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
from homeassistant.exceptions import TemplateError
from homeassistant.helpers import selector
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.device import async_device_info_to_link_from_device_id
from homeassistant.helpers.entity import async_generate_entity_id
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.restore_state import RestoreEntity
Expand Down Expand Up @@ -233,7 +234,10 @@ def __init__(
self._trigger_script = Script(hass, trigger_action, name, DOMAIN)

self._state: str | None = None

self._attr_device_info = async_device_info_to_link_from_device_id(
hass,
config.get(CONF_DEVICE_ID),
)
supported_features = AlarmControlPanelEntityFeature(0)
if self._arm_night_script is not None:
supported_features = (
Expand Down
1 change: 1 addition & 0 deletions homeassistant/components/tesla_fleet/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
Platform.DEVICE_TRACKER,
Platform.LOCK,
Platform.MEDIA_PLAYER,
Platform.NUMBER,
Platform.SELECT,
Platform.SENSOR,
Platform.SWITCH,
Expand Down
17 changes: 11 additions & 6 deletions homeassistant/config_entries.py
Original file line number Diff line number Diff line change
Expand Up @@ -1558,7 +1558,7 @@ def __init__(self, hass: HomeAssistant) -> None:
super().__init__()
self._hass = hass
self._domain_index: dict[str, list[ConfigEntry]] = {}
self._domain_unique_id_index: dict[str, dict[str, ConfigEntry]] = {}
self._domain_unique_id_index: dict[str, dict[str, list[ConfigEntry]]] = {}

def values(self) -> ValuesView[ConfigEntry]:
"""Return the underlying values to avoid __iter__ overhead."""
Expand Down Expand Up @@ -1601,9 +1601,9 @@ def _index_entry(self, entry: ConfigEntry) -> None:
report_issue,
)

self._domain_unique_id_index.setdefault(entry.domain, {})[
unique_id_hash
] = entry
self._domain_unique_id_index.setdefault(entry.domain, {}).setdefault(
unique_id_hash, []
).append(entry)

def _unindex_entry(self, entry_id: str) -> None:
"""Unindex an entry."""
Expand All @@ -1616,7 +1616,9 @@ def _unindex_entry(self, entry_id: str) -> None:
# Check type first to avoid expensive isinstance call
if type(unique_id) is not str and not isinstance(unique_id, Hashable): # noqa: E721
unique_id = str(entry.unique_id) # type: ignore[unreachable]
del self._domain_unique_id_index[domain][unique_id]
self._domain_unique_id_index[domain][unique_id].remove(entry)
if not self._domain_unique_id_index[domain][unique_id]:
del self._domain_unique_id_index[domain][unique_id]
if not self._domain_unique_id_index[domain]:
del self._domain_unique_id_index[domain]

Expand Down Expand Up @@ -1647,7 +1649,10 @@ def get_entry_by_domain_and_unique_id(
# Check type first to avoid expensive isinstance call
if type(unique_id) is not str and not isinstance(unique_id, Hashable): # noqa: E721
unique_id = str(unique_id) # type: ignore[unreachable]
return self._domain_unique_id_index.get(domain, {}).get(unique_id)
entries = self._domain_unique_id_index.get(domain, {}).get(unique_id)
if not entries:
return None
return entries[0]


class ConfigEntryStore(storage.Store[dict[str, list[dict[str, Any]]]]):
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
APPLICATION_NAME: Final = "HomeAssistant"
MAJOR_VERSION: Final = 2024
MINOR_VERSION: Final = 10
PATCH_VERSION: Final = "0"
PATCH_VERSION: Final = "1"
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 12, 0)
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "homeassistant"
version = "2024.10.0"
version = "2024.10.1"
license = {text = "Apache-2.0"}
description = "Open-source home automation platform running on Python 3."
readme = "README.rst"
Expand Down
8 changes: 4 additions & 4 deletions requirements_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ ProgettiHWSW==0.1.3
# PyBluez==0.22

# homeassistant.components.cast
PyChromecast==14.0.2
PyChromecast==14.0.1

# homeassistant.components.flick_electric
PyFlick==0.0.2
Expand Down Expand Up @@ -294,7 +294,7 @@ aiolookin==1.0.0
aiolyric==2.0.1

# homeassistant.components.mealie
aiomealie==0.9.2
aiomealie==0.9.3

# homeassistant.components.modern_forms
aiomodernforms==0.1.8
Expand Down Expand Up @@ -1324,7 +1324,7 @@ lw12==0.9.2
lxml==5.3.0

# homeassistant.components.matrix
matrix-nio==0.25.1
matrix-nio==0.25.2

# homeassistant.components.maxcube
maxcube-api==0.4.3
Expand Down Expand Up @@ -2244,7 +2244,7 @@ pysmarty2==0.10.1
pysml==0.0.12

# homeassistant.components.smlight
pysmlight==0.1.1
pysmlight==0.1.2

# homeassistant.components.snmp
pysnmp==6.2.6
Expand Down
8 changes: 4 additions & 4 deletions requirements_test_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ PlexAPI==4.15.16
ProgettiHWSW==0.1.3

# homeassistant.components.cast
PyChromecast==14.0.2
PyChromecast==14.0.1

# homeassistant.components.flick_electric
PyFlick==0.0.2
Expand Down Expand Up @@ -276,7 +276,7 @@ aiolookin==1.0.0
aiolyric==2.0.1

# homeassistant.components.mealie
aiomealie==0.9.2
aiomealie==0.9.3

# homeassistant.components.modern_forms
aiomodernforms==0.1.8
Expand Down Expand Up @@ -1099,7 +1099,7 @@ lupupy==0.3.2
lxml==5.3.0

# homeassistant.components.matrix
matrix-nio==0.25.1
matrix-nio==0.25.2

# homeassistant.components.maxcube
maxcube-api==0.4.3
Expand Down Expand Up @@ -1798,7 +1798,7 @@ pysmartthings==0.7.8
pysml==0.0.12

# homeassistant.components.smlight
pysmlight==0.1.1
pysmlight==0.1.2

# homeassistant.components.snmp
pysnmp==6.2.6
Expand Down
21 changes: 21 additions & 0 deletions tests/components/nyt_games/test_config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,27 @@ async def test_full_flow(
assert result["result"].unique_id == "218886794"


async def test_stripping_token(
hass: HomeAssistant,
mock_nyt_games_client: AsyncMock,
mock_setup_entry: AsyncMock,
) -> None:
"""Test stripping token."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_USER},
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"

result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_TOKEN: " token "},
)
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["data"] == {CONF_TOKEN: "token"}


@pytest.mark.parametrize(
("exception", "error"),
[
Expand Down
Loading

0 comments on commit 2182bc3

Please sign in to comment.