Skip to content

Commit

Permalink
Added some sensors.
Browse files Browse the repository at this point in the history
  • Loading branch information
StephanU committed Jun 3, 2024
1 parent fa386da commit ac10957
Show file tree
Hide file tree
Showing 12 changed files with 131 additions and 45 deletions.
4 changes: 1 addition & 3 deletions custom_components/talent_monitor/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):

await coordinator.async_config_entry_first_refresh()

_LOGGER.debug("received data %s", json.dumps(coordinator.data))

hass.data[DOMAIN][entry.entry_id] = coordinator

for platform in PLATFORMS:
coordinator.platforms.append(platform)
hass.async_add_job(
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(entry, platform)
)

Expand Down
3 changes: 2 additions & 1 deletion custom_components/talent_monitor/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

import logging

from custom_components.talent_monitor.pyTalentMonitor import AuthenticationError, TalentSolarMonitor
from custom_components.talent_monitor.pyTalentMonitor import TalentSolarMonitor
from custom_components.talent_monitor.pyTalentMonitor.data_provider import AuthenticationError
import homeassistant.helpers.config_validation as cv
import voluptuous as vol
from aiohttp import ClientConnectorError
Expand Down
2 changes: 1 addition & 1 deletion custom_components/talent_monitor/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ async def _async_update_data(self):
"""Update data via library."""
_LOGGER.debug("_async_update_data ")
try:
return await self.api.fetch_data()
await self.api.fetch_data()
except Exception as exception:
_LOGGER.exception("_async_update_data failed")
raise UpdateFailed() from exception
4 changes: 2 additions & 2 deletions custom_components/talent_monitor/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ class TalentMonitorEntity(CoordinatorEntity):
_attr_has_entity_name = True

def __init__(
self, coordinator, entity: Entity
self, coordinator, entity: Entity, entity_suffix: str = ""
):
"""Initialize a TalentMonitor entity."""
super().__init__(coordinator)

device_id = f"{entity.entity_id}"
device_name = entity.name

self._attr_unique_id = f"{device_id}"
self._attr_unique_id = f"{device_id}{entity_suffix}"

self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, device_id)},
Expand Down
17 changes: 6 additions & 11 deletions custom_components/talent_monitor/pyTalentMonitor/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import argparse
import asyncio
import json
import logging

from aiohttp import ClientSession
Expand All @@ -13,13 +12,6 @@
# Configure logging
_LOGGER: logging.Logger = logging.getLogger(__name__)

BASE_URL = "https://www.talent-monitoring.com/prod-api"
TIMEZONE = "+02:00"

class AuthenticationError(Exception):
"""AuthenticationError when connecting to the Talent API."""
pass

class TalentSolarMonitor:
"""TalentSolarMonitor API client."""

Expand All @@ -38,13 +30,16 @@ def __init__(
def get_power_stations(self) -> list[PowerStation]:
return self._power_station_data_provider.power_stations

def fetch_data(self):
self._inverter_data_provider.fetch_data()
self._power_station_data_provider.fetch_data()
async def fetch_data(self):
await self._inverter_data_provider.fetch_data()
await self._power_station_data_provider.fetch_data()

async def fetch_solar_data(self):
await self.fetch_data()

async def login(self):
await self._data_provider.login()

async def main(username: str, password: str):
"""Connect to the TalentSolarMonitor API and fetch the solar data."""
async with ClientSession() as session:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import os

from aiohttp import ClientSession
from custom_components.talent_monitor.pyTalentMonitor import AuthenticationError

# Configure logging
_LOGGER: logging.Logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -31,7 +30,7 @@ def get_credentials(self):
async def login(self):
"""Log in using the given credentials."""
login_data = {"username": self._username, "password": self._password}
response = await self.session.post(f"{self._url}/login", json=login_data)
response = await self._session.post(f"{self._url}/login", json=login_data)
response_data = await response.json()
if "token" in response_data:
self._token = response_data["token"]
Expand All @@ -48,13 +47,13 @@ async def refresh_token(self):
async def get_data(self, endpoint):
"""Get data from the given endpoint."""
if not self._token:
self.login()
await self.login()
headers = {"Authorization": f"Bearer {self._token}"}
response = await self.session.get(f"{self._url}/{endpoint}", headers=headers)
response = await self._session.get(f"{self._url}/{endpoint}", headers=headers)
if response.status == 401: # Unauthorized, token might be expired
self.refresh_token()
headers["Authorization"] = f"Bearer {self._token}"
response = await self.session.get(f"{self._url}/{endpoint}", headers=headers)
response = await self._session.get(f"{self._url}/{endpoint}", headers=headers)

if response.status == 200:
return await response.json()
Expand All @@ -67,4 +66,8 @@ class Entity:

def __init__(self, entity_id: str, name: str) -> None:
self.entity_id = entity_id
self.name = name
self.name = name

class AuthenticationError(Exception):
"""AuthenticationError when connecting to the Talent API."""
pass
18 changes: 10 additions & 8 deletions custom_components/talent_monitor/pyTalentMonitor/inverter.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@ def __init__(
self, entity_id: str, name: str
) -> None:
super().__init__(entity_id, name)
self._data

@property
def data(self, data):
self._data = data
self._data = {}

@property
def data(self):
return self._data

@data.setter
def data(self, data):
self._data = data


class InverterDataProvider():
def __init__(
Expand All @@ -47,6 +47,8 @@ async def fetch_data(self):
if "deviceGuid" in inverter_data:
deviceGuid = inverter_data["deviceGuid"]

_LOGGER.debug("Data for inverter GUID %s: %s", deviceGuid, json.dumps(inverter_data))

if not deviceGuid in self._inverters:
self._inverters["deviceGuid"] = Inverter()

Expand All @@ -56,6 +58,6 @@ async def fetch_data(self):
endpoint=f"tools/device/selectDeviceInverterInfo?deviceGuid={deviceGuid}"
)

_LOGGER.debug("Data for inverter GUID %s: %s", deviceGuid, json.dumps(inverter_info))
if inverter_info:
inverter.data = inverter_info
_LOGGER.debug("Details for inverter GUID %s: %s", deviceGuid, json.dumps(inverter_info))
if inverter_info and "data" in inverter_info:
inverter.data = inverter_info["data"]
20 changes: 11 additions & 9 deletions custom_components/talent_monitor/pyTalentMonitor/power_station.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,16 @@ def __init__(
self, entity_id: str, name: str
) -> None:
super().__init__(entity_id, name)
self._data

@property
def data(self, data):
self._data = data
self._data = {}

@property
def data(self):
return self._data

@data.setter
def data(self, data):
self._data = data


class PowerStationDataProvider():
def __init__(
Expand All @@ -50,15 +50,17 @@ async def fetch_data(self):
powerStationGuid = power_station_data["powerStationGuid"]
powerStationName = power_station_data["stationName"]

_LOGGER.debug("Data for powerstation GUID %s: %s", powerStationGuid, json.dumps(power_station_data))

if not powerStationGuid in self._power_stations:
self._power_stations["powerStationGuid"] = PowerStation(powerStationGuid, powerStationName)

power_station = self._power_stations["deviceGuid"]
power_station = self._power_stations["powerStationGuid"]

power_station_info = await self._data_provider.get_data(
endpoint=f"system/station/getPowerStationByGuid?powerStationGuid={powerStationGuid}&timezone={TIMEZONE}"
)

_LOGGER.debug("Data for powerstation GUID %s: %s", powerStationGuid, json.dumps(data))
if power_station_info:
power_station.data = power_station_info
_LOGGER.debug("Details for powerstation GUID %s: %s", powerStationGuid, json.dumps(power_station_info))
if power_station_info and "data" in power_station_info:
power_station.data = power_station_info["data"]
51 changes: 49 additions & 2 deletions custom_components/talent_monitor/sensor.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
"""Sensor platform for TalentMonitor."""
from datetime import datetime
import logging
from custom_components.talent_monitor.entity import TalentMonitorEntity
from custom_components.talent_monitor.pyTalentMonitor.power_station import PowerStation
from homeassistant.components.sensor import SensorDeviceClass
Expand All @@ -10,13 +12,40 @@

from .const import DOMAIN


_LOGGER: logging.Logger = logging.getLogger(__name__)

SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
SensorEntityDescription(
key="totalActivePower",
translation_key="talentmonitor_powerstation_totalActivePower",
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.POWER,
)
),
SensorEntityDescription(
key="dayEnergy",
translation_key="talentmonitor_powerstation_dayEnergy",
state_class=SensorStateClass.TOTAL_INCREASING,
device_class=SensorDeviceClass.ENERGY,
),
SensorEntityDescription(
key="monthEnergy",
translation_key="talentmonitor_powerstation_monthEnergy",
state_class=SensorStateClass.TOTAL_INCREASING,
device_class=SensorDeviceClass.ENERGY,
),
SensorEntityDescription(
key="yearEnergy",
translation_key="talentmonitor_powerstation_yearEnergy",
state_class=SensorStateClass.TOTAL_INCREASING,
device_class=SensorDeviceClass.ENERGY,
),
SensorEntityDescription(
key="lastDataUpdateTime",
translation_key="talentmonitor_powerstation_lastDataUpdateTime",
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.DATE,
),
)

SENSORS = {desc.key: desc for desc in SENSOR_TYPES}
Expand All @@ -35,6 +64,7 @@ async def async_setup_entry(hass, entry, async_add_devices):
power_stations: list[PowerStation] = coordinator.api.get_power_stations()
for power_station in power_stations:
for index, value in enumerate(power_station.data):
_LOGGER.debug("Iterate data for powerstation %s", value)
if value and value in SENSORS:
async_add_devices(
[
Expand All @@ -59,6 +89,7 @@ def __init__(
super().__init__(
coordinator,
power_station,
sensorEntityDescription.key
)

self._power_station = power_station
Expand All @@ -67,5 +98,21 @@ def __init__(
@property
def native_value(self):
"""Return the state of the sensor."""
return self._power_station.data[self.entity_description.key]
if (self.entity_description.key == "lastDataUpdateTime"):
return datetime.fromisoformat(self._power_station.data[self.entity_description.key])
else:
return self._power_station.data[self.entity_description.key]

@property
def native_unit_of_measurement(self) -> str | None:
"""Return the unit of measurement."""
key_for_value_with_unit = self.entity_description.key + "Named"

if (key_for_value_with_unit in self._power_station.data and self._power_station.data[key_for_value_with_unit]):
value_split = self._power_station.data[key_for_value_with_unit].split(" ")
if (value_split and len(value_split) == 2):
unit = value_split[1]
return SENSOR_UNIT_MAPPING[unit]

return None

19 changes: 19 additions & 0 deletions custom_components/talent_monitor/translations/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,24 @@
"abort": {
"single_instance_allowed": "Es ist nur eine einzige Instanz zulässig."
}
},
"entity": {
"sensor": {
"talentmonitor_powerstation_totalActivePower": {
"name": "Aktuelle Leistung"
},
"talentmonitor_powerstation_dayEnergy": {
"name": "Energie insgesamt (heute)"
},
"talentmonitor_powerstation_monthEnergy": {
"name": "Energie insgesamt (dieser Monat)"
},
"talentmonitor_powerstation_yearEnergy": {
"name": "Energie insgesamt (dieses Jahr)"
},
"talentmonitor_powerstation_lastDataUpdateTime": {
"name": "Letzte Datenaktualisierung"
}
}
}
}
19 changes: 19 additions & 0 deletions custom_components/talent_monitor/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,24 @@
"abort": {
"single_instance_allowed": "Only a single instance is allowed."
}
},
"entity": {
"sensor": {
"talentmonitor_powerstation_totalActivePower": {
"name": "Current Power"
},
"talentmonitor_powerstation_dayEnergy": {
"name": "Total Energy Today"
},
"talentmonitor_powerstation_monthEnergy": {
"name": "Total Energy This Month"
},
"talentmonitor_powerstation_yearEnergy": {
"name": "Total Energy This Year"
},
"talentmonitor_powerstation_lastDataUpdateTime": {
"name": "Last Data Update"
}
}
}
}
4 changes: 2 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
colorlog==6.8.2
homeassistant==2024.5.5
homeassistant==2024.6.0b5
pip>=21.0,<24.1
ruff==0.4.5
pytest-asyncio
pytest-homeassistant-custom-component==0.13.125
pytest-homeassistant-custom-component==0.13.129

0 comments on commit ac10957

Please sign in to comment.