<commit_before>#pragma once

#include "wled.h"

// v2 Usermod to automatically save settings 
// to configurable preset after a change to any of
//
// * brightness
// * effect speed
// * effect intensity
// * mode (effect)
// * palette
//
// but it will wait for configurable number of seconds, a "settle" 
// period in case there are other changes (any change will 
// extend the "settle" window).
//
// It can be configured to load auto saved preset at startup,
// during the first `loop()`.
//
// AutoSaveUsermod is standalone, but if FourLineDisplayUsermod 
// is installed, it will notify the user of the saved changes.

// format: "~ MM-DD HH:MM:SS ~"
#define PRESET_NAME_BUFFER_SIZE 25

// strings
const char _um_AutoSave[]         PROGMEM = "Autosave";
const char _autoSaveAfterSec[]    PROGMEM = "autoSaveAfterSec";
const char _autoSavePreset[]      PROGMEM = "autoSavePreset";
const char _autoSaveApplyOnBoot[] PROGMEM = "autoSaveApplyOnBoot";

class AutoSaveUsermod : public Usermod {

  private:

    bool firstLoop = true;
    bool initDone = false;

    // configurable parameters
    unsigned long autoSaveAfterSec = 15;  // 15s by default
    uint8_t autoSavePreset = 250;         // last possible preset
    bool applyAutoSaveOnBoot = false;     // do we load auto-saved preset on boot?

    // If we've detected the need to auto save, this will be non zero.
    unsigned long autoSaveAfter = 0;

    uint8_t knownBrightness = 0;
    uint8_t knownEffectSpeed = 0;
    uint8_t knownEffectIntensity = 0;
    uint8_t knownMode = 0;
    uint8_t knownPalette = 0;

    #ifdef USERMOD_FOUR_LINE_DISPLAY
    FourLineDisplayUsermod* display;
    #endif

    void inline saveSettings() {
      char presetNameBuffer[PRESET_NAME_BUFFER_SIZE];
      updateLocalTime();
      sprintf_P(presetNameBuffer, 
        PSTR("~ %02d-%02d %02d:%02d:%02d ~"),
        month(localTime), day(localTime),
        hour(localTime), minute(localTime), second(localTime));
      savePreset(autoSavePreset, true, presetNameBuffer);
    }

    void inline displayOverlay() {
      #ifdef USERMOD_FOUR_LINE_DISPLAY
      if (display != nullptr) {
        display->wakeDisplay();
        display->overlay("Settings", "Auto Saved", 1500);
      }
      #endif
    }

  public:

    // gets called once at boot. Do all initialization that doesn't depend on
    // network here
    void setup() {
      #ifdef USERMOD_FOUR_LINE_DISPLAY    
      // This Usermod has enhanced funcionality if
      // FourLineDisplayUsermod is available.
      display = (FourLineDisplayUsermod*) usermods.lookup(USERMOD_ID_FOUR_LINE_DISP);
      #endif
      initDone = true;
    }

    // gets called every time WiFi is (re-)connected. Initialize own network
    // interfaces here
    void connected() {}

    /*
     * Da loop.
     */
    void loop() {
      if (!autoSaveAfterSec) return;  // setting 0 as autosave seconds disables autosave

      unsigned long now = millis();
      uint8_t currentMode = strip.getMode();
      uint8_t currentPalette = strip.getSegment(0).palette;
      if (firstLoop) {
        firstLoop = false;
        if (applyAutoSaveOnBoot) applyPreset(autoSavePreset);
        knownBrightness = bri;
        knownEffectSpeed = effectSpeed;
        knownEffectIntensity = effectIntensity;
        knownMode = currentMode;
        knownPalette = currentPalette;
        return;
      }

      unsigned long wouldAutoSaveAfter = now + autoSaveAfterSec*1000;
      if (knownBrightness != bri) {
        knownBrightness = bri;
        autoSaveAfter = wouldAutoSaveAfter;
      } else if (knownEffectSpeed != effectSpeed) {
        knownEffectSpeed = effectSpeed;
        autoSaveAfter = wouldAutoSaveAfter;
      } else if (knownEffectIntensity != effectIntensity) {
        knownEffectIntensity = effectIntensity;
        autoSaveAfter = wouldAutoSaveAfter;
      } else if (knownMode != currentMode) {
        knownMode = currentMode;
        autoSaveAfter = wouldAutoSaveAfter;
      } else if (knownPalette != currentPalette) {
        knownPalette = currentPalette;
        autoSaveAfter = wouldAutoSaveAfter;
      }

      if (autoSaveAfter && now > autoSaveAfter) {
        autoSaveAfter = 0;
        // Time to auto save. You may have some flickry?
        saveSettings();
        displayOverlay();
      }
    }

    /*
     * addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
     * Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.
     * Below it is shown how this could be used for e.g. a light sensor
     */
    //void addToJsonInfo(JsonObject& root) {
      //JsonObject user = root["u"];
      //if (user.isNull()) user = root.createNestedObject("u");
      //JsonArray data = user.createNestedArray(F("Autosave"));
      //data.add(F("Loaded."));
    //}

    /*
     * addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
     * Values in the state object may be modified by connected clients
     */
    //void addToJsonState(JsonObject& root) {
    //}

    /*
     * readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
     * Values in the state object may be modified by connected clients
     */
    void readFromJsonState(JsonObject& root) {
      if (!initDone) return;  // prevent crash on boot applyPreset()

      if (root[F("Autosave_autoSaveAfterSec")] != nullptr) {
        autoSaveAfterSec = min(60,max(0,(int)root[F("Autosave_autoSaveAfterSec")]));
      }
      if (root[F("Autosave_autoSavePreset")] != nullptr) {
        autoSavePreset = min(250,max(0,(int)root[F("Autosave_autoSavePreset")]));
      }
      if (root[F("Autosave_autoSaveApplyOnBoot")] != nullptr) {
        applyAutoSaveOnBoot = (bool)root[F("Autosave_autoSaveApplyOnBoot")];
      }
    }<commit_msg>Fix for AutoSave checkbox not saving.<commit_after>#pragma once

#include "wled.h"

// v2 Usermod to automatically save settings 
// to configurable preset after a change to any of
//
// * brightness
// * effect speed
// * effect intensity
// * mode (effect)
// * palette
//
// but it will wait for configurable number of seconds, a "settle" 
// period in case there are other changes (any change will 
// extend the "settle" window).
//
// It can be configured to load auto saved preset at startup,
// during the first `loop()`.
//
// AutoSaveUsermod is standalone, but if FourLineDisplayUsermod 
// is installed, it will notify the user of the saved changes.

// format: "~ MM-DD HH:MM:SS ~"
#define PRESET_NAME_BUFFER_SIZE 25

// strings
const char _um_AutoSave[]         PROGMEM = "Autosave";
const char _autoSaveAfterSec[]    PROGMEM = "autoSaveAfterSec";
const char _autoSavePreset[]      PROGMEM = "autoSavePreset";
const char _autoSaveApplyOnBoot[] PROGMEM = "autoSaveApplyOnBoot";

class AutoSaveUsermod : public Usermod {

  private:

    bool firstLoop = true;
    bool initDone = false;

    // configurable parameters
    unsigned long autoSaveAfterSec = 15;  // 15s by default
    uint8_t autoSavePreset = 250;         // last possible preset
    bool applyAutoSaveOnBoot = false;     // do we load auto-saved preset on boot?

    // If we've detected the need to auto save, this will be non zero.
    unsigned long autoSaveAfter = 0;

    uint8_t knownBrightness = 0;
    uint8_t knownEffectSpeed = 0;
    uint8_t knownEffectIntensity = 0;
    uint8_t knownMode = 0;
    uint8_t knownPalette = 0;

    #ifdef USERMOD_FOUR_LINE_DISPLAY
    FourLineDisplayUsermod* display;
    #endif

    void inline saveSettings() {
      char presetNameBuffer[PRESET_NAME_BUFFER_SIZE];
      updateLocalTime();
      sprintf_P(presetNameBuffer, 
        PSTR("~ %02d-%02d %02d:%02d:%02d ~"),
        month(localTime), day(localTime),
        hour(localTime), minute(localTime), second(localTime));
      savePreset(autoSavePreset, true, presetNameBuffer);
    }

    void inline displayOverlay() {
      #ifdef USERMOD_FOUR_LINE_DISPLAY
      if (display != nullptr) {
        display->wakeDisplay();
        display->overlay("Settings", "Auto Saved", 1500);
      }
      #endif
    }

  public:

    // gets called once at boot. Do all initialization that doesn't depend on
    // network here
    void setup() {
      #ifdef USERMOD_FOUR_LINE_DISPLAY    
      // This Usermod has enhanced funcionality if
      // FourLineDisplayUsermod is available.
      display = (FourLineDisplayUsermod*) usermods.lookup(USERMOD_ID_FOUR_LINE_DISP);
      #endif
      initDone = true;
    }

    // gets called every time WiFi is (re-)connected. Initialize own network
    // interfaces here
    void connected() {}

    /*
     * Da loop.
     */
    void loop() {
      if (!autoSaveAfterSec) return;  // setting 0 as autosave seconds disables autosave

      unsigned long now = millis();
      uint8_t currentMode = strip.getMode();
      uint8_t currentPalette = strip.getSegment(0).palette;
      if (firstLoop) {
        firstLoop = false;
        if (applyAutoSaveOnBoot) applyPreset(autoSavePreset);
        knownBrightness = bri;
        knownEffectSpeed = effectSpeed;
        knownEffectIntensity = effectIntensity;
        knownMode = currentMode;
        knownPalette = currentPalette;
        return;
      }

      unsigned long wouldAutoSaveAfter = now + autoSaveAfterSec*1000;
      if (knownBrightness != bri) {
        knownBrightness = bri;
        autoSaveAfter = wouldAutoSaveAfter;
      } else if (knownEffectSpeed != effectSpeed) {
        knownEffectSpeed = effectSpeed;
        autoSaveAfter = wouldAutoSaveAfter;
      } else if (knownEffectIntensity != effectIntensity) {
        knownEffectIntensity = effectIntensity;
        autoSaveAfter = wouldAutoSaveAfter;
      } else if (knownMode != currentMode) {
        knownMode = currentMode;
        autoSaveAfter = wouldAutoSaveAfter;
      } else if (knownPalette != currentPalette) {
        knownPalette = currentPalette;
        autoSaveAfter = wouldAutoSaveAfter;
      }

      if (autoSaveAfter && now > autoSaveAfter) {
        autoSaveAfter = 0;
        // Time to auto save. You may have some flickry?
        saveSettings();
        displayOverlay();
      }
    }

    /*
     * addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
     * Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.
     * Below it is shown how this could be used for e.g. a light sensor
     */
    //void addToJsonInfo(JsonObject& root) {
      //JsonObject user = root["u"];
      //if (user.isNull()) user = root.createNestedObject("u");
      //JsonArray data = user.createNestedArray(F("Autosave"));
      //data.add(F("Loaded."));
    //}

    /*
     * addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
     * Values in the state object may be modified by connected clients
     */
    //void addToJsonState(JsonObject& root) {
    //}

    /*
     * readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
     * Values in the state object may be modified by connected clients
     */
    void readFromJsonState(JsonObject& root) {
      if (!initDone) return;  // prevent crash on boot applyPreset()

      if (root[F("Autosave_autoSaveAfterSec")] != nullptr) {
        autoSaveAfterSec = min(60,max(0,(int)root[F("Autosave_autoSaveAfterSec")]));
      }
      if (root[F("Autosave_autoSavePreset")] != nullptr) {
        autoSavePreset = min(250,max(0,(int)root[F("Autosave_autoSavePreset")]));
      }
      if (root[F("Autosave_autoSaveApplyOnBoot")] != nullptr) {
        String str = root[F("Autosave_autoSaveApplyOnBoot")]; // checkbox -> off or on
        applyAutoSaveOnBoot = (bool)(str!="off"); // off is guaranteed to be present
      }
    }
{'fraction_non_alphanumeric': 0.0997403902520727, 'fraction_numerical': 0.008709488317561343, 'mean_word_length': 2.7707609725292075, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 10, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7584928', 'n_tokens_mistral': 3634, 'n_tokens_neox': 3516, 'n_words': 1056}