Add PECO smart meter binary_sensor (#71034)

* Add support for PECO smart meter

* Add support for PECO smart meter

* Conform to black

* Fix tests and additional clean-up

* Return init file to original state

* Move to FlowResultType

* Catch up to upstream

* Remove commented code

* isort

* Merge smart meter and outage count into one entry

* Test coverage

* Remove logging exceptions from config flow verification

* Fix comments from @emontnemery

* Revert "Add support for PECO smart meter"

This reverts commit 36ca90856684f328e71bc3778fa7aa52a6bde5ca.

* More fixes
This commit is contained in:
IceBotYT 2023-11-29 03:08:27 -05:00 committed by GitHub
parent 3aa9066a50
commit 526180a8af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 525 additions and 43 deletions

View File

@ -5,7 +5,14 @@ from dataclasses import dataclass
from datetime import timedelta from datetime import timedelta
from typing import Final from typing import Final
from peco import AlertResults, BadJSONError, HttpError, OutageResults, PecoOutageApi from peco import (
AlertResults,
BadJSONError,
HttpError,
OutageResults,
PecoOutageApi,
UnresponsiveMeterError,
)
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform from homeassistant.const import Platform
@ -13,9 +20,16 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import CONF_COUNTY, DOMAIN, LOGGER, SCAN_INTERVAL from .const import (
CONF_COUNTY,
CONF_PHONE_NUMBER,
DOMAIN,
LOGGER,
OUTAGE_SCAN_INTERVAL,
SMART_METER_SCAN_INTERVAL,
)
PLATFORMS: Final = [Platform.SENSOR] PLATFORMS: Final = [Platform.SENSOR, Platform.BINARY_SENSOR]
@dataclass @dataclass
@ -31,9 +45,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
websession = async_get_clientsession(hass) websession = async_get_clientsession(hass)
api = PecoOutageApi() api = PecoOutageApi()
# Outage Counter Setup
county: str = entry.data[CONF_COUNTY] county: str = entry.data[CONF_COUNTY]
async def async_update_data() -> PECOCoordinatorData: async def async_update_outage_data() -> OutageResults:
"""Fetch data from API.""" """Fetch data from API."""
try: try:
outages: OutageResults = ( outages: OutageResults = (
@ -53,15 +68,42 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
hass, hass,
LOGGER, LOGGER,
name="PECO Outage Count", name="PECO Outage Count",
update_method=async_update_data, update_method=async_update_outage_data,
update_interval=timedelta(minutes=SCAN_INTERVAL), update_interval=timedelta(minutes=OUTAGE_SCAN_INTERVAL),
) )
await coordinator.async_config_entry_first_refresh() await coordinator.async_config_entry_first_refresh()
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator hass.data.setdefault(DOMAIN, {})[entry.entry_id] = {"outage_count": coordinator}
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
if phone_number := entry.data.get(CONF_PHONE_NUMBER):
# Smart Meter Setup]
async def async_update_meter_data() -> bool:
"""Fetch data from API."""
try:
data: bool = await api.meter_check(phone_number, websession)
except UnresponsiveMeterError as err:
raise UpdateFailed("Unresponsive meter") from err
except HttpError as err:
raise UpdateFailed(f"Error fetching data: {err}") from err
except BadJSONError as err:
raise UpdateFailed(f"Error parsing data: {err}") from err
return data
coordinator = DataUpdateCoordinator(
hass,
LOGGER,
name="PECO Smart Meter",
update_method=async_update_meter_data,
update_interval=timedelta(minutes=SMART_METER_SCAN_INTERVAL),
)
await coordinator.async_config_entry_first_refresh()
hass.data[DOMAIN][entry.entry_id]["smart_meter"] = coordinator
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True return True

View File

@ -0,0 +1,59 @@
"""Binary sensor for PECO outage counter."""
from __future__ import annotations
from typing import Final
from homeassistant.components.binary_sensor import (
BinarySensorDeviceClass,
BinarySensorEntity,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
DataUpdateCoordinator,
)
from .const import DOMAIN
PARALLEL_UPDATES: Final = 0
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up binary sensor for PECO."""
if "smart_meter" not in hass.data[DOMAIN][config_entry.entry_id]:
return
coordinator: DataUpdateCoordinator[bool] = hass.data[DOMAIN][config_entry.entry_id][
"smart_meter"
]
async_add_entities(
[PecoBinarySensor(coordinator, phone_number=config_entry.data["phone_number"])]
)
class PecoBinarySensor(
CoordinatorEntity[DataUpdateCoordinator[bool]], BinarySensorEntity
):
"""Binary sensor for PECO outage counter."""
_attr_icon = "mdi:gauge"
_attr_device_class = BinarySensorDeviceClass.POWER
_attr_name = "Meter Status"
def __init__(
self, coordinator: DataUpdateCoordinator[bool], phone_number: str
) -> None:
"""Initialize binary sensor for PECO."""
super().__init__(coordinator)
self._attr_unique_id = f"{phone_number}"
@property
def is_on(self) -> bool:
"""Return if the meter has power."""
return self.coordinator.data

View File

@ -1,41 +1,122 @@
"""Config flow for PECO Outage Counter integration.""" """Config flow for PECO Outage Counter integration."""
from __future__ import annotations from __future__ import annotations
import logging
from typing import Any from typing import Any
from peco import (
HttpError,
IncompatibleMeterError,
PecoOutageApi,
UnresponsiveMeterError,
)
import voluptuous as vol import voluptuous as vol
from homeassistant import config_entries from homeassistant import config_entries
from homeassistant.data_entry_flow import FlowResult from homeassistant.data_entry_flow import FlowResult
from homeassistant.helpers import config_validation as cv
from .const import CONF_COUNTY, COUNTY_LIST, DOMAIN from .const import CONF_COUNTY, CONF_PHONE_NUMBER, COUNTY_LIST, DOMAIN
STEP_USER_DATA_SCHEMA = vol.Schema( STEP_USER_DATA_SCHEMA = vol.Schema(
{ {
vol.Required(CONF_COUNTY): vol.In(COUNTY_LIST), vol.Required(CONF_COUNTY): vol.In(COUNTY_LIST),
vol.Optional(CONF_PHONE_NUMBER): cv.string,
} }
) )
_LOGGER = logging.getLogger(__name__)
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a config flow for PECO Outage Counter.""" """Handle a config flow for PECO Outage Counter."""
VERSION = 1 VERSION = 1
meter_verification: bool = False
meter_data: dict[str, str] = {}
meter_error: dict[str, str] = {}
async def _verify_meter(self, phone_number: str) -> None:
"""Verify if the meter is compatible."""
api = PecoOutageApi()
try:
await api.meter_check(phone_number)
except ValueError:
self.meter_error = {"phone_number": "invalid_phone_number", "type": "error"}
except IncompatibleMeterError:
self.meter_error = {"phone_number": "incompatible_meter", "type": "abort"}
except UnresponsiveMeterError:
self.meter_error = {"phone_number": "unresponsive_meter", "type": "error"}
except HttpError:
self.meter_error = {"phone_number": "http_error", "type": "error"}
self.hass.async_create_task(
self.hass.config_entries.flow.async_configure(flow_id=self.flow_id)
)
async def async_step_user( async def async_step_user(
self, user_input: dict[str, Any] | None = None self, user_input: dict[str, Any] | None = None
) -> FlowResult: ) -> FlowResult:
"""Handle the initial step.""" """Handle the initial step."""
if self.meter_verification is True:
return self.async_show_progress_done(next_step_id="finish_smart_meter")
if user_input is None: if user_input is None:
return self.async_show_form( return self.async_show_form(
step_id="user", data_schema=STEP_USER_DATA_SCHEMA step_id="user",
data_schema=STEP_USER_DATA_SCHEMA,
) )
county = user_input[CONF_COUNTY] county = user_input[CONF_COUNTY]
if CONF_PHONE_NUMBER not in user_input:
await self.async_set_unique_id(county) await self.async_set_unique_id(county)
self._abort_if_unique_id_configured() self._abort_if_unique_id_configured()
return self.async_create_entry( return self.async_create_entry(
title=f"{county.capitalize()} Outage Count", data=user_input title=f"{user_input[CONF_COUNTY].capitalize()} Outage Count",
data=user_input,
)
phone_number = user_input[CONF_PHONE_NUMBER]
await self.async_set_unique_id(f"{county}-{phone_number}")
self._abort_if_unique_id_configured()
self.meter_verification = True
if self.meter_error is not None:
# Clear any previous errors, since the user may have corrected them
self.meter_error = {}
self.hass.async_create_task(self._verify_meter(phone_number))
self.meter_data = user_input
return self.async_show_progress(
step_id="user",
progress_action="verifying_meter",
)
async def async_step_finish_smart_meter(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Handle the finish smart meter step."""
if "phone_number" in self.meter_error:
if self.meter_error["type"] == "error":
self.meter_verification = False
return self.async_show_form(
step_id="user",
data_schema=STEP_USER_DATA_SCHEMA,
errors={"phone_number": self.meter_error["phone_number"]},
)
return self.async_abort(reason=self.meter_error["phone_number"])
return self.async_create_entry(
title=f"{self.meter_data[CONF_COUNTY].capitalize()} - {self.meter_data[CONF_PHONE_NUMBER]}",
data=self.meter_data,
) )

View File

@ -14,6 +14,8 @@ COUNTY_LIST: Final = [
"TOTAL", "TOTAL",
] ]
CONFIG_FLOW_COUNTIES: Final = [{county: county.capitalize()} for county in COUNTY_LIST] CONFIG_FLOW_COUNTIES: Final = [{county: county.capitalize()} for county in COUNTY_LIST]
SCAN_INTERVAL: Final = 9 OUTAGE_SCAN_INTERVAL: Final = 9 # minutes
SMART_METER_SCAN_INTERVAL: Final = 15 # minutes
CONF_COUNTY: Final = "county" CONF_COUNTY: Final = "county"
ATTR_CONTENT: Final = "content" ATTR_CONTENT: Final = "content"
CONF_PHONE_NUMBER: Final = "phone_number"

View File

@ -91,7 +91,7 @@ async def async_setup_entry(
) -> None: ) -> None:
"""Set up the sensor platform.""" """Set up the sensor platform."""
county: str = config_entry.data[CONF_COUNTY] county: str = config_entry.data[CONF_COUNTY]
coordinator = hass.data[DOMAIN][config_entry.entry_id] coordinator = hass.data[DOMAIN][config_entry.entry_id]["outage_count"]
async_add_entities( async_add_entities(
PecoSensor(sensor, county, coordinator) for sensor in SENSOR_LIST PecoSensor(sensor, county, coordinator) for sensor in SENSOR_LIST

View File

@ -3,12 +3,26 @@
"step": { "step": {
"user": { "user": {
"data": { "data": {
"county": "County" "county": "County",
"phone_number": "Phone Number"
},
"data_description": {
"county": "County used for outage number retrieval",
"phone_number": "Phone number associated with the PECO account (optional). Adding a phone number adds a binary sensor confirming if your power is out or not, and not an issue with a breaker or an issue on your end."
} }
} }
}, },
"abort": { "abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]" "already_configured": "[%key:common::config_flow::abort::already_configured_service%]",
"incompatible_meter": "Your meter is not compatible with smart meter checking."
},
"progress": {
"verifying_meter": "One moment. Verifying that your meter is compatible. This may take a minute or two."
},
"error": {
"invalid_phone_number": "Please enter a valid phone number.",
"unresponsive_meter": "Your meter is not responding. Please try again later.",
"http_error": "There was an error communicating with PECO. The issue that is most likely is that you entered an invalid phone number. Please check the phone number or try again later."
} }
}, },
"entity": { "entity": {

View File

@ -1,6 +1,7 @@
"""Test the PECO Outage Counter config flow.""" """Test the PECO Outage Counter config flow."""
from unittest.mock import patch from unittest.mock import patch
from peco import HttpError, IncompatibleMeterError, UnresponsiveMeterError
import pytest import pytest
from voluptuous.error import MultipleInvalid from voluptuous.error import MultipleInvalid
@ -17,6 +18,7 @@ async def test_form(hass: HomeAssistant) -> None:
) )
assert result["type"] == FlowResultType.FORM assert result["type"] == FlowResultType.FORM
assert result["errors"] is None assert result["errors"] is None
assert result["step_id"] == "user"
with patch( with patch(
"homeassistant.components.peco.async_setup_entry", "homeassistant.components.peco.async_setup_entry",
@ -35,6 +37,7 @@ async def test_form(hass: HomeAssistant) -> None:
assert result2["data"] == { assert result2["data"] == {
"county": "PHILADELPHIA", "county": "PHILADELPHIA",
} }
assert result2["context"]["unique_id"] == "PHILADELPHIA"
async def test_invalid_county(hass: HomeAssistant) -> None: async def test_invalid_county(hass: HomeAssistant) -> None:
@ -43,37 +46,160 @@ async def test_invalid_county(hass: HomeAssistant) -> None:
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
) )
assert result["type"] == FlowResultType.FORM assert result["type"] == FlowResultType.FORM
assert result["errors"] is None assert result["step_id"] == "user"
with pytest.raises(MultipleInvalid):
await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"county": "INVALID_COUNTY_THAT_SHOULD_NOT_EXIST",
},
)
await hass.async_block_till_done()
second_result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert second_result["type"] == FlowResultType.FORM
assert second_result["errors"] is None
with patch( with patch(
"homeassistant.components.peco.async_setup_entry", "homeassistant.components.peco.async_setup_entry",
return_value=True, return_value=True,
): ), pytest.raises(MultipleInvalid):
second_result2 = await hass.config_entries.flow.async_configure( await hass.config_entries.flow.async_configure(
second_result["flow_id"], result["flow_id"],
{ {
"county": "PHILADELPHIA", "county": "INVALID_COUNTY_THAT_SHOULDNT_EXIST",
}, },
) )
await hass.async_block_till_done() await hass.async_block_till_done()
assert second_result2["type"] == FlowResultType.CREATE_ENTRY
assert second_result2["title"] == "Philadelphia Outage Count" async def test_meter_value_error(hass: HomeAssistant) -> None:
assert second_result2["data"] == { """Test if the MeterValueError error works."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] == FlowResultType.FORM
assert result["step_id"] == "user"
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"county": "PHILADELPHIA", "county": "PHILADELPHIA",
} "phone_number": "INVALID_SMART_METER_THAT_SHOULD_NOT_EXIST",
},
)
await hass.async_block_till_done()
assert result["type"] == FlowResultType.SHOW_PROGRESS
assert result["step_id"] == "user"
assert result["progress_action"] == "verifying_meter"
result = await hass.config_entries.flow.async_configure(result["flow_id"])
assert result["type"] == FlowResultType.FORM
assert result["step_id"] == "user"
assert result["errors"] == {"phone_number": "invalid_phone_number"}
async def test_incompatible_meter_error(hass: HomeAssistant) -> None:
"""Test if the IncompatibleMeter error works."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] == FlowResultType.FORM
assert result["step_id"] == "user"
with patch("peco.PecoOutageApi.meter_check", side_effect=IncompatibleMeterError()):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"county": "PHILADELPHIA",
"phone_number": "1234567890",
},
)
await hass.async_block_till_done()
assert result["type"] == FlowResultType.SHOW_PROGRESS
assert result["step_id"] == "user"
assert result["progress_action"] == "verifying_meter"
result = await hass.config_entries.flow.async_configure(result["flow_id"])
assert result["type"] == FlowResultType.ABORT
assert result["reason"] == "incompatible_meter"
async def test_unresponsive_meter_error(hass: HomeAssistant) -> None:
"""Test if the UnresponsiveMeter error works."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] == FlowResultType.FORM
assert result["step_id"] == "user"
with patch("peco.PecoOutageApi.meter_check", side_effect=UnresponsiveMeterError()):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"county": "PHILADELPHIA",
"phone_number": "1234567890",
},
)
await hass.async_block_till_done()
assert result["type"] == FlowResultType.SHOW_PROGRESS
assert result["step_id"] == "user"
assert result["progress_action"] == "verifying_meter"
result = await hass.config_entries.flow.async_configure(result["flow_id"])
assert result["type"] == FlowResultType.FORM
assert result["step_id"] == "user"
assert result["errors"] == {"phone_number": "unresponsive_meter"}
async def test_meter_http_error(hass: HomeAssistant) -> None:
"""Test if the InvalidMeter error works."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] == FlowResultType.FORM
assert result["step_id"] == "user"
with patch("peco.PecoOutageApi.meter_check", side_effect=HttpError):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"county": "PHILADELPHIA",
"phone_number": "1234567890",
},
)
await hass.async_block_till_done()
assert result["type"] == FlowResultType.SHOW_PROGRESS
assert result["step_id"] == "user"
assert result["progress_action"] == "verifying_meter"
result = await hass.config_entries.flow.async_configure(result["flow_id"])
assert result["type"] == FlowResultType.FORM
assert result["step_id"] == "user"
assert result["errors"] == {"phone_number": "http_error"}
async def test_smart_meter(hass: HomeAssistant) -> None:
"""Test if the Smart Meter step works."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] == FlowResultType.FORM
assert result["step_id"] == "user"
with patch("peco.PecoOutageApi.meter_check", return_value=True):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"county": "PHILADELPHIA",
"phone_number": "1234567890",
},
)
await hass.async_block_till_done()
assert result["type"] == FlowResultType.SHOW_PROGRESS
assert result["step_id"] == "user"
assert result["progress_action"] == "verifying_meter"
result = await hass.config_entries.flow.async_configure(result["flow_id"])
assert result["type"] == FlowResultType.CREATE_ENTRY
assert result["title"] == "Philadelphia - 1234567890"
assert result["data"]["phone_number"] == "1234567890"
assert result["context"]["unique_id"] == "PHILADELPHIA-1234567890"

View File

@ -2,7 +2,13 @@
import asyncio import asyncio
from unittest.mock import patch from unittest.mock import patch
from peco import AlertResults, BadJSONError, HttpError, OutageResults from peco import (
AlertResults,
BadJSONError,
HttpError,
OutageResults,
UnresponsiveMeterError,
)
import pytest import pytest
from homeassistant.components.peco.const import DOMAIN from homeassistant.components.peco.const import DOMAIN
@ -14,6 +20,7 @@ from tests.common import MockConfigEntry
MOCK_ENTRY_DATA = {"county": "TOTAL"} MOCK_ENTRY_DATA = {"county": "TOTAL"}
COUNTY_ENTRY_DATA = {"county": "BUCKS"} COUNTY_ENTRY_DATA = {"county": "BUCKS"}
INVALID_COUNTY_DATA = {"county": "INVALID"} INVALID_COUNTY_DATA = {"county": "INVALID"}
METER_DATA = {"county": "BUCKS", "phone_number": "1234567890"}
async def test_unload_entry(hass: HomeAssistant) -> None: async def test_unload_entry(hass: HomeAssistant) -> None:
@ -149,3 +156,154 @@ async def test_bad_json(hass: HomeAssistant, sensor: str) -> None:
assert hass.states.get(f"sensor.{sensor}") is None assert hass.states.get(f"sensor.{sensor}") is None
assert config_entry.state == ConfigEntryState.SETUP_RETRY assert config_entry.state == ConfigEntryState.SETUP_RETRY
async def test_unresponsive_meter_error(hass: HomeAssistant) -> None:
"""Test if it raises an error when the meter will not respond."""
config_entry = MockConfigEntry(domain=DOMAIN, data=METER_DATA)
config_entry.add_to_hass(hass)
with patch(
"peco.PecoOutageApi.meter_check",
side_effect=UnresponsiveMeterError(),
), patch(
"peco.PecoOutageApi.get_outage_count",
return_value=OutageResults(
customers_out=0,
percent_customers_out=0,
outage_count=0,
customers_served=350394,
),
), patch(
"peco.PecoOutageApi.get_map_alerts",
return_value=AlertResults(
alert_content="Testing 1234", alert_title="Testing 4321"
),
):
assert not await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
assert hass.states.get("binary_sensor.meter_status") is None
assert config_entry.state == ConfigEntryState.SETUP_RETRY
async def test_meter_http_error(hass: HomeAssistant) -> None:
"""Test if it raises an error when there is an HTTP error."""
config_entry = MockConfigEntry(domain=DOMAIN, data=METER_DATA)
config_entry.add_to_hass(hass)
with patch(
"peco.PecoOutageApi.meter_check",
side_effect=HttpError(),
), patch(
"peco.PecoOutageApi.get_outage_count",
return_value=OutageResults(
customers_out=0,
percent_customers_out=0,
outage_count=0,
customers_served=350394,
),
), patch(
"peco.PecoOutageApi.get_map_alerts",
return_value=AlertResults(
alert_content="Testing 1234", alert_title="Testing 4321"
),
):
assert not await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
assert hass.states.get("binary_sensor.meter_status") is None
assert config_entry.state == ConfigEntryState.SETUP_RETRY
async def test_meter_bad_json(hass: HomeAssistant) -> None:
"""Test if it raises an error when there is bad JSON."""
config_entry = MockConfigEntry(domain=DOMAIN, data=METER_DATA)
config_entry.add_to_hass(hass)
with patch(
"peco.PecoOutageApi.meter_check",
side_effect=BadJSONError(),
), patch(
"peco.PecoOutageApi.get_outage_count",
return_value=OutageResults(
customers_out=0,
percent_customers_out=0,
outage_count=0,
customers_served=350394,
),
), patch(
"peco.PecoOutageApi.get_map_alerts",
return_value=AlertResults(
alert_content="Testing 1234", alert_title="Testing 4321"
),
):
assert not await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
assert hass.states.get("binary_sensor.meter_status") is None
assert config_entry.state == ConfigEntryState.SETUP_RETRY
async def test_meter_timeout(hass: HomeAssistant) -> None:
"""Test if it raises an error when there is a timeout."""
config_entry = MockConfigEntry(domain=DOMAIN, data=METER_DATA)
config_entry.add_to_hass(hass)
with patch(
"peco.PecoOutageApi.meter_check",
side_effect=asyncio.TimeoutError(),
), patch(
"peco.PecoOutageApi.get_outage_count",
return_value=OutageResults(
customers_out=0,
percent_customers_out=0,
outage_count=0,
customers_served=350394,
),
), patch(
"peco.PecoOutageApi.get_map_alerts",
return_value=AlertResults(
alert_content="Testing 1234", alert_title="Testing 4321"
),
):
assert not await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
assert hass.states.get("binary_sensor.meter_status") is None
assert config_entry.state == ConfigEntryState.SETUP_RETRY
async def test_meter_data(hass: HomeAssistant) -> None:
"""Test if the meter returns the value successfully."""
config_entry = MockConfigEntry(domain=DOMAIN, data=METER_DATA)
config_entry.add_to_hass(hass)
with patch(
"peco.PecoOutageApi.meter_check",
return_value=True,
), patch(
"peco.PecoOutageApi.get_outage_count",
return_value=OutageResults(
customers_out=0,
percent_customers_out=0,
outage_count=0,
customers_served=350394,
),
), patch(
"peco.PecoOutageApi.get_map_alerts",
return_value=AlertResults(
alert_content="Testing 1234", alert_title="Testing 4321"
),
):
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
assert hass.states.get("binary_sensor.meter_status") is not None
assert hass.states.get("binary_sensor.meter_status").state == "on"
assert config_entry.state == ConfigEntryState.LOADED