|
6 | 6 | from datetime import datetime, timedelta
|
7 | 7 | from typing import cast, Any # noqa: UP035
|
8 | 8 | from collections import Counter
|
| 9 | +from uuid import UUID |
9 | 10 |
|
10 | 11 | import pytz
|
11 | 12 | from homeassistant.components.recorder import get_instance
|
|
26 | 27 | from iec_api.models.device import Device, Devices
|
27 | 28 | from iec_api.models.exceptions import IECError
|
28 | 29 | from iec_api.models.jwt import JWT
|
| 30 | +from iec_api.models.meter_reading import MeterReading |
29 | 31 | from iec_api.models.remote_reading import ReadingResolution, RemoteReading, FutureConsumptionInfo, RemoteReadingResponse
|
30 | 32 |
|
31 | 33 | from .commons import find_reading_by_date
|
@@ -64,13 +66,16 @@ def __init__(
|
64 | 66 | self._entry_data = config_entry.data
|
65 | 67 | self._today_readings = {}
|
66 | 68 | self._devices_by_contract_id = {}
|
| 69 | + self._last_meter_reading = {} |
67 | 70 | self._devices_by_meter_id = {}
|
68 | 71 | self._delivery_tariff_by_pahse = {}
|
69 | 72 | self._distribution_tariff_by_pahse = {}
|
70 | 73 | self._power_size_by_connection_size = {}
|
71 | 74 | self._kwh_tariff: float | None = None
|
72 | 75 | self._kva_tariff: float | None = None
|
73 | 76 | self._readings = {}
|
| 77 | + self._account_id: str | None = None |
| 78 | + self._connection_size: str | None = None |
74 | 79 | self.api = IecClient(
|
75 | 80 | self._entry_data[CONF_USER_ID],
|
76 | 81 | session=aiohttp_client.async_get_clientsession(hass, family=socket.AF_INET)
|
@@ -107,6 +112,29 @@ async def _get_devices_by_device_id(self, meter_id) -> Devices:
|
107 | 112 | _LOGGER.exception(f"Failed fetching device details by meter id {meter_id}", e)
|
108 | 113 | return devices
|
109 | 114 |
|
| 115 | + async def _get_last_meter_reading(self, bp_number, contract_id, meter_id) -> MeterReading: |
| 116 | + key = (contract_id, int(meter_id)) |
| 117 | + last_meter_reading = self._last_meter_reading.get(key) |
| 118 | + if not last_meter_reading: |
| 119 | + try: |
| 120 | + meter_readings = await self.api.get_last_meter_reading(bp_number, contract_id) |
| 121 | + |
| 122 | + for reading in meter_readings.last_meters: |
| 123 | + reading_meter_id = int(reading.serial_number) |
| 124 | + if len(reading.meter_readings) > 0: |
| 125 | + readings = reading.meter_readings |
| 126 | + readings.sort(key=lambda rdng: rdng.reading_date, reverse=True) |
| 127 | + last_meter_reading = readings[0] |
| 128 | + _LOGGER.debug(f"Last Reading for contract {contract_id}, Meter {reading_meter_id}: " |
| 129 | + f"{last_meter_reading}") |
| 130 | + reading_key = (contract_id, reading_meter_id) |
| 131 | + self._last_meter_reading[reading_key] = last_meter_reading |
| 132 | + else: |
| 133 | + _LOGGER.debug(f"No Reading found for contract {contract_id}, Meter {reading_meter_id}") |
| 134 | + except IECError as e: |
| 135 | + _LOGGER.exception(f"Failed fetching device details by meter id {meter_id}", e) |
| 136 | + return self._last_meter_reading.get(key) |
| 137 | + |
110 | 138 | async def _get_kwh_tariff(self) -> float:
|
111 | 139 | if not self._kwh_tariff:
|
112 | 140 | try:
|
@@ -143,6 +171,23 @@ async def _get_distribution_tariff(self, phase) -> float:
|
143 | 171 | _LOGGER.exception(f"Failed fetching Distribution Tariff by phase {phase}", e)
|
144 | 172 | return distribution_tariff or 0.0
|
145 | 173 |
|
| 174 | + async def _get_account_id(self) -> UUID | None: |
| 175 | + if not self._account_id: |
| 176 | + try: |
| 177 | + account = await self.api.get_default_account() |
| 178 | + self._account_id = account.id |
| 179 | + except IECError as e: |
| 180 | + _LOGGER.exception("Failed fetching Account", e) |
| 181 | + return self._account_id |
| 182 | + |
| 183 | + async def _get_connection_size(self, account_id) -> str | None: |
| 184 | + if not self._connection_size: |
| 185 | + try: |
| 186 | + self._connection_size = await self.api.get_masa_connection_size_from_masa(account_id) |
| 187 | + except IECError as e: |
| 188 | + _LOGGER.exception("Failed fetching Masa Connection Size", e) |
| 189 | + return self._connection_size |
| 190 | + |
146 | 191 | async def _get_power_size(self, connection_size) -> float:
|
147 | 192 | power_size = self._power_size_by_connection_size.get(connection_size)
|
148 | 193 | if not power_size:
|
@@ -374,34 +419,46 @@ async def _async_update_data(
|
374 | 419 | else:
|
375 | 420 | _LOGGER.debug("Failed fetching FutureConsumption, data in IEC API is corrupted")
|
376 | 421 |
|
377 |
| - if not is_private_producer: |
| 422 | + if is_private_producer: |
| 423 | + last_meter_reading = await self._get_last_meter_reading(self._bp_number, contract_id, |
| 424 | + device.device_number) |
| 425 | + last_meter_read = last_meter_reading.reading |
| 426 | + last_meter_read_date = last_meter_reading.reading_date.date() |
| 427 | + |
| 428 | + account_id = await self._get_account_id() |
| 429 | + connection_size = await self._get_connection_size(account_id) |
| 430 | + phase_count_str = connection_size.split("X")[0] \ |
| 431 | + if connection_size.find("X") != -1 else "1" |
| 432 | + phase_count = int(phase_count_str) |
| 433 | + |
| 434 | + else: |
378 | 435 | devices_by_id: Devices = await self._get_devices_by_device_id(device.device_number)
|
379 |
| - last_meter_read = int(devices_by_id.counter_devices[0].last_mr) |
| 436 | + last_meter_read = devices_by_id.counter_devices[0].last_mr |
380 | 437 | last_meter_read_date = devices_by_id.counter_devices[0].last_mr_date
|
381 | 438 | phase_count = devices_by_id.counter_devices[0].connection_size.phase
|
382 | 439 | connection_size = (devices_by_id.counter_devices[0].
|
383 |
| - connection_size.representative_connection_size) |
| 440 | + connection_size.representative_connection_size) |
384 | 441 |
|
385 |
| - distribution_tariff = await self._get_distribution_tariff(phase_count) |
386 |
| - delivery_tariff = await self._get_delivery_tariff(phase_count) |
387 |
| - power_size = await self._get_power_size(connection_size) |
| 442 | + distribution_tariff = await self._get_distribution_tariff(phase_count) |
| 443 | + delivery_tariff = await self._get_delivery_tariff(phase_count) |
| 444 | + power_size = await self._get_power_size(connection_size) |
388 | 445 |
|
389 |
| - estimated_bill, fixed_price, consumption_price, total_days, delivery_price, distribution_price, \ |
| 446 | + estimated_bill, fixed_price, consumption_price, total_days, delivery_price, distribution_price, \ |
390 | 447 | total_kva_price, estimated_kwh_consumption = (
|
391 |
| - self._calculate_estimated_bill(device.device_number, future_consumption, |
392 |
| - last_meter_read, last_meter_read_date, |
393 |
| - kwh_tariff, kva_tariff, distribution_tariff, |
394 |
| - delivery_tariff, power_size, last_invoice)) |
395 |
| - |
396 |
| - estimated_bill_dict = { |
397 |
| - TOTAL_EST_BILL_ATTR_NAME: estimated_bill, |
398 |
| - EST_BILL_DAYS_ATTR_NAME: total_days, |
399 |
| - EST_BILL_CONSUMPTION_PRICE_ATTR_NAME: consumption_price, |
400 |
| - EST_BILL_DELIVERY_PRICE_ATTR_NAME: delivery_price, |
401 |
| - EST_BILL_DISTRIBUTION_PRICE_ATTR_NAME: distribution_price, |
402 |
| - EST_BILL_TOTAL_KVA_PRICE_ATTR_NAME: total_kva_price, |
403 |
| - EST_BILL_KWH_CONSUMPTION_ATTR_NAME: estimated_kwh_consumption |
404 |
| - } |
| 448 | + self._calculate_estimated_bill(device.device_number, future_consumption, |
| 449 | + last_meter_read, last_meter_read_date, |
| 450 | + kwh_tariff, kva_tariff, distribution_tariff, |
| 451 | + delivery_tariff, power_size, last_invoice)) |
| 452 | + |
| 453 | + estimated_bill_dict = { |
| 454 | + TOTAL_EST_BILL_ATTR_NAME: estimated_bill, |
| 455 | + EST_BILL_DAYS_ATTR_NAME: total_days, |
| 456 | + EST_BILL_CONSUMPTION_PRICE_ATTR_NAME: consumption_price, |
| 457 | + EST_BILL_DELIVERY_PRICE_ATTR_NAME: delivery_price, |
| 458 | + EST_BILL_DISTRIBUTION_PRICE_ATTR_NAME: distribution_price, |
| 459 | + EST_BILL_TOTAL_KVA_PRICE_ATTR_NAME: total_kva_price, |
| 460 | + EST_BILL_KWH_CONSUMPTION_ATTR_NAME: estimated_kwh_consumption |
| 461 | + } |
405 | 462 |
|
406 | 463 | data[str(contract_id)] = {CONTRACT_DICT_NAME: contracts.get(contract_id),
|
407 | 464 | INVOICE_DICT_NAME: last_invoice,
|
|
0 commit comments