Skip to content

Commit

Permalink
Added energy efficiency sensor, fixed pre-commit warnings
Browse files Browse the repository at this point in the history
  • Loading branch information
signalkraft committed Feb 23, 2023
1 parent 70dd1ea commit 6e30ea6
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 22 deletions.
6 changes: 3 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ repos:
args:
- --safe
- --quiet
files: ^((homeassistant|script|tests)/.+)?[^/]+\.py$
files: ^((homeassistant|script|tests|custom_components)/.+)?[^/]+\.py$
- repo: https://github.com/codespell-project/codespell
rev: v2.2.2
hooks:
Expand All @@ -27,7 +27,7 @@ repos:
- id: flake8
additional_dependencies:
- pydocstyle==5.0.2
files: ^(homeassistant|script|tests)/.+\.py$
files: ^(homeassistant|script|tests|custom_components)/.+\.py$
- repo: https://github.com/PyCQA/bandit
rev: 1.7.4
hooks:
Expand All @@ -36,7 +36,7 @@ repos:
- --quiet
- --format=custom
- --configfile=tests/bandit.yaml
files: ^(homeassistant|script|tests)/.+\.py$
files: ^(homeassistant|script|tests|custom_components)/.+\.py$
- repo: https://github.com/pre-commit/mirrors-isort
rev: v5.10.1
hooks:
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## v0.0.16

* Added energy efficiency sensor

## v0.0.15

* Fallback for None values in sensors
Expand Down
54 changes: 45 additions & 9 deletions custom_components/mypyllant/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,24 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
system_coordinator = SystemCoordinator(
hass, api, entry, timedelta(seconds=update_interval)
)
_LOGGER.debug(f"Refreshing SystemCoordinator")
_LOGGER.debug("Refreshing SystemCoordinator")
await system_coordinator.async_refresh()
data_coordinator = HistoricalDataCoordinator(hass, api, entry, timedelta(hours=1))
_LOGGER.debug(f"Refreshing HistoricalDataCoordinator")
await data_coordinator.async_refresh()

hourly_data_coordinator = HourlyDataCoordinator(
hass, api, entry, timedelta(hours=1)
)
_LOGGER.debug("Refreshing HourlyDataCoordinator")
await hourly_data_coordinator.async_refresh()

daily_data_coordinator = DailyDataCoordinator(hass, api, entry, timedelta(hours=1))
_LOGGER.debug("Refreshing DailyDataCoordinator")
await daily_data_coordinator.async_refresh()

hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][entry.entry_id] = {
"system_coordinator": system_coordinator,
"data_coordinator": data_coordinator,
"hourly_data_coordinator": hourly_data_coordinator,
"daily_data_coordinator": daily_data_coordinator,
}

await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
Expand All @@ -57,7 +65,13 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
if unload_ok:
await hass.data[DOMAIN][entry.entry_id][
"system_coordinator"
].api.session.close()
].api.aiohttp_session.close()
await hass.data[DOMAIN][entry.entry_id][
"hourly_data_coordinator"
].api.aiohttp_session.close()
await hass.data[DOMAIN][entry.entry_id][
"daily_data_coordinator"
].api.aiohttp_session.close()
hass.data[DOMAIN].pop(entry.entry_id)

return unload_ok
Expand Down Expand Up @@ -110,7 +124,7 @@ class SystemCoordinator(MyPyllantCoordinator):
data: list[System]

async def _async_update_data(self) -> list[System]:
_LOGGER.debug(f"Starting async update data for SystemCoordinator")
_LOGGER.debug("Starting async update data for SystemCoordinator")
await self._refresh_session()
data = [
s
Expand All @@ -119,11 +133,11 @@ async def _async_update_data(self) -> list[System]:
return data


class HistoricalDataCoordinator(MyPyllantCoordinator):
class HourlyDataCoordinator(MyPyllantCoordinator):
data: list[list[DeviceData]]

async def _async_update_data(self) -> list[list[DeviceData]]:
_LOGGER.debug(f"Starting async update data for HistoricalDataCoordinator")
_LOGGER.debug("Starting async update data for HourlyDataCoordinator")
await self._refresh_session()
data = []
start = datetime.now().replace(microsecond=0, second=0, minute=0, hour=0)
Expand All @@ -138,3 +152,25 @@ async def _async_update_data(self) -> list[list[DeviceData]]:
)
data.append([da async for da in device_data])
return data


class DailyDataCoordinator(MyPyllantCoordinator):
data: dict[str, list[DeviceData]]

async def _async_update_data(self) -> dict[str, list[DeviceData]]:
_LOGGER.debug("Starting async update data for DailyDataCoordinator")
await self._refresh_session()
data = {}
start = datetime.now().replace(microsecond=0, second=0, minute=0, hour=0)
end = start + timedelta(days=1)
_LOGGER.debug(f"Getting data from {start} to {end}")
async for system in await self.hass.async_add_executor_job(
self.api.get_systems
):
data[system.id] = []
async for device in self.api.get_devices_by_system(system):
device_data = self.api.get_data_by_device(
device, DeviceDataBucketResolution.DAY, start, end
)
data[system.id] += [da async for da in device_data]
return data
2 changes: 1 addition & 1 deletion custom_components/mypyllant/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ async def validate_input(hass: HomeAssistant, data: dict) -> dict[str, Any]:
api = MyPyllantAPI(data["username"], data["password"])
try:
await api.login()
except:
except Exception:
raise AuthenticationFailed
finally:
await api.aiohttp_session.close()
Expand Down
69 changes: 62 additions & 7 deletions custom_components/mypyllant/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity

from . import HistoricalDataCoordinator, SystemCoordinator
from . import DailyDataCoordinator, HourlyDataCoordinator, SystemCoordinator
from .const import DOMAIN

_LOGGER = logging.getLogger(__name__)
Expand All @@ -52,8 +52,11 @@ async def async_setup_entry(
system_coordinator: SystemCoordinator = hass.data[DOMAIN][config.entry_id][
"system_coordinator"
]
data_coordinator: HistoricalDataCoordinator = hass.data[DOMAIN][config.entry_id][
"data_coordinator"
hourly_data_coordinator: HourlyDataCoordinator = hass.data[DOMAIN][config.entry_id][
"hourly_data_coordinator"
]
daily_data_coordinator: DailyDataCoordinator = hass.data[DOMAIN][config.entry_id][
"daily_data_coordinator"
]
sensors: list[SensorEntity] = []
for index, system in enumerate(system_coordinator.data):
Expand Down Expand Up @@ -116,9 +119,12 @@ async def async_setup_entry(
index, dhw_index, system_coordinator
)
)
for device_index, device_data_list in enumerate(data_coordinator.data):
for device_index, device_data_list in enumerate(hourly_data_coordinator.data):
for da_index, _ in enumerate(device_data_list):
sensors.append(DataSensor(device_index, da_index, data_coordinator))
sensors.append(DataSensor(device_index, da_index, hourly_data_coordinator))

for system_id in daily_data_coordinator.data.keys():
sensors.append(EfficiencySensor(system_id, daily_data_coordinator))

async_add_entities(sensors)

Expand Down Expand Up @@ -562,11 +568,11 @@ def unique_id(self) -> str:


class DataSensor(CoordinatorEntity, SensorEntity):
coordinator: HistoricalDataCoordinator
coordinator: HourlyDataCoordinator
_attr_state_class = SensorStateClass.TOTAL

def __init__(
self, device_index: int, da_index: int, coordinator: HistoricalDataCoordinator
self, device_index: int, da_index: int, coordinator: HourlyDataCoordinator
) -> None:
super().__init__(coordinator)
self.device_index = device_index
Expand Down Expand Up @@ -616,3 +622,52 @@ def device_info(self):
@property
def native_value(self):
return self.data_bucket.value if self.data_bucket else None


class EfficiencySensor(CoordinatorEntity, SensorEntity):
coordinator: DailyDataCoordinator
_attr_state_class = SensorStateClass.MEASUREMENT
_attr_name = "Heating Energy Efficiency"

def __init__(self, system_id: str, coordinator: DailyDataCoordinator) -> None:
super().__init__(coordinator)
self.system_id = system_id

@property
def device_data_list(self) -> list[DeviceData]:
return self.coordinator.data[self.system_id]

@property
def energy_consumed(self) -> float:
return sum(
[
v.data[-1].value
for v in self.device_data_list
if v.energy_type == "CONSUMED_ELECTRICAL_ENERGY" and len(v.data)
]
)

@property
def heat_energy_generated(self) -> float:
return sum(
[
v.data[-1].value
for v in self.device_data_list
if v.energy_type == "HEAT_GENERATED" and len(v.data)
]
)

@property
def unique_id(self) -> str:
return f"{DOMAIN}_heating_energy_efficiency_{self.system_id}"

@property
def device_info(self):
return {"identifiers": {(DOMAIN, f"system{self.system_id}")}}

@property
def native_value(self) -> float | None:
if self.energy_consumed:
return round(self.heat_energy_generated / self.energy_consumed, 1)
else:
return None
2 changes: 1 addition & 1 deletion custom_components/mypyllant/water_heater.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import logging
from typing import Any, List
from typing import Any

from myPyllant.models import (
DHWCurrentSpecialFunction,
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ not_skip = __init__.py
force_sort_within_sections = true
sections = FUTURE,STDLIB,INBETWEENS,THIRDPARTY,FIRSTPARTY,LOCALFOLDER
default_section = THIRDPARTY
known_first_party = homeassistant,tests
known_first_party = homeassistant,tests,custom_components
forced_separate = tests
combine_as_imports = true

Expand Down

0 comments on commit 6e30ea6

Please sign in to comment.