mirror of
https://github.com/home-assistant/core.git
synced 2025-04-23 16:57:53 +00:00
Add PECO power outage counter integration (#65194)
* Create a new NWS Alerts integration * Create a new NWS Alerts integration * Create new PECO integration * Remove empty keys * Revert "Create a new NWS Alerts integration" This reverts commit 38309c5a878d78f26df56a62e56cb602d9dc9a9e. * Revert "Create a new NWS Alerts integration" This reverts commit aeabdd37b86c370bdb8009e885806bdac6e464d8. * Fix test with new mock data * Add init and sensor to .coveragerc and more tests for config flow * Small fixes and replacing patch with pytest.raises in testing invalid county * Add type defs and fix test_config_flow to use MultipleValid instead * Fix issues with 'typing.Dict' * Move API communication to seperate PyPI library * Switch PyPI library from httpx to aiohttp to allow for passing in websessions * Commit file changes requested by farmio as listed here:d267e4300a
* Add suggestions requested by farmio as listed here:586d8ffa42
* Move native_unit_of_measurement from prop to attr * Update PLATFORMS constant type annotation Co-authored-by: Matthias Alphart <farmio@alphart.net> * Add peco to .strict-typing I am from school so I can't run mypy atm * Forgot to import Final * Do as requested [here](https://github.com/home-assistant/core/runs/5070634928?check_suite_focus=true) * Updated mypy.ini, checks should pass now * Fix to conform to mypy restrictions https://github.com/home-assistant/core/runs/5072861837\?check_suite_focus\=true * Fix type annotations * Fix tests * Use cast in async_update_data * Add data type to CoordinatorEntity and DataUpdateCoordinator * More cleanup from suggestions here: https://github.com/home-assistant/core/pull/65194\#pullrequestreview-908183493 * Fix tests for new code * Cleaning up a speck of dust * Remove unused variable from the peco sensor * Add rounding to percentage, and extra clean-up * Final suggestions from @farmio * Update SCAN_INTERVAL to be a little bit faster * Change the SCAN_INTERVAL to be somewhat near the update interval of the outage map, as noted by farmio * New UpdateCoordinator typing
This commit is contained in:
parent
247af2e74f
commit
a43505a0a3
@ -151,6 +151,7 @@ homeassistant.components.oncue.*
|
||||
homeassistant.components.onewire.*
|
||||
homeassistant.components.open_meteo.*
|
||||
homeassistant.components.openuv.*
|
||||
homeassistant.components.peco.*
|
||||
homeassistant.components.overkiz.*
|
||||
homeassistant.components.persistent_notification.*
|
||||
homeassistant.components.pi_hole.*
|
||||
|
@ -741,6 +741,8 @@ homeassistant/components/panel_custom/* @home-assistant/frontend
|
||||
tests/components/panel_custom/* @home-assistant/frontend
|
||||
homeassistant/components/panel_iframe/* @home-assistant/frontend
|
||||
tests/components/panel_iframe/* @home-assistant/frontend
|
||||
homeassistant/components/peco/* @IceBotYT
|
||||
tests/components/peco/* @IceBotYT
|
||||
homeassistant/components/persistent_notification/* @home-assistant/core
|
||||
tests/components/persistent_notification/* @home-assistant/core
|
||||
homeassistant/components/philips_js/* @elupus
|
||||
|
31
homeassistant/components/peco/__init__.py
Normal file
31
homeassistant/components/peco/__init__.py
Normal file
@ -0,0 +1,31 @@
|
||||
"""The PECO Outage Counter integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Final
|
||||
|
||||
import peco
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
PLATFORMS: Final = [Platform.SENSOR]
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up PECO Outage Counter from a config entry."""
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = peco.PecoOutageApi()
|
||||
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
43
homeassistant/components/peco/config_flow.py
Normal file
43
homeassistant/components/peco/config_flow.py
Normal file
@ -0,0 +1,43 @@
|
||||
"""Config flow for PECO Outage Counter integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
|
||||
from .const import CONF_COUNTY, COUNTY_LIST, DOMAIN
|
||||
|
||||
STEP_USER_DATA_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_COUNTY): vol.In(COUNTY_LIST),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
"""Handle a config flow for PECO Outage Counter."""
|
||||
|
||||
VERSION = 1
|
||||
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> FlowResult:
|
||||
"""Handle the initial step."""
|
||||
if user_input is None:
|
||||
return self.async_show_form(
|
||||
step_id="user", data_schema=STEP_USER_DATA_SCHEMA
|
||||
)
|
||||
|
||||
county = user_input[
|
||||
CONF_COUNTY
|
||||
] # Voluptuous automatically detects if the county is invalid
|
||||
|
||||
await self.async_set_unique_id(county)
|
||||
self._abort_if_unique_id_configured()
|
||||
|
||||
return self.async_create_entry(
|
||||
title=f"{county.capitalize()} Outage Count", data=user_input
|
||||
)
|
17
homeassistant/components/peco/const.py
Normal file
17
homeassistant/components/peco/const.py
Normal file
@ -0,0 +1,17 @@
|
||||
"""Constants for the PECO Outage Counter integration."""
|
||||
import logging
|
||||
from typing import Final
|
||||
|
||||
DOMAIN: Final = "peco"
|
||||
LOGGER: Final = logging.getLogger(__package__)
|
||||
COUNTY_LIST: Final = [
|
||||
"BUCKS",
|
||||
"CHESTER",
|
||||
"DELAWARE",
|
||||
"MONTGOMERY",
|
||||
"PHILADELPHIA",
|
||||
"YORK",
|
||||
"TOTAL",
|
||||
]
|
||||
SCAN_INTERVAL: Final = 9
|
||||
CONF_COUNTY: Final = "county"
|
13
homeassistant/components/peco/manifest.json
Normal file
13
homeassistant/components/peco/manifest.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"domain": "peco",
|
||||
"name": "PECO Outage Counter",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/peco",
|
||||
"codeowners": [
|
||||
"@IceBotYT"
|
||||
],
|
||||
"iot_class": "cloud_polling",
|
||||
"requirements": [
|
||||
"peco==0.0.21"
|
||||
]
|
||||
}
|
113
homeassistant/components/peco/sensor.py
Normal file
113
homeassistant/components/peco/sensor.py
Normal file
@ -0,0 +1,113 @@
|
||||
"""Sensor component for PECO outage counter."""
|
||||
import asyncio
|
||||
from datetime import timedelta
|
||||
from typing import Final, cast
|
||||
|
||||
from peco import BadJSONError, HttpError
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
SensorEntity,
|
||||
SensorEntityDescription,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import PERCENTAGE
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import (
|
||||
CoordinatorEntity,
|
||||
DataUpdateCoordinator,
|
||||
UpdateFailed,
|
||||
)
|
||||
|
||||
from .const import CONF_COUNTY, DOMAIN, LOGGER, SCAN_INTERVAL
|
||||
|
||||
PARALLEL_UPDATES: Final = 0
|
||||
SENSOR_LIST = (
|
||||
SensorEntityDescription(key="customers_out", name="Customers Out"),
|
||||
SensorEntityDescription(
|
||||
key="percent_customers_out",
|
||||
name="Percent Customers Out",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
),
|
||||
SensorEntityDescription(key="outage_count", name="Outage Count"),
|
||||
SensorEntityDescription(key="customers_served", name="Customers Served"),
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the sensor platform."""
|
||||
api = hass.data[DOMAIN][config_entry.entry_id]
|
||||
websession = async_get_clientsession(hass)
|
||||
county: str = config_entry.data[CONF_COUNTY]
|
||||
|
||||
async def async_update_data() -> dict[str, float]:
|
||||
"""Fetch data from API."""
|
||||
try:
|
||||
data = (
|
||||
cast(dict[str, float], await api.get_outage_totals(websession))
|
||||
if county == "TOTAL"
|
||||
else cast(
|
||||
dict[str, float],
|
||||
await api.get_outage_count(county, websession),
|
||||
)
|
||||
)
|
||||
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
|
||||
except asyncio.TimeoutError as err:
|
||||
raise UpdateFailed(f"Timeout fetching data: {err}") from err
|
||||
if data["percent_customers_out"] < 5:
|
||||
percent_out = round(
|
||||
data["customers_out"] / data["customers_served"] * 100, 3
|
||||
)
|
||||
data["percent_customers_out"] = percent_out
|
||||
return data
|
||||
|
||||
coordinator = DataUpdateCoordinator(
|
||||
hass,
|
||||
LOGGER,
|
||||
name="PECO Outage Count",
|
||||
update_method=async_update_data,
|
||||
update_interval=timedelta(minutes=SCAN_INTERVAL),
|
||||
)
|
||||
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
||||
async_add_entities(
|
||||
[PecoSensor(sensor, county, coordinator) for sensor in SENSOR_LIST],
|
||||
True,
|
||||
)
|
||||
return
|
||||
|
||||
|
||||
class PecoSensor(
|
||||
CoordinatorEntity[DataUpdateCoordinator[dict[str, float]]], SensorEntity
|
||||
):
|
||||
"""PECO outage counter sensor."""
|
||||
|
||||
_attr_state_class = SensorStateClass.MEASUREMENT
|
||||
_attr_icon: str = "mdi:power-plug-off"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
description: SensorEntityDescription,
|
||||
county: str,
|
||||
coordinator: DataUpdateCoordinator[dict[str, float]],
|
||||
) -> None:
|
||||
"""Initialize the sensor."""
|
||||
super().__init__(coordinator)
|
||||
self._attr_name = f"{county.capitalize()} {description.name}"
|
||||
self._attr_unique_id = f"{county}-{description.key}"
|
||||
self.entity_description = description
|
||||
|
||||
@property
|
||||
def native_value(self) -> float:
|
||||
"""Return the value of the sensor."""
|
||||
return self.coordinator.data[self.entity_description.key]
|
16
homeassistant/components/peco/strings.json
Normal file
16
homeassistant/components/peco/strings.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "PECO Outage Counter",
|
||||
"description": "Please choose your county below.",
|
||||
"data": {
|
||||
"county": "County"
|
||||
}
|
||||
}
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]"
|
||||
}
|
||||
}
|
||||
}
|
16
homeassistant/components/peco/translations/en.json
Normal file
16
homeassistant/components/peco/translations/en.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Service is already configured"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"county": "County"
|
||||
},
|
||||
"description": "Please choose your county below.",
|
||||
"title": "PECO Outage Counter"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -249,6 +249,7 @@ FLOWS = {
|
||||
"owntracks",
|
||||
"p1_monitor",
|
||||
"panasonic_viera",
|
||||
"peco",
|
||||
"philips_js",
|
||||
"pi_hole",
|
||||
"picnic",
|
||||
|
11
mypy.ini
11
mypy.ini
@ -1463,6 +1463,17 @@ no_implicit_optional = true
|
||||
warn_return_any = true
|
||||
warn_unreachable = true
|
||||
|
||||
[mypy-homeassistant.components.peco.*]
|
||||
check_untyped_defs = true
|
||||
disallow_incomplete_defs = true
|
||||
disallow_subclassing_any = true
|
||||
disallow_untyped_calls = true
|
||||
disallow_untyped_decorators = true
|
||||
disallow_untyped_defs = true
|
||||
no_implicit_optional = true
|
||||
warn_return_any = true
|
||||
warn_unreachable = true
|
||||
|
||||
[mypy-homeassistant.components.overkiz.*]
|
||||
check_untyped_defs = true
|
||||
disallow_incomplete_defs = true
|
||||
|
@ -1177,6 +1177,9 @@ panasonic_viera==0.3.6
|
||||
# homeassistant.components.dunehd
|
||||
pdunehd==1.3.2
|
||||
|
||||
# homeassistant.components.peco
|
||||
peco==0.0.21
|
||||
|
||||
# homeassistant.components.pencom
|
||||
pencompy==0.0.3
|
||||
|
||||
|
@ -768,6 +768,9 @@ panasonic_viera==0.3.6
|
||||
# homeassistant.components.dunehd
|
||||
pdunehd==1.3.2
|
||||
|
||||
# homeassistant.components.peco
|
||||
peco==0.0.21
|
||||
|
||||
# homeassistant.components.aruba
|
||||
# homeassistant.components.cisco_ios
|
||||
# homeassistant.components.pandora
|
||||
|
1
tests/components/peco/__init__.py
Normal file
1
tests/components/peco/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
"""Tests for the PECO Outage Counter integration."""
|
57
tests/components/peco/test_config_flow.py
Normal file
57
tests/components/peco/test_config_flow.py
Normal file
@ -0,0 +1,57 @@
|
||||
"""Test the PECO Outage Counter config flow."""
|
||||
from unittest.mock import patch
|
||||
|
||||
from pytest import raises
|
||||
from voluptuous.error import MultipleInvalid
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.peco.const import DOMAIN
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import RESULT_TYPE_CREATE_ENTRY, RESULT_TYPE_FORM
|
||||
|
||||
|
||||
async def test_form(hass: HomeAssistant) -> None:
|
||||
"""Test we get the form."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["errors"] is None
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.peco.async_setup_entry",
|
||||
return_value=True,
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
"county": "PHILADELPHIA",
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result2["type"] == RESULT_TYPE_CREATE_ENTRY
|
||||
assert result2["title"] == "Philadelphia Outage Count"
|
||||
assert result2["data"] == {
|
||||
"county": "PHILADELPHIA",
|
||||
}
|
||||
|
||||
|
||||
async def test_invalid_county(hass: HomeAssistant) -> None:
|
||||
"""Test if the InvalidCounty error works."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["errors"] is None
|
||||
|
||||
with 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()
|
||||
|
||||
# it should have errored, instead of returning an errors dict, since this error should never happen
|
38
tests/components/peco/test_init.py
Normal file
38
tests/components/peco/test_init.py
Normal file
@ -0,0 +1,38 @@
|
||||
"""Test the PECO Outage Counter init file."""
|
||||
from peco import PecoOutageApi
|
||||
|
||||
from homeassistant.components.peco.const import DOMAIN
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
MOCK_ENTRY_DATA = {"county": "TOTAL"}
|
||||
INVALID_COUNTY_DATA = {"county": "INVALID_COUNTY_THAT_SHOULD_NOT_EXIST", "test": True}
|
||||
|
||||
|
||||
async def test_unload_entry(hass: HomeAssistant) -> None:
|
||||
"""Test the unload entry."""
|
||||
config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_ENTRY_DATA)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
assert hass.data[DOMAIN]
|
||||
|
||||
entries = hass.config_entries.async_entries(DOMAIN)
|
||||
assert len(entries) == 1
|
||||
assert entries[0].state == ConfigEntryState.LOADED
|
||||
|
||||
await hass.config_entries.async_unload(entries[0].entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert entries[0].state == ConfigEntryState.NOT_LOADED
|
||||
|
||||
|
||||
async def test_data(hass: HomeAssistant) -> None:
|
||||
"""Test the data."""
|
||||
config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_ENTRY_DATA)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
assert hass.data[DOMAIN]
|
||||
assert isinstance(hass.data[DOMAIN][config_entry.entry_id], PecoOutageApi)
|
256
tests/components/peco/test_sensor.py
Normal file
256
tests/components/peco/test_sensor.py
Normal file
@ -0,0 +1,256 @@
|
||||
"""Test the PECO Outage Counter sensors."""
|
||||
import asyncio
|
||||
|
||||
from homeassistant.components.peco.const import DOMAIN
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
from tests.test_util.aiohttp import AiohttpClientMocker
|
||||
|
||||
MOCK_ENTRY_DATA = {"county": "TOTAL"}
|
||||
COUNTY_ENTRY_DATA = {"county": "BUCKS"}
|
||||
INVALID_COUNTY_DATA = {"county": "INVALID"}
|
||||
|
||||
|
||||
async def test_sensor_available(
|
||||
aioclient_mock: AiohttpClientMocker, hass: HomeAssistant
|
||||
) -> None:
|
||||
"""Test that the sensors are working."""
|
||||
# Totals Test
|
||||
aioclient_mock.get(
|
||||
"https://kubra.io/stormcenter/api/v1/stormcenters/39e6d9f3-fdea-4539-848f-b8631945da6f/views/74de8a50-3f45-4f6a-9483-fd618bb9165d/currentState?preview=false",
|
||||
json={"data": {"interval_generation_data": "data/TEST"}},
|
||||
)
|
||||
aioclient_mock.get(
|
||||
"https://kubra.io/data/TEST/public/reports/a36a6292-1c55-44de-a6a9-44fedf9482ee_report.json",
|
||||
json={
|
||||
"file_data": {
|
||||
"totals": {
|
||||
"cust_a": {
|
||||
"val": 123,
|
||||
},
|
||||
"percent_cust_a": {
|
||||
"val": 1.23,
|
||||
},
|
||||
"n_out": 456,
|
||||
"cust_s": 789,
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_ENTRY_DATA)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.data[DOMAIN]
|
||||
|
||||
entries = hass.config_entries.async_entries(DOMAIN)
|
||||
assert len(entries) == 1
|
||||
assert config_entry.state == ConfigEntryState.LOADED
|
||||
|
||||
sensors_to_get = [
|
||||
"total_customers_out",
|
||||
"total_percent_customers_out",
|
||||
"total_outage_count",
|
||||
"total_customers_served",
|
||||
]
|
||||
|
||||
for sensor in sensors_to_get:
|
||||
sensor_entity = hass.states.get(f"sensor.{sensor}")
|
||||
assert sensor_entity is not None
|
||||
assert sensor_entity.state != "unavailable"
|
||||
|
||||
if sensor == "total_customers_out":
|
||||
assert sensor_entity.state == "123"
|
||||
elif sensor == "total_percent_customers_out":
|
||||
assert sensor_entity.state == "15.589"
|
||||
elif sensor == "total_outage_count":
|
||||
assert sensor_entity.state == "456"
|
||||
elif sensor == "total_customers_served":
|
||||
assert sensor_entity.state == "789"
|
||||
|
||||
# County Test
|
||||
aioclient_mock.clear_requests()
|
||||
aioclient_mock.get(
|
||||
"https://kubra.io/stormcenter/api/v1/stormcenters/39e6d9f3-fdea-4539-848f-b8631945da6f/views/74de8a50-3f45-4f6a-9483-fd618bb9165d/currentState?preview=false",
|
||||
json={"data": {"interval_generation_data": "data/TEST"}},
|
||||
)
|
||||
aioclient_mock.get(
|
||||
"https://kubra.io/data/TEST/public/reports/a36a6292-1c55-44de-a6a9-44fedf9482ee_report.json",
|
||||
json={
|
||||
"file_data": {
|
||||
"areas": [
|
||||
{
|
||||
"name": "BUCKS",
|
||||
"cust_a": {
|
||||
"val": 123,
|
||||
},
|
||||
"percent_cust_a": {
|
||||
"val": 1.23,
|
||||
},
|
||||
"n_out": 456,
|
||||
"cust_s": 789,
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
config_entry = MockConfigEntry(domain=DOMAIN, data=COUNTY_ENTRY_DATA)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.data[DOMAIN]
|
||||
|
||||
entries = hass.config_entries.async_entries(DOMAIN)
|
||||
assert len(entries) == 2
|
||||
assert config_entry.state == ConfigEntryState.LOADED
|
||||
|
||||
sensors_to_get = [
|
||||
"bucks_customers_out",
|
||||
"bucks_percent_customers_out",
|
||||
"bucks_outage_count",
|
||||
"bucks_customers_served",
|
||||
]
|
||||
|
||||
for sensor in sensors_to_get:
|
||||
sensor_entity = hass.states.get(f"sensor.{sensor}")
|
||||
assert sensor_entity is not None
|
||||
assert sensor_entity.state != "unavailable"
|
||||
|
||||
if sensor == "bucks_customers_out":
|
||||
assert sensor_entity.state == "123"
|
||||
elif sensor == "bucks_percent_customers_out":
|
||||
assert sensor_entity.state == "15.589"
|
||||
elif sensor == "bucks_outage_count":
|
||||
assert sensor_entity.state == "456"
|
||||
elif sensor == "bucks_customers_served":
|
||||
assert sensor_entity.state == "789"
|
||||
|
||||
|
||||
async def test_http_error(
|
||||
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, caplog
|
||||
):
|
||||
"""Test if it raises an error when there is an HTTP error."""
|
||||
aioclient_mock.get(
|
||||
"https://kubra.io/stormcenter/api/v1/stormcenters/39e6d9f3-fdea-4539-848f-b8631945da6f/views/74de8a50-3f45-4f6a-9483-fd618bb9165d/currentState?preview=false",
|
||||
json={"data": {"interval_generation_data": "data/TEST"}},
|
||||
)
|
||||
aioclient_mock.get(
|
||||
"https://kubra.io/data/TEST/public/reports/a36a6292-1c55-44de-a6a9-44fedf9482ee_report.json",
|
||||
status=500,
|
||||
json={"error": "Internal Server Error"},
|
||||
)
|
||||
|
||||
config_entry = MockConfigEntry(domain=DOMAIN, data=COUNTY_ENTRY_DATA)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.data[DOMAIN]
|
||||
|
||||
assert "Error getting PECO outage counter data" in caplog.text
|
||||
|
||||
|
||||
async def test_bad_json(
|
||||
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, caplog
|
||||
):
|
||||
"""Test if it raises an error when there is bad JSON."""
|
||||
aioclient_mock.get(
|
||||
"https://kubra.io/stormcenter/api/v1/stormcenters/39e6d9f3-fdea-4539-848f-b8631945da6f/views/74de8a50-3f45-4f6a-9483-fd618bb9165d/currentState?preview=false",
|
||||
json={"data": {}},
|
||||
)
|
||||
|
||||
config_entry = MockConfigEntry(domain=DOMAIN, data=COUNTY_ENTRY_DATA)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.data[DOMAIN]
|
||||
|
||||
assert "ConfigEntryNotReady" in caplog.text
|
||||
|
||||
|
||||
async def test_total_http_error(
|
||||
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, caplog
|
||||
):
|
||||
"""Test if it raises an error when there is an HTTP error."""
|
||||
aioclient_mock.get(
|
||||
"https://kubra.io/stormcenter/api/v1/stormcenters/39e6d9f3-fdea-4539-848f-b8631945da6f/views/74de8a50-3f45-4f6a-9483-fd618bb9165d/currentState?preview=false",
|
||||
json={"data": {"interval_generation_data": "data/TEST"}},
|
||||
)
|
||||
aioclient_mock.get(
|
||||
"https://kubra.io/data/TEST/public/reports/a36a6292-1c55-44de-a6a9-44fedf9482ee_report.json",
|
||||
status=500,
|
||||
json={"error": "Internal Server Error"},
|
||||
)
|
||||
|
||||
config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_ENTRY_DATA)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.data[DOMAIN]
|
||||
|
||||
assert "Error getting PECO outage counter data" in caplog.text
|
||||
|
||||
|
||||
async def test_total_bad_json(
|
||||
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, caplog
|
||||
):
|
||||
"""Test if it raises an error when there is bad JSON."""
|
||||
aioclient_mock.get(
|
||||
"https://kubra.io/stormcenter/api/v1/stormcenters/39e6d9f3-fdea-4539-848f-b8631945da6f/views/74de8a50-3f45-4f6a-9483-fd618bb9165d/currentState?preview=false",
|
||||
json={"data": {}},
|
||||
)
|
||||
|
||||
config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_ENTRY_DATA)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.data[DOMAIN]
|
||||
|
||||
assert "ConfigEntryNotReady" in caplog.text
|
||||
|
||||
|
||||
async def test_update_timeout(
|
||||
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, caplog
|
||||
):
|
||||
"""Test if it raises an error when there is a timeout."""
|
||||
aioclient_mock.get(
|
||||
"https://kubra.io/stormcenter/api/v1/stormcenters/39e6d9f3-fdea-4539-848f-b8631945da6f/views/74de8a50-3f45-4f6a-9483-fd618bb9165d/currentState?preview=false",
|
||||
exc=asyncio.TimeoutError(),
|
||||
)
|
||||
|
||||
config_entry = MockConfigEntry(domain=DOMAIN, data=COUNTY_ENTRY_DATA)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.data[DOMAIN]
|
||||
|
||||
assert "Timeout fetching data" in caplog.text
|
||||
|
||||
|
||||
async def test_total_update_timeout(
|
||||
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, caplog
|
||||
):
|
||||
"""Test if it raises an error when there is a timeout."""
|
||||
aioclient_mock.get(
|
||||
"https://kubra.io/stormcenter/api/v1/stormcenters/39e6d9f3-fdea-4539-848f-b8631945da6f/views/74de8a50-3f45-4f6a-9483-fd618bb9165d/currentState?preview=false",
|
||||
exc=asyncio.TimeoutError(),
|
||||
)
|
||||
|
||||
config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_ENTRY_DATA)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.data[DOMAIN]
|
||||
|
||||
assert "Timeout fetching data" in caplog.text
|
Loading…
x
Reference in New Issue
Block a user