From e44e72231530a818f1c949ca3b90fa7c581ebfeb Mon Sep 17 00:00:00 2001
From: rejoe2 <rejoe2@users.noreply.github.com>
Date: Mon, 15 Jan 2024 13:09:23 +0100
Subject: [PATCH 1/6] MI-MQTT and last retransmit

- fix MI not sending data over MQTT (discord https://discord.com/channels/984173303147155506/1144045244166443159/1196092306873397351)
- change logic for last retransmit (discord https://discord.com/channels/984173303147155506/1144045244166443159/1196098984167608360)
---
 src/hm/CommQueue.h     |  6 +++---
 src/hm/Communication.h | 31 +++++++++++++++++++------------
 2 files changed, 22 insertions(+), 15 deletions(-)

diff --git a/src/hm/CommQueue.h b/src/hm/CommQueue.h
index 4e73d53a..d62efe07 100644
--- a/src/hm/CommQueue.h
+++ b/src/hm/CommQueue.h
@@ -12,9 +12,9 @@
 #include "../utils/dbg.h"
 
 // needs a '+1' because the comparison does not send if attempts is equal 0
-#define DEFAULT_ATTEMPS                 5 + 1
-#define MORE_ATTEMPS_ALARMDATA          15 + 1
-#define MORE_ATTEMPS_GRIDONPROFILEPARA  15 + 1
+#define DEFAULT_ATTEMPS                 5
+#define MORE_ATTEMPS_ALARMDATA          15
+#define MORE_ATTEMPS_GRIDONPROFILEPARA  15
 
 template <uint8_t N=100>
 class CommQueue {
diff --git a/src/hm/Communication.h b/src/hm/Communication.h
index a8a46e1a..5f14a5af 100644
--- a/src/hm/Communication.h
+++ b/src/hm/Communication.h
@@ -178,11 +178,11 @@ class Communication : public CommQueue<> {
                             yield();
                         }
 
-                        if(0 == q->attempts) {
+                        /*if(0 == q->attempts) {
                             DPRINT_IVID(DBG_INFO, q->iv->id);
                             DBGPRINT(F("no attempts left"));
                             closeRequest(q, false);
-                        } else {
+                        } else {*/
                             if(q->iv->ivGen != IV_MI) {
                                 mState = States::CHECK_PACKAGE;
                             } else {
@@ -207,7 +207,7 @@ class Communication : public CommQueue<> {
                                         closeRequest(q, true);
                                 }
                             }
-                        }
+                        //}
 
                         }
                         break;
@@ -235,6 +235,12 @@ class Communication : public CommQueue<> {
                         }
 
                         if(framnr) {
+                            if(0 == q->attempts) {
+                                DPRINT_IVID(DBG_INFO, q->iv->id);
+                                DBGPRINT(F("no attempts left"));
+                                closeRequest(q, false);
+                                return;
+                            }
                             setAttempt();
 
                             if(*mSerialDebug) {
@@ -516,15 +522,15 @@ class Communication : public CommQueue<> {
         }
 
         void sendRetransmit(const queue_s *q, uint8_t i) {
-            if(q->attempts) {
+            //if(q->attempts) {
                 q->iv->radio->sendCmdPacket(q->iv, TX_REQ_INFO, (SINGLE_FRAME + i), true);
                 q->iv->radioStatistics.retransmits++;
                 mWaitTime.startTimeMonitor(SINGLEFR_TIMEOUT); // timeout
                 mState = States::WAIT;
-            } else {
+            /*} else {
                 //add(q, true);
                 closeRequest(q, false);
-            }
+            }*/
         }
 
     private:
@@ -604,6 +610,8 @@ class Communication : public CommQueue<> {
                 rec->ts = q->ts;
                 q->iv->setValue(1, rec, (uint32_t) ((p->packet[24] << 8) + p->packet[25])/1);
                 q->iv->miMultiParts +=4;
+                rec->mqttSentStatus = MqttSentStatus::NEW_DATA;
+
             } else if ( p->packet[9] == 0x01 || p->packet[9] == 0x10 ) {//second frame for MI, 3rd gen. answers in 0x10
                 DPRINT_IVID(DBG_INFO, q->iv->id);
                 if ( p->packet[9] == 0x01 ) {
@@ -620,6 +628,7 @@ class Communication : public CommQueue<> {
                     record_t<> *rec = q->iv->getRecordStruct(InverterDevInform_Simple);  // choose the record structure
                     rec->ts = q->ts;
                     q->iv->setValue(0, rec, (uint32_t) ((((p->packet[10] << 8) | p->packet[11]) << 8 | p->packet[12]) << 8 | p->packet[13])/1);
+                    rec->mqttSentStatus = MqttSentStatus::NEW_DATA;
 
                     if(*mSerialDebug) {
                         DPRINT(DBG_INFO,F("HW_FB_TLmValue "));
@@ -667,6 +676,7 @@ class Communication : public CommQueue<> {
         inline void miGPFDecode(packet_t *p, const queue_s *q) {
             record_t<> *rec = q->iv->getRecordStruct(InverterDevInform_Simple);  // choose the record structure
             rec->ts = q->ts;
+            rec->mqttSentStatus = MqttSentStatus::NEW_DATA;
 
             q->iv->setValue(2, rec, (uint32_t) (((p->packet[10] << 8) | p->packet[11]))); //FLD_GRID_PROFILE_CODE
             q->iv->setValue(3, rec, (uint32_t) (((p->packet[12] << 8) | p->packet[13]))); //FLD_GRID_PROFILE_VERSION
@@ -849,6 +859,8 @@ class Communication : public CommQueue<> {
             if (!stsok) {
                 q->iv->setValue(q->iv->getPosByChFld(0, FLD_EVT, rec), rec, prntsts);
                 q->iv->lastAlarm[0] = alarm_t(prntsts, q->ts, 0);
+                rec->ts = q->ts;
+                rec->mqttSentStatus = MqttSentStatus::NEW_DATA;
             }
 
             if (q->iv->alarmMesIndex < rec->record[q->iv->getPosByChFld(0, FLD_EVT, rec)]) {
@@ -907,17 +919,12 @@ class Communication : public CommQueue<> {
             iv->setValue(iv->getPosByChFld(0, FLD_PAC, rec), rec, (float) ac_pow/10);
 
             iv->doCalculations();
+            rec->mqttSentStatus = MqttSentStatus::NEW_DATA;
             // update status state-machine,
             if (ac_pow)
                 iv->isProducing();
-            //closeRequest(iv, iv->miMultiParts > 5);
-
-            //mHeu.setGotAll(iv);
-            //cmdDone(false);
             if(NULL != mCbPayload)
                 (mCbPayload)(RealTimeRunData_Debug, iv);
-
-            //mState = States::RESET; // everything ok, next request
         }
 
     private:

From ac2f772b74ed8c9fa4439ba01acde517eb5ee748 Mon Sep 17 00:00:00 2001
From: you69man <maschin@gmx.net>
Date: Sun, 14 Jan 2024 17:16:32 +0100
Subject: [PATCH 2/6] fix immediate clearing of display after sunset

---
 src/plugins/Display/Display_Mono.h        | 121 ++++++++++++++--------
 src/plugins/Display/Display_Mono_128X64.h |   3 +-
 2 files changed, 79 insertions(+), 45 deletions(-)

diff --git a/src/plugins/Display/Display_Mono.h b/src/plugins/Display/Display_Mono.h
index c8d25f8b..e0a03c61 100644
--- a/src/plugins/Display/Display_Mono.h
+++ b/src/plugins/Display/Display_Mono.h
@@ -81,7 +81,7 @@ class DisplayMono {
             mDispWidth = mDisplay->getDisplayWidth();
             mDispHeight = mDisplay->getDisplayHeight();
             mDispSwitchTime.stopTimeMonitor();
-            if (mCfg->graph_ratio == 100)           // if graph ratio is 100% start in graph mode
+            if (100 == mCfg->graph_ratio)           // if graph ratio is 100% start in graph mode
                 mDispSwitchState = DispSwitchState::GRAPH;
             else if (mCfg->graph_ratio != 0)
                 mDispSwitchTime.startTimeMonitor(150 * (100 - mCfg->graph_ratio));  // start display mode change only if ratio is neither 0 nor 100
@@ -90,39 +90,70 @@ class DisplayMono {
         // pixelshift screensaver with wipe effect
         void calcPixelShift(int range) {
             int8_t mod = (millis() / 10000) % ((range >> 1) << 2);
-            mPixelshift = mCfg->screenSaver == 1 ? ((mod < range) ? mod - (range >> 1) : -(mod - range - (range >> 1) + 1)) : 0;
+            mPixelshift = (1 == mCfg->screenSaver) ? ((mod < range) ? mod - (range >> 1) : -(mod - range - (range >> 1) + 1)) : 0;
         }
 
+    protected:
+        enum class PowerGraphState {
+            NO_TIME_SYNC,
+            IN_PERIOD,
+            WAIT_4_NEW_PERIOD,
+            WAIT_4_RESTART
+        };
+
+        // initialize power graph and allocate data buffer based on pixel width
         void initPowerGraph(uint8_t width, uint8_t height) {
             DBGPRINTLN(F("---- Init Power Graph ----"));
             mPgWidth = width;
             mPgHeight = height;
             mPgData = new float[mPgWidth];
+            mPgState = PowerGraphState::NO_TIME_SYNC;
             resetPowerGraph();
-/*
-            Inverter<> *iv;
-            mPgMaxAvailPower = 0;
-            uint8_t nInv = mSys->getNumInverters();
-            for (uint8_t i = 0; i < nInv; i++) {
-                iv = mSys->getInverterByPos(i);
-                if (iv == NULL)
-                    continue;
-                for (uint8_t ch = 0; ch < 6; ch++) {
-                    mPgMaxAvailPower += iv->config->chMaxPwr[ch];
-                }
-            }
-            DBGPRINTLN("max. Power = " + String(mPgMaxAvailPower));*/
         }
 
+        // add new value to power graph and maintain state engine for period times
         void addPowerGraphEntry(float val) {
-            if ((nullptr != mPgData) && (mDisplayData->utcTs > 0)) {  // precondition: power graph initialized and utc time available
-                calcPowerGraphValues();
-                //mPgData[mPgLastPos] = std::max(mPgData[mPgLastPos], (uint8_t) (val * 255.0 / mPgMaxAvailPower));  // normalizing of data to 0-255
-                mPgData[mPgLastPos] = std::max(mPgData[mPgLastPos], val);
-                mPgMaxPwr = std::max(mPgMaxPwr, val);  // max value of stored data for scaling of y-axis
+            if (nullptr == mPgData)  // power graph not initialized
+                return;
+
+            bool store_entry = false;
+            switch(mPgState) {
+                case PowerGraphState::NO_TIME_SYNC:  
+                    if ((mDisplayData->pGraphStartTime > 0) && (mDisplayData->pGraphEndTime > 0) &&                                         // wait until period data is available ...
+                        (mDisplayData->utcTs >= mDisplayData->pGraphStartTime) && (mDisplayData->utcTs < mDisplayData->pGraphEndTime)) {    // and current time is in period
+                        storeStartEndTimes();  // period was received -> store
+                        store_entry = true;
+                        mPgState = PowerGraphState::IN_PERIOD;
+                    }
+                    break;
+                case PowerGraphState::IN_PERIOD:
+                    if (mDisplayData->utcTs > mPgEndTime)                   // check if end of day is reached ...
+                        mPgState = PowerGraphState::WAIT_4_NEW_PERIOD;      // then wait for new period setting
+                    else
+                        store_entry = true;                                
+                    break;
+                case PowerGraphState::WAIT_4_NEW_PERIOD:
+                    if ((mPgStartTime != mDisplayData->pGraphStartTime) || (mPgEndTime != mDisplayData->pGraphEndTime)) { // wait until new time period was received ...
+                        storeStartEndTimes();                                                                             // and store it for next period
+                        mPgState = PowerGraphState::WAIT_4_RESTART;
+                    }
+                    break;
+                case PowerGraphState::WAIT_4_RESTART:
+                    if ((mDisplayData->utcTs >= mPgStartTime) && (mDisplayData->utcTs < mPgEndTime)) { // wait until current time is in period again ...
+                        resetPowerGraph();                                                             // then reset power graph data
+                        store_entry = true;
+                        mPgState = PowerGraphState::IN_PERIOD;
+                    }
+                    break;
+            }
+            if (store_entry) {
+                mPgLastPos = std::min((uint8_t) sss2PgPos(mDisplayData->utcTs - mPgStartTime), (uint8_t) (mPgWidth - 1));  // current datapoint based on seconds since start
+                mPgData[mPgLastPos] = std::max(mPgData[mPgLastPos], val); // update current datapoint to maximum of all seen values
+                mPgMaxPwr = std::max(mPgMaxPwr, val);  // update max value of stored data for scaling of y-axis
             }
         }
 
+        // plot power graph to given display offset
         void plotPowerGraph(uint8_t xoff, uint8_t yoff) {
             if (nullptr == mPgData)  // power graph not initialized
                 return;
@@ -146,7 +177,7 @@ class DisplayMono {
             tm.Minute = 0;
             tm.Second = 0;
             for (; tm.Hour <= endHour; tm.Hour++) {
-                uint8_t x_pos_screen = getPowerGraphXpos(sss2pgpos((uint32_t) makeTime(tm) - mDisplayData->pGraphStartTime)); // scale horizontal axis
+                uint8_t x_pos_screen = getPowerGraphXpos(sss2PgPos((uint32_t) makeTime(tm) - mDisplayData->pGraphStartTime)); // scale horizontal axis
                 mDisplay->drawPixel(xoff + x_pos_screen, yoff - 1);
             }
 
@@ -163,8 +194,8 @@ class DisplayMono {
 
             // draw curve
             for (uint8_t i = 1; i <= mPgLastPos; i++) {
-                mDisplay->drawLine(xoff + getPowerGraphXpos(i - 1), yoff - getPowerGraphYpos(i - 1),
-                                   xoff + getPowerGraphXpos(i),     yoff - getPowerGraphYpos(i));
+                mDisplay->drawLine(xoff + getPowerGraphXpos(i - 1), yoff - getPowerGraphValueYpos(i - 1),
+                                   xoff + getPowerGraphXpos(i),     yoff - getPowerGraphValueYpos(i));
             }
 
             // print max power value
@@ -195,6 +226,7 @@ class DisplayMono {
             return change;
         }
 
+        // reset power graph 
         void resetPowerGraph() {
             if (mPgData != nullptr) {
                 mPgMaxPwr = 0.0;
@@ -205,34 +237,35 @@ class DisplayMono {
             }
         }
 
-        uint8_t sss2pgpos(uint seconds_since_start) {
-            uint32_t diff = (mDisplayData->pGraphEndTime - mDisplayData->pGraphStartTime);
-            if(diff)
-                return (seconds_since_start * (mPgWidth - 1) / diff);
-            return 0;
+        // store start and end times of current time period and calculate period length
+        void storeStartEndTimes() {
+            mPgStartTime = mDisplayData->pGraphStartTime;
+            mPgEndTime = mDisplayData->pGraphEndTime;
+            mPgPeriod = mDisplayData->pGraphEndTime - mDisplayData->pGraphStartTime;  // time period of power graph in sec for scaling of x-axis
         }
 
-        void calcPowerGraphValues() {
-            mPgPeriod = mDisplayData->pGraphEndTime - mDisplayData->pGraphStartTime;  // length of power graph for scaling of x-axis
-            uint32_t oldTimeOfDay = mPgTimeOfDay;
-            mPgTimeOfDay = (mDisplayData->utcTs > mDisplayData->pGraphStartTime) ? mDisplayData->utcTs - mDisplayData->pGraphStartTime : 0; // current time of day with respect to current sunrise time
-            if (oldTimeOfDay > mPgTimeOfDay) // new day -> reset old data
-                resetPowerGraph();
-            if(0 == mPgPeriod)
-                mPgPeriod = 1;
-            mPgLastPos = std::min((uint8_t) (mPgTimeOfDay * (mPgWidth - 1) / mPgPeriod), (uint8_t) (mPgWidth - 1));  // current datapoint based on currenct time of day
+        // get power graph datapoint index, scaled to current time period, by seconds since start
+        uint8_t sss2PgPos(uint seconds_since_start) { 
+            if(mPgPeriod)                       
+                return (seconds_since_start * (mPgWidth - 1) / mPgPeriod);
+            else
+                return 0;
         }
 
-        uint8_t getPowerGraphXpos(uint8_t p) {
+        // get X-position of power graph, scaled to lastpos, by according data point index
+        uint8_t getPowerGraphXpos(uint8_t p) {     
             if ((p <= mPgLastPos) && (mPgLastPos > 0))
                 return((p * (mPgWidth - 1)) / mPgLastPos);  // scaling of x-axis
-            return 0;
+            else
+                return 0;
         }
 
-        uint8_t getPowerGraphYpos(uint8_t p) {
+        // get Y-position of power graph, scaled to maximum value, by according datapoint index
+        uint8_t getPowerGraphValueYpos(uint8_t p) { 
             if ((p < mPgWidth) && (mPgMaxPwr > 0))
                 return((mPgData[p] * (uint32_t) mPgHeight / mPgMaxPwr)); // scaling of data to graph height
-            return 0;
+            else
+                return 0;
         }
 
     protected:
@@ -254,9 +287,11 @@ class DisplayMono {
         float  *mPgData = nullptr;
         uint8_t mPgHeight = 0;
         float   mPgMaxPwr = 0.0;
-        uint32_t mPgPeriod = 0; // seconds
-        uint32_t mPgTimeOfDay = 0;
+        uint32_t mPgStartTime = 0;
+        uint32_t mPgEndTime = 0;
+        uint32_t mPgPeriod = 0;  // seconds
         uint8_t  mPgLastPos = 0;
+        PowerGraphState mPgState = PowerGraphState::NO_TIME_SYNC;
 
         uint16_t mDispHeight;
         uint8_t mLuminance;
diff --git a/src/plugins/Display/Display_Mono_128X64.h b/src/plugins/Display/Display_Mono_128X64.h
index 34f35834..b4aa08cc 100644
--- a/src/plugins/Display/Display_Mono_128X64.h
+++ b/src/plugins/Display/Display_Mono_128X64.h
@@ -89,9 +89,8 @@ class DisplayMono128X64 : public DisplayMono {
             calcPixelShift(pixelShiftRange);
 
             // add new power data to power graph
-            if (mDisplayData->nrProducing > 0) {
+            if (mDisplayData->nrProducing > 0)
                 addPowerGraphEntry(mDisplayData->totalPower);
-            }
 
             // print Date and time
             if (0 != mDisplayData->utcTs)

From 9d29276c06de2296bcc4b2d2f46770697ea8e62d Mon Sep 17 00:00:00 2001
From: rejoe2 <rejoe2@users.noreply.github.com>
Date: Mon, 15 Jan 2024 16:12:51 +0100
Subject: [PATCH 3/6] Add "second try"

- serial info deactivated
- first tx is counted as retransmit
---
 src/hm/Communication.h | 23 ++++++++++++++++++++---
 1 file changed, 20 insertions(+), 3 deletions(-)

diff --git a/src/hm/Communication.h b/src/hm/Communication.h
index 5f14a5af..9e383b6a 100644
--- a/src/hm/Communication.h
+++ b/src/hm/Communication.h
@@ -91,6 +91,7 @@ class Communication : public CommQueue<> {
                         mIsRetransmit = false;
                         if(NULL == q->iv->radio)
                             cmdDone(false); // can't communicate while radio is not defined!
+                        mFirstTry = q->iv->isAvailable();
                         q->iv->mCmd = q->cmd;
                         q->iv->mIsSingleframeReq = false;
                         mState = States::START;
@@ -140,8 +141,25 @@ class Communication : public CommQueue<> {
                                 if((IV_HMS == q->iv->ivGen) || (IV_HMT == q->iv->ivGen)) {
                                     q->iv->radio->switchFrequency(q->iv, HOY_BOOT_FREQ_KHZ, (q->iv->config->frequency*FREQ_STEP_KHZ + HOY_BASE_FREQ_KHZ));
                                     mWaitTime.startTimeMonitor(1000);
-                                } else if(IV_MI == q->iv->ivGen)
-                                    q->iv->mIvTxCnt++;
+                                } else {
+                                    if(IV_MI == q->iv->ivGen)
+                                        q->iv->mIvTxCnt++;
+                                    if(mFirstTry){
+                                        mFirstTry     = false;
+                                        mState = States::START;
+                                        setAttempt();
+                                        mHeu.evalTxChQuality(q->iv, false, 0, 0);
+                                        //q->iv->radioStatistics.rxFailNoAnser++;
+                                        q->iv->radioStatistics.retransmits++;
+                                        mWaitTime.stopTimeMonitor();
+
+                                        /*if(*mSerialDebug) {
+                                            DPRINT_IVID(DBG_INFO, q->iv->id);
+                                            DBGPRINTLN(F("second try"));
+                                        }*/
+                                       return;
+                                    }
+                                }
                             }
                             closeRequest(q, false);
                             break;
@@ -553,7 +571,6 @@ class Communication : public CommQueue<> {
             q->iv->mGotLastMsg  = false;
             q->iv->miMultiParts = 0;
             mIsRetransmit       = false;
-            mFirstTry           = false; // for correct reset
             mState              = States::RESET;
             DBGPRINTLN(F("-----"));
         }

From 1ac920d52d8b5768f0be26db5b5959543af84e38 Mon Sep 17 00:00:00 2001
From: lumapu <lp@lufami.de>
Date: Mon, 15 Jan 2024 21:38:54 +0100
Subject: [PATCH 4/6] 0.8.57 * merge PR: fix immediate clearing of display
 after sunset #1364

---
 src/CHANGES.md                     |  3 +++
 src/defines.h                      |  2 +-
 src/plugins/Display/Display_Mono.h | 27 ++++++++++++++++-----------
 3 files changed, 20 insertions(+), 12 deletions(-)

diff --git a/src/CHANGES.md b/src/CHANGES.md
index d566216a..2032f009 100644
--- a/src/CHANGES.md
+++ b/src/CHANGES.md
@@ -1,5 +1,8 @@
 # Development Changes
 
+## 0.8.57 - 2024-01-15
+* merge PR: fix immediate clearing of display after sunset #1364
+
 ## 0.8.56 - 2024-01-15
 * potential fix of update problems and random reboots #1359 #1354
 
diff --git a/src/defines.h b/src/defines.h
index 46c9a249..528aa714 100644
--- a/src/defines.h
+++ b/src/defines.h
@@ -13,7 +13,7 @@
 //-------------------------------------
 #define VERSION_MAJOR       0
 #define VERSION_MINOR       8
-#define VERSION_PATCH       56
+#define VERSION_PATCH       57
 
 //-------------------------------------
 typedef struct {
diff --git a/src/plugins/Display/Display_Mono.h b/src/plugins/Display/Display_Mono.h
index e0a03c61..58b4224f 100644
--- a/src/plugins/Display/Display_Mono.h
+++ b/src/plugins/Display/Display_Mono.h
@@ -116,12 +116,16 @@ class DisplayMono {
             if (nullptr == mPgData)  // power graph not initialized
                 return;
 
+            bool storeStartEndTimes = false;
             bool store_entry = false;
             switch(mPgState) {
                 case PowerGraphState::NO_TIME_SYNC:  
-                    if ((mDisplayData->pGraphStartTime > 0) && (mDisplayData->pGraphEndTime > 0) &&                                         // wait until period data is available ...
-                        (mDisplayData->utcTs >= mDisplayData->pGraphStartTime) && (mDisplayData->utcTs < mDisplayData->pGraphEndTime)) {    // and current time is in period
-                        storeStartEndTimes();  // period was received -> store
+                    if ((mDisplayData->pGraphStartTime > 0)
+                        && (mDisplayData->pGraphEndTime > 0)                      // wait until period data is available ...
+                        && (mDisplayData->utcTs >= mDisplayData->pGraphStartTime)
+                        && (mDisplayData->utcTs < mDisplayData->pGraphEndTime)) // and current time is in period
+                    {
+                        storeStartEndTimes = true;                                     // period was received -> store
                         store_entry = true;
                         mPgState = PowerGraphState::IN_PERIOD;
                     }
@@ -134,7 +138,7 @@ class DisplayMono {
                     break;
                 case PowerGraphState::WAIT_4_NEW_PERIOD:
                     if ((mPgStartTime != mDisplayData->pGraphStartTime) || (mPgEndTime != mDisplayData->pGraphEndTime)) { // wait until new time period was received ...
-                        storeStartEndTimes();                                                                             // and store it for next period
+                        storeStartEndTimes = true;                                                                             // and store it for next period
                         mPgState = PowerGraphState::WAIT_4_RESTART;
                     }
                     break;
@@ -146,6 +150,14 @@ class DisplayMono {
                     }
                     break;
             }
+
+            // store start and end times of current time period and calculate period length
+            if (storeStartEndTimes) {
+                mPgStartTime = mDisplayData->pGraphStartTime;
+                mPgEndTime = mDisplayData->pGraphEndTime;
+                mPgPeriod = mDisplayData->pGraphEndTime - mDisplayData->pGraphStartTime;  // time period of power graph in sec for scaling of x-axis
+            }
+
             if (store_entry) {
                 mPgLastPos = std::min((uint8_t) sss2PgPos(mDisplayData->utcTs - mPgStartTime), (uint8_t) (mPgWidth - 1));  // current datapoint based on seconds since start
                 mPgData[mPgLastPos] = std::max(mPgData[mPgLastPos], val); // update current datapoint to maximum of all seen values
@@ -237,13 +249,6 @@ class DisplayMono {
             }
         }
 
-        // store start and end times of current time period and calculate period length
-        void storeStartEndTimes() {
-            mPgStartTime = mDisplayData->pGraphStartTime;
-            mPgEndTime = mDisplayData->pGraphEndTime;
-            mPgPeriod = mDisplayData->pGraphEndTime - mDisplayData->pGraphStartTime;  // time period of power graph in sec for scaling of x-axis
-        }
-
         // get power graph datapoint index, scaled to current time period, by seconds since start
         uint8_t sss2PgPos(uint seconds_since_start) { 
             if(mPgPeriod)                       

From 358344ad6d0f23f20aa5f52a0c493b473decb182 Mon Sep 17 00:00:00 2001
From: lumapu <lp@lufami.de>
Date: Mon, 15 Jan 2024 22:55:56 +0100
Subject: [PATCH 5/6] 0.8.57 * merge PR: MI-MQTT and last retransmit #1363 *
 fixed DTU-ID, now built from the unique part of the MAC * fix lang in
 `/system` #1346

---
 src/CHANGES.md           |  3 +++
 src/hm/CommQueue.h       |  1 -
 src/hm/Communication.h   |  4 ++--
 src/hm/radio.h           | 15 +++++++++------
 src/web/html/update.html |  2 +-
 src/web/lang.h           |  8 ++++----
 src/web/lang.json        |  2 +-
 7 files changed, 20 insertions(+), 15 deletions(-)

diff --git a/src/CHANGES.md b/src/CHANGES.md
index 2032f009..15dcfba2 100644
--- a/src/CHANGES.md
+++ b/src/CHANGES.md
@@ -2,6 +2,9 @@
 
 ## 0.8.57 - 2024-01-15
 * merge PR: fix immediate clearing of display after sunset #1364
+* merge PR: MI-MQTT and last retransmit #1363
+* fixed DTU-ID, now built from the unique part of the MAC
+* fix lang in `/system` #1346
 
 ## 0.8.56 - 2024-01-15
 * potential fix of update problems and random reboots #1359 #1354
diff --git a/src/hm/CommQueue.h b/src/hm/CommQueue.h
index d62efe07..13decc01 100644
--- a/src/hm/CommQueue.h
+++ b/src/hm/CommQueue.h
@@ -11,7 +11,6 @@
 #include "hmInverter.h"
 #include "../utils/dbg.h"
 
-// needs a '+1' because the comparison does not send if attempts is equal 0
 #define DEFAULT_ATTEMPS                 5
 #define MORE_ATTEMPS_ALARMDATA          15
 #define MORE_ATTEMPS_GRIDONPROFILEPARA  15
diff --git a/src/hm/Communication.h b/src/hm/Communication.h
index 9e383b6a..7b5c84f9 100644
--- a/src/hm/Communication.h
+++ b/src/hm/Communication.h
@@ -144,14 +144,14 @@ class Communication : public CommQueue<> {
                                 } else {
                                     if(IV_MI == q->iv->ivGen)
                                         q->iv->mIvTxCnt++;
-                                    if(mFirstTry){
+                                    if(mFirstTry) {
                                         mFirstTry     = false;
-                                        mState = States::START;
                                         setAttempt();
                                         mHeu.evalTxChQuality(q->iv, false, 0, 0);
                                         //q->iv->radioStatistics.rxFailNoAnser++;
                                         q->iv->radioStatistics.retransmits++;
                                         mWaitTime.stopTimeMonitor();
+                                        mState = States::START;
 
                                         /*if(*mSerialDebug) {
                                             DPRINT_IVID(DBG_INFO, q->iv->id);
diff --git a/src/hm/radio.h b/src/hm/radio.h
index e5eda128..1422b285 100644
--- a/src/hm/radio.h
+++ b/src/hm/radio.h
@@ -103,16 +103,19 @@ class Radio {
         void generateDtuSn(void) {
             uint32_t chipID = 0;
             #ifdef ESP32
-            uint64_t MAC = ESP.getEfuseMac();
-            chipID = ((MAC >> 8) & 0xFF0000) | ((MAC >> 24) & 0xFF00) | ((MAC >> 40) & 0xFF);
+            chipID = (ESP.getEfuseMac() & 0xffffffff);
             #else
             chipID = ESP.getChipId();
             #endif
-            mDtuSn = 0x80000000; // the first digit is an 8 for DTU production year 2022, the rest is filled with the ESP chipID in decimal
-            for(int i = 0; i < 7; i++) {
-                mDtuSn |= (chipID % 10) << (i * 4);
-                chipID /= 10;
+
+            uint8_t t;
+            for(int i = 0; i < (7 << 2); i += 4) {
+                t = (chipID >> i) & 0x0f;
+                if(t > 0x09)
+                    t -= 6;
+                mDtuSn |= (t << i);
             }
+            mDtuSn |= 0x80000000; // the first digit is an 8 for DTU production year 2022, the rest is filled with the ESP chipID in decimal
         }
 
         uint32_t mDtuSn;
diff --git a/src/web/html/update.html b/src/web/html/update.html
index 3fed75a5..800d9dfe 100644
--- a/src/web/html/update.html
+++ b/src/web/html/update.html
@@ -10,7 +10,7 @@
             <div id="content">
                 <fieldset>
                     <legend class="des">{#SELECT_FILE} (*.bin)</legend>
-                    <p>{#INSTALLED_VERSION}: <span id="version" style="background-color: var(--input-bg); padding: 7px;"></span></p>
+                    <p>{#INSTALLED_VERSION}:<br/><span id="version" style="background-color: var(--input-bg); padding: 7px; display: block; margin: 3px;"></span></p>
                     <form id="form" method="POST" action="/update" enctype="multipart/form-data" accept-charset="utf-8">
                         <input type="file" name="update">
                         <input type="button" class="btn my-4" value="{#BTN_UPDATE}" onclick="hide()">
diff --git a/src/web/lang.h b/src/web/lang.h
index 3c3cd631..546399aa 100644
--- a/src/web/lang.h
+++ b/src/web/lang.h
@@ -73,15 +73,15 @@
 #endif
 
 #ifdef LANG_DE
-    #define FACTORY_RESET "Ahoy Factory Reset"
-#else /*LANG_EN*/
     #define FACTORY_RESET "Ahoy auf Werkseinstellungen zurücksetzen"
+#else /*LANG_EN*/
+    #define FACTORY_RESET "Ahoy Factory Reset"
 #endif
 
 #ifdef LANG_DE
-    #define BTN_REBOOT "Reboot"
-#else /*LANG_EN*/
     #define BTN_REBOOT "Ahoy neustarten"
+#else /*LANG_EN*/
+    #define BTN_REBOOT "Reboot"
 #endif
 
 #endif /*__LANG_H__*/
diff --git a/src/web/lang.json b/src/web/lang.json
index c910650e..ae8c0ca2 100644
--- a/src/web/lang.json
+++ b/src/web/lang.json
@@ -781,7 +781,7 @@
                 {
                     "token": "IRQ_WORKING",
                     "en": "Interrupt Pin working",
-                    "de": "Interrupt Pin funktoniert"
+                    "de": "Interrupt Pin funktioniert"
                 },
                 {
                     "token": "NRF24_DATA_RATE",

From f503516c9feba7a7cc6fb427954165ce05cd7746 Mon Sep 17 00:00:00 2001
From: lumapu <lp@lufami.de>
Date: Tue, 16 Jan 2024 00:11:34 +0100
Subject: [PATCH 6/6] 0.8.57 * added protection to prevent update to wrong
 firmware (environment check)

---
 ahoy.code-workspace      | 45 ++++++++++++++++++++++++++++++++++++++++
 src/CHANGES.md           |  1 +
 src/app.cpp              |  2 +-
 src/web/RestApi.h        |  4 ++--
 src/web/html/update.html | 29 ++++++++++++++++++++------
 src/web/lang.json        | 20 ++++++++++++++++++
 6 files changed, 92 insertions(+), 9 deletions(-)
 create mode 100644 ahoy.code-workspace

diff --git a/ahoy.code-workspace b/ahoy.code-workspace
new file mode 100644
index 00000000..20d5909e
--- /dev/null
+++ b/ahoy.code-workspace
@@ -0,0 +1,45 @@
+{
+	"folders": [
+		{
+			"path": "."
+		},
+		{
+			"path": "src"
+		}
+	],
+	"settings": {
+		"files.associations": {
+			"algorithm": "cpp",
+			"array": "cpp",
+			"chrono": "cpp",
+			"deque": "cpp",
+			"format": "cpp",
+			"forward_list": "cpp",
+			"functional": "cpp",
+			"initializer_list": "cpp",
+			"iterator": "cpp",
+			"list": "cpp",
+			"memory": "cpp",
+			"queue": "cpp",
+			"random": "cpp",
+			"regex": "cpp",
+			"vector": "cpp",
+			"xhash": "cpp",
+			"xlocmon": "cpp",
+			"xlocnum": "cpp",
+			"xmemory": "cpp",
+			"xstring": "cpp",
+			"xtree": "cpp",
+			"xutility": "cpp",
+			"*.tcc": "cpp",
+			"string": "cpp",
+			"unordered_map": "cpp",
+			"unordered_set": "cpp",
+			"string_view": "cpp",
+			"sstream": "cpp",
+			"istream": "cpp",
+			"ostream": "cpp"
+		},
+		"editor.formatOnSave": false
+	}
+}
\ No newline at end of file
diff --git a/src/CHANGES.md b/src/CHANGES.md
index 15dcfba2..316224ab 100644
--- a/src/CHANGES.md
+++ b/src/CHANGES.md
@@ -5,6 +5,7 @@
 * merge PR: MI-MQTT and last retransmit #1363
 * fixed DTU-ID, now built from the unique part of the MAC
 * fix lang in `/system` #1346
+* added protection to prevent update to wrong firmware (environment check)
 
 ## 0.8.56 - 2024-01-15
 * potential fix of update problems and random reboots #1359 #1354
diff --git a/src/app.cpp b/src/app.cpp
index 6350e990..8dfcdb4d 100644
--- a/src/app.cpp
+++ b/src/app.cpp
@@ -35,7 +35,7 @@ void app::setup() {
     }
     #if defined(ESP32)
     if(mConfig->cmt.enabled) {
-        mCmtRadio.setup(&mConfig->serial.debug, &mConfig->serial.privacyLog, &mConfig->serial.printWholeTrace, mConfig->cmt.pinSclk, mConfig->cmt.pinSdio, mConfig->cmt.pinCsb, mConfig->cmt.pinFcsb, false);
+        mCmtRadio.setup(&mConfig->serial.debug, &mConfig->serial.privacyLog, &mConfig->serial.printWholeTrace, mConfig->cmt.pinSclk, mConfig->cmt.pinSdio, mConfig->cmt.pinCsb, mConfig->cmt.pinFcsb);
     }
     #endif
     #ifdef ETHERNET
diff --git a/src/web/RestApi.h b/src/web/RestApi.h
index edf92a97..097adac3 100644
--- a/src/web/RestApi.h
+++ b/src/web/RestApi.h
@@ -326,8 +326,8 @@ class RestApi {
         void getHtmlSystem(AsyncWebServerRequest *request, JsonObject obj) {
             getSysInfo(request, obj.createNestedObject(F("system")));
             getGeneric(request, obj.createNestedObject(F("generic")));
-            char tmp[100];
-            snprintf(tmp, 100, "<a href=\"/factory\" class=\"btn\">%s</a><br/><br/><a href=\"/reboot\" class=\"btn\">%s</a>", FACTORY_RESET, BTN_REBOOT);
+            char tmp[200];
+            snprintf(tmp, 200, "<a href=\"/factory\" class=\"btn\">%s</a><br/><br/><a href=\"/reboot\" class=\"btn\">%s</a>", FACTORY_RESET, BTN_REBOOT);
             obj[F("html")] = String(tmp);
         }
 
diff --git a/src/web/html/update.html b/src/web/html/update.html
index 800d9dfe..52ace5f1 100644
--- a/src/web/html/update.html
+++ b/src/web/html/update.html
@@ -23,17 +23,34 @@
         </div>
         {#HTML_FOOTER}
         <script type="text/javascript">
+            var env;
             function parseGeneric(obj) {
-                parseNav(obj);
-                parseESP(obj);
-                parseRssi(obj);
+                parseNav(obj)
+                parseESP(obj)
+                parseRssi(obj)
+                env = obj.env
                 document.getElementById("version").innerHTML = "{#VERSION_FULL}_" + obj.env + ".bin"
             }
 
             function hide() {
-                document.getElementById("form").submit();
-                var e = document.getElementById("content");
-                e.replaceChildren(span("{#UPDATE_STARTED}"));
+                var bin = document.getElementsByName("update")[0].value.slice(-env.length-4, -4)
+                if (bin !== env) {
+                    var html = ml("div", {class: "row"}, [
+                        ml("div", {class: "row my-3"}, "{#WARN_DIFF_ENV}"),
+                        ml("div", {class: "row"}, [
+                            ml("div", {class: "col-6"}, ml("input", {type: "button", class: "btn", value: "{#CANCEL}", onclick: function() { modalClose(); }}, null)),
+                            ml("div", {class: "col-6"}, ml("input", {type: "button", class: "btn", value: "{#CONTIUE}", onclick: function() { start(); modalClose(); }}, null))
+                        ])
+                    ])
+                    modal("{#UPDATE_MODAL}", html)
+                } else
+                    start()
+            }
+
+            function start() {
+                document.getElementById("form").submit()
+                var e = document.getElementById("content")
+                e.replaceChildren(span("{#UPDATE_STARTED}"))
             }
 
             getAjax("/api/generic", parseGeneric);
diff --git a/src/web/lang.json b/src/web/lang.json
index ae8c0ca2..316ca67b 100644
--- a/src/web/lang.json
+++ b/src/web/lang.json
@@ -1052,6 +1052,26 @@
                     "token": "UPDATE_STARTED",
                     "en": "update started",
                     "de": "Aktualisierung gestartet"
+                },
+                {
+                    "token": "UPDATE_MODAL",
+                    "en": "Warning",
+                    "de": "Warnung"
+                },
+                {
+                    "token": "WARN_DIFF_ENV",
+                    "en": "your environment does not match the update file!",
+                    "de": "Die ausgew&auml;hlte Firmware passt uU. nicht zum Chipsatz!"
+                },
+                {
+                    "token": "CONTIUE",
+                    "en": "continue",
+                    "de": "fortfahren"
+                },
+                {
+                    "token": "CANCEL",
+                    "en": "cancel",
+                    "de": "abbrechen"
                 }
             ]
         },