Skip to content

Commit

Permalink
Utility meter (#19718)
Browse files Browse the repository at this point in the history
* initial commit

* test service calls

* lint

* float -> Decimal

* extra tests

* lint

* lint

* lint

* lint

* fix self reset

* clean

* add services

* improve service example description

* add optional paused initialization

* fix

* travis fix

* fix YEARLY

* add tests for previous bug

* address comments and suggestions from @OttoWinter

* lint

* remove debug

* add discoverability capabilities

* no need for _hass

* Update homeassistant/components/sensor/utility_meter.py

Co-Authored-By: dgomes <diogogomes@gmail.com>

* Update homeassistant/components/sensor/utility_meter.py

Co-Authored-By: dgomes <diogogomes@gmail.com>

* correct comment

* improve error handling

* address @MartinHjelmare comments

* address @MartinHjelmare comments

* one patch is enought

* follow @ballob suggestion in home-assistant/architecture#131

* fix tests

* review fixes

* major refactor

* lint

* lint

* address comments by @MartinHjelmare

* rename variable
  • Loading branch information
dgomes authored and fabaff committed Jan 26, 2019
1 parent ed6e349 commit 1d5ffe9
Show file tree
Hide file tree
Showing 7 changed files with 713 additions and 0 deletions.
176 changes: 176 additions & 0 deletions homeassistant/components/utility_meter/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
"""
Component to track utility consumption over given periods of time.
For more details about this component, please refer to the documentation
at https://www.home-assistant.io/components/utility_meter/
"""

import logging

import voluptuous as vol

from homeassistant.const import (ATTR_ENTITY_ID, CONF_NAME)
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers import discovery
from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.restore_state import RestoreEntity
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
from .const import (
DOMAIN, SIGNAL_RESET_METER, METER_TYPES, CONF_SOURCE_SENSOR,
CONF_METER_TYPE, CONF_METER_OFFSET, CONF_TARIFF_ENTITY, CONF_TARIFF,
CONF_TARIFFS, CONF_METER, DATA_UTILITY, SERVICE_RESET,
SERVICE_SELECT_TARIFF, SERVICE_SELECT_NEXT_TARIFF,
ATTR_TARIFF)

_LOGGER = logging.getLogger(__name__)

TARIFF_ICON = "mdi:clock-outline"

ATTR_TARIFFS = 'tariffs'

SERVICE_METER_SCHEMA = vol.Schema({
vol.Required(ATTR_ENTITY_ID): cv.entity_ids,
})

SERVICE_SELECT_TARIFF_SCHEMA = SERVICE_METER_SCHEMA.extend({
vol.Required(ATTR_TARIFF): cv.string
})

METER_CONFIG_SCHEMA = vol.Schema({
vol.Required(CONF_SOURCE_SENSOR): cv.entity_id,
vol.Optional(CONF_NAME): cv.string,
vol.Optional(CONF_METER_TYPE): vol.In(METER_TYPES),
vol.Optional(CONF_METER_OFFSET, default=0): cv.positive_int,
vol.Optional(CONF_TARIFFS, default=[]): vol.All(
cv.ensure_list, [cv.string]),
})

CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
cv.slug: METER_CONFIG_SCHEMA,
}),
}, extra=vol.ALLOW_EXTRA)


async def async_setup(hass, config):
"""Set up an Utility Meter."""
component = EntityComponent(_LOGGER, DOMAIN, hass)
hass.data[DATA_UTILITY] = {}

for meter, conf in config.get(DOMAIN).items():
_LOGGER.debug("Setup %s.%s", DOMAIN, meter)

hass.data[DATA_UTILITY][meter] = conf

if not conf[CONF_TARIFFS]:
# only one entity is required
hass.async_create_task(discovery.async_load_platform(
hass, SENSOR_DOMAIN, DOMAIN,
[{CONF_METER: meter, CONF_NAME: meter}], config))
else:
# create tariff selection
await component.async_add_entities([
TariffSelect(meter, list(conf[CONF_TARIFFS]))
])
hass.data[DATA_UTILITY][meter][CONF_TARIFF_ENTITY] =\
"{}.{}".format(DOMAIN, meter)

# add one meter for each tariff
tariff_confs = []
for tariff in conf[CONF_TARIFFS]:
tariff_confs.append({
CONF_METER: meter,
CONF_NAME: "{} {}".format(meter, tariff),
CONF_TARIFF: tariff,
})
hass.async_create_task(discovery.async_load_platform(
hass, SENSOR_DOMAIN, DOMAIN, tariff_confs, config))

component.async_register_entity_service(
SERVICE_RESET, SERVICE_METER_SCHEMA,
'async_reset_meters'
)

component.async_register_entity_service(
SERVICE_SELECT_TARIFF, SERVICE_SELECT_TARIFF_SCHEMA,
'async_select_tariff'
)

component.async_register_entity_service(
SERVICE_SELECT_NEXT_TARIFF, SERVICE_METER_SCHEMA,
'async_next_tariff'
)

return True


class TariffSelect(RestoreEntity):
"""Representation of a Tariff selector."""

def __init__(self, name, tariffs):
"""Initialize a tariff selector."""
self._name = name
self._current_tariff = None
self._tariffs = tariffs
self._icon = TARIFF_ICON

async def async_added_to_hass(self):
"""Run when entity about to be added."""
await super().async_added_to_hass()
if self._current_tariff is not None:
return

state = await self.async_get_last_state()
if not state or state.state not in self._tariffs:
self._current_tariff = self._tariffs[0]
else:
self._current_tariff = state.state

@property
def should_poll(self):
"""If entity should be polled."""
return False

@property
def name(self):
"""Return the name of the select input."""
return self._name

@property
def icon(self):
"""Return the icon to be used for this entity."""
return self._icon

@property
def state(self):
"""Return the state of the component."""
return self._current_tariff

@property
def state_attributes(self):
"""Return the state attributes."""
return {
ATTR_TARIFFS: self._tariffs,
}

async def async_reset_meters(self):
"""Reset all sensors of this meter."""
async_dispatcher_send(self.hass, SIGNAL_RESET_METER,
self.entity_id)

async def async_select_tariff(self, tariff):
"""Select new option."""
if tariff not in self._tariffs:
_LOGGER.warning('Invalid tariff: %s (possible tariffs: %s)',
tariff, ', '.join(self._tariffs))
return
self._current_tariff = tariff
await self.async_update_ha_state()

async def async_next_tariff(self):
"""Offset current index."""
current_index = self._tariffs.index(self._current_tariff)
new_index = (current_index + 1) % len(self._tariffs)
self._current_tariff = self._tariffs[new_index]
await self.async_update_ha_state()
30 changes: 30 additions & 0 deletions homeassistant/components/utility_meter/const.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"""Constants for the utility meter component."""
DOMAIN = 'utility_meter'

HOURLY = 'hourly'
DAILY = 'daily'
WEEKLY = 'weekly'
MONTHLY = 'monthly'
YEARLY = 'yearly'

METER_TYPES = [HOURLY, DAILY, WEEKLY, MONTHLY, YEARLY]

DATA_UTILITY = 'utility_meter_data'

CONF_METER = 'meter'
CONF_SOURCE_SENSOR = 'source'
CONF_METER_TYPE = 'cycle'
CONF_METER_OFFSET = 'offset'
CONF_PAUSED = 'paused'
CONF_TARIFFS = 'tariffs'
CONF_TARIFF = 'tariff'
CONF_TARIFF_ENTITY = 'tariff_entity'

ATTR_TARIFF = 'tariff'

SIGNAL_START_PAUSE_METER = 'utility_meter_start_pause'
SIGNAL_RESET_METER = 'utility_meter_reset'

SERVICE_RESET = 'reset'
SERVICE_SELECT_TARIFF = 'select_tariff'
SERVICE_SELECT_NEXT_TARIFF = 'next_tariff'
Loading

0 comments on commit 1d5ffe9

Please sign in to comment.