Skip to content

Commit

Permalink
Merge pull request #1 from tijsverkoyen/service
Browse files Browse the repository at this point in the history
Service
  • Loading branch information
tijsverkoyen authored Nov 1, 2022
2 parents b2be804 + b4f398c commit c126b1c
Show file tree
Hide file tree
Showing 9 changed files with 167 additions and 15 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,19 @@ At this point the integration is not part of the default HACS repositories, so
you will need to add this repository as a custom repository in HACS.

When this is done, just install the repository.

The configuration happens in the configuration flow when you add the integration.


## Sensors
For each meter in EnergyID there will be a sensor for the:

* latest/current reading
* previous reading

These will be updated every 15 minutes.


## Services
### set_meter_reading
This services allows you to add a meter reading from within Home Assistant.
4 changes: 4 additions & 0 deletions custom_components/energy_id/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from homeassistant.config_entries import ConfigEntry

from .const import DOMAIN
from .services import async_setup_services


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
Expand All @@ -15,4 +16,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(entry, 'sensor')
)

async_setup_services(hass)

return True
1 change: 1 addition & 0 deletions custom_components/energy_id/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
CONF_RECORD = 'record'
CONF_API_KEY = 'api_key'
CONF_ENERGY_ID_API_HOST = 'https://api.energyid.eu'
CONF_METER_IDS = 'meter_ids'

RESPONSE_ATTRIBUTE_READINGS = 'readings'
RESPONSE_ATTRIBUTE_VALUE = 'value'
Expand Down
41 changes: 30 additions & 11 deletions custom_components/energy_id/energy_id/api.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""API client for EnergyID API."""
import urllib.parse

from requests import get, HTTPError
from requests import get, post, HTTPError

from .meter import EnergyIDMeter
from .record import EnergyIDRecord
Expand Down Expand Up @@ -79,38 +79,57 @@ def get_meter_readings(self, meter: str, take: int = 20, next_row_key: str = Non
params=params
)

def set_meter_readings(self, meter: str, timestamp: str, value: float):
return self._do_call(
'POST',
f'api/v1/Meters/{meter}/readings',
data={
'timestamp': timestamp,
'value': value
}
)

def _do_call(self, method: str, path: str, **kwargs) -> dict:
"""Make a request."""
headers = kwargs.get("headers")
json = kwargs.get("json")
data = kwargs.get("data")

if headers is None:
headers = {}
else:
headers = dict(headers)

if json is None:
json = {}
else:
json = dict(json)

if data is not None:
data = dict(data)

headers["authorization"] = 'apikey ' + self._api_key

try:
if method == 'GET':
url = f'{self._host}/{path}'
if kwargs.get("params") is not None:
url = f'{url}?{urllib.parse.urlencode(kwargs.get("params"))}'
url = f'{self._host}/{path}'
if kwargs.get("params") is not None:
url = f'{url}?{urllib.parse.urlencode(kwargs.get("params"))}'

if method == 'GET':
response = get(url, headers=headers, json=json)
response.raise_for_status()
json_data = response.json()
_LOGGER.debug(f'JSON data for {url}: {json_data}')
if method == 'POST':
response = post(url, headers=headers, data=data)

response.raise_for_status()

return response.json()
json_data = response.json()
_LOGGER.debug(f'JSON data for {url}: {json_data}')
return json_data

except HTTPError as error:
_LOGGER.error(error)
raise EnergyIDApiError(f'HTTP Error: {error}')
_LOGGER.error(f'error:')
_LOGGER.error(response.json())
raise EnergyIDApiError(f'HTTP Error: {error}: {response.json()}')


class EnergyIDApiError(Exception):
Expand Down
2 changes: 1 addition & 1 deletion custom_components/energy_id/meter_reading_coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def __init__(self, hass: HomeAssistant, api: EnergyIDApi, meters: List[EnergyIDM
hass,
_LOGGER,
name="EnergyIDMeterReadingCoordinator",
update_interval=timedelta(minutes=1),
update_interval=timedelta(minutes=15),
)
self.api = api
self.meters = meters
Expand Down
2 changes: 1 addition & 1 deletion custom_components/energy_id/meter_reading_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ def device_info(self) -> DeviceInfo:
identifiers={(DOMAIN, f'meter-{self._meter.id}')},
name=f'{self._meter.name}',
model=self._meter.meter_type,
via_device=(DOMAIN, f'record-{self._record.id}'),
via_device=(DOMAIN, f'record-{self._record.id}')
)

@property
Expand Down
7 changes: 5 additions & 2 deletions custom_components/energy_id/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from homeassistant.config_entries import ConfigEntry
from homeassistant.helpers import device_registry as dr

from .const import DOMAIN, CONF_RECORD, CONF_API_KEY, CONF_ENERGY_ID_API_HOST
from .const import DOMAIN, CONF_RECORD, CONF_API_KEY, CONF_ENERGY_ID_API_HOST, CONF_METER_IDS
from .meter_reading_coordinator import EnergyIDMeterReadingCoordinator
from .energy_id.api import EnergyIDApi
from .meter_reading_sensor import EnergyIDMeterReading
Expand Down Expand Up @@ -43,11 +43,14 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry, asyn
)

coordinator = EnergyIDMeterReadingCoordinator(hass, api, meters)
await coordinator.async_config_entry_first_refresh()

record_config[CONF_METER_IDS] = []
entities = []
for meter in meters:
record_config[CONF_METER_IDS].append(meter.id)
entities.append(EnergyIDMeterReading(coordinator, meter, record, 'last'))
entities.append(EnergyIDMeterReading(coordinator, meter, record, 'previous'))

async_add_entities(entities)

await coordinator.async_config_entry_first_refresh()
83 changes: 83 additions & 0 deletions custom_components/energy_id/services.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
from homeassistant.core import callback, HomeAssistant, ServiceCall
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.device_registry import DeviceEntry
from homeassistant.exceptions import HomeAssistantError

from .const import DOMAIN, CONF_RECORD, CONF_METER_IDS, CONF_ENERGY_ID_API_HOST, CONF_API_KEY

from .energy_id.api import EnergyIDApi, EnergyIDApiError

import voluptuous as vol
import logging

_LOGGER = logging.getLogger(__name__)


def meter_id_from_device(device: DeviceEntry) -> str:
for identifier in device.identifiers:
if identifier[0] == DOMAIN:
return identifier[1].replace('meter-', '')

return None


def find_record_for_meter_id(meter_id: str, config: dict) -> dict:
for config_entry in config.items():
for record in config_entry[1][CONF_RECORD]:
for config_meter_id in record[CONF_METER_IDS]:
if meter_id == config_meter_id:
return record

return None


@callback
def async_setup_services(hass: HomeAssistant) -> None:
"""Set up services for EnergyID."""

async def handle_set_meter_reading(call: ServiceCall):
"""Handle the service call."""
_LOGGER.debug(f'Service set_meter_reading called: {call.data}')

# get device
device_registry = dr.async_get(hass)
device = device_registry.async_get(call.data['device_id'])

if device is None:
raise HomeAssistantError(f'Meter not found')

meter_id = meter_id_from_device(device)
if meter_id is None:
raise HomeAssistantError(f'Meter not found')

meter_record = find_record_for_meter_id(meter_id, hass.data[DOMAIN])
if meter_record is None:
raise HomeAssistantError(f'Meter not found')

api = EnergyIDApi(CONF_ENERGY_ID_API_HOST, meter_record[CONF_API_KEY])

try:
response = await hass.async_add_executor_job(
api.set_meter_readings,
meter_id,
call.data['date'],
call.data['value'],
)
_LOGGER.debug(f'Service set_meter_reading response: {response}')
except EnergyIDApiError as e:
raise HomeAssistantError(f'Invalid response from EnergyID API: {e}')

hass.services.async_register(
DOMAIN,
'set_meter_reading',
handle_set_meter_reading,
schema=vol.All(
vol.Schema(
{
vol.Required('device_id'): str,
vol.Required('date'): str,
vol.Required('value'): vol.Coerce(float),
}
)
)
)
26 changes: 26 additions & 0 deletions custom_components/energy_id/services.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Describes the format for available EnergyID services
set_meter_reading:
name: Set new meter reading
description: Adds a reading for a given meter.
fields:
device_id:
name: Meter
description: The meter to add the reading to.
required: true
selector:
device:
integration: 'energy_id'
device_class: 'meter'
multiple: false
date:
name: Date
description: The date for the reading.
required: true
selector:
date:
value:
name: Value
description: The new value.
required: true
selector:
text:

0 comments on commit c126b1c

Please sign in to comment.