Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Energy Statistics - Update Week/Month for Partial Time Period #65

Open
Roving-Ronin opened this issue Jan 2, 2025 · 13 comments
Open

Energy Statistics - Update Week/Month for Partial Time Period #65

Roving-Ronin opened this issue Jan 2, 2025 · 13 comments

Comments

@Roving-Ronin
Copy link
Contributor

Hi, @dentra

Currently the energy_statistics sensor only starts counting for the week and month, if the device is running as it changes from Sunday to Monday / from the last day of the month to the first day of the month. So for example if you plug in a power sensor on day 2 of the week/month (or even part way through the first day) it won't update the weekly or monthly sensors, instead you have to wait for another week/month to pass before they start update.

Would you know how to change this, so these sensors all initalize and start couning immediately, like the today sensor?

Thanks.

@Roving-Ronin
Copy link
Contributor Author

Worked it out, in .CPP change the Process code to:

void EnergyStatistics::process_(float total) {
  // Publish today's energy
  if (this->energy_today_ && !std::isnan(this->energy_.start_today)) {
    this->energy_today_->publish_state(total - this->energy_.start_today);
  } else if (std::isnan(this->energy_.start_today)) {
    // Initialize today's start value
    this->energy_.start_today = total;
    if (this->energy_today_) {
      this->energy_today_->publish_state(0);  // Publish initial value as 0
    }
  }

  // Publish yesterday's energy
  if (this->energy_yesterday_ && !std::isnan(this->energy_.start_yesterday)) {
    this->energy_yesterday_->publish_state(this->energy_.start_today - this->energy_.start_yesterday);
  }

  // Publish weekly energy (partial or full)
  if (this->energy_week_ && !std::isnan(this->energy_.start_week)) {
    if (this->energy_.full_week_started) {
      // Publish full calendar week value
      this->energy_week_->publish_state(total - this->energy_.start_week);
    } else {
      // Publish partial week value
      this->energy_week_->publish_state(total - this->energy_.start_week);
    }
  } else if (std::isnan(this->energy_.start_week)) {
    // Initialize start_week for the first time
    this->energy_.start_week = total;
    if (this->energy_week_) {
      this->energy_week_->publish_state(0);  // Publish initial value as 0
    }
  }

  // Publish monthly energy (partial or full)
  if (this->energy_month_ && !std::isnan(this->energy_.start_month)) {
    if (this->energy_.full_month_started) {
      // Publish full calendar month value
      this->energy_month_->publish_state(total - this->energy_.start_month);
    } else {
      // Publish partial month value
      this->energy_month_->publish_state(total - this->energy_.start_month);
    }
  } else if (std::isnan(this->energy_.start_month)) {
    // Initialize start_month for the first time
    this->energy_.start_month = total;
    if (this->energy_month_) {
      this->energy_month_->publish_state(0);  // Publish initial value as 0
    }
  }
 
  // Save the current state
  this->save_();
}

And in the .h change the definitions in energy_data_t to add the full week/month, whilst the existing are used for partial time periods:

  struct energy_data_t {
    uint16_t current_day_of_year{0};
    float start_today{NAN};
    float start_yesterday{NAN};
    float start_week{NAN};
    float start_month{NAN};
    bool full_week_started{false};    // Added
    bool full_month_started{false};  // Added
    bool full_year_started{false};   // Added
  } energy_;  // Instance of the struct

@dentra any interest in this? If so, will create a PULL request for you...?

@dentra
Copy link
Owner

dentra commented Jan 14, 2025

please have a look update at master branch

@Roving-Ronin
Copy link
Contributor Author

You seems to have done it a bit different, looking at energy_statistics.cpp though, are you missing:

    // at first day of year we start a new year calculation
    if (t.day_of_year == 1) {
      this->energy_.start_year = total;
    }

around line 87 -

Also in energy_statistics.h would you need:

  // start day of year configuration
  int energy_year_start_day_{1};

@Roving-Ronin
Copy link
Contributor Author

Roving-Ronin commented Jan 15, 2025

Deleted, replaced with comment below

@Roving-Ronin
Copy link
Contributor Author

Just testing your v2 energy_statistics and using your current 'master' code, it appears NOT to be updating all the sensors.

Edit:

Took a while to update the week / month sensors, but they are now updating. However I note that the Year sensor is NOT updating. I would assume this as the code for year is missing in places within the .cpp and .h

Image

Please refer to PULL 67 for patches: #67


As a side issue, in the past I found that if/when updating the firmware due to a change or new release of ESPHome, all the sensors would lose their data. Given this, in the yaml I've found it best to use the sensors from the custom_components to be 'internal: true' and have a lambda that updates a global. From this I then have corresponding template sensors, that read the globals and publish the values. Using this approach means that the today / yesterday / week / month / year and total sensors are all stored by the globals and this data persists between reflashing/updating firmware.

If of interest, here is the templated yaml that I use for all my Athom Smart Plugs (no relay) models, there's also versions for Smart Plug v2 and v3 (that have relays), see: https://github.com/Roving-Ronin/myHomeAssistant/blob/main/esphome/sensors/athom-power-plug-no-relay.yaml

PS. Probably possible to have this functionality to save to global then publish via sensor built into the custom_component, buy just did it in yaml as it was faster and easier.

@dentra
Copy link
Owner

dentra commented Jan 18, 2025

I do not use year stats, so I might have missed something. Thanks. I will look your PR.

My code uses absolutely the same approach to store data as globals do. Are you using "restore_from_flash: true" for your esp8266? Also "flash_write_interval" may matters when you reflash faster than it was set.

@Roving-Ronin
Copy link
Contributor Author

Yes on ESP8285 I have:

esp8266:
  board: esp8285
  restore_from_flash: true

Whilst ESP8285 and ESP32 both have:

preferences:
   # Enable to allow storing of 'Read Total' between reboots and flashing.
  flash_write_interval: 5min

Still erases at times with reflashing / updating ESPHome versions on them, whereas the Energy Total sensor (that is feed into the component) that is stored as a global remains safe, only wiping with a factory reset of the device.

@Roving-Ronin
Copy link
Contributor Author

Roving-Ronin commented Jan 20, 2025

@dentra
I just updated to the latest version / merge and see 'NA' for week, month & year. (i.e. they aren't updating).
Could you please confirm that your seeing these all update constantly i.e. during partial week / month / year time periods?

@Roving-Ronin
Copy link
Contributor Author

No idea why, but after 1.5 - 2 days the plugs have now started updating and showing the Week / Month kWh readings.
Year readings are still showing 'NA', but have just worked out that this is because the .cpp file at the Process component is missing the task to tell it to update the Year sensor.

I've submitted a PULL to correct this omission: #70

@Roving-Ronin
Copy link
Contributor Author

Roving-Ronin commented Jan 23, 2025

@dentra, This code in the 'EnergyStatistics::loop' would be the cause of why the week/month/year sensors are taking 36-48 hours to first update. This appears to be looking to the value of energy_.start_yesterday and expecting this to be a number, but when its first started its a NAN (showing as NA) value, that is not a valid number that is required. Given this these sensors then fail to initialise for the first day. Its only when the today sensor rolls over and updates the yesterday sensor that the week / month / year sensors are then able to initialise.

  // Intitialize all sensors. https://github.com/dentra/esphome-components/issues/65
  if (this->energy_week_ && std::isnan(this->energy_.start_week)) {
    this->energy_.start_week = this->energy_.start_yesterday;
  }
  if (this->energy_month_ && std::isnan(this->energy_.start_month)) {
    this->energy_.start_month = this->energy_.start_yesterday;
  }
  if (this->energy_year_ && std::isnan(this->energy_.start_year)) {
    this->energy_.start_year = this->energy_.start_yesterday;
  }

@Roving-Ronin
Copy link
Contributor Author

Roving-Ronin commented Jan 23, 2025

As it appears the Yesterday value initially being NAN (NA in web interface) does cause the week / month / year sensors to not update, till the yesterday sensor received a numerical value. Thinking we could simply default Yesterday value to 0 (IF there is no prior value in globals), by using this for the Process component:

void EnergyStatistics::process_(float total) {
  if (this->energy_today_ && !std::isnan(this->energy_.start_today)) {
    this->energy_today_->publish_state(total - this->energy_.start_today);
  }

  if (this->energy_yesterday_ && !std::isnan(this->energy_.start_yesterday)) {
    this->energy_yesterday_->publish_state(this->energy_.start_today - this->energy_.start_yesterday);
  } else if (this->energy_yesterday_) {
    // If there's no value for yesterday (NaN), publish 0
    this->energy_yesterday_->publish_state(0);
  }

  if (this->energy_week_ && !std::isnan(this->energy_.start_week)) {
    this->energy_week_->publish_state(total - this->energy_.start_week);
  } else if (this->energy_week_) {
    // If there's no value for week (NaN), publish 0
    this->energy_week_->publish_state(0);
  }

  if (this->energy_month_ && !std::isnan(this->energy_.start_month)) {
    this->energy_month_->publish_state(total - this->energy_.start_month);
  } else if (this->energy_month_) {
    // If there's no value for month (NaN), publish 0
    this->energy_month_->publish_state(0);
  }

  if (this->energy_year_ && !std::isnan(this->energy_.start_year)) {
    this->energy_year_->publish_state(total - this->energy_.start_year);
  } else if (this->energy_year_) {
    // If there's no value for year (NaN), publish 0
    this->energy_year_->publish_state(0);
  }

  this->pref_.save(&this->energy_);
}

@dentra
Copy link
Owner

dentra commented Jan 26, 2025

Yes, the year, month and week will update on next day, it is by design to optimize calculation time. NaN is showing that this info is unavailable yet.

@Roving-Ronin
Copy link
Contributor Author

Roving-Ronin commented Jan 26, 2025

This PULL still needs to be merged anyhow, as your PROCESS component is missing the code for Year.

#70

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants