-
-
Notifications
You must be signed in to change notification settings - Fork 32.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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 36ca908. * More fixes
- Loading branch information
Showing
8 changed files
with
516 additions
and
34 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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() | ||
|
||
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"{county.capitalize()} Outage Count", data=user_input | ||
title=f"{self.meter_data[CONF_COUNTY].capitalize()} - {self.meter_data[CONF_PHONE_NUMBER]}", | ||
data=self.meter_data, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.