diff --git a/homeassistant/components/amberelectric/binary_sensor.py b/homeassistant/components/amberelectric/binary_sensor.py index 25a6c2fe267..cd06fb04f39 100644 --- a/homeassistant/components/amberelectric/binary_sensor.py +++ b/homeassistant/components/amberelectric/binary_sensor.py @@ -71,6 +71,18 @@ class AmberPriceSpikeBinarySensor(AmberPriceGridSensor): } +class AmberDemandWindowBinarySensor(AmberPriceGridSensor): + """Sensor to show whether demand window is active.""" + + @property + def is_on(self) -> bool | None: + """Return true if the binary sensor is on.""" + grid = self.coordinator.data["grid"] + if "demand_window" in grid: + return grid["demand_window"] # type: ignore[no-any-return] + return None + + async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, @@ -83,6 +95,14 @@ async def async_setup_entry( key="price_spike", name=f"{entry.title} - Price Spike", ) - async_add_entities( - [AmberPriceSpikeBinarySensor(coordinator, price_spike_description)] + demand_window_description = BinarySensorEntityDescription( + key="demand_window", + name=f"{entry.title} - Demand Window", + translation_key="demand_window", + ) + async_add_entities( + [ + AmberPriceSpikeBinarySensor(coordinator, price_spike_description), + AmberDemandWindowBinarySensor(coordinator, demand_window_description), + ] ) diff --git a/homeassistant/components/amberelectric/coordinator.py b/homeassistant/components/amberelectric/coordinator.py index 9fb6293c9a2..a95aa3fa529 100644 --- a/homeassistant/components/amberelectric/coordinator.py +++ b/homeassistant/components/amberelectric/coordinator.py @@ -111,6 +111,9 @@ class AmberUpdateCoordinator(DataUpdateCoordinator): ] result["grid"]["renewables"] = round(general[0].renewables) result["grid"]["price_spike"] = general[0].spike_status.value + tariff_information = general[0].tariff_information + if tariff_information: + result["grid"]["demand_window"] = tariff_information.demand_window controlled_load = [ interval for interval in current if is_controlled_load(interval) diff --git a/homeassistant/components/amberelectric/icons.json b/homeassistant/components/amberelectric/icons.json index b9716387b53..7dd6ae3217c 100644 --- a/homeassistant/components/amberelectric/icons.json +++ b/homeassistant/components/amberelectric/icons.json @@ -13,6 +13,14 @@ "renewables": { "default": "mdi:solar-power" } + }, + "binary_sensor": { + "demand_window": { + "default": "mdi:meter-electric", + "state": { + "off": "mdi:meter-electric-outline" + } + } } } } diff --git a/tests/components/amberelectric/test_binary_sensor.py b/tests/components/amberelectric/test_binary_sensor.py index 1e5eb572e07..2c1ee22b644 100644 --- a/tests/components/amberelectric/test_binary_sensor.py +++ b/tests/components/amberelectric/test_binary_sensor.py @@ -8,6 +8,7 @@ from unittest.mock import Mock, patch from amberelectric.model.channel import ChannelType from amberelectric.model.current_interval import CurrentInterval from amberelectric.model.interval import SpikeStatus +from amberelectric.model.tariff_information import TariffInformation from dateutil import parser import pytest @@ -111,7 +112,7 @@ async def setup_spike(hass: HomeAssistant) -> AsyncGenerator[Mock]: @pytest.mark.usefixtures("setup_no_spike") def test_no_spike_sensor(hass: HomeAssistant) -> None: """Testing the creation of the Amber renewables sensor.""" - assert len(hass.states.async_all()) == 5 + assert len(hass.states.async_all()) == 6 sensor = hass.states.get("binary_sensor.mock_title_price_spike") assert sensor assert sensor.state == "off" @@ -122,7 +123,7 @@ def test_no_spike_sensor(hass: HomeAssistant) -> None: @pytest.mark.usefixtures("setup_potential_spike") def test_potential_spike_sensor(hass: HomeAssistant) -> None: """Testing the creation of the Amber renewables sensor.""" - assert len(hass.states.async_all()) == 5 + assert len(hass.states.async_all()) == 6 sensor = hass.states.get("binary_sensor.mock_title_price_spike") assert sensor assert sensor.state == "off" @@ -133,9 +134,85 @@ def test_potential_spike_sensor(hass: HomeAssistant) -> None: @pytest.mark.usefixtures("setup_spike") def test_spike_sensor(hass: HomeAssistant) -> None: """Testing the creation of the Amber renewables sensor.""" - assert len(hass.states.async_all()) == 5 + assert len(hass.states.async_all()) == 6 sensor = hass.states.get("binary_sensor.mock_title_price_spike") assert sensor assert sensor.state == "on" assert sensor.attributes["icon"] == "mdi:power-plug-off" assert sensor.attributes["spike_status"] == "spike" + + +@pytest.fixture +async def setup_inactive_demand_window(hass: HomeAssistant) -> AsyncGenerator[Mock]: + """Set up general channel.""" + MockConfigEntry( + domain="amberelectric", + data={ + CONF_SITE_NAME: "mock_title", + CONF_API_TOKEN: MOCK_API_TOKEN, + CONF_SITE_ID: GENERAL_ONLY_SITE_ID, + }, + ).add_to_hass(hass) + + instance = Mock() + with patch( + "amberelectric.api.AmberApi.create", + return_value=instance, + ) as mock_update: + general_channel: list[CurrentInterval] = [ + generate_current_interval( + ChannelType.GENERAL, parser.parse("2021-09-21T08:30:00+10:00") + ), + ] + general_channel[0].tariff_information = TariffInformation(demandWindow=False) + instance.get_current_price = Mock(return_value=general_channel) + assert await async_setup_component(hass, DOMAIN, {}) + await hass.async_block_till_done() + yield mock_update.return_value + + +@pytest.fixture +async def setup_active_demand_window(hass: HomeAssistant) -> AsyncGenerator[Mock]: + """Set up general channel.""" + MockConfigEntry( + domain="amberelectric", + data={ + CONF_SITE_NAME: "mock_title", + CONF_API_TOKEN: MOCK_API_TOKEN, + CONF_SITE_ID: GENERAL_ONLY_SITE_ID, + }, + ).add_to_hass(hass) + + instance = Mock() + with patch( + "amberelectric.api.AmberApi.create", + return_value=instance, + ) as mock_update: + general_channel: list[CurrentInterval] = [ + generate_current_interval( + ChannelType.GENERAL, parser.parse("2021-09-21T08:30:00+10:00") + ), + ] + general_channel[0].tariff_information = TariffInformation(demandWindow=True) + instance.get_current_price = Mock(return_value=general_channel) + assert await async_setup_component(hass, DOMAIN, {}) + await hass.async_block_till_done() + yield mock_update.return_value + + +@pytest.mark.usefixtures("setup_inactive_demand_window") +def test_inactive_demand_window_sensor(hass: HomeAssistant) -> None: + """Testing the creation of the Amber demand_window sensor.""" + assert len(hass.states.async_all()) == 6 + sensor = hass.states.get("binary_sensor.mock_title_demand_window") + assert sensor + assert sensor.state == "off" + + +@pytest.mark.usefixtures("setup_active_demand_window") +def test_active_demand_window_sensor(hass: HomeAssistant) -> None: + """Testing the creation of the Amber demand_window sensor.""" + assert len(hass.states.async_all()) == 6 + sensor = hass.states.get("binary_sensor.mock_title_demand_window") + assert sensor + assert sensor.state == "on" diff --git a/tests/components/amberelectric/test_sensor.py b/tests/components/amberelectric/test_sensor.py index 3c0910f0afc..3a5626d14d5 100644 --- a/tests/components/amberelectric/test_sensor.py +++ b/tests/components/amberelectric/test_sensor.py @@ -105,7 +105,7 @@ async def setup_general_and_feed_in(hass: HomeAssistant) -> AsyncGenerator[Mock] async def test_general_price_sensor(hass: HomeAssistant, setup_general: Mock) -> None: """Test the General Price sensor.""" - assert len(hass.states.async_all()) == 5 + assert len(hass.states.async_all()) == 6 price = hass.states.get("sensor.mock_title_general_price") assert price assert price.state == "0.08" @@ -143,7 +143,7 @@ async def test_general_price_sensor(hass: HomeAssistant, setup_general: Mock) -> @pytest.mark.usefixtures("setup_general_and_controlled_load") async def test_general_and_controlled_load_price_sensor(hass: HomeAssistant) -> None: """Test the Controlled Price sensor.""" - assert len(hass.states.async_all()) == 8 + assert len(hass.states.async_all()) == 9 price = hass.states.get("sensor.mock_title_controlled_load_price") assert price assert price.state == "0.08" @@ -165,7 +165,7 @@ async def test_general_and_controlled_load_price_sensor(hass: HomeAssistant) -> @pytest.mark.usefixtures("setup_general_and_feed_in") async def test_general_and_feed_in_price_sensor(hass: HomeAssistant) -> None: """Test the Feed In sensor.""" - assert len(hass.states.async_all()) == 8 + assert len(hass.states.async_all()) == 9 price = hass.states.get("sensor.mock_title_feed_in_price") assert price assert price.state == "-0.08" @@ -188,7 +188,7 @@ async def test_general_forecast_sensor( hass: HomeAssistant, setup_general: Mock ) -> None: """Test the General Forecast sensor.""" - assert len(hass.states.async_all()) == 5 + assert len(hass.states.async_all()) == 6 price = hass.states.get("sensor.mock_title_general_forecast") assert price assert price.state == "0.09" @@ -230,7 +230,7 @@ async def test_general_forecast_sensor( @pytest.mark.usefixtures("setup_general_and_controlled_load") async def test_controlled_load_forecast_sensor(hass: HomeAssistant) -> None: """Test the Controlled Load Forecast sensor.""" - assert len(hass.states.async_all()) == 8 + assert len(hass.states.async_all()) == 9 price = hass.states.get("sensor.mock_title_controlled_load_forecast") assert price assert price.state == "0.09" @@ -254,7 +254,7 @@ async def test_controlled_load_forecast_sensor(hass: HomeAssistant) -> None: @pytest.mark.usefixtures("setup_general_and_feed_in") async def test_feed_in_forecast_sensor(hass: HomeAssistant) -> None: """Test the Feed In Forecast sensor.""" - assert len(hass.states.async_all()) == 8 + assert len(hass.states.async_all()) == 9 price = hass.states.get("sensor.mock_title_feed_in_forecast") assert price assert price.state == "-0.09" @@ -278,7 +278,7 @@ async def test_feed_in_forecast_sensor(hass: HomeAssistant) -> None: @pytest.mark.usefixtures("setup_general") def test_renewable_sensor(hass: HomeAssistant) -> None: """Testing the creation of the Amber renewables sensor.""" - assert len(hass.states.async_all()) == 5 + assert len(hass.states.async_all()) == 6 sensor = hass.states.get("sensor.mock_title_renewables") assert sensor assert sensor.state == "51" @@ -287,7 +287,7 @@ def test_renewable_sensor(hass: HomeAssistant) -> None: @pytest.mark.usefixtures("setup_general") def test_general_price_descriptor_descriptor_sensor(hass: HomeAssistant) -> None: """Test the General Price Descriptor sensor.""" - assert len(hass.states.async_all()) == 5 + assert len(hass.states.async_all()) == 6 price = hass.states.get("sensor.mock_title_general_price_descriptor") assert price assert price.state == "extremely_low" @@ -298,7 +298,7 @@ def test_general_and_controlled_load_price_descriptor_sensor( hass: HomeAssistant, ) -> None: """Test the Controlled Price Descriptor sensor.""" - assert len(hass.states.async_all()) == 8 + assert len(hass.states.async_all()) == 9 price = hass.states.get("sensor.mock_title_controlled_load_price_descriptor") assert price assert price.state == "extremely_low" @@ -307,7 +307,7 @@ def test_general_and_controlled_load_price_descriptor_sensor( @pytest.mark.usefixtures("setup_general_and_feed_in") def test_general_and_feed_in_price_descriptor_sensor(hass: HomeAssistant) -> None: """Test the Feed In Price Descriptor sensor.""" - assert len(hass.states.async_all()) == 8 + assert len(hass.states.async_all()) == 9 price = hass.states.get("sensor.mock_title_feed_in_price_descriptor") assert price assert price.state == "extremely_low"