mirror of
https://github.com/home-assistant/core.git
synced 2025-07-25 06:07:17 +00:00
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:
parent
3aa9066a50
commit
526180a8af
@ -5,7 +5,14 @@ from dataclasses import dataclass
|
||||
from datetime import timedelta
|
||||
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.const import Platform
|
||||
@ -13,9 +20,16 @@ from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
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
|
||||
@ -31,9 +45,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
websession = async_get_clientsession(hass)
|
||||
api = PecoOutageApi()
|
||||
# Outage Counter Setup
|
||||
county: str = entry.data[CONF_COUNTY]
|
||||
|
||||
async def async_update_data() -> PECOCoordinatorData:
|
||||
async def async_update_outage_data() -> OutageResults:
|
||||
"""Fetch data from API."""
|
||||
try:
|
||||
outages: OutageResults = (
|
||||
@ -53,15 +68,42 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
hass,
|
||||
LOGGER,
|
||||
name="PECO Outage Count",
|
||||
update_method=async_update_data,
|
||||
update_interval=timedelta(minutes=SCAN_INTERVAL),
|
||||
update_method=async_update_outage_data,
|
||||
update_interval=timedelta(minutes=OUTAGE_SCAN_INTERVAL),
|
||||
)
|
||||
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = {"outage_count": coordinator}
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
59
homeassistant/components/peco/binary_sensor.py
Normal file
59
homeassistant/components/peco/binary_sensor.py
Normal 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
|
@ -1,41 +1,122 @@
|
||||
"""Config flow for PECO Outage Counter integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from peco import (
|
||||
HttpError,
|
||||
IncompatibleMeterError,
|
||||
PecoOutageApi,
|
||||
UnresponsiveMeterError,
|
||||
)
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries
|
||||
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(
|
||||
{
|
||||
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):
|
||||
"""Handle a config flow for PECO Outage Counter."""
|
||||
|
||||
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(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> FlowResult:
|
||||
"""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:
|
||||
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]
|
||||
|
||||
await self.async_set_unique_id(county)
|
||||
if CONF_PHONE_NUMBER not in user_input:
|
||||
await self.async_set_unique_id(county)
|
||||
self._abort_if_unique_id_configured()
|
||||
|
||||
return self.async_create_entry(
|
||||
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()
|
||||
|
||||
return self.async_create_entry(
|
||||
title=f"{county.capitalize()} Outage Count", data=user_input
|
||||
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,
|
||||
)
|
||||
|
@ -14,6 +14,8 @@ COUNTY_LIST: Final = [
|
||||
"TOTAL",
|
||||
]
|
||||
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"
|
||||
ATTR_CONTENT: Final = "content"
|
||||
CONF_PHONE_NUMBER: Final = "phone_number"
|
||||
|
@ -91,7 +91,7 @@ async def async_setup_entry(
|
||||
) -> None:
|
||||
"""Set up the sensor platform."""
|
||||
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(
|
||||
PecoSensor(sensor, county, coordinator) for sensor in SENSOR_LIST
|
||||
|
@ -3,12 +3,26 @@
|
||||
"step": {
|
||||
"user": {
|
||||
"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": {
|
||||
"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": {
|
||||
|
@ -1,6 +1,7 @@
|
||||
"""Test the PECO Outage Counter config flow."""
|
||||
from unittest.mock import patch
|
||||
|
||||
from peco import HttpError, IncompatibleMeterError, UnresponsiveMeterError
|
||||
import pytest
|
||||
from voluptuous.error import MultipleInvalid
|
||||
|
||||
@ -17,6 +18,7 @@ async def test_form(hass: HomeAssistant) -> None:
|
||||
)
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["errors"] is None
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.peco.async_setup_entry",
|
||||
@ -35,6 +37,7 @@ async def test_form(hass: HomeAssistant) -> None:
|
||||
assert result2["data"] == {
|
||||
"county": "PHILADELPHIA",
|
||||
}
|
||||
assert result2["context"]["unique_id"] == "PHILADELPHIA"
|
||||
|
||||
|
||||
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}
|
||||
)
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["errors"] is None
|
||||
|
||||
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
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.peco.async_setup_entry",
|
||||
return_value=True,
|
||||
):
|
||||
second_result2 = await hass.config_entries.flow.async_configure(
|
||||
second_result["flow_id"],
|
||||
), pytest.raises(MultipleInvalid):
|
||||
await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
"county": "PHILADELPHIA",
|
||||
"county": "INVALID_COUNTY_THAT_SHOULDNT_EXIST",
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert second_result2["type"] == FlowResultType.CREATE_ENTRY
|
||||
assert second_result2["title"] == "Philadelphia Outage Count"
|
||||
assert second_result2["data"] == {
|
||||
"county": "PHILADELPHIA",
|
||||
}
|
||||
|
||||
async def test_meter_value_error(hass: HomeAssistant) -> None:
|
||||
"""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",
|
||||
"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"
|
||||
|
@ -2,7 +2,13 @@
|
||||
import asyncio
|
||||
from unittest.mock import patch
|
||||
|
||||
from peco import AlertResults, BadJSONError, HttpError, OutageResults
|
||||
from peco import (
|
||||
AlertResults,
|
||||
BadJSONError,
|
||||
HttpError,
|
||||
OutageResults,
|
||||
UnresponsiveMeterError,
|
||||
)
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.peco.const import DOMAIN
|
||||
@ -14,6 +20,7 @@ from tests.common import MockConfigEntry
|
||||
MOCK_ENTRY_DATA = {"county": "TOTAL"}
|
||||
COUNTY_ENTRY_DATA = {"county": "BUCKS"}
|
||||
INVALID_COUNTY_DATA = {"county": "INVALID"}
|
||||
METER_DATA = {"county": "BUCKS", "phone_number": "1234567890"}
|
||||
|
||||
|
||||
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 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
|
||||
|
Loading…
x
Reference in New Issue
Block a user