Skip to content

Commit c21e344

Browse files
committed
updates to number sensor and const
1 parent 7043d91 commit c21e344

3 files changed

Lines changed: 74 additions & 59 deletions

File tree

custom_components/monitormysolar/const.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -232,12 +232,6 @@
232232
ENTITIES = {
233233
"Lux": {
234234
"sensor": {
235-
"calculated": [
236-
{"name": "Battery Time to Empty", "type": "sensor", "unique_id": "battery_time_empty", "state_class": SensorStateClass.MEASUREMENT, "device_class": SensorDeviceClass.DURATION, "unit_of_measurement": UnitOfTime.HOURS, "calculation": {"operation": "battery_time", "sensors": ["batcapacity", "vbat", "pload", "batteryflow_live", "soc", "pall"], "attributes": ["calculated_kwh_storage_total", "calculated_kwh_left", "time_battery_empty", "human_readable_time_left"]}},
237-
{"name": "PV1 Current", "type": "sensor", "unique_id": "ipv1", "state_class": SensorStateClass.MEASUREMENT, "device_class": SensorDeviceClass.CURRENT, "unit_of_measurement": UnitOfElectricCurrent.AMPERE, "calculation": {"operation": "division", "sensors": ["ppv1", "vpv1"]}, "allowed_firmware_codes": ["AAAA", "AAAB", "FAAA", "FAAB", "EAAA", "EAAB", "ccaa"]},
238-
{"name": "PV2 Current", "type": "sensor", "unique_id": "ipv2", "state_class": SensorStateClass.MEASUREMENT, "device_class": SensorDeviceClass.CURRENT, "unit_of_measurement": UnitOfElectricCurrent.AMPERE, "calculation": {"operation": "division", "sensors": ["ppv2", "vpv2"]}, "allowed_firmware_codes": ["AAAA", "AAAB", "FAAA", "FAAB", "EAAA", "EAAB", "ccaa"]},
239-
{"name": "PV3 Current", "type": "sensor", "unique_id": "ipv3", "state_class": SensorStateClass.MEASUREMENT, "device_class": SensorDeviceClass.CURRENT, "unit_of_measurement": UnitOfElectricCurrent.AMPERE, "calculation": {"operation": "division", "sensors": ["ppv3", "vpv3"]}, "allowed_firmware_codes": ["FAAB","FAAA", "FAAB", "EAAA", "EAAB"]},
240-
],
241235
"status": [
242236
{"name": "Uptime Sensor", "type": "sensor", "unique_id": "uptime", "attributes":["status", "freeheap", "minfreeheap", "stackusage", "HA_State_MQTT", "Web_State_MQTT", "Web_Error", "HA_Error"] },
243237

@@ -815,6 +809,12 @@
815809
{"name": "ACcouple4 End2", "type": "time", "unique_id": "ACcouple4End2", "allowed_firmware_codes": ["IAAB"]},
816810
],
817811
},
812+
"calculated": [
813+
{"name": "Battery Time to Empty", "type": "sensor", "unique_id": "battery_time_empty", "state_class": SensorStateClass.MEASUREMENT, "device_class": SensorDeviceClass.DURATION, "unit_of_measurement": UnitOfTime.HOURS, "calculation": {"operation": "battery_time", "sensors": ["batcapacity", "vbat", "pload", "peps", "batteryflow_live", "soc", "pall"], "attributes": ["calculated_kwh_storage_total", "calculated_kwh_left", "time_battery_empty", "human_readable_time_left"]}},
814+
{"name": "PV1 Current", "type": "sensor", "unique_id": "ipv1", "state_class": SensorStateClass.MEASUREMENT, "device_class": SensorDeviceClass.CURRENT, "unit_of_measurement": UnitOfElectricCurrent.AMPERE, "calculation": {"operation": "division", "sensors": ["ppv1", "vpv1"]}, "allowed_firmware_codes": ["AAAA", "AAAB", "FAAA", "FAAB", "EAAA", "EAAB", "ccaa", "HAAA", "ceaa"]},
815+
{"name": "PV2 Current", "type": "sensor", "unique_id": "ipv2", "state_class": SensorStateClass.MEASUREMENT, "device_class": SensorDeviceClass.CURRENT, "unit_of_measurement": UnitOfElectricCurrent.AMPERE, "calculation": {"operation": "division", "sensors": ["ppv2", "vpv2"]}, "allowed_firmware_codes": ["AAAA", "AAAB", "FAAA", "FAAB", "EAAA", "EAAB", "ccaa", "HAAA", "ceaa"]},
816+
{"name": "PV3 Current", "type": "sensor", "unique_id": "ipv3", "state_class": SensorStateClass.MEASUREMENT, "device_class": SensorDeviceClass.CURRENT, "unit_of_measurement": UnitOfElectricCurrent.AMPERE, "calculation": {"operation": "division", "sensors": ["ppv3", "vpv3"]}, "allowed_firmware_codes": ["FAAB","FAAA", "FAAB", "EAAA", "EAAB", "HAAA","ceaa"]},
817+
],
818818
"update": {
819819
"system": [
820820
{

custom_components/monitormysolar/number.py

Lines changed: 63 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,6 @@ def __init__(self, entity_info, hass, entry: MonitorMySolarEntry, bank_name, don
104104
self._attr_native_unit_of_measurement = entity_info.get("unit", None)
105105
self._attr_device_class = entity_info.get("class", None)
106106
self._manufacturer = entry.data.get("inverter_brand")
107-
self._previous_value = self._attr_native_value # Track the previous value for revert
108107

109108
super().__init__(self.coordinator)
110109

@@ -131,43 +130,44 @@ def name(self):
131130
async def async_set_native_value(self, value):
132131
"""Set the number value."""
133132
LOGGER.debug(f"Setting value of number {self.entity_id} to {value}")
134-
133+
135134
# Check if entity should be available based on conditional settings
136135
availability_info = self.coordinator.get_entity_availability_info(self._dongle_id, self._entity_type)
137136
if not availability_info["available"] and availability_info["reason"]:
138137
raise HomeAssistantError(availability_info["reason"])
139-
138+
140139
mqtt_handler = self.coordinator.mqtt_handler
141-
if mqtt_handler is not None:
142-
# Save the current value before changing
143-
self._previous_value = self._attr_native_value
144-
# Set the new value
145-
self._attr_native_value = value
146-
self.throttled_async_write_ha_state()
140+
if mqtt_handler is None:
141+
raise HomeAssistantError("MQTT Handler is not initialized")
147142

148-
# Send the update via MQTT
149-
success = await mqtt_handler.send_update(
150-
self._dongle_id,
151-
self.entity_info["unique_id"],
152-
value,
153-
self,
154-
)
155-
if not success:
156-
self.revert_state()
157-
else:
158-
LOGGER.error("MQTT Handler is not initialized")
143+
# Save old value in case we need to revert
144+
old_value = self._attr_native_value
159145

160-
def revert_state(self):
161-
"""Revert to the previous state."""
162-
LOGGER.info(f"Reverting state for {self.entity_id} to {self._previous_value}")
163-
self._attr_native_value = self._previous_value
164-
self.hass.loop.call_soon_threadsafe(self.throttled_async_write_ha_state)
146+
# Set the new value optimistically for immediate UI feedback
147+
self._attr_native_value = value
148+
# Update coordinator's stored value so it doesn't overwrite us
149+
self.coordinator.entities[self.entity_id] = value
150+
self.throttled_async_write_ha_state()
151+
152+
# Send the update via MQTT and wait for response
153+
success = await mqtt_handler.send_update(
154+
self._dongle_id,
155+
self.entity_info["unique_id"],
156+
value,
157+
self,
158+
)
159+
160+
# If MQTT update failed, revert both UI and coordinator
161+
if not success:
162+
LOGGER.error(f"Failed to update {self.entity_id} to {value}, reverting to {old_value}")
163+
self._attr_native_value = old_value
164+
self.coordinator.entities[self.entity_id] = old_value
165+
self.throttled_async_write_ha_state()
166+
raise HomeAssistantError(f"Failed to update {self.entity_id} - no response from inverter")
165167

166168
@callback
167169
def _handle_coordinator_update(self) -> None:
168170
"""Update sensor with latest data from coordinator."""
169-
170-
171171
if self.entity_id in self.coordinator.entities:
172172
value = self.coordinator.entities[self.entity_id]
173173
if value is not None:
@@ -203,8 +203,7 @@ def __init__(self, entity_info, hass, entry: MonitorMySolarEntry, dongle_ids):
203203
self._attr_native_unit_of_measurement = entity_info.get("unit", None)
204204
self._attr_device_class = entity_info.get("class", None)
205205
self._manufacturer = entry.data.get("inverter_brand")
206-
self._previous_value = self._attr_native_value # Track the previous value for revert
207-
206+
208207
# Track source entities that we need to monitor
209208
self._tracked_entities = []
210209
self._source_values = {}
@@ -252,19 +251,19 @@ def async_state_changed_listener(event: Event) -> None:
252251

253252
async def _update_combined_state(self):
254253
"""Update the combined state based on source entities.
255-
254+
256255
Takes the average of all current values.
257256
"""
258257
# Filter out None values
259258
values = [v for v in self._source_values.values() if v is not None]
260-
259+
261260
if not values:
262261
LOGGER.debug(f"No values available for combined number {self._name}")
263262
return
264-
263+
265264
# Take the average for the display state
266265
avg_value = sum(values) / len(values)
267-
266+
268267
if avg_value != self._attr_native_value:
269268
self._attr_native_value = avg_value
270269
self.throttled_async_write_ha_state()
@@ -304,27 +303,38 @@ def device_info(self):
304303
async def async_set_native_value(self, value):
305304
"""Set the value on all dongles."""
306305
mqtt_handler = self.coordinator.mqtt_handler
307-
if mqtt_handler is not None:
308-
# Save the current value before changing
309-
self._previous_value = self._attr_native_value
310-
# Set the new value
311-
self._attr_native_value = value
306+
if mqtt_handler is None:
307+
raise HomeAssistantError("MQTT Handler is not initialized")
308+
309+
# Save old value in case we need to revert
310+
old_value = self._attr_native_value
311+
312+
# Set the new value optimistically for immediate UI feedback
313+
self._attr_native_value = value
314+
# Update all source values to prevent them from overwriting us
315+
for entity_id in self._source_values.keys():
316+
self._source_values[entity_id] = value
317+
self.throttled_async_write_ha_state()
318+
319+
LOGGER.info(f"Setting Combined Number value for {self.entity_id} to {value} across {len(self._dongle_ids)} dongles")
320+
success = await mqtt_handler.send_update_to_multiple_dongles(
321+
self._dongle_ids, self._source_entity, value, self
322+
)
323+
324+
# If MQTT update failed, revert and raise error
325+
if not success:
326+
LOGGER.error(f"Failed to update {self.entity_id} to {value}, reverting to {old_value}")
327+
self._attr_native_value = old_value
328+
# Revert source values - they'll update from coordinator naturally
329+
for entity_id in self._source_values.keys():
330+
state = self.hass.states.get(entity_id)
331+
if state:
332+
try:
333+
self._source_values[entity_id] = float(state.state)
334+
except (ValueError, TypeError):
335+
pass
312336
self.throttled_async_write_ha_state()
313-
314-
LOGGER.info(f"Setting Combined Number value for {self.entity_id} to {value} across {len(self._dongle_ids)} dongles")
315-
success = await mqtt_handler.send_update_to_multiple_dongles(
316-
self._dongle_ids, self._source_entity, value, self
317-
)
318-
if not success:
319-
self.revert_state()
320-
else:
321-
LOGGER.error("MQTT Handler is not initialized")
322-
323-
def revert_state(self):
324-
"""Revert to the previous state."""
325-
LOGGER.info(f"Reverting state for {self.entity_id} to {self._previous_value}")
326-
self._attr_native_value = self._previous_value
327-
self.hass.loop.call_soon_threadsafe(self.throttled_async_write_ha_state)
337+
raise HomeAssistantError(f"Failed to update {self.entity_id} - no response from inverter")
328338

329339
@property
330340
def extra_state_attributes(self):

custom_components/monitormysolar/sensor.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -826,10 +826,15 @@ def _calculate_state(self):
826826
capacity_ah = self._sensor_values.get("batcapacity", 0)
827827
voltage = self._sensor_values.get("vbat", 0)
828828
load_watts = self._sensor_values.get("pload", 0)
829+
eps_watts = self._sensor_values.get("peps", 0)
829830
battery_flow = self._sensor_values.get("batteryflow_live", 0)
830831
soc = self._sensor_values.get("soc", 0)
831832
pv_power = self._sensor_values.get("pall", 0)
832833

834+
# Use peps if pload is 0 (grid is down, load is on EPS)
835+
if load_watts == 0 and eps_watts > 0:
836+
load_watts = eps_watts
837+
833838
if capacity_ah > 0 and voltage > 0 and soc > 0:
834839
usable_energy_wh = (capacity_ah * voltage) * (soc / 100)
835840
net_load = max(load_watts - pv_power, 0)

0 commit comments

Comments
 (0)