Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2023.12.3 #105757

Merged
merged 9 commits into from Dec 14, 2023
2 changes: 1 addition & 1 deletion homeassistant/components/aemet/manifest.json
Expand Up @@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/aemet",
"iot_class": "cloud_polling",
"loggers": ["aemet_opendata"],
"requirements": ["AEMET-OpenData==0.4.6"]
"requirements": ["AEMET-OpenData==0.4.7"]
}
21 changes: 0 additions & 21 deletions homeassistant/components/auth/login_flow.py
Expand Up @@ -91,7 +91,6 @@
from homeassistant.components.http.view import HomeAssistantView
from homeassistant.core import HomeAssistant
from homeassistant.helpers.network import is_cloud_connection
from homeassistant.util.network import is_local

from . import indieauth

Expand Down Expand Up @@ -165,8 +164,6 @@ async def get(self, request: web.Request) -> web.Response:

providers = []
for provider in hass.auth.auth_providers:
additional_data = {}

if provider.type == "trusted_networks":
if cloud_connection:
# Skip quickly as trusted networks are not available on cloud
Expand All @@ -179,30 +176,12 @@ async def get(self, request: web.Request) -> web.Response:
except InvalidAuthError:
# Not a trusted network, so we don't expose that trusted_network authenticator is setup
continue
elif (
provider.type == "homeassistant"
and not cloud_connection
and is_local(remote_address)
and "person" in hass.config.components
):
# We are local, return user id and username
users = await provider.store.async_get_users()
additional_data["users"] = {
user.id: credentials.data["username"]
for user in users
for credentials in user.credentials
if (
credentials.auth_provider_type == provider.type
and credentials.auth_provider_id == provider.id
)
}

providers.append(
{
"name": provider.name,
"id": provider.id,
"type": provider.type,
**additional_data,
}
)

Expand Down
10 changes: 6 additions & 4 deletions homeassistant/components/fritzbox/binary_sensor.py
Expand Up @@ -70,20 +70,22 @@ async def async_setup_entry(
coordinator = get_coordinator(hass, entry.entry_id)

@callback
def _add_entities() -> None:
def _add_entities(devices: set[str] | None = None) -> None:
"""Add devices."""
if not coordinator.new_devices:
if devices is None:
devices = coordinator.new_devices
if not devices:
return
async_add_entities(
FritzboxBinarySensor(coordinator, ain, description)
for ain in coordinator.new_devices
for ain in devices
for description in BINARY_SENSOR_TYPES
if description.suitable(coordinator.data.devices[ain])
)

entry.async_on_unload(coordinator.async_add_listener(_add_entities))

_add_entities()
_add_entities(set(coordinator.data.devices.keys()))


class FritzboxBinarySensor(FritzBoxDeviceEntity, BinarySensorEntity):
Expand Down
12 changes: 6 additions & 6 deletions homeassistant/components/fritzbox/button.py
Expand Up @@ -19,17 +19,17 @@ async def async_setup_entry(
coordinator = get_coordinator(hass, entry.entry_id)

@callback
def _add_entities() -> None:
def _add_entities(templates: set[str] | None = None) -> None:
"""Add templates."""
if not coordinator.new_templates:
if templates is None:
templates = coordinator.new_templates
if not templates:
return
async_add_entities(
FritzBoxTemplate(coordinator, ain) for ain in coordinator.new_templates
)
async_add_entities(FritzBoxTemplate(coordinator, ain) for ain in templates)

entry.async_on_unload(coordinator.async_add_listener(_add_entities))

_add_entities()
_add_entities(set(coordinator.data.templates.keys()))


class FritzBoxTemplate(FritzBoxEntity, ButtonEntity):
Expand Down
10 changes: 6 additions & 4 deletions homeassistant/components/fritzbox/climate.py
Expand Up @@ -52,19 +52,21 @@ async def async_setup_entry(
coordinator = get_coordinator(hass, entry.entry_id)

@callback
def _add_entities() -> None:
def _add_entities(devices: set[str] | None = None) -> None:
"""Add devices."""
if not coordinator.new_devices:
if devices is None:
devices = coordinator.new_devices
if not devices:
return
async_add_entities(
FritzboxThermostat(coordinator, ain)
for ain in coordinator.new_devices
for ain in devices
if coordinator.data.devices[ain].has_thermostat
)

entry.async_on_unload(coordinator.async_add_listener(_add_entities))

_add_entities()
_add_entities(set(coordinator.data.devices.keys()))


class FritzboxThermostat(FritzBoxDeviceEntity, ClimateEntity):
Expand Down
10 changes: 6 additions & 4 deletions homeassistant/components/fritzbox/cover.py
Expand Up @@ -24,19 +24,21 @@ async def async_setup_entry(
coordinator = get_coordinator(hass, entry.entry_id)

@callback
def _add_entities() -> None:
def _add_entities(devices: set[str] | None = None) -> None:
"""Add devices."""
if not coordinator.new_devices:
if devices is None:
devices = coordinator.new_devices
if not devices:
return
async_add_entities(
FritzboxCover(coordinator, ain)
for ain in coordinator.new_devices
for ain in devices
if coordinator.data.devices[ain].has_blind
)

entry.async_on_unload(coordinator.async_add_listener(_add_entities))

_add_entities()
_add_entities(set(coordinator.data.devices.keys()))


class FritzboxCover(FritzBoxDeviceEntity, CoverEntity):
Expand Down
17 changes: 8 additions & 9 deletions homeassistant/components/fritzbox/light.py
Expand Up @@ -30,22 +30,21 @@ async def async_setup_entry(
coordinator = get_coordinator(hass, entry.entry_id)

@callback
def _add_entities() -> None:
def _add_entities(devices: set[str] | None = None) -> None:
"""Add devices."""
if not coordinator.new_devices:
if devices is None:
devices = coordinator.new_devices
if not devices:
return
async_add_entities(
FritzboxLight(
coordinator,
ain,
)
for ain in coordinator.new_devices
if (coordinator.data.devices[ain]).has_lightbulb
FritzboxLight(coordinator, ain)
for ain in devices
if coordinator.data.devices[ain].has_lightbulb
)

entry.async_on_unload(coordinator.async_add_listener(_add_entities))

_add_entities()
_add_entities(set(coordinator.data.devices.keys()))


class FritzboxLight(FritzBoxDeviceEntity, LightEntity):
Expand Down
10 changes: 6 additions & 4 deletions homeassistant/components/fritzbox/sensor.py
Expand Up @@ -215,20 +215,22 @@ async def async_setup_entry(
coordinator = get_coordinator(hass, entry.entry_id)

@callback
def _add_entities() -> None:
def _add_entities(devices: set[str] | None = None) -> None:
"""Add devices."""
if not coordinator.new_devices:
if devices is None:
devices = coordinator.new_devices
if not devices:
return
async_add_entities(
FritzBoxSensor(coordinator, ain, description)
for ain in coordinator.new_devices
for ain in devices
for description in SENSOR_TYPES
if description.suitable(coordinator.data.devices[ain])
)

entry.async_on_unload(coordinator.async_add_listener(_add_entities))

_add_entities()
_add_entities(set(coordinator.data.devices.keys()))


class FritzBoxSensor(FritzBoxDeviceEntity, SensorEntity):
Expand Down
10 changes: 6 additions & 4 deletions homeassistant/components/fritzbox/switch.py
Expand Up @@ -19,19 +19,21 @@ async def async_setup_entry(
coordinator = get_coordinator(hass, entry.entry_id)

@callback
def _add_entities() -> None:
def _add_entities(devices: set[str] | None = None) -> None:
"""Add devices."""
if not coordinator.new_devices:
if devices is None:
devices = coordinator.new_devices
if not devices:
return
async_add_entities(
FritzboxSwitch(coordinator, ain)
for ain in coordinator.new_devices
for ain in devices
if coordinator.data.devices[ain].has_switch
)

entry.async_on_unload(coordinator.async_add_listener(_add_entities))

_add_entities()
_add_entities(set(coordinator.data.devices.keys()))


class FritzboxSwitch(FritzBoxDeviceEntity, SwitchEntity):
Expand Down
4 changes: 3 additions & 1 deletion homeassistant/components/fully_kiosk/entity.py
Expand Up @@ -74,12 +74,14 @@ async def mqtt_subscribe(
@callback
def message_callback(message: mqtt.ReceiveMessage) -> None:
payload = json.loads(message.payload)
event_callback(**payload)
if "event" in payload and payload["event"] == event:
event_callback(**payload)

topic_template = data["settings"]["mqttEventTopic"]
topic = (
topic_template.replace("$appId", "fully")
.replace("$event", event)
.replace("$deviceId", data["deviceID"])
)

return await mqtt.async_subscribe(self.hass, topic, message_callback)
36 changes: 4 additions & 32 deletions homeassistant/components/person/__init__.py
Expand Up @@ -2,7 +2,6 @@
from __future__ import annotations

from http import HTTPStatus
from ipaddress import ip_address
import logging
from typing import Any

Expand Down Expand Up @@ -51,12 +50,10 @@
)
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.event import async_track_state_change_event
from homeassistant.helpers.network import is_cloud_connection
from homeassistant.helpers.restore_state import RestoreEntity
from homeassistant.helpers.storage import Store
from homeassistant.helpers.typing import ConfigType
from homeassistant.loader import bind_hass
from homeassistant.util.network import is_local

_LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -588,33 +585,8 @@ class ListPersonsView(HomeAssistantView):

async def get(self, request: web.Request) -> web.Response:
"""Return a list of persons if request comes from a local IP."""
try:
remote_address = ip_address(request.remote) # type: ignore[arg-type]
except ValueError:
return self.json_message(
message="Invalid remote IP",
status_code=HTTPStatus.BAD_REQUEST,
message_code="invalid_remote_ip",
)

hass: HomeAssistant = request.app["hass"]
if is_cloud_connection(hass) or not is_local(remote_address):
return self.json_message(
message="Not local",
status_code=HTTPStatus.BAD_REQUEST,
message_code="not_local",
)

yaml, storage, _ = hass.data[DOMAIN]
persons = [*yaml.async_items(), *storage.async_items()]

return self.json(
{
person[ATTR_USER_ID]: {
ATTR_NAME: person[ATTR_NAME],
CONF_PICTURE: person.get(CONF_PICTURE),
}
for person in persons
if person.get(ATTR_USER_ID)
}
return self.json_message(
message="Not local",
status_code=HTTPStatus.BAD_REQUEST,
message_code="not_local",
)
2 changes: 1 addition & 1 deletion homeassistant/components/renault/manifest.json
Expand Up @@ -8,5 +8,5 @@
"iot_class": "cloud_polling",
"loggers": ["renault_api"],
"quality_scale": "platinum",
"requirements": ["renault-api==0.2.0"]
"requirements": ["renault-api==0.2.1"]
}
16 changes: 9 additions & 7 deletions homeassistant/components/renault/services.py
Expand Up @@ -43,13 +43,15 @@
{
vol.Required("id"): cv.positive_int,
vol.Optional("activated"): cv.boolean,
vol.Optional("monday"): vol.Schema(SERVICE_CHARGE_SET_SCHEDULE_DAY_SCHEMA),
vol.Optional("tuesday"): vol.Schema(SERVICE_CHARGE_SET_SCHEDULE_DAY_SCHEMA),
vol.Optional("wednesday"): vol.Schema(SERVICE_CHARGE_SET_SCHEDULE_DAY_SCHEMA),
vol.Optional("thursday"): vol.Schema(SERVICE_CHARGE_SET_SCHEDULE_DAY_SCHEMA),
vol.Optional("friday"): vol.Schema(SERVICE_CHARGE_SET_SCHEDULE_DAY_SCHEMA),
vol.Optional("saturday"): vol.Schema(SERVICE_CHARGE_SET_SCHEDULE_DAY_SCHEMA),
vol.Optional("sunday"): vol.Schema(SERVICE_CHARGE_SET_SCHEDULE_DAY_SCHEMA),
vol.Optional("monday"): vol.Any(None, SERVICE_CHARGE_SET_SCHEDULE_DAY_SCHEMA),
vol.Optional("tuesday"): vol.Any(None, SERVICE_CHARGE_SET_SCHEDULE_DAY_SCHEMA),
vol.Optional("wednesday"): vol.Any(
None, SERVICE_CHARGE_SET_SCHEDULE_DAY_SCHEMA
),
vol.Optional("thursday"): vol.Any(None, SERVICE_CHARGE_SET_SCHEDULE_DAY_SCHEMA),
vol.Optional("friday"): vol.Any(None, SERVICE_CHARGE_SET_SCHEDULE_DAY_SCHEMA),
vol.Optional("saturday"): vol.Any(None, SERVICE_CHARGE_SET_SCHEDULE_DAY_SCHEMA),
vol.Optional("sunday"): vol.Any(None, SERVICE_CHARGE_SET_SCHEDULE_DAY_SCHEMA),
}
)
SERVICE_CHARGE_SET_SCHEDULES_SCHEMA = SERVICE_VEHICLE_SCHEMA.extend(
Expand Down
1 change: 1 addition & 0 deletions homeassistant/components/rest_command/services.yaml
@@ -0,0 +1 @@
reload:
8 changes: 8 additions & 0 deletions homeassistant/components/rest_command/strings.json
@@ -0,0 +1,8 @@
{
"services": {
"reload": {
"name": "[%key:common::action::reload%]",
"description": "Reloads RESTful commands from the YAML-configuration."
}
}
}
4 changes: 2 additions & 2 deletions homeassistant/components/unifi/controller.py
Expand Up @@ -260,8 +260,8 @@ async def initialize(self) -> None:
for entry in async_entries_for_config_entry(
entity_registry, self.config_entry.entry_id
):
if entry.domain == Platform.DEVICE_TRACKER:
macs.append(entry.unique_id.split("-", 1)[0])
if entry.domain == Platform.DEVICE_TRACKER and "-" in entry.unique_id:
macs.append(entry.unique_id.split("-", 1)[1])

for mac in self.option_supported_clients + self.option_block_clients + macs:
if mac not in self.api.clients and mac in self.api.clients_all:
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/zeroconf/manifest.json
Expand Up @@ -8,5 +8,5 @@
"iot_class": "local_push",
"loggers": ["zeroconf"],
"quality_scale": "internal",
"requirements": ["zeroconf==0.128.4"]
"requirements": ["zeroconf==0.128.5"]
}
2 changes: 1 addition & 1 deletion homeassistant/const.py
Expand Up @@ -7,7 +7,7 @@
APPLICATION_NAME: Final = "HomeAssistant"
MAJOR_VERSION: Final = 2023
MINOR_VERSION: Final = 12
PATCH_VERSION: Final = "2"
PATCH_VERSION: Final = "3"
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 11, 0)
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/package_constraints.txt
Expand Up @@ -57,7 +57,7 @@ voluptuous-serialize==2.6.0
voluptuous==0.13.1
webrtc-noise-gain==1.2.3
yarl==1.9.2
zeroconf==0.128.4
zeroconf==0.128.5

# Constrain pycryptodome to avoid vulnerability
# see https://github.com/home-assistant/core/pull/16238
Expand Down