Merge branch 'dev'

This commit is contained in:
Yordan Suarez
2022-07-21 21:53:25 -04:00
20 changed files with 464 additions and 625 deletions

View File

@@ -1,7 +1,7 @@
// See https://aka.ms/vscode-remote/devcontainer.json for format details. // See https://aka.ms/vscode-remote/devcontainer.json for format details.
{ {
"image": "ludeeus/container:integration-debian", "image": "ghcr.io/ludeeus/devcontainer/integration:stable",
"name": "Blueprint integration development", "name": "FPL integration development",
"context": "..", "context": "..",
"appPort": [ "appPort": [
"9123:8123" "9123:8123"

View File

@@ -1,8 +1,8 @@
name: Cron actions name: Cron actions
on: #on:
schedule: #schedule:
- cron: '0 0 * * *' #- cron: '0 0 * * *'
jobs: jobs:
validate: validate:

2
.gitignore vendored
View File

@@ -6,3 +6,5 @@ custom_components/fpl/__pycache__/
.vscode/launch.json .vscode/launch.json
test.py test.py
custom_components/fpl1/__pycache__/

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,36 +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."""
if "budget_bill" in self.data.keys():
self.attr["budget_bill"] = self.data["budget_bill"]
return self._state
@property
def icon(self):
return "mdi:currency-usd"

View File

@@ -61,7 +61,7 @@ async def async_setup_entry(hass, entry):
password = entry.data.get(CONF_PASSWORD) password = entry.data.get(CONF_PASSWORD)
# Configure the client. # Configure the client.
_LOGGER.info(f"Configuring the client") _LOGGER.info("Configuring the client")
session = async_get_clientsession(hass) session = async_get_clientsession(hass)
client = FplApi(username, password, session) client = FplApi(username, password, session)
@@ -77,7 +77,7 @@ async def async_setup_entry(hass, entry):
hass.config_entries.async_forward_entry_setup(entry, platform) hass.config_entries.async_forward_entry_setup(entry, platform)
) )
"""Set up Fpl as config entry.""" # Set up Fpl as config entry.
entry.add_update_listener(async_reload_entry) entry.add_update_listener(async_reload_entry)
return True return True

View File

@@ -1,21 +1,22 @@
"""Home Assistant Fpl integration Config Flow"""
from collections import OrderedDict from collections import OrderedDict
import voluptuous as vol import voluptuous as vol
from .fplapi import FplApi
from homeassistant import config_entries from homeassistant import config_entries
from homeassistant.helpers.aiohttp_client import async_create_clientsession from homeassistant.helpers.aiohttp_client import async_create_clientsession
from homeassistant.core import callback
from .const import DOMAIN, CONF_USERNAME, CONF_PASSWORD, CONF_NAME from .const import DOMAIN, CONF_USERNAME, CONF_PASSWORD, CONF_NAME
from .fplapi import ( from .fplapi import (
LOGIN_RESULT_OK, LOGIN_RESULT_OK,
LOGIN_RESULT_FAILURE,
LOGIN_RESULT_INVALIDUSER, LOGIN_RESULT_INVALIDUSER,
LOGIN_RESULT_INVALIDPASSWORD, LOGIN_RESULT_INVALIDPASSWORD,
FplApi,
) )
from homeassistant.core import callback
@callback @callback
def configured_instances(hass): def configured_instances(hass):
@@ -27,6 +28,7 @@ def configured_instances(hass):
class FplFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): class FplFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
"""Fpl Config Flow Handler"""
VERSION = 1 VERSION = 1
CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL
@@ -57,8 +59,9 @@ class FplFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
result = await api.login() result = await api.login()
if result == LOGIN_RESULT_OK: if result == LOGIN_RESULT_OK:
fplData = await api.async_get_data()
accounts = fplData["accounts"] accounts = await api.async_get_open_accounts()
await api.logout()
user_input["accounts"] = accounts user_input["accounts"] = accounts
@@ -70,8 +73,8 @@ class FplFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
if result == LOGIN_RESULT_INVALIDPASSWORD: if result == LOGIN_RESULT_INVALIDPASSWORD:
self._errors[CONF_PASSWORD] = "invalid_password" self._errors[CONF_PASSWORD] = "invalid_password"
if result == None: if result == LOGIN_RESULT_FAILURE:
self._errors["base"] = "auth" self._errors["base"] = "failure"
else: else:
self._errors[CONF_NAME] = "name_exists" self._errors[CONF_NAME] = "name_exists"

View File

@@ -3,7 +3,7 @@
NAME = "FPL Integration" NAME = "FPL Integration"
DOMAIN = "fpl" DOMAIN = "fpl"
DOMAIN_DATA = f"{DOMAIN}_data" DOMAIN_DATA = f"{DOMAIN}_data"
VERSION = "0.1.0" VERSION = "0.0.1"
PLATFORMS = ["sensor"] PLATFORMS = ["sensor"]
REQUIRED_FILES = [ REQUIRED_FILES = [
".translations/en.json", ".translations/en.json",
@@ -12,10 +12,9 @@ REQUIRED_FILES = [
"config_flow.py", "config_flow.py",
"manifest.json", "manifest.json",
"sensor.py", "sensor.py",
"switch.py",
] ]
ISSUE_URL = "https://github.com/dotKrad/hass-fpl/issues" ISSUE_URL = "https://github.com/dotKrad/hass-fpl/issues"
ATTRIBUTION = "This data is provided by FPL." ATTRIBUTION = "Data provided by FPL."
# Platforms # Platforms
BINARY_SENSOR = "binary_sensor" BINARY_SENSOR = "binary_sensor"

View File

@@ -1,8 +1,10 @@
"""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

View File

@@ -1,4 +1,7 @@
"""BlueprintEntity class""" """Fpl Entity class"""
from datetime import datetime, timedelta
from homeassistant.components.sensor import SensorEntity, STATE_CLASS_MEASUREMENT from homeassistant.components.sensor import SensorEntity, STATE_CLASS_MEASUREMENT
from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.helpers.update_coordinator import CoordinatorEntity
@@ -7,14 +10,13 @@ from homeassistant.const import (
DEVICE_CLASS_ENERGY, DEVICE_CLASS_ENERGY,
ENERGY_KILO_WATT_HOUR, ENERGY_KILO_WATT_HOUR,
DEVICE_CLASS_MONETARY, DEVICE_CLASS_MONETARY,
DEVICE_CLASS_TIMESTAMP,
) )
from datetime import datetime, timedelta
from .const import DOMAIN, VERSION, ATTRIBUTION from .const import DOMAIN, VERSION, ATTRIBUTION
class FplEntity(CoordinatorEntity, SensorEntity): class FplEntity(CoordinatorEntity, SensorEntity):
"""FPL base entity"""
def __init__(self, coordinator, config_entry, account, sensorName): def __init__(self, coordinator, config_entry, account, sensorName):
super().__init__(coordinator) super().__init__(coordinator)
self.config_entry = config_entry self.config_entry = config_entry
@@ -24,22 +26,25 @@ class FplEntity(CoordinatorEntity, SensorEntity):
@property @property
def unique_id(self): def unique_id(self):
"""Return the ID of this device.""" """Return the ID of this device."""
id = "{}{}{}".format( return "{}{}{}".format(
DOMAIN, self.account, self.sensorName.lower().replace(" ", "") DOMAIN, self.account, self.sensorName.lower().replace(" ", "")
) )
return id
@property
def name(self):
return f"{DOMAIN.upper()} {self.account} {self.sensorName}"
@property @property
def device_info(self): def device_info(self):
return { return {
"identifiers": {(DOMAIN, self.account)}, "identifiers": {(DOMAIN, self.account)},
"name": f"FPL Account {self.account}", "name": f"FPL Account {self.account}",
"model": "FPL Monitoring API", "model": VERSION,
"sw_version": VERSION,
"manufacturer": "Florida Power & Light", "manufacturer": "Florida Power & Light",
} }
def defineAttributes(self): def customAttributes(self) -> dict:
"""override this method to set custom attributes"""
return {} return {}
@property @property
@@ -49,16 +54,16 @@ class FplEntity(CoordinatorEntity, SensorEntity):
"attribution": ATTRIBUTION, "attribution": ATTRIBUTION,
"integration": "FPL", "integration": "FPL",
} }
attributes.update(self.defineAttributes()) attributes.update(self.customAttributes())
return attributes return attributes
def getData(self, field): def getData(self, field):
return self.coordinator.data.get(self.account).get(field) """call this method to retrieve sensor data"""
return self.coordinator.data.get(self.account).get(field, None)
class FplEnergyEntity(FplEntity): class FplEnergyEntity(FplEntity):
def __init__(self, coordinator, config_entry, account, sensorName): """Represents a energy sensor"""
super().__init__(coordinator, config_entry, account, sensorName)
@property @property
def state_class(self) -> str: def state_class(self) -> str:
@@ -90,8 +95,7 @@ class FplEnergyEntity(FplEntity):
class FplMoneyEntity(FplEntity): class FplMoneyEntity(FplEntity):
def __init__(self, coordinator, config_entry, account, sensorName): """Represents a money sensor"""
super().__init__(coordinator, config_entry, account, sensorName)
@property @property
def icon(self): def icon(self):
@@ -109,14 +113,31 @@ class FplMoneyEntity(FplEntity):
class FplDateEntity(FplEntity): class FplDateEntity(FplEntity):
def __init__(self, coordinator, config_entry, account, sensorName): """Represents a date or days"""
super().__init__(coordinator, config_entry, account, sensorName)
# @property # @property
# def device_class(self) -> str: # def device_class(self) -> str:
# """Return the class of this device, from component DEVICE_CLASSES.""" # """Return the class of this device, from component DEVICE_CLASSES."""
# return DEVICE_CLASS_TIMESTAMP # return DEVICE_CLASS_DATE
@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,13 +1,11 @@
"""Custom FPl api client"""
import logging import logging
from datetime import datetime from datetime import datetime, timedelta
import sys
import json
import aiohttp import aiohttp
import async_timeout import async_timeout
import json
import sys
# from bs4 import BeautifulSoup
STATUS_CATEGORY_OPEN = "OPEN" STATUS_CATEGORY_OPEN = "OPEN"
# Api login result # Api login result
@@ -18,18 +16,40 @@ 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"
URL_LOGIN = API_HOST + "/api/resources/login"
URL_LOGOUT = API_HOST + "/api/resources/logout"
URL_RESOURCES_HEADER = API_HOST + "/api/resources/header"
URL_RESOURCES_ACCOUNT = API_HOST + "/api/resources/account/{account}"
URL_BUDGET_BILLING_GRAPH = (
API_HOST + "/api/resources/account/{account}/budgetBillingGraph"
)
URL_RESOURCES_PROJECTED_BILL = (
API_HOST
+ "/api/resources/account/{account}/projectedBill"
+ "?premiseNumber={premise}&lastBilledDate={lastBillDate}"
)
URL_ENERGY_SERVICE = (
API_HOST + "/dashboard-api/resources/account/{account}/energyService/{account}"
)
URL_APPLIANCE_USAGE = (
API_HOST + "/dashboard-api/resources/account/{account}/applianceUsage/{account}"
)
URL_BUDGET_BILLING_PREMISE_DETAILS = (
API_HOST + "/api/resources/account/{account}/budgetBillingGraph/premiseDetails"
)
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" ENROLLED = "ENROLLED"
NOTENROLLED = "NOTENROLLED" NOTENROLLED = "NOTENROLLED"
class FplApi(object): class FplApi:
"""A class for getting energy usage information from Florida Power & Light.""" """A class for getting energy usage information from Florida Power & Light."""
def __init__(self, username, password, session): def __init__(self, username, password, session):
@@ -39,7 +59,7 @@ class FplApi(object):
self._session = session self._session = session
async def async_get_data(self) -> dict: async def async_get_data(self) -> dict:
# self._session = aiohttp.ClientSession() """Get data from fpl api"""
data = {} data = {}
data["accounts"] = [] data["accounts"] = []
if await self.login() == LOGIN_RESULT_OK: if await self.login() == LOGIN_RESULT_OK:
@@ -47,130 +67,151 @@ class FplApi(object):
data["accounts"] = accounts data["accounts"] = accounts
for account in accounts: for account in accounts:
accountData = await self.__async_get_data(account) account_data = await self.__async_get_data(account)
data[account] = accountData data[account] = account_data
await self.logout() await self.logout()
return data return data
async def login(self): async def login(self):
"""login into fpl"""
_LOGGER.info("Logging in") _LOGGER.info("Logging in")
# login and get account information # login and get account information
result = LOGIN_RESULT_OK
try: try:
async with async_timeout.timeout(TIMEOUT): async with async_timeout.timeout(TIMEOUT):
response = await self._session.get( response = await self._session.get(
URL_LOGIN, auth=aiohttp.BasicAuth(self._username, self._password) URL_LOGIN, auth=aiohttp.BasicAuth(self._username, self._password)
) )
js = json.loads(await response.text()) if response.status == 200:
_LOGGER.info("Logging Successful")
return LOGIN_RESULT_OK
if response.reason == "Unauthorized": if response.status == 401:
result = LOGIN_RESULT_UNAUTHORIZED _LOGGER.error("Logging Unauthorized")
json_data = json.loads(await response.text())
if js["messages"][0]["messageCode"] != "login.success": if json_data["messageCode"] == LOGIN_RESULT_INVALIDUSER:
_LOGGER.error(f"Logging Failure") return LOGIN_RESULT_INVALIDUSER
result = LOGIN_RESULT_FAILURE
_LOGGER.info(f"Logging Successful") if json_data["messageCode"] == LOGIN_RESULT_INVALIDPASSWORD:
return LOGIN_RESULT_INVALIDPASSWORD
except Exception as e: except Exception as exception:
_LOGGER.error(f"Error {e} : {sys.exc_info()[0]}") _LOGGER.error("Error %s : %s", exception, sys.exc_info()[0])
result = LOGIN_RESULT_FAILURE return LOGIN_RESULT_FAILURE
return result return LOGIN_RESULT_FAILURE
async def logout(self): async def logout(self):
"""Logging out from fpl"""
_LOGGER.info("Logging out") _LOGGER.info("Logging out")
URL = "https://www.fpl.com/api/resources/logout" try:
async with async_timeout.timeout(TIMEOUT): async with async_timeout.timeout(TIMEOUT):
await self._session.get(URL) await self._session.get(URL_LOGOUT)
except Exception:
pass
async def async_get_open_accounts(self): async def async_get_open_accounts(self):
_LOGGER.info(f"Getting accounts") """Getting open accounts"""
_LOGGER.info("Getting open accounts")
result = [] result = []
try: try:
async with async_timeout.timeout(TIMEOUT): async with async_timeout.timeout(TIMEOUT):
response = await self._session.get(URL_RESOURCES_HEADER) response = await self._session.get(URL_RESOURCES_HEADER)
js = await response.json() json_data = await response.json()
accounts = js["data"]["accounts"]["data"]["data"] accounts = json_data["data"]["accounts"]["data"]["data"]
for account in accounts: for account in accounts:
if account["statusCategory"] == STATUS_CATEGORY_OPEN: if account["statusCategory"] == STATUS_CATEGORY_OPEN:
result.append(account["accountNumber"]) result.append(account["accountNumber"])
except Exception as e:
_LOGGER.error(f"Getting accounts {e}")
# self._account_number = js["data"]["selectedAccount"]["data"]["accountNumber"] except Exception:
# self._premise_number = js["data"]["selectedAccount"]["data"]["acctSecSettings"]["premiseNumber"] _LOGGER.error("Getting accounts %s", sys.exc_info())
return result return result
async def __async_get_data(self, account) -> dict: async def __async_get_data(self, account) -> dict:
_LOGGER.info(f"Getting Data") """Get data from resources endpoint"""
_LOGGER.info("Getting Data")
data = {} data = {}
async with async_timeout.timeout(TIMEOUT): async with async_timeout.timeout(TIMEOUT):
response = await self._session.get( response = await self._session.get(
URL_RESOURCES_ACCOUNT.format(account=account) URL_RESOURCES_ACCOUNT.format(account=account)
) )
accountData = (await response.json())["data"] account_data = (await response.json())["data"]
premise = accountData["premiseNumber"].zfill(9) premise = account_data.get("premiseNumber").zfill(9)
data["meterSerialNo"] = account_data["meterSerialNo"]
# currentBillDate # currentBillDate
currentBillDate = datetime.strptime( currentBillDate = datetime.strptime(
accountData["currentBillDate"].replace("-", "").split("T")[0], "%Y%m%d" account_data["currentBillDate"].replace("-", "").split("T")[0], "%Y%m%d"
).date() ).date()
# nextBillDate # nextBillDate
nextBillDate = datetime.strptime( nextBillDate = datetime.strptime(
accountData["nextBillDate"].replace("-", "").split("T")[0], "%Y%m%d" account_data["nextBillDate"].replace("-", "").split("T")[0], "%Y%m%d"
).date() ).date()
data["current_bill_date"] = str(currentBillDate) data["current_bill_date"] = str(currentBillDate)
data["next_bill_date"] = str(nextBillDate) data["next_bill_date"] = str(nextBillDate)
today = datetime.now().date() today = datetime.now().date()
remaining = (nextBillDate - today).days
days = (today - currentBillDate).days
data["service_days"] = (nextBillDate - currentBillDate).days data["service_days"] = (nextBillDate - currentBillDate).days
data["as_of_days"] = days data["as_of_days"] = (today - currentBillDate).days
data["remaining_days"] = remaining data["remaining_days"] = (nextBillDate - today).days
# zip code # zip code
zip_code = accountData["serviceAddress"]["zip"] # zip_code = accountData["serviceAddress"]["zip"]
# projected bill # projected bill
pbData = await self.__getFromProjectedBill(account, premise, currentBillDate) pbData = await self.__getFromProjectedBill(account, premise, currentBillDate)
data.update(pbData) data.update(pbData)
# programs # programs
programsData = accountData["programs"]["data"] programsData = account_data["programs"]["data"]
programs = dict() programs = dict()
_LOGGER.info(f"Getting Programs") _LOGGER.info("Getting Programs")
for program in programsData: for program in programsData:
if "enrollmentStatus" in program.keys(): if "enrollmentStatus" in program.keys():
key = program["name"] key = program["name"]
programs[key] = program["enrollmentStatus"] == ENROLLED programs[key] = program["enrollmentStatus"] == ENROLLED
if programs["BBL"]: def hasProgram(programName) -> bool:
# budget billing return programName in programs and programs[programName]
data["budget_bill"] = True
bblData = await self.__getBBL_async(account, data)
data.update(bblData)
# Budget Billing program
if hasProgram("BBL"):
data["budget_bill"] = True
bbl_data = await self.__getBBL_async(account, data)
data.update(bbl_data)
else:
data["budget_bill"] = False
# Get data from energy service
data.update( data.update(
await self.__getDataFromEnergyService(account, premise, currentBillDate) await self.__getDataFromEnergyService(account, premise, currentBillDate)
) )
# Get data from energy service ( hourly )
# data.update(
# await self.__getDataFromEnergyServiceHourly(
# account, premise, currentBillDate
# )
# )
data.update(await self.__getDataFromApplianceUsage(account, currentBillDate)) data.update(await self.__getDataFromApplianceUsage(account, currentBillDate))
return data return data
async def __getFromProjectedBill(self, account, premise, currentBillDate) -> dict: async def __getFromProjectedBill(self, account, premise, currentBillDate) -> dict:
"""get data from projected bill endpoint"""
data = {} data = {}
try: try:
@@ -196,24 +237,26 @@ class FplApi(object):
data["projected_bill"] = projectedBill data["projected_bill"] = projectedBill
data["daily_avg"] = dailyAvg data["daily_avg"] = dailyAvg
data["avg_high_temp"] = avgHighTemp data["avg_high_temp"] = avgHighTemp
except:
except Exception:
pass pass
return data return data
async def __getBBL_async(self, account, projectedBillData) -> dict: async def __getBBL_async(self, account, projectedBillData) -> dict:
_LOGGER.info(f"Getting budget billing data") """Get budget billing data"""
_LOGGER.info("Getting budget billing data")
data = {} data = {}
URL = "https://www.fpl.com/api/resources/account/{account}/budgetBillingGraph/premiseDetails"
try: try:
async with async_timeout.timeout(TIMEOUT): async with async_timeout.timeout(TIMEOUT):
response = await self._session.get(URL.format(account=account)) response = await self._session.get(
URL_BUDGET_BILLING_PREMISE_DETAILS.format(account=account)
)
if response.status == 200: if response.status == 200:
r = (await response.json())["data"] r = (await response.json())["data"]
dataList = r["graphData"] dataList = r["graphData"]
startIndex = len(dataList) - 1 # startIndex = len(dataList) - 1
billingCharge = 0 billingCharge = 0
budgetBillDeferBalance = r["defAmt"] budgetBillDeferBalance = r["defAmt"]
@@ -235,28 +278,29 @@ class FplApi(object):
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:
pass
URL = "https://www.fpl.com/api/resources/account/{account}/budgetBillingGraph" except Exception as e:
_LOGGER.error("Error getting BBL: %s", e)
try: try:
async with async_timeout.timeout(TIMEOUT): async with async_timeout.timeout(TIMEOUT):
response = await self._session.get(URL.format(account=account)) response = await self._session.get(
URL_BUDGET_BILLING_GRAPH.format(account=account)
)
if response.status == 200: if response.status == 200:
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:
pass except Exception as e:
_LOGGER.error("Error getting BBL: %s", e)
return data return data
async def __getDataFromEnergyService( async def __getDataFromEnergyService(
self, account, premise, lastBilledDate self, account, premise, lastBilledDate
) -> dict: ) -> dict:
_LOGGER.info(f"Getting data from energy service") _LOGGER.info("Getting data from energy service")
URL = "https://www.fpl.com/dashboard-api/resources/account/{account}/energyService/{account}"
date = str(lastBilledDate.strftime("%m%d%Y")) date = str(lastBilledDate.strftime("%m%d%Y"))
JSON = { JSON = {
@@ -278,7 +322,9 @@ class FplApi(object):
data = {} data = {}
async with async_timeout.timeout(TIMEOUT): async with async_timeout.timeout(TIMEOUT):
response = await self._session.post(URL.format(account=account), json=JSON) response = await self._session.post(
URL_ENERGY_SERVICE.format(account=account), json=JSON
)
if response.status == 200: if response.status == 200:
r = (await response.json())["data"] r = (await response.json())["data"]
dailyUsage = [] dailyUsage = []
@@ -294,15 +340,21 @@ class FplApi(object):
): ):
dailyUsage.append( dailyUsage.append(
{ {
"usage": daily.get("kwhUsed"), "usage": daily["kwhUsed"],
"cost": daily.get("billingCharge"), "cost": daily["billingCharge"],
"date": daily.get("date"), # "date": daily["date"],
"max_temperature": daily.get( "max_temperature": daily["averageHighTemperature"],
"averageHighTemperature" "netDeliveredKwh": daily["netDeliveredKwh"]
if "netDeliveredKwh" in daily.keys()
else 0,
"netReceivedKwh": daily["netReceivedKwh"]
if "netReceivedKwh" in daily.keys()
else 0,
"readTime": datetime.fromisoformat(
daily[
"readTime"
] # 2022-02-25T00:00:00.000-05:00
), ),
"netDeliveredKwh": daily.get("netDeliveredKwh"),
"netReceivedKwh": daily.get("netReceivedKwh"),
"readTime": daily.get("readTime"),
} }
) )
# totalPowerUsage += int(daily["kwhUsed"]) # totalPowerUsage += int(daily["kwhUsed"])
@@ -310,23 +362,103 @@ class FplApi(object):
# data["total_power_usage"] = totalPowerUsage # data["total_power_usage"] = totalPowerUsage
data["daily_usage"] = dailyUsage data["daily_usage"] = dailyUsage
data["projectedKWH"] = r["CurrentUsage"].get("projectedKWH") data["projectedKWH"] = r["CurrentUsage"]["projectedKWH"]
data["dailyAverageKWH"] = r["CurrentUsage"].get("dailyAverageKWH") data["dailyAverageKWH"] = r["CurrentUsage"]["dailyAverageKWH"]
data["billToDateKWH"] = r["CurrentUsage"].get("billToDateKWH") data["billToDateKWH"] = r["CurrentUsage"]["billToDateKWH"]
data["recMtrReading"] = r["CurrentUsage"].get("recMtrReading") data["recMtrReading"] = r["CurrentUsage"]["recMtrReading"]
data["delMtrReading"] = r["CurrentUsage"].get("delMtrReading") data["delMtrReading"] = r["CurrentUsage"]["delMtrReading"]
data["billStartDate"] = r["CurrentUsage"].get("billStartDate") data["billStartDate"] = r["CurrentUsage"]["billStartDate"]
return data
async def __getDataFromEnergyServiceHourly(
self, account, premise, lastBilledDate
) -> dict:
_LOGGER.info("Getting data from energy service Hourly")
# date = str(lastBilledDate.strftime("%m%d%Y"))
date = str((datetime.now() - timedelta(days=1)).strftime("%m%d%Y"))
JSON = {
"status": 2,
"channel": "WEB",
"amrFlag": "Y",
"accountType": "RESIDENTIAL",
"revCode": "1",
"premiseNumber": premise,
"projectedBillFlag": False,
"billComparisionFlag": False,
"monthlyFlag": False,
"frequencyType": "Hourly",
"applicationPage": "resDashBoard",
"startDate": date,
}
data = {}
# now = homeassistant.util.dt.utcnow()
# now = datetime.now().astimezone()
# hour = now.hour
async with async_timeout.timeout(TIMEOUT):
response = await self._session.post(
URL_ENERGY_SERVICE.format(account=account), json=JSON
)
if response.status == 200:
r = (await response.json())["data"]
dailyUsage = []
# totalPowerUsage = 0
if "data" in r["HourlyUsage"]:
for daily in r["HourlyUsage"]["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"]
if "netDeliveredKwh" in daily.keys()
else 0,
"netReceivedKwh": daily["netReceivedKwh"]
if "netReceivedKwh" in daily.keys()
else 0,
"readTime": datetime.fromisoformat(
daily[
"readTime"
] # 2022-02-25T00:00:00.000-05:00
),
}
)
# totalPowerUsage += int(daily["kwhUsed"])
# data["total_power_usage"] = totalPowerUsage
data["daily_usage"] = dailyUsage
data["projectedKWH"] = r["HourlyUsage"]["projectedKWH"]
data["dailyAverageKWH"] = r["HourlyUsage"]["dailyAverageKWH"]
data["billToDateKWH"] = r["HourlyUsage"]["billToDateKWH"]
data["recMtrReading"] = r["HourlyUsage"]["recMtrReading"]
data["delMtrReading"] = r["HourlyUsage"]["delMtrReading"]
data["billStartDate"] = r["HourlyUsage"]["billStartDate"]
return data return data
async def __getDataFromApplianceUsage(self, account, lastBilledDate) -> dict: async def __getDataFromApplianceUsage(self, account, lastBilledDate) -> dict:
_LOGGER.info(f"Getting data from applicance usage") """get data from appliance usage"""
URL = "https://www.fpl.com/dashboard-api/resources/account/{account}/applianceUsage/{account}" _LOGGER.info("Getting data from appliance usage")
JSON = {"startDate": str(lastBilledDate.strftime("%m%d%Y"))} JSON = {"startDate": str(lastBilledDate.strftime("%m%d%Y"))}
data = {} data = {}
try: try:
async with async_timeout.timeout(TIMEOUT): async with async_timeout.timeout(TIMEOUT):
response = await self._session.post( response = await self._session.post(
URL.format(account=account), json=JSON URL_APPLIANCE_USAGE.format(account=account), json=JSON
) )
if response.status == 200: if response.status == 200:
electric = (await response.json())["data"]["electric"] electric = (await response.json())["data"]["electric"]
@@ -340,7 +472,7 @@ class FplApi(object):
rr = full rr = full
data[e["category"].replace(" ", "_")] = rr data[e["category"].replace(" ", "_")] = rr
except: except Exception:
pass pass
return {"energy_percent_by_applicance": data} return {"energy_percent_by_applicance": data}

View File

@@ -12,6 +12,5 @@
"integrationhelper" "integrationhelper"
], ],
"version": "1.0.0", "version": "1.0.0",
"issue_tracker": "https://github.com/dotKrad/hass-fpl/issues", "issue_tracker": "https://github.com/dotKrad/hass-fpl/issues"
"homeassistant": "2021.12.7"
} }

View File

@@ -21,8 +21,9 @@ from .sensor_ProjectedBillSensor import (
DeferedAmountSensor, DeferedAmountSensor,
) )
from .sensor_AverageDailySensor import ( from .sensor_AverageDailySensor import (
FplAverageDailySensor, DailyAverageSensor,
BudgetDailyAverageSensor, BudgetDailyAverageSensor,
ActualDailyAverageSensor,
) )
from .sensor_DailyUsageSensor import ( from .sensor_DailyUsageSensor import (
FplDailyUsageKWHSensor, FplDailyUsageKWHSensor,
@@ -30,10 +31,9 @@ from .sensor_DailyUsageSensor import (
FplDailyDeliveredKWHSensor, FplDailyDeliveredKWHSensor,
FplDailyReceivedKWHSensor, 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):
@@ -54,12 +54,12 @@ 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(FplDailyUsageSensor(coordinator, entry, account)) fpl_accounts.append(FplDailyUsageSensor(coordinator, entry, account))
fpl_accounts.append(FplDailyUsageKWHSensor(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 # date sensors
fpl_accounts.append(CurrentBillDateSensor(coordinator, entry, account)) fpl_accounts.append(CurrentBillDateSensor(coordinator, entry, account))
@@ -72,11 +72,11 @@ async def async_setup_entry(hass, entry, async_add_devices):
fpl_accounts.append(ProjectedKWHSensor(coordinator, entry, account)) fpl_accounts.append(ProjectedKWHSensor(coordinator, entry, account))
fpl_accounts.append(DailyAverageKWHSensor(coordinator, entry, account)) fpl_accounts.append(DailyAverageKWHSensor(coordinator, entry, account))
fpl_accounts.append(BillToDateKWHSensor(coordinator, entry, account)) fpl_accounts.append(BillToDateKWHSensor(coordinator, entry, account))
fpl_accounts.append(NetReceivedKWHSensor(coordinator, entry, account))
fpl_accounts.append(NetDeliveredKWHSensor(coordinator, entry, account))
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,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")
try:
self._state=self.getData("projected_bill")
except:
pass
return self._state
@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"] = "monetary"
attributes["state_class"] = "total"
attributes["unit_of_measurement"] = "$"
return attributes

View File

@@ -1,7 +1,11 @@
"""Average daily sensors"""
from homeassistant.components.sensor import STATE_CLASS_TOTAL
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,64 +14,47 @@ 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")
try: return self.getData("daily_avg")
self._state=self.getData("daily_avg")
except:
pass
return self._state
<<<<<<< HEAD def customAttributes(self):
=======
@property
def icon(self):
return "mdi:currency-usd"
def defineAttributes(self):
"""Return the state attributes.""" """Return the state attributes."""
attributes = {} attributes = {}
attributes["friendly_name"] = "Daily Average" attributes["state_class"] = STATE_CLASS_TOTAL
attributes["device_class"] = "monetary"
attributes["state_class"] = "total"
attributes["unit_of_measurement"] = "$"
return attributes return attributes
>>>>>>> master
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")
@property @property
def state(self): def state(self):
try: return self.getData("budget_billing_daily_avg")
self._state= self.getData("budget_billing_daily_avg")
except: def customAttributes(self):
pass """Return the state attributes."""
return self._state attributes = {}
attributes["state_class"] = STATE_CLASS_TOTAL
return attributes
<<<<<<< HEAD
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")
=======
@property
def icon(self):
return "mdi:currency-usd"
def defineAttributes(self): def customAttributes(self):
"""Return the state attributes.""" """Return the state attributes."""
attributes = {} attributes = {}
attributes["friendly_name"] = "Budget Daily Average" attributes["state_class"] = STATE_CLASS_TOTAL
attributes["device_class"] = "monitary"
attributes["state_class"] = "total"
attributes["unit_of_measurement"] = "$"
return attributes return attributes
>>>>>>> master

View File

@@ -1,7 +1,12 @@
"""Daily Usage Sensors"""
from homeassistant.components.sensor import STATE_CLASS_TOTAL_INCREASING
from datetime import timedelta
from .fplEntity import FplEnergyEntity, FplMoneyEntity from .fplEntity import FplEnergyEntity, FplMoneyEntity
class FplDailyUsageSensor(FplMoneyEntity): class FplDailyUsageSensor(FplMoneyEntity):
"""Daily Usage Cost Sensor"""
def __init__(self, coordinator, config, account): def __init__(self, coordinator, config, account):
super().__init__(coordinator, config, account, "Daily Usage") super().__init__(coordinator, config, account, "Daily Usage")
@@ -9,37 +14,25 @@ class FplDailyUsageSensor(FplMoneyEntity):
def state(self): def state(self):
data = self.getData("daily_usage") data = self.getData("daily_usage")
try: if data is not None and len(data) > 0 and "cost" in data[-1].keys():
self._state = data[-1]["cost"] return data[-1]["cost"]
except:
pass
return self._state
def defineAttributes(self): return None
def customAttributes(self):
"""Return the state attributes.""" """Return the state attributes."""
data = self.getData("daily_usage") data = self.getData("daily_usage")
attributes = {} attributes = {}
attributes["friendly_name"] = "Daily Usage" # attributes["state_class"] = STATE_CLASS_TOTAL_INCREASING
attributes["device_class"] = "monetary" if data is not None and len(data) > 0 and "readTime" in data[-1].keys():
attributes["state_class"] = "total_increasing" attributes["date"] = data[-1]["readTime"]
attributes["unit_of_measurement"] = "$"
if data is not None:
if (
(len(data) > 0)
and (data[-1] is not None)
and (data[-1]["readTime"] is not None)
):
attributes["date"] = data[-1]["readTime"]
if (
(len(data) > 1)
and (data[-2] is not None)
and (data[-2]["readTime"] is not None)
):
attributes["last_reset"] = data[-2]["readTime"]
return attributes return attributes
class FplDailyUsageKWHSensor(FplEnergyEntity): class FplDailyUsageKWHSensor(FplEnergyEntity):
"""Daily Usage Kwh Sensor"""
def __init__(self, coordinator, config, account): def __init__(self, coordinator, config, account):
super().__init__(coordinator, config, account, "Daily Usage KWH") super().__init__(coordinator, config, account, "Daily Usage KWH")
@@ -47,126 +40,71 @@ class FplDailyUsageKWHSensor(FplEnergyEntity):
def state(self): def state(self):
data = self.getData("daily_usage") data = self.getData("daily_usage")
try: if data is not None and len(data) > 0 and "usage" in data[-1].keys():
self._state = data[-1]["usage"] return data[-1]["usage"]
except:
pass
return self._state
def defineAttributes(self): return None
def customAttributes(self):
"""Return the state attributes.""" """Return the state attributes."""
data = self.getData("daily_usage") data = self.getData("daily_usage")
date = data[-1]["readTime"]
last_reset = date - timedelta(days=1)
attributes = {} attributes = {}
attributes["friendly_name"] = "Daily Usage" # attributes["state_class"] = STATE_CLASS_TOTAL_INCREASING
attributes["device_class"] = "energy" attributes["date"] = date
attributes["state_class"] = "total_increasing" # attributes["last_reset"] = last_reset
attributes["unit_of_measurement"] = "kWh"
if data is not None:
if (
(len(data) > 0)
and (data[-1] is not None)
and (data[-1]["readTime"] is not None)
):
attributes["date"] = data[-1]["readTime"]
if (
(len(data) > 1)
and (data[-2] is not None)
and (data[-2]["readTime"] is not None)
):
attributes["last_reset"] = data[-2]["readTime"]
return attributes return attributes
@property
def icon(self):
return "mdi:flash"
class FplDailyReceivedKWHSensor(FplEnergyEntity):
"""daily received Kwh sensor"""
class FplDailyReceivedKWHSensor(FplEntity):
def __init__(self, coordinator, config, account): def __init__(self, coordinator, config, account):
super().__init__(coordinator, config, account, "Daily Received KWH") super().__init__(coordinator, config, account, "Daily Received KWH")
@property @property
def state(self): def state(self):
data = self.getData("daily_usage") data = self.getData("daily_usage")
try: if data is not None and len(data) > 0 and "netReceivedKwh" in data[-1].keys():
self._state = data[-1]["netReceivedKwh"] return data[-1]["netReceivedKwh"]
except: return 0
pass
return self._state
def defineAttributes(self): def customAttributes(self):
"""Return the state attributes.""" """Return the state attributes."""
data = self.getData("daily_usage") data = self.getData("daily_usage")
date = data[-1]["readTime"]
last_reset = date - timedelta(days=1)
attributes = {} attributes = {}
attributes["friendly_name"] = "Daily Return to Grid" attributes["state_class"] = STATE_CLASS_TOTAL_INCREASING
attributes["device_class"] = "energy" attributes["date"] = date
attributes["state_class"] = "total_increasing" attributes["last_reset"] = last_reset
attributes["unit_of_measurement"] = "kWh"
if data is not None:
if (
(len(data) > 0)
and (data[-1] is not None)
and (data[-1]["readTime"] is not None)
):
attributes["date"] = data[-1]["readTime"]
if (
(len(data) > 1)
and (data[-2] is not None)
and (data[-2]["readTime"] is not None)
):
attributes["last_reset"] = data[-2]["readTime"]
return attributes return attributes
@property
def icon(self):
return "mdi:flash"
class FplDailyDeliveredKWHSensor(FplEnergyEntity):
"""daily delivered Kwh sensor"""
class FplDailyDeliveredKWHSensor(FplEntity):
def __init__(self, coordinator, config, account): def __init__(self, coordinator, config, account):
super().__init__(coordinator, config, account, "Daily Delivered KWH") super().__init__(coordinator, config, account, "Daily Delivered KWH")
@property @property
def state(self): def state(self):
data = self.getData("daily_usage") data = self.getData("daily_usage")
try: if data is not None and len(data) > 0 and "netDeliveredKwh" in data[-1].keys():
self._state = data[-1]["netDeliveredKwh"] return data[-1]["netDeliveredKwh"]
except: return 0
pass
return self._state
def defineAttributes(self): def customAttributes(self):
"""Return the state attributes.""" """Return the state attributes."""
data = self.getData("daily_usage") data = self.getData("daily_usage")
date = data[-1]["readTime"]
last_reset = date - timedelta(days=1)
<<<<<<< HEAD
return {}
=======
attributes = {} attributes = {}
attributes["friendly_name"] = "Daily Consumption" attributes["state_class"] = STATE_CLASS_TOTAL_INCREASING
attributes["device_class"] = "energy" attributes["date"] = date
attributes["state_class"] = "total_increasing" attributes["last_reset"] = last_reset
attributes["unit_of_measurement"] = "kWh"
if data is not None:
if (
(len(data) > 0)
and (data[-1] is not None)
and (data[-1]["readTime"] is not None)
):
attributes["date"] = data[-1]["readTime"]
if (
(len(data) > 1)
and (data[-2] is not None)
and (data[-2]["readTime"] is not None)
):
attributes["last_reset"] = data[-2]["readTime"]
return attributes return attributes
@property
def icon(self):
return "mdi:flash"
>>>>>>> master

View File

@@ -1,136 +1,48 @@
<<<<<<< HEAD """dates sensors"""
from .fplEntity import FplDateEntity
=======
from .fplEntity import FplEntity
import datetime import datetime
>>>>>>> master from .fplEntity import FplDateEntity, FplDayEntity
class CurrentBillDateSensor(FplDateEntity): class CurrentBillDateSensor(FplDateEntity):
def __init__(self, coordinator, config, account): def __init__(self, coordinator, config, account):
super().__init__(coordinator, config, account, "Billing Current Date") super().__init__(coordinator, config, account, "Current Bill Date")
@property @property
def state(self): def state(self):
try: return datetime.date.fromisoformat(self.getData("current_bill_date"))
self._state= datetime.date.fromisoformat(self.getData("current_bill_date"))
except:
pass
return self._state
<<<<<<< HEAD
=======
@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
>>>>>>> master
class NextBillDateSensor(FplDateEntity): class NextBillDateSensor(FplDateEntity):
def __init__(self, coordinator, config, account): def __init__(self, coordinator, config, account):
super().__init__(coordinator, config, account, "Billing Next") super().__init__(coordinator, config, account, "Next Bill Date")
@property @property
def state(self): def state(self):
try: return datetime.date.fromisoformat(self.getData("next_bill_date"))
self._state= datetime.date.fromisoformat(self.getData("next_bill_date"))
except:
pass
return self._state
<<<<<<< HEAD class ServiceDaysSensor(FplDayEntity):
class ServiceDaysSensor(FplDateEntity):
=======
def defineAttributes(self):
"""Return the state attributes."""
attributes = {}
attributes["device_class"] = "date"
attributes["friendly_name"] = "Billing Next"
return attributes
class ServiceDaysSensor(FplEntity):
>>>>>>> master
def __init__(self, coordinator, config, account): def __init__(self, coordinator, config, account):
super().__init__(coordinator, config, account, "Billing Total Days") super().__init__(coordinator, config, account, "Service Days")
@property @property
def state(self): def state(self):
try: return self.getData("service_days")
self._state= self.getData("service_days")
except:
pass
return self._state
<<<<<<< HEAD
=======
@property
def icon(self):
return "mdi:calendar"
def defineAttributes(self): class AsOfDaysSensor(FplDayEntity):
"""Return the state attributes."""
attributes = {}
attributes["unit_of_measurement"] = "days"
attributes["friendly_name"] = "Billing Total"
return attributes
>>>>>>> master
class AsOfDaysSensor(FplDateEntity):
def __init__(self, coordinator, config, account): def __init__(self, coordinator, config, account):
super().__init__(coordinator, config, account, "Billing As Of") super().__init__(coordinator, config, account, "As Of Days")
@property @property
def state(self): def state(self):
try: return self.getData("as_of_days")
self._state= self.getData("as_of_days")
except:
pass
return self._state
<<<<<<< HEAD
=======
@property
def icon(self):
return "mdi:calendar"
def defineAttributes(self): class RemainingDaysSensor(FplDayEntity):
"""Return the state attributes."""
attributes = {}
attributes["unit_of_measurement"] = "days"
attributes["friendly_name"] = "Billing As Of"
return attributes
>>>>>>> master
class RemainingDaysSensor(FplDateEntity):
def __init__(self, coordinator, config, account): def __init__(self, coordinator, config, account):
super().__init__(coordinator, config, account, "Billing Remaining") super().__init__(coordinator, config, account, "Remaining Days")
@property @property
def state(self): def state(self):
<<<<<<< HEAD
return self.getData("remaining_days") return self.getData("remaining_days")
=======
try:
self._state= self.getData("remaining_days")
except:
pass
return self._state
@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
>>>>>>> master

View File

@@ -1,157 +1,89 @@
from homeassistant.components.sensor import STATE_CLASS_TOTAL_INCREASING """energy sensors"""
from datetime import date, timedelta
from homeassistant.components.sensor import (
STATE_CLASS_TOTAL_INCREASING,
STATE_CLASS_TOTAL,
)
from .fplEntity import FplEnergyEntity from .fplEntity import FplEnergyEntity
class ProjectedKWHSensor(FplEnergyEntity): class ProjectedKWHSensor(FplEnergyEntity):
def __init__(self, coordinator, config, account): def __init__(self, coordinator, config, account):
super().__init__(coordinator, config, account, "Projected") super().__init__(coordinator, config, account, "Projected KWH")
@property @property
def state(self): def state(self):
try: return self.getData("projectedKWH")
self._state = self.getData("projectedKWH")
except:
pass
return self._state
def customAttributes(self):
<<<<<<< HEAD
class DailyAverageKWHSensor(FplEnergyEntity):
=======
def defineAttributes(self):
"""Return the state attributes.""" """Return the state attributes."""
attributes = {} attributes = {}
attributes["friendly_name"] = "Projected KWH" attributes["state_class"] = STATE_CLASS_TOTAL
attributes["device_class"] = "energy"
attributes["state_class"] = "total"
attributes["unit_of_measurement"] = "kWh"
return attributes return attributes
class DailyAverageKWHSensor(FplEntity): class DailyAverageKWHSensor(FplEnergyEntity):
>>>>>>> master
def __init__(self, coordinator, config, account): def __init__(self, coordinator, config, account):
super().__init__(coordinator, config, account, "Daily Average KWH") super().__init__(coordinator, config, account, "Daily Average KWH")
@property @property
def state(self): def state(self):
try: return self.getData("dailyAverageKWH")
self._state = self.getData("dailyAverageKWH")
except:
pass
return self._state
<<<<<<< HEAD def customAttributes(self):
=======
@property
def icon(self):
return "mdi:flash"
def defineAttributes(self):
"""Return the state attributes.""" """Return the state attributes."""
attributes = {} attributes = {}
attributes["friendly_name"] = "Daily Average" attributes["state_class"] = STATE_CLASS_TOTAL
attributes["device_class"] = "energy"
attributes["state_class"] = "total"
attributes["unit_of_measurement"] = "kWh"
return attributes return attributes
>>>>>>> master
class BillToDateKWHSensor(FplEnergyEntity): class BillToDateKWHSensor(FplEnergyEntity):
def __init__(self, coordinator, config, account): def __init__(self, coordinator, config, account):
super().__init__(coordinator, config, account, "Bill To Date") super().__init__(coordinator, config, account, "Bill To Date KWH")
@property @property
def state(self): def state(self):
try: return self.getData("billToDateKWH")
self._state = self.getData("billToDateKWH")
except:
pass
return self._state
@property def customAttributes(self):
def state_class(self) -> str:
"""Return the state class of this entity, from STATE_CLASSES, if any."""
<<<<<<< HEAD
class NetReceivedKWHSensor(FplEnergyEntity):
=======
def defineAttributes(self):
"""Return the state attributes.""" """Return the state attributes."""
# data = self.getData("daily_usage")
# date = data[-1]["readTime"]
asOfDays = self.getData("as_of_days")
last_reset = date.today() - timedelta(days=asOfDays)
attributes = {} attributes = {}
attributes["friendly_name"] = "Billing Usage" attributes["state_class"] = STATE_CLASS_TOTAL_INCREASING
attributes["device_class"] = "energy" attributes["last_reset"] = last_reset
attributes["state_class"] = "total_increasing"
attributes["unit_of_measurement"] = "kWh"
if self.getData("billStartDate") is not None:
attributes["last_reset"] = self.getData("billStartDate")
return attributes return attributes
class NetReceivedKWHSensor(FplEntity): class NetReceivedKWHSensor(FplEnergyEntity):
>>>>>>> master
def __init__(self, coordinator, config, account): def __init__(self, coordinator, config, account):
super().__init__(coordinator, config, account, "Received Reading") super().__init__(coordinator, config, account, "Received Meter Reading KWH")
@property @property
def state(self): def state(self):
try: return self.getData("recMtrReading")
self._state = self.getData("recMtrReading")
except:
pass
return self._state
@property def customAttributes(self):
def icon(self): """Return the state attributes."""
return "mdi:flash" attributes = {}
attributes["state_class"] = STATE_CLASS_TOTAL_INCREASING
return attributes
<<<<<<< HEAD
class NetDeliveredKWHSensor(FplEnergyEntity): class NetDeliveredKWHSensor(FplEnergyEntity):
=======
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"
if self.getData("billStartDate") is not None:
attributes["last_reset"] = self.getData("billStartDate")
return attributes
class NetDeliveredKWHSensor(FplEntity):
>>>>>>> master
def __init__(self, coordinator, config, account): def __init__(self, coordinator, config, account):
super().__init__(coordinator, config, account, "Delivered Reading") super().__init__(coordinator, config, account, "Delivered Meter Reading KWH")
@property @property
def state(self): def state(self):
try: return self.getData("delMtrReading")
self._state = self.getData("delMtrReading")
except:
try:
self._state = self.getData("billToDateKWH")
except:
pass
return self._state
@property def customAttributes(self):
def icon(self):
return "mdi:flash"
def defineAttributes(self):
"""Return the state attributes.""" """Return the state attributes."""
attributes = {} attributes = {}
attributes["friendly_name"] = "Meter Consumption" attributes["state_class"] = STATE_CLASS_TOTAL_INCREASING
attributes["device_class"] = "energy"
attributes["state_class"] = "total_increasing"
attributes["unit_of_measurement"] = "kWh"
if self.getData("billStartDate") is not None:
attributes["last_reset"] = self.getData("billStartDate")
return attributes return attributes

View File

@@ -1,120 +1,96 @@
"""Projected bill sensors"""
from homeassistant.components.sensor import (
# STATE_CLASS_TOTAL_INCREASING,
STATE_CLASS_TOTAL,
)
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")
@property
def native_value(self):
budget = self.getData("budget_bill")
budget_billing_projected_bill = self.getData("budget_billing_projected_bill")
if budget and budget_billing_projected_bill is not None:
return self.getData("budget_billing_projected_bill")
return self.getData("projected_bill")
"""
@property @property
def state(self): def state(self):
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")
try: if budget and budget_billing_projected_bill is not None:
if budget == True and budget_billing_projected_bill is not None: return self.getData("budget_billing_projected_bill")
self._state = self.getData("budget_billing_projected_bill")
else:
self._state = self.getData("projected_bill")
except:
self._state = None
return self._state
def defineAttributes(self): return self.getData("projected_bill")
"""
def customAttributes(self):
"""Return the state attributes.""" """Return the state attributes."""
attributes = {} attributes = {}
attributes["friendly_name"] = "Projected Bill Payment Due" attributes["state_class"] = STATE_CLASS_TOTAL
attributes["device_class"] = "monetary" attributes["budget_bill"] = self.getData("budget_bill")
attributes["state_class"] = "total"
attributes["unit_of_measurement"] = "$"
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, "Deferred Amount") super().__init__(coordinator, config, account, "Defered Amount")
@property @property
def state(self): def state(self):
try: if self.getData("budget_bill"):
self._state = self.getData("deferred_amount") return self.getData("defered_amount")
except: return 0
self._state = 0
pass
return self._state
<<<<<<< HEAD def customAttributes(self):
=======
@property
def icon(self):
return "mdi:currency-usd"
def defineAttributes(self):
"""Return the state attributes.""" """Return the state attributes."""
attributes = {} attributes = {}
attributes["friendly_name"] = "Deferred Amount" attributes["state_class"] = STATE_CLASS_TOTAL
attributes["device_class"] = "monetary"
attributes["state_class"] = "total"
attributes["unit_of_measurement"] = "$"
return attributes return attributes
>>>>>>> master
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")
@property @property
def state(self): def state(self):
try: return self.getData("budget_billing_projected_bill")
self._state = self.getData("budget_billing_projected_bill")
except:
pass
return self._state
<<<<<<< HEAD def customAttributes(self):
=======
@property
def icon(self):
return "mdi:currency-usd"
def defineAttributes(self):
"""Return the state attributes.""" """Return the state attributes."""
attributes = {} attributes = {}
attributes["friendly_name"] = "Projected Budget Bill" attributes["state_class"] = STATE_CLASS_TOTAL
attributes["device_class"] = "monitary"
attributes["state_class"] = "total"
attributes["unit_of_measurement"] = "$"
return attributes return attributes
>>>>>>> master
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):
<<<<<<< HEAD
return self.getData("projected_bill") return self.getData("projected_bill")
=======
try:
self._state = self.getData("projected_bill")
except:
pass
return self._state
@property def customAttributes(self):
def icon(self):
return "mdi:currency-usd"
def defineAttributes(self):
"""Return the state attributes.""" """Return the state attributes."""
attributes = {} attributes = {}
attributes["friendly_name"] = "Projected Actual Bill" attributes["state_class"] = STATE_CLASS_TOTAL
attributes["device_class"] = "monitary"
attributes["state_class"] = "total"
attributes["unit_of_measurement"] = "$"
return attributes return attributes
>>>>>>> master

View File

@@ -13,6 +13,7 @@
} }
}, },
"error": { "error": {
"failure": "An error ocurred, please check the logs",
"auth": "Username/Password is wrong.", "auth": "Username/Password is wrong.",
"name_exists": "Configuration already exists.", "name_exists": "Configuration already exists.",
"invalid_username": "Invalid Email", "invalid_username": "Invalid Email",