mostly comments and formatting + 2 sensors

This commit is contained in:
Yordan Suarez
2022-01-22 11:54:54 -05:00
parent d99ea36427
commit 3be969883a
10 changed files with 220 additions and 53 deletions

View File

@@ -2,6 +2,10 @@
"python.linting.pylintEnabled": true, "python.linting.pylintEnabled": true,
"python.linting.enabled": true, "python.linting.enabled": true,
"python.pythonPath": "/usr/local/bin/python", "python.pythonPath": "/usr/local/bin/python",
"python.linting.pylintArgs": [
"--disable=C0103",
"--max-line-length=200"
],
"files.associations": { "files.associations": {
"*.yaml": "home-assistant" "*.yaml": "home-assistant"
} }

View File

@@ -1,13 +1,15 @@
"""Data Update Coordinator"""
import logging import logging
from datetime import timedelta
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from datetime import timedelta
from .fplapi import FplApi from .fplapi import FplApi
from .const import DOMAIN from .const import DOMAIN
SCAN_INTERVAL = timedelta(seconds=7200) SCAN_INTERVAL = timedelta(seconds=1200)
_LOGGER: logging.Logger = logging.getLogger(__package__) _LOGGER: logging.Logger = logging.getLogger(__package__)

View File

@@ -58,8 +58,8 @@ class FplEntity(CoordinatorEntity, SensorEntity):
return attributes return attributes
def getData(self, field): def getData(self, field):
"""method is called to update sensor data""" """call this method to retrieve sensor data"""
return self.coordinator.data.get(self.account).get(field) return self.coordinator.data.get(self.account).get(field, None)
class FplEnergyEntity(FplEntity): class FplEnergyEntity(FplEntity):
@@ -123,3 +123,21 @@ class FplDateEntity(FplEntity):
@property @property
def icon(self): def icon(self):
return "mdi:calendar" return "mdi:calendar"
class FplDayEntity(FplEntity):
"""Represents a date or days"""
# @property
# def device_class(self) -> str:
# """Return the class of this device, from component DEVICE_CLASSES."""
# return DEVICE_CLASS_DATE
@property
def icon(self):
return "mdi:calendar"
@property
def unit_of_measurement(self) -> str:
"""Return the unit of measurement of this entity, if any."""
return "days"

View File

@@ -1,5 +1,4 @@
"""Custom FPl api client""" """Custom FPl api client"""
from asyncio import exceptions as asyncio_exceptions
import logging import logging
from datetime import datetime from datetime import datetime
@@ -17,7 +16,7 @@ LOGIN_RESULT_UNAUTHORIZED = "UNAUTHORIZED"
LOGIN_RESULT_FAILURE = "FAILURE" LOGIN_RESULT_FAILURE = "FAILURE"
_LOGGER = logging.getLogger(__package__) _LOGGER = logging.getLogger(__package__)
TIMEOUT = 30 TIMEOUT = 5
API_HOST = "https://www.fpl.com" API_HOST = "https://www.fpl.com"
@@ -98,7 +97,7 @@ class FplApi:
if json_data["messageCode"] == LOGIN_RESULT_INVALIDPASSWORD: if json_data["messageCode"] == LOGIN_RESULT_INVALIDPASSWORD:
return LOGIN_RESULT_INVALIDPASSWORD return LOGIN_RESULT_INVALIDPASSWORD
except asyncio_exceptions as exception: except Exception as exception:
_LOGGER.error("Error %s : %s", exception, sys.exc_info()[0]) _LOGGER.error("Error %s : %s", exception, sys.exc_info()[0])
return LOGIN_RESULT_FAILURE return LOGIN_RESULT_FAILURE
@@ -110,7 +109,7 @@ class FplApi:
try: try:
async with async_timeout.timeout(TIMEOUT): async with async_timeout.timeout(TIMEOUT):
await self._session.get(URL_LOGOUT) await self._session.get(URL_LOGOUT)
except asyncio_exceptions: except Exception:
pass pass
async def async_get_open_accounts(self): async def async_get_open_accounts(self):
@@ -129,8 +128,8 @@ class FplApi:
if account["statusCategory"] == STATUS_CATEGORY_OPEN: if account["statusCategory"] == STATUS_CATEGORY_OPEN:
result.append(account["accountNumber"]) result.append(account["accountNumber"])
except asyncio_exceptions as exception: except Exception:
_LOGGER.error("Getting accounts %s", exception) _LOGGER.error("Getting accounts %s", sys.exc_info())
return result return result
@@ -186,11 +185,13 @@ class FplApi:
def hasProgram(programName) -> bool: def hasProgram(programName) -> bool:
return programName in programs.keys() and programs[programName] return programName in programs.keys() and programs[programName]
# Budget Billing program
if hasProgram("BBL"): if hasProgram("BBL"):
# budget billing
data["budget_bill"] = True data["budget_bill"] = True
bbl_data = await self.__getBBL_async(account, data) bbl_data = await self.__getBBL_async(account, data)
data.update(bbl_data) data.update(bbl_data)
else:
data["budget_bill"] = False
data.update( data.update(
await self.__getDataFromEnergyService(account, premise, currentBillDate) await self.__getDataFromEnergyService(account, premise, currentBillDate)
@@ -227,7 +228,7 @@ class FplApi:
data["daily_avg"] = dailyAvg data["daily_avg"] = dailyAvg
data["avg_high_temp"] = avgHighTemp data["avg_high_temp"] = avgHighTemp
except asyncio_exceptions: except Exception:
pass pass
return data return data
@@ -267,7 +268,7 @@ class FplApi:
data["budget_billing_bill_to_date"] = bbAsOfDateAmt data["budget_billing_bill_to_date"] = bbAsOfDateAmt
data["budget_billing_projected_bill"] = float(projectedBudgetBill) data["budget_billing_projected_bill"] = float(projectedBudgetBill)
except asyncio_exceptions: except Exception:
pass pass
try: try:
@@ -279,7 +280,7 @@ class FplApi:
r = (await response.json())["data"] r = (await response.json())["data"]
data["bill_to_date"] = float(r["eleAmt"]) data["bill_to_date"] = float(r["eleAmt"])
data["defered_amount"] = float(r["defAmt"]) data["defered_amount"] = float(r["defAmt"])
except asyncio_exceptions: except Exception:
pass pass
return data return data
@@ -329,8 +330,15 @@ class FplApi:
{ {
"usage": daily["kwhUsed"], "usage": daily["kwhUsed"],
"cost": daily["billingCharge"], "cost": daily["billingCharge"],
"date": daily["date"], # "date": daily["date"],
"max_temperature": daily["averageHighTemperature"], "max_temperature": daily["averageHighTemperature"],
"netDeliveredKwh": daily["netDeliveredKwh"]
if "netDeliveredKwh" in daily.keys()
else 0,
"netReceivedKwh": daily["netReceivedKwh"]
if "netReceivedKwh" in daily.keys()
else 0,
"readTime": daily["readTime"],
} }
) )
# totalPowerUsage += int(daily["kwhUsed"]) # totalPowerUsage += int(daily["kwhUsed"])
@@ -343,6 +351,7 @@ class FplApi:
data["billToDateKWH"] = r["CurrentUsage"]["billToDateKWH"] data["billToDateKWH"] = r["CurrentUsage"]["billToDateKWH"]
data["recMtrReading"] = r["CurrentUsage"]["recMtrReading"] data["recMtrReading"] = r["CurrentUsage"]["recMtrReading"]
data["delMtrReading"] = r["CurrentUsage"]["delMtrReading"] data["delMtrReading"] = r["CurrentUsage"]["delMtrReading"]
data["billStartDate"] = r["CurrentUsage"]["billStartDate"]
return data return data
async def __getDataFromApplianceUsage(self, account, lastBilledDate) -> dict: async def __getDataFromApplianceUsage(self, account, lastBilledDate) -> dict:
@@ -368,7 +377,7 @@ class FplApi:
rr = full rr = full
data[e["category"].replace(" ", "_")] = rr data[e["category"].replace(" ", "_")] = rr
except asyncio_exceptions: except Exception:
pass pass
return {"energy_percent_by_applicance": data} return {"energy_percent_by_applicance": data}

View File

@@ -21,14 +21,19 @@ from .sensor_ProjectedBillSensor import (
DeferedAmountSensor, DeferedAmountSensor,
) )
from .sensor_AverageDailySensor import ( from .sensor_AverageDailySensor import (
FplAverageDailySensor, DailyAverageSensor,
BudgetDailyAverageSensor, BudgetDailyAverageSensor,
ActualDailyAverageSensor, ActualDailyAverageSensor,
) )
from .sensor_DailyUsageSensor import FplDailyUsageKWHSensor, FplDailyUsageSensor from .sensor_DailyUsageSensor import (
FplDailyUsageKWHSensor,
FplDailyUsageSensor,
FplDailyDeliveredKWHSensor,
FplDailyReceivedKWHSensor,
)
from .const import DOMAIN from .const import DOMAIN
from .TestSensor import TestSensor # from .TestSensor import TestSensor
async def async_setup_entry(hass, entry, async_add_devices): async def async_setup_entry(hass, entry, async_add_devices):
@@ -49,7 +54,7 @@ async def async_setup_entry(hass, entry, async_add_devices):
fpl_accounts.append(DeferedAmountSensor(coordinator, entry, account)) fpl_accounts.append(DeferedAmountSensor(coordinator, entry, account))
# usage sensors # usage sensors
fpl_accounts.append(FplAverageDailySensor(coordinator, entry, account)) fpl_accounts.append(DailyAverageSensor(coordinator, entry, account))
fpl_accounts.append(BudgetDailyAverageSensor(coordinator, entry, account)) fpl_accounts.append(BudgetDailyAverageSensor(coordinator, entry, account))
fpl_accounts.append(ActualDailyAverageSensor(coordinator, entry, account)) fpl_accounts.append(ActualDailyAverageSensor(coordinator, entry, account))
@@ -71,4 +76,7 @@ async def async_setup_entry(hass, entry, async_add_devices):
fpl_accounts.append(NetReceivedKWHSensor(coordinator, entry, account)) fpl_accounts.append(NetReceivedKWHSensor(coordinator, entry, account))
fpl_accounts.append(NetDeliveredKWHSensor(coordinator, entry, account)) fpl_accounts.append(NetDeliveredKWHSensor(coordinator, entry, account))
fpl_accounts.append(FplDailyReceivedKWHSensor(coordinator, entry, account))
fpl_accounts.append(FplDailyDeliveredKWHSensor(coordinator, entry, account))
async_add_devices(fpl_accounts) async_add_devices(fpl_accounts)

View File

@@ -1,7 +1,10 @@
"""Average daily sensors"""
from .fplEntity import FplMoneyEntity from .fplEntity import FplMoneyEntity
class FplAverageDailySensor(FplMoneyEntity): class DailyAverageSensor(FplMoneyEntity):
"""average daily sensor, use budget value if available, otherwise use actual daily values"""
def __init__(self, coordinator, config, account): def __init__(self, coordinator, config, account):
super().__init__(coordinator, config, account, "Daily Average") super().__init__(coordinator, config, account, "Daily Average")
@@ -10,13 +13,21 @@ class FplAverageDailySensor(FplMoneyEntity):
budget = self.getData("budget_bill") budget = self.getData("budget_bill")
budget_billing_projected_bill = self.getData("budget_billing_daily_avg") budget_billing_projected_bill = self.getData("budget_billing_daily_avg")
if budget == True and budget_billing_projected_bill is not None: if budget and budget_billing_projected_bill is not None:
return self.getData("budget_billing_daily_avg") return self.getData("budget_billing_daily_avg")
return self.getData("daily_avg") return self.getData("daily_avg")
def defineAttributes(self):
"""Return the state attributes."""
attributes = {}
attributes["state_class"] = "total"
return attributes
class BudgetDailyAverageSensor(FplMoneyEntity): class BudgetDailyAverageSensor(FplMoneyEntity):
"""budget daily average sensor"""
def __init__(self, coordinator, config, account): def __init__(self, coordinator, config, account):
super().__init__(coordinator, config, account, "Budget Daily Average") super().__init__(coordinator, config, account, "Budget Daily Average")
@@ -24,11 +35,25 @@ class BudgetDailyAverageSensor(FplMoneyEntity):
def state(self): def state(self):
return self.getData("budget_billing_daily_avg") return self.getData("budget_billing_daily_avg")
def defineAttributes(self):
"""Return the state attributes."""
attributes = {}
attributes["state_class"] = "total"
return attributes
class ActualDailyAverageSensor(FplMoneyEntity): class ActualDailyAverageSensor(FplMoneyEntity):
"""Actual daily average sensor"""
def __init__(self, coordinator, config, account): def __init__(self, coordinator, config, account):
super().__init__(coordinator, config, account, "Actual Daily Average") super().__init__(coordinator, config, account, "Actual Daily Average")
@property @property
def state(self): def state(self):
return self.getData("daily_avg") return self.getData("daily_avg")
def defineAttributes(self):
"""Return the state attributes."""
attributes = {}
attributes["state_class"] = "total"
return attributes

View File

@@ -20,11 +20,12 @@ class FplDailyUsageSensor(FplMoneyEntity):
def defineAttributes(self): def defineAttributes(self):
"""Return the state attributes.""" """Return the state attributes."""
data = self.getData("daily_usage") data = self.getData("daily_usage")
attributes = {}
attributes["state_class"] = "total_increasing"
if data is not None and len(data) > 0 and "readTime" in data[-1].keys():
attributes["date"] = data[-1]["readTime"]
if data is not None and len(data) > 0 and "date" in data[-1].keys(): return attributes
return {"date": data[-1]["date"]}
return {}
class FplDailyUsageKWHSensor(FplEnergyEntity): class FplDailyUsageKWHSensor(FplEnergyEntity):
@@ -45,8 +46,61 @@ class FplDailyUsageKWHSensor(FplEnergyEntity):
def defineAttributes(self): def defineAttributes(self):
"""Return the state attributes.""" """Return the state attributes."""
data = self.getData("daily_usage") data = self.getData("daily_usage")
attributes = {}
attributes["state_class"] = "total_increasing"
if data is not None and len(data) > 0 and "date" in data[-1].keys(): if data is not None:
return {"date": data[-1]["date"]} if data[-1] is not None and "readTime" in data[-1].keys():
attributes["date"] = data[-1]["readTime"]
if data[-2] is not None and "readTime" in data[-2].keys():
attributes["last_reset"] = data[-2]["readTime"]
return {} return attributes
class FplDailyReceivedKWHSensor(FplEnergyEntity):
"""daily received Kwh sensor"""
def __init__(self, coordinator, config, account):
super().__init__(coordinator, config, account, "Daily Received KWH")
@property
def state(self):
data = self.getData("daily_usage")
if data is not None and len(data) > 0 and "netReceivedKwh" in data[-1].keys():
return data[-1]["netReceivedKwh"]
return 0
def defineAttributes(self):
"""Return the state attributes."""
data = self.getData("daily_usage")
attributes = {}
attributes["state_class"] = "total_increasing"
attributes["date"] = data[-1]["readTime"]
attributes["last_reset"] = data[-2]["readTime"]
return attributes
class FplDailyDeliveredKWHSensor(FplEnergyEntity):
"""daily delivered Kwh sensor"""
def __init__(self, coordinator, config, account):
super().__init__(coordinator, config, account, "Daily Delivered KWH")
@property
def state(self):
data = self.getData("daily_usage")
if data is not None and len(data) > 0 and "netDeliveredKwh" in data[-1].keys():
return data[-1]["netDeliveredKwh"]
return 0
def defineAttributes(self):
"""Return the state attributes."""
data = self.getData("daily_usage")
attributes = {}
attributes["state_class"] = "total_increasing"
attributes["date"] = data[-1]["readTime"]
attributes["last_reset"] = data[-2]["readTime"]
return attributes

View File

@@ -1,4 +1,6 @@
from .fplEntity import FplDateEntity """dates sensors"""
import datetime
from .fplEntity import FplDateEntity, FplDayEntity
class CurrentBillDateSensor(FplDateEntity): class CurrentBillDateSensor(FplDateEntity):
@@ -7,7 +9,7 @@ class CurrentBillDateSensor(FplDateEntity):
@property @property
def state(self): def state(self):
return self.getData("current_bill_date") return datetime.date.fromisoformat(self.getData("current_bill_date"))
class NextBillDateSensor(FplDateEntity): class NextBillDateSensor(FplDateEntity):
@@ -16,10 +18,10 @@ class NextBillDateSensor(FplDateEntity):
@property @property
def state(self): def state(self):
return self.getData("next_bill_date") return datetime.date.fromisoformat(self.getData("next_bill_date"))
class ServiceDaysSensor(FplDateEntity): class ServiceDaysSensor(FplDayEntity):
def __init__(self, coordinator, config, account): def __init__(self, coordinator, config, account):
super().__init__(coordinator, config, account, "Service Days") super().__init__(coordinator, config, account, "Service Days")
@@ -28,7 +30,7 @@ class ServiceDaysSensor(FplDateEntity):
return self.getData("service_days") return self.getData("service_days")
class AsOfDaysSensor(FplDateEntity): class AsOfDaysSensor(FplDayEntity):
def __init__(self, coordinator, config, account): def __init__(self, coordinator, config, account):
super().__init__(coordinator, config, account, "As Of Days") super().__init__(coordinator, config, account, "As Of Days")
@@ -37,7 +39,7 @@ class AsOfDaysSensor(FplDateEntity):
return self.getData("as_of_days") return self.getData("as_of_days")
class RemainingDaysSensor(FplDateEntity): class RemainingDaysSensor(FplDayEntity):
def __init__(self, coordinator, config, account): def __init__(self, coordinator, config, account):
super().__init__(coordinator, config, account, "Remaining Days") super().__init__(coordinator, config, account, "Remaining Days")

View File

@@ -1,4 +1,8 @@
from homeassistant.components.sensor import STATE_CLASS_TOTAL_INCREASING """energy sensors"""
from homeassistant.components.sensor import (
STATE_CLASS_TOTAL_INCREASING,
STATE_CLASS_TOTAL,
)
from .fplEntity import FplEnergyEntity from .fplEntity import FplEnergyEntity
@@ -10,6 +14,12 @@ class ProjectedKWHSensor(FplEnergyEntity):
def state(self): def state(self):
return self.getData("projectedKWH") return self.getData("projectedKWH")
def defineAttributes(self):
"""Return the state attributes."""
attributes = {}
attributes["state_class"] = STATE_CLASS_TOTAL
return attributes
class DailyAverageKWHSensor(FplEnergyEntity): class DailyAverageKWHSensor(FplEnergyEntity):
def __init__(self, coordinator, config, account): def __init__(self, coordinator, config, account):
@@ -19,6 +29,12 @@ class DailyAverageKWHSensor(FplEnergyEntity):
def state(self): def state(self):
return self.getData("dailyAverageKWH") return self.getData("dailyAverageKWH")
def defineAttributes(self):
"""Return the state attributes."""
attributes = {}
attributes["state_class"] = STATE_CLASS_TOTAL
return attributes
class BillToDateKWHSensor(FplEnergyEntity): class BillToDateKWHSensor(FplEnergyEntity):
def __init__(self, coordinator, config, account): def __init__(self, coordinator, config, account):
@@ -28,9 +44,11 @@ class BillToDateKWHSensor(FplEnergyEntity):
def state(self): def state(self):
return self.getData("billToDateKWH") return self.getData("billToDateKWH")
@property def defineAttributes(self):
def state_class(self) -> str: """Return the state attributes."""
"""Return the state class of this entity, from STATE_CLASSES, if any.""" attributes = {}
attributes["state_class"] = STATE_CLASS_TOTAL_INCREASING
return attributes
class NetReceivedKWHSensor(FplEnergyEntity): class NetReceivedKWHSensor(FplEnergyEntity):
@@ -41,9 +59,11 @@ class NetReceivedKWHSensor(FplEnergyEntity):
def state(self): def state(self):
return self.getData("recMtrReading") return self.getData("recMtrReading")
@property def defineAttributes(self):
def icon(self): """Return the state attributes."""
return "mdi:flash" attributes = {}
attributes["state_class"] = STATE_CLASS_TOTAL_INCREASING
return attributes
class NetDeliveredKWHSensor(FplEnergyEntity): class NetDeliveredKWHSensor(FplEnergyEntity):
@@ -54,6 +74,8 @@ class NetDeliveredKWHSensor(FplEnergyEntity):
def state(self): def state(self):
return self.getData("delMtrReading") return self.getData("delMtrReading")
@property def defineAttributes(self):
def icon(self): """Return the state attributes."""
return "mdi:flash" attributes = {}
attributes["state_class"] = STATE_CLASS_TOTAL_INCREASING
return attributes

View File

@@ -1,7 +1,10 @@
"""Projected bill sensors"""
from .fplEntity import FplMoneyEntity from .fplEntity import FplMoneyEntity
class FplProjectedBillSensor(FplMoneyEntity): class FplProjectedBillSensor(FplMoneyEntity):
"""projected bill sensor"""
def __init__(self, coordinator, config, account): def __init__(self, coordinator, config, account):
super().__init__(coordinator, config, account, "Projected Bill") super().__init__(coordinator, config, account, "Projected Bill")
@@ -10,7 +13,7 @@ class FplProjectedBillSensor(FplMoneyEntity):
budget = self.getData("budget_bill") budget = self.getData("budget_bill")
budget_billing_projected_bill = self.getData("budget_billing_projected_bill") budget_billing_projected_bill = self.getData("budget_billing_projected_bill")
if budget == True and budget_billing_projected_bill is not None: if budget and budget_billing_projected_bill is not None:
return self.getData("budget_billing_projected_bill") return self.getData("budget_billing_projected_bill")
return self.getData("projected_bill") return self.getData("projected_bill")
@@ -18,28 +21,34 @@ class FplProjectedBillSensor(FplMoneyEntity):
def defineAttributes(self): def defineAttributes(self):
"""Return the state attributes.""" """Return the state attributes."""
attributes = {} attributes = {}
try: attributes["state_class"] = "total"
if self.getData("budget_bill") == True: attributes["budget_bill"] = self.getData("budget_bill")
attributes["budget_bill"] = self.getData("budget_bill")
except:
pass
return attributes return attributes
# Defered Amount # Defered Amount
class DeferedAmountSensor(FplMoneyEntity): class DeferedAmountSensor(FplMoneyEntity):
"""Defered amount sensor"""
def __init__(self, coordinator, config, account): def __init__(self, coordinator, config, account):
super().__init__(coordinator, config, account, "Defered Amount") super().__init__(coordinator, config, account, "Defered Amount")
@property @property
def state(self): def state(self):
if self.getData("budget_bill") == True: if self.getData("budget_bill"):
return self.getData("defered_amount") return self.getData("defered_amount")
return 0 return 0
def defineAttributes(self):
"""Return the state attributes."""
attributes = {}
attributes["state_class"] = "total"
return attributes
class ProjectedBudgetBillSensor(FplMoneyEntity): class ProjectedBudgetBillSensor(FplMoneyEntity):
"""projected budget bill sensor"""
def __init__(self, coordinator, config, account): def __init__(self, coordinator, config, account):
super().__init__(coordinator, config, account, "Projected Budget Bill") super().__init__(coordinator, config, account, "Projected Budget Bill")
@@ -47,11 +56,25 @@ class ProjectedBudgetBillSensor(FplMoneyEntity):
def state(self): def state(self):
return self.getData("budget_billing_projected_bill") return self.getData("budget_billing_projected_bill")
def defineAttributes(self):
"""Return the state attributes."""
attributes = {}
attributes["state_class"] = "total"
return attributes
class ProjectedActualBillSensor(FplMoneyEntity): class ProjectedActualBillSensor(FplMoneyEntity):
"""projeted actual bill sensor"""
def __init__(self, coordinator, config, account): def __init__(self, coordinator, config, account):
super().__init__(coordinator, config, account, "Projected Actual Bill") super().__init__(coordinator, config, account, "Projected Actual Bill")
@property @property
def state(self): def state(self):
return self.getData("projected_bill") return self.getData("projected_bill")
def defineAttributes(self):
"""Return the state attributes."""
attributes = {}
attributes["state_class"] = "total"
return attributes