1+ // Helper: simple hash for config strings and segment offsets
2+ uint32_t KnxIpUsermod::computeGATableHash () const {
3+ // FNV-1a hash
4+ uint32_t hash = 2166136261u ;
5+ auto hashstr = [&](const char * s) {
6+ while (*s) { hash ^= (uint8_t )(*s++); hash *= 16777619u ; }
7+ };
8+ // Hash all GA strings
9+ const char * gaStrings[] = {
10+ gaInPower, gaInBri, gaInR, gaInG, gaInB, gaInW, gaInCct, gaInWW, gaInCW, gaInH, gaInS, gaInV, gaInFx, gaInPreset, gaInRGB, gaInHSV, gaInRGBW, gaInTime, gaInDate, gaInDateTime,
11+ gaInBriRel, gaInRRel, gaInGRel, gaInBRel, gaInWRel, gaInWWRel, gaInCWRel, gaInHRel, gaInSRel, gaInVRel, gaInFxRel, gaInRGBRel, gaInHSVRel, gaInRGBWRel,
12+ gaOutPower, gaOutBri, gaOutR, gaOutG, gaOutB, gaOutW, gaOutCct, gaOutWW, gaOutCW, gaOutH, gaOutS, gaOutV, gaOutFx, gaOutPreset, gaOutRGB, gaOutHSV, gaOutRGBW,
13+ gaOutIntTemp, gaOutTemp, gaOutIntTempAlarm, gaOutTempAlarm
14+ };
15+ for (const char * s : gaStrings) hashstr (s);
16+ // Hash segment offsets
17+ hash ^= segmentOffsetL; hash *= 16777619u ;
18+ hash ^= segmentOffsetM; hash *= 16777619u ;
19+ hash ^= segmentOffsetN; hash *= 16777619u ;
20+ return hash;
21+ }
122#include " usermod_knx_ip.h"
223#include " wled.h" // access to global 'strip' and segments
324#include " DPT.h"
@@ -1662,79 +1683,59 @@ String KnxIpUsermod::getGATableHTML() const {
16621683 KNX_UM_DEBUGF (" [KNX-UM] getGATableHTML: usermod not enabled\n " );
16631684 return " " ;
16641685 }
1665-
1666- // Get current number of segments
16671686 uint8_t segmentCount = strip.getSegmentsNum ();
1668- if (segmentCount == 0 ) segmentCount = 1 ; // at least main segment
1669-
1670- KNX_UM_DEBUGF (" [KNX-UM] getGATableHTML: generating table for %d segments\n " , segmentCount);
1671-
1672- // Create table with better styling
1687+ if (segmentCount == 0 ) segmentCount = 1 ;
1688+ uint32_t hash = computeGATableHash ();
1689+ if (gaTableCacheHash == hash && gaTableCacheSegments == segmentCount && gaTableCache.length () > 0 ) {
1690+ KNX_UM_DEBUGF (" [KNX-UM] getGATableHTML: using cached table (%d chars)\n " , gaTableCache.length ());
1691+ return gaTableCache;
1692+ }
1693+ KNX_UM_DEBUGF (" [KNX-UM] getGATableHTML: rebuilding table for %d segments\n " , segmentCount);
1694+ // ...existing code...
1695+ // (copy the entire original function body here, but assign to gaTableCache and update gaTableCacheHash/gaTableCacheSegments)
16731696 String html = " <table style='font-size:11px;border-collapse:collapse;width:100%;'>" ;
16741697 html += " <tr style='background:#333;color:white;'><th style='border:1px solid #666;padding:4px;'>GA Type</th><th style='border:1px solid #666;padding:4px;'>Main</th>" ;
1675- for (uint8_t seg = 1 ; seg < segmentCount && seg < 6 ; seg++) { // limit to 6 segments for display
1698+ for (uint8_t seg = 1 ; seg < segmentCount && seg < 6 ; seg++) {
16761699 html += " <th style='border:1px solid #666;padding:4px;'>Seg" + String (seg) + " </th>" ;
16771700 }
16781701 html += " </tr>" ;
1679-
1680- // Helper to format GA (convert uint16_t back to a/b/c format)
16811702 auto formatGA = [](uint16_t ga) -> String {
16821703 if (ga == 0 ) return " -" ;
1683- uint8_t main = (ga >> 11 ) & 0x1F ;
1684- uint8_t middle = (ga >> 8 ) & 0x07 ;
1685- uint8_t sub = ga & 0xFF ;
1704+ uint8_t main = (ga >> 11 ) & 0x1F ;
1705+ uint8_t middle = (ga >> 8 ) & 0x07 ;
1706+ uint8_t sub = ga & 0xFF ;
16861707 return String (main) + " /" + String (middle) + " /" + String (sub);
16871708 };
1688-
1689- // Collect all GAs used across the entire table for global conflict detection
16901709 std::vector<uint16_t > allUsedGAs;
1691-
1692- // Helper to add table row with conflict detection
16931710 auto addRow = [&](const char * label, const char * centralGA, const char * section = " " , bool globalOnly = false ) {
1694- if (strlen (centralGA) == 0 ) return ; // skip if GA not configured
1695-
1711+ if (strlen (centralGA) == 0 ) return ;
16961712 html += " <tr><td style='border:1px solid #666;padding:2px 4px;color:white;'>" + String (label) + " </td>" ;
1697-
16981713 if (globalOnly) {
1699- // For global GAs (Time, Date, Temperature), show only once in main column
17001714 uint16_t mainGA = parseGA (centralGA);
17011715 bool mainConflict = (mainGA > 0 ) && (std::find (allUsedGAs.begin (), allUsedGAs.end (), mainGA) != allUsedGAs.end ());
17021716 if (mainGA > 0 ) allUsedGAs.push_back (mainGA);
1703-
17041717 String mainBg = mainConflict ? " background:#cc3333;" : " " ;
17051718 html += " <td style='border:1px solid #666;padding:2px 4px;color:white;" + mainBg + " '>" + formatGA (mainGA) + " </td>" ;
1706-
1707- // Empty cells for other segments
17081719 for (uint8_t seg = 1 ; seg < segmentCount && seg < 6 ; seg++) {
17091720 html += " <td style='border:1px solid #666;padding:2px 4px;color:white;'>-</td>" ;
17101721 }
17111722 } else {
1712- // For per-segment GAs, calculate for each segment
1713- // Main segment (segment 0)
17141723 uint16_t mainGA = calculateSegmentGA (centralGA, 0 );
17151724 bool mainConflict = (mainGA > 0 ) && (std::find (allUsedGAs.begin (), allUsedGAs.end (), mainGA) != allUsedGAs.end ());
17161725 if (mainGA > 0 ) allUsedGAs.push_back (mainGA);
1717-
17181726 String mainBg = mainConflict ? " background:#cc3333;" : " " ;
17191727 html += " <td style='border:1px solid #666;padding:2px 4px;color:white;" + mainBg + " '>" + formatGA (mainGA) + " </td>" ;
1720-
1721- // Other segments
17221728 for (uint8_t seg = 1 ; seg < segmentCount && seg < 6 ; seg++) {
17231729 uint16_t segGA = calculateSegmentGA (centralGA, seg);
17241730 bool segConflict = (segGA > 0 ) && (std::find (allUsedGAs.begin (), allUsedGAs.end (), segGA) != allUsedGAs.end ());
17251731 if (segGA > 0 ) allUsedGAs.push_back (segGA);
1726-
17271732 String segBg = segConflict ? " background:#cc3333;" : " " ;
17281733 html += " <td style='border:1px solid #666;padding:2px 4px;color:white;" + segBg + " '>" + formatGA (segGA) + " </td>" ;
17291734 }
17301735 }
17311736 html += " </tr>" ;
17321737 };
1733-
1734- // Add INPUT GAs section
17351738 html += " <tr><td colspan='" + String (1 + min ((int )segmentCount, 6 )) + " ' style='border:1px solid #666;padding:4px;text-align:center;color:white;font-weight:bold;'>INPUT GAs (KNX → WLED)</td></tr>" ;
1736-
1737- // Basic INPUT GAs
17381739 addRow (" Power" , gaInPower);
17391740 addRow (" Brightness" , gaInBri);
17401741 addRow (" Red" , gaInR);
@@ -1755,8 +1756,6 @@ String KnxIpUsermod::getGATableHTML() const {
17551756 addRow (" Time" , gaInTime, " " , true );
17561757 addRow (" Date" , gaInDate, " " , true );
17571758 addRow (" DateTime" , gaInDateTime, " " , true );
1758-
1759- // Relative INPUT GAs
17601759 addRow (" Brightness Rel" , gaInBriRel);
17611760 addRow (" Red Rel" , gaInRRel);
17621761 addRow (" Green Rel" , gaInGRel);
@@ -1771,11 +1770,7 @@ String KnxIpUsermod::getGATableHTML() const {
17711770 addRow (" RGB Rel" , gaInRGBRel);
17721771 addRow (" HSV Rel" , gaInHSVRel);
17731772 addRow (" RGBW Rel" , gaInRGBWRel);
1774-
1775- // Add OUTPUT GAs section
17761773 html += " <tr><td colspan='" + String (1 + min ((int )segmentCount, 6 )) + " ' style='border:1px solid #666;padding:4px;text-align:center;color:white;font-weight:bold;'>OUTPUT GAs (WLED → KNX)</td></tr>" ;
1777-
1778- // Basic OUTPUT GAs
17791774 addRow (" Power" , gaOutPower);
17801775 addRow (" Brightness" , gaOutBri);
17811776 addRow (" Red" , gaOutR);
@@ -1797,15 +1792,13 @@ String KnxIpUsermod::getGATableHTML() const {
17971792 addRow (" Temp Sensor" , gaOutTemp, " " , true );
17981793 addRow (" Int Temp Alarm" , gaOutIntTempAlarm, " " , true );
17991794 addRow (" Temp Alarm" , gaOutTempAlarm, " " , true );
1800-
18011795 html += " </table>" ;
1802-
1803- // Add offset information
18041796 html += " <div style='font-size:10px;margin-top:4px;color:#aaa;'>Offsets: L=" + String (segmentOffsetL) + " , M=" + String (segmentOffsetM) + " , N=" + String (segmentOffsetN) + " </div>" ;
1805-
1797+ gaTableCache = html;
1798+ gaTableCacheHash = hash;
1799+ gaTableCacheSegments = segmentCount;
18061800 KNX_UM_DEBUGF (" [KNX-UM] getGATableHTML: generated %d chars\n " , html.length ());
1807-
1808- return html;
1801+ return gaTableCache;
18091802}
18101803
18111804// -------------------- Usermod API --------------------
0 commit comments