Skip to content

Commit

Permalink
Merge pull request #34 from RogerSelwyn/main
Browse files Browse the repository at this point in the history
Add sensor for Last Charge
  • Loading branch information
nickknissen authored Oct 31, 2023
2 parents 3efe5cb + f3c7476 commit 15ae6eb
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 29 deletions.
18 changes: 8 additions & 10 deletions custom_components/monta/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,10 @@

from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import (
DataUpdateCoordinator,
UpdateFailed,
)
from homeassistant.exceptions import ConfigEntryAuthFailed
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed

from .api import (
MontaApiClient,
MontaApiClientAuthenticationError,
MontaApiClientError,
)
from .api import MontaApiClient, MontaApiClientAuthenticationError, MontaApiClientError
from .const import DOMAIN, LOGGER


Expand All @@ -38,7 +31,12 @@ def __init__(self, hass: HomeAssistant, client: MontaApiClient) -> None:
async def _async_update_data(self):
"""Update data via library."""
try:
return await self.client.async_get_charge_points()
chargepoints = await self.client.async_get_charge_points()
for charge_point_id, _ in chargepoints.items():
chargepoints[charge_point_id][
"charges"
] = await self.client.async_get_charges(charge_point_id)
return chargepoints
except MontaApiClientAuthenticationError as exception:
raise ConfigEntryAuthFailed(exception) from exception
except MontaApiClientError as exception:
Expand Down
110 changes: 91 additions & 19 deletions custom_components/monta/sensor.py
Original file line number Diff line number Diff line change
@@ -1,61 +1,119 @@
"""Sensor platform for monta."""
from __future__ import annotations
import logging

from attr import dataclass
from collections.abc import Callable
from dataclasses import dataclass
from typing import Any

from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.entity import generate_entity_id
from dateutil import parser
from homeassistant.components.sensor import (
ENTITY_ID_FORMAT,
SensorDeviceClass,
SensorEntity,
SensorEntityDescription,
SensorDeviceClass,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import generate_entity_id
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import StateType

from .const import DOMAIN, ChargerStatus
from .coordinator import MontaDataUpdateCoordinator
from .entity import MontaEntity
from .utils import snake_case

_LOGGER = logging.getLogger(__name__)
@dataclass
class MontaSensorEntityDescriptionMixin:
"""Mixin for required keys."""

value_fn: Callable[[Any], StateType]
extra_state_attributes_fn: Callable[[Any], dict[str, str]] | None


@dataclass
class MontaSensorEntityDescription(SensorEntityDescription):
"""Describes a Monta sensor."""
class MontaSensorEntityDescription(
SensorEntityDescription, MontaSensorEntityDescriptionMixin
):
"""Describes MontaSensor sensor entity."""


def last_charge_state(data: dict[str, Any]) -> str:
"""Process state for last charge (if available)."""
return data["charges"][0]["state"] if len(data["charges"]) > 0 else None

ENTITY_DESCRIPTIONS = (
SensorEntityDescription(

def last_charge_extra_attributes(data: dict[str, Any]) -> dict[str, Any]:
"""Process extra attributes for last charge (if available)."""
if data["charges"]:
attributes = data["charges"][0]
date_keys = [
"createdAt",
"updatedAt",
"startedAt",
"stoppedAt",
"cablePluggedInAt",
"fullyChargedAt",
"failedAt",
"timeoutAt",
]

for key in date_keys:
if key in attributes:
attributes[key] = _parse_date(attributes[key])

return attributes

return None


def _parse_date(chargedate) -> str:
return parser.parse(chargedate) if chargedate else None


ENTITY_DESCRIPTIONS: tuple[MontaSensorEntityDescription, ...] = (
MontaSensorEntityDescription(
key="charger_visibility",
name="Visibility",
icon="mdi:eye",
device_class=SensorDeviceClass.ENUM,
options=["public", "private"],
value_fn=lambda data: data["visibility"],
extra_state_attributes_fn=None,
),
SensorEntityDescription(
MontaSensorEntityDescription(
key="charger_type",
name="Type",
icon="mdi:current-ac",
device_class=SensorDeviceClass.ENUM,
options=["ac", "dc"],
value_fn=lambda data: data["type"],
extra_state_attributes_fn=None,
),
SensorEntityDescription(
MontaSensorEntityDescription(
key="charger_state",
name="State",
icon="mdi:state-machine",
device_class=SensorDeviceClass.ENUM,
options=[x.value for x in ChargerStatus],
value_fn=lambda data: data["state"],
extra_state_attributes_fn=None,
),
SensorEntityDescription(
MontaSensorEntityDescription(
key="charger_lastMeterReadingKwh",
name="Last meter reading",
icon="mdi:counter",
device_class=SensorDeviceClass.ENERGY,
native_unit_of_measurement="kWh",
value_fn=lambda data: data["lastMeterReadingKwh"],
extra_state_attributes_fn=None,
),
MontaSensorEntityDescription(
key="charge_state",
name="Last Charge",
icon="mdi:ev-station",
value_fn=last_charge_state,
extra_state_attributes_fn=last_charge_extra_attributes,
),
)

Expand Down Expand Up @@ -101,8 +159,22 @@ def __init__(
)

@property
def native_value(self) -> str:
"""Return the native value of the sensor."""
_, data_key = self.entity_description.key.split("charger_")
def native_value(self) -> StateType:
"""Return the state."""
return self.entity_description.value_fn(
self.coordinator.data[self.charge_point_id]
)

@property
def extra_attributes(self) -> str:
"""Return extra attributes for trhe sensor."""
return None

return self.coordinator.data[self.charge_point_id].get(data_key)
@property
def extra_state_attributes(self) -> dict[str, str] | None:
"""Return the state attributes."""
if self.entity_description.extra_state_attributes_fn:
return self.entity_description.extra_state_attributes_fn(
self.coordinator.data[self.charge_point_id]
)
return None

0 comments on commit 15ae6eb

Please sign in to comment.