Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions homeassistant/components/google_cloud/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ async def async_tts_voices(
list_voices_response = await client.list_voices()
for voice in list_voices_response.voices:
language_code = voice.language_codes[0]
if not voice.name.startswith(language_code):
continue
if language_code not in voices:
voices[language_code] = []
voices[language_code].append(voice.name)
Expand Down
4 changes: 1 addition & 3 deletions homeassistant/components/vesync/quality_scale.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ rules:
brands: done
common-modules: done
config-flow-test-coverage: done
config-flow:
status: todo
comment: Missing data descriptions
config-flow: done
dependency-transparency: done
docs-actions: done
docs-high-level-description: done
Expand Down
12 changes: 12 additions & 0 deletions homeassistant/components/vesync/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
"password": "[%key:common::config_flow::data::password%]",
"username": "[%key:common::config_flow::data::email%]"
},
"data_description": {
"password": "[%key:component::vesync::config::step::user::data_description::password%]",
"username": "[%key:component::vesync::config::step::user::data_description::username%]"
},
"description": "The VeSync integration needs to re-authenticate your account",
"title": "[%key:common::config_flow::title::reauth%]"
},
Expand All @@ -23,6 +27,11 @@
"password": "[%key:common::config_flow::data::password%]",
"username": "[%key:common::config_flow::data::email%]"
},
"data_description": {
"password": "Password associated with your VeSync account",
"username": "Email address associated with your VeSync account"
},
"description": "Enter the account used in the vesync app. 2FA is not supported and must be disabled.",
"title": "Enter username and password"
}
}
Expand Down Expand Up @@ -106,6 +115,9 @@
}
},
"switch": {
"auto_off_config": {
"name": "Auto Off"
},
"child_lock": {
"name": "Child lock"
},
Expand Down
10 changes: 10 additions & 0 deletions homeassistant/components/vesync/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,16 @@ class VeSyncSwitchEntityDescription(SwitchEntityDescription):
on_fn=lambda device: device.toggle_child_lock(True),
off_fn=lambda device: device.toggle_child_lock(False),
),
VeSyncSwitchEntityDescription(
key="auto_off_config",
is_on=lambda device: device.state.automatic_stop_config,
exists_fn=(
lambda device: rgetattr(device, "state.automatic_stop_config") is not None
),
translation_key="auto_off_config",
on_fn=lambda device: device.toggle_automatic_stop(True),
off_fn=lambda device: device.toggle_automatic_stop(False),
),
)


Expand Down
1 change: 1 addition & 0 deletions tests/components/google_cloud/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ def mock_api_tts() -> AsyncMock:
voices=[
cloud_tts.Voice(language_codes=["en-US"], name="en-US-Standard-A"),
cloud_tts.Voice(language_codes=["en-US"], name="en-US-Standard-B"),
cloud_tts.Voice(language_codes=["en-US"], name="Standard-A"),
cloud_tts.Voice(language_codes=["el-GR"], name="el-GR-Standard-A"),
]
)
Expand Down
28 changes: 22 additions & 6 deletions tests/components/roborock/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,20 @@ async def close(self) -> None:
"""Close the device."""


def set_trait_attributes(
trait: AsyncMock,
dataclass_template: RoborockBase,
init_none: bool = False,
) -> None:
"""Set attributes on a mock roborock trait."""
template_copy = deepcopy(dataclass_template)
for attr_name in dir(template_copy):
if attr_name.startswith("_"):
continue
attr_value = getattr(template_copy, attr_name) if not init_none else None
setattr(trait, attr_name, attr_value)


def make_mock_trait(
trait_spec: type[V1TraitMixin] | None = None,
dataclass_template: RoborockBase | None = None,
Expand All @@ -183,12 +197,14 @@ def make_mock_trait(
trait = AsyncMock(spec=trait_spec or V1TraitMixin)
if dataclass_template is not None:
# Copy all attributes and property methods (e.g. computed properties)
template_copy = deepcopy(dataclass_template)
for attr_name in dir(template_copy):
if attr_name.startswith("_"):
continue
setattr(trait, attr_name, getattr(template_copy, attr_name))
trait.refresh = AsyncMock()
# on the first call to refresh(). The object starts uninitialized.
set_trait_attributes(trait, dataclass_template, init_none=True)

async def refresh() -> None:
if dataclass_template is not None:
set_trait_attributes(trait, dataclass_template)

trait.refresh = AsyncMock(side_effect=refresh)
return trait


Expand Down
13 changes: 10 additions & 3 deletions tests/components/roborock/test_vacuum.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@
from homeassistant.helpers import device_registry as dr, entity_registry as er
from homeassistant.setup import async_setup_component

from .conftest import FakeDevice
from .conftest import FakeDevice, set_trait_attributes
from .mock_data import STATUS

from tests.common import MockConfigEntry

Expand Down Expand Up @@ -134,8 +135,14 @@ async def test_resume_cleaning(
vacuum_command: Mock,
) -> None:
"""Test resuming clean on start button when a clean is paused."""
fake_vacuum.v1_properties.status.in_cleaning = in_cleaning_int
fake_vacuum.v1_properties.status.in_returning = in_returning_int

async def refresh_properties() -> None:
set_trait_attributes(fake_vacuum.v1_properties.status, STATUS)
fake_vacuum.v1_properties.status.in_cleaning = in_cleaning_int
fake_vacuum.v1_properties.status.in_returning = in_returning_int

fake_vacuum.v1_properties.status.refresh.side_effect = refresh_properties

await async_setup_component(hass, DOMAIN, {})
vacuum = hass.states.get(ENTITY_ID)
assert vacuum
Expand Down
24 changes: 24 additions & 0 deletions tests/components/vesync/snapshots/test_diagnostics.ambr
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,30 @@
}),
'unit_of_measurement': 'μg/m³',
}),
dict({
'device_class': None,
'disabled': False,
'disabled_by': None,
'domain': 'switch',
'entity_category': None,
'entity_id': 'switch.test_fan_auto_off',
'icon': None,
'name': None,
'original_device_class': None,
'original_icon': None,
'original_name': 'Auto Off',
'state': dict({
'attributes': dict({
'friendly_name': 'Test Fan Auto Off',
}),
'entity_id': 'switch.test_fan_auto_off',
'last_changed': str,
'last_reported': str,
'last_updated': str,
'state': 'unavailable',
}),
'unit_of_measurement': None,
}),
dict({
'device_class': None,
'disabled': False,
Expand Down
138 changes: 138 additions & 0 deletions tests/components/vesync/snapshots/test_switch.ambr
Original file line number Diff line number Diff line change
Expand Up @@ -611,8 +611,54 @@
'unique_id': '200s-humidifier4321-display',
'unit_of_measurement': None,
}),
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': None,
'entity_id': 'switch.humidifier_200s_auto_off',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Auto Off',
'platform': 'vesync',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'auto_off_config',
'unique_id': '200s-humidifier4321-auto_off_config',
'unit_of_measurement': None,
}),
])
# ---
# name: test_switch_state[Humidifier 200s][switch.humidifier_200s_auto_off]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Humidifier 200s Auto Off',
}),
'context': <ANY>,
'entity_id': 'switch.humidifier_200s_auto_off',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---
# name: test_switch_state[Humidifier 200s][switch.humidifier_200s_display]
StateSnapshot({
'attributes': ReadOnlyDict({
Expand Down Expand Up @@ -727,8 +773,54 @@
'unique_id': '6000s-child_lock',
'unit_of_measurement': None,
}),
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': None,
'entity_id': 'switch.humidifier_6000s_auto_off',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Auto Off',
'platform': 'vesync',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'auto_off_config',
'unique_id': '6000s-auto_off_config',
'unit_of_measurement': None,
}),
])
# ---
# name: test_switch_state[Humidifier 6000s][switch.humidifier_6000s_auto_off]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Humidifier 6000s Auto Off',
}),
'context': <ANY>,
'entity_id': 'switch.humidifier_6000s_auto_off',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---
# name: test_switch_state[Humidifier 6000s][switch.humidifier_6000s_child_lock]
StateSnapshot({
'attributes': ReadOnlyDict({
Expand Down Expand Up @@ -823,8 +915,54 @@
'unique_id': '600s-humidifier-display',
'unit_of_measurement': None,
}),
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': None,
'entity_id': 'switch.humidifier_600s_auto_off',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Auto Off',
'platform': 'vesync',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'auto_off_config',
'unique_id': '600s-humidifier-auto_off_config',
'unit_of_measurement': None,
}),
])
# ---
# name: test_switch_state[Humidifier 600S][switch.humidifier_600s_auto_off]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Humidifier 600S Auto Off',
}),
'context': <ANY>,
'entity_id': 'switch.humidifier_600s_auto_off',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---
# name: test_switch_state[Humidifier 600S][switch.humidifier_600s_display]
StateSnapshot({
'attributes': ReadOnlyDict({
Expand Down
2 changes: 1 addition & 1 deletion tests/components/vesync/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ async def test_migrate_config_entry(
switch_entities = [
e for e in entity_registry.entities.values() if e.domain == "switch"
]
assert len(switch_entities) == 2
assert len(switch_entities) == 3

humidifier_entities = [
e for e in entity_registry.entities.values() if e.domain == "humidifier"
Expand Down
Loading