Hi all,
I’m having difficulty adding a description of the custom component along with selection box descriptions. Can anybody please assist?
My code below presents the following:
Ideally, I’d like the “wizard” / GUI to look something like this:
.
manifest.json
{
"domain": "energy_bill_estimator",
"name": "Energy Bill Estimator",
"version": "1.0.0",
"documentation": "https://github.com/pppaulie/energy_bill_estimator/",
"requirements": [],
"codeowners": ["@pppaulie"],
"config_flow": true,
"iot_class": "local_polling",
"issue_tracker": "https://github.com/pppaulie/energy_bill_estimator/issues"
}
init.py
"""Initialize the Energy Bill Estimator integration."""
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from .const import DOMAIN
async def async_setup(hass: HomeAssistant, config: dict):
"""Set up the Energy Bill Estimator integration."""
hass.data.setdefault(DOMAIN, {})
return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
"""Set up Energy Bill Estimator from a config entry."""
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(entry, "sensor")
)
return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
"""Unload Energy Bill Estimator config entry."""
await hass.config_entries.async_forward_entry_unload(entry, "sensor")
return True
const.py
"""Constants for the Energy Bill Estimator integration."""
DOMAIN = "energy_bill_estimator"
CONF_ENERGY_COST_SENSOR = "energy_cost_sensor"
CONF_SUPPLY_COST_ENTITY = "supply_cost_entity"
config_flow.py
"""Config flow for Energy Bill Estimator integration."""
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.helpers import selector
from .const import DOMAIN, CONF_ENERGY_COST_SENSOR, CONF_SUPPLY_COST_ENTITY
class EnergyBillEstimatorConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Energy Bill Estimator."""
VERSION = 1
async def async_step_user(self, user_input=None):
"""Handle the initial step."""
if user_input is not None:
return self.async_create_entry(title="Energy Bill Estimator", data=user_input)
data_schema = vol.Schema({
vol.Required(CONF_ENERGY_COST_SENSOR): selector.EntitySelector(
selector.EntitySelectorConfig(domain="sensor")
),
vol.Required(CONF_SUPPLY_COST_ENTITY): selector.EntitySelector(
selector.EntitySelectorConfig(domain=["input_number", "sensor"])
),
})
return self.async_show_form(
step_id="user",
data_schema=data_schema,
)
strings.json
{
"title": "Energy Bill Estimator",
"config": {
"step": {
"user": {
"title": "Configure Energy Bill Estimator",
"description": "Select the entities for energy cost and supply cost.",
"data": {
"energy_cost_sensor": "House Monthly Energy Cost Sensor",
"supply_cost_entity": "Daily Supply Cost Entity"
}
}
}
}
}
sensor.py
"""Sensor platform for Energy Bill Estimator integration."""
from datetime import timedelta
from homeassistant.components.sensor import SensorEntity
from homeassistant.const import CURRENCY_DOLLAR
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.util.dt import now as ha_now
from .const import DOMAIN, CONF_ENERGY_COST_SENSOR, CONF_SUPPLY_COST_ENTITY
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities):
"""Set up the Energy Bill Estimator sensor platform."""
async_add_entities([EnergyBillEstimatorSensor(hass, config_entry)], True)
class EnergyBillEstimatorSensor(SensorEntity):
"""Representation of the Energy Bill Estimator sensor."""
def __init__(self, hass, config_entry):
"""Initialize the sensor."""
self.hass = hass
self.config_entry = config_entry
self._attr_name = "Energy Bill Estimate"
self._attr_unique_id = f"{DOMAIN}_sensor"
self._state = None
self._attr_unit_of_measurement = CURRENCY_DOLLAR
self._attr_extra_state_attributes = {}
async def async_update(self):
"""Fetch new state data for the sensor."""
energy_cost_entity = self.config_entry.data[CONF_ENERGY_COST_SENSOR]
supply_cost_entity = self.config_entry.data[CONF_SUPPLY_COST_ENTITY]
energy_cost_state = self.hass.states.get(energy_cost_entity)
supply_cost_state = self.hass.states.get(supply_cost_entity)
# Error handling for unavailable entities
if energy_cost_state is None or supply_cost_state is None:
self._state = None
return
if energy_cost_state.state in [None, "unknown", "unavailable"] or \
supply_cost_state.state in [None, "unknown", "unavailable"]:
self._state = None
return
try:
energy_cost = float(energy_cost_state.state)
supply_cost = float(supply_cost_state.state)
except (ValueError, TypeError):
self._state = None
return
now = ha_now() # Use Home Assistant's time zone
# Corrected billing cycle calculations
if now.day >= 27:
# Billing cycle starts this month on the 27th
billing_cycle_start = now.replace(day=27)
# Next month's 27th
next_month = (now.month % 12) + 1
next_year = now.year + 1 if now.month == 12 else now.year
try:
billing_cycle_end = billing_cycle_start.replace(year=next_year, month=next_month)
except ValueError:
# Handle months where the next month doesn't have 27 days
billing_cycle_end = (billing_cycle_start + timedelta(days=31)).replace(day=27)
else:
# Billing cycle started last month on the 27th
prev_month = (now.month - 2) % 12 + 1
prev_year = now.year - 1 if now.month == 1 else now.year
try:
billing_cycle_start = now.replace(year=prev_year, month=prev_month, day=27)
except ValueError:
# Handle months where the previous month doesn't have 27 days
billing_cycle_start = (now - timedelta(days=now.day)).replace(day=27)
billing_cycle_end = now.replace(day=27)
# Adjust dates to include both start and end dates
days_in_billing_cycle = (billing_cycle_end - billing_cycle_start).days + 1
days_in_billing_cycle = days_in_billing_cycle if days_in_billing_cycle > 0 else 1 # Avoid zero
days_past = (now - billing_cycle_start).days + 1
days_past = days_past if days_past > 0 else 1 # Avoid zero
days_remaining = days_in_billing_cycle - days_past
days_remaining = days_remaining if days_remaining >= 0 else 0
average_daily_cost = energy_cost / days_past if days_past > 0 else 0
total_supply_cost = supply_cost * days_in_billing_cycle
total_estimated_cost = (average_daily_cost * days_in_billing_cycle) + total_supply_cost
# Update the state and attributes
self._state = round(total_estimated_cost, 2)
self._attr_extra_state_attributes = {
"Days in Billing Cycle": days_in_billing_cycle,
"Days Remaining": days_remaining,
"Days Past": days_past,
"Average Daily Cost": f"${average_daily_cost:.2f}",
"Total Monthly Supply Cost": f"${total_supply_cost:.2f}",
}
@property
def state(self):
"""Return the state of the sensor."""
return self._state`Preformatted text`