Create Home Connect active and selected program entities only when there are programs (#143185)

* Create active and selected program entities only when there are programs

* Test improvements
This commit is contained in:
J. Diego Rodríguez Royo 2025-04-18 00:09:52 +02:00 committed by GitHub
parent e7994b3da1
commit 60293648dc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 265 additions and 78 deletions

View File

@ -252,9 +252,7 @@ class HomeConnectCoordinator(
appliance_data = await self._get_appliance_data(
appliance_info, self.data.get(appliance_info.ha_id)
)
if event_message_ha_id in self.data:
self.data[event_message_ha_id].update(appliance_data)
else:
if event_message_ha_id not in self.data:
self.data[event_message_ha_id] = appliance_data
for listener, context in self._special_listeners.values():
if (

View File

@ -17,7 +17,6 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .common import setup_home_connect_entry
from .const import (
APPLIANCES_WITH_PROGRAMS,
AVAILABLE_MAPS_ENUM,
BEAN_AMOUNT_OPTIONS,
BEAN_CONTAINER_OPTIONS,
@ -313,7 +312,7 @@ def _get_entities_for_appliance(
HomeConnectProgramSelectEntity(entry.runtime_data, appliance, desc)
for desc in PROGRAM_SELECT_ENTITY_DESCRIPTIONS
]
if appliance.info.type in APPLIANCES_WITH_PROGRAMS
if appliance.programs
else []
),
*[

View File

@ -181,5 +181,29 @@
}
]
}
},
"Hood": {
"data": {
"programs": [
{
"key": "Cooking.Common.Program.Hood.Automatic",
"constraints": {
"execution": "selectandstart"
}
},
{
"key": "Cooking.Common.Program.Hood.Venting",
"constraints": {
"execution": "selectandstart"
}
},
{
"key": "Cooking.Common.Program.Hood.DelayedShutOff",
"constraints": {
"execution": "selectandstart"
}
}
]
}
}
}

View File

@ -90,6 +90,9 @@
'ha_id': 'BOSCH-HCS000000-D00000000004',
'name': 'Hood',
'programs': list([
'Cooking.Common.Program.Hood.Automatic',
'Cooking.Common.Program.Hood.Venting',
'Cooking.Common.Program.Hood.DelayedShutOff',
]),
'settings': dict({
'BSH.Common.Setting.AmbientLightBrightness': 70,

View File

@ -10,6 +10,7 @@ from aiohomeconnect.model import (
EventMessage,
EventType,
HomeAppliance,
StatusKey,
)
from aiohomeconnect.model.error import HomeConnectApiError
import pytest
@ -105,9 +106,19 @@ async def test_paired_depaired_devices_flow(
assert entity_registry.async_get(entity_entry.entity_id)
@pytest.mark.parametrize("appliance", ["Washer"], indirect=True)
@pytest.mark.parametrize(
("appliance", "keys_to_check"),
[
(
"Washer",
(StatusKey.BSH_COMMON_REMOTE_CONTROL_ACTIVE,),
)
],
indirect=["appliance"],
)
async def test_connected_devices(
appliance: HomeAppliance,
keys_to_check: tuple,
hass: HomeAssistant,
config_entry: MockConfigEntry,
integration_setup: Callable[[MagicMock], Awaitable[bool]],
@ -138,7 +149,17 @@ async def test_connected_devices(
device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)})
assert device
entity_entries = entity_registry.entities.get_entries_for_device_id(device.id)
assert entity_registry.async_get_entity_id(
Platform.BINARY_SENSOR,
DOMAIN,
f"{appliance.ha_id}-{EventKey.BSH_COMMON_APPLIANCE_CONNECTED}",
)
for key in keys_to_check:
assert not entity_registry.async_get_entity_id(
Platform.BINARY_SENSOR,
DOMAIN,
f"{appliance.ha_id}-{key}",
)
await client.add_events(
[
@ -151,10 +172,12 @@ async def test_connected_devices(
)
await hass.async_block_till_done()
device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)})
assert device
new_entity_entries = entity_registry.entities.get_entries_for_device_id(device.id)
assert len(new_entity_entries) > len(entity_entries)
for key in (*keys_to_check, EventKey.BSH_COMMON_APPLIANCE_CONNECTED):
assert entity_registry.async_get_entity_id(
Platform.BINARY_SENSOR,
DOMAIN,
f"{appliance.ha_id}-{key}",
)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")

View File

@ -99,9 +99,19 @@ async def test_paired_depaired_devices_flow(
assert entity_registry.async_get(entity_entry.entity_id)
@pytest.mark.parametrize("appliance", ["Washer"], indirect=True)
@pytest.mark.parametrize(
("appliance", "keys_to_check"),
[
(
"Washer",
(CommandKey.BSH_COMMON_PAUSE_PROGRAM,),
)
],
indirect=["appliance"],
)
async def test_connected_devices(
appliance: HomeAppliance,
keys_to_check: tuple,
hass: HomeAssistant,
config_entry: MockConfigEntry,
integration_setup: Callable[[MagicMock], Awaitable[bool]],
@ -116,7 +126,7 @@ async def test_connected_devices(
not be obtained while disconnected and once connected, the entities are added.
"""
get_available_commands_original_mock = client.get_available_commands
get_available_programs_mock = client.get_available_programs
get_all_programs_mock = client.get_all_programs
async def get_available_commands_side_effect(ha_id: str):
if ha_id == appliance.ha_id:
@ -125,28 +135,36 @@ async def test_connected_devices(
)
return await get_available_commands_original_mock.side_effect(ha_id)
async def get_available_programs_side_effect(ha_id: str):
async def get_all_programs_side_effect(ha_id: str):
if ha_id == appliance.ha_id:
raise HomeConnectApiError(
"SDK.Error.HomeAppliance.Connection.Initialization.Failed"
)
return await get_available_programs_mock.side_effect(ha_id)
return await get_all_programs_mock.side_effect(ha_id)
client.get_available_commands = AsyncMock(
side_effect=get_available_commands_side_effect
)
client.get_available_programs = AsyncMock(
side_effect=get_available_programs_side_effect
)
client.get_all_programs = AsyncMock(side_effect=get_all_programs_side_effect)
assert config_entry.state == ConfigEntryState.NOT_LOADED
assert await integration_setup(client)
assert config_entry.state == ConfigEntryState.LOADED
client.get_available_commands = get_available_commands_original_mock
client.get_available_programs = get_available_programs_mock
client.get_all_programs = get_all_programs_mock
device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)})
assert device
entity_entries = entity_registry.entities.get_entries_for_device_id(device.id)
assert entity_registry.async_get_entity_id(
Platform.BUTTON,
DOMAIN,
f"{appliance.ha_id}-StopProgram",
)
for key in keys_to_check:
assert not entity_registry.async_get_entity_id(
Platform.BUTTON,
DOMAIN,
f"{appliance.ha_id}-{key}",
)
await client.add_events(
[
@ -159,10 +177,12 @@ async def test_connected_devices(
)
await hass.async_block_till_done()
device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)})
assert device
new_entity_entries = entity_registry.entities.get_entries_for_device_id(device.id)
assert len(new_entity_entries) > len(entity_entries)
for key in (*keys_to_check, "StopProgram"):
assert entity_registry.async_get_entity_id(
Platform.BUTTON,
DOMAIN,
f"{appliance.ha_id}-{key}",
)
@pytest.mark.parametrize("appliance", ["Washer"], indirect=True)

View File

@ -119,9 +119,19 @@ async def test_paired_depaired_devices_flow(
assert entity_registry.async_get(entity_entry.entity_id)
@pytest.mark.parametrize("appliance", ["Hood"], indirect=True)
@pytest.mark.parametrize(
("appliance", "keys_to_check"),
[
(
"Hood",
(SettingKey.COOKING_COMMON_LIGHTING,),
)
],
indirect=["appliance"],
)
async def test_connected_devices(
appliance: HomeAppliance,
keys_to_check: tuple,
hass: HomeAssistant,
config_entry: MockConfigEntry,
integration_setup: Callable[[MagicMock], Awaitable[bool]],
@ -136,7 +146,6 @@ async def test_connected_devices(
not be obtained while disconnected and once connected, the entities are added.
"""
get_settings_original_mock = client.get_settings
get_available_programs_mock = client.get_available_programs
async def get_settings_side_effect(ha_id: str):
if ha_id == appliance.ha_id:
@ -145,26 +154,20 @@ async def test_connected_devices(
)
return await get_settings_original_mock.side_effect(ha_id)
async def get_available_programs_side_effect(ha_id: str):
if ha_id == appliance.ha_id:
raise HomeConnectApiError(
"SDK.Error.HomeAppliance.Connection.Initialization.Failed"
)
return await get_available_programs_mock.side_effect(ha_id)
client.get_settings = AsyncMock(side_effect=get_settings_side_effect)
client.get_available_programs = AsyncMock(
side_effect=get_available_programs_side_effect
)
assert config_entry.state == ConfigEntryState.NOT_LOADED
assert await integration_setup(client)
assert config_entry.state == ConfigEntryState.LOADED
client.get_settings = get_settings_original_mock
client.get_available_programs = get_available_programs_mock
device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)})
assert device
entity_entries = entity_registry.entities.get_entries_for_device_id(device.id)
for key in keys_to_check:
assert not entity_registry.async_get_entity_id(
Platform.LIGHT,
DOMAIN,
f"{appliance.ha_id}-{key}",
)
await client.add_events(
[
@ -177,10 +180,12 @@ async def test_connected_devices(
)
await hass.async_block_till_done()
device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)})
assert device
new_entity_entries = entity_registry.entities.get_entries_for_device_id(device.id)
assert len(new_entity_entries) > len(entity_entries)
for key in keys_to_check:
assert entity_registry.async_get_entity_id(
Platform.LIGHT,
DOMAIN,
f"{appliance.ha_id}-{key}",
)
@pytest.mark.parametrize("appliance", ["Hood"], indirect=True)

View File

@ -135,9 +135,21 @@ async def test_paired_depaired_devices_flow(
assert entity_registry.async_get(entity_entry.entity_id)
@pytest.mark.parametrize("appliance", ["FridgeFreezer"], indirect=True)
@pytest.mark.parametrize(
("appliance", "keys_to_check"),
[
(
"FridgeFreezer",
(
SettingKey.REFRIGERATION_FRIDGE_FREEZER_SETPOINT_TEMPERATURE_REFRIGERATOR,
),
)
],
indirect=["appliance"],
)
async def test_connected_devices(
appliance: HomeAppliance,
keys_to_check: tuple,
hass: HomeAssistant,
config_entry: MockConfigEntry,
integration_setup: Callable[[MagicMock], Awaitable[bool]],
@ -168,7 +180,12 @@ async def test_connected_devices(
device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)})
assert device
entity_entries = entity_registry.entities.get_entries_for_device_id(device.id)
for key in keys_to_check:
assert not entity_registry.async_get_entity_id(
Platform.NUMBER,
DOMAIN,
f"{appliance.ha_id}-{key}",
)
await client.add_events(
[
@ -181,10 +198,12 @@ async def test_connected_devices(
)
await hass.async_block_till_done()
device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)})
assert device
new_entity_entries = entity_registry.entities.get_entries_for_device_id(device.id)
assert len(new_entity_entries) > len(entity_entries)
for key in keys_to_check:
assert entity_registry.async_get_entity_id(
Platform.NUMBER,
DOMAIN,
f"{appliance.ha_id}-{key}",
)
@pytest.mark.parametrize("appliance", ["FridgeFreezer"], indirect=True)

View File

@ -20,6 +20,7 @@ from aiohomeconnect.model import (
)
from aiohomeconnect.model.error import (
ActiveProgramNotSetError,
HomeConnectApiError,
HomeConnectError,
SelectedProgramNotSetError,
TooManyRequestsError,
@ -138,9 +139,23 @@ async def test_paired_depaired_devices_flow(
assert entity_registry.async_get(entity_entry.entity_id)
@pytest.mark.parametrize("appliance", ["Washer"], indirect=True)
@pytest.mark.parametrize(
("appliance", "keys_to_check"),
[
(
"Hood",
(
EventKey.BSH_COMMON_ROOT_ACTIVE_PROGRAM,
EventKey.BSH_COMMON_ROOT_SELECTED_PROGRAM,
SettingKey.COOKING_HOOD_COLOR_TEMPERATURE,
),
)
],
indirect=["appliance"],
)
async def test_connected_devices(
appliance: HomeAppliance,
keys_to_check: tuple,
hass: HomeAssistant,
config_entry: MockConfigEntry,
integration_setup: Callable[[MagicMock], Awaitable[bool]],
@ -154,13 +169,39 @@ async def test_connected_devices(
Specifically those devices whose settings, status, etc. could
not be obtained while disconnected and once connected, the entities are added.
"""
get_settings_original_mock = client.get_settings
get_all_programs_mock = client.get_all_programs
async def get_settings_side_effect(ha_id: str):
if ha_id == appliance.ha_id:
raise HomeConnectApiError(
"SDK.Error.HomeAppliance.Connection.Initialization.Failed"
)
return await get_settings_original_mock.side_effect(ha_id)
async def get_all_programs_side_effect(ha_id: str):
if ha_id == appliance.ha_id:
raise HomeConnectApiError(
"SDK.Error.HomeAppliance.Connection.Initialization.Failed"
)
return await get_all_programs_mock.side_effect(ha_id)
client.get_settings = AsyncMock(side_effect=get_settings_side_effect)
client.get_all_programs = AsyncMock(side_effect=get_all_programs_side_effect)
assert config_entry.state == ConfigEntryState.NOT_LOADED
assert await integration_setup(client)
assert config_entry.state == ConfigEntryState.LOADED
client.get_settings = get_settings_original_mock
client.get_all_programs = get_all_programs_mock
device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)})
assert device
for key in keys_to_check:
assert not entity_registry.async_get_entity_id(
Platform.SELECT,
DOMAIN,
f"{appliance.ha_id}-{key}",
)
await client.add_events(
[
@ -173,10 +214,12 @@ async def test_connected_devices(
)
await hass.async_block_till_done()
device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)})
assert device
entity_entries = entity_registry.entities.get_entries_for_device_id(device.id)
assert entity_entries
for key in keys_to_check:
assert entity_registry.async_get_entity_id(
Platform.SELECT,
DOMAIN,
f"{appliance.ha_id}-{key}",
)
@pytest.mark.parametrize("appliance", ["Washer"], indirect=True)

View File

@ -178,9 +178,19 @@ async def test_paired_depaired_devices_flow(
assert hass.states.is_state("sensor.washer_poor_i_dos_1_fill_level", "present")
@pytest.mark.parametrize("appliance", ["Washer"], indirect=True)
@pytest.mark.parametrize(
("appliance", "keys_to_check"),
[
(
"Washer",
(StatusKey.BSH_COMMON_OPERATION_STATE,),
)
],
indirect=["appliance"],
)
async def test_connected_devices(
appliance: HomeAppliance,
keys_to_check: tuple,
hass: HomeAssistant,
config_entry: MockConfigEntry,
integration_setup: Callable[[MagicMock], Awaitable[bool]],
@ -211,7 +221,12 @@ async def test_connected_devices(
device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)})
assert device
entity_entries = entity_registry.entities.get_entries_for_device_id(device.id)
for key in keys_to_check:
assert not entity_registry.async_get_entity_id(
Platform.SENSOR,
DOMAIN,
f"{appliance.ha_id}-{key}",
)
await client.add_events(
[
@ -224,10 +239,12 @@ async def test_connected_devices(
)
await hass.async_block_till_done()
device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)})
assert device
new_entity_entries = entity_registry.entities.get_entries_for_device_id(device.id)
assert len(new_entity_entries) > len(entity_entries)
for key in keys_to_check:
assert entity_registry.async_get_entity_id(
Platform.SENSOR,
DOMAIN,
f"{appliance.ha_id}-{key}",
)
@pytest.mark.parametrize("appliance", [TEST_HC_APP], indirect=True)

View File

@ -147,9 +147,23 @@ async def test_paired_depaired_devices_flow(
assert entity_registry.async_get(entity_entry.entity_id)
@pytest.mark.parametrize("appliance", ["Washer"], indirect=True)
@pytest.mark.parametrize(
("appliance", "keys_to_check"),
[
(
"Washer",
(
SettingKey.BSH_COMMON_POWER_STATE,
SettingKey.BSH_COMMON_CHILD_LOCK,
"Program Cotton",
),
)
],
indirect=["appliance"],
)
async def test_connected_devices(
appliance: HomeAppliance,
keys_to_check: tuple,
hass: HomeAssistant,
config_entry: MockConfigEntry,
integration_setup: Callable[[MagicMock], Awaitable[bool]],
@ -164,7 +178,7 @@ async def test_connected_devices(
not be obtained while disconnected and once connected, the entities are added.
"""
get_settings_original_mock = client.get_settings
get_available_programs_mock = client.get_available_programs
get_all_programs_mock = client.get_all_programs
async def get_settings_side_effect(ha_id: str):
if ha_id == appliance.ha_id:
@ -173,26 +187,29 @@ async def test_connected_devices(
)
return await get_settings_original_mock.side_effect(ha_id)
async def get_available_programs_side_effect(ha_id: str):
async def get_all_programs_side_effect(ha_id: str):
if ha_id == appliance.ha_id:
raise HomeConnectApiError(
"SDK.Error.HomeAppliance.Connection.Initialization.Failed"
)
return await get_available_programs_mock.side_effect(ha_id)
return await get_all_programs_mock.side_effect(ha_id)
client.get_settings = AsyncMock(side_effect=get_settings_side_effect)
client.get_available_programs = AsyncMock(
side_effect=get_available_programs_side_effect
)
client.get_all_programs = AsyncMock(side_effect=get_all_programs_side_effect)
assert config_entry.state == ConfigEntryState.NOT_LOADED
assert await integration_setup(client)
assert config_entry.state == ConfigEntryState.LOADED
client.get_settings = get_settings_original_mock
client.get_available_programs = get_available_programs_mock
client.get_all_programs = get_all_programs_mock
device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)})
assert device
entity_entries = entity_registry.entities.get_entries_for_device_id(device.id)
for key in keys_to_check:
assert not entity_registry.async_get_entity_id(
Platform.SWITCH,
DOMAIN,
f"{appliance.ha_id}-{key}",
)
await client.add_events(
[
@ -205,10 +222,12 @@ async def test_connected_devices(
)
await hass.async_block_till_done()
device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)})
assert device
new_entity_entries = entity_registry.entities.get_entries_for_device_id(device.id)
assert len(new_entity_entries) > len(entity_entries)
for key in keys_to_check:
assert entity_registry.async_get_entity_id(
Platform.SWITCH,
DOMAIN,
f"{appliance.ha_id}-{key}",
)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")

View File

@ -113,9 +113,19 @@ async def test_paired_depaired_devices_flow(
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
@pytest.mark.parametrize("appliance", ["Oven"], indirect=True)
@pytest.mark.parametrize(
("appliance", "keys_to_check"),
[
(
"Oven",
(SettingKey.BSH_COMMON_ALARM_CLOCK,),
)
],
indirect=["appliance"],
)
async def test_connected_devices(
appliance: HomeAppliance,
keys_to_check: tuple,
hass: HomeAssistant,
config_entry: MockConfigEntry,
integration_setup: Callable[[MagicMock], Awaitable[bool]],
@ -146,7 +156,12 @@ async def test_connected_devices(
device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)})
assert device
entity_entries = entity_registry.entities.get_entries_for_device_id(device.id)
for key in keys_to_check:
assert not entity_registry.async_get_entity_id(
Platform.TIME,
DOMAIN,
f"{appliance.ha_id}-{key}",
)
await client.add_events(
[
@ -159,10 +174,12 @@ async def test_connected_devices(
)
await hass.async_block_till_done()
device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)})
assert device
new_entity_entries = entity_registry.entities.get_entries_for_device_id(device.id)
assert len(new_entity_entries) > len(entity_entries)
for key in keys_to_check:
assert entity_registry.async_get_entity_id(
Platform.TIME,
DOMAIN,
f"{appliance.ha_id}-{key}",
)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")