From 31dc80c6167db4fa5142e2c8b6f12a38b34f7adb Mon Sep 17 00:00:00 2001 From: ilan <31193909+iloveicedgreentea@users.noreply.github.com> Date: Tue, 9 Jul 2024 13:11:08 -0400 Subject: [PATCH] Add binary sensor to madVR integration (#121465) * feat: add binary sensor and tests * fix: update test * fix: use entity description * feat: use translation key * feat: implement base entity * fix: change device classes * fix: remove some types * fix: coordinator.data none on init * fix: names, tests * feat: parameterize tests --- homeassistant/components/madvr/__init__.py | 2 +- .../components/madvr/binary_sensor.py | 86 ++++ homeassistant/components/madvr/coordinator.py | 3 + homeassistant/components/madvr/entity.py | 24 ++ homeassistant/components/madvr/icons.json | 30 ++ homeassistant/components/madvr/remote.py | 14 +- homeassistant/components/madvr/strings.json | 16 + tests/components/madvr/conftest.py | 35 +- .../madvr/snapshots/test_binary_sensors.ambr | 373 ++++++++++++++++++ tests/components/madvr/test_binary_sensors.py | 79 ++++ 10 files changed, 648 insertions(+), 14 deletions(-) create mode 100644 homeassistant/components/madvr/binary_sensor.py create mode 100644 homeassistant/components/madvr/entity.py create mode 100644 homeassistant/components/madvr/icons.json create mode 100644 tests/components/madvr/snapshots/test_binary_sensors.ambr create mode 100644 tests/components/madvr/test_binary_sensors.py diff --git a/homeassistant/components/madvr/__init__.py b/homeassistant/components/madvr/__init__.py index df9ffce6d95..b73c1f0dece 100644 --- a/homeassistant/components/madvr/__init__.py +++ b/homeassistant/components/madvr/__init__.py @@ -12,7 +12,7 @@ from homeassistant.core import Event, HomeAssistant, callback from .coordinator import MadVRCoordinator -PLATFORMS: list[Platform] = [Platform.REMOTE] +PLATFORMS: list[Platform] = [Platform.BINARY_SENSOR, Platform.REMOTE] type MadVRConfigEntry = ConfigEntry[MadVRCoordinator] diff --git a/homeassistant/components/madvr/binary_sensor.py b/homeassistant/components/madvr/binary_sensor.py new file mode 100644 index 00000000000..6a31f9cdcda --- /dev/null +++ b/homeassistant/components/madvr/binary_sensor.py @@ -0,0 +1,86 @@ +"""Binary sensor entities for the madVR integration.""" + +from __future__ import annotations + +from collections.abc import Callable +from dataclasses import dataclass + +from homeassistant.components.binary_sensor import ( + BinarySensorEntity, + BinarySensorEntityDescription, +) +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from . import MadVRConfigEntry +from .coordinator import MadVRCoordinator +from .entity import MadVREntity + +_HDR_FLAG = "hdr_flag" +_OUTGOING_HDR_FLAG = "outgoing_hdr_flag" +_POWER_STATE = "power_state" +_SIGNAL_STATE = "signal_state" + + +@dataclass(frozen=True, kw_only=True) +class MadvrBinarySensorEntityDescription(BinarySensorEntityDescription): + """Describe madVR binary sensor entity.""" + + value_fn: Callable[[MadVRCoordinator], bool] + + +BINARY_SENSORS: tuple[MadvrBinarySensorEntityDescription, ...] = ( + MadvrBinarySensorEntityDescription( + key=_POWER_STATE, + translation_key=_POWER_STATE, + value_fn=lambda coordinator: coordinator.data.get("is_on", False), + ), + MadvrBinarySensorEntityDescription( + key=_SIGNAL_STATE, + translation_key=_SIGNAL_STATE, + value_fn=lambda coordinator: coordinator.data.get("is_signal", False), + ), + MadvrBinarySensorEntityDescription( + key=_HDR_FLAG, + translation_key=_HDR_FLAG, + value_fn=lambda coordinator: coordinator.data.get("hdr_flag", False), + ), + MadvrBinarySensorEntityDescription( + key=_OUTGOING_HDR_FLAG, + translation_key=_OUTGOING_HDR_FLAG, + value_fn=lambda coordinator: coordinator.data.get("outgoing_hdr_flag", False), + ), +) + + +async def async_setup_entry( + hass: HomeAssistant, + entry: MadVRConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up the binary sensor entities.""" + coordinator = entry.runtime_data + async_add_entities( + MadvrBinarySensor(coordinator, description) for description in BINARY_SENSORS + ) + + +class MadvrBinarySensor(MadVREntity, BinarySensorEntity): + """Base class for madVR binary sensors.""" + + entity_description: MadvrBinarySensorEntityDescription + + def __init__( + self, + coordinator: MadVRCoordinator, + description: MadvrBinarySensorEntityDescription, + ) -> None: + """Initialize the binary sensor.""" + super().__init__(coordinator) + self.entity_description = description + self._attr_unique_id = f"{coordinator.mac}_{description.key}" + + @property + def is_on(self) -> bool: + """Return true if the binary sensor is on.""" + return self.entity_description.value_fn(self.coordinator) diff --git a/homeassistant/components/madvr/coordinator.py b/homeassistant/components/madvr/coordinator.py index fb6dbb0d8b2..4031ba127f7 100644 --- a/homeassistant/components/madvr/coordinator.py +++ b/homeassistant/components/madvr/coordinator.py @@ -33,6 +33,9 @@ class MadVRCoordinator(DataUpdateCoordinator[dict[str, Any]]): assert self.config_entry.unique_id self.mac = self.config_entry.unique_id self.client = client + # this does not use poll/refresh, so we need to set this to not None on init + self.data = {} + # this passes a callback to the client to push new data to the coordinator self.client.set_update_callback(self.handle_push_data) _LOGGER.debug("MadVRCoordinator initialized with mac: %s", self.mac) diff --git a/homeassistant/components/madvr/entity.py b/homeassistant/components/madvr/entity.py new file mode 100644 index 00000000000..a11e8c087ad --- /dev/null +++ b/homeassistant/components/madvr/entity.py @@ -0,0 +1,24 @@ +"""Base class for madVR entities.""" + +from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo +from homeassistant.helpers.update_coordinator import CoordinatorEntity + +from .const import DOMAIN +from .coordinator import MadVRCoordinator + + +class MadVREntity(CoordinatorEntity[MadVRCoordinator]): + """Defines a base madVR entity.""" + + _attr_has_entity_name = True + + def __init__(self, coordinator: MadVRCoordinator) -> None: + """Initialize madvr entity.""" + super().__init__(coordinator) + self._attr_device_info = DeviceInfo( + identifiers={(DOMAIN, coordinator.mac)}, + name="madVR Envy", + manufacturer="madVR", + model="Envy", + connections={(CONNECTION_NETWORK_MAC, coordinator.mac)}, + ) diff --git a/homeassistant/components/madvr/icons.json b/homeassistant/components/madvr/icons.json new file mode 100644 index 00000000000..42645787767 --- /dev/null +++ b/homeassistant/components/madvr/icons.json @@ -0,0 +1,30 @@ +{ + "entity": { + "binary_sensor": { + "hdr_flag": { + "default": "mdi:hdr", + "state": { + "off": "mdi:hdr-off" + } + }, + "outgoing_hdr_flag": { + "default": "mdi:hdr", + "state": { + "off": "mdi:hdr-off" + } + }, + "power_state": { + "default": "mdi:power", + "state": { + "off": "mdi:power-off" + } + }, + "signal_state": { + "default": "mdi:signal", + "state": { + "off": "mdi:signal-off" + } + } + } + } +} diff --git a/homeassistant/components/madvr/remote.py b/homeassistant/components/madvr/remote.py index 7477888d342..4fe02b7ae47 100644 --- a/homeassistant/components/madvr/remote.py +++ b/homeassistant/components/madvr/remote.py @@ -8,13 +8,11 @@ from typing import Any from homeassistant.components.remote import RemoteEntity from homeassistant.core import HomeAssistant -from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.update_coordinator import CoordinatorEntity from . import MadVRConfigEntry -from .const import DOMAIN from .coordinator import MadVRCoordinator +from .entity import MadVREntity _LOGGER = logging.getLogger(__name__) @@ -33,10 +31,9 @@ async def async_setup_entry( ) -class MadvrRemote(CoordinatorEntity[MadVRCoordinator], RemoteEntity): +class MadvrRemote(MadVREntity, RemoteEntity): """Remote entity for the madVR integration.""" - _attr_has_entity_name = True _attr_name = None def __init__( @@ -47,13 +44,6 @@ class MadvrRemote(CoordinatorEntity[MadVRCoordinator], RemoteEntity): super().__init__(coordinator) self.madvr_client = coordinator.client self._attr_unique_id = coordinator.mac - self._attr_device_info = DeviceInfo( - identifiers={(DOMAIN, coordinator.mac)}, - name="madVR Envy", - manufacturer="madVR", - model="Envy", - connections={(CONNECTION_NETWORK_MAC, coordinator.mac)}, - ) @property def is_on(self) -> bool: diff --git a/homeassistant/components/madvr/strings.json b/homeassistant/components/madvr/strings.json index a51eb093e7b..c8b9851e780 100644 --- a/homeassistant/components/madvr/strings.json +++ b/homeassistant/components/madvr/strings.json @@ -21,5 +21,21 @@ "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", "no_mac": "A MAC address was not found. It required to identify the device. Please ensure your device is connectable." } + }, + "entity": { + "binary_sensor": { + "hdr_flag": { + "name": "HDR flag" + }, + "outgoing_hdr_flag": { + "name": "Outgoing HDR flag" + }, + "power_state": { + "name": "Power state" + }, + "signal_state": { + "name": "Signal state" + } + } } } diff --git a/tests/components/madvr/conftest.py b/tests/components/madvr/conftest.py index 10da7ba0982..1fdf3302eb6 100644 --- a/tests/components/madvr/conftest.py +++ b/tests/components/madvr/conftest.py @@ -1,7 +1,7 @@ """MadVR conftest for shared testing setup.""" from collections.abc import Generator -from unittest.mock import AsyncMock, patch +from unittest.mock import AsyncMock, MagicMock, PropertyMock, patch import pytest @@ -40,6 +40,12 @@ def mock_madvr_client() -> Generator[AsyncMock, None, None]: client.is_device_connectable.return_value = True client.loop = AsyncMock() client.tasks = AsyncMock() + client.set_update_callback = MagicMock() + + # mock the property to be off on startup (which it is) + is_on_mock = PropertyMock(return_value=True) + type(client).is_on = is_on_mock + yield client @@ -52,3 +58,30 @@ def mock_config_entry() -> MockConfigEntry: unique_id=MOCK_MAC, title=DEFAULT_NAME, ) + + +def get_update_callback(mock_client: MagicMock): + """Retrieve the update callback function from the mocked client. + + This function extracts the callback that was passed to set_update_callback + on the mocked MadVR client. This callback is typically the handle_push_data + method of the MadVRCoordinator. + + Args: + mock_client (MagicMock): The mocked MadVR client. + + Returns: + function: The update callback function. + + """ + # Get all the calls made to set_update_callback + calls = mock_client.set_update_callback.call_args_list + + if not calls: + raise ValueError("set_update_callback was not called on the mock client") + + # Get the first (and usually only) call + first_call = calls[0] + + # Get the first argument of this call, which should be the callback function + return first_call.args[0] diff --git a/tests/components/madvr/snapshots/test_binary_sensors.ambr b/tests/components/madvr/snapshots/test_binary_sensors.ambr new file mode 100644 index 00000000000..87069542eb1 --- /dev/null +++ b/tests/components/madvr/snapshots/test_binary_sensors.ambr @@ -0,0 +1,373 @@ +# serializer version: 1 +# name: test_binary_sensor_setup[binary_sensor.madvr_envy_hdr_flag-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.madvr_envy_hdr_flag', + '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': 'HDR flag', + 'platform': 'madvr', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'hdr_flag', + 'unique_id': '00:11:22:33:44:55_hdr_flag', + 'unit_of_measurement': None, + }) +# --- +# name: test_binary_sensor_setup[binary_sensor.madvr_envy_hdr_flag-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'madVR Envy HDR flag', + }), + 'context': , + 'entity_id': 'binary_sensor.madvr_envy_hdr_flag', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_binary_sensor_setup[binary_sensor.madvr_envy_madvr_hdr_flag-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.madvr_envy_madvr_hdr_flag', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': 'mdi:hdr-off', + 'original_name': 'madvr HDR Flag', + 'platform': 'madvr', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:11:22:33:44:55_hdr_flag', + 'unit_of_measurement': None, + }) +# --- +# name: test_binary_sensor_setup[binary_sensor.madvr_envy_madvr_hdr_flag-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'madVR Envy madvr HDR Flag', + 'icon': 'mdi:hdr-off', + }), + 'context': , + 'entity_id': 'binary_sensor.madvr_envy_madvr_hdr_flag', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_binary_sensor_setup[binary_sensor.madvr_envy_madvr_outgoing_hdr_flag-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.madvr_envy_madvr_outgoing_hdr_flag', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': 'mdi:hdr-off', + 'original_name': 'madvr Outgoing HDR Flag', + 'platform': 'madvr', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:11:22:33:44:55_outgoing_hdr_flag', + 'unit_of_measurement': None, + }) +# --- +# name: test_binary_sensor_setup[binary_sensor.madvr_envy_madvr_outgoing_hdr_flag-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'madVR Envy madvr Outgoing HDR Flag', + 'icon': 'mdi:hdr-off', + }), + 'context': , + 'entity_id': 'binary_sensor.madvr_envy_madvr_outgoing_hdr_flag', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_binary_sensor_setup[binary_sensor.madvr_envy_madvr_power_state-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.madvr_envy_madvr_power_state', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': 'mdi:power-off', + 'original_name': 'madvr Power State', + 'platform': 'madvr', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:11:22:33:44:55_power_state', + 'unit_of_measurement': None, + }) +# --- +# name: test_binary_sensor_setup[binary_sensor.madvr_envy_madvr_power_state-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'madVR Envy madvr Power State', + 'icon': 'mdi:power-off', + }), + 'context': , + 'entity_id': 'binary_sensor.madvr_envy_madvr_power_state', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_binary_sensor_setup[binary_sensor.madvr_envy_madvr_signal_state-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.madvr_envy_madvr_signal_state', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': 'mdi:signal-off', + 'original_name': 'madvr Signal State', + 'platform': 'madvr', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:11:22:33:44:55_signal_state', + 'unit_of_measurement': None, + }) +# --- +# name: test_binary_sensor_setup[binary_sensor.madvr_envy_madvr_signal_state-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'madVR Envy madvr Signal State', + 'icon': 'mdi:signal-off', + }), + 'context': , + 'entity_id': 'binary_sensor.madvr_envy_madvr_signal_state', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_binary_sensor_setup[binary_sensor.madvr_envy_outgoing_hdr_flag-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.madvr_envy_outgoing_hdr_flag', + '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': 'Outgoing HDR flag', + 'platform': 'madvr', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'outgoing_hdr_flag', + 'unique_id': '00:11:22:33:44:55_outgoing_hdr_flag', + 'unit_of_measurement': None, + }) +# --- +# name: test_binary_sensor_setup[binary_sensor.madvr_envy_outgoing_hdr_flag-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'madVR Envy Outgoing HDR flag', + }), + 'context': , + 'entity_id': 'binary_sensor.madvr_envy_outgoing_hdr_flag', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_binary_sensor_setup[binary_sensor.madvr_envy_power_state-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.madvr_envy_power_state', + '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': 'Power state', + 'platform': 'madvr', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'power_state', + 'unique_id': '00:11:22:33:44:55_power_state', + 'unit_of_measurement': None, + }) +# --- +# name: test_binary_sensor_setup[binary_sensor.madvr_envy_power_state-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'madVR Envy Power state', + }), + 'context': , + 'entity_id': 'binary_sensor.madvr_envy_power_state', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_binary_sensor_setup[binary_sensor.madvr_envy_signal_state-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.madvr_envy_signal_state', + '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': 'Signal state', + 'platform': 'madvr', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'signal_state', + 'unique_id': '00:11:22:33:44:55_signal_state', + 'unit_of_measurement': None, + }) +# --- +# name: test_binary_sensor_setup[binary_sensor.madvr_envy_signal_state-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'madVR Envy Signal state', + }), + 'context': , + 'entity_id': 'binary_sensor.madvr_envy_signal_state', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- diff --git a/tests/components/madvr/test_binary_sensors.py b/tests/components/madvr/test_binary_sensors.py new file mode 100644 index 00000000000..469a3225ca0 --- /dev/null +++ b/tests/components/madvr/test_binary_sensors.py @@ -0,0 +1,79 @@ +"""Tests for the MadVR binary sensor entities.""" + +from __future__ import annotations + +from unittest.mock import AsyncMock, patch + +import pytest +from syrupy import SnapshotAssertion + +from homeassistant.const import STATE_OFF, STATE_ON, Platform +from homeassistant.core import HomeAssistant +import homeassistant.helpers.entity_registry as er + +from . import setup_integration +from .conftest import get_update_callback + +from tests.common import MockConfigEntry, snapshot_platform + + +async def test_binary_sensor_setup( + hass: HomeAssistant, + snapshot: SnapshotAssertion, + mock_config_entry: MockConfigEntry, + entity_registry: er.EntityRegistry, +) -> None: + """Test setup of the binary sensor entities.""" + with patch("homeassistant.components.madvr.PLATFORMS", [Platform.BINARY_SENSOR]): + await setup_integration(hass, mock_config_entry) + await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id) + + +@pytest.mark.parametrize( + ("entity_id", "positive_payload", "negative_payload"), + [ + ( + "binary_sensor.madvr_envy_power_state", + {"is_on": True}, + {"is_on": False}, + ), + ( + "binary_sensor.madvr_envy_signal_state", + {"is_signal": True}, + {"is_signal": False}, + ), + ( + "binary_sensor.madvr_envy_hdr_flag", + {"hdr_flag": True}, + {"hdr_flag": False}, + ), + ( + "binary_sensor.madvr_envy_outgoing_hdr_flag", + {"outgoing_hdr_flag": True}, + {"outgoing_hdr_flag": False}, + ), + ], +) +async def test_binary_sensors( + hass: HomeAssistant, + mock_madvr_client: AsyncMock, + mock_config_entry: MockConfigEntry, + entity_id: str, + positive_payload: dict, + negative_payload: dict, +) -> None: + """Test the binary sensors.""" + await setup_integration(hass, mock_config_entry) + update_callback = get_update_callback(mock_madvr_client) + + # Test positive state + update_callback(positive_payload) + await hass.async_block_till_done() + state = hass.states.get(entity_id) + assert state.state == STATE_ON + + # Test negative state + update_callback(negative_payload) + await hass.async_block_till_done() + state = hass.states.get(entity_id) + assert state.state == STATE_OFF