diff --git a/homeassistant/components/matter/icons.json b/homeassistant/components/matter/icons.json index f21a7b7a931..92c6e38a610 100644 --- a/homeassistant/components/matter/icons.json +++ b/homeassistant/components/matter/icons.json @@ -146,6 +146,13 @@ "off": "mdi:lock-off" } }, + "speaker_mute": { + "default": "mdi:volume-high", + "state": { + "on": "mdi:volume-mute", + "off": "mdi:volume-high" + } + }, "evse_charging_switch": { "default": "mdi:ev-station" }, diff --git a/homeassistant/components/matter/number.py b/homeassistant/components/matter/number.py index f9783127673..4162d406e7c 100644 --- a/homeassistant/components/matter/number.py +++ b/homeassistant/components/matter/number.py @@ -176,6 +176,7 @@ DISCOVERY_SCHEMAS = [ ), entity_class=MatterNumber, required_attributes=(clusters.LevelControl.Attributes.OnLevel,), + not_device_type=(device_types.Speaker,), # allow None value to account for 'default' value allow_none_value=True, ), diff --git a/homeassistant/components/matter/strings.json b/homeassistant/components/matter/strings.json index a46fbddd612..6766cd57e5e 100644 --- a/homeassistant/components/matter/strings.json +++ b/homeassistant/components/matter/strings.json @@ -514,6 +514,9 @@ "power": { "name": "Power" }, + "speaker_mute": { + "name": "Mute" + }, "child_lock": { "name": "Child lock" }, diff --git a/homeassistant/components/matter/switch.py b/homeassistant/components/matter/switch.py index 2c02522f0a1..682285e9c97 100644 --- a/homeassistant/components/matter/switch.py +++ b/homeassistant/components/matter/switch.py @@ -203,7 +203,6 @@ DISCOVERY_SCHEMAS = [ device_types.Refrigerator, device_types.RoboticVacuumCleaner, device_types.RoomAirConditioner, - device_types.Speaker, ), ), MatterDiscoverySchema( @@ -242,6 +241,24 @@ DISCOVERY_SCHEMAS = [ device_types.Speaker, ), ), + MatterDiscoverySchema( + platform=Platform.SWITCH, + entity_description=MatterNumericSwitchEntityDescription( + key="MatterMuteToggle", + translation_key="speaker_mute", + device_to_ha={ + True: False, # True means volume is on, so HA should show mute as off + False: True, # False means volume is off (muted), so HA should show mute as on + }.get, + ha_to_device={ + False: True, # HA showing mute as off means volume is on, so send True + True: False, # HA showing mute as on means volume is off (muted), so send False + }.get, + ), + entity_class=MatterNumericSwitch, + required_attributes=(clusters.OnOff.Attributes.OnOff,), + device_type=(device_types.Speaker,), + ), MatterDiscoverySchema( platform=Platform.SWITCH, entity_description=MatterNumericSwitchEntityDescription( diff --git a/tests/components/matter/conftest.py b/tests/components/matter/conftest.py index 9b82f2ac305..b7f55ec2abd 100644 --- a/tests/components/matter/conftest.py +++ b/tests/components/matter/conftest.py @@ -120,6 +120,7 @@ async def integration_fixture( "silabs_water_heater", "smoke_detector", "solar_power", + "speaker", "switch_unit", "tado_smart_radiator_thermostat_x", "temperature_sensor", diff --git a/tests/components/matter/fixtures/nodes/speaker.json b/tests/components/matter/fixtures/nodes/speaker.json new file mode 100644 index 00000000000..f28923b3b3c --- /dev/null +++ b/tests/components/matter/fixtures/nodes/speaker.json @@ -0,0 +1,237 @@ +{ + "node_id": 107, + "date_commissioned": "2025-07-21T13:03:35.743927", + "last_interview": "2025-07-23T12:06:45.342425", + "interview_version": 6, + "available": true, + "is_bridge": false, + "attributes": { + "0/29/0": [ + { + "0": 22, + "1": 3 + } + ], + "0/29/1": [29, 31, 40, 48, 49, 51, 60, 62, 63], + "0/29/2": [], + "0/29/3": [1], + "0/29/65532": 0, + "0/29/65533": 3, + "0/29/65528": [], + "0/29/65529": [], + "0/29/65531": [0, 1, 2, 3, 65532, 65533, 65528, 65529, 65531], + "0/31/0": [ + { + "254": 1 + }, + { + "254": 2 + }, + { + "1": 5, + "2": 2, + "3": [112233], + "4": null, + "254": 3 + } + ], + "0/31/2": 4, + "0/31/4": 4, + "0/31/3": 3, + "0/31/65532": 0, + "0/31/65533": 2, + "0/31/65528": [], + "0/31/65529": [], + "0/31/65531": [0, 2, 4, 3, 65532, 65533, 65528, 65529, 65531], + "0/40/65532": 0, + "0/40/0": 19, + "0/40/6": "**REDACTED**", + "0/40/1": "Beep Home", + "0/40/2": 65521, + "0/40/3": "Mock speaker", + "0/40/4": 32768, + "0/40/7": 0, + "0/40/8": "1.0", + "0/40/9": 1, + "0/40/10": "1.0", + "0/40/18": "A576929DE6D138DC", + "0/40/19": { + "0": 3, + "1": 3 + }, + "0/40/21": 17104896, + "0/40/22": 1, + "0/40/65533": 5, + "0/40/5": "", + "0/40/65528": [], + "0/40/65529": [], + "0/40/65531": [ + 65532, 0, 6, 1, 2, 3, 4, 7, 8, 9, 10, 18, 19, 21, 22, 65533, 5, 65528, + 65529, 65531 + ], + "0/48/65532": 0, + "0/48/2": 0, + "0/48/3": 0, + "0/48/1": { + "0": 60, + "1": 900 + }, + "0/48/4": true, + "0/48/65533": 2, + "0/48/0": 0, + "0/48/65528": [1, 3, 5], + "0/48/65529": [0, 2, 4], + "0/48/65531": [65532, 2, 3, 1, 4, 65533, 0, 65528, 65529, 65531], + "0/49/0": 1, + "0/49/1": [ + { + "0": "", + "1": true + } + ], + "0/49/4": true, + "0/49/5": null, + "0/49/6": null, + "0/49/7": null, + "0/49/65532": 4, + "0/49/65533": 2, + "0/49/65528": [], + "0/49/65529": [], + "0/49/65531": [0, 1, 4, 5, 6, 7, 65532, 65533, 65528, 65529, 65531], + "0/51/0": [ + { + "0": "ETH_DEF", + "1": true, + "2": null, + "3": null, + "4": "dk29//5j", + "5": ["wKhP9A=="], + "6": ["/oAAAAAAAAB0Tb3//v/+Yw==", "/Qn01rCr1Hl0Tb3//v/+Yw=="], + "7": 2 + } + ], + "0/51/1": 12, + "0/51/2": 105, + "0/51/8": false, + "0/51/65532": 0, + "0/51/65533": 2, + "0/51/65528": [2], + "0/51/65529": [0, 1], + "0/51/65531": [0, 1, 2, 8, 65532, 65533, 65528, 65529, 65531], + "0/60/65532": 0, + "0/60/0": 1, + "0/60/1": 1, + "0/60/2": 4937, + "0/60/65533": 1, + "0/60/65528": [], + "0/60/65529": [0, 2], + "0/60/65531": [65532, 0, 1, 2, 65533, 65528, 65529, 65531], + "0/62/65532": 0, + "0/62/0": [ + { + "1": "FTABAQEkAgE3AyYUr7XOACYVpq4e6hgmBID2EDAkBQA3BiYVpq4e6iYRHXXVNxgkBwEkCAEwCUEEKNQ0xxl6/rzKRobxdnx+QXGmjCSeFUw6bBTrKgfYVkDVFUuqg9hozYtzj142gJXKYE5a6VZQUrdX12BkVPmm9zcKNQEoARgkAgE2AwQCBAEYMAQU4DNJO9VPf30U7ckieLsZ9Ab89WwwBRQuto07k43Cuvos2+idCKKlR2DjxBgwC0DCJxyTPnVbXleKY4QGudFSpLRwjTOl35z2AqVxPH/EcEM4jRc7dh/8K+x0yzJ6I8+2pm40nwICfzITnm62Am5QGA==", + "2": null, + "254": 1 + }, + { + "1": "FTABAQEkAgE3AycU13xCfZkhuUsmFSYjBIkYJgSH9hAwJAUANwYmFSYjBIkmEVAYUpUYJAcBJAgBMAlBBN7EuwOoVRUJW2qlVIHm4C2zH8hUxwSehRhqrihihxJSS8CVjwB8FsiQ4ChGFVJ/ZM8CzDIxROm0SNN7L199Czw3CjUBKAEYJAIBNgMEAgQBGDAEFNKmpZs7t/7neBldyIiSUc4qtAwrMAUUqSSMvmWet6YT/IEvHDGHhlJUr8QYMAtA/qCoDPaDbjHhVAkr9VBugUQX8QGqUADnjhbWljjl8t7eA9bttntqwZzsB2AGPeYYg7B3E5SIGPpefLFFh9mnSBg=", + "2": null, + "254": 2 + }, + { + "1": "FTABAQEkAgE3AyQTAhgmBIAigScmBYAlTTo3BiQVAiQRaxgkBwEkCAEwCUEEWKrZlA9bVwcwMsGjb+7gW8OpSFz3uosgT4gDydSflnuU+gDEumOBodeEQRR4rA0J1n1xIXLi5SAXEgHNDe/IzTcKNQEoARgkAgE2AwQCBAEYMAQUKcKB0dKsw1C31hygt4xKX2DdjiIwBRRi03D+l+yahKETUhU/VMUJYJK7CBgwC0DUBJ6vh/KWrjHdWWBmTSFs4dRye6+TD4nSALMhT0jgDPzGHI6yhMsrbs0/GlXfMNruVmSVOdYDjzANhzwKTjr/GA==", + "2": "FTABAQEkAgE3AyQUARgmBIAigScmBYAlTTo3BiQTAhgkBwEkCAEwCUEE9ZELz0go1r5np3XYnsCRPOqEWH9H/5Sg3RLTVeN8iq4MthnWplYvLfXFvMVaw8IEXHD/97aRGKUCkvTHW32PZTcKNQEpARgkAmAwBBRi03D+l+yahKETUhU/VMUJYJK7CDAFFJ1roLAG+djI0sZE5SMRIPfcwCOvGDALQDi7JqN8dF7piijkZ/YQddu4yTRfN9gonsLIOQ+AHaxA4WNr0lxlK5Lx/PPDA6T1CcaIbUOZ3p7sMOaSWZQSvnIY", + "254": 3 + } + ], + "0/62/2": 5, + "0/62/3": 3, + "0/62/1": [ + { + "1": "BCLmZe5bFElV+dZkFNaQLhuxkAejdcY41G7ZVTob2ezSI3MUZWSM7nFSJP/5hNA1FokKHg5WnX6nAfnX4eMoc98=", + "2": 4937, + "3": 3927879334, + "4": 936736029, + "5": "Home", + "254": 1 + }, + { + "1": "BM19vyMJrIK/NEJUy/J3yfZxzPLt0NSSq/31Uoo1g8Bgby+YKP3Gj3AA5AKTaPppx9aSfO1wYbPVyFaLF6ISaE8=", + "2": 4996, + "3": 2298749734, + "4": 2505185360, + "5": "", + "254": 2 + }, + { + "1": "BE63W9VgU/wxhR2+c0RPC4BFOE9X6bu0nfUuH7SErlaZKcNqSxKy3Qy3k1gjA3nkFT/1VzHzqXGuMJ3kwTRySDs=", + "2": 4939, + "3": 2, + "4": 107, + "5": "", + "254": 3 + } + ], + "0/62/4": [ + "FTABAQAkAgE3AyYUr7XOACYVpq4e6hgmBHdODDAkBQA3BiYUr7XOACYVpq4e6hgkBwEkCAEwCUEEIuZl7lsUSVX51mQU1pAuG7GQB6N1xjjUbtlVOhvZ7NIjcxRlZIzucVIk//mE0DUWiQoeDladfqcB+dfh4yhz3zcKNQEpARgkAmAwBBQuto07k43Cuvos2+idCKKlR2DjxDAFFC62jTuTjcK6+izb6J0IoqVHYOPEGDALQKjJ/1H3GFUojevBS05lU+idNFpSbXbuzkMtixrBKa++dMvTPBX8fqp0ElvMHiSOGoHhBS07bc26vRe7nWcz+FAY", + "FTABAQAkAgE3AycU13xCfZkhuUsmFSYjBIkYJgRw+UstJAUANwYnFNd8Qn2ZIblLJhUmIwSJGCQHASQIATAJQQTNfb8jCayCvzRCVMvyd8n2cczy7dDUkqv99VKKNYPAYG8vmCj9xo9wAOQCk2j6acfWknztcGGz1chWixeiEmhPNwo1ASkBGCQCYDAEFKkkjL5lnremE/yBLxwxh4ZSVK/EMAUUqSSMvmWet6YT/IEvHDGHhlJUr8QYMAtAG4951kJhhdOpU2mr57a1uSmhdp7o1kcFYS88DvQEZXoZVfKXQNSwTAxapobGado5U7FdTlOihLlZRTNqtZZTjhg=", + "FTABAQEkAgE3AyQUARgmBIAigScmBYAlTTo3BiQUARgkBwEkCAEwCUEETrdb1WBT/DGFHb5zRE8LgEU4T1fpu7Sd9S4ftISuVpkpw2pLErLdDLeTWCMDeeQVP/VXMfOpca4wneTBNHJIOzcKNQEpARgkAmAwBBSda6CwBvnYyNLGROUjESD33MAjrzAFFJ1roLAG+djI0sZE5SMRIPfcwCOvGDALQNEMd8zHt6yaUCoi+atIEWEVTc7VKCxYNRkxFg64YWOaHEjo4rJ022E3DtQRdXC7K0aEkctOhqVYLn7yq8Vk6tkY" + ], + "0/62/5": 3, + "0/62/65533": 2, + "0/62/65528": [1, 3, 5, 8, 14], + "0/62/65529": [0, 2, 4, 6, 7, 9, 10, 11, 12, 13], + "0/62/65531": [65532, 0, 2, 3, 1, 4, 5, 65533, 65528, 65529, 65531], + "0/63/65532": 0, + "0/63/65533": 2, + "0/63/0": [], + "0/63/1": [], + "0/63/2": 4, + "0/63/3": 3, + "0/63/65528": [2, 5], + "0/63/65529": [0, 1, 3, 4], + "0/63/65531": [65532, 65533, 0, 1, 2, 3, 65528, 65529, 65531], + "1/29/0": [ + { + "0": 34, + "1": 1 + } + ], + "1/29/1": [29, 3, 6, 8, 30], + "1/29/2": [], + "1/29/3": [2, 3], + "1/29/65532": 0, + "1/29/65533": 3, + "1/29/65528": [], + "1/29/65529": [], + "1/29/65531": [0, 1, 2, 3, 65532, 65533, 65528, 65529, 65531], + "1/3/65532": 0, + "1/3/65533": 5, + "1/3/0": 15, + "1/3/1": 0, + "1/3/65528": [], + "1/3/65529": [0], + "1/3/65531": [65532, 65533, 0, 1, 65528, 65529, 65531], + "1/6/65532": 0, + "1/6/65533": 6, + "1/6/0": true, + "1/6/65528": [], + "1/6/65529": [0], + "1/6/65531": [65532, 65533, 0, 65528, 65529, 65531], + "1/8/65532": 0, + "1/8/65533": 6, + "1/8/0": 47, + "1/8/17": null, + "1/8/15": 0, + "1/8/65528": [], + "1/8/65529": [0, 1, 2, 3, 4, 5, 6, 7], + "1/8/65531": [65532, 65533, 0, 17, 15, 65528, 65529, 65531], + "1/30/65532": 0, + "1/30/0": [], + "1/30/65533": 1, + "1/30/65528": [], + "1/30/65529": [], + "1/30/65531": [65532, 0, 65533, 65528, 65529, 65531] + }, + "attribute_subscriptions": [] +} diff --git a/tests/components/matter/snapshots/test_switch.ambr b/tests/components/matter/snapshots/test_switch.ambr index d7c2aba92a3..50542fcdddc 100644 --- a/tests/components/matter/snapshots/test_switch.ambr +++ b/tests/components/matter/snapshots/test_switch.ambr @@ -828,6 +828,54 @@ 'state': 'off', }) # --- +# name: test_switches[speaker][switch.mock_speaker_mute-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'switch', + 'entity_category': None, + 'entity_id': 'switch.mock_speaker_mute', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Mute', + 'platform': 'matter', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'speaker_mute', + 'unique_id': '00000000000004D2-000000000000006B-MatterNodeDevice-1-MatterMuteToggle-6-0', + 'unit_of_measurement': None, + }) +# --- +# name: test_switches[speaker][switch.mock_speaker_mute-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Mock speaker Mute', + }), + 'context': , + 'entity_id': 'switch.mock_speaker_mute', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- # name: test_switches[switch_unit][switch.mock_switchunit-entry] EntityRegistryEntrySnapshot({ 'aliases': set({