From bfdadd12e98ffd0c6b34c554dca3508fb04e2fc4 Mon Sep 17 00:00:00 2001 From: Michael <35783820+mib1185@users.noreply.github.com> Date: Wed, 13 Dec 2023 18:07:29 +0100 Subject: [PATCH 1/9] Ensure platform setup for all AVM FRITZ!SmartHome devices (#105515) --- .../components/fritzbox/binary_sensor.py | 10 ++++++---- homeassistant/components/fritzbox/button.py | 12 ++++++------ homeassistant/components/fritzbox/climate.py | 10 ++++++---- homeassistant/components/fritzbox/cover.py | 10 ++++++---- homeassistant/components/fritzbox/light.py | 17 ++++++++--------- homeassistant/components/fritzbox/sensor.py | 10 ++++++---- homeassistant/components/fritzbox/switch.py | 10 ++++++---- 7 files changed, 44 insertions(+), 35 deletions(-) diff --git a/homeassistant/components/fritzbox/binary_sensor.py b/homeassistant/components/fritzbox/binary_sensor.py index 2460635351e2d0..e766a53518aadb 100644 --- a/homeassistant/components/fritzbox/binary_sensor.py +++ b/homeassistant/components/fritzbox/binary_sensor.py @@ -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): diff --git a/homeassistant/components/fritzbox/button.py b/homeassistant/components/fritzbox/button.py index 732c41bfb7de13..e56ebc1e3b05d4 100644 --- a/homeassistant/components/fritzbox/button.py +++ b/homeassistant/components/fritzbox/button.py @@ -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): diff --git a/homeassistant/components/fritzbox/climate.py b/homeassistant/components/fritzbox/climate.py index 70359d9b2af761..6ce885a3fdbb32 100644 --- a/homeassistant/components/fritzbox/climate.py +++ b/homeassistant/components/fritzbox/climate.py @@ -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): diff --git a/homeassistant/components/fritzbox/cover.py b/homeassistant/components/fritzbox/cover.py index 7d27356fdf9fc1..d3d4c9080ea0dd 100644 --- a/homeassistant/components/fritzbox/cover.py +++ b/homeassistant/components/fritzbox/cover.py @@ -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): diff --git a/homeassistant/components/fritzbox/light.py b/homeassistant/components/fritzbox/light.py index 8dc51e5973826d..88d32fe33a56f4 100644 --- a/homeassistant/components/fritzbox/light.py +++ b/homeassistant/components/fritzbox/light.py @@ -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): diff --git a/homeassistant/components/fritzbox/sensor.py b/homeassistant/components/fritzbox/sensor.py index 1e5d7754934b19..fda8b239859f7d 100644 --- a/homeassistant/components/fritzbox/sensor.py +++ b/homeassistant/components/fritzbox/sensor.py @@ -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): diff --git a/homeassistant/components/fritzbox/switch.py b/homeassistant/components/fritzbox/switch.py index 617a5242c5bfca..4a2960a18ea298 100644 --- a/homeassistant/components/fritzbox/switch.py +++ b/homeassistant/components/fritzbox/switch.py @@ -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): From d5ecc55f894f0ca06055f08497f2aefe4c363acc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Thu, 14 Dec 2023 18:59:04 +0100 Subject: [PATCH 2/9] Update AEMET-OpenData to v0.4.7 (#105676) --- homeassistant/components/aemet/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/aemet/manifest.json b/homeassistant/components/aemet/manifest.json index 544931b50b596d..2bc30860803445 100644 --- a/homeassistant/components/aemet/manifest.json +++ b/homeassistant/components/aemet/manifest.json @@ -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"] } diff --git a/requirements_all.txt b/requirements_all.txt index 27a14e0b13771a..5a2307ce5a210c 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -4,7 +4,7 @@ -r requirements.txt # homeassistant.components.aemet -AEMET-OpenData==0.4.6 +AEMET-OpenData==0.4.7 # homeassistant.components.aladdin_connect AIOAladdinConnect==0.1.58 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index cae0417ff0a55c..ae4bbcc1f9547e 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -4,7 +4,7 @@ -r requirements_test.txt # homeassistant.components.aemet -AEMET-OpenData==0.4.6 +AEMET-OpenData==0.4.7 # homeassistant.components.aladdin_connect AIOAladdinConnect==0.1.58 From 3adda6b11024edff427a6522d2fb9e4c1b440db0 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Wed, 13 Dec 2023 22:35:41 +0100 Subject: [PATCH 3/9] Fix restoring UniFi clients with old unique id (#105691) Fix restoring UniFi clients with bad unique id --- homeassistant/components/unifi/controller.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/unifi/controller.py b/homeassistant/components/unifi/controller.py index 035cf66a9833e2..a941e836ae2382 100644 --- a/homeassistant/components/unifi/controller.py +++ b/homeassistant/components/unifi/controller.py @@ -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: From 73e234dfa5461918c2d785373eb17c6f14a9f21b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 13 Dec 2023 12:14:07 -1000 Subject: [PATCH 4/9] Bump zeroconf to 0.128.5 (#105694) --- homeassistant/components/zeroconf/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/zeroconf/manifest.json b/homeassistant/components/zeroconf/manifest.json index 6738431b304d0a..d78f33f0d91611 100644 --- a/homeassistant/components/zeroconf/manifest.json +++ b/homeassistant/components/zeroconf/manifest.json @@ -8,5 +8,5 @@ "iot_class": "local_push", "loggers": ["zeroconf"], "quality_scale": "internal", - "requirements": ["zeroconf==0.128.4"] + "requirements": ["zeroconf==0.128.5"] } diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 7d5f53f8f56a91..ba511ba7e408ca 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -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 diff --git a/requirements_all.txt b/requirements_all.txt index 5a2307ce5a210c..cbd2bdbf87bde0 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2810,7 +2810,7 @@ zamg==0.3.3 zengge==0.2 # homeassistant.components.zeroconf -zeroconf==0.128.4 +zeroconf==0.128.5 # homeassistant.components.zeversolar zeversolar==0.3.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index ae4bbcc1f9547e..d62114a8f6e0d9 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -2105,7 +2105,7 @@ yt-dlp==2023.11.16 zamg==0.3.3 # homeassistant.components.zeroconf -zeroconf==0.128.4 +zeroconf==0.128.5 # homeassistant.components.zeversolar zeversolar==0.3.1 From f8e92ddcb3c48904ede4febb90bc98894c1b8d2f Mon Sep 17 00:00:00 2001 From: Jan-Philipp Benecke Date: Thu, 14 Dec 2023 15:20:34 +0100 Subject: [PATCH 5/9] Add missing rest_command reload service to services.yaml (#105714) * Add missing rest_command reload service to services.yaml * Add missing strings.json * retrigger stale CI --- homeassistant/components/rest_command/services.yaml | 1 + homeassistant/components/rest_command/strings.json | 8 ++++++++ 2 files changed, 9 insertions(+) create mode 100644 homeassistant/components/rest_command/strings.json diff --git a/homeassistant/components/rest_command/services.yaml b/homeassistant/components/rest_command/services.yaml index e69de29bb2d1d6..c983a105c93977 100644 --- a/homeassistant/components/rest_command/services.yaml +++ b/homeassistant/components/rest_command/services.yaml @@ -0,0 +1 @@ +reload: diff --git a/homeassistant/components/rest_command/strings.json b/homeassistant/components/rest_command/strings.json new file mode 100644 index 00000000000000..15f59ec8e291a6 --- /dev/null +++ b/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." + } + } +} From 25bfe7ec82025dd0598d8252927182539dfd3a5e Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 14 Dec 2023 12:54:03 +0100 Subject: [PATCH 6/9] Fix issue clearing renault schedules (#105719) * Fix issue clearing renault schedules * Adjust --- .../components/renault/manifest.json | 2 +- homeassistant/components/renault/services.py | 16 ++++++++------ requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/renault/test_services.py | 22 +++++++++++++------ 5 files changed, 27 insertions(+), 17 deletions(-) diff --git a/homeassistant/components/renault/manifest.json b/homeassistant/components/renault/manifest.json index e5470259aa42ac..98e1c8b1e7c811 100644 --- a/homeassistant/components/renault/manifest.json +++ b/homeassistant/components/renault/manifest.json @@ -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"] } diff --git a/homeassistant/components/renault/services.py b/homeassistant/components/renault/services.py index d25b73cafc27c7..d2c7d45184479d 100644 --- a/homeassistant/components/renault/services.py +++ b/homeassistant/components/renault/services.py @@ -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( diff --git a/requirements_all.txt b/requirements_all.txt index cbd2bdbf87bde0..eb5034fdfd048b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2332,7 +2332,7 @@ raspyrfm-client==1.2.8 regenmaschine==2023.06.0 # homeassistant.components.renault -renault-api==0.2.0 +renault-api==0.2.1 # homeassistant.components.renson renson-endura-delta==1.6.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index d62114a8f6e0d9..593bea301cffe9 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1744,7 +1744,7 @@ rapt-ble==0.1.2 regenmaschine==2023.06.0 # homeassistant.components.renault -renault-api==0.2.0 +renault-api==0.2.1 # homeassistant.components.renson renson-endura-delta==1.6.0 diff --git a/tests/components/renault/test_services.py b/tests/components/renault/test_services.py index 58d51eca537155..7f5cb9a81846ad 100644 --- a/tests/components/renault/test_services.py +++ b/tests/components/renault/test_services.py @@ -203,13 +203,12 @@ async def test_service_set_charge_schedule_multi( { "id": 2, "activated": True, - "monday": {"startTime": "T12:00Z", "duration": 15}, - "tuesday": {"startTime": "T12:00Z", "duration": 15}, - "wednesday": {"startTime": "T12:00Z", "duration": 15}, - "thursday": {"startTime": "T12:00Z", "duration": 15}, - "friday": {"startTime": "T12:00Z", "duration": 15}, - "saturday": {"startTime": "T12:00Z", "duration": 15}, - "sunday": {"startTime": "T12:00Z", "duration": 15}, + "monday": {"startTime": "T12:00Z", "duration": 30}, + "tuesday": {"startTime": "T12:00Z", "duration": 30}, + "wednesday": None, + "friday": {"startTime": "T12:00Z", "duration": 30}, + "saturday": {"startTime": "T12:00Z", "duration": 30}, + "sunday": {"startTime": "T12:00Z", "duration": 30}, }, {"id": 3}, ] @@ -238,6 +237,15 @@ async def test_service_set_charge_schedule_multi( mock_call_data: list[ChargeSchedule] = mock_action.mock_calls[0][1][0] assert mock_action.mock_calls[0][1] == (mock_call_data,) + # Monday updated with new values + assert mock_call_data[1].monday.startTime == "T12:00Z" + assert mock_call_data[1].monday.duration == 30 + # Wednesday has original values cleared + assert mock_call_data[1].wednesday is None + # Thursday keeps original values + assert mock_call_data[1].thursday.startTime == "T23:30Z" + assert mock_call_data[1].thursday.duration == 15 + async def test_service_invalid_device_id( hass: HomeAssistant, config_entry: ConfigEntry From 4aa03b33f65e552c4639ced55e092ff6c6e1213d Mon Sep 17 00:00:00 2001 From: Charles Garwood Date: Thu, 14 Dec 2023 12:59:37 -0500 Subject: [PATCH 7/9] Fix Fully Kiosk Browser MQTT event callbacks with non-standard event topics (#105735) --- .../components/fully_kiosk/entity.py | 4 +++- tests/components/fully_kiosk/test_switch.py | 24 +++++++++++++++---- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/fully_kiosk/entity.py b/homeassistant/components/fully_kiosk/entity.py index 5fd9f75a6a06ff..b053508ae414ba 100644 --- a/homeassistant/components/fully_kiosk/entity.py +++ b/homeassistant/components/fully_kiosk/entity.py @@ -74,7 +74,8 @@ 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 = ( @@ -82,4 +83,5 @@ def message_callback(message: mqtt.ReceiveMessage) -> None: .replace("$event", event) .replace("$deviceId", data["deviceID"]) ) + return await mqtt.async_subscribe(self.hass, topic, message_callback) diff --git a/tests/components/fully_kiosk/test_switch.py b/tests/components/fully_kiosk/test_switch.py index 20b5ed11998cca..3c0874384c276f 100644 --- a/tests/components/fully_kiosk/test_switch.py +++ b/tests/components/fully_kiosk/test_switch.py @@ -107,19 +107,35 @@ async def test_switches_mqtt_update( assert entity assert entity.state == "on" - async_fire_mqtt_message(hass, "fully/event/onScreensaverStart/abcdef-123456", "{}") + async_fire_mqtt_message( + hass, + "fully/event/onScreensaverStart/abcdef-123456", + '{"deviceId": "abcdef-123456","event": "onScreensaverStart"}', + ) entity = hass.states.get("switch.amazon_fire_screensaver") assert entity.state == "on" - async_fire_mqtt_message(hass, "fully/event/onScreensaverStop/abcdef-123456", "{}") + async_fire_mqtt_message( + hass, + "fully/event/onScreensaverStop/abcdef-123456", + '{"deviceId": "abcdef-123456","event": "onScreensaverStop"}', + ) entity = hass.states.get("switch.amazon_fire_screensaver") assert entity.state == "off" - async_fire_mqtt_message(hass, "fully/event/screenOff/abcdef-123456", "{}") + async_fire_mqtt_message( + hass, + "fully/event/screenOff/abcdef-123456", + '{"deviceId": "abcdef-123456","event": "screenOff"}', + ) entity = hass.states.get("switch.amazon_fire_screen") assert entity.state == "off" - async_fire_mqtt_message(hass, "fully/event/screenOn/abcdef-123456", "{}") + async_fire_mqtt_message( + hass, + "fully/event/screenOn/abcdef-123456", + '{"deviceId": "abcdef-123456","event": "screenOn"}', + ) entity = hass.states.get("switch.amazon_fire_screen") assert entity.state == "on" From dbfc5ea8f96bde6cd165892f5a6a6f9a65731c76 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Thu, 14 Dec 2023 20:28:08 +0100 Subject: [PATCH 8/9] Disable user profiles on login screen (#105749) --- homeassistant/components/auth/login_flow.py | 21 ----------- homeassistant/components/person/__init__.py | 36 +++---------------- tests/components/auth/test_login_flow.py | 13 +------ tests/components/person/test_init.py | 39 ++------------------- 4 files changed, 7 insertions(+), 102 deletions(-) diff --git a/homeassistant/components/auth/login_flow.py b/homeassistant/components/auth/login_flow.py index 96255f59c7be11..9b96e57dbd38c8 100644 --- a/homeassistant/components/auth/login_flow.py +++ b/homeassistant/components/auth/login_flow.py @@ -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 @@ -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 @@ -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, } ) diff --git a/homeassistant/components/person/__init__.py b/homeassistant/components/person/__init__.py index b6f8b5b2db686e..c796cb8d843cca 100644 --- a/homeassistant/components/person/__init__.py +++ b/homeassistant/components/person/__init__.py @@ -2,7 +2,6 @@ from __future__ import annotations from http import HTTPStatus -from ipaddress import ip_address import logging from typing import Any @@ -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__) @@ -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", ) diff --git a/tests/components/auth/test_login_flow.py b/tests/components/auth/test_login_flow.py index 639bbb9a9cb600..27652ca2be4184 100644 --- a/tests/components/auth/test_login_flow.py +++ b/tests/components/auth/test_login_flow.py @@ -1,12 +1,10 @@ """Tests for the login flow.""" -from collections.abc import Callable from http import HTTPStatus from typing import Any from unittest.mock import patch import pytest -from homeassistant.auth.models import User from homeassistant.core import HomeAssistant from homeassistant.setup import async_setup_component @@ -67,22 +65,16 @@ async def _test_fetch_auth_providers_home_assistant( hass: HomeAssistant, aiohttp_client: ClientSessionGenerator, ip: str, - additional_expected_fn: Callable[[User], dict[str, Any]], ) -> None: """Test fetching auth providers for homeassistant auth provider.""" client = await async_setup_auth( hass, aiohttp_client, [{"type": "homeassistant"}], custom_ip=ip ) - provider = hass.auth.auth_providers[0] - credentials = await provider.async_get_or_create_credentials({"username": "hello"}) - user = await hass.auth.async_get_or_create_user(credentials) - expected = { "name": "Home Assistant Local", "type": "homeassistant", "id": None, - **additional_expected_fn(user), } resp = await client.get("/auth/providers") @@ -105,9 +97,7 @@ async def test_fetch_auth_providers_home_assistant_person_not_loaded( ip: str, ) -> None: """Test fetching auth providers for homeassistant auth provider, where person integration is not loaded.""" - await _test_fetch_auth_providers_home_assistant( - hass, aiohttp_client, ip, lambda _: {} - ) + await _test_fetch_auth_providers_home_assistant(hass, aiohttp_client, ip) @pytest.mark.parametrize( @@ -134,7 +124,6 @@ async def test_fetch_auth_providers_home_assistant_person_loaded( hass, aiohttp_client, ip, - lambda user: {"users": {user.id: user.name}} if is_local else {}, ) diff --git a/tests/components/person/test_init.py b/tests/components/person/test_init.py index 4d7781a095f74b..1866f682b5538f 100644 --- a/tests/components/person/test_init.py +++ b/tests/components/person/test_init.py @@ -1,5 +1,4 @@ """The tests for the person component.""" -from collections.abc import Callable from http import HTTPStatus from typing import Any from unittest.mock import patch @@ -31,7 +30,6 @@ from .conftest import DEVICE_TRACKER, DEVICE_TRACKER_2 from tests.common import MockUser, mock_component, mock_restore_cache -from tests.test_util import mock_real_ip from tests.typing import ClientSessionGenerator, WebSocketGenerator @@ -852,42 +850,10 @@ async def test_entities_in_person(hass: HomeAssistant) -> None: ] -@pytest.mark.parametrize( - ("ip", "status_code", "expected_fn"), - [ - ( - "192.168.0.10", - HTTPStatus.OK, - lambda user: { - user["user_id"]: {"name": user["name"], "picture": user["picture"]} - }, - ), - ( - "::ffff:192.168.0.10", - HTTPStatus.OK, - lambda user: { - user["user_id"]: {"name": user["name"], "picture": user["picture"]} - }, - ), - ( - "1.2.3.4", - HTTPStatus.BAD_REQUEST, - lambda _: {"code": "not_local", "message": "Not local"}, - ), - ( - "2001:db8::1", - HTTPStatus.BAD_REQUEST, - lambda _: {"code": "not_local", "message": "Not local"}, - ), - ], -) async def test_list_persons( hass: HomeAssistant, hass_client_no_auth: ClientSessionGenerator, hass_admin_user: MockUser, - ip: str, - status_code: HTTPStatus, - expected_fn: Callable[[dict[str, Any]], dict[str, Any]], ) -> None: """Test listing persons from a not local ip address.""" @@ -902,11 +868,10 @@ async def test_list_persons( assert await async_setup_component(hass, DOMAIN, config) await async_setup_component(hass, "api", {}) - mock_real_ip(hass.http.app)(ip) client = await hass_client_no_auth() resp = await client.get("/api/person/list") - assert resp.status == status_code + assert resp.status == HTTPStatus.BAD_REQUEST result = await resp.json() - assert result == expected_fn(admin) + assert result == {"code": "not_local", "message": "Not local"} From 07667a6aee92b6763607471be8fefd4ec9d3ff15 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Thu, 14 Dec 2023 20:37:09 +0100 Subject: [PATCH 9/9] Bump version to 2023.12.3 --- homeassistant/const.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index fcebd21eafdc1a..e4c6c2ed86a90f 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -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) diff --git a/pyproject.toml b/pyproject.toml index 7b06c7a6506972..c39c23819f4a4a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "homeassistant" -version = "2023.12.2" +version = "2023.12.3" license = {text = "Apache-2.0"} description = "Open-source home automation platform running on Python 3." readme = "README.rst"