From 621fea3042200cea233a43b82371bff706a4bc98 Mon Sep 17 00:00:00 2001 From: Adam Outler Date: Thu, 30 Dec 2021 14:44:27 -0500 Subject: [PATCH] merge changes into primary folder --- custom_components/fpl/const.py | 2 +- .../fpl/fpl/AverageDailySensor.py | 20 - custom_components/fpl/fpl/DailyUsageSensor.py | 31 -- .../fpl/fpl/ProjectedBillSensor.py | 37 -- custom_components/fpl/fpl/TestSensor.py | 28 -- custom_components/fpl/fpl/__init__.py | 107 ------ custom_components/fpl/fpl/config_flow.py | 110 ------ custom_components/fpl/fpl/const.py | 50 --- .../fpl/fpl/fplDataUpdateCoordinator.py | 30 -- custom_components/fpl/fpl/fplEntity.py | 45 --- custom_components/fpl/fpl/fplapi.py | 346 ------------------ custom_components/fpl/fpl/manifest.json | 17 - custom_components/fpl/fpl/sensor.py | 82 ----- custom_components/fpl/fpl/sensor_AllData.py | 33 -- .../fpl/fpl/sensor_AverageDailySensor.py | 51 --- .../fpl/fpl/sensor_DailyUsageSensor.py | 120 ------ .../fpl/fpl/sensor_DatesSensor.py | 97 ----- custom_components/fpl/fpl/sensor_KWHSensor.py | 110 ------ .../fpl/fpl/sensor_ProjectedBillSensor.py | 99 ----- .../fpl/fplDataUpdateCoordinator.py | 2 +- custom_components/fpl/fplEntity.py | 6 +- custom_components/fpl/fplapi.py | 4 + custom_components/fpl/sensor.py | 16 +- custom_components/fpl/sensor_AllData.py | 9 + .../fpl/sensor_AverageDailySensor.py | 27 +- .../fpl/sensor_DailyUsageSensor.py | 87 ++++- custom_components/fpl/sensor_DatesSensor.py | 47 ++- custom_components/fpl/sensor_KWHSensor.py | 58 ++- .../fpl/sensor_ProjectedBillSensor.py | 40 +- 29 files changed, 244 insertions(+), 1467 deletions(-) delete mode 100644 custom_components/fpl/fpl/AverageDailySensor.py delete mode 100644 custom_components/fpl/fpl/DailyUsageSensor.py delete mode 100644 custom_components/fpl/fpl/ProjectedBillSensor.py delete mode 100644 custom_components/fpl/fpl/TestSensor.py delete mode 100644 custom_components/fpl/fpl/__init__.py delete mode 100644 custom_components/fpl/fpl/config_flow.py delete mode 100644 custom_components/fpl/fpl/const.py delete mode 100644 custom_components/fpl/fpl/fplDataUpdateCoordinator.py delete mode 100644 custom_components/fpl/fpl/fplEntity.py delete mode 100644 custom_components/fpl/fpl/fplapi.py delete mode 100644 custom_components/fpl/fpl/manifest.json delete mode 100644 custom_components/fpl/fpl/sensor.py delete mode 100644 custom_components/fpl/fpl/sensor_AllData.py delete mode 100644 custom_components/fpl/fpl/sensor_AverageDailySensor.py delete mode 100644 custom_components/fpl/fpl/sensor_DailyUsageSensor.py delete mode 100644 custom_components/fpl/fpl/sensor_DatesSensor.py delete mode 100644 custom_components/fpl/fpl/sensor_KWHSensor.py delete mode 100644 custom_components/fpl/fpl/sensor_ProjectedBillSensor.py diff --git a/custom_components/fpl/const.py b/custom_components/fpl/const.py index 63615e0..5a418bb 100644 --- a/custom_components/fpl/const.py +++ b/custom_components/fpl/const.py @@ -3,7 +3,7 @@ NAME = "FPL Integration" DOMAIN = "fpl" DOMAIN_DATA = f"{DOMAIN}_data" -VERSION = "0.0.1" +VERSION = "0.1.0" PLATFORMS = ["sensor"] REQUIRED_FILES = [ ".translations/en.json", diff --git a/custom_components/fpl/fpl/AverageDailySensor.py b/custom_components/fpl/fpl/AverageDailySensor.py deleted file mode 100644 index d0f4778..0000000 --- a/custom_components/fpl/fpl/AverageDailySensor.py +++ /dev/null @@ -1,20 +0,0 @@ -from .FplSensor import FplSensor - - -class FplAverageDailySensor(FplSensor): - def __init__(self, hass, config, account): - FplSensor.__init__(self, hass, config, account, "Average Daily") - - @property - def state(self): - try: - if "daily_avg" in self.data: - self._state = self.data["daily_avg"] - except: - pass - return self._state - - @property - def device_state_attributes(self): - """Return the state attributes.""" - return self.attr \ No newline at end of file diff --git a/custom_components/fpl/fpl/DailyUsageSensor.py b/custom_components/fpl/fpl/DailyUsageSensor.py deleted file mode 100644 index 255e0e7..0000000 --- a/custom_components/fpl/fpl/DailyUsageSensor.py +++ /dev/null @@ -1,31 +0,0 @@ -from .FplSensor import FplSensor - - -class FplDailyUsageSensor(FplSensor): - def __init__(self, hass, config, account): - FplSensor.__init__(self, hass, config, account, "Daily Usage") - - @property - def state(self): - try: - if "daily_usage" in self.data: - if len(self.data["daily_usage"]) > 0: - if "cost" in self.data["daily_usage"][-1]: - self._state = self.data["daily_usage"][-1]["cost"] - except: - pass - - return self._state - - @property - def device_state_attributes(self): - """Return the state attributes.""" - try: - if "daily_usage" in self.data: - if len(self.data["daily_usage"]) > 0: - if "date" in self.data["daily_usage"][-1]: - self.attr["date"] = self.data["daily_usage"][-1]["date"] - except: - pass - - return self.attr diff --git a/custom_components/fpl/fpl/ProjectedBillSensor.py b/custom_components/fpl/fpl/ProjectedBillSensor.py deleted file mode 100644 index bf14a32..0000000 --- a/custom_components/fpl/fpl/ProjectedBillSensor.py +++ /dev/null @@ -1,37 +0,0 @@ -from .FplSensor import FplSensor - - -class FplProjectedBillSensor(FplSensor): - def __init__(self, hass, config, account): - FplSensor.__init__(self, hass, config, account, "Projected Bill") - - @property - def state(self): - data = self.data - try: - if "budget_bill" in data.keys(): - if data["budget_bill"]: - if "budget_billing_projected_bill" in data.keys(): - self._state = data["budget_billing_projected_bill"] - else: - if "projected_bill" in data.keys(): - self._state = data["projected_bill"] - except: - pass - - return self._state - - @property - def device_state_attributes(self): - """Return the state attributes.""" - try: - if "budget_bill" in self.data.keys(): - self.attr["budget_bill"] = self.data["budget_bill"] - except: - pass - - return self.attr - - @property - def icon(self): - return "mdi:currency-usd" \ No newline at end of file diff --git a/custom_components/fpl/fpl/TestSensor.py b/custom_components/fpl/fpl/TestSensor.py deleted file mode 100644 index 4d8b63d..0000000 --- a/custom_components/fpl/fpl/TestSensor.py +++ /dev/null @@ -1,28 +0,0 @@ -from .fplEntity import FplEntity -import pprint - - -class TestSensor(FplEntity): - def __init__(self, coordinator, config, account): - super().__init__(coordinator, config, account, "Test Sensor") - - @property - def state(self): - pprint.pprint(self.coordinator.data) - - return self.getData("projected_bill") - - def defineAttributes(self): - """Return the state attributes.""" - attributes = {} - try: - if self.getData("budget_bill"): - attributes["budget_bill"] = self.getData("budget_bill") - except: - pass - - return attributes - - @property - def icon(self): - return "mdi:currency-usd" diff --git a/custom_components/fpl/fpl/__init__.py b/custom_components/fpl/fpl/__init__.py deleted file mode 100644 index 18af9fe..0000000 --- a/custom_components/fpl/fpl/__init__.py +++ /dev/null @@ -1,107 +0,0 @@ -""" FPL Component """ - - -import logging -import asyncio - -from datetime import timedelta -from homeassistant.core import Config, HomeAssistant -from homeassistant.config_entries import ConfigEntry -from homeassistant.helpers.aiohttp_client import async_get_clientsession -from homeassistant.util import Throttle - -from .fplapi import FplApi -from .const import ( - DOMAIN, - DOMAIN_DATA, - CONF_USERNAME, - CONF_PASSWORD, - PLATFORMS, - STARTUP_MESSAGE, -) -from .fplDataUpdateCoordinator import FplDataUpdateCoordinator - -MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=30) - -_LOGGER = logging.getLogger(__package__) - - -class FplData: - """This class handle communication and stores the data.""" - - def __init__(self, hass, client): - """Initialize the class.""" - self.hass = hass - self.client = client - - @Throttle(MIN_TIME_BETWEEN_UPDATES) - async def update_data(self): - """Update data.""" - # This is where the main logic to update platform data goes. - try: - data = await self.client.get_data() - self.hass.data[DOMAIN_DATA]["data"] = data - except Exception as error: # pylint: disable=broad-except - _LOGGER.error("Could not update data - %s", error) - - -async def async_setup(hass: HomeAssistant, config: Config) -> bool: - """Set up configured Fpl.""" - return True - - -async def async_setup_entry(hass, entry): - """Set up this integration using UI.""" - if hass.data.get(DOMAIN) is None: - hass.data.setdefault(DOMAIN, {}) - _LOGGER.info(STARTUP_MESSAGE) - - # Get "global" configuration. - username = entry.data.get(CONF_USERNAME) - password = entry.data.get(CONF_PASSWORD) - - # Configure the client. - _LOGGER.info(f"Configuring the client") - session = async_get_clientsession(hass) - client = FplApi(username, password, session) - - coordinator = FplDataUpdateCoordinator(hass, client=client) - await coordinator.async_refresh() - - hass.data[DOMAIN][entry.entry_id] = coordinator - - for platform in PLATFORMS: - if entry.options.get(platform, True): - coordinator.platforms.append(platform) - hass.async_add_job( - hass.config_entries.async_forward_entry_setup(entry, platform) - ) - - """Set up Fpl as config entry.""" - - entry.add_update_listener(async_reload_entry) - return True - - -async def async_reload_entry(hass: HomeAssistant, entry: ConfigEntry) -> None: - """Reload config entry.""" - await async_unload_entry(hass, entry) - await async_setup_entry(hass, entry) - - -async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: - """Handle removal of an entry.""" - coordinator = hass.data[DOMAIN][entry.entry_id] - unloaded = all( - await asyncio.gather( - *[ - hass.config_entries.async_forward_entry_unload(entry, platform) - for platform in PLATFORMS - if platform in coordinator.platforms - ] - ) - ) - if unloaded: - hass.data[DOMAIN].pop(entry.entry_id) - - return unloaded diff --git a/custom_components/fpl/fpl/config_flow.py b/custom_components/fpl/fpl/config_flow.py deleted file mode 100644 index b94d54d..0000000 --- a/custom_components/fpl/fpl/config_flow.py +++ /dev/null @@ -1,110 +0,0 @@ -from collections import OrderedDict - -import voluptuous as vol -from .fplapi import FplApi - -from homeassistant import config_entries -from homeassistant.helpers.aiohttp_client import async_create_clientsession - -from .const import DOMAIN, CONF_USERNAME, CONF_PASSWORD, CONF_NAME - -from .fplapi import ( - LOGIN_RESULT_OK, - LOGIN_RESULT_INVALIDUSER, - LOGIN_RESULT_INVALIDPASSWORD, -) - -from homeassistant.core import callback - - -@callback -def configured_instances(hass): - """Return a set of configured SimpliSafe instances.""" - entites = [] - for entry in hass.config_entries.async_entries(DOMAIN): - entites.append(f"{entry.data.get(CONF_USERNAME)}") - return set(entites) - - -class FplFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): - - VERSION = 1 - CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL - - def __init__(self): - """Initialize.""" - self._errors = {} - - async def async_step_user( - self, user_input={} - ): # pylint: disable=dangerous-default-value - """Handle a flow initialized by the user.""" - self._errors = {} - - # if self._async_current_entries(): - # return self.async_abort(reason="single_instance_allowed") - - # if self.hass.data.get(DOMAIN): - # return self.async_abort(reason="single_instance_allowed") - - if user_input is not None: - username = user_input[CONF_USERNAME] - password = user_input[CONF_PASSWORD] - - if username not in configured_instances(self.hass): - session = async_create_clientsession(self.hass) - api = FplApi(username, password, session) - result = await api.login() - - if result == LOGIN_RESULT_OK: - fplData = await api.async_get_data() - accounts = fplData["accounts"] - - user_input["accounts"] = accounts - - return self.async_create_entry(title=username, data=user_input) - - if result == LOGIN_RESULT_INVALIDUSER: - self._errors[CONF_USERNAME] = "invalid_username" - - if result == LOGIN_RESULT_INVALIDPASSWORD: - self._errors[CONF_PASSWORD] = "invalid_password" - - if result == None: - self._errors["base"] = "auth" - - else: - self._errors[CONF_NAME] = "name_exists" - - return await self._show_config_form(user_input) - - return await self._show_config_form(user_input) - - async def _show_config_form(self, user_input): - """Show the configuration form to edit location data.""" - username = "" - password = "" - - if user_input is not None: - if CONF_USERNAME in user_input: - username = user_input[CONF_USERNAME] - if CONF_PASSWORD in user_input: - password = user_input[CONF_PASSWORD] - - data_schema = OrderedDict() - data_schema[vol.Required(CONF_USERNAME, default=username)] = str - data_schema[vol.Required(CONF_PASSWORD, default=password)] = str - - return self.async_show_form( - step_id="user", data_schema=vol.Schema(data_schema), errors=self._errors - ) - - async def async_step_import(self, user_input): # pylint: disable=unused-argument - """Import a config entry. - Special type of import, we're not actually going to store any data. - Instead, we're going to rely on the values that are in config file. - """ - if self._async_current_entries(): - return self.async_abort(reason="single_instance_allowed") - - return self.async_create_entry(title="configuration.yaml", data={}) diff --git a/custom_components/fpl/fpl/const.py b/custom_components/fpl/fpl/const.py deleted file mode 100644 index 5a418bb..0000000 --- a/custom_components/fpl/fpl/const.py +++ /dev/null @@ -1,50 +0,0 @@ -"""Constants for fpl.""" -# Base component constants -NAME = "FPL Integration" -DOMAIN = "fpl" -DOMAIN_DATA = f"{DOMAIN}_data" -VERSION = "0.1.0" -PLATFORMS = ["sensor"] -REQUIRED_FILES = [ - ".translations/en.json", - "binary_sensor.py", - "const.py", - "config_flow.py", - "manifest.json", - "sensor.py", - "switch.py", -] -ISSUE_URL = "https://github.com/dotKrad/hass-fpl/issues" -ATTRIBUTION = "This data is provided by FPL." - -# Platforms -BINARY_SENSOR = "binary_sensor" -SENSOR = "sensor" -SWITCH = "switch" -PLATFORMS = [SENSOR] - -# Device classes -BINARY_SENSOR_DEVICE_CLASS = "connectivity" - -# Configuration -CONF_BINARY_SENSOR = "binary_sensor" -CONF_SENSOR = "sensor" -CONF_SWITCH = "switch" -CONF_ENABLED = "enabled" -CONF_NAME = "name" -CONF_USERNAME = "username" -CONF_PASSWORD = "password" - -# Defaults -DEFAULT_NAME = DOMAIN - - -STARTUP_MESSAGE = f""" -------------------------------------------------------------------- -{NAME} -Version: {VERSION} -This is a custom integration! -If you have any issues with this you need to open an issue here: -{ISSUE_URL} -------------------------------------------------------------------- -""" diff --git a/custom_components/fpl/fpl/fplDataUpdateCoordinator.py b/custom_components/fpl/fpl/fplDataUpdateCoordinator.py deleted file mode 100644 index 5679cbf..0000000 --- a/custom_components/fpl/fpl/fplDataUpdateCoordinator.py +++ /dev/null @@ -1,30 +0,0 @@ -import logging - -from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed -from homeassistant.core import HomeAssistant -from datetime import timedelta - -from .fplapi import FplApi -from .const import DOMAIN - -SCAN_INTERVAL = timedelta(seconds=1200) - -_LOGGER: logging.Logger = logging.getLogger(__package__) - - -class FplDataUpdateCoordinator(DataUpdateCoordinator): - """Class to manage fetching data from the API.""" - - def __init__(self, hass: HomeAssistant, client: FplApi) -> None: - """Initialize.""" - self.api = client - self.platforms = [] - - super().__init__(hass, _LOGGER, name=DOMAIN, update_interval=SCAN_INTERVAL) - - async def _async_update_data(self): - """Update data via library.""" - try: - return await self.api.async_get_data() - except Exception as exception: - raise UpdateFailed() from exception diff --git a/custom_components/fpl/fpl/fplEntity.py b/custom_components/fpl/fpl/fplEntity.py deleted file mode 100644 index e9bf200..0000000 --- a/custom_components/fpl/fpl/fplEntity.py +++ /dev/null @@ -1,45 +0,0 @@ -"""BlueprintEntity class""" -from homeassistant.helpers.update_coordinator import CoordinatorEntity - -from .const import DOMAIN, VERSION, ATTRIBUTION - - -class FplEntity(CoordinatorEntity): - def __init__(self, coordinator, config_entry, account, sensorName): - super().__init__(coordinator) - self.config_entry = config_entry - self.account = account - self.sensorName = sensorName - - @property - def unique_id(self): - """Return the ID of this device.""" - id = "{}{}{}".format( - DOMAIN, self.account, self.sensorName.lower().replace(" ", "") - ) - return id - - @property - def device_info(self): - return { - "identifiers": {(DOMAIN, self.account)}, - "name": f"FPL Account {self.account}", - "model": VERSION, - "manufacturer": "Florida Power & Light", - } - - def defineAttributes(self): - return {} - - @property - def extra_state_attributes(self): - """Return the state attributes.""" - attributes = { - "attribution": ATTRIBUTION, - "integration": "FPL", - } - attributes.update(self.defineAttributes()) - return attributes - - def getData(self, field): - return self.coordinator.data.get(self.account).get(field) diff --git a/custom_components/fpl/fpl/fplapi.py b/custom_components/fpl/fpl/fplapi.py deleted file mode 100644 index 4790cd0..0000000 --- a/custom_components/fpl/fpl/fplapi.py +++ /dev/null @@ -1,346 +0,0 @@ -import asyncio -import logging -import re -from datetime import timedelta, datetime, date as dt - -import aiohttp -import async_timeout -import json -import sys - - -from bs4 import BeautifulSoup - -STATUS_CATEGORY_OPEN = "OPEN" -# Api login result -LOGIN_RESULT_OK = "OK" -LOGIN_RESULT_INVALIDUSER = "NOTVALIDUSER" -LOGIN_RESULT_INVALIDPASSWORD = "FAILEDPASSWORD" -LOGIN_RESULT_UNAUTHORIZED = "UNAUTHORIZED" -LOGIN_RESULT_FAILURE = "FAILURE" - -_LOGGER = logging.getLogger(__package__) -TIMEOUT = 30 - -URL_LOGIN = "https://www.fpl.com/api/resources/login" -URL_RESOURCES_HEADER = "https://www.fpl.com/api/resources/header" -URL_RESOURCES_ACCOUNT = "https://www.fpl.com/api/resources/account/{account}" -URL_RESOURCES_PROJECTED_BILL = "https://www.fpl.com/api/resources/account/{account}/projectedBill?premiseNumber={premise}&lastBilledDate={lastBillDate}" - -ENROLLED = "ENROLLED" -NOTENROLLED = "NOTENROLLED" - - -class FplApi(object): - """A class for getting energy usage information from Florida Power & Light.""" - - def __init__(self, username, password, session): - """Initialize the data retrieval. Session should have BasicAuth flag set.""" - self._username = username - self._password = password - self._session = session - - async def async_get_data(self) -> dict: - # self._session = aiohttp.ClientSession() - data = {} - data["accounts"] = [] - if await self.login() == LOGIN_RESULT_OK: - accounts = await self.async_get_open_accounts() - - data["accounts"] = accounts - for account in accounts: - accountData = await self.__async_get_data(account) - data[account] = accountData - - await self.logout() - return data - - async def login(self): - _LOGGER.info("Logging in") - """login and get account information""" - result = LOGIN_RESULT_OK - try: - async with async_timeout.timeout(TIMEOUT): - response = await self._session.get( - URL_LOGIN, auth=aiohttp.BasicAuth(self._username, self._password) - ) - - js = json.loads(await response.text()) - - if response.reason == "Unauthorized": - result = LOGIN_RESULT_UNAUTHORIZED - - if js["messages"][0]["messageCode"] != "login.success": - _LOGGER.error(f"Logging Failure") - result = LOGIN_RESULT_FAILURE - - _LOGGER.info(f"Logging Successful") - - except Exception as e: - _LOGGER.error(f"Error {e} : {sys.exc_info()[0]}") - result = LOGIN_RESULT_FAILURE - - return result - - async def logout(self): - _LOGGER.info("Logging out") - URL = "https://www.fpl.com/api/resources/logout" - async with async_timeout.timeout(TIMEOUT): - await self._session.get(URL) - - async def async_get_open_accounts(self): - _LOGGER.info(f"Getting accounts") - result = [] - - try: - async with async_timeout.timeout(TIMEOUT): - response = await self._session.get(URL_RESOURCES_HEADER) - - js = await response.json() - accounts = js["data"]["accounts"]["data"]["data"] - - for account in accounts: - if account["statusCategory"] == STATUS_CATEGORY_OPEN: - result.append(account["accountNumber"]) - except Exception as e: - _LOGGER.error(f"Getting accounts {e}") - - # self._account_number = js["data"]["selectedAccount"]["data"]["accountNumber"] - # self._premise_number = js["data"]["selectedAccount"]["data"]["acctSecSettings"]["premiseNumber"] - return result - - async def __async_get_data(self, account) -> dict: - _LOGGER.info(f"Getting Data") - data = {} - - async with async_timeout.timeout(TIMEOUT): - response = await self._session.get( - URL_RESOURCES_ACCOUNT.format(account=account) - ) - accountData = (await response.json())["data"] - - premise = accountData["premiseNumber"].zfill(9) - - # currentBillDate - currentBillDate = datetime.strptime( - accountData["currentBillDate"].replace("-", "").split("T")[0], "%Y%m%d" - ).date() - - # nextBillDate - nextBillDate = datetime.strptime( - accountData["nextBillDate"].replace("-", "").split("T")[0], "%Y%m%d" - ).date() - - data["current_bill_date"] = str(currentBillDate) - data["next_bill_date"] = str(nextBillDate) - - today = datetime.now().date() - remaining = (nextBillDate - today).days - days = (today - currentBillDate).days - - data["service_days"] = (nextBillDate - currentBillDate).days - data["as_of_days"] = days - data["remaining_days"] = remaining - - # zip code - zip_code = accountData["serviceAddress"]["zip"] - - # projected bill - pbData = await self.__getFromProjectedBill(account, premise, currentBillDate) - data.update(pbData) - - # programs - programsData = accountData["programs"]["data"] - - programs = dict() - _LOGGER.info(f"Getting Programs") - for program in programsData: - if "enrollmentStatus" in program.keys(): - key = program["name"] - programs[key] = program["enrollmentStatus"] == ENROLLED - - if programs["BBL"]: - # budget billing - data["budget_bill"] = True - bblData = await self.__getBBL_async(account, data) - data.update(bblData) - - data.update( - await self.__getDataFromEnergyService(account, premise, currentBillDate) - ) - - data.update(await self.__getDataFromApplianceUsage(account, currentBillDate)) - return data - - async def __getFromProjectedBill(self, account, premise, currentBillDate) -> dict: - data = {} - - try: - async with async_timeout.timeout(TIMEOUT): - response = await self._session.get( - URL_RESOURCES_PROJECTED_BILL.format( - account=account, - premise=premise, - lastBillDate=currentBillDate.strftime("%m%d%Y"), - ) - ) - - if response.status == 200: - - projectedBillData = (await response.json())["data"] - - billToDate = float(projectedBillData["billToDate"]) - projectedBill = float(projectedBillData["projectedBill"]) - dailyAvg = float(projectedBillData["dailyAvg"]) - avgHighTemp = int(projectedBillData["avgHighTemp"]) - - data["bill_to_date"] = billToDate - data["projected_bill"] = projectedBill - data["daily_avg"] = dailyAvg - data["avg_high_temp"] = avgHighTemp - except: - pass - - return data - - async def __getBBL_async(self, account, projectedBillData) -> dict: - _LOGGER.info(f"Getting budget billing data") - data = {} - - URL = "https://www.fpl.com/api/resources/account/{account}/budgetBillingGraph/premiseDetails" - try: - async with async_timeout.timeout(TIMEOUT): - response = await self._session.get(URL.format(account=account)) - if response.status == 200: - r = (await response.json())["data"] - dataList = r["graphData"] - - startIndex = len(dataList) - 1 - - billingCharge = 0 - budgetBillDeferBalance = r["defAmt"] - - projectedBill = projectedBillData["projected_bill"] - asOfDays = projectedBillData["as_of_days"] - - for det in dataList: - billingCharge += det["actuallBillAmt"] - - calc1 = (projectedBill + billingCharge) / 12 - calc2 = (1 / 12) * (budgetBillDeferBalance) - - projectedBudgetBill = round(calc1 + calc2, 2) - bbDailyAvg = round(projectedBudgetBill / 30, 2) - bbAsOfDateAmt = round(projectedBudgetBill / 30 * asOfDays, 2) - - data["budget_billing_daily_avg"] = bbDailyAvg - data["budget_billing_bill_to_date"] = bbAsOfDateAmt - - data["budget_billing_projected_bill"] = float(projectedBudgetBill) - except: - pass - - URL = "https://www.fpl.com/api/resources/account/{account}/budgetBillingGraph" - - try: - async with async_timeout.timeout(TIMEOUT): - response = await self._session.get(URL.format(account=account)) - if response.status == 200: - r = (await response.json())["data"] - data["bill_to_date"] = float(r["eleAmt"]) - data["defered_amount"] = float(r["defAmt"]) - except: - pass - - return data - - async def __getDataFromEnergyService( - self, account, premise, lastBilledDate - ) -> dict: - _LOGGER.info(f"Getting data from energy service") - URL = "https://www.fpl.com/dashboard-api/resources/account/{account}/energyService/{account}" - - date = str(lastBilledDate.strftime("%m%d%Y")) - JSON = { - "recordCount": 24, - "status": 2, - "channel": "WEB", - "amrFlag": "Y", - "accountType": "RESIDENTIAL", - "revCode": "1", - "premiseNumber": premise, - "projectedBillFlag": True, - "billComparisionFlag": True, - "monthlyFlag": True, - "frequencyType": "Daily", - "lastBilledDate": date, - "applicationPage": "resDashBoard", - } - - data = {} - - async with async_timeout.timeout(TIMEOUT): - response = await self._session.post(URL.format(account=account), json=JSON) - if response.status == 200: - r = (await response.json())["data"] - dailyUsage = [] - - # totalPowerUsage = 0 - if "data" in r["DailyUsage"]: - for daily in r["DailyUsage"]["data"]: - if ( - "kwhUsed" in daily.keys() - and "billingCharge" in daily.keys() - and "date" in daily.keys() - and "averageHighTemperature" in daily.keys() - ): - dailyUsage.append( - { - "usage": daily["kwhUsed"], - "cost": daily["billingCharge"], - "date": daily["date"], - "max_temperature": daily["averageHighTemperature"], - "netDeliveredKwh": daily["netDeliveredKwh"], - "netReceivedKwh": daily["netReceivedKwh"], - "readTime": daily["readTime"], - } - ) - # totalPowerUsage += int(daily["kwhUsed"]) - - # data["total_power_usage"] = totalPowerUsage - data["daily_usage"] = dailyUsage - - data["projectedKWH"] = r["CurrentUsage"]["projectedKWH"] - data["dailyAverageKWH"] = r["CurrentUsage"]["dailyAverageKWH"] - data["billToDateKWH"] = r["CurrentUsage"]["billToDateKWH"] - data["recMtrReading"] = r["CurrentUsage"]["recMtrReading"] - data["delMtrReading"] = r["CurrentUsage"]["delMtrReading"] - data["billStartDate"] = r["CurrentUsage"]["billStartDate"] - return data - - async def __getDataFromApplianceUsage(self, account, lastBilledDate) -> dict: - _LOGGER.info(f"Getting data from applicance usage") - URL = "https://www.fpl.com/dashboard-api/resources/account/{account}/applianceUsage/{account}" - JSON = {"startDate": str(lastBilledDate.strftime("%m%d%Y"))} - data = {} - try: - async with async_timeout.timeout(TIMEOUT): - response = await self._session.post( - URL.format(account=account), json=JSON - ) - if response.status == 200: - electric = (await response.json())["data"]["electric"] - - full = 100 - for e in electric: - rr = round(float(e["percentageDollar"])) - if rr < full: - full = full - rr - else: - rr = full - data[e["category"].replace(" ", "_")] = rr - - except: - pass - - return {"energy_percent_by_applicance": data} diff --git a/custom_components/fpl/fpl/manifest.json b/custom_components/fpl/fpl/manifest.json deleted file mode 100644 index 805319d..0000000 --- a/custom_components/fpl/fpl/manifest.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "domain": "fpl", - "name": "FPL", - "documentation": "https://github.com/dotKrad/hass-fpl", - "iot_class": "cloud_polling", - "dependencies": [], - "config_flow": true, - "codeowners": [ - "@dotKrad" - ], - "requirements": [ - "bs4", - "integrationhelper" - ], - "homeassistant": "0.96.0", - "version": "1.0.0" -} \ No newline at end of file diff --git a/custom_components/fpl/fpl/sensor.py b/custom_components/fpl/fpl/sensor.py deleted file mode 100644 index 6b2c85d..0000000 --- a/custom_components/fpl/fpl/sensor.py +++ /dev/null @@ -1,82 +0,0 @@ -"""Sensor platform for integration_blueprint.""" - -from .sensor_KWHSensor import ( - ProjectedKWHSensor, - DailyAverageKWHSensor, - BillToDateKWHSensor, - NetReceivedKWHSensor, - NetDeliveredKWHSensor, -) -from .sensor_DatesSensor import ( - CurrentBillDateSensor, - NextBillDateSensor, - ServiceDaysSensor, - AsOfDaysSensor, - RemainingDaysSensor, -) -from .sensor_ProjectedBillSensor import ( - FplProjectedBillSensor, - ProjectedBudgetBillSensor, - ProjectedActualBillSensor, - DeferedAmountSensor, -) -from .sensor_AverageDailySensor import ( - FplAverageDailySensor, - BudgetDailyAverageSensor, -) -from .sensor_DailyUsageSensor import ( - FplDailyUsageKWHSensor, - FplDailyUsageSensor, - FplDailyDeliveredKWHSensor, - FplDailyReceivedKWHSensor, -) - -from .const import DOMAIN - -from .sensor_AllData import AllDataSensor -from .TestSensor import TestSensor - - -async def async_setup_entry(hass, entry, async_add_devices): - """Setup sensor platform.""" - accounts = entry.data.get("accounts") - - coordinator = hass.data[DOMAIN][entry.entry_id] - fpl_accounts = [] - - for account in accounts: - # Test Sensor - # fpl_accounts.append(TestSensor(coordinator, entry, account)) - # All data sensor - # fpl_accounts.append(AllDataSensor(coordinator, entry, account)) - - # bill sensors - fpl_accounts.append(FplProjectedBillSensor(coordinator, entry, account)) - fpl_accounts.append(ProjectedBudgetBillSensor(coordinator, entry, account)) - fpl_accounts.append(ProjectedActualBillSensor(coordinator, entry, account)) - fpl_accounts.append(DeferedAmountSensor(coordinator, entry, account)) - - # usage sensors - fpl_accounts.append(FplAverageDailySensor(coordinator, entry, account)) - fpl_accounts.append(BudgetDailyAverageSensor(coordinator, entry, account)) - fpl_accounts.append(FplDailyUsageSensor(coordinator, entry, account)) - fpl_accounts.append(FplDailyUsageKWHSensor(coordinator, entry, account)) - fpl_accounts.append(FplDailyReceivedKWHSensor(coordinator, entry, account)) - fpl_accounts.append(FplDailyDeliveredKWHSensor(coordinator, entry, account)) - - # date sensors - fpl_accounts.append(CurrentBillDateSensor(coordinator, entry, account)) - fpl_accounts.append(NextBillDateSensor(coordinator, entry, account)) - fpl_accounts.append(ServiceDaysSensor(coordinator, entry, account)) - fpl_accounts.append(AsOfDaysSensor(coordinator, entry, account)) - fpl_accounts.append(RemainingDaysSensor(coordinator, entry, account)) - - # KWH sensors - fpl_accounts.append(ProjectedKWHSensor(coordinator, entry, account)) - fpl_accounts.append(DailyAverageKWHSensor(coordinator, entry, account)) - fpl_accounts.append(BillToDateKWHSensor(coordinator, entry, account)) - fpl_accounts.append(NetReceivedKWHSensor(coordinator, entry, account)) - fpl_accounts.append(NetDeliveredKWHSensor(coordinator, entry, account)) - - - async_add_devices(fpl_accounts) diff --git a/custom_components/fpl/fpl/sensor_AllData.py b/custom_components/fpl/fpl/sensor_AllData.py deleted file mode 100644 index 9f9840d..0000000 --- a/custom_components/fpl/fpl/sensor_AllData.py +++ /dev/null @@ -1,33 +0,0 @@ -from .fplEntity import FplEntity - - -class AllDataSensor(FplEntity): - def __init__(self, coordinator, config, account): - super().__init__(coordinator, config, account, "") - - @property - def state(self): - budget = self.getData("budget_bill") - budget_billing_projected_bill = self.getData("budget_billing_projected_bill") - - if budget == True and budget_billing_projected_bill is not None: - return self.getData("budget_billing_projected_bill") - - return self.getData("projected_bill") - - def defineAttributes(self): - """Return the state attributes.""" - return self.coordinator.data.get(self.account) - - @property - def icon(self): - return "mdi:currency-usd" - - def defineAttributes(self): - """Return the state attributes.""" - attributes = {} - attributes["friendly_name"] = "Budget Projected Bill" - attributes["device_class"] = "monitary" - attributes["state_class"] = "total" - attributes["unit_of_measurement"] = "$" - return attributes \ No newline at end of file diff --git a/custom_components/fpl/fpl/sensor_AverageDailySensor.py b/custom_components/fpl/fpl/sensor_AverageDailySensor.py deleted file mode 100644 index d170470..0000000 --- a/custom_components/fpl/fpl/sensor_AverageDailySensor.py +++ /dev/null @@ -1,51 +0,0 @@ -from .fplEntity import FplEntity - - -class FplAverageDailySensor(FplEntity): - def __init__(self, coordinator, config, account): - super().__init__(coordinator, config, account, "Daily Average") - - @property - def state(self): - budget = self.getData("budget_bill") - budget_billing_projected_bill = self.getData("budget_billing_daily_avg") - - if budget == True and budget_billing_projected_bill is not None: - return self.getData("budget_billing_daily_avg") - - return self.getData("daily_avg") - - @property - def icon(self): - return "mdi:currency-usd" - - def defineAttributes(self): - """Return the state attributes.""" - attributes = {} - attributes["friendly_name"] = "Daily Average" - attributes["device_class"] = "monitary" - attributes["state_class"] = "total" - attributes["unit_of_measurement"] = "$" - return attributes - -class BudgetDailyAverageSensor(FplEntity): - def __init__(self, coordinator, config, account): - super().__init__(coordinator, config, account, "Budget Daily Average") - - @property - def state(self): - return self.getData("budget_billing_daily_avg") - - @property - def icon(self): - return "mdi:currency-usd" - - def defineAttributes(self): - """Return the state attributes.""" - attributes = {} - attributes["friendly_name"] = "Budget Daily Average" - attributes["device_class"] = "monitary" - attributes["state_class"] = "total" - attributes["unit_of_measurement"] = "$" - return attributes - diff --git a/custom_components/fpl/fpl/sensor_DailyUsageSensor.py b/custom_components/fpl/fpl/sensor_DailyUsageSensor.py deleted file mode 100644 index 3ec2928..0000000 --- a/custom_components/fpl/fpl/sensor_DailyUsageSensor.py +++ /dev/null @@ -1,120 +0,0 @@ -from .fplEntity import FplEntity - - -class FplDailyUsageSensor(FplEntity): - def __init__(self, coordinator, config, account): - super().__init__(coordinator, config, account, "Daily Usage") - - @property - def state(self): - data = self.getData("daily_usage") - - if ((data is not None) and (len(data) > 0)): - return data[-1]["cost"] - - return None - - def defineAttributes(self): - """Return the state attributes.""" - data = self.getData("daily_usage") - attributes = {} - attributes["friendly_name"] = "Daily Usage" - attributes["device_class"] = "monetary" - attributes["state_class"] = "total_increasing" - attributes["unit_of_measurement"] = "$" - if ((data is not None) and (data[-1]["cost"] is not None)): - attributes["date"] = data[-1]["readTime"] - return attributes - - @property - def icon(self): - return "mdi:currency-usd" - - -class FplDailyUsageKWHSensor(FplEntity): - def __init__(self, coordinator, config, account): - super().__init__(coordinator, config, account, "Daily Usage KWH") - - @property - def state(self): - data = self.getData("daily_usage") - - if ((data is not None) and (data[-1]["usage"] is not None)): - return data[-1]["usage"] - - return None - - def defineAttributes(self): - """Return the state attributes.""" - data = self.getData("daily_usage") - - attributes = {} - attributes["friendly_name"] = "Daily Usage" - attributes["device_class"] = "energy" - attributes["state_class"] = "total_increasing" - attributes["unit_of_measurement"] = "kWh" - - if data is not None: - if ((data[-1] is not None) and (data[-1]["readTime"] is not None)): - attributes["date"] = data[-1]["readTime"] - if ((data[-2] is not None) and (data[-2]["readTime"] is not None)): - attributes["last_reset"] = data[-2]["readTime"] - - return attributes - - @property - def icon(self): - return "mdi:flash" - -class FplDailyReceivedKWHSensor(FplEntity): - def __init__(self, coordinator, config, account): - super().__init__(coordinator, config, account, "Daily Received KWH") - - @property - def state(self): - data = self.getData("daily_usage") - return data[-1]["netReceivedKwh"] - - def defineAttributes(self): - """Return the state attributes.""" - data = self.getData("daily_usage") - - attributes = {} - attributes["friendly_name"] = "Daily Return to Grid" - attributes["device_class"] = "energy" - attributes["state_class"] = "total_increasing" - attributes["unit_of_measurement"] = "kWh" - attributes["date"] = data[-1]["readTime"] - attributes["last_reset"] = data[-2]["readTime"] - return attributes - - - @property - def icon(self): - return "mdi:flash" - -class FplDailyDeliveredKWHSensor(FplEntity): - def __init__(self, coordinator, config, account): - super().__init__(coordinator, config, account, "Daily Delivered KWH") - - @property - def state(self): - data = self.getData("daily_usage") - return data[-1]["netDeliveredKwh"] - - def defineAttributes(self): - """Return the state attributes.""" - data = self.getData("daily_usage") - - attributes = {} - attributes["friendly_name"] = "Daily Consumption" - attributes["device_class"] = "energy" - attributes["state_class"] = "total_increasing" - attributes["unit_of_measurement"] = "kWh" - attributes["date"] = data[-1]["readTime"] - attributes["last_reset"] = data[-2]["readTime"] - return attributes - - @property - def icon(self): - return "mdi:flash" diff --git a/custom_components/fpl/fpl/sensor_DatesSensor.py b/custom_components/fpl/fpl/sensor_DatesSensor.py deleted file mode 100644 index a301847..0000000 --- a/custom_components/fpl/fpl/sensor_DatesSensor.py +++ /dev/null @@ -1,97 +0,0 @@ -from .fplEntity import FplEntity -import datetime - -class CurrentBillDateSensor(FplEntity): - def __init__(self, coordinator, config, account): - super().__init__(coordinator, config, account, "Billing Current Date") - - @property - def state(self): - return datetime.date.fromisoformat(self.getData("current_bill_date")) - - @property - def icon(self): - return "mdi:calendar" - - def defineAttributes(self): - """Return the state attributes.""" - attributes = {} - attributes["device_class"] = "date" - attributes["friendly_name"] = "Billing Current" - return attributes - -class NextBillDateSensor(FplEntity): - def __init__(self, coordinator, config, account): - super().__init__(coordinator, config, account, "Billing Next") - - @property - def state(self): - return datetime.date.fromisoformat(self.getData("next_bill_date")) - - @property - def icon(self): - return "mdi:calendar" - - def defineAttributes(self): - """Return the state attributes.""" - attributes = {} - attributes["device_class"] = "date" - attributes["friendly_name"] = "Billing Next" - return attributes - -class ServiceDaysSensor(FplEntity): - def __init__(self, coordinator, config, account): - super().__init__(coordinator, config, account, "Billing Total Days") - - @property - def state(self): - return self.getData("service_days") - - @property - def icon(self): - return "mdi:calendar" - - def defineAttributes(self): - """Return the state attributes.""" - attributes = {} - attributes["unit_of_measurement"] = "days" - attributes["friendly_name"] = "Billing Total" - return attributes - -class AsOfDaysSensor(FplEntity): - def __init__(self, coordinator, config, account): - super().__init__(coordinator, config, account, "Billing As Of") - - @property - def state(self): - return self.getData("as_of_days") - - @property - def icon(self): - return "mdi:calendar" - - def defineAttributes(self): - """Return the state attributes.""" - attributes = {} - attributes["unit_of_measurement"] = "days" - attributes["friendly_name"] = "Billing As Of" - return attributes - -class RemainingDaysSensor(FplEntity): - def __init__(self, coordinator, config, account): - super().__init__(coordinator, config, account, "Billing Remaining") - - @property - def state(self): - return self.getData("remaining_days") - - @property - def icon(self): - return "mdi:calendar" - - def defineAttributes(self): - """Return the state attributes.""" - attributes = {} - attributes["unit_of_measurement"] = "days" - attributes["friendly_name"] = "Billing Remaining" - return attributes diff --git a/custom_components/fpl/fpl/sensor_KWHSensor.py b/custom_components/fpl/fpl/sensor_KWHSensor.py deleted file mode 100644 index 449f893..0000000 --- a/custom_components/fpl/fpl/sensor_KWHSensor.py +++ /dev/null @@ -1,110 +0,0 @@ -from .fplEntity import FplEntity - - -class ProjectedKWHSensor(FplEntity): - def __init__(self, coordinator, config, account): - super().__init__(coordinator, config, account, "Projected") - - @property - def state(self): - return self.getData("projectedKWH") - - @property - def icon(self): - return "mdi:flash" - def defineAttributes(self): - """Return the state attributes.""" - attributes = {} - attributes["friendly_name"] = "Projected KWH" - attributes["device_class"] = "energy" - attributes["state_class"] = "total_increasing" - attributes["unit_of_measurement"] = "kWh" - return attributes - -class DailyAverageKWHSensor(FplEntity): - def __init__(self, coordinator, config, account): - super().__init__(coordinator, config, account, "Daily Average") - - @property - def state(self): - return self.getData("dailyAverageKWH") - - @property - def icon(self): - return "mdi:flash" - - def defineAttributes(self): - """Return the state attributes.""" - attributes = {} - attributes["friendly_name"] = "Daily Average" - attributes["device_class"] = "energy" - attributes["state_class"] = "total_increasing" - attributes["unit_of_measurement"] = "kWh" - return attributes - -class BillToDateKWHSensor(FplEntity): - def __init__(self, coordinator, config, account): - super().__init__(coordinator, config, account, "Bill To Date") - - @property - def state(self): - return self.getData("billToDateKWH") - - @property - def icon(self): - return "mdi:flash" - - def defineAttributes(self): - """Return the state attributes.""" - attributes = {} - attributes["friendly_name"] = "Bill To Date" - attributes["device_class"] = "energy" - attributes["state_class"] = "total_increasing" - attributes["unit_of_measurement"] = "kWh" - return attributes - -class NetReceivedKWHSensor(FplEntity): - def __init__(self, coordinator, config, account): - super().__init__(coordinator, config, account, "Received Reading") - - @property - def state(self): - return self.getData("recMtrReading") - - @property - def icon(self): - return "mdi:flash" - - def defineAttributes(self): - """Return the state attributes.""" - attributes = {} - attributes["friendly_name"] = "Meter Return to Grid" - attributes["device_class"] = "energy" - attributes["state_class"] = "total_increasing" - attributes["unit_of_measurement"] = "kWh" - attributes["last_reset"] = self.getData("billStartDate") - - return attributes - -class NetDeliveredKWHSensor(FplEntity): - def __init__(self, coordinator, config, account): - super().__init__(coordinator, config, account, "Delivered Reading") - - @property - def state(self): - return self.getData("delMtrReading") - - @property - def icon(self): - return "mdi:flash" - - def defineAttributes(self): - """Return the state attributes.""" - attributes = {} - attributes["friendly_name"] = "Meter Consumption" - attributes["device_class"] = "energy" - attributes["state_class"] = "total_increasing" - attributes["unit_of_measurement"] = "kWh" - attributes["last_reset"] = self.getData("billStartDate") - - return attributes diff --git a/custom_components/fpl/fpl/sensor_ProjectedBillSensor.py b/custom_components/fpl/fpl/sensor_ProjectedBillSensor.py deleted file mode 100644 index 44650f0..0000000 --- a/custom_components/fpl/fpl/sensor_ProjectedBillSensor.py +++ /dev/null @@ -1,99 +0,0 @@ -from .fplEntity import FplEntity - - -class FplProjectedBillSensor(FplEntity): - def __init__(self, coordinator, config, account): - super().__init__(coordinator, config, account, "Projected Bill") - - @property - def state(self): - budget = self.getData("budget_bill") - budget_billing_projected_bill = self.getData("budget_billing_projected_bill") - - if budget == True and budget_billing_projected_bill is not None: - return self.getData("budget_billing_projected_bill") - - return self.getData("projected_bill") - - def defineAttributes(self): - """Return the state attributes.""" - attributes = {} - attributes["friendly_name"] = "Projected Bill" - attributes["device_class"] = "monitary" - attributes["state_class"] = "total" - attributes["unit_of_measurement"] = "$" - return attributes - - @property - def icon(self): - return "mdi:currency-usd" - - -# Defered Amount -class DeferedAmountSensor(FplEntity): - def __init__(self, coordinator, config, account): - super().__init__(coordinator, config, account, "Defered Amount") - - @property - def state(self): - if self.getData("defered_amount") is not None: - return self.getData("defered_amount") - return 0 - - @property - def icon(self): - return "mdi:currency-usd" - - def defineAttributes(self): - """Return the state attributes.""" - attributes = {} - attributes["friendly_name"] = "Defered Amount" - attributes["device_class"] = "monitary" - attributes["state_class"] = "total" - attributes["unit_of_measurement"] = "$" - return attributes - - -class ProjectedBudgetBillSensor(FplEntity): - def __init__(self, coordinator, config, account): - super().__init__(coordinator, config, account, "Projected Budget Bill") - - @property - def state(self): - return self.getData("budget_billing_projected_bill") - - @property - def icon(self): - return "mdi:currency-usd" - - def defineAttributes(self): - """Return the state attributes.""" - attributes = {} - attributes["friendly_name"] = "Projected Budget Bill" - attributes["device_class"] = "monitary" - attributes["state_class"] = "total" - attributes['unit_of_measurement'] = "$" - return attributes - - -class ProjectedActualBillSensor(FplEntity): - def __init__(self, coordinator, config, account): - super().__init__(coordinator, config, account, "Projected Actual Bill") - - @property - def state(self): - return self.getData("projected_bill") - - @property - def icon(self): - return "mdi:currency-usd" - - def defineAttributes(self): - """Return the state attributes.""" - attributes = {} - attributes["friendly_name"] = "Projected Actual Bill" - attributes["device_class"] = "monitary" - attributes["state_class"] = "total" - attributes['unit_of_measurement'] = "$" - - return attributes diff --git a/custom_components/fpl/fplDataUpdateCoordinator.py b/custom_components/fpl/fplDataUpdateCoordinator.py index e4056ac..5679cbf 100644 --- a/custom_components/fpl/fplDataUpdateCoordinator.py +++ b/custom_components/fpl/fplDataUpdateCoordinator.py @@ -7,7 +7,7 @@ from datetime import timedelta from .fplapi import FplApi from .const import DOMAIN -SCAN_INTERVAL = timedelta(seconds=7200) +SCAN_INTERVAL = timedelta(seconds=1200) _LOGGER: logging.Logger = logging.getLogger(__package__) diff --git a/custom_components/fpl/fplEntity.py b/custom_components/fpl/fplEntity.py index 7b30d27..e9bf200 100644 --- a/custom_components/fpl/fplEntity.py +++ b/custom_components/fpl/fplEntity.py @@ -19,15 +19,11 @@ class FplEntity(CoordinatorEntity): ) return id - @property - def name(self): - return f"{DOMAIN.upper()} {self.account} {self.sensorName}" - @property def device_info(self): return { "identifiers": {(DOMAIN, self.account)}, - "name": f"Account {self.account}", + "name": f"FPL Account {self.account}", "model": VERSION, "manufacturer": "Florida Power & Light", } diff --git a/custom_components/fpl/fplapi.py b/custom_components/fpl/fplapi.py index 3265f81..4790cd0 100644 --- a/custom_components/fpl/fplapi.py +++ b/custom_components/fpl/fplapi.py @@ -300,6 +300,9 @@ class FplApi(object): "cost": daily["billingCharge"], "date": daily["date"], "max_temperature": daily["averageHighTemperature"], + "netDeliveredKwh": daily["netDeliveredKwh"], + "netReceivedKwh": daily["netReceivedKwh"], + "readTime": daily["readTime"], } ) # totalPowerUsage += int(daily["kwhUsed"]) @@ -312,6 +315,7 @@ class FplApi(object): data["billToDateKWH"] = r["CurrentUsage"]["billToDateKWH"] data["recMtrReading"] = r["CurrentUsage"]["recMtrReading"] data["delMtrReading"] = r["CurrentUsage"]["delMtrReading"] + data["billStartDate"] = r["CurrentUsage"]["billStartDate"] return data async def __getDataFromApplianceUsage(self, account, lastBilledDate) -> dict: diff --git a/custom_components/fpl/sensor.py b/custom_components/fpl/sensor.py index a868e1e..6b2c85d 100644 --- a/custom_components/fpl/sensor.py +++ b/custom_components/fpl/sensor.py @@ -23,9 +23,14 @@ from .sensor_ProjectedBillSensor import ( from .sensor_AverageDailySensor import ( FplAverageDailySensor, BudgetDailyAverageSensor, - ActualDailyAverageSensor, ) -from .sensor_DailyUsageSensor import FplDailyUsageKWHSensor, FplDailyUsageSensor +from .sensor_DailyUsageSensor import ( + FplDailyUsageKWHSensor, + FplDailyUsageSensor, + FplDailyDeliveredKWHSensor, + FplDailyReceivedKWHSensor, +) + from .const import DOMAIN from .sensor_AllData import AllDataSensor @@ -54,10 +59,10 @@ async def async_setup_entry(hass, entry, async_add_devices): # usage sensors fpl_accounts.append(FplAverageDailySensor(coordinator, entry, account)) fpl_accounts.append(BudgetDailyAverageSensor(coordinator, entry, account)) - fpl_accounts.append(ActualDailyAverageSensor(coordinator, entry, account)) - fpl_accounts.append(FplDailyUsageSensor(coordinator, entry, account)) fpl_accounts.append(FplDailyUsageKWHSensor(coordinator, entry, account)) + fpl_accounts.append(FplDailyReceivedKWHSensor(coordinator, entry, account)) + fpl_accounts.append(FplDailyDeliveredKWHSensor(coordinator, entry, account)) # date sensors fpl_accounts.append(CurrentBillDateSensor(coordinator, entry, account)) @@ -70,5 +75,8 @@ async def async_setup_entry(hass, entry, async_add_devices): fpl_accounts.append(ProjectedKWHSensor(coordinator, entry, account)) fpl_accounts.append(DailyAverageKWHSensor(coordinator, entry, account)) fpl_accounts.append(BillToDateKWHSensor(coordinator, entry, account)) + fpl_accounts.append(NetReceivedKWHSensor(coordinator, entry, account)) + fpl_accounts.append(NetDeliveredKWHSensor(coordinator, entry, account)) + async_add_devices(fpl_accounts) diff --git a/custom_components/fpl/sensor_AllData.py b/custom_components/fpl/sensor_AllData.py index 98044b3..9f9840d 100644 --- a/custom_components/fpl/sensor_AllData.py +++ b/custom_components/fpl/sensor_AllData.py @@ -22,3 +22,12 @@ class AllDataSensor(FplEntity): @property def icon(self): return "mdi:currency-usd" + + def defineAttributes(self): + """Return the state attributes.""" + attributes = {} + attributes["friendly_name"] = "Budget Projected Bill" + attributes["device_class"] = "monitary" + attributes["state_class"] = "total" + attributes["unit_of_measurement"] = "$" + return attributes \ No newline at end of file diff --git a/custom_components/fpl/sensor_AverageDailySensor.py b/custom_components/fpl/sensor_AverageDailySensor.py index 66d25f1..d170470 100644 --- a/custom_components/fpl/sensor_AverageDailySensor.py +++ b/custom_components/fpl/sensor_AverageDailySensor.py @@ -19,6 +19,14 @@ class FplAverageDailySensor(FplEntity): def icon(self): return "mdi:currency-usd" + def defineAttributes(self): + """Return the state attributes.""" + attributes = {} + attributes["friendly_name"] = "Daily Average" + attributes["device_class"] = "monitary" + attributes["state_class"] = "total" + attributes["unit_of_measurement"] = "$" + return attributes class BudgetDailyAverageSensor(FplEntity): def __init__(self, coordinator, config, account): @@ -32,15 +40,12 @@ class BudgetDailyAverageSensor(FplEntity): def icon(self): return "mdi:currency-usd" + def defineAttributes(self): + """Return the state attributes.""" + attributes = {} + attributes["friendly_name"] = "Budget Daily Average" + attributes["device_class"] = "monitary" + attributes["state_class"] = "total" + attributes["unit_of_measurement"] = "$" + return attributes -class ActualDailyAverageSensor(FplEntity): - def __init__(self, coordinator, config, account): - super().__init__(coordinator, config, account, "Actual Daily Average") - - @property - def state(self): - return self.getData("daily_avg") - - @property - def icon(self): - return "mdi:currency-usd" diff --git a/custom_components/fpl/sensor_DailyUsageSensor.py b/custom_components/fpl/sensor_DailyUsageSensor.py index 299188e..3ec2928 100644 --- a/custom_components/fpl/sensor_DailyUsageSensor.py +++ b/custom_components/fpl/sensor_DailyUsageSensor.py @@ -9,7 +9,7 @@ class FplDailyUsageSensor(FplEntity): def state(self): data = self.getData("daily_usage") - if len(data) > 0: + if ((data is not None) and (len(data) > 0)): return data[-1]["cost"] return None @@ -17,11 +17,14 @@ class FplDailyUsageSensor(FplEntity): def defineAttributes(self): """Return the state attributes.""" data = self.getData("daily_usage") - - if len(data) > 0: - return {"date": data[-1]["date"]} - - return {} + attributes = {} + attributes["friendly_name"] = "Daily Usage" + attributes["device_class"] = "monetary" + attributes["state_class"] = "total_increasing" + attributes["unit_of_measurement"] = "$" + if ((data is not None) and (data[-1]["cost"] is not None)): + attributes["date"] = data[-1]["readTime"] + return attributes @property def icon(self): @@ -36,7 +39,7 @@ class FplDailyUsageKWHSensor(FplEntity): def state(self): data = self.getData("daily_usage") - if len(data) > 0: + if ((data is not None) and (data[-1]["usage"] is not None)): return data[-1]["usage"] return None @@ -45,11 +48,73 @@ class FplDailyUsageKWHSensor(FplEntity): """Return the state attributes.""" data = self.getData("daily_usage") - if len(data) > 0: - return {"date": data[-1]["date"]} + attributes = {} + attributes["friendly_name"] = "Daily Usage" + attributes["device_class"] = "energy" + attributes["state_class"] = "total_increasing" + attributes["unit_of_measurement"] = "kWh" - return {} + if data is not None: + if ((data[-1] is not None) and (data[-1]["readTime"] is not None)): + attributes["date"] = data[-1]["readTime"] + if ((data[-2] is not None) and (data[-2]["readTime"] is not None)): + attributes["last_reset"] = data[-2]["readTime"] + + return attributes @property def icon(self): - return "mdi:currency-usd" + return "mdi:flash" + +class FplDailyReceivedKWHSensor(FplEntity): + def __init__(self, coordinator, config, account): + super().__init__(coordinator, config, account, "Daily Received KWH") + + @property + def state(self): + data = self.getData("daily_usage") + return data[-1]["netReceivedKwh"] + + def defineAttributes(self): + """Return the state attributes.""" + data = self.getData("daily_usage") + + attributes = {} + attributes["friendly_name"] = "Daily Return to Grid" + attributes["device_class"] = "energy" + attributes["state_class"] = "total_increasing" + attributes["unit_of_measurement"] = "kWh" + attributes["date"] = data[-1]["readTime"] + attributes["last_reset"] = data[-2]["readTime"] + return attributes + + + @property + def icon(self): + return "mdi:flash" + +class FplDailyDeliveredKWHSensor(FplEntity): + def __init__(self, coordinator, config, account): + super().__init__(coordinator, config, account, "Daily Delivered KWH") + + @property + def state(self): + data = self.getData("daily_usage") + return data[-1]["netDeliveredKwh"] + + def defineAttributes(self): + """Return the state attributes.""" + data = self.getData("daily_usage") + + attributes = {} + attributes["friendly_name"] = "Daily Consumption" + attributes["device_class"] = "energy" + attributes["state_class"] = "total_increasing" + attributes["unit_of_measurement"] = "kWh" + attributes["date"] = data[-1]["readTime"] + attributes["last_reset"] = data[-2]["readTime"] + return attributes + + @property + def icon(self): + return "mdi:flash" diff --git a/custom_components/fpl/sensor_DatesSensor.py b/custom_components/fpl/sensor_DatesSensor.py index b0414d7..a301847 100644 --- a/custom_components/fpl/sensor_DatesSensor.py +++ b/custom_components/fpl/sensor_DatesSensor.py @@ -1,35 +1,47 @@ from .fplEntity import FplEntity - +import datetime class CurrentBillDateSensor(FplEntity): def __init__(self, coordinator, config, account): - super().__init__(coordinator, config, account, "Current Bill Date") + super().__init__(coordinator, config, account, "Billing Current Date") @property def state(self): - return self.getData("current_bill_date") + return datetime.date.fromisoformat(self.getData("current_bill_date")) @property def icon(self): return "mdi:calendar" + def defineAttributes(self): + """Return the state attributes.""" + attributes = {} + attributes["device_class"] = "date" + attributes["friendly_name"] = "Billing Current" + return attributes class NextBillDateSensor(FplEntity): def __init__(self, coordinator, config, account): - super().__init__(coordinator, config, account, "Next Bill Date") + super().__init__(coordinator, config, account, "Billing Next") @property def state(self): - return self.getData("next_bill_date") + return datetime.date.fromisoformat(self.getData("next_bill_date")) @property def icon(self): return "mdi:calendar" + def defineAttributes(self): + """Return the state attributes.""" + attributes = {} + attributes["device_class"] = "date" + attributes["friendly_name"] = "Billing Next" + return attributes class ServiceDaysSensor(FplEntity): def __init__(self, coordinator, config, account): - super().__init__(coordinator, config, account, "Service Days") + super().__init__(coordinator, config, account, "Billing Total Days") @property def state(self): @@ -39,10 +51,16 @@ class ServiceDaysSensor(FplEntity): def icon(self): return "mdi:calendar" + def defineAttributes(self): + """Return the state attributes.""" + attributes = {} + attributes["unit_of_measurement"] = "days" + attributes["friendly_name"] = "Billing Total" + return attributes class AsOfDaysSensor(FplEntity): def __init__(self, coordinator, config, account): - super().__init__(coordinator, config, account, "As Of Days") + super().__init__(coordinator, config, account, "Billing As Of") @property def state(self): @@ -52,10 +70,16 @@ class AsOfDaysSensor(FplEntity): def icon(self): return "mdi:calendar" + def defineAttributes(self): + """Return the state attributes.""" + attributes = {} + attributes["unit_of_measurement"] = "days" + attributes["friendly_name"] = "Billing As Of" + return attributes class RemainingDaysSensor(FplEntity): def __init__(self, coordinator, config, account): - super().__init__(coordinator, config, account, "Remaining Days") + super().__init__(coordinator, config, account, "Billing Remaining") @property def state(self): @@ -64,3 +88,10 @@ class RemainingDaysSensor(FplEntity): @property def icon(self): return "mdi:calendar" + + def defineAttributes(self): + """Return the state attributes.""" + attributes = {} + attributes["unit_of_measurement"] = "days" + attributes["friendly_name"] = "Billing Remaining" + return attributes diff --git a/custom_components/fpl/sensor_KWHSensor.py b/custom_components/fpl/sensor_KWHSensor.py index ba2d24a..449f893 100644 --- a/custom_components/fpl/sensor_KWHSensor.py +++ b/custom_components/fpl/sensor_KWHSensor.py @@ -3,7 +3,7 @@ from .fplEntity import FplEntity class ProjectedKWHSensor(FplEntity): def __init__(self, coordinator, config, account): - super().__init__(coordinator, config, account, "Projected KWH") + super().__init__(coordinator, config, account, "Projected") @property def state(self): @@ -12,11 +12,18 @@ class ProjectedKWHSensor(FplEntity): @property def icon(self): return "mdi:flash" - + def defineAttributes(self): + """Return the state attributes.""" + attributes = {} + attributes["friendly_name"] = "Projected KWH" + attributes["device_class"] = "energy" + attributes["state_class"] = "total_increasing" + attributes["unit_of_measurement"] = "kWh" + return attributes class DailyAverageKWHSensor(FplEntity): def __init__(self, coordinator, config, account): - super().__init__(coordinator, config, account, "Daily Average KWH") + super().__init__(coordinator, config, account, "Daily Average") @property def state(self): @@ -26,10 +33,18 @@ class DailyAverageKWHSensor(FplEntity): def icon(self): return "mdi:flash" + def defineAttributes(self): + """Return the state attributes.""" + attributes = {} + attributes["friendly_name"] = "Daily Average" + attributes["device_class"] = "energy" + attributes["state_class"] = "total_increasing" + attributes["unit_of_measurement"] = "kWh" + return attributes class BillToDateKWHSensor(FplEntity): def __init__(self, coordinator, config, account): - super().__init__(coordinator, config, account, "Bill To Date KWH") + super().__init__(coordinator, config, account, "Bill To Date") @property def state(self): @@ -39,9 +54,18 @@ class BillToDateKWHSensor(FplEntity): def icon(self): return "mdi:flash" + def defineAttributes(self): + """Return the state attributes.""" + attributes = {} + attributes["friendly_name"] = "Bill To Date" + attributes["device_class"] = "energy" + attributes["state_class"] = "total_increasing" + attributes["unit_of_measurement"] = "kWh" + return attributes + class NetReceivedKWHSensor(FplEntity): def __init__(self, coordinator, config, account): - super().__init__(coordinator, config, account, "Received Meter Reading KWH") + super().__init__(coordinator, config, account, "Received Reading") @property def state(self): @@ -51,9 +75,20 @@ class NetReceivedKWHSensor(FplEntity): def icon(self): return "mdi:flash" + def defineAttributes(self): + """Return the state attributes.""" + attributes = {} + attributes["friendly_name"] = "Meter Return to Grid" + attributes["device_class"] = "energy" + attributes["state_class"] = "total_increasing" + attributes["unit_of_measurement"] = "kWh" + attributes["last_reset"] = self.getData("billStartDate") + + return attributes + class NetDeliveredKWHSensor(FplEntity): def __init__(self, coordinator, config, account): - super().__init__(coordinator, config, account, "Delivered Meter Reading KWH") + super().__init__(coordinator, config, account, "Delivered Reading") @property def state(self): @@ -62,3 +97,14 @@ class NetDeliveredKWHSensor(FplEntity): @property def icon(self): return "mdi:flash" + + def defineAttributes(self): + """Return the state attributes.""" + attributes = {} + attributes["friendly_name"] = "Meter Consumption" + attributes["device_class"] = "energy" + attributes["state_class"] = "total_increasing" + attributes["unit_of_measurement"] = "kWh" + attributes["last_reset"] = self.getData("billStartDate") + + return attributes diff --git a/custom_components/fpl/sensor_ProjectedBillSensor.py b/custom_components/fpl/sensor_ProjectedBillSensor.py index acf7482..44650f0 100644 --- a/custom_components/fpl/sensor_ProjectedBillSensor.py +++ b/custom_components/fpl/sensor_ProjectedBillSensor.py @@ -18,12 +18,10 @@ class FplProjectedBillSensor(FplEntity): def defineAttributes(self): """Return the state attributes.""" attributes = {} - try: - if self.getData("budget_bill") == True: - attributes["budget_bill"] = self.getData("budget_bill") - except: - pass - + attributes["friendly_name"] = "Projected Bill" + attributes["device_class"] = "monitary" + attributes["state_class"] = "total" + attributes["unit_of_measurement"] = "$" return attributes @property @@ -38,7 +36,7 @@ class DeferedAmountSensor(FplEntity): @property def state(self): - if self.getData("budget_bill") == True: + if self.getData("defered_amount") is not None: return self.getData("defered_amount") return 0 @@ -46,6 +44,15 @@ class DeferedAmountSensor(FplEntity): def icon(self): return "mdi:currency-usd" + def defineAttributes(self): + """Return the state attributes.""" + attributes = {} + attributes["friendly_name"] = "Defered Amount" + attributes["device_class"] = "monitary" + attributes["state_class"] = "total" + attributes["unit_of_measurement"] = "$" + return attributes + class ProjectedBudgetBillSensor(FplEntity): def __init__(self, coordinator, config, account): @@ -59,6 +66,15 @@ class ProjectedBudgetBillSensor(FplEntity): def icon(self): return "mdi:currency-usd" + def defineAttributes(self): + """Return the state attributes.""" + attributes = {} + attributes["friendly_name"] = "Projected Budget Bill" + attributes["device_class"] = "monitary" + attributes["state_class"] = "total" + attributes['unit_of_measurement'] = "$" + return attributes + class ProjectedActualBillSensor(FplEntity): def __init__(self, coordinator, config, account): @@ -71,3 +87,13 @@ class ProjectedActualBillSensor(FplEntity): @property def icon(self): return "mdi:currency-usd" + + def defineAttributes(self): + """Return the state attributes.""" + attributes = {} + attributes["friendly_name"] = "Projected Actual Bill" + attributes["device_class"] = "monitary" + attributes["state_class"] = "total" + attributes['unit_of_measurement'] = "$" + + return attributes