Compare commits

...

14 Commits

Author SHA1 Message Date
Paulus Schoutsen
c3906e1a3a Handle validation error when starting stream from audio 2026-03-22 07:49:32 -04:00
EnjoyingM
21863cd9d7 Bump wolf_comm to 0.0.48 (#166144) 2026-03-22 10:27:18 +01:00
Sean O'Keeffe
d67caec5c1 Add additional miele oven programs (#166100) 2026-03-22 09:04:07 +01:00
J. Nick Koston
8286014ae1 Bump habluetooth to 5.11.1 (#166161) 2026-03-21 18:22:53 -10:00
J. Nick Koston
1ff8d2279a Bump oralb-ble to 1.1.0 (#166165) 2026-03-21 18:22:21 -10:00
Ludovic BOUÉ
5dcbc1d5d9 feat(roborock): Add Q10 empty dustbin button entity (#166149) 2026-03-22 00:36:43 +01:00
Ludovic BOUÉ
3068653cc7 Update python-roborock to 4.26.2 (#166152) 2026-03-21 23:44:02 +01:00
Paulus Schoutsen
61b1a45889 Add logger to OpenDisplay (#166146) 2026-03-21 22:30:01 +01:00
Ray Xue
573d4eba02 Bump xiaomi-ble to 1.10.0 (#166099) 2026-03-21 20:40:54 +01:00
Ludovic BOUÉ
09895aa601 Update python-roborock to 4.26.1 (#166138) 2026-03-21 20:25:24 +01:00
Joost Lekkerkerker
aa6a4c7eab Add binary sensor for stick cleaner status to SmartThings (#166122) 2026-03-21 20:24:53 +01:00
Michael
662c44b125 Fix reload of FRITZ!Box Tools in case of connection issues (#166111) 2026-03-21 20:24:21 +01:00
Josef Zweck
5a80087cf4 Bump aiotedee to 0.2.27 (#166101)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-03-21 20:23:15 +01:00
TimL
c28dc32168 Add PSRAM sensor for SMLIGHT integration (#166104) 2026-03-21 20:20:33 +01:00
30 changed files with 463 additions and 46 deletions

View File

@@ -24,7 +24,7 @@ from .const import (
SAMPLE_WIDTH,
SAMPLES_PER_CHUNK,
)
from .error import PipelineNotFound
from .error import PipelineError, PipelineNotFound
from .pipeline import (
AudioSettings,
Pipeline,
@@ -137,5 +137,21 @@ async def async_pipeline_from_audio_stream(
audio_settings=audio_settings or AudioSettings(),
),
)
await pipeline_input.validate()
try:
await pipeline_input.validate()
except PipelineError as err:
pipeline_input.run.start(
conversation_id=session.conversation_id,
device_id=device_id,
satellite_id=satellite_id,
)
pipeline_input.run.process_event(
PipelineEvent(
PipelineEventType.ERROR,
{"code": err.code, "message": err.message},
)
)
await pipeline_input.run.end()
return
await pipeline_input.execute()

View File

@@ -21,6 +21,6 @@
"bluetooth-auto-recovery==1.5.3",
"bluetooth-data-tools==1.28.4",
"dbus-fast==3.1.2",
"habluetooth==5.10.2"
"habluetooth==5.11.1"
]
}

View File

@@ -13,6 +13,7 @@ from fritzconnection.core.exceptions import (
FritzSecurityError,
FritzServiceError,
)
from requests.exceptions import ConnectionError
from homeassistant.const import Platform
@@ -68,6 +69,7 @@ BUTTON_TYPE_WOL = "WakeOnLan"
UPTIME_DEVIATION = 5
FRITZ_EXCEPTIONS = (
ConnectionError,
FritzActionError,
FritzActionFailedError,
FritzConnectionException,

View File

@@ -617,8 +617,10 @@ class OvenProgramId(MieleEnum, missing_to_none=True):
pyrolytic = 323
descale = 326
evaporate_water = 327
rinse = 333
shabbat_program = 335
yom_tov = 336
hydroclean = 341
drying = 357, 2028
heat_crockery = 358
prove_dough = 359, 2023
@@ -723,7 +725,7 @@ class OvenProgramId(MieleEnum, missing_to_none=True):
belgian_sponge_cake = 624
goose_unstuffed = 625
rack_of_lamb_with_vegetables = 634
yorkshire_pudding = 635
yorkshire_pudding = 635, 2352
meat_loaf = 636
defrost_meat = 647
defrost_vegetables = 654
@@ -1123,7 +1125,7 @@ class OvenProgramId(MieleEnum, missing_to_none=True):
wholegrain_rice = 3376
parboiled_rice_steam_cooking = 3380
parboiled_rice_rapid_steam_cooking = 3381
basmati_rice_steam_cooking = 3383
basmati_rice_steam_cooking = 3382, 3383
basmati_rice_rapid_steam_cooking = 3384
jasmine_rice_steam_cooking = 3386
jasmine_rice_rapid_steam_cooking = 3387
@@ -1131,7 +1133,7 @@ class OvenProgramId(MieleEnum, missing_to_none=True):
huanghuanian_rapid_steam_cooking = 3390
simiao_steam_cooking = 3392
simiao_rapid_steam_cooking = 3393
long_grain_rice_general_steam_cooking = 3395
long_grain_rice_general_steam_cooking = 3394, 3395
long_grain_rice_general_rapid_steam_cooking = 3396
chongming_steam_cooking = 3398
chongming_rapid_steam_cooking = 3399

View File

@@ -560,6 +560,7 @@
"hot_water": "Hot water",
"huanghuanian_rapid_steam_cooking": "Huanghuanian (rapid steam cooking)",
"huanghuanian_steam_cooking": "Huanghuanian (steam cooking)",
"hydroclean": "HydroClean",
"hygiene": "Hygiene",
"intensive": "Intensive",
"intensive_bake": "Intensive bake",

View File

@@ -13,6 +13,7 @@
"documentation": "https://www.home-assistant.io/integrations/opendisplay",
"integration_type": "device",
"iot_class": "local_push",
"loggers": ["opendisplay"],
"quality_scale": "silver",
"requirements": ["py-opendisplay==5.5.0"]
}

View File

@@ -13,5 +13,5 @@
"integration_type": "device",
"iot_class": "local_push",
"loggers": ["oralb_ble"],
"requirements": ["oralb-ble==1.0.2"]
"requirements": ["oralb-ble==1.1.0"]
}

View File

@@ -20,12 +20,18 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import DOMAIN
from .coordinator import (
RoborockB01Q10UpdateCoordinator,
RoborockConfigEntry,
RoborockDataUpdateCoordinator,
RoborockDataUpdateCoordinatorA01,
RoborockWashingMachineUpdateCoordinator,
)
from .entity import RoborockCoordinatedEntityA01, RoborockEntity, RoborockEntityV1
from .entity import (
RoborockCoordinatedEntityA01,
RoborockCoordinatedEntityB01Q10,
RoborockEntity,
RoborockEntityV1,
)
_LOGGER = logging.getLogger(__name__)
@@ -97,6 +103,14 @@ ZEO_BUTTON_DESCRIPTIONS = [
]
Q10_BUTTON_DESCRIPTIONS = [
ButtonEntityDescription(
key="empty_dustbin",
translation_key="empty_dustbin",
),
]
async def async_setup_entry(
hass: HomeAssistant,
config_entry: RoborockConfigEntry,
@@ -139,6 +153,15 @@ async def async_setup_entry(
if isinstance(coordinator, RoborockWashingMachineUpdateCoordinator)
for description in ZEO_BUTTON_DESCRIPTIONS
),
(
RoborockQ10EmptyDustbinButtonEntity(
coordinator,
description,
)
for coordinator in config_entry.runtime_data.b01_q10
if isinstance(coordinator, RoborockB01Q10UpdateCoordinator)
for description in Q10_BUTTON_DESCRIPTIONS
),
)
)
@@ -233,3 +256,37 @@ class RoborockButtonEntityA01(RoborockCoordinatedEntityA01, ButtonEntity):
) from err
finally:
await self.coordinator.async_request_refresh()
class RoborockQ10EmptyDustbinButtonEntity(
RoborockCoordinatedEntityB01Q10, ButtonEntity
):
"""A class to define Q10 empty dustbin button entity."""
entity_description: ButtonEntityDescription
coordinator: RoborockB01Q10UpdateCoordinator
def __init__(
self,
coordinator: RoborockB01Q10UpdateCoordinator,
entity_description: ButtonEntityDescription,
) -> None:
"""Create a Q10 empty dustbin button entity."""
self.entity_description = entity_description
super().__init__(
f"{entity_description.key}_{coordinator.duid_slug}",
coordinator,
)
async def async_press(self, **kwargs: Any) -> None:
"""Press the button to empty dustbin."""
try:
await self.coordinator.api.vacuum.empty_dustbin()
except RoborockException as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="command_failed",
translation_placeholders={
"command": "empty_dustbin",
},
) from err

View File

@@ -20,7 +20,7 @@
"loggers": ["roborock"],
"quality_scale": "silver",
"requirements": [
"python-roborock==4.25.0",
"python-roborock==4.26.2",
"vacuum-map-parser-roborock==0.1.4"
]
}

View File

@@ -84,6 +84,9 @@
}
},
"button": {
"empty_dustbin": {
"name": "Empty dustbin"
},
"pause": {
"name": "Pause"
},

View File

@@ -208,6 +208,16 @@ CAPABILITY_TO_SENSORS: dict[
supported_states_attributes=Attribute.SUPPORTED_COOKTOP_OPERATING_STATE,
)
},
Capability.SAMSUNG_CE_CLEAN_STATION_STICK_STATUS: {
Attribute.STATUS: SmartThingsBinarySensorEntityDescription(
key=Attribute.STATUS,
component_translation_key={
"station": "stick_cleaner_status",
},
exists_fn=lambda component, _: component == "station",
is_on_key="attached",
)
},
Capability.SAMSUNG_CE_MICROFIBER_FILTER_STATUS: {
Attribute.STATUS: SmartThingsBinarySensorEntityDescription(
key=Attribute.STATUS,

View File

@@ -85,6 +85,9 @@
"robot_cleaner_dust_bag": {
"name": "Dust bag full"
},
"stick_cleaner_status": {
"name": "Stick cleaner in station"
},
"sub_remote_control": {
"name": "Upper washer remote control"
},

View File

@@ -105,6 +105,7 @@ SENSORS: list[SmSensorEntityDescription] = [
),
]
EXTRA_SENSOR = SmSensorEntityDescription(
key="zigbee_temperature_2",
translation_key="zigbee_temperature",
@@ -115,6 +116,15 @@ EXTRA_SENSOR = SmSensorEntityDescription(
value_fn=lambda x: x.zb_temp2,
)
PSRAM_SENSOR = SmSensorEntityDescription(
key="psram_usage",
translation_key="psram_usage",
device_class=SensorDeviceClass.DATA_SIZE,
native_unit_of_measurement=UnitOfInformation.KILOBYTES,
entity_registry_enabled_default=False,
value_fn=lambda x: x.psram_usage,
)
UPTIME: list[SmSensorEntityDescription] = [
SmSensorEntityDescription(
key="core_uptime",
@@ -156,6 +166,9 @@ async def async_setup_entry(
if coordinator.data.sensors.zb_temp2 is not None:
entities.append(SmSensorEntity(coordinator, EXTRA_SENSOR))
if coordinator.data.info.u_device:
entities.append(SmSensorEntity(coordinator, PSRAM_SENSOR))
async_add_entities(entities)

View File

@@ -104,6 +104,9 @@
"fs_usage": {
"name": "Filesystem usage"
},
"psram_usage": {
"name": "PSRAM usage"
},
"ram_usage": {
"name": "RAM usage"
},

View File

@@ -9,5 +9,5 @@
"iot_class": "local_push",
"loggers": ["aiotedee"],
"quality_scale": "platinum",
"requirements": ["aiotedee==0.2.25"]
"requirements": ["aiotedee==0.2.27"]
}

View File

@@ -7,5 +7,5 @@
"integration_type": "device",
"iot_class": "cloud_polling",
"loggers": ["wolf_comm"],
"requirements": ["wolf-comm==0.0.23"]
"requirements": ["wolf-comm==0.0.48"]
}

View File

@@ -25,5 +25,5 @@
"documentation": "https://www.home-assistant.io/integrations/xiaomi_ble",
"integration_type": "device",
"iot_class": "local_push",
"requirements": ["xiaomi-ble==1.6.0"]
"requirements": ["xiaomi-ble==1.10.0"]
}

View File

@@ -35,7 +35,7 @@ file-read-backwards==2.0.0
fnv-hash-fast==2.0.0
go2rtc-client==0.4.0
ha-ffmpeg==3.2.2
habluetooth==5.10.2
habluetooth==5.11.1
hass-nabucasa==2.0.0
hassil==3.5.0
home-assistant-bluetooth==1.13.1

12
requirements_all.txt generated
View File

@@ -422,7 +422,7 @@ aiosyncthing==0.7.1
aiotankerkoenig==0.5.1
# homeassistant.components.tedee
aiotedee==0.2.25
aiotedee==0.2.27
# homeassistant.components.tractive
aiotractive==1.0.0
@@ -1176,7 +1176,7 @@ ha-silabs-firmware-client==0.3.0
habiticalib==0.4.6
# homeassistant.components.bluetooth
habluetooth==5.10.2
habluetooth==5.11.1
# homeassistant.components.hanna
hanna-cloud==0.0.7
@@ -1732,7 +1732,7 @@ openwrt-ubus-rpc==0.0.2
opower==0.17.1
# homeassistant.components.oralb
oralb-ble==1.0.2
oralb-ble==1.1.0
# homeassistant.components.oru
oru==0.1.11
@@ -2660,7 +2660,7 @@ python-rabbitair==0.0.8
python-ripple-api==0.0.3
# homeassistant.components.roborock
python-roborock==4.25.0
python-roborock==4.26.2
# homeassistant.components.smarttub
python-smarttub==0.0.47
@@ -3310,7 +3310,7 @@ wirelesstagpy==0.8.1
wled==0.21.0
# homeassistant.components.wolflink
wolf-comm==0.0.23
wolf-comm==0.0.48
# homeassistant.components.wsdot
wsdot==0.0.1
@@ -3319,7 +3319,7 @@ wsdot==0.0.1
wyoming==1.7.2
# homeassistant.components.xiaomi_ble
xiaomi-ble==1.6.0
xiaomi-ble==1.10.0
# homeassistant.components.knx
xknx==3.15.0

View File

@@ -407,7 +407,7 @@ aiosyncthing==0.7.1
aiotankerkoenig==0.5.1
# homeassistant.components.tedee
aiotedee==0.2.25
aiotedee==0.2.27
# homeassistant.components.tractive
aiotractive==1.0.0
@@ -1046,7 +1046,7 @@ ha-silabs-firmware-client==0.3.0
habiticalib==0.4.6
# homeassistant.components.bluetooth
habluetooth==5.10.2
habluetooth==5.11.1
# homeassistant.components.hanna
hanna-cloud==0.0.7
@@ -1509,7 +1509,7 @@ openwebifpy==4.3.1
opower==0.17.1
# homeassistant.components.oralb
oralb-ble==1.0.2
oralb-ble==1.1.0
# homeassistant.components.orvibo
orvibo==1.1.2
@@ -2256,7 +2256,7 @@ python-pooldose==0.8.6
python-rabbitair==0.0.8
# homeassistant.components.roborock
python-roborock==4.25.0
python-roborock==4.26.2
# homeassistant.components.smarttub
python-smarttub==0.0.47
@@ -2792,7 +2792,7 @@ wiim==0.1.0
wled==0.21.0
# homeassistant.components.wolflink
wolf-comm==0.0.23
wolf-comm==0.0.48
# homeassistant.components.wsdot
wsdot==0.0.1
@@ -2801,7 +2801,7 @@ wsdot==0.0.1
wyoming==1.7.2
# homeassistant.components.xiaomi_ble
xiaomi-ble==1.6.0
xiaomi-ble==1.10.0
# homeassistant.components.knx
xknx==3.15.0

View File

@@ -330,6 +330,49 @@ async def test_pipeline_from_audio_stream_unknown_pipeline(
assert not events
async def test_pipeline_from_audio_stream_validation_pipeline_error(
hass: HomeAssistant,
mock_stt_provider_entity: MockSTTProviderEntity,
init_components,
) -> None:
"""Test validation pipeline errors are emitted as terminal events."""
events: list[assist_pipeline.PipelineEvent] = []
await assist_pipeline.async_update_pipeline(
hass,
assist_pipeline.async_get_pipeline(hass),
conversation_engine="conversation.non_existing",
)
async def audio_data():
yield b"audio"
await assist_pipeline.async_pipeline_from_audio_stream(
hass,
context=Context(),
event_callback=events.append,
stt_metadata=stt.SpeechMetadata(
language="",
format=stt.AudioFormats.WAV,
codec=stt.AudioCodecs.PCM,
bit_rate=stt.AudioBitRates.BITRATE_16,
sample_rate=stt.AudioSampleRates.SAMPLERATE_16000,
channel=stt.AudioChannels.CHANNEL_MONO,
),
stt_stream=audio_data(),
end_stage=assist_pipeline.PipelineStage.INTENT,
)
assert len(events) == 3
assert events[0].type == assist_pipeline.PipelineEventType.RUN_START
assert events[1].type == assist_pipeline.PipelineEventType.ERROR
assert events[1].data == {
"code": "intent-not-supported",
"message": "Intent recognition engine conversation.non_existing is not found",
}
assert events[2].type == assist_pipeline.PipelineEventType.RUN_END
async def test_pipeline_from_audio_stream_wake_word(
hass: HomeAssistant,
mock_stt_provider_entity: MockSTTProviderEntity,

View File

@@ -184,6 +184,43 @@ async def test_new_pipeline_cancels_pipeline(
await pipeline2_finished.wait()
async def test_pipeline_validation_error_ends_pipeline(
hass: HomeAssistant,
init_components: ConfigEntry,
entity: MockAssistSatellite,
) -> None:
"""Test validation pipeline errors end the satellite pipeline cleanly."""
await async_update_pipeline(
hass,
async_get_pipeline(hass),
stt_engine="test-stt-engine",
stt_language="en",
conversation_engine="conversation.non_existing",
)
with patch(
"homeassistant.components.assist_pipeline.pipeline.PipelineRun.prepare_speech_to_text"
):
await entity.async_accept_pipeline_from_satellite(
object(), # type: ignore[arg-type]
end_stage=PipelineStage.INTENT,
)
assert [event.type for event in entity.events[-3:]] == [
PipelineEventType.RUN_START,
PipelineEventType.ERROR,
PipelineEventType.RUN_END,
]
assert entity.events[-2].data == {
"code": "intent-not-supported",
"message": "Intent recognition engine conversation.non_existing is not found",
}
state = hass.states.get(ENTITY_ID)
assert state is not None
assert state.state == AssistSatelliteState.IDLE
@pytest.mark.parametrize(
("service_data", "expected_params"),
[

View File

@@ -5474,6 +5474,7 @@
'hens_eggs_size_xl_soft',
'huanghuanian_rapid_steam_cooking',
'huanghuanian_steam_cooking',
'hydroclean',
'intensive_bake',
'iridescent_shark_fillet',
'jasmine_rice_rapid_steam_cooking',
@@ -5675,6 +5676,7 @@
'rhubarb_chunks',
'rice_pudding_rapid_steam_cooking',
'rice_pudding_steam_cooking',
'rinse',
'risotto',
'roast_beef_low_temperature_cooking',
'roast_beef_roast',
@@ -6085,6 +6087,7 @@
'hens_eggs_size_xl_soft',
'huanghuanian_rapid_steam_cooking',
'huanghuanian_steam_cooking',
'hydroclean',
'intensive_bake',
'iridescent_shark_fillet',
'jasmine_rice_rapid_steam_cooking',
@@ -6286,6 +6289,7 @@
'rhubarb_chunks',
'rice_pudding_rapid_steam_cooking',
'rice_pudding_steam_cooking',
'rinse',
'risotto',
'roast_beef_low_temperature_cooking',
'roast_beef_roast',
@@ -9268,6 +9272,7 @@
'hens_eggs_size_xl_soft',
'huanghuanian_rapid_steam_cooking',
'huanghuanian_steam_cooking',
'hydroclean',
'intensive_bake',
'iridescent_shark_fillet',
'jasmine_rice_rapid_steam_cooking',
@@ -9469,6 +9474,7 @@
'rhubarb_chunks',
'rice_pudding_rapid_steam_cooking',
'rice_pudding_steam_cooking',
'rinse',
'risotto',
'roast_beef_low_temperature_cooking',
'roast_beef_roast',
@@ -9879,6 +9885,7 @@
'hens_eggs_size_xl_soft',
'huanghuanian_rapid_steam_cooking',
'huanghuanian_steam_cooking',
'hydroclean',
'intensive_bake',
'iridescent_shark_fillet',
'jasmine_rice_rapid_steam_cooking',
@@ -10080,6 +10087,7 @@
'rhubarb_chunks',
'rice_pudding_rapid_steam_cooking',
'rice_pudding_steam_cooking',
'rinse',
'risotto',
'roast_beef_low_temperature_cooking',
'roast_beef_roast',

View File

@@ -26,7 +26,7 @@ async def test_async_step_bluetooth_valid_device(hass: HomeAssistant) -> None:
result["flow_id"], user_input={}
)
assert result2["type"] is FlowResultType.CREATE_ENTRY
assert result2["title"] == "Smart Series 7000 48BE"
assert result2["title"] == "Triumph D36 48BE"
assert result2["data"] == {}
assert result2["result"].unique_id == "78:DB:2F:C2:48:BE"
@@ -91,7 +91,7 @@ async def test_async_step_user_with_found_devices(hass: HomeAssistant) -> None:
user_input={"address": "78:DB:2F:C2:48:BE"},
)
assert result2["type"] is FlowResultType.CREATE_ENTRY
assert result2["title"] == "Smart Series 7000 48BE"
assert result2["title"] == "Triumph D36 48BE"
assert result2["data"] == {}
assert result2["result"].unique_id == "78:DB:2F:C2:48:BE"
@@ -121,7 +121,7 @@ async def test_async_step_user_replace_ignored(hass: HomeAssistant) -> None:
user_input={"address": "78:DB:2F:C2:48:BE"},
)
assert result2["type"] is FlowResultType.CREATE_ENTRY
assert result2["title"] == "Smart Series 7000 48BE"
assert result2["title"] == "Triumph D36 48BE"
assert result2["data"] == {}
assert result2["result"].unique_id == "78:DB:2F:C2:48:BE"
@@ -240,7 +240,7 @@ async def test_async_step_user_takes_precedence_over_discovery(
user_input={"address": "78:DB:2F:C2:48:BE"},
)
assert result2["type"] is FlowResultType.CREATE_ENTRY
assert result2["title"] == "Smart Series 7000 48BE"
assert result2["title"] == "Triumph D36 48BE"
assert result2["data"] == {}
assert result2["result"].unique_id == "78:DB:2F:C2:48:BE"

View File

@@ -47,10 +47,10 @@ async def test_sensors(hass: HomeAssistant) -> None:
await hass.async_block_till_done()
assert len(hass.states.async_all("sensor")) == 9
toothbrush_sensor = hass.states.get("sensor.smart_series_7000_48be")
toothbrush_sensor = hass.states.get("sensor.triumph_d36_48be")
toothbrush_sensor_attrs = toothbrush_sensor.attributes
assert toothbrush_sensor.state == "running"
assert toothbrush_sensor_attrs[ATTR_FRIENDLY_NAME] == "Smart Series 7000 48BE"
assert toothbrush_sensor_attrs[ATTR_FRIENDLY_NAME] == "Triumph D36 48BE"
assert ATTR_ASSUMED_STATE not in toothbrush_sensor_attrs
assert await hass.config_entries.async_unload(entry.entry_id)
@@ -76,7 +76,7 @@ async def test_sensors(hass: HomeAssistant) -> None:
await hass.async_block_till_done()
# All of these devices are sleepy so we should still be available
toothbrush_sensor = hass.states.get("sensor.smart_series_7000_48be")
toothbrush_sensor = hass.states.get("sensor.triumph_d36_48be")
assert toothbrush_sensor.state == "running"
@@ -155,9 +155,9 @@ async def test_sensors_battery(hass: HomeAssistant) -> None:
await hass.async_block_till_done()
assert len(hass.states.async_all()) == 7
bat_sensor = hass.states.get("sensor.io_series_6_7_1dcf_battery")
bat_sensor = hass.states.get("sensor.io_series_1dcf_battery")
assert bat_sensor.state == "49"
assert bat_sensor.name == "IO Series 6/7 1DCF Battery"
assert bat_sensor.name == "IO Series 1DCF Battery"
assert await hass.config_entries.async_unload(entry.entry_id)
await hass.async_block_till_done()

View File

@@ -1,4 +1,54 @@
# serializer version: 1
# name: test_buttons[button.roborock_q10_s5_empty_dustbin-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'button',
'entity_category': None,
'entity_id': 'button.roborock_q10_s5_empty_dustbin',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Empty dustbin',
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Empty dustbin',
'platform': 'roborock',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'empty_dustbin',
'unique_id': 'empty_dustbin_q10_duid',
'unit_of_measurement': None,
})
# ---
# name: test_buttons[button.roborock_q10_s5_empty_dustbin-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Roborock Q10 S5+ Empty dustbin',
}),
'context': <ANY>,
'entity_id': 'button.roborock_q10_s5_empty_dustbin',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
})
# ---
# name: test_buttons[button.roborock_s7_2_reset_air_filter_consumable-entry]
EntityRegistryEntrySnapshot({
'aliases': list([

View File

@@ -272,3 +272,55 @@ async def test_press_a01_button_failure(
washing_machine.zeo.set_value.assert_called_once()
assert hass.states.get(entity_id).state == "2023-10-30T08:50:00+00:00"
@pytest.mark.freeze_time("2023-10-30 08:50:00")
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_press_q10_empty_dustbin_button_success(
hass: HomeAssistant,
bypass_api_client_fixture: None,
setup_entry: MockConfigEntry,
fake_q10_vacuum: FakeDevice,
) -> None:
"""Test pressing Q10 empty dustbin button entity."""
entity_id = "button.roborock_q10_s5_empty_dustbin"
assert hass.states.get(entity_id) is not None
await hass.services.async_call(
"button",
SERVICE_PRESS,
blocking=True,
target={"entity_id": entity_id},
)
assert fake_q10_vacuum.b01_q10_properties is not None
fake_q10_vacuum.b01_q10_properties.vacuum.empty_dustbin.assert_called_once()
assert hass.states.get(entity_id).state == "2023-10-30T08:50:00+00:00"
@pytest.mark.freeze_time("2023-10-30 08:50:00")
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_press_q10_empty_dustbin_button_failure(
hass: HomeAssistant,
bypass_api_client_fixture: None,
setup_entry: MockConfigEntry,
fake_q10_vacuum: FakeDevice,
) -> None:
"""Test failure while pressing Q10 empty dustbin button entity."""
entity_id = "button.roborock_q10_s5_empty_dustbin"
assert fake_q10_vacuum.b01_q10_properties is not None
fake_q10_vacuum.b01_q10_properties.vacuum.empty_dustbin.side_effect = (
RoborockException
)
assert hass.states.get(entity_id) is not None
with pytest.raises(HomeAssistantError, match="Error while calling empty_dustbin"):
await hass.services.async_call(
"button",
SERVICE_PRESS,
blocking=True,
target={"entity_id": entity_id},
)
fake_q10_vacuum.b01_q10_properties.vacuum.empty_dustbin.assert_called_once()
assert hass.states.get(entity_id).state == "2023-10-30T08:50:00+00:00"

View File

@@ -2023,6 +2023,56 @@
'state': 'off',
})
# ---
# name: test_all_entities[da_vc_stick_01001][binary_sensor.stick_vacuum_stick_cleaner_in_station-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'binary_sensor',
'entity_category': None,
'entity_id': 'binary_sensor.stick_vacuum_stick_cleaner_in_station',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Stick cleaner in station',
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Stick cleaner in station',
'platform': 'smartthings',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'stick_cleaner_status',
'unique_id': 'e1f93c0c-6fe0-c65a-a314-c8f7b163c86b_station_samsungce.cleanStationStickStatus_status_status',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[da_vc_stick_01001][binary_sensor.stick_vacuum_stick_cleaner_in_station-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Stick vacuum Stick cleaner in station',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.stick_vacuum_stick_cleaner_in_station',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---
# name: test_all_entities[da_wm_dw_000001][binary_sensor.dishwasher_child_lock-entry]
EntityRegistryEntrySnapshot({
'aliases': list([

View File

@@ -6,6 +6,7 @@ from pysmlight import Info, Sensors
import pytest
from syrupy.assertion import SnapshotAssertion
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
from homeassistant.components.smlight.const import DOMAIN
from homeassistant.const import STATE_UNKNOWN, Platform
from homeassistant.core import HomeAssistant
@@ -113,3 +114,55 @@ async def test_zigbee_type_sensors(
state = hass.states.get("sensor.mock_title_zigbee_type_2")
assert state
assert state.state == "router"
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_psram_usage_sensor(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_smlight_client: MagicMock,
entity_registry: er.EntityRegistry,
) -> None:
"""Test PSRAM usage sensor creation for u-devices."""
mock_smlight_client.get_info.side_effect = None
mock_smlight_client.get_info.return_value = Info(
MAC="AA:BB:CC:DD:EE:FF",
model="SLZB-MR3U",
u_device=True,
)
mock_smlight_client.get_sensors.return_value = Sensors(psram_usage=156)
await setup_integration(hass, mock_config_entry)
entity_id = entity_registry.async_get_entity_id(
SENSOR_DOMAIN, DOMAIN, "aa:bb:cc:dd:ee:ff_psram_usage"
)
assert entity_id is not None
state = hass.states.get(entity_id)
assert state is not None
assert state.state == "156"
assert state.attributes["unit_of_measurement"] == "kB"
async def test_psram_usage_sensor_not_created(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_smlight_client: MagicMock,
entity_registry: er.EntityRegistry,
) -> None:
"""Test PSRAM usage sensor is not created for non-u devices."""
mock_smlight_client.get_info.side_effect = None
mock_smlight_client.get_info.return_value = Info(
MAC="AA:BB:CC:DD:EE:FF",
model="SLZB-MR3",
u_device=False,
)
await setup_integration(hass, mock_config_entry)
assert hass.states.get("sensor.mock_title_psram_usage") is None
entity_id = entity_registry.async_get_entity_id(
SENSOR_DOMAIN, DOMAIN, "aa:bb:cc:dd:ee:ff_psram_usage"
)
assert entity_id is None

View File

@@ -71,7 +71,7 @@ def mock_wolflink() -> Generator[MagicMock]:
wolflink.fetch_parameters.return_value = [
EnergyParameter(
6002800000, "Energy Parameter", "Heating", 6005200000, 2000
6002800000, "Energy Parameter", "Heating", 6005200000, 2000, True
),
ListItemParameter(
8002800000,
@@ -80,22 +80,35 @@ def mock_wolflink() -> Generator[MagicMock]:
[ListItem("0", "Aus"), ListItem("1", "Ein")],
8005200000,
3001,
True,
),
PowerParameter(
5002800000, "Power Parameter", "Heating", 5005200000, 1000, True
),
Pressure(
4002800000, "Pressure Parameter", "Heating", 4005200000, 1000, True
),
Temperature(
3002800000, "Temperature Parameter", "Solar", 3005200000, 1000, True
),
PowerParameter(5002800000, "Power Parameter", "Heating", 5005200000, 1000),
Pressure(4002800000, "Pressure Parameter", "Heating", 4005200000, 1000),
Temperature(3002800000, "Temperature Parameter", "Solar", 3005200000, 1000),
PercentageParameter(
2002800000, "Percentage Parameter", "Solar", 2005200000, 1000
2002800000, "Percentage Parameter", "Solar", 2005200000, 1000, True
),
HoursParameter(
7002800000, "Hours Parameter", "Heating", 7005200000, 1000, True
),
SimpleParameter(
1002800000, "Simple Parameter", "DHW", 1005200000, 1000, True
),
HoursParameter(7002800000, "Hours Parameter", "Heating", 7005200000, 1000),
SimpleParameter(1002800000, "Simple Parameter", "DHW", 1005200000, 1000),
FrequencyParameter(
9002800000, "Frequency Parameter", "Heating", 9005200000, 1000
9002800000, "Frequency Parameter", "Heating", 9005200000, 1000, True
),
RPMParameter(
1000280001, "RPM Parameter", "Heating", 10005200000, 7000, True
),
FlowParameter(
1100280001, "Flow Parameter", "Heating", 11005200000, 8000, True
),
RPMParameter(1000280001, "RPM Parameter", "Heating", 10005200000, 7000),
FlowParameter(1100280001, "Flow Parameter", "Heating", 11005200000, 8000),
HoursParameter(7002800000, "Hours Parameter", "Heating", 7005200000, 1000),
SimpleParameter(1002800000, "Simple Parameter", "DHW", 1005200000, 1000),
]
wolflink.fetch_value.return_value = [