Created new sensor with specific data and group them by devices(account)
This commit is contained in:
15
custom_components/fpl/AverageDailySensor.py
Normal file
15
custom_components/fpl/AverageDailySensor.py
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
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):
|
||||||
|
return self.data["daily_avg"]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device_state_attributes(self):
|
||||||
|
"""Return the state attributes."""
|
||||||
|
return self.attr
|
||||||
16
custom_components/fpl/DailyUsageSensor.py
Normal file
16
custom_components/fpl/DailyUsageSensor.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
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):
|
||||||
|
return self.data["daily_usage"][-1]["cost"]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device_state_attributes(self):
|
||||||
|
"""Return the state attributes."""
|
||||||
|
self.attr["date"] = self.data["daily_usage"][-1]["date"]
|
||||||
|
return self.attr
|
||||||
68
custom_components/fpl/FplSensor.py
Normal file
68
custom_components/fpl/FplSensor.py
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
from homeassistant.helpers.entity import Entity
|
||||||
|
from homeassistant import util
|
||||||
|
from .const import DOMAIN, DOMAIN_DATA, ATTRIBUTION
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
|
MIN_TIME_BETWEEN_SCANS = timedelta(minutes=30)
|
||||||
|
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=60)
|
||||||
|
|
||||||
|
|
||||||
|
class FplSensor(Entity):
|
||||||
|
def __init__(self, hass, config, account, sensorName):
|
||||||
|
self._config = config
|
||||||
|
self._state = None
|
||||||
|
self.loop = hass.loop
|
||||||
|
|
||||||
|
self._account = account
|
||||||
|
self.attr = {}
|
||||||
|
self.data = None
|
||||||
|
self.sensorName = sensorName
|
||||||
|
|
||||||
|
@util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_UPDATES)
|
||||||
|
async def async_update(self):
|
||||||
|
"""Update the sensor."""
|
||||||
|
# Send update "signal" to the component
|
||||||
|
await self.hass.data[DOMAIN_DATA]["client"].update_data()
|
||||||
|
|
||||||
|
# Get new data (if any)
|
||||||
|
if "data" in self.hass.data[DOMAIN_DATA]:
|
||||||
|
self.data = self.hass.data[DOMAIN_DATA]["data"][self._account]
|
||||||
|
|
||||||
|
# Set/update attributes
|
||||||
|
self.attr["attribution"] = ATTRIBUTION
|
||||||
|
|
||||||
|
async def async_added_to_hass(self):
|
||||||
|
await self.async_update()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device_info(self):
|
||||||
|
return {
|
||||||
|
"identifiers": {(DOMAIN, self._account)},
|
||||||
|
"name": f"Account {self._account}",
|
||||||
|
"manufacturer": "Florida Power & Light",
|
||||||
|
}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unique_id(self):
|
||||||
|
"""Return the ID of this device."""
|
||||||
|
id = "{}{}{}".format(
|
||||||
|
DOMAIN, self._account, self.sensorName.lower().replace(" ", "")
|
||||||
|
)
|
||||||
|
return id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return f"{DOMAIN.upper()} {self._account} {self.sensorName}"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state(self):
|
||||||
|
return self._state
|
||||||
|
|
||||||
|
@property
|
||||||
|
def icon(self):
|
||||||
|
return "mdi:flash"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device_state_attributes(self):
|
||||||
|
"""Return the state attributes."""
|
||||||
|
return self.attr
|
||||||
31
custom_components/fpl/ProjectedBillSensor.py
Normal file
31
custom_components/fpl/ProjectedBillSensor.py
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
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
|
||||||
|
state = None
|
||||||
|
if "budget_bill" in data.keys():
|
||||||
|
if data["budget_bill"]:
|
||||||
|
if "budget_billing_projected_bill" in data.keys():
|
||||||
|
state = data["budget_billing_projected_bill"]
|
||||||
|
else:
|
||||||
|
if "projected_bill" in data.keys():
|
||||||
|
state = data["projected_bill"]
|
||||||
|
|
||||||
|
return 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.attr
|
||||||
|
|
||||||
|
@property
|
||||||
|
def icon(self):
|
||||||
|
return "mdi:currency-usd"
|
||||||
@@ -1,18 +1,62 @@
|
|||||||
""" FPL Component """
|
""" FPL Component """
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from datetime import timedelta
|
||||||
from homeassistant.core import Config, HomeAssistant
|
from homeassistant.core import Config, HomeAssistant
|
||||||
|
from homeassistant.util import Throttle
|
||||||
|
from .fplapi import FplApi
|
||||||
|
from .const import DOMAIN_DATA, CONF_USERNAME, CONF_PASSWORD
|
||||||
|
|
||||||
|
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=30)
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
from .config_flow import FplFlowHandler
|
from .config_flow import FplFlowHandler
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
|
|
||||||
|
|
||||||
|
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:
|
async def async_setup(hass: HomeAssistant, config: Config) -> bool:
|
||||||
"""Set up configured Fpl."""
|
"""Set up configured Fpl."""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass, config_entry):
|
async def async_setup_entry(hass, config_entry):
|
||||||
|
|
||||||
|
# Get "global" configuration.
|
||||||
|
username = config_entry.data.get(CONF_USERNAME)
|
||||||
|
password = config_entry.data.get(CONF_PASSWORD)
|
||||||
|
|
||||||
|
# Create DATA dict
|
||||||
|
hass.data[DOMAIN_DATA] = {}
|
||||||
|
|
||||||
|
# Configure the client.
|
||||||
|
_LOGGER.info(f"Configuring the client")
|
||||||
|
client = FplApi(username, password, hass.loop)
|
||||||
|
fplData = FplData(hass, client)
|
||||||
|
|
||||||
|
await fplData.update_data()
|
||||||
|
|
||||||
|
hass.data[DOMAIN_DATA]["client"] = fplData
|
||||||
|
|
||||||
"""Set up Fpl as config entry."""
|
"""Set up Fpl as config entry."""
|
||||||
hass.async_create_task(
|
hass.async_create_task(
|
||||||
hass.config_entries.async_forward_entry_setup(config_entry, "sensor")
|
hass.config_entries.async_forward_entry_setup(config_entry, "sensor")
|
||||||
|
|||||||
@@ -2,17 +2,17 @@ from collections import OrderedDict
|
|||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
from .fplapi import FplApi
|
from .fplapi import FplApi
|
||||||
|
|
||||||
from homeassistant import config_entries
|
from homeassistant import config_entries
|
||||||
import aiohttp
|
import aiohttp
|
||||||
from .const import (
|
from .const import DOMAIN, CONF_USERNAME, CONF_PASSWORD, CONF_NAME
|
||||||
DOMAIN,
|
|
||||||
CONF_USERNAME,
|
from .fplapi import (
|
||||||
CONF_PASSWORD,
|
|
||||||
CONF_NAME,
|
|
||||||
LOGIN_RESULT_OK,
|
LOGIN_RESULT_OK,
|
||||||
LOGIN_RESULT_INVALIDUSER,
|
LOGIN_RESULT_INVALIDUSER,
|
||||||
LOGIN_RESULT_INVALIDPASSWORD,
|
LOGIN_RESULT_INVALIDPASSWORD,
|
||||||
)
|
)
|
||||||
|
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
|
|
||||||
|
|
||||||
@@ -34,7 +34,9 @@ class FplFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
"""Initialize."""
|
"""Initialize."""
|
||||||
self._errors = {}
|
self._errors = {}
|
||||||
|
|
||||||
async def async_step_user(self, user_input={}): # pylint: disable=dangerous-default-value
|
async def async_step_user(
|
||||||
|
self, user_input={}
|
||||||
|
): # pylint: disable=dangerous-default-value
|
||||||
"""Handle a flow initialized by the user."""
|
"""Handle a flow initialized by the user."""
|
||||||
self._errors = {}
|
self._errors = {}
|
||||||
|
|
||||||
@@ -48,12 +50,16 @@ class FplFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
password = user_input[CONF_PASSWORD]
|
password = user_input[CONF_PASSWORD]
|
||||||
|
|
||||||
if username not in configured_instances(self.hass):
|
if username not in configured_instances(self.hass):
|
||||||
result = await self._test_credentials(username, password)
|
api = FplApi(username, password, None)
|
||||||
|
result = await api.login()
|
||||||
|
|
||||||
if result == LOGIN_RESULT_OK:
|
if result == LOGIN_RESULT_OK:
|
||||||
return self.async_create_entry(
|
fplData = await api.get_data()
|
||||||
title=user_input[CONF_NAME], data=user_input
|
accounts = fplData["accounts"]
|
||||||
)
|
|
||||||
|
user_input["accounts"] = accounts
|
||||||
|
|
||||||
|
return self.async_create_entry(title="", data=user_input)
|
||||||
|
|
||||||
if result == LOGIN_RESULT_INVALIDUSER:
|
if result == LOGIN_RESULT_INVALIDUSER:
|
||||||
self._errors[CONF_USERNAME] = "invalid_username"
|
self._errors[CONF_USERNAME] = "invalid_username"
|
||||||
@@ -77,18 +83,14 @@ class FplFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
# Defaults
|
# Defaults
|
||||||
username = ""
|
username = ""
|
||||||
password = ""
|
password = ""
|
||||||
name = "Home"
|
|
||||||
|
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
if CONF_USERNAME in user_input:
|
if CONF_USERNAME in user_input:
|
||||||
username = user_input[CONF_USERNAME]
|
username = user_input[CONF_USERNAME]
|
||||||
if CONF_PASSWORD in user_input:
|
if CONF_PASSWORD in user_input:
|
||||||
password = user_input[CONF_PASSWORD]
|
password = user_input[CONF_PASSWORD]
|
||||||
if CONF_NAME in user_input:
|
|
||||||
name = user_input[CONF_NAME]
|
|
||||||
|
|
||||||
data_schema = OrderedDict()
|
data_schema = OrderedDict()
|
||||||
data_schema[vol.Required(CONF_NAME, default=name)] = str
|
|
||||||
data_schema[vol.Required(CONF_USERNAME, default=username)] = str
|
data_schema[vol.Required(CONF_USERNAME, default=username)] = str
|
||||||
data_schema[vol.Required(CONF_PASSWORD, default=password)] = str
|
data_schema[vol.Required(CONF_PASSWORD, default=password)] = str
|
||||||
|
|
||||||
@@ -96,14 +98,12 @@ class FplFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
step_id="user", data_schema=vol.Schema(data_schema), errors=self._errors
|
step_id="user", data_schema=vol.Schema(data_schema), errors=self._errors
|
||||||
)
|
)
|
||||||
|
|
||||||
async def _test_credentials(self, username, password):
|
async def async_step_import(self, user_input): # pylint: disable=unused-argument
|
||||||
"""Return true if credentials is valid."""
|
"""Import a config entry.
|
||||||
session = aiohttp.ClientSession()
|
Special type of import, we're not actually going to store any data.
|
||||||
try:
|
Instead, we're going to rely on the values that are in config file.
|
||||||
api = FplApi(username, password, None, session)
|
"""
|
||||||
result = await api.login()
|
if self._async_current_entries():
|
||||||
except Exception: # pylint: disable=broad-except
|
return self.async_abort(reason="single_instance_allowed")
|
||||||
pass
|
|
||||||
|
|
||||||
await session.close()
|
return self.async_create_entry(title="configuration.yaml", data={})
|
||||||
return result
|
|
||||||
|
|||||||
@@ -16,9 +16,6 @@ REQUIRED_FILES = [
|
|||||||
ISSUE_URL = "https://github.com/dotKrad/hass-fpl/issues"
|
ISSUE_URL = "https://github.com/dotKrad/hass-fpl/issues"
|
||||||
ATTRIBUTION = "Data from this is provided by FPL."
|
ATTRIBUTION = "Data from this is provided by FPL."
|
||||||
|
|
||||||
# Icons
|
|
||||||
ICON = "mdi:flash"
|
|
||||||
|
|
||||||
# Device classes
|
# Device classes
|
||||||
BINARY_SENSOR_DEVICE_CLASS = "connectivity"
|
BINARY_SENSOR_DEVICE_CLASS = "connectivity"
|
||||||
|
|
||||||
@@ -32,12 +29,4 @@ CONF_USERNAME = "username"
|
|||||||
CONF_PASSWORD = "password"
|
CONF_PASSWORD = "password"
|
||||||
|
|
||||||
# Defaults
|
# Defaults
|
||||||
DEFAULT_NAME = DOMAIN
|
DEFAULT_NAME = DOMAIN
|
||||||
|
|
||||||
# Api login result
|
|
||||||
LOGIN_RESULT_OK = "OK"
|
|
||||||
LOGIN_RESULT_INVALIDUSER = "NOTVALIDUSER"
|
|
||||||
LOGIN_RESULT_INVALIDPASSWORD = "FAILEDPASSWORD"
|
|
||||||
|
|
||||||
|
|
||||||
STATUS_CATEGORY_OPEN = "OPEN"
|
|
||||||
@@ -9,7 +9,12 @@ import json
|
|||||||
|
|
||||||
|
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
from .const import STATUS_CATEGORY_OPEN, LOGIN_RESULT_OK
|
|
||||||
|
STATUS_CATEGORY_OPEN = "OPEN"
|
||||||
|
# Api login result
|
||||||
|
LOGIN_RESULT_OK = "OK"
|
||||||
|
LOGIN_RESULT_INVALIDUSER = "NOTVALIDUSER"
|
||||||
|
LOGIN_RESULT_INVALIDPASSWORD = "FAILEDPASSWORD"
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
TIMEOUT = 5
|
TIMEOUT = 5
|
||||||
@@ -26,31 +31,59 @@ NOTENROLLED = "NOTENROLLED"
|
|||||||
class FplApi(object):
|
class FplApi(object):
|
||||||
"""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, loop, session):
|
def __init__(self, username, password, loop):
|
||||||
"""Initialize the data retrieval. Session should have BasicAuth flag set."""
|
"""Initialize the data retrieval. Session should have BasicAuth flag set."""
|
||||||
self._username = username
|
self._username = username
|
||||||
self._password = password
|
self._password = password
|
||||||
self._loop = loop
|
self._loop = loop
|
||||||
self._session = session
|
self._session = None
|
||||||
|
|
||||||
|
async def get_data(self):
|
||||||
|
self._session = aiohttp.ClientSession()
|
||||||
|
data = {}
|
||||||
|
await self.login()
|
||||||
|
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._session.close()
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
async def login(self):
|
async def login(self):
|
||||||
|
if self._session is not None:
|
||||||
|
session = self._session
|
||||||
|
close = False
|
||||||
|
else:
|
||||||
|
session = aiohttp.ClientSession()
|
||||||
|
close = True
|
||||||
|
|
||||||
_LOGGER.info("Logging")
|
_LOGGER.info("Logging")
|
||||||
"""login and get account information"""
|
"""login and get account information"""
|
||||||
async with async_timeout.timeout(TIMEOUT, loop=self._loop):
|
async with async_timeout.timeout(TIMEOUT, loop=self._loop):
|
||||||
response = await self._session.get(
|
response = await 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())
|
js = json.loads(await response.text())
|
||||||
|
|
||||||
if response.reason == "Unauthorized":
|
if response.reason == "Unauthorized":
|
||||||
|
await session.close()
|
||||||
raise Exception(js["messageCode"])
|
raise Exception(js["messageCode"])
|
||||||
|
|
||||||
if js["messages"][0]["messageCode"] != "login.success":
|
if js["messages"][0]["messageCode"] != "login.success":
|
||||||
_LOGGER.error(f"Logging Failure")
|
_LOGGER.error(f"Logging Failure")
|
||||||
|
await session.close()
|
||||||
raise Exception("login failure")
|
raise Exception("login failure")
|
||||||
|
|
||||||
_LOGGER.info(f"Logging Successful")
|
_LOGGER.info(f"Logging Successful")
|
||||||
|
|
||||||
|
if close:
|
||||||
|
await session.close()
|
||||||
|
|
||||||
return LOGIN_RESULT_OK
|
return LOGIN_RESULT_OK
|
||||||
|
|
||||||
async def async_get_open_accounts(self):
|
async def async_get_open_accounts(self):
|
||||||
@@ -71,7 +104,7 @@ class FplApi(object):
|
|||||||
# self._premise_number = js["data"]["selectedAccount"]["data"]["acctSecSettings"]["premiseNumber"]
|
# self._premise_number = js["data"]["selectedAccount"]["data"]["acctSecSettings"]["premiseNumber"]
|
||||||
return result
|
return result
|
||||||
|
|
||||||
async def async_get_data(self, account):
|
async def __async_get_data(self, account):
|
||||||
_LOGGER.info(f"Getting Data")
|
_LOGGER.info(f"Getting Data")
|
||||||
data = {}
|
data = {}
|
||||||
|
|
||||||
@@ -108,7 +141,7 @@ class FplApi(object):
|
|||||||
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
|
||||||
@@ -124,17 +157,17 @@ class FplApi(object):
|
|||||||
if programs["BBL"]:
|
if programs["BBL"]:
|
||||||
# budget billing
|
# budget billing
|
||||||
data["budget_bill"] = True
|
data["budget_bill"] = True
|
||||||
bblData = await self.getBBL_async(account, data)
|
bblData = await self.__getBBL_async(account, data)
|
||||||
data.update(bblData)
|
data.update(bblData)
|
||||||
|
|
||||||
data.update(
|
data.update(
|
||||||
await self.getDataFromEnergyService(account, premise, currentBillDate)
|
await self.__getDataFromEnergyService(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):
|
async def __getFromProjectedBill(self, account, premise, currentBillDate):
|
||||||
async with async_timeout.timeout(TIMEOUT, loop=self._loop):
|
async with async_timeout.timeout(TIMEOUT, loop=self._loop):
|
||||||
response = await self._session.get(
|
response = await self._session.get(
|
||||||
URL_RESOURCES_PROJECTED_BILL.format(
|
URL_RESOURCES_PROJECTED_BILL.format(
|
||||||
@@ -161,7 +194,7 @@ class FplApi(object):
|
|||||||
|
|
||||||
return []
|
return []
|
||||||
|
|
||||||
async def getBBL_async(self, account, projectedBillData):
|
async def __getBBL_async(self, account, projectedBillData):
|
||||||
_LOGGER.info(f"Getting budget billing data")
|
_LOGGER.info(f"Getting budget billing data")
|
||||||
data = {}
|
data = {}
|
||||||
|
|
||||||
@@ -179,7 +212,7 @@ class FplApi(object):
|
|||||||
|
|
||||||
projectedBill = projectedBillData["projected_bill"]
|
projectedBill = projectedBillData["projected_bill"]
|
||||||
asOfDays = projectedBillData["as_of_days"]
|
asOfDays = projectedBillData["as_of_days"]
|
||||||
|
|
||||||
for det in dataList:
|
for det in dataList:
|
||||||
billingCharge += det["actuallBillAmt"]
|
billingCharge += det["actuallBillAmt"]
|
||||||
|
|
||||||
@@ -206,7 +239,7 @@ class FplApi(object):
|
|||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
async def getDataFromEnergyService(self, account, premise, lastBilledDate):
|
async def __getDataFromEnergyService(self, account, premise, lastBilledDate):
|
||||||
_LOGGER.info(f"Getting data from energy service")
|
_LOGGER.info(f"Getting data from energy service")
|
||||||
URL = "https://www.fpl.com/dashboard-api/resources/account/{account}/energyService/{account}"
|
URL = "https://www.fpl.com/dashboard-api/resources/account/{account}/energyService/{account}"
|
||||||
|
|
||||||
@@ -252,7 +285,7 @@ class FplApi(object):
|
|||||||
|
|
||||||
return []
|
return []
|
||||||
|
|
||||||
async def getDataFromApplianceUsage(self, account, lastBilledDate):
|
async def __getDataFromApplianceUsage(self, account, lastBilledDate):
|
||||||
_LOGGER.info(f"Getting data from applicance usage")
|
_LOGGER.info(f"Getting data from applicance usage")
|
||||||
URL = "https://www.fpl.com/dashboard-api/resources/account/{account}/applianceUsage/{account}"
|
URL = "https://www.fpl.com/dashboard-api/resources/account/{account}/applianceUsage/{account}"
|
||||||
JSON = {"startDate": str(lastBilledDate.strftime("%m%d%Y"))}
|
JSON = {"startDate": str(lastBilledDate.strftime("%m%d%Y"))}
|
||||||
|
|||||||
@@ -15,8 +15,12 @@ from homeassistant.const import (
|
|||||||
CONF_USERNAME,
|
CONF_USERNAME,
|
||||||
CONF_PASSWORD,
|
CONF_PASSWORD,
|
||||||
STATE_UNKNOWN,
|
STATE_UNKNOWN,
|
||||||
|
ATTR_FRIENDLY_NAME,
|
||||||
)
|
)
|
||||||
from .const import DOMAIN, ICON, LOGIN_RESULT_OK
|
from .const import DOMAIN, DOMAIN_DATA, ATTRIBUTION
|
||||||
|
from .DailyUsageSensor import FplDailyUsageSensor
|
||||||
|
from .AverageDailySensor import FplAverageDailySensor
|
||||||
|
from .ProjectedBillSensor import FplProjectedBillSensor
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -29,38 +33,25 @@ def setup(hass, config):
|
|||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
username = config_entry.data.get(CONF_USERNAME)
|
|
||||||
password = config_entry.data.get(CONF_PASSWORD)
|
|
||||||
|
|
||||||
session = aiohttp.ClientSession()
|
accounts = config_entry.data.get("accounts")
|
||||||
try:
|
|
||||||
api = FplApi(username, password, hass.loop, session)
|
|
||||||
result = await api.login()
|
|
||||||
|
|
||||||
fpl_accounts = []
|
fpl_accounts = []
|
||||||
|
|
||||||
if result == LOGIN_RESULT_OK:
|
for account in accounts:
|
||||||
accounts = await api.async_get_open_accounts()
|
_LOGGER.info(f"Adding fpl account: {account}")
|
||||||
for account in accounts:
|
fpl_accounts.append(FplSensor(hass, config_entry.data, account))
|
||||||
_LOGGER.info(f"Adding fpl account: {account}")
|
fpl_accounts.append(FplDailyUsageSensor(hass, config_entry.data, account))
|
||||||
fpl_accounts.append(FplSensor(hass, config_entry.data, account))
|
fpl_accounts.append(FplAverageDailySensor(hass, config_entry.data, account))
|
||||||
|
fpl_accounts.append(FplProjectedBillSensor(hass, config_entry.data, account))
|
||||||
|
|
||||||
async_add_entities(fpl_accounts)
|
async_add_entities(fpl_accounts)
|
||||||
except Exception as e: # pylint: disable=broad-except
|
|
||||||
_LOGGER.error(f"Adding fpl accounts: {str(e)}")
|
|
||||||
async_call_later(
|
|
||||||
hass, 15, async_setup_entry(hass, config_entry, async_add_entities)
|
|
||||||
)
|
|
||||||
|
|
||||||
await session.close()
|
|
||||||
|
|
||||||
|
|
||||||
class FplSensor(Entity):
|
class FplSensor(Entity):
|
||||||
def __init__(self, hass, config, account):
|
def __init__(self, hass, config, account):
|
||||||
self._config = config
|
self._config = config
|
||||||
self.username = config.get(CONF_USERNAME)
|
self._state = None
|
||||||
self.password = config.get(CONF_PASSWORD)
|
|
||||||
self._state = STATE_UNKNOWN
|
|
||||||
self.loop = hass.loop
|
self.loop = hass.loop
|
||||||
|
|
||||||
self._account = account
|
self._account = account
|
||||||
@@ -69,6 +60,14 @@ class FplSensor(Entity):
|
|||||||
async def async_added_to_hass(self):
|
async def async_added_to_hass(self):
|
||||||
await self.async_update()
|
await self.async_update()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device_info(self):
|
||||||
|
return {
|
||||||
|
"identifiers": {(DOMAIN, self._account)},
|
||||||
|
"name": f"Account {self._account}",
|
||||||
|
"manufacturer": "Florida Power & Light",
|
||||||
|
}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def unique_id(self):
|
def unique_id(self):
|
||||||
"""Return the ID of this device."""
|
"""Return the ID of this device."""
|
||||||
@@ -101,7 +100,7 @@ class FplSensor(Entity):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def icon(self):
|
def icon(self):
|
||||||
return ICON
|
return "mdi:flash"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def state_attributes(self):
|
def state_attributes(self):
|
||||||
@@ -109,16 +108,13 @@ class FplSensor(Entity):
|
|||||||
|
|
||||||
@util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_UPDATES)
|
@util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_UPDATES)
|
||||||
async def async_update(self):
|
async def async_update(self):
|
||||||
try:
|
# Send update "signal" to the component
|
||||||
session = aiohttp.ClientSession()
|
await self.hass.data[DOMAIN_DATA]["client"].update_data()
|
||||||
api = FplApi(self.username, self.password, self.loop, session)
|
|
||||||
await api.login()
|
# Get new data (if any)
|
||||||
data = await api.async_get_data(self._account)
|
if "data" in self.hass.data[DOMAIN_DATA]:
|
||||||
|
data = self.hass.data[DOMAIN_DATA]["data"][self._account]
|
||||||
|
|
||||||
if data != {}:
|
if data != {}:
|
||||||
self._data = data
|
self._data = data
|
||||||
|
self._data["attribution"] = ATTRIBUTION
|
||||||
except Exception as e: # pylint: disable=broad-except
|
|
||||||
_LOGGER.warning(f"Error ocurred during update: { str(e)}")
|
|
||||||
|
|
||||||
finally:
|
|
||||||
await session.close()
|
|
||||||
Reference in New Issue
Block a user