@@ -218,10 +218,16 @@ def __init__(self, coordinator) -> None:
218218 self ._last_power : float | None = None
219219 self ._last_update : dt_util .datetime | None = None
220220 self ._total_energy : float = 0.0
221+ self ._initialized : bool = False
221222
222223 def _get_server_energy_usage (self , data : dict ) -> float | None :
223224 """Get cumulative server energy usage from UPS."""
224225 try :
226+ # Initialize from previous state if not done yet
227+ if not self ._initialized :
228+ self ._restore_state ()
229+ self ._initialized = True
230+
225231 ups_info = data .get ("system_stats" , {}).get ("ups_info" , {})
226232 nominal_power = self ._validate_ups_metric (
227233 "NOMPOWER" ,
@@ -235,44 +241,123 @@ def _get_server_energy_usage(self, data: dict) -> float | None:
235241 current_power = self ._calculate_power (nominal_power , load_percent )
236242 current_time = dt_util .now ()
237243
238- if current_power is not None and self ._last_power is not None and self ._last_update is not None :
239- # Calculate time difference in hours
240- time_diff = (current_time - self ._last_update ).total_seconds () / 3600.0
241-
242- # Calculate average power during this period
243- avg_power = (current_power + self ._last_power ) / 2.0
244-
245- # Calculate energy consumed (kWh)
246- energy_consumed = (avg_power * time_diff ) / 1000.0 # Convert W*h to kWh
247-
248- # Add to total energy
249- self ._total_energy += energy_consumed
244+ # Only calculate energy if we have valid current power
245+ if current_power is not None :
246+ # If we have previous data, calculate energy consumed since last update
247+ if self ._last_power is not None and self ._last_update is not None :
248+ # Calculate time difference in hours
249+ time_diff = (current_time - self ._last_update ).total_seconds () / 3600.0
250+
251+ # Only calculate if time difference is reasonable (minimum 10 seconds, maximum 2 hours)
252+ if 0.0028 <= time_diff <= 2.0 : # 10 seconds to 2 hours
253+ # Calculate average power during this period
254+ avg_power = (current_power + self ._last_power ) / 2.0
255+
256+ # Calculate energy consumed (kWh)
257+ energy_consumed = (avg_power * time_diff ) / 1000.0 # Convert W*h to kWh
258+
259+ # Add to total energy
260+ self ._total_energy += energy_consumed
261+
262+ _LOGGER .debug (
263+ "UPS energy calculation: avg_power=%.2fW, time_diff=%.4fh, "
264+ "energy_consumed=%.6fkWh, total_energy=%.6fkWh" ,
265+ avg_power , time_diff , energy_consumed , self ._total_energy
266+ )
267+ elif time_diff > 0 :
268+ # For very small time differences, still accumulate but with minimal energy
269+ if time_diff < 0.0028 : # Less than 10 seconds
270+ # Use minimum time difference to avoid division by zero but still accumulate
271+ min_time_diff = 0.0028 # 10 seconds
272+ avg_power = (current_power + self ._last_power ) / 2.0
273+ energy_consumed = (avg_power * min_time_diff ) / 1000.0
274+ self ._total_energy += energy_consumed
275+
276+ _LOGGER .debug (
277+ "UPS energy calculation (short interval): avg_power=%.2fW, "
278+ "actual_time_diff=%.6fh, used_time_diff=%.4fh, "
279+ "energy_consumed=%.6fkWh, total_energy=%.6fkWh" ,
280+ avg_power , time_diff , min_time_diff , energy_consumed , self ._total_energy
281+ )
282+ else :
283+ _LOGGER .debug (
284+ "UPS energy: skipping calculation due to unusual time difference: %.4fh" ,
285+ time_diff
286+ )
287+ else :
288+ _LOGGER .debug (
289+ "UPS energy: skipping calculation due to negative time difference: %.6fh" ,
290+ time_diff
291+ )
250292
251- # Update tracking variables
252- self ._last_power = current_power
253- self ._last_update = current_time
293+ # Update tracking variables
294+ self ._last_power = current_power
295+ self ._last_update = current_time
254296
255297 return round (self ._total_energy , 3 ) if self ._total_energy > 0 else 0.0
256298
257299 except (KeyError , TypeError ) as err :
258300 _LOGGER .debug ("Error getting server energy usage: %s" , err )
259301 return None
260302
303+ def _restore_state (self ) -> None :
304+ """Restore previous energy state from Home Assistant registry."""
305+ try :
306+ if self .hass and hasattr (self .hass .states , 'get' ):
307+ previous_state = self .hass .states .get (self .entity_id )
308+ if previous_state and previous_state .state not in ('unknown' , 'unavailable' ):
309+ try :
310+ self ._total_energy = float (previous_state .state )
311+ _LOGGER .debug (
312+ "UPS energy sensor restored previous state: %.3f kWh" ,
313+ self ._total_energy
314+ )
315+ except (ValueError , TypeError ):
316+ _LOGGER .debug ("Could not restore UPS energy state, starting from 0" )
317+ self ._total_energy = 0.0
318+ else :
319+ _LOGGER .debug ("No previous UPS energy state found, starting from 0" )
320+ self ._total_energy = 0.0
321+ except Exception as err :
322+ _LOGGER .debug ("Error restoring UPS energy state: %s" , err )
323+ self ._total_energy = 0.0
324+
261325 @property
262326 def extra_state_attributes (self ) -> dict [str , Any ]:
263327 """Return additional state attributes."""
264328 try :
265329 ups_info = self .coordinator .data .get ("system_stats" , {}).get ("ups_info" , {})
266330
331+ # Base attributes with proper capitalization
267332 attrs = {
268- "ups_model" : ups_info .get ("MODEL" , "Unknown" ),
269- "rated_power" : f"{ ups_info .get ('NOMPOWER' , '0' )} W" ,
270- "current_power" : f"{ self ._last_power or 0 } W" ,
271- "last_updated" : dt_util .now ().isoformat (),
272- "energy_dashboard_ready" : True ,
273- "measurement_type" : "cumulative_energy" ,
333+ "UPS Model" : ups_info .get ("MODEL" , "Unknown" ),
334+ "Rated Power" : f"{ ups_info .get ('NOMPOWER' , '0' )} W" ,
335+ "Current Power" : f"{ self ._last_power or 0 :.1f} W" ,
336+ "Last Updated" : dt_util .now ().isoformat (),
337+ "Energy Dashboard Ready" : True ,
274338 }
275339
340+ # Add useful UPS status information
341+ load_pct = ups_info .get ("LOADPCT" )
342+ if load_pct :
343+ attrs ["Load" ] = f"{ load_pct } %"
344+
345+ battery_charge = ups_info .get ("BCHARGE" )
346+ if battery_charge :
347+ attrs ["Battery" ] = f"{ battery_charge } %"
348+
349+ runtime = ups_info .get ("TIMELEFT" )
350+ if runtime :
351+ attrs ["Runtime" ] = f"{ runtime } minutes"
352+
353+ status = ups_info .get ("STATUS" )
354+ if status :
355+ attrs ["Status" ] = status
356+
357+ line_voltage = ups_info .get ("LINEV" )
358+ if line_voltage :
359+ attrs ["Line Voltage" ] = f"{ line_voltage } V"
360+
276361 return attrs
277362
278363 except (KeyError , TypeError ) as err :
0 commit comments