Skip to content

Commit c3bfb84

Browse files
authored
Merge pull request #41 from cabberley/multi
Bug Fixes for Calculation sensor value not surviving HA restarts
2 parents ddd1953 + 30affa4 commit c3bfb84

2 files changed

Lines changed: 39 additions & 14 deletions

File tree

custom_components/utility_meter_next_gen/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@
1111
"issue_tracker": "https://github.com/cabberley/utility_meter_evolved/issues",
1212
"requirements": ["cronsim==2.6"],
1313
"ssdp": [],
14-
"version": "2025.7.10",
14+
"version": "2025.7.11",
1515
"zeroconf": []
1616
}

custom_components/utility_meter_next_gen/sensor.py

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,9 @@ class UtilitySensorExtraStoredData(SensorExtraStoredData):
518518
last_valid_state: Decimal | None
519519
status: str
520520
input_device_class: SensorDeviceClass | None
521+
calculated_current_value: Decimal | None
522+
calculated_last_value: Decimal | None
523+
521524

522525
def as_dict(self) -> dict[str, Any]:
523526
"""Return a dict representation of the utility sensor data."""
@@ -530,6 +533,8 @@ def as_dict(self) -> dict[str, Any]:
530533
)
531534
data["status"] = self.status
532535
data["input_device_class"] = str(self.input_device_class)
536+
data["calculated_current_value"] = str(self.calculated_current_value)
537+
data["calculated_last_value"] = str(self.calculated_last_value)
533538

534539
return data
535540

@@ -552,6 +557,17 @@ def from_dict(cls, restored: dict[str, Any]) -> Self | None:
552557
input_device_class = try_parse_enum(
553558
SensorDeviceClass, restored.get("input_device_class")
554559
)
560+
calculated_current_value: Decimal | None = (
561+
Decimal(restored["calculated_current_value"])
562+
if restored.get("calculated_current_value")
563+
else None
564+
)
565+
calculated_last_value: Decimal | None = (
566+
Decimal(restored["calculated_last_value"])
567+
if restored.get("calculated_last_value")
568+
else None
569+
)
570+
555571
except (KeyError, InvalidOperation):
556572
# last_period is corrupted
557573
return None
@@ -564,6 +580,8 @@ def from_dict(cls, restored: dict[str, Any]) -> Self | None:
564580
last_valid_state,
565581
status,
566582
input_device_class,
583+
calculated_current_value,
584+
calculated_last_value,
567585
)
568586

569587

@@ -599,8 +617,8 @@ def __init__(
599617
source_entity,
600618
source_calc_entity,
601619
source_calc_multiplier,
602-
calibrate_value, # =Decimal(0),
603-
calibrate_calc_value, # =Decimal(0),
620+
calibrate_value,
621+
calibrate_calc_value,
604622
tariff_entity,
605623
tariff,
606624
unique_id,
@@ -622,8 +640,8 @@ def __init__(
622640
self._attr_name = name
623641
self._input_device_class = None
624642
self._attr_native_unit_of_measurement = None
625-
self._attr_calculated_current_value = 0
626-
self._attr_calculated_last_value = 0
643+
self._attr_calculated_current_value = Decimal(0)
644+
self._attr_calculated_last_value = Decimal(0)
627645
self._attr_multiplier = source_calc_multiplier or Decimal(1)
628646
self._period = meter_type
629647
if meter_type is not None:
@@ -669,7 +687,6 @@ def start(self, attributes: Mapping[str, Any]) -> None:
669687
self._attr_calculated_current_value = Decimal(
670688
self._calibrate_calc_value
671689
)
672-
self._attr_calculated_last_value = Decimal(0)
673690
self.async_write_ha_state()
674691

675692
@staticmethod
@@ -726,7 +743,7 @@ def async_reading(self, event: Event[EventStateChangedData]) -> None:
726743
self.async_write_ha_state()
727744
return
728745

729-
self._attr_available = True
746+
#self._attr_available = True
730747

731748
old_state = event.data["old_state"]
732749
new_state = event.data["new_state"]
@@ -776,9 +793,6 @@ def async_reading(self, event: Event[EventStateChangedData]) -> None:
776793
self._attr_calculated_current_value += round(
777794
(
778795
Decimal(source_calc_state.state)
779-
# Bug fix with Multi Meters we need to use
780-
# the adjustment and add not just recalc
781-
# Decimal(self._attr_native_value)
782796
* Decimal(adjustment)
783797
* Decimal(self._attr_multiplier)
784798
),
@@ -876,7 +890,7 @@ async def async_reset_meter(self, entity_id):
876890
if self._sensor_calc_source_id is not None:
877891
self._attr_calculated_last_value = self._attr_calculated_current_value
878892
if str(self._tariff).lower() in [SINGLE_TARIFF, TOTAL_TARIFF]:
879-
self._attr_calculated_current_value = self._calibrate_calc_value
893+
self._attr_calculated_current_value = Decimal(self._calibrate_calc_value)
880894
else:
881895
self._attr_calculated_current_value = Decimal(0)
882896
if str(self._tariff).lower() in [SINGLE_TARIFF, TOTAL_TARIFF]:
@@ -923,6 +937,16 @@ async def async_added_to_hass(self) -> None:
923937
if last_sensor_data.status == COLLECTING:
924938
# Null lambda to allow cancelling the collection on tariff change
925939
self._collecting = lambda: None
940+
self._attr_calculated_current_value = (
941+
Decimal(0)
942+
if last_sensor_data.calculated_current_value is None
943+
else Decimal(last_sensor_data.calculated_current_value)
944+
)
945+
self._attr_calculated_last_value = (
946+
Decimal(0)
947+
if last_sensor_data.calculated_last_value is None
948+
else Decimal(last_sensor_data.calculated_last_value)
949+
)
926950

927951
@callback
928952
def async_source_tracking(event):
@@ -1051,6 +1075,8 @@ def extra_restore_state_data(self) -> UtilitySensorExtraStoredData:
10511075
self._last_valid_state,
10521076
PAUSED if self._collecting is None else COLLECTING,
10531077
self._input_device_class,
1078+
self._attr_calculated_current_value,
1079+
self._attr_calculated_last_value,
10541080
)
10551081

10561082
async def async_get_last_sensor_data(self) -> UtilitySensorExtraStoredData | None:
@@ -1110,6 +1136,7 @@ def __init__(
11101136
self._attr_icon = "mdi:currency-usd" if icon is None else icon
11111137
self._attr_name = name
11121138
self._attr_native_unit_of_measurement = CURRENCY_DOLLAR if uom is None else uom
1139+
self._attr_suggested_display_precision = PRECISION
11131140
self._attr_state_class = (
11141141
SensorStateClass.TOTAL if state_class is None else state_class
11151142
)
@@ -1184,8 +1211,6 @@ def _async_attribute_sensor_state_listener(
11841211
new_state = event.data["new_state"]
11851212
_LOGGER.debug("Received new state: %s", new_state)
11861213

1187-
self._attr_available = True
1188-
11891214
self._attr_native_value = None
11901215
if (
11911216
new_state is None
@@ -1200,7 +1225,7 @@ def _async_attribute_sensor_state_listener(
12001225
"State update for %s is None or unavailable, setting to STATE_UNAVAILABLE",
12011226
self._entity_id,
12021227
)
1203-
self._attr_native_value = Decimal(0)
1228+
#self._attr_native_value = STATE_UNAVAILABLE #Decimal(0)
12041229
self._attr_collecting_status = STATE_UNAVAILABLE
12051230
self.async_write_ha_state()
12061231
return

0 commit comments

Comments
 (0)