From 3418584da70907e684bcf92c2b6b05f4c973f0fe Mon Sep 17 00:00:00 2001 From: sogidaned <42743424+sogidaned@users.noreply.github.com> Date: Fri, 7 Mar 2025 10:58:03 +0100 Subject: [PATCH 01/23] Add files via upload Ich habe eine Uhr mit einem bayerischen Layout. kenn mich aber nicht sonderlich gut aus hab einfach das nero Layout auf meines umgeschrieben. Lade es nur hoch falls es noch wer braucht. Kenne mich aber nicht mit Websites aus, daher nur die datei --- include/Uhrtypes/DE10x11.bayerisch.hpp | 134 +++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 include/Uhrtypes/DE10x11.bayerisch.hpp diff --git a/include/Uhrtypes/DE10x11.bayerisch.hpp b/include/Uhrtypes/DE10x11.bayerisch.hpp new file mode 100644 index 00000000..d7c4abe3 --- /dev/null +++ b/include/Uhrtypes/DE10x11.bayerisch.hpp @@ -0,0 +1,134 @@ +#pragma once + +#include "Uhrtype.hpp" + +/* + * Layout Front + * COL + * X 9 8 7 6 5 4 3 2 1 0 + * ROW + - - - - - - - - - - - + * 0 | E T Z K I S T F Ü N F + * 1 | Z E H N E Z W A N Z G + * 2 | D R E I V I E R T L E + * 3 | J T N O C H V O R X M + * 4 | H O I B E P Z D R E I + * 5 | Z W O A N S S I E M E + * 6 | K A C H T E F Ü N F E + * 7 | N E I N E V I E R E Q + * 8 | Z W O I F E Z E H N E + * 9 | Y S E C H S E F U H R + */ + +class De10x11bayerisch_t : public iUhrType { +public: + virtual LanguageAbbreviation usedLang() override { + return LanguageAbbreviation::DE; + }; + + //------------------------------------------------------------------------------ + + virtual const bool hasDreiviertel() override { return true; } + + //------------------------------------------------------------------------------ + + void show(FrontWord word) override { + switch (word) { + + case FrontWord::es_ist: + setFrontMatrixWord(0, 8, 10); + setFrontMatrixWord(0, 5, 6); + break; + + case FrontWord::nach: + case FrontWord::v_nach: + setFrontMatrixWord(3, 5, 8); + break; + + case FrontWord::vor: + case FrontWord::v_vor: + setFrontMatrixWord(3, 2, 4); + break; + + case FrontWord::viertel: + setFrontMatrixWord(2, 1, 6); + break; + + case FrontWord::dreiviertel: + setFrontMatrixWord(2, 1, 10); + break; + + case FrontWord::min_5: + setFrontMatrixWord(0, 0, 3); + break; + + case FrontWord::min_10: + setFrontMatrixWord(1, 7, 10); + break; + + case FrontWord::min_20: + setFrontMatrixWord(1, 0, 5); + break; + + case FrontWord::halb: + setFrontMatrixWord(4, 6, 10); + break; + + case FrontWord::eins: + setFrontMatrixWord(5, 5, 8); + break; + + case FrontWord::hour_1: + setFrontMatrixWord(5, 5, 8); + break; + + case FrontWord::hour_2: + setFrontMatrixWord(5, 7, 10); + break; + + case FrontWord::hour_3: + setFrontMatrixWord(4, 0, 3); + break; + + case FrontWord::hour_4: + setFrontMatrixWord(7, 1, 5); + break; + + case FrontWord::hour_5: + setFrontMatrixWord(6, 0, 4); + break; + + case FrontWord::hour_6: + setFrontMatrixWord(9, 4, 9); + break; + + case FrontWord::hour_7: + setFrontMatrixWord(5, 0, 4); + break; + + case FrontWord::hour_8: + setFrontMatrixWord(6, 5, 9); + break; + + case FrontWord::hour_9: + setFrontMatrixWord(7, 6, 10); + break; + + case FrontWord::hour_10: + setFrontMatrixWord(8, 0, 4); + break; + + case FrontWord::hour_11: + setFrontMatrixWord(8, 5, 8); + break; + + case FrontWord::hour_12: + setFrontMatrixWord(8, 5, 10); + break; + + default: + break; + }; + }; +}; + +De10x11bayerisch_t _de10x11bayerisch; \ No newline at end of file From a5605aacbec75e6ec0693beb7988265c4a3ab63c Mon Sep 17 00:00:00 2001 From: sogidaned <42743424+sogidaned@users.noreply.github.com> Date: Fri, 7 Mar 2025 12:22:57 +0100 Subject: [PATCH 02/23] Update DE10x11.bayerisch.hpp remove space --- include/Uhrtypes/DE10x11.bayerisch.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/Uhrtypes/DE10x11.bayerisch.hpp b/include/Uhrtypes/DE10x11.bayerisch.hpp index d7c4abe3..bda8b39d 100644 --- a/include/Uhrtypes/DE10x11.bayerisch.hpp +++ b/include/Uhrtypes/DE10x11.bayerisch.hpp @@ -16,7 +16,7 @@ * 6 | K A C H T E F Ü N F E * 7 | N E I N E V I E R E Q * 8 | Z W O I F E Z E H N E - * 9 | Y S E C H S E F U H R + * 9 | Y S E C H S E F U H R */ class De10x11bayerisch_t : public iUhrType { @@ -131,4 +131,4 @@ class De10x11bayerisch_t : public iUhrType { }; }; -De10x11bayerisch_t _de10x11bayerisch; \ No newline at end of file +De10x11bayerisch_t _de10x11bayerisch; From e4b691dfa4115ceadcd98c2201cb5b5ba17b2541 Mon Sep 17 00:00:00 2001 From: Sogidaned Date: Sat, 8 Mar 2025 10:20:46 +0100 Subject: [PATCH 03/23] add bavarian layout --- include/Uhr.h | 1 + include/clockWork.hpp | 2 ++ webpage/index.html | 1 + webpage/language/de.js | 1 + webpage/language/en.js | 1 + webpage/language/es.js | 1 + webpage/language/hu.js | 1 + webpage/language/it.js | 1 + webpage/language/nl.js | 1 + webpage/language/ru.js | 1 + 10 files changed, 11 insertions(+) diff --git a/include/Uhr.h b/include/Uhr.h index 5bf5059a..ef4acd02 100644 --- a/include/Uhr.h +++ b/include/Uhr.h @@ -279,6 +279,7 @@ enum ClockType { Ger10x11schwaebisch = 20, Ger10x11Nero = 11, Ger10x11NeroFrame = 26, + Ger10x11bayerisch = 27, Ger11x11 = 3, Ger11x11V2 = 8, Ger11x11V3 = 14, diff --git a/include/clockWork.hpp b/include/clockWork.hpp index 1732a530..cdfa10c7 100644 --- a/include/clockWork.hpp +++ b/include/clockWork.hpp @@ -104,6 +104,8 @@ iUhrType *ClockWork::getPointer(uint8_t type) { return &_de10x11Nero; case Ger10x11NeroFrame: return &_de10x11NeroFrame; + case Ger10x11bayerisch: + return &_de10x11bayerisch; case Nl10x11: return &_nl10x11; case Ger11x11: diff --git a/webpage/index.html b/webpage/index.html index 05dd3187..d2d2e42b 100644 --- a/webpage/index.html +++ b/webpage/index.html @@ -345,6 +345,7 @@

+ diff --git a/webpage/language/de.js b/webpage/language/de.js index 250a4d30..ef58288f 100644 --- a/webpage/language/de.js +++ b/webpage/language/de.js @@ -128,6 +128,7 @@ let TRANSLATION_DE_DE = { "de-10-11-clock": "🇩🇪 10 × 11 Uhr", "de-10-11-nero": "🇩🇪 10 × 11 Nero", "de-10-11-nero-frame": "🇩🇪 10 × 11 Nero Rahmen", + "de-10-11-bayerisch": "🇩🇪 10 × 11 Bayerisch", "de-10-11-schwaebisch": "🇩🇪 10 × 11 Schwäbisch", "de-11-11": "🇩🇪 11 × 11", "de-11-11-v2": "🇩🇪 11 × 11 Version 2", diff --git a/webpage/language/en.js b/webpage/language/en.js index 8330c76f..be780ef4 100644 --- a/webpage/language/en.js +++ b/webpage/language/en.js @@ -128,6 +128,7 @@ let TRANSLATION_EN_US = { "de-10-11-clock": "🇩🇪 10 × 11 Clock", "de-10-11-nero": "🇩🇪 10 × 11 Nero", "de-10-11-nero-frame": "🇩🇪 10 × 11 Nero Frame", + "de-10-11-bayerisch": "🇩🇪 10 × 11 Bavarian", "de-10-11-schwaebisch": "🇩🇪 10 × 11 Swabian Style", "de-11-11": "🇩🇪 11 × 11", "de-11-11-v2": "🇩🇪 11 × 11 Version 2", diff --git a/webpage/language/es.js b/webpage/language/es.js index 5214fd60..fadcaa73 100644 --- a/webpage/language/es.js +++ b/webpage/language/es.js @@ -104,6 +104,7 @@ let TRANSLATION_ES = { "de-10-11-clock": "🇩🇪 10 × 11 Uhr", "de-10-11-nero": "🇩🇪 10 × 11 Nero", "de-10-11-nero-frame": "🇩🇪 10 × 11 Nero Rahmen", + "de-10-11-bayerisch": "🇩🇪 10 × 11 bávaro", "de-10-11-schwaebisch": "🇩🇪 10 × 11 Estilo suabo", "de-11-11": "🇩🇪 11 × 11", "de-11-11-v2": "🇩🇪 11 × 11 Version 2", diff --git a/webpage/language/hu.js b/webpage/language/hu.js index c05d0792..82aae1ac 100644 --- a/webpage/language/hu.js +++ b/webpage/language/hu.js @@ -104,6 +104,7 @@ let TRANSLATION_HU = { "de-10-11-clock": "🇩🇪 10 × 11 óra", "de-10-11-nero": "🇩🇪 10 × 11 Nero", "de-10-11-nero-frame": "🇩🇪 10 × 11 Nero keretek", + "de-10-11-bayerisch": "🇩🇪 10 × 11 bajor", "de-10-11-schwaebisch": "🇩🇪 10 × 11 sváb stílus", "de-11-11": "🇩🇪 11 × 11", "de-11-11-v2": "🇩🇪 11 × 11 verzió 2", diff --git a/webpage/language/it.js b/webpage/language/it.js index 06c4772c..1b283241 100644 --- a/webpage/language/it.js +++ b/webpage/language/it.js @@ -104,6 +104,7 @@ let TRANSLATION_IT = { "de-10-11-clock": "🇩🇪 10 × 11 Uhr", "de-10-11-nero": "🇩🇪 10 × 11 Nero", "de-10-11-nero-frame": "🇩🇪 10 × 11 Nero Rahmen", + "de-10-11-bayerisch": "🇩🇪 10 × 11 bavarese", "de-10-11-schwaebisch": "🇩🇪 10 × 11 Stile svevo", "de-11-11": "🇩🇪 11 × 11", "de-11-11-v2": "🇩🇪 11 × 11 Version 2", diff --git a/webpage/language/nl.js b/webpage/language/nl.js index d5292306..d60278ca 100644 --- a/webpage/language/nl.js +++ b/webpage/language/nl.js @@ -104,6 +104,7 @@ let TRANSLATION_NL = { "de-10-11-clock": "🇩🇪 10 × 11 Clock", "de-10-11-nero": "🇩🇪 10 × 11 Nero", "de-10-11-nero-frame": "🇩🇪 10 × 11 Nero Frame", + "de-10-11-bayerisch": "🇩🇪 10 × 11 Beiers", "de-10-11-schwaebisch": "🇩🇪 10 × 11 Zwabische stijl", "de-11-11": "🇩🇪 11 × 11", "de-11-11-v2": "🇩🇪 11 × 11 Versie 2", diff --git a/webpage/language/ru.js b/webpage/language/ru.js index 2b05c23c..f140d4f0 100755 --- a/webpage/language/ru.js +++ b/webpage/language/ru.js @@ -92,6 +92,7 @@ let TRANSLATION_RU = { "de-10-11-clock": "🇩🇪 10 × 11 Часы", "de-10-11-nero": "🇩🇪 10 × 11 Nero", "de-10-11-nero-frame": "🇩🇪 10 × 11 Nero Rahmen", + "de-10-11-bayerisch": "🇩🇪 10 × 11 Баварский", "de-10-11-schwaebisch": "🇩🇪 10 × 11 Швабия", "de-11-11": "🇩🇪 11 × 11", "de-11-11-v2": "🇩🇪 11 × 11 V2", From cd8e77b0af85582d987d17cc2153ee11a095fe2c Mon Sep 17 00:00:00 2001 From: sogidaned <42743424+sogidaned@users.noreply.github.com> Date: Sat, 8 Mar 2025 12:41:01 +0100 Subject: [PATCH 04/23] add bavarian layout --- include/clockWork.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/clockWork.hpp b/include/clockWork.hpp index 1732a530..cdfa10c7 100644 --- a/include/clockWork.hpp +++ b/include/clockWork.hpp @@ -104,6 +104,8 @@ iUhrType *ClockWork::getPointer(uint8_t type) { return &_de10x11Nero; case Ger10x11NeroFrame: return &_de10x11NeroFrame; + case Ger10x11bayerisch: + return &_de10x11bayerisch; case Nl10x11: return &_nl10x11; case Ger11x11: From 8b06d7feaddf957f946085ab687bf8e6cc52aa8e Mon Sep 17 00:00:00 2001 From: sogidaned <42743424+sogidaned@users.noreply.github.com> Date: Sat, 8 Mar 2025 12:42:11 +0100 Subject: [PATCH 05/23] add bavarian layout --- include/Uhr.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/Uhr.h b/include/Uhr.h index 5bf5059a..ef4acd02 100644 --- a/include/Uhr.h +++ b/include/Uhr.h @@ -279,6 +279,7 @@ enum ClockType { Ger10x11schwaebisch = 20, Ger10x11Nero = 11, Ger10x11NeroFrame = 26, + Ger10x11bayerisch = 27, Ger11x11 = 3, Ger11x11V2 = 8, Ger11x11V3 = 14, From 055ce43528bc168fd6a3becdce8e57a5fec6b5db Mon Sep 17 00:00:00 2001 From: sogidaned <42743424+sogidaned@users.noreply.github.com> Date: Sat, 8 Mar 2025 12:43:06 +0100 Subject: [PATCH 06/23] add bavarian layout --- webpage/index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/webpage/index.html b/webpage/index.html index 05dd3187..d2d2e42b 100644 --- a/webpage/index.html +++ b/webpage/index.html @@ -345,6 +345,7 @@

+ From ae7d9203cce31485d3868d1470f78c9326802ed5 Mon Sep 17 00:00:00 2001 From: sogidaned <42743424+sogidaned@users.noreply.github.com> Date: Sat, 8 Mar 2025 12:43:36 +0100 Subject: [PATCH 07/23] add bavarian layout --- webpage/language/de.js | 1 + 1 file changed, 1 insertion(+) diff --git a/webpage/language/de.js b/webpage/language/de.js index 250a4d30..ef58288f 100644 --- a/webpage/language/de.js +++ b/webpage/language/de.js @@ -128,6 +128,7 @@ let TRANSLATION_DE_DE = { "de-10-11-clock": "🇩🇪 10 × 11 Uhr", "de-10-11-nero": "🇩🇪 10 × 11 Nero", "de-10-11-nero-frame": "🇩🇪 10 × 11 Nero Rahmen", + "de-10-11-bayerisch": "🇩🇪 10 × 11 Bayerisch", "de-10-11-schwaebisch": "🇩🇪 10 × 11 Schwäbisch", "de-11-11": "🇩🇪 11 × 11", "de-11-11-v2": "🇩🇪 11 × 11 Version 2", From 31126c696b03df8afcb85d0d20058ac552cb00dd Mon Sep 17 00:00:00 2001 From: sogidaned <42743424+sogidaned@users.noreply.github.com> Date: Sat, 8 Mar 2025 12:44:06 +0100 Subject: [PATCH 08/23] add bavarian layout --- webpage/language/en.js | 1 + 1 file changed, 1 insertion(+) diff --git a/webpage/language/en.js b/webpage/language/en.js index 8330c76f..be780ef4 100644 --- a/webpage/language/en.js +++ b/webpage/language/en.js @@ -128,6 +128,7 @@ let TRANSLATION_EN_US = { "de-10-11-clock": "🇩🇪 10 × 11 Clock", "de-10-11-nero": "🇩🇪 10 × 11 Nero", "de-10-11-nero-frame": "🇩🇪 10 × 11 Nero Frame", + "de-10-11-bayerisch": "🇩🇪 10 × 11 Bavarian", "de-10-11-schwaebisch": "🇩🇪 10 × 11 Swabian Style", "de-11-11": "🇩🇪 11 × 11", "de-11-11-v2": "🇩🇪 11 × 11 Version 2", From 8135ad2732fc94a4ea8e911e5fe6a7900d387730 Mon Sep 17 00:00:00 2001 From: sogidaned <42743424+sogidaned@users.noreply.github.com> Date: Sat, 8 Mar 2025 12:44:33 +0100 Subject: [PATCH 09/23] add bavarian layout --- webpage/language/es.js | 1 + 1 file changed, 1 insertion(+) diff --git a/webpage/language/es.js b/webpage/language/es.js index 5214fd60..fadcaa73 100644 --- a/webpage/language/es.js +++ b/webpage/language/es.js @@ -104,6 +104,7 @@ let TRANSLATION_ES = { "de-10-11-clock": "🇩🇪 10 × 11 Uhr", "de-10-11-nero": "🇩🇪 10 × 11 Nero", "de-10-11-nero-frame": "🇩🇪 10 × 11 Nero Rahmen", + "de-10-11-bayerisch": "🇩🇪 10 × 11 bávaro", "de-10-11-schwaebisch": "🇩🇪 10 × 11 Estilo suabo", "de-11-11": "🇩🇪 11 × 11", "de-11-11-v2": "🇩🇪 11 × 11 Version 2", From 5239ac3a242a70a6c13c8774de3b2553735b35a7 Mon Sep 17 00:00:00 2001 From: sogidaned <42743424+sogidaned@users.noreply.github.com> Date: Sat, 8 Mar 2025 12:45:43 +0100 Subject: [PATCH 10/23] add bavarian layout --- webpage/language/hu.js | 1 + 1 file changed, 1 insertion(+) diff --git a/webpage/language/hu.js b/webpage/language/hu.js index c05d0792..82aae1ac 100644 --- a/webpage/language/hu.js +++ b/webpage/language/hu.js @@ -104,6 +104,7 @@ let TRANSLATION_HU = { "de-10-11-clock": "🇩🇪 10 × 11 óra", "de-10-11-nero": "🇩🇪 10 × 11 Nero", "de-10-11-nero-frame": "🇩🇪 10 × 11 Nero keretek", + "de-10-11-bayerisch": "🇩🇪 10 × 11 bajor", "de-10-11-schwaebisch": "🇩🇪 10 × 11 sváb stílus", "de-11-11": "🇩🇪 11 × 11", "de-11-11-v2": "🇩🇪 11 × 11 verzió 2", From 2d54cee07ddd2bf58df2859dd219d7a687f93743 Mon Sep 17 00:00:00 2001 From: sogidaned <42743424+sogidaned@users.noreply.github.com> Date: Sat, 8 Mar 2025 12:46:12 +0100 Subject: [PATCH 11/23] =?UTF-8?q?"de-10-11-bayerisch":=20"=F0=9F=87=A9?= =?UTF-8?q?=F0=9F=87=AA=2010=20=C3=97=2011=20bavarese",?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- webpage/language/it.js | 1 + 1 file changed, 1 insertion(+) diff --git a/webpage/language/it.js b/webpage/language/it.js index 06c4772c..1b283241 100644 --- a/webpage/language/it.js +++ b/webpage/language/it.js @@ -104,6 +104,7 @@ let TRANSLATION_IT = { "de-10-11-clock": "🇩🇪 10 × 11 Uhr", "de-10-11-nero": "🇩🇪 10 × 11 Nero", "de-10-11-nero-frame": "🇩🇪 10 × 11 Nero Rahmen", + "de-10-11-bayerisch": "🇩🇪 10 × 11 bavarese", "de-10-11-schwaebisch": "🇩🇪 10 × 11 Stile svevo", "de-11-11": "🇩🇪 11 × 11", "de-11-11-v2": "🇩🇪 11 × 11 Version 2", From 7a36f38f4a08aed39ef21988f0efea1e1c8729f2 Mon Sep 17 00:00:00 2001 From: sogidaned <42743424+sogidaned@users.noreply.github.com> Date: Sat, 8 Mar 2025 12:48:27 +0100 Subject: [PATCH 12/23] add bavarian layout --- webpage/language/nl.js | 1 + 1 file changed, 1 insertion(+) diff --git a/webpage/language/nl.js b/webpage/language/nl.js index d5292306..d60278ca 100644 --- a/webpage/language/nl.js +++ b/webpage/language/nl.js @@ -104,6 +104,7 @@ let TRANSLATION_NL = { "de-10-11-clock": "🇩🇪 10 × 11 Clock", "de-10-11-nero": "🇩🇪 10 × 11 Nero", "de-10-11-nero-frame": "🇩🇪 10 × 11 Nero Frame", + "de-10-11-bayerisch": "🇩🇪 10 × 11 Beiers", "de-10-11-schwaebisch": "🇩🇪 10 × 11 Zwabische stijl", "de-11-11": "🇩🇪 11 × 11", "de-11-11-v2": "🇩🇪 11 × 11 Versie 2", From 664c524e2b20cb2237b2c9806f248e8b40959515 Mon Sep 17 00:00:00 2001 From: sogidaned <42743424+sogidaned@users.noreply.github.com> Date: Sat, 8 Mar 2025 12:49:03 +0100 Subject: [PATCH 13/23] add bavarian layout --- webpage/language/ru.js | 1 + 1 file changed, 1 insertion(+) diff --git a/webpage/language/ru.js b/webpage/language/ru.js index 2b05c23c..f140d4f0 100755 --- a/webpage/language/ru.js +++ b/webpage/language/ru.js @@ -92,6 +92,7 @@ let TRANSLATION_RU = { "de-10-11-clock": "🇩🇪 10 × 11 Часы", "de-10-11-nero": "🇩🇪 10 × 11 Nero", "de-10-11-nero-frame": "🇩🇪 10 × 11 Nero Rahmen", + "de-10-11-bayerisch": "🇩🇪 10 × 11 Баварский", "de-10-11-schwaebisch": "🇩🇪 10 × 11 Швабия", "de-11-11": "🇩🇪 11 × 11", "de-11-11-v2": "🇩🇪 11 × 11 V2", From f6dfa13d32d194f99c1ebef86c2b0fbab89c1207 Mon Sep 17 00:00:00 2001 From: Sogidaned Date: Mon, 10 Mar 2025 01:57:37 +0100 Subject: [PATCH 14/23] ldr auswahl und homeassistant verbesserung --- include/Uhr.h | 3 + include/clockWork.hpp | 21 +- include/mqtt.h | 16 +- include/mqtt.hpp | 570 +++++++++++++++++++++++++++++++++------ include/webPageAdapter.h | 8 + src/Wortuhr.cpp | 1 + webpage/index.html | 8 + webpage/language/de.js | 5 +- webpage/language/en.js | 5 +- webpage/language/es.js | 9 +- webpage/language/hu.js | 9 +- webpage/language/it.js | 9 +- webpage/language/nl.js | 9 +- webpage/language/ru.js | 9 +- webpage/script.js | 31 ++- 15 files changed, 599 insertions(+), 114 deletions(-) diff --git a/include/Uhr.h b/include/Uhr.h index ef4acd02..6398cc8b 100644 --- a/include/Uhr.h +++ b/include/Uhr.h @@ -151,6 +151,8 @@ struct GLOBAL { bool bootShowIP; Birthday birthday[MAX_BIRTHDAY_COUNT]; + + uint8_t ldrType; // 0 = 1 LDR, 1 = 4 LDR parallel }; GLOBAL G = {}; @@ -253,6 +255,7 @@ enum CommandWords { COMMAND_SET_AUTO_BRIGHT = 102, COMMAND_SET_LAYOUT_VARIANT = 103, COMMAND_SET_MQTT_HA_DISCOVERY = 104, + COMMAND_SET_LDR_TYPE = 105, COMMAND_SPEED = 152, diff --git a/include/clockWork.hpp b/include/clockWork.hpp index cdfa10c7..28a4e53b 100644 --- a/include/clockWork.hpp +++ b/include/clockWork.hpp @@ -45,13 +45,19 @@ void ClockWork::loopAutoBrightLogic() { if (adcValue < adcValue0Lux) adcValue0Lux = adcValue; float ldrValue = adcValue - adcValue0Lux; - // Derive LUX value from ldrValue via a second degree polinomial. - // The polinomial was derived using an Excel trend line, see - // LDR-Calibration.xlsx - const float x2 = 0.0427; - const float x1 = 2.679; - const float x0 = 10.857; - lux = x2 * ldrValue * ldrValue + x1 * ldrValue + x0; + + // Derive LUX value from ldrValue via a second degree polinomial based on LDR type + if (G.ldrType == 0) { // 1 LDR Sensor + const float x2 = 0.0427; + const float x1 = 2.679; + const float x0 = 10.857; + lux = x2 * ldrValue * ldrValue + x1 * ldrValue + x0; + } else { // 4 LDR Sensoren parallel + const float x2 = 0.0005; + const float x1 = 0.0687; + const float x0 = 3.9907; + lux = x2 * ldrValue * ldrValue - x1 * ldrValue + x0; + } } // Based on the LUX value derive the gain for the LEDs 0.0 - 100.0% @@ -1178,6 +1184,7 @@ void ClockWork::loop(struct tm &tm) { config["bootShowWifi"] = G.bootShowWifi; config["bootShowIP"] = G.bootShowIP; config["autoBrightEnabled"] = G.autoBrightEnabled; + config["ldrType"] = G.ldrType; config["isRomanLanguage"] = isRomanLanguage(); config["hasDreiviertel"] = usedUhrType->hasDreiviertel(); config["hasZwanzig"] = usedUhrType->hasZwanzig(); diff --git a/include/mqtt.h b/include/mqtt.h index 7850a0e1..18cb8ffa 100644 --- a/include/mqtt.h +++ b/include/mqtt.h @@ -1,6 +1,6 @@ #pragma once -#include +#include class Mqtt { private: @@ -11,15 +11,23 @@ class Mqtt { static void processScrollingText(const JsonDocument &doc); static void processColor(const JsonDocument &doc); static void processBrightness(const JsonDocument &doc); + static void processAutobrightSwitch(const JsonDocument &doc); + static void processBrightOffset(const JsonDocument &doc); + static void processBrightSlope(const JsonDocument &doc); + static void processScrollSpeed(const JsonDocument &doc); + static void processEffectSpeed(const JsonDocument &doc); + static bool checkIfMqttUserIsEmpty(); public: - Mqtt() = default; - ~Mqtt() = default; + Mqtt(); + ~Mqtt(); void init(); void loop(); void sendState(); void sendDiscovery(); - bool isConnected(); }; + +// Globale Instanz +extern Mqtt* mqttInstance; diff --git a/include/mqtt.hpp b/include/mqtt.hpp index b38ac676..9164599d 100644 --- a/include/mqtt.hpp +++ b/include/mqtt.hpp @@ -1,4 +1,3 @@ - #include "Uhr.h" #include "mqtt.h" #include @@ -14,8 +13,22 @@ extern WiFiClient client; +// Globale Instanz +Mqtt* mqttInstance = nullptr; + PubSubClient mqttClient(client); +// Konstruktor und Destruktor +Mqtt::Mqtt() { + mqttInstance = this; +} + +Mqtt::~Mqtt() { + if (mqttInstance == this) { + mqttInstance = nullptr; + } +} + // ToDo : MQTT Notify https: // www.home-assistant.io/integrations/notify.mqtt/ //------------------------------------------------------------------------------ @@ -41,12 +54,13 @@ void Mqtt::processState(const JsonDocument &doc) { if (doc.containsKey("state")) { const char *state = doc["state"]; if (!strcmp(state, "ON")) { - Serial.println("ON"); + Serial.println("MQTT: Turning ON"); led.setState(true); } else if (!strcmp(state, "OFF")) { - Serial.println("OFF"); + Serial.println("MQTT: Turning OFF"); led.setState(false); } + if (mqttInstance) mqttInstance->sendState(); } } @@ -71,27 +85,39 @@ None void Mqtt::processEffect(const JsonDocument &doc) { if (doc.containsKey("effect")) { const char *effect = doc["effect"]; + bool effectChanged = false; + if (!strcmp("Wordclock", effect)) { G.prog = COMMAND_MODE_WORD_CLOCK; - parametersChanged = true; + effectChanged = true; } else if (!strcmp("Seconds", effect)) { G.prog = COMMAND_MODE_SECONDS; + effectChanged = true; } else if (!strcmp("Digitalclock", effect)) { G.prog = COMMAND_MODE_DIGITAL_CLOCK; - parametersChanged = true; + effectChanged = true; } else if (!strcmp("Scrollingtext", effect)) { G.prog = COMMAND_MODE_SCROLLINGTEXT; + effectChanged = true; } else if (!strcmp("Rainbowcycle", effect)) { G.prog = COMMAND_MODE_RAINBOWCYCLE; + effectChanged = true; } else if (!strcmp("Rainbow", effect)) { G.prog = COMMAND_MODE_RAINBOW; + effectChanged = true; } else if (!strcmp("Color", effect)) { G.prog = COMMAND_MODE_COLOR; - parametersChanged = true; + effectChanged = true; } else if (!strcmp("Symbol", effect)) { G.prog = COMMAND_MODE_SYMBOL; + effectChanged = true; + } + + if (effectChanged) { + G.progInit = true; + parametersChanged = true; + if (mqttInstance) mqttInstance->sendState(); } - G.progInit = true; } } @@ -115,6 +141,14 @@ None void Mqtt::processScrollingText(const JsonDocument &doc) { if (doc.containsKey("scrolling_text")) { strcpy(G.scrollingText, doc["scrolling_text"]); + + // Send update to web interface + StaticJsonDocument<200> webDoc; + webDoc["command"] = "scrolltext"; + webDoc["scrolling_text"] = G.scrollingText; + char buffer[200]; + serializeJson(webDoc, buffer); + webSocket.broadcastTXT(buffer, strlen(buffer)); } } @@ -139,10 +173,51 @@ None void Mqtt::processColor(const JsonDocument &doc) { JsonObjectConst color = doc["color"]; if (!color.isNull()) { - G.color[Foreground] = - HsbColor(float(color["h"]) / 360.f, float(color["s"]) / 100.f, - G.color[Foreground].B); + uint8_t r = color["r"] | 0; + uint8_t g = color["g"] | 0; + uint8_t b = color["b"] | 0; + + // Direkte Umwandlung in HSB + float h, s; + float rf = r / 255.0f; + float gf = g / 255.0f; + float bf = b / 255.0f; + + float cmax = max(max(rf, gf), bf); + float cmin = min(min(rf, gf), bf); + float diff = cmax - cmin; + + // Berechne Hue + if (diff == 0) { + h = 0; + } else if (cmax == rf) { + h = fmod((60 * ((gf - bf) / diff) + 360), 360) / 360.0f; + } else if (cmax == gf) { + h = fmod((60 * ((bf - rf) / diff) + 120), 360) / 360.0f; + } else { + h = fmod((60 * ((rf - gf) / diff) + 240), 360) / 360.0f; + } + + // Berechne Saturation + s = (cmax == 0) ? 0 : (diff / cmax); + + // Behalte aktuelle Helligkeit bei + G.color[Foreground] = HsbColor(h, s, G.color[Foreground].B); parametersChanged = true; + + // Sende Update an die Weboberfläche + StaticJsonDocument<200> webDoc; + webDoc["command"] = "color"; + webDoc["h"] = round(h * 360); // Konvertiere zu 0-360 Grad + webDoc["s"] = round(s * 100); // Konvertiere zu 0-100% + webDoc["v"] = round(G.color[Foreground].B * 100); // Konvertiere zu 0-100% + char buffer[200]; + serializeJson(webDoc, buffer); + webSocket.broadcastTXT(buffer, strlen(buffer)); + + if (mqttInstance) { + mqttInstance->sendState(); + } } } @@ -168,10 +243,17 @@ None void Mqtt::processBrightness(const JsonDocument &doc) { if (doc.containsKey("brightness")) { - G.color[Foreground] = - HsbColor(G.color[Foreground].H, G.color[Foreground].S, - uint8_t(doc["brightness"]) / 255.f); + float brightness = float(uint8_t(doc["brightness"])) / 255.0f; + brightness = max(0.0f, min(1.0f, brightness)); + + G.color[Foreground] = HsbColor( + G.color[Foreground].H, + G.color[Foreground].S, + brightness + ); parametersChanged = true; + + if (mqttInstance) mqttInstance->sendState(); } } @@ -192,7 +274,7 @@ true if the MQTT user array is empty. false if the MQTT user array is not empty. */ -bool checkIfMqttUserIsEmpty() { +/* static */ bool Mqtt::checkIfMqttUserIsEmpty() { for (uint8_t i = 0; i < PAYLOAD_LENGTH; i++) { if (G.mqtt.user[i] != '\0' && !isSpace(G.mqtt.user[i])) { return false; // Array is not empty @@ -221,16 +303,47 @@ None void Mqtt::init() { mqttClient.setServer(G.mqtt.serverAdress, G.mqtt.port); mqttClient.setCallback(callback); + + // LWT (Last Will and Testament) konfigurieren + String availabilityTopic = String(G.mqtt.topic) + "/availability"; + if (checkIfMqttUserIsEmpty()) { - mqttClient.connect(G.mqtt.clientId); + mqttClient.connect(G.mqtt.clientId, + availabilityTopic.c_str(), + 0, // QoS + true, // retain + "offline"); // LWT Nachricht } else { - mqttClient.connect(G.mqtt.clientId, G.mqtt.user, G.mqtt.password); + mqttClient.connect(G.mqtt.clientId, + G.mqtt.user, + G.mqtt.password, + availabilityTopic.c_str(), + 0, // QoS + true, // retain + "offline"); // LWT Nachricht } delay(50); + + // Sofort nach Verbindung den Online-Status senden + mqttClient.publish(availabilityTopic.c_str(), "online", true); + + // Hauptsteuerung mqttClient.subscribe((std::string(G.mqtt.topic) + "/cmd").c_str()); delay(50); + + // Zusätzliche Topics + mqttClient.subscribe((std::string(G.mqtt.topic) + "/scrolltext/set").c_str()); + delay(50); + mqttClient.subscribe((std::string(G.mqtt.topic) + "/bright_offset/set").c_str()); + delay(50); + mqttClient.subscribe((std::string(G.mqtt.topic) + "/bright_slope/set").c_str()); + delay(50); + mqttClient.subscribe((std::string(G.mqtt.topic) + "/scrolling_text_speed/set").c_str()); + delay(50); + if (isConnected()) { Serial.println("MQTT Connected"); + sendState(); // Sende initialen Status } } @@ -263,7 +376,6 @@ void Mqtt::reInit() { init(); - // Check if maximum retries reached if (retryCount >= MAX_RETRIES_WITHIN_5_MINUTES) { Serial.println("Switched to hourly MQTT connect retry"); retryIntervall = RETRY_INTERVALL; @@ -294,7 +406,9 @@ true if the MQTT client is connected to the broker. false if the MQTT client is not connected to the broker. */ -bool Mqtt::isConnected() { return mqttClient.connected(); } +bool Mqtt::isConnected() { + return mqttClient.connected(); +} //------------------------------------------------------------------------------ @@ -314,10 +428,19 @@ None */ void Mqtt::loop() { + static uint32_t lastStateUpdate = 0; + const uint32_t STATE_UPDATE_INTERVAL = 30000; // Alle 30 Sekunden + if (!isConnected()) { reInit(); } mqttClient.loop(); + + // Regelmäßiges Update des Status + if (millis() - lastStateUpdate >= STATE_UPDATE_INTERVAL) { + sendState(); + lastStateUpdate = millis(); + } } //------------------------------------------------------------------------------ @@ -347,8 +470,9 @@ void Mqtt::callback(char *topic, byte *payload, unsigned int length) { msg[length] = '\0'; Serial.print("Received message ["); - Serial.print(msg); + Serial.print(topic); Serial.print("] "); + Serial.println(msg); // Deserialize JSON DeserializationError error = deserializeJson(doc, msg); @@ -358,12 +482,53 @@ void Mqtt::callback(char *topic, byte *payload, unsigned int length) { return; } - // Process received JSON data - processState(doc); - processEffect(doc); - processScrollingText(doc); - processColor(doc); - processBrightness(doc); + // Bestimme den Nachrichtentyp anhand des Topics + String topicStr = String(topic); + String baseTopic = String(G.mqtt.topic); + + if (topicStr == baseTopic + "/cmd") { + // Hauptsteuerung (wie bisher) + processState(doc); + processEffect(doc); + processScrollingText(doc); + processColor(doc); + processBrightness(doc); + } + else if (topicStr == baseTopic + "/scrolltext/set") { + processScrollingText(doc); + } + else if (topicStr == baseTopic + "/auto_bright/set") { + processAutobrightSwitch(doc); + } + else if (topicStr == baseTopic + "/bright_offset/set") { + processBrightOffset(doc); + } + else if (topicStr == baseTopic + "/bright_slope/set") { + processBrightSlope(doc); + } + else if (topicStr == baseTopic + "/scroll_speed/set") { + processScrollSpeed(doc); + } + else if (topicStr == baseTopic + "/effect_speed/set") { + processEffectSpeed(doc); + } + else if (topicStr == baseTopic + "/scrolling_text_speed/set") { + int speed = atoi(msg); // Konvertiere den String direkt in eine Zahl + G.effectSpeed = constrain(speed, 1, 10); + parametersChanged = true; + + // Publish new state + String stateTopic = String(G.mqtt.topic) + "/scrolling_text_speed/state"; + mqttClient.publish(stateTopic.c_str(), String(G.effectSpeed).c_str(), true); + + // Send update to web interface + StaticJsonDocument<200> webDoc; + webDoc["command"] = "speed"; + webDoc["value"] = G.effectSpeed; + char buffer[200]; + serializeJson(webDoc, buffer); + webSocket.broadcastTXT(buffer, strlen(buffer)); + } } //------------------------------------------------------------------------------ @@ -385,20 +550,114 @@ None */ void Mqtt::sendState() { - StaticJsonDocument<200> doc; + // Hauptstatus + { + StaticJsonDocument<200> doc; + doc["state"] = (led.getState()) ? "ON" : "OFF"; + doc["brightness"] = round(G.color[Foreground].B * 255); // Konvertiere von 0-1 zu 0-255 + doc["color_mode"] = "rgb"; + + // Konvertiere HSB zu RGB + float h = G.color[Foreground].H; + float s = G.color[Foreground].S; + float v = 1.0f; // Volle Helligkeit für Farbe + + float c = v * s; + float x = c * (1 - abs(fmod(h * 6, 2) - 1)); + float m = v - c; + + float r, g, b; + if (h < 1.0f/6.0f) { + r = c; g = x; b = 0; + } else if (h < 2.0f/6.0f) { + r = x; g = c; b = 0; + } else if (h < 3.0f/6.0f) { + r = 0; g = c; b = x; + } else if (h < 4.0f/6.0f) { + r = 0; g = x; b = c; + } else if (h < 5.0f/6.0f) { + r = x; g = 0; b = c; + } else { + r = c; g = 0; b = x; + } + + JsonObject color = doc.createNestedObject("color"); + color["r"] = round((r + m) * 255); + color["g"] = round((g + m) * 255); + color["b"] = round((b + m) * 255); + + switch(G.prog) { + case COMMAND_MODE_WORD_CLOCK: + doc["effect"] = "Wordclock"; + break; + case COMMAND_MODE_SECONDS: + doc["effect"] = "Seconds"; + break; + case COMMAND_MODE_DIGITAL_CLOCK: + doc["effect"] = "Digitalclock"; + break; + case COMMAND_MODE_SCROLLINGTEXT: + doc["effect"] = "Scrollingtext"; + break; + case COMMAND_MODE_RAINBOWCYCLE: + doc["effect"] = "Rainbowcycle"; + break; + case COMMAND_MODE_RAINBOW: + doc["effect"] = "Rainbow"; + break; + case COMMAND_MODE_COLOR: + doc["effect"] = "Color"; + break; + case COMMAND_MODE_SYMBOL: + doc["effect"] = "Symbol"; + break; + } - doc["state"] = (led.getState()) ? "ON" : "OFF"; + char buffer[200]; + serializeJson(doc, buffer); + mqttClient.publish((std::string(G.mqtt.topic) + "/status").c_str(), buffer, true); + } - JsonObject color = doc.createNestedObject("color"); + // Auto-Helligkeit Status + { + StaticJsonDocument<64> doc; + doc["state"] = G.autoBrightEnabled ? "ON" : "OFF"; + char buffer[64]; + serializeJson(doc, buffer); + mqttClient.publish((std::string(G.mqtt.topic) + "/auto_bright/state").c_str(), buffer, true); + } - color["h"] = G.color[Foreground].H * 360; - color["s"] = G.color[Foreground].S * 100; + // Helligkeitsoffset Status + { + mqttClient.publish((std::string(G.mqtt.topic) + "/bright_offset/state").c_str(), String(G.autoBrightOffset).c_str(), true); + } - doc["brightness"] = G.color[Foreground].B * 255; + // Helligkeitssteigung Status + { + mqttClient.publish((std::string(G.mqtt.topic) + "/bright_slope/state").c_str(), String(G.autoBrightSlope).c_str(), true); + } + + // Laufschriftgeschwindigkeit Status + { + mqttClient.publish((std::string(G.mqtt.topic) + "/scroll_speed/state").c_str(), String(G.effectSpeed).c_str(), true); + } + + // Scrollingtext Status + { + StaticJsonDocument<200> doc; + doc["scrolling_text"] = G.scrollingText; + char buffer[200]; + serializeJson(doc, buffer); + mqttClient.publish((std::string(G.mqtt.topic) + "/scrolltext/state").c_str(), buffer, true); + } - char buffer[200]; - serializeJson(doc, buffer); - mqttClient.publish((std::string(G.mqtt.topic) + "/status").c_str(), buffer); + // Effektgeschwindigkeit Status + { + mqttClient.publish((std::string(G.mqtt.topic) + "/effect_speed/state").c_str(), String(G.effectSpeed).c_str(), true); + } + + // Aktualisiere den Online-Status + mqttClient.publish((std::string(G.mqtt.topic) + "/availability").c_str(), "online", true); } //------------------------------------------------------------------------------ @@ -454,49 +713,204 @@ None */ void Mqtt::sendDiscovery() { + // Erstelle eine unique_id basierend auf MAC-Adresse + String unique_id = WiFi.macAddress(); + unique_id.replace(":", ""); // Entferne die Doppelpunkte aus der MAC-Adresse + + // Hauptlicht-Entity + { + StaticJsonDocument<700> root; + mqttClient.setBufferSize(700); + + // Basis-Konfiguration + root["name"] = String(G.mqtt.clientId); + root["unique_id"] = unique_id; + + // Topics + root["state_topic"] = String(G.mqtt.topic) + "/status"; + root["command_topic"] = String(G.mqtt.topic) + "/cmd"; + root["availability_topic"] = String(G.mqtt.topic) + "/availability"; + root["payload_available"] = "online"; + root["payload_not_available"] = "offline"; + + // Funktionen + root["brightness"] = true; + root["brightness_scale"] = 255; + JsonArray colorModes = root.createNestedArray("supported_color_modes"); + colorModes.add("rgb"); + root["optimistic"] = false; + + // Schema + root["schema"] = "json"; + + // Effekte + root["effect"] = true; + JsonArray effectList = root.createNestedArray("effect_list"); + effectList.add("Wordclock"); + effectList.add("Seconds"); + effectList.add("Digitalclock"); + effectList.add("Scrollingtext"); + effectList.add("Rainbowcycle"); + effectList.add("Rainbow"); + effectList.add("Color"); + effectList.add("Symbol"); + + // Geräte-Information + JsonObject device = root.createNestedObject("device"); + JsonArray identifiers = device.createNestedArray("identifiers"); + identifiers.add(unique_id); + device["name"] = G.mqtt.clientId; + device["sw_version"] = VERSION; + device["model"] = "Wortuhr"; + device["manufacturer"] = "ESPWortuhr"; + device["configuration_url"] = "http://" + WiFi.localIP().toString(); + + char buffer[700]; + serializeJson(root, buffer); + + // Debug-Ausgabe + Serial.println("Sending discovery message:"); + Serial.println(buffer); + + String discoveryTopic = String(HOMEASSISTANT_DISCOVERY_TOPIC) + "/light/" + unique_id + "/config"; + mqttClient.publish(discoveryTopic.c_str(), buffer, true); + } + + // 2. Lauftext Switch + { + StaticJsonDocument<512> root; + + root["name"] = String(G.mqtt.clientId) + " Scrolling Text"; + root["unique_id"] = unique_id + "_scrolltext"; + root["icon"] = "mdi:text"; + + JsonObject deviceCopy = root.createNestedObject("device"); + // Manuelles Kopieren der Device-Informationen + JsonArray deviceIdentifiers = deviceCopy.createNestedArray("identifiers"); + deviceIdentifiers.add(unique_id); + deviceCopy["name"] = G.mqtt.clientId; + deviceCopy["sw_version"] = VERSION; + deviceCopy["model"] = "Wortuhr"; + deviceCopy["manufacturer"] = "ESPWortuhr"; + deviceCopy["configuration_url"] = "http://" + WiFi.localIP().toString(); + + root["state_topic"] = std::string(G.mqtt.topic) + "/scrolltext/state"; + root["command_topic"] = std::string(G.mqtt.topic) + "/scrolltext/set"; + root["value_template"] = "{{ value_json.scrolling_text }}"; + root["command_template"] = "{ \"scrolling_text\": \"{{ value }}\" }"; + + char buffer[512]; + serializeJson(root, buffer); + mqttClient.publish((String(HOMEASSISTANT_DISCOVERY_TOPIC) + "/text/" + unique_id + "_scrolltext/config").c_str(), buffer, true); + } + + // 3. Auto-Helligkeit Switch + { + StaticJsonDocument<512> root; + + root["name"] = String(G.mqtt.clientId) + " Auto Brightness"; + root["unique_id"] = unique_id + "_auto_bright"; + root["device_class"] = "switch"; + root["icon"] = "mdi:brightness-auto"; + + JsonObject deviceCopy = root.createNestedObject("device"); + // Manuelles Kopieren der Device-Informationen + JsonArray deviceIdentifiers = deviceCopy.createNestedArray("identifiers"); + deviceIdentifiers.add(unique_id); + deviceCopy["name"] = G.mqtt.clientId; + deviceCopy["sw_version"] = VERSION; + deviceCopy["model"] = "Wortuhr"; + deviceCopy["manufacturer"] = "ESPWortuhr"; + deviceCopy["configuration_url"] = "http://" + WiFi.localIP().toString(); + + root["state_topic"] = std::string(G.mqtt.topic) + "/auto_bright/state"; + root["command_topic"] = std::string(G.mqtt.topic) + "/auto_bright/set"; + root["state_on"] = "ON"; + root["state_off"] = "OFF"; + root["payload_on"] = "{\"state\":\"ON\"}"; + root["payload_off"] = "{\"state\":\"OFF\"}"; + root["value_template"] = "{{ value_json.state }}"; + + char buffer[512]; + serializeJson(root, buffer); + mqttClient.publish((String(HOMEASSISTANT_DISCOVERY_TOPIC) + "/switch/" + unique_id + "_auto_bright/config").c_str(), buffer, true); + } + + // 4. Lauftext-Geschwindigkeit + { + StaticJsonDocument<512> root; + + root["name"] = String(G.mqtt.clientId) + " Scrolling Text Speed"; + root["unique_id"] = unique_id + "_scrolling_text_speed"; + root["icon"] = "mdi:speedometer"; + + JsonObject deviceCopy = root.createNestedObject("device"); + // Manuelles Kopieren der Device-Informationen + JsonArray deviceIdentifiers = deviceCopy.createNestedArray("identifiers"); + deviceIdentifiers.add(unique_id); + deviceCopy["name"] = G.mqtt.clientId; + deviceCopy["sw_version"] = VERSION; + deviceCopy["model"] = "Wortuhr"; + deviceCopy["manufacturer"] = "ESPWortuhr"; + deviceCopy["configuration_url"] = "http://" + WiFi.localIP().toString(); + + root["state_topic"] = std::string(G.mqtt.topic) + "/scrolling_text_speed/state"; + root["command_topic"] = std::string(G.mqtt.topic) + "/scrolling_text_speed/set"; + root["min"] = 1; + root["max"] = 10; + root["step"] = 1; + + char buffer[512]; + serializeJson(root, buffer); + mqttClient.publish((String(HOMEASSISTANT_DISCOVERY_TOPIC) + "/number/" + unique_id + "_scrolling_text_speed/config").c_str(), buffer, true); + } + + // Sende Online-Status + mqttClient.publish((std::string(G.mqtt.topic) + "/availability").c_str(), "online", true); +} + +// Neue Callback-Funktionen für die zusätzlichen Entitäten +void Mqtt::processAutobrightSwitch(const JsonDocument &doc) { + if (doc.containsKey("state")) { + const char *state = doc["state"]; + if (!strcmp(state, "ON")) { + G.autoBrightEnabled = true; + } else if (!strcmp(state, "OFF")) { + G.autoBrightEnabled = false; + } + parametersChanged = true; + if (mqttInstance) mqttInstance->sendState(); + } +} - StaticJsonDocument<700> root; - mqttClient.setBufferSize(700); - - root["brightness"] = true; - root["color_mode"] = true; - - JsonArray colorMode = root.createNestedArray("supported_color_modes"); - colorMode.add("hs"); - - root["schema"] = "json"; - root["name"] = G.mqtt.clientId; - - JsonObject device = root.createNestedObject("device"); - - JsonArray identifiers = device.createNestedArray("identifiers"); - identifiers.add(G.mqtt.topic); - - device["name"] = G.mqtt.clientId; - device["sw_version"] = VERSION; - device["configuration_url"] = "http://" + WiFi.localIP().toString(); - - root["state_topic"] = std::string(G.mqtt.topic) + "/status"; - root["command_topic"] = std::string(G.mqtt.topic) + "/cmd"; - root["unique_id"] = WiFi.macAddress(); - root["plattform"] = "mqtt"; - - root["effect"] = true; - JsonArray effectList = root.createNestedArray("effect_list"); - effectList.add("Wordclock"); - effectList.add("Seconds"); - effectList.add("Digitalclock"); - effectList.add("Scrollingtext"); - effectList.add("Rainbowcycle"); - effectList.add("Rainbow"); - effectList.add("Color"); - effectList.add("Symbol"); - - char buffer[700]; - serializeJson(root, buffer); - mqttClient.publish((std::string(HOMEASSISTANT_DISCOVERY_TOPIC) + - std::string("/light/") + std::string(G.mqtt.topic) + - std::string("/light/config")) - .c_str(), - buffer, true); +void Mqtt::processBrightOffset(const JsonDocument &doc) { + if (doc.containsKey("value")) { + G.autoBrightOffset = doc["value"]; + parametersChanged = true; + if (mqttInstance) mqttInstance->sendState(); + } } + +void Mqtt::processBrightSlope(const JsonDocument &doc) { + if (doc.containsKey("value")) { + G.autoBrightSlope = doc["value"]; + parametersChanged = true; + if (mqttInstance) mqttInstance->sendState(); + } +} +void Mqtt::processScrollSpeed(const JsonDocument &doc) { + if (doc.containsKey("value")) { + G.effectSpeed = doc["value"]; + parametersChanged = true; + if (mqttInstance) mqttInstance->sendState(); + } +} + +void Mqtt::processEffectSpeed(const JsonDocument &doc) { + if (doc.containsKey("value")) { + G.effectSpeed = doc["value"]; + parametersChanged = true; + if (mqttInstance) mqttInstance->sendState(); + } +} + diff --git a/include/webPageAdapter.h b/include/webPageAdapter.h index 6b46e55b..5f7a387f 100644 --- a/include/webPageAdapter.h +++ b/include/webPageAdapter.h @@ -369,6 +369,14 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t *payload, //------------------------------------------------------------------------------ + case COMMAND_SET_LDR_TYPE: { + G.ldrType = split(payload, 3); + eeprom::write(); + break; + } + + //------------------------------------------------------------------------------ + case COMMAND_SET_LANGUAGE_VARIANT: { G.languageVariant[ItIs15] = split(payload, 3); G.languageVariant[ItIs20] = split(payload, 6); diff --git a/src/Wortuhr.cpp b/src/Wortuhr.cpp index d82ebcc7..9c0a5678 100644 --- a/src/Wortuhr.cpp +++ b/src/Wortuhr.cpp @@ -220,6 +220,7 @@ void setup() { G.layoutVariant[ReverseMinDirection] = REVERSE_MINUTE_DIR; G.layoutVariant[MirrorVertical] = MIRROR_FRONT_VERTICAL; G.layoutVariant[MirrorHorizontal] = MIRROR_FRONT_HORIZONTAL; + G.ldrType = 0; // Standard: Einzelner LDR for (uint8_t i = 0; i < sizeof(G.languageVariant) / sizeof(G.languageVariant[0]); i++) { diff --git a/webpage/index.html b/webpage/index.html index d2d2e42b..db916324 100644 --- a/webpage/index.html +++ b/webpage/index.html @@ -701,6 +701,14 @@


+
+ + +
+
diff --git a/webpage/language/de.js b/webpage/language/de.js index ef58288f..d6775d3e 100644 --- a/webpage/language/de.js +++ b/webpage/language/de.js @@ -271,7 +271,10 @@ let TRANSLATION_DE_DE = { "value-offset": "Offset (0–255)", "help-slope": "Konfiguration der Steilheit:
Sie bestimmt den Grad der LED-Helligkeitsänderung bei Änderung des Umgebungslichts.
" + "0=schwache LED-Helligkeitsänderung, 16=neutrale LED-Helligkeitsänderung, 255=starke LED-Helligkeitsänderung", - "value-slope": "Steilheit (0–255)" + "value-slope": "Steilheit (0–255)", + "ldr-type": "LDR-Konfiguration:", + "ldr-single": "Einzelner LDR", + "ldr-quad": "Vier LDR parallel" }, "hostname": { "h2": "Hostname", diff --git a/webpage/language/en.js b/webpage/language/en.js index be780ef4..2ad79cb5 100644 --- a/webpage/language/en.js +++ b/webpage/language/en.js @@ -271,7 +271,10 @@ let TRANSLATION_EN_US = { "value-offset": "Offset (0–255)", "help-slope": "Configuration of the slope:
It determines the degree of LED brightness change when the ambient light changes.
" + "0=weak LED brightness change, 16=neutral LED brightness change, 255=strong LED brightness change", - "value-slope": "Slope (0–255)" + "value-slope": "Slope (0–255)", + "ldr-type": "LDR Configuration:", + "ldr-single": "Single LDR", + "ldr-quad": "Four LDR parallel" }, "hostname": { "h2": "Hostname", diff --git a/webpage/language/es.js b/webpage/language/es.js index fadcaa73..1e47db7b 100644 --- a/webpage/language/es.js +++ b/webpage/language/es.js @@ -242,9 +242,12 @@ let TRANSLATION_ES = { "help-offset": "Configuration of the brightness offset:
It determines the minimum brightness of the LEDs at 0 lux ambient light:
" + "0=LEDs off, n=LED brightness is n/255, 255=LEDs always have the maximum brightness", "value-offset": "Offset (0–255)", - "help-slope": "Configuration of the slope:
It determines the degree of LED brightness change when the ambient light changes.
" + - "0=weak LED brightness change, 16=neutral LED brightness change, 255=strong LED brightness change", - "value-slope": "Slope (0–255)" + "help-slope": "Configuración de la pendiente:
Determina el grado de cambio de brillo del LED cuando cambia la luz ambiental.
" + + "0=cambio de brillo LED débil, 16=cambio de brillo LED neutral, 255=cambio de brillo LED fuerte", + "value-slope": "Pendiente (0–255)", + "ldr-type": "Configuración LDR:", + "ldr-single": "LDR individual", + "ldr-quad": "Cuatro LDR en paralelo" }, "hostname": { "h2": "Hostname", diff --git a/webpage/language/hu.js b/webpage/language/hu.js index 82aae1ac..333bcaaa 100644 --- a/webpage/language/hu.js +++ b/webpage/language/hu.js @@ -242,9 +242,12 @@ let TRANSLATION_HU = { "help-offset": "A fényerő eltolás konfigurálása:
Meghatározza a LED-ek minimális fényerejét 0 lux környezeti fénynél:
" + "0=LED-ek kikapcsolva, n=LED fényerőssége n/255, 255=LED-ek mindig maximális fényerősségűek.", "value-offset": "Offset (0–255)", - "help-slope": "A lejtő konfigurációja:
Meghatározza a LED fényerejének változásának mértékét, amikor a környezeti fény változik.
" + - "0=gyenge LED-fényerő változás, 16=semleges LED-fényerő változás, 255=erős LED-fényerő változás.", - "value-slope": "Lejtő (0–255)" + "help-slope": "A meredekség konfigurálása:
Meghatározza a LED fényerő változásának mértékét a környezeti fény változásakor.
" + + "0=gyenge LED fényerő változás, 16=semleges LED fényerő változás, 255=erős LED fényerő változás", + "value-slope": "Meredekség (0–255)", + "ldr-type": "LDR konfiguráció:", + "ldr-single": "Egyetlen LDR", + "ldr-quad": "Négy LDR párhuzamosan" }, "hostname": { "h2": "Host-név", diff --git a/webpage/language/it.js b/webpage/language/it.js index 1b283241..29785f6b 100644 --- a/webpage/language/it.js +++ b/webpage/language/it.js @@ -242,9 +242,12 @@ let TRANSLATION_IT = { "help-offset": "Configurazione dell'offset di luminosità:
Determina la luminosità minima dei LED a 0 lux di luce ambientale:
" + "0=LED spenti, n=luminosità dei LED pari a n/255, 255=LED sempre con la massima luminosità", "value-offset": "Offset (0–255)", - "help-slope": "Configurazione della pendenza:
Determina il grado di variazione della luminosità del LED al variare della luce ambientale.
" + - "0=modifica debole della luminosità del LED, 16=modifica neutra della luminosità del LED, 255=modifica forte della luminosità del LED.", - "value-slope": "Pendenza (0–255)" + "help-slope": "Configurazione della pendenza:
Determina il grado di variazione della luminosità del LED quando cambia la luce ambientale.
" + + "0=variazione debole della luminosità LED, 16=variazione neutra della luminosità LED, 255=variazione forte della luminosità LED", + "value-slope": "Pendenza (0–255)", + "ldr-type": "Configurazione LDR:", + "ldr-single": "LDR singolo", + "ldr-quad": "Quattro LDR in parallelo" }, "hostname": { "h2": "Hostname", diff --git a/webpage/language/nl.js b/webpage/language/nl.js index d60278ca..44efae9b 100644 --- a/webpage/language/nl.js +++ b/webpage/language/nl.js @@ -245,9 +245,12 @@ let TRANSLATION_NL = { "help-offset": "Configuratie van de helderheidsoffset:
Deze bepaalt de minimale helderheid van de LED's bij 0 lux omgevingslicht:
" + "0=LEDs uit, n=LED-helderheid is n/255, 255=LEDs hebben altijd de maximale helderheid", "value-offset": "Offset (0–255)", - "help-slope": "Configuratie van de helling:
deze bepaalt de mate waarin de LED-helderheid verandert wanneer het omgevingslicht verandert.
" + - "0=zwakke LED helderheidsverandering, 16=neutrale LED helderheidsverandering, 255=sterke LED helderheidsverandering", - "value-slope": "Donker (0 – 255)" + "help-slope": "Configuratie van de helling:
Bepaalt de mate van LED-helderheidsverandering bij verandering van omgevingslicht.
" + + "0=zwakke LED-helderheidsverandering, 16=neutrale LED-helderheidsverandering, 255=sterke LED-helderheidsverandering", + "value-slope": "Helling (0–255)", + "ldr-type": "LDR-configuratie:", + "ldr-single": "Enkele LDR", + "ldr-quad": "Vier LDR parallel" }, "hostname": { "h2": "Hostnaam", diff --git a/webpage/language/ru.js b/webpage/language/ru.js index f140d4f0..c12aa721 100755 --- a/webpage/language/ru.js +++ b/webpage/language/ru.js @@ -230,9 +230,12 @@ let TRANSLATION_RU = { "help-offset": "Конфигурация смещения яркости:
Определяет минимальную яркость светодиодов при освещенности 0 люкс:
" + "0=Светодиоды выключены, n=Яркость светодиодов равна n/255, 255=Светодиоды всегда имеют максимальную яркость", "value-offset": "Смещение (0–255)", - "help-slope": "Конфигурация наклона:
Определяет степень изменения яркости светодиода при изменении освещенности.
" + - "0=слабое изменение яркости светодиода, 16=нейтральное изменение яркости светодиода, 255=сильное изменение яркости светодиода", - "value-slope": "Склон (0–255)" + "help-slope": "Настройка наклона:
Определяет степень изменения яркости светодиодов при изменении окружающего освещения.
" + + "0=слабое изменение яркости, 16=нейтральное изменение яркости, 255=сильное изменение яркости", + "value-slope": "Наклон (0–255)", + "ldr-type": "Конфигурация LDR:", + "ldr-single": "Один LDR", + "ldr-quad": "Четыре LDR параллельно" }, "hostname": { "h2": "Имя хоста", diff --git a/webpage/script.js b/webpage/script.js index 89ed60d6..9c6cbc38 100644 --- a/webpage/script.js +++ b/webpage/script.js @@ -150,6 +150,7 @@ var COMMAND_SET_BOOT = 101; var COMMAND_SET_AUTO_BRIGHT = 102; var COMMAND_SET_LAYOUT_VARIANT = 103; var COMMAND_SET_MQTT_HA_DISCOVERY = 104; +var COMMAND_SET_LDR_TYPE = 105; var COMMAND_SPEED = 152; @@ -343,6 +344,16 @@ function initWebsocket() { $("#mqtt-topic").set("value", data.MQTT_Topic); } + if (data.command === "scrolltext") { + $("#scrollingtext").set("value", data.scrolling_text); + } + + if (data.command === "speed") { + $("#slider-speed").set("value", data.value); + $("#slider-speed-value").fill(data.value); + effectSpeed = data.value; + } + if (data.command === "birthdays") { hasHappyBirthday = data.hasHappyBirthday; $("#birthdays-date0").set("value", data.birthdayDate0); @@ -420,6 +431,7 @@ function initWebsocket() { $("#auto-bright-enabled").set("value", autoBrightEnabled); enableSpecific("specific-layout-brightness-man", autoBrightEnabled === 0); enableSpecific("specific-layout-brightness-auto", autoBrightEnabled === 1); + $("#ldr-type").set("value", data.ldrType); } if (data.command === "set") { hsb[0][0] = data.hsb00; @@ -569,15 +581,9 @@ function updateManualTimeInput() { } function autoBrightUpdater() { - if (autoBrightInterval !== null || autoBrightEnabled !== 1) { - return; + if (autoBrightEnabled === 1) { + sendCmd(COMMAND_REQUEST_AUTO_BRIGHT); } - autoBrightInterval = setInterval(function() { - if ($("#auto-bright-enabled").get("value") === "1") { - sendCmd(COMMAND_REQUEST_AUTO_BRIGHT, 1); - } - }, 1000); // 1000 milliseconds intervall - debugMessage(`Start timer autoBrightInterval with ID ${autoBrightInterval}`); } function autoBrightStop() { @@ -993,4 +999,13 @@ $.ready(function() { sendCmd(COMMAND_SET_WHITETYPE, nstr(wType)); debugMessage("whitetype" + debugMessageReconfigured); }); + $("#ldr-type").on("change", function() { + setLdrType(); + }); }); + +function setLdrType() { + var ldrType = $("#ldr-type").get("value"); + sendCmd(COMMAND_SET_LDR_TYPE, ldrType); + debugMessage("LDR type" + debugMessageReconfigured); +} From 430c4863a81e0601853507c3aaf756fb26fc2aa1 Mon Sep 17 00:00:00 2001 From: sogidaned <42743424+sogidaned@users.noreply.github.com> Date: Mon, 10 Mar 2025 02:07:46 +0100 Subject: [PATCH 15/23] Update platformio.yml --- .github/workflows/platformio.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/platformio.yml b/.github/workflows/platformio.yml index e179a94a..6b9e6c76 100644 --- a/.github/workflows/platformio.yml +++ b/.github/workflows/platformio.yml @@ -8,16 +8,16 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Cache pip - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} restore-keys: | ${{ runner.os }}-pip- - name: Cache PlatformIO - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ~/.platformio key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} From 002693568fa111af497a3eba0a36ed959a1abd6d Mon Sep 17 00:00:00 2001 From: sogidaned <42743424+sogidaned@users.noreply.github.com> Date: Mon, 10 Mar 2025 02:12:14 +0100 Subject: [PATCH 16/23] Update platformio.yml --- .github/workflows/platformio.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/platformio.yml b/.github/workflows/platformio.yml index e179a94a..6b9e6c76 100644 --- a/.github/workflows/platformio.yml +++ b/.github/workflows/platformio.yml @@ -8,16 +8,16 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Cache pip - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} restore-keys: | ${{ runner.os }}-pip- - name: Cache PlatformIO - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ~/.platformio key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} From d3666d7bfc4b934fd4024795bb3e12508a013095 Mon Sep 17 00:00:00 2001 From: sogidaned <42743424+sogidaned@users.noreply.github.com> Date: Mon, 10 Mar 2025 02:17:15 +0100 Subject: [PATCH 17/23] Update clang-format.yml --- .github/workflows/clang-format.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index ee491716..18d5f9af 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - uses: DoozyX/clang-format-lint-action@v0.18.2 with: source: '.' From b58c4af2dad11d24f37677db2d9f28779f0f6297 Mon Sep 17 00:00:00 2001 From: sogidaned Date: Mon, 10 Mar 2025 13:17:54 +0100 Subject: [PATCH 18/23] clang-format tet --- include/Uhr.h | 2 +- include/clockWork.h | 9 + include/mqtt.h | 5 +- include/mqtt.hpp | 449 +++++++++++++++++++++++++++----------------- src/Wortuhr.cpp | 2 +- 5 files changed, 294 insertions(+), 173 deletions(-) diff --git a/include/Uhr.h b/include/Uhr.h index 6398cc8b..de180f25 100644 --- a/include/Uhr.h +++ b/include/Uhr.h @@ -152,7 +152,7 @@ struct GLOBAL { Birthday birthday[MAX_BIRTHDAY_COUNT]; - uint8_t ldrType; // 0 = 1 LDR, 1 = 4 LDR parallel + uint8_t ldrType; // 0 = 1 LDR, 1 = 4 LDR parallel }; GLOBAL G = {}; diff --git a/include/clockWork.h b/include/clockWork.h index 2f278add..a54e9743 100644 --- a/include/clockWork.h +++ b/include/clockWork.h @@ -76,6 +76,15 @@ class ClockWork { //------------------------------------------------------------------------------ iUhrType *getPointer(uint8_t type); void initLedStrip(uint8_t num); + float getLuxValue() const { return lux; } + float getAdcValue() const { + uint16_t adcRaw = analogRead(A0); + float voltage = (adcRaw * 3.3f) / 1023.0f; + return round(voltage * 100.0f) / 100.0f; // Runde auf 2 Nachkommastellen + } + uint16_t getAdcRawValue() const { + return analogRead(A0); // Roher ADC-Wert (0-1023) + } //------------------------------------------------------------------------------ // Minute Functions diff --git a/include/mqtt.h b/include/mqtt.h index 18cb8ffa..3f648542 100644 --- a/include/mqtt.h +++ b/include/mqtt.h @@ -1,9 +1,12 @@ #pragma once #include +#include "Uhr.h" +#include "clockWork.h" class Mqtt { private: + ClockWork& clockWork; // Referenz auf ClockWork-Instanz void reInit(); static void callback(char *topic, byte *payload, unsigned int length); static void processState(const JsonDocument &doc); @@ -19,7 +22,7 @@ class Mqtt { static bool checkIfMqttUserIsEmpty(); public: - Mqtt(); + Mqtt(ClockWork& cw); // Nur Deklaration ~Mqtt(); void init(); diff --git a/include/mqtt.hpp b/include/mqtt.hpp index 9164599d..ff62a62b 100644 --- a/include/mqtt.hpp +++ b/include/mqtt.hpp @@ -14,14 +14,12 @@ extern WiFiClient client; // Globale Instanz -Mqtt* mqttInstance = nullptr; +Mqtt *mqttInstance = nullptr; PubSubClient mqttClient(client); // Konstruktor und Destruktor -Mqtt::Mqtt() { - mqttInstance = this; -} +Mqtt::Mqtt(ClockWork& cw) : clockWork(cw) { mqttInstance = this; } Mqtt::~Mqtt() { if (mqttInstance == this) { @@ -60,7 +58,8 @@ void Mqtt::processState(const JsonDocument &doc) { Serial.println("MQTT: Turning OFF"); led.setState(false); } - if (mqttInstance) mqttInstance->sendState(); + if (mqttInstance) + mqttInstance->sendState(); } } @@ -86,7 +85,7 @@ void Mqtt::processEffect(const JsonDocument &doc) { if (doc.containsKey("effect")) { const char *effect = doc["effect"]; bool effectChanged = false; - + if (!strcmp("Wordclock", effect)) { G.prog = COMMAND_MODE_WORD_CLOCK; effectChanged = true; @@ -112,11 +111,12 @@ void Mqtt::processEffect(const JsonDocument &doc) { G.prog = COMMAND_MODE_SYMBOL; effectChanged = true; } - + if (effectChanged) { G.progInit = true; parametersChanged = true; - if (mqttInstance) mqttInstance->sendState(); + if (mqttInstance) + mqttInstance->sendState(); } } } @@ -176,17 +176,17 @@ void Mqtt::processColor(const JsonDocument &doc) { uint8_t r = color["r"] | 0; uint8_t g = color["g"] | 0; uint8_t b = color["b"] | 0; - + // Direkte Umwandlung in HSB float h, s; float rf = r / 255.0f; float gf = g / 255.0f; float bf = b / 255.0f; - + float cmax = max(max(rf, gf), bf); float cmin = min(min(rf, gf), bf); float diff = cmax - cmin; - + // Berechne Hue if (diff == 0) { h = 0; @@ -197,24 +197,24 @@ void Mqtt::processColor(const JsonDocument &doc) { } else { h = fmod((60 * ((rf - gf) / diff) + 240), 360) / 360.0f; } - + // Berechne Saturation s = (cmax == 0) ? 0 : (diff / cmax); - + // Behalte aktuelle Helligkeit bei G.color[Foreground] = HsbColor(h, s, G.color[Foreground].B); parametersChanged = true; - + // Sende Update an die Weboberfläche StaticJsonDocument<200> webDoc; webDoc["command"] = "color"; - webDoc["h"] = round(h * 360); // Konvertiere zu 0-360 Grad - webDoc["s"] = round(s * 100); // Konvertiere zu 0-100% - webDoc["v"] = round(G.color[Foreground].B * 100); // Konvertiere zu 0-100% + webDoc["h"] = round(h * 360); // Konvertiere zu 0-360 Grad + webDoc["s"] = round(s * 100); // Konvertiere zu 0-100% + webDoc["v"] = round(G.color[Foreground].B * 100); // Konvertiere zu 0-100% char buffer[200]; serializeJson(webDoc, buffer); webSocket.broadcastTXT(buffer, strlen(buffer)); - + if (mqttInstance) { mqttInstance->sendState(); } @@ -245,15 +245,13 @@ void Mqtt::processBrightness(const JsonDocument &doc) { if (doc.containsKey("brightness")) { float brightness = float(uint8_t(doc["brightness"])) / 255.0f; brightness = max(0.0f, min(1.0f, brightness)); - - G.color[Foreground] = HsbColor( - G.color[Foreground].H, - G.color[Foreground].S, - brightness - ); + + G.color[Foreground] = + HsbColor(G.color[Foreground].H, G.color[Foreground].S, brightness); parametersChanged = true; - - if (mqttInstance) mqttInstance->sendState(); + + if (mqttInstance) + mqttInstance->sendState(); } } @@ -406,9 +404,7 @@ true if the MQTT client is connected to the broker. false if the MQTT client is not connected to the broker. */ -bool Mqtt::isConnected() { - return mqttClient.connected(); -} +bool Mqtt::isConnected() { return mqttClient.connected(); } //------------------------------------------------------------------------------ @@ -493,34 +489,26 @@ void Mqtt::callback(char *topic, byte *payload, unsigned int length) { processScrollingText(doc); processColor(doc); processBrightness(doc); - } - else if (topicStr == baseTopic + "/scrolltext/set") { + } else if (topicStr == baseTopic + "/scrolltext/set") { processScrollingText(doc); - } - else if (topicStr == baseTopic + "/auto_bright/set") { - processAutobrightSwitch(doc); - } - else if (topicStr == baseTopic + "/bright_offset/set") { + } else if (topicStr == baseTopic + "/bright_offset/set") { processBrightOffset(doc); - } - else if (topicStr == baseTopic + "/bright_slope/set") { + } else if (topicStr == baseTopic + "/bright_slope/set") { processBrightSlope(doc); - } - else if (topicStr == baseTopic + "/scroll_speed/set") { + } else if (topicStr == baseTopic + "/scroll_speed/set") { processScrollSpeed(doc); - } - else if (topicStr == baseTopic + "/effect_speed/set") { + } else if (topicStr == baseTopic + "/effect_speed/set") { processEffectSpeed(doc); - } - else if (topicStr == baseTopic + "/scrolling_text_speed/set") { - int speed = atoi(msg); // Konvertiere den String direkt in eine Zahl + } else if (topicStr == baseTopic + "/scrolling_text_speed/set") { + int speed = atoi(msg); // Konvertiere den String direkt in eine Zahl G.effectSpeed = constrain(speed, 1, 10); parametersChanged = true; - + // Publish new state String stateTopic = String(G.mqtt.topic) + "/scrolling_text_speed/state"; - mqttClient.publish(stateTopic.c_str(), String(G.effectSpeed).c_str(), true); - + mqttClient.publish(stateTopic.c_str(), String(G.effectSpeed).c_str(), + true); + // Send update to web interface StaticJsonDocument<200> webDoc; webDoc["command"] = "speed"; @@ -554,92 +542,116 @@ void Mqtt::sendState() { { StaticJsonDocument<200> doc; doc["state"] = (led.getState()) ? "ON" : "OFF"; - doc["brightness"] = round(G.color[Foreground].B * 255); // Konvertiere von 0-1 zu 0-255 + doc["brightness"] = + round(G.color[Foreground].B * 255); // Konvertiere von 0-1 zu 0-255 doc["color_mode"] = "rgb"; // Konvertiere HSB zu RGB float h = G.color[Foreground].H; float s = G.color[Foreground].S; - float v = 1.0f; // Volle Helligkeit für Farbe - + float v = 1.0f; // Volle Helligkeit für Farbe + float c = v * s; float x = c * (1 - abs(fmod(h * 6, 2) - 1)); float m = v - c; - + float r, g, b; - if (h < 1.0f/6.0f) { - r = c; g = x; b = 0; - } else if (h < 2.0f/6.0f) { - r = x; g = c; b = 0; - } else if (h < 3.0f/6.0f) { - r = 0; g = c; b = x; - } else if (h < 4.0f/6.0f) { - r = 0; g = x; b = c; - } else if (h < 5.0f/6.0f) { - r = x; g = 0; b = c; + if (h < 1.0f / 6.0f) { + r = c; + g = x; + b = 0; + } else if (h < 2.0f / 6.0f) { + r = x; + g = c; + b = 0; + } else if (h < 3.0f / 6.0f) { + r = 0; + g = c; + b = x; + } else if (h < 4.0f / 6.0f) { + r = 0; + g = x; + b = c; + } else if (h < 5.0f / 6.0f) { + r = x; + g = 0; + b = c; } else { - r = c; g = 0; b = x; + r = c; + g = 0; + b = x; } - + JsonObject color = doc.createNestedObject("color"); color["r"] = round((r + m) * 255); color["g"] = round((g + m) * 255); color["b"] = round((b + m) * 255); - - switch(G.prog) { - case COMMAND_MODE_WORD_CLOCK: - doc["effect"] = "Wordclock"; - break; - case COMMAND_MODE_SECONDS: - doc["effect"] = "Seconds"; - break; - case COMMAND_MODE_DIGITAL_CLOCK: - doc["effect"] = "Digitalclock"; - break; - case COMMAND_MODE_SCROLLINGTEXT: - doc["effect"] = "Scrollingtext"; - break; - case COMMAND_MODE_RAINBOWCYCLE: - doc["effect"] = "Rainbowcycle"; - break; - case COMMAND_MODE_RAINBOW: - doc["effect"] = "Rainbow"; - break; - case COMMAND_MODE_COLOR: - doc["effect"] = "Color"; - break; - case COMMAND_MODE_SYMBOL: - doc["effect"] = "Symbol"; - break; + + switch (G.prog) { + case COMMAND_MODE_WORD_CLOCK: + doc["effect"] = "Wordclock"; + break; + case COMMAND_MODE_SECONDS: + doc["effect"] = "Seconds"; + break; + case COMMAND_MODE_DIGITAL_CLOCK: + doc["effect"] = "Digitalclock"; + break; + case COMMAND_MODE_SCROLLINGTEXT: + doc["effect"] = "Scrollingtext"; + break; + case COMMAND_MODE_RAINBOWCYCLE: + doc["effect"] = "Rainbowcycle"; + break; + case COMMAND_MODE_RAINBOW: + doc["effect"] = "Rainbow"; + break; + case COMMAND_MODE_COLOR: + doc["effect"] = "Color"; + break; + case COMMAND_MODE_SYMBOL: + doc["effect"] = "Symbol"; + break; } char buffer[200]; serializeJson(doc, buffer); - mqttClient.publish((std::string(G.mqtt.topic) + "/status").c_str(), buffer, true); + mqttClient.publish((std::string(G.mqtt.topic) + "/status").c_str(), + buffer, true); } - // Auto-Helligkeit Status + // Diagnose-Werte senden { - StaticJsonDocument<64> doc; - doc["state"] = G.autoBrightEnabled ? "ON" : "OFF"; - char buffer[64]; + StaticJsonDocument<200> doc; + doc["lux"] = round(clockWork.getLuxValue()); // Runde den Lux-Wert + doc["led_gain"] = round(ledGain); // Konvertiere zu Prozent und runde + doc["adc_value"] = clockWork.getAdcValue(); // Spannungswert (bereits gerundet) + doc["adc_raw"] = clockWork.getAdcRawValue(); // Roher ADC-Wert + char buffer[200]; serializeJson(doc, buffer); - mqttClient.publish((std::string(G.mqtt.topic) + "/auto_bright/state").c_str(), buffer, true); + mqttClient.publish((std::string(G.mqtt.topic) + "/diagnostics").c_str(), + buffer, true); } // Helligkeitsoffset Status { - mqttClient.publish((std::string(G.mqtt.topic) + "/bright_offset/state").c_str(), String(G.autoBrightOffset).c_str(), true); + mqttClient.publish( + (std::string(G.mqtt.topic) + "/bright_offset/state").c_str(), + String(G.autoBrightOffset).c_str(), true); } // Helligkeitssteigung Status { - mqttClient.publish((std::string(G.mqtt.topic) + "/bright_slope/state").c_str(), String(G.autoBrightSlope).c_str(), true); + mqttClient.publish( + (std::string(G.mqtt.topic) + "/bright_slope/state").c_str(), + String(G.autoBrightSlope).c_str(), true); } - + // Laufschriftgeschwindigkeit Status { - mqttClient.publish((std::string(G.mqtt.topic) + "/scroll_speed/state").c_str(), String(G.effectSpeed).c_str(), true); + mqttClient.publish( + (std::string(G.mqtt.topic) + "/scroll_speed/state").c_str(), + String(G.effectSpeed).c_str(), true); } // Scrollingtext Status @@ -648,16 +660,21 @@ void Mqtt::sendState() { doc["scrolling_text"] = G.scrollingText; char buffer[200]; serializeJson(doc, buffer); - mqttClient.publish((std::string(G.mqtt.topic) + "/scrolltext/state").c_str(), buffer, true); + mqttClient.publish( + (std::string(G.mqtt.topic) + "/scrolltext/state").c_str(), buffer, + true); } // Effektgeschwindigkeit Status { - mqttClient.publish((std::string(G.mqtt.topic) + "/effect_speed/state").c_str(), String(G.effectSpeed).c_str(), true); + mqttClient.publish( + (std::string(G.mqtt.topic) + "/effect_speed/state").c_str(), + String(G.effectSpeed).c_str(), true); } - + // Aktualisiere den Online-Status - mqttClient.publish((std::string(G.mqtt.topic) + "/availability").c_str(), "online", true); + mqttClient.publish((std::string(G.mqtt.topic) + "/availability").c_str(), + "online", true); } //------------------------------------------------------------------------------ @@ -737,7 +754,7 @@ void Mqtt::sendDiscovery() { root["brightness"] = true; root["brightness_scale"] = 255; JsonArray colorModes = root.createNestedArray("supported_color_modes"); - colorModes.add("rgb"); + colorModes.add("rgb"); // Korrekter Color Mode root["optimistic"] = false; // Schema @@ -761,36 +778,33 @@ void Mqtt::sendDiscovery() { identifiers.add(unique_id); device["name"] = G.mqtt.clientId; device["sw_version"] = VERSION; - device["model"] = "Wortuhr"; + device["model"] = "Word Clock"; device["manufacturer"] = "ESPWortuhr"; device["configuration_url"] = "http://" + WiFi.localIP().toString(); char buffer[700]; serializeJson(root, buffer); - - // Debug-Ausgabe - Serial.println("Sending discovery message:"); - Serial.println(buffer); - - String discoveryTopic = String(HOMEASSISTANT_DISCOVERY_TOPIC) + "/light/" + unique_id + "/config"; + + String discoveryTopic = + String(HOMEASSISTANT_DISCOVERY_TOPIC) + "/light/" + unique_id + + "/config"; mqttClient.publish(discoveryTopic.c_str(), buffer, true); } // 2. Lauftext Switch { StaticJsonDocument<512> root; - - root["name"] = String(G.mqtt.clientId) + " Scrolling Text"; + + root["name"] = "Scrolling Text"; root["unique_id"] = unique_id + "_scrolltext"; root["icon"] = "mdi:text"; - + JsonObject deviceCopy = root.createNestedObject("device"); - // Manuelles Kopieren der Device-Informationen JsonArray deviceIdentifiers = deviceCopy.createNestedArray("identifiers"); deviceIdentifiers.add(unique_id); deviceCopy["name"] = G.mqtt.clientId; deviceCopy["sw_version"] = VERSION; - deviceCopy["model"] = "Wortuhr"; + deviceCopy["model"] = "Word Clock"; deviceCopy["manufacturer"] = "ESPWortuhr"; deviceCopy["configuration_url"] = "http://" + WiFi.localIP().toString(); @@ -798,96 +812,187 @@ void Mqtt::sendDiscovery() { root["command_topic"] = std::string(G.mqtt.topic) + "/scrolltext/set"; root["value_template"] = "{{ value_json.scrolling_text }}"; root["command_template"] = "{ \"scrolling_text\": \"{{ value }}\" }"; - + char buffer[512]; serializeJson(root, buffer); - mqttClient.publish((String(HOMEASSISTANT_DISCOVERY_TOPIC) + "/text/" + unique_id + "_scrolltext/config").c_str(), buffer, true); + mqttClient.publish( + (String(HOMEASSISTANT_DISCOVERY_TOPIC) + "/text/" + unique_id + + "_scrolltext/config") + .c_str(), + buffer, true); } - // 3. Auto-Helligkeit Switch + // 3. Lauftext-Geschwindigkeit { StaticJsonDocument<512> root; - - root["name"] = String(G.mqtt.clientId) + " Auto Brightness"; - root["unique_id"] = unique_id + "_auto_bright"; - root["device_class"] = "switch"; - root["icon"] = "mdi:brightness-auto"; - + + root["name"] = "Scrolling Speed"; + root["unique_id"] = unique_id + "_scrolling_text_speed"; + root["icon"] = "mdi:speedometer"; + root["device_class"] = "speed"; // Korrekte Device Class + JsonObject deviceCopy = root.createNestedObject("device"); - // Manuelles Kopieren der Device-Informationen JsonArray deviceIdentifiers = deviceCopy.createNestedArray("identifiers"); deviceIdentifiers.add(unique_id); deviceCopy["name"] = G.mqtt.clientId; deviceCopy["sw_version"] = VERSION; - deviceCopy["model"] = "Wortuhr"; + deviceCopy["model"] = "Word Clock"; deviceCopy["manufacturer"] = "ESPWortuhr"; deviceCopy["configuration_url"] = "http://" + WiFi.localIP().toString(); - root["state_topic"] = std::string(G.mqtt.topic) + "/auto_bright/state"; - root["command_topic"] = std::string(G.mqtt.topic) + "/auto_bright/set"; - root["state_on"] = "ON"; - root["state_off"] = "OFF"; - root["payload_on"] = "{\"state\":\"ON\"}"; - root["payload_off"] = "{\"state\":\"OFF\"}"; - root["value_template"] = "{{ value_json.state }}"; - + root["state_topic"] = + std::string(G.mqtt.topic) + "/scrolling_text_speed/state"; + root["command_topic"] = + std::string(G.mqtt.topic) + "/scrolling_text_speed/set"; + root["min"] = 1; + root["max"] = 10; + root["step"] = 1; + char buffer[512]; serializeJson(root, buffer); - mqttClient.publish((String(HOMEASSISTANT_DISCOVERY_TOPIC) + "/switch/" + unique_id + "_auto_bright/config").c_str(), buffer, true); + mqttClient.publish( + (String(HOMEASSISTANT_DISCOVERY_TOPIC) + "/number/" + unique_id + + "_scrolling_text_speed/config") + .c_str(), + buffer, true); } - // 4. Lauftext-Geschwindigkeit + // Diagnose-Entities { + // Lux Sensor StaticJsonDocument<512> root; - - root["name"] = String(G.mqtt.clientId) + " Scrolling Text Speed"; - root["unique_id"] = unique_id + "_scrolling_text_speed"; - root["icon"] = "mdi:speedometer"; - + root["name"] = "Illuminance"; + root["unique_id"] = unique_id + "_lux"; + root["device_class"] = "illuminance"; + root["unit_of_measurement"] = "lx"; + root["icon"] = "mdi:white-balance-sunny"; + root["state_class"] = "measurement"; + JsonObject deviceCopy = root.createNestedObject("device"); - // Manuelles Kopieren der Device-Informationen JsonArray deviceIdentifiers = deviceCopy.createNestedArray("identifiers"); deviceIdentifiers.add(unique_id); deviceCopy["name"] = G.mqtt.clientId; deviceCopy["sw_version"] = VERSION; - deviceCopy["model"] = "Wortuhr"; + deviceCopy["model"] = "Word Clock"; deviceCopy["manufacturer"] = "ESPWortuhr"; deviceCopy["configuration_url"] = "http://" + WiFi.localIP().toString(); - root["state_topic"] = std::string(G.mqtt.topic) + "/scrolling_text_speed/state"; - root["command_topic"] = std::string(G.mqtt.topic) + "/scrolling_text_speed/set"; - root["min"] = 1; - root["max"] = 10; - root["step"] = 1; - + root["state_topic"] = std::string(G.mqtt.topic) + "/diagnostics"; + root["value_template"] = "{{ value_json.lux }}"; + char buffer[512]; serializeJson(root, buffer); - mqttClient.publish((String(HOMEASSISTANT_DISCOVERY_TOPIC) + "/number/" + unique_id + "_scrolling_text_speed/config").c_str(), buffer, true); + mqttClient.publish( + (String(HOMEASSISTANT_DISCOVERY_TOPIC) + "/sensor/" + unique_id + + "_lux/config") + .c_str(), + buffer, true); } - // Sende Online-Status - mqttClient.publish((std::string(G.mqtt.topic) + "/availability").c_str(), "online", true); -} + { + // LED Gain Sensor + StaticJsonDocument<512> root; + root["name"] = "LED Gain"; + root["unique_id"] = unique_id + "_led_gain"; + root["device_class"] = "power_factor"; + root["unit_of_measurement"] = "%"; + root["icon"] = "mdi:brightness-percent"; + root["state_class"] = "measurement"; -// Neue Callback-Funktionen für die zusätzlichen Entitäten -void Mqtt::processAutobrightSwitch(const JsonDocument &doc) { - if (doc.containsKey("state")) { - const char *state = doc["state"]; - if (!strcmp(state, "ON")) { - G.autoBrightEnabled = true; - } else if (!strcmp(state, "OFF")) { - G.autoBrightEnabled = false; - } - parametersChanged = true; - if (mqttInstance) mqttInstance->sendState(); + JsonObject deviceCopy = root.createNestedObject("device"); + JsonArray deviceIdentifiers = deviceCopy.createNestedArray("identifiers"); + deviceIdentifiers.add(unique_id); + deviceCopy["name"] = G.mqtt.clientId; + deviceCopy["sw_version"] = VERSION; + deviceCopy["model"] = "Word Clock"; + deviceCopy["manufacturer"] = "ESPWortuhr"; + deviceCopy["configuration_url"] = "http://" + WiFi.localIP().toString(); + + root["state_topic"] = std::string(G.mqtt.topic) + "/diagnostics"; + root["value_template"] = "{{ value_json.led_gain }}"; + + char buffer[512]; + serializeJson(root, buffer); + mqttClient.publish( + (String(HOMEASSISTANT_DISCOVERY_TOPIC) + "/sensor/" + unique_id + + "_led_gain/config") + .c_str(), + buffer, true); } + + { + // ADC Value Sensor + StaticJsonDocument<512> root; + root["name"] = "ADC Value"; + root["unique_id"] = unique_id + "_adc_value"; + root["device_class"] = "voltage"; + root["unit_of_measurement"] = "V"; + root["icon"] = "mdi:flash"; + root["state_class"] = "measurement"; + root["value_template"] = "{{ value_json.adc_value | round(2) }}"; + + JsonObject deviceCopy = root.createNestedObject("device"); + JsonArray deviceIdentifiers = deviceCopy.createNestedArray("identifiers"); + deviceIdentifiers.add(unique_id); + deviceCopy["name"] = G.mqtt.clientId; + deviceCopy["sw_version"] = VERSION; + deviceCopy["model"] = "Word Clock"; + deviceCopy["manufacturer"] = "ESPWortuhr"; + deviceCopy["configuration_url"] = "http://" + WiFi.localIP().toString(); + + root["state_topic"] = std::string(G.mqtt.topic) + "/diagnostics"; + + char buffer[512]; + serializeJson(root, buffer); + mqttClient.publish( + (String(HOMEASSISTANT_DISCOVERY_TOPIC) + "/sensor/" + unique_id + + "_adc_value/config") + .c_str(), + buffer, true); + } + + { + // ADC Raw Value Sensor + StaticJsonDocument<512> root; + root["name"] = "ADC Raw Value"; + root["unique_id"] = unique_id + "_adc_raw"; + root["unit_of_measurement"] = ""; + root["icon"] = "mdi:flash"; + root["state_class"] = "measurement"; + + JsonObject deviceCopy = root.createNestedObject("device"); + JsonArray deviceIdentifiers = deviceCopy.createNestedArray("identifiers"); + deviceIdentifiers.add(unique_id); + deviceCopy["name"] = G.mqtt.clientId; + deviceCopy["sw_version"] = VERSION; + deviceCopy["model"] = "Word Clock"; + deviceCopy["manufacturer"] = "ESPWortuhr"; + deviceCopy["configuration_url"] = "http://" + WiFi.localIP().toString(); + + root["state_topic"] = std::string(G.mqtt.topic) + "/diagnostics"; + root["value_template"] = "{{ value_json.adc_raw | int }}"; + + char buffer[512]; + serializeJson(root, buffer); + mqttClient.publish( + (String(HOMEASSISTANT_DISCOVERY_TOPIC) + "/sensor/" + unique_id + + "_adc_raw/config") + .c_str(), + buffer, true); + } + + // Sende Online-Status + mqttClient.publish((std::string(G.mqtt.topic) + "/availability").c_str(), + "online", true); } +// Neue Callback-Funktionen für die zusätzlichen Entitäten void Mqtt::processBrightOffset(const JsonDocument &doc) { if (doc.containsKey("value")) { G.autoBrightOffset = doc["value"]; parametersChanged = true; - if (mqttInstance) mqttInstance->sendState(); + if (mqttInstance) + mqttInstance->sendState(); } } @@ -895,14 +1000,17 @@ void Mqtt::processBrightSlope(const JsonDocument &doc) { if (doc.containsKey("value")) { G.autoBrightSlope = doc["value"]; parametersChanged = true; - if (mqttInstance) mqttInstance->sendState(); + if (mqttInstance) + mqttInstance->sendState(); } } + void Mqtt::processScrollSpeed(const JsonDocument &doc) { if (doc.containsKey("value")) { G.effectSpeed = doc["value"]; parametersChanged = true; - if (mqttInstance) mqttInstance->sendState(); + if (mqttInstance) + mqttInstance->sendState(); } } @@ -910,7 +1018,8 @@ void Mqtt::processEffectSpeed(const JsonDocument &doc) { if (doc.containsKey("value")) { G.effectSpeed = doc["value"]; parametersChanged = true; - if (mqttInstance) mqttInstance->sendState(); + if (mqttInstance) + mqttInstance->sendState(); } } diff --git a/src/Wortuhr.cpp b/src/Wortuhr.cpp index 9c0a5678..eb5556d0 100644 --- a/src/Wortuhr.cpp +++ b/src/Wortuhr.cpp @@ -69,7 +69,7 @@ Transition *transition; SecondsFrame *secondsFrame; Led led; ClockWork clockWork; -Mqtt mqtt; +Mqtt mqtt(clockWork); Network network; #include "Transitiontypes/Transition.hpp" From d51a38f6ab8fbdbf21e2f6b5924cff1182857f54 Mon Sep 17 00:00:00 2001 From: sogidaned Date: Mon, 10 Mar 2025 13:30:04 +0100 Subject: [PATCH 19/23] test 2 --- include/Uhr.h | 2 +- include/clockWork.hpp | 4 ++-- include/mqtt.h | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/Uhr.h b/include/Uhr.h index de180f25..1a63d266 100644 --- a/include/Uhr.h +++ b/include/Uhr.h @@ -168,7 +168,7 @@ uint8_t lastMinute = 0; uint32_t frontMatrix[MAX_ROW_SIZE] = {0}; uint32_t lastFrontMatrix[MAX_ROW_SIZE] = {0}; -uint8_t minuteArray = 0; /* Using a byte as a per bit array */ +uint8_t minuteArray = 0; // Using a byte as a per bit array uint8_t lastMinuteArray = 0; uint16_t minutePixelArray[4] = {0}; bool frameArray[200] = {false}; diff --git a/include/clockWork.hpp b/include/clockWork.hpp index 28a4e53b..df2ba1a5 100644 --- a/include/clockWork.hpp +++ b/include/clockWork.hpp @@ -47,12 +47,12 @@ void ClockWork::loopAutoBrightLogic() { float ldrValue = adcValue - adcValue0Lux; // Derive LUX value from ldrValue via a second degree polinomial based on LDR type - if (G.ldrType == 0) { // 1 LDR Sensor + if (G.ldrType == 0) { // 1 LDR Sensor const float x2 = 0.0427; const float x1 = 2.679; const float x0 = 10.857; lux = x2 * ldrValue * ldrValue + x1 * ldrValue + x0; - } else { // 4 LDR Sensoren parallel + } else { // 4 LDR Sensoren parallel const float x2 = 0.0005; const float x1 = 0.0687; const float x0 = 3.9907; diff --git a/include/mqtt.h b/include/mqtt.h index 3f648542..95ed177b 100644 --- a/include/mqtt.h +++ b/include/mqtt.h @@ -6,7 +6,7 @@ class Mqtt { private: - ClockWork& clockWork; // Referenz auf ClockWork-Instanz + ClockWork &clockWork; // Referenz auf ClockWork-Instanz void reInit(); static void callback(char *topic, byte *payload, unsigned int length); static void processState(const JsonDocument &doc); @@ -22,7 +22,7 @@ class Mqtt { static bool checkIfMqttUserIsEmpty(); public: - Mqtt(ClockWork& cw); // Nur Deklaration + Mqtt(ClockWork &cw); // Nur Deklaration ~Mqtt(); void init(); @@ -33,4 +33,4 @@ class Mqtt { }; // Globale Instanz -extern Mqtt* mqttInstance; +extern Mqtt *mqttInstance; From 40ab8231b32477c0f05651a46636fd3d1772514f Mon Sep 17 00:00:00 2001 From: sogidaned Date: Mon, 10 Mar 2025 13:49:22 +0100 Subject: [PATCH 20/23] hoffendlich letzter test --- include/clockWork.hpp | 3 +- include/mqtt.h | 7 +++- include/mqtt.hpp | 98 +++++++++++++++++++++++++++---------------- 3 files changed, 70 insertions(+), 38 deletions(-) diff --git a/include/clockWork.hpp b/include/clockWork.hpp index df2ba1a5..9e028870 100644 --- a/include/clockWork.hpp +++ b/include/clockWork.hpp @@ -46,7 +46,8 @@ void ClockWork::loopAutoBrightLogic() { adcValue0Lux = adcValue; float ldrValue = adcValue - adcValue0Lux; - // Derive LUX value from ldrValue via a second degree polinomial based on LDR type + // Derive LUX value from ldrValue via a second degree polinomial + // based on LDR type if (G.ldrType == 0) { // 1 LDR Sensor const float x2 = 0.0427; const float x1 = 2.679; diff --git a/include/mqtt.h b/include/mqtt.h index 95ed177b..920c6d4a 100644 --- a/include/mqtt.h +++ b/include/mqtt.h @@ -6,7 +6,9 @@ class Mqtt { private: - ClockWork &clockWork; // Referenz auf ClockWork-Instanz + ClockWork &clockWork; // Referenz auf ClockWork-Instanz + float lux = 0.0f; // Aktueller LUX-Wert + float ledGain = 0.0f; // Aktueller LED-Gain void reInit(); static void callback(char *topic, byte *payload, unsigned int length); static void processState(const JsonDocument &doc); @@ -19,10 +21,11 @@ class Mqtt { static void processBrightSlope(const JsonDocument &doc); static void processScrollSpeed(const JsonDocument &doc); static void processEffectSpeed(const JsonDocument &doc); + static void processLuxCalculation(float ldrValue); static bool checkIfMqttUserIsEmpty(); public: - Mqtt(ClockWork &cw); // Nur Deklaration + Mqtt(ClockWork &cw); // Nur Deklaration ~Mqtt(); void init(); diff --git a/include/mqtt.hpp b/include/mqtt.hpp index ff62a62b..7a1ace21 100644 --- a/include/mqtt.hpp +++ b/include/mqtt.hpp @@ -19,7 +19,7 @@ Mqtt *mqttInstance = nullptr; PubSubClient mqttClient(client); // Konstruktor und Destruktor -Mqtt::Mqtt(ClockWork& cw) : clockWork(cw) { mqttInstance = this; } +Mqtt::Mqtt(ClockWork &cw) : clockWork(cw) { mqttInstance = this; } Mqtt::~Mqtt() { if (mqttInstance == this) { @@ -159,7 +159,7 @@ void Mqtt::processScrollingText(const JsonDocument &doc) { This function processes the "color" key in the provided JSON document. If the "color" key is present, it updates the foreground color based on the hue (h) and saturation (s) values provided in the JSON document. The brightness component of -the color remains unchanged. +the color is preserved from the current foreground color. Input: @@ -208,9 +208,12 @@ void Mqtt::processColor(const JsonDocument &doc) { // Sende Update an die Weboberfläche StaticJsonDocument<200> webDoc; webDoc["command"] = "color"; - webDoc["h"] = round(h * 360); // Konvertiere zu 0-360 Grad - webDoc["s"] = round(s * 100); // Konvertiere zu 0-100% - webDoc["v"] = round(G.color[Foreground].B * 100); // Konvertiere zu 0-100% + webDoc["h"] = round(h * 360); + // Konvertiere zu 0-360 Grad + webDoc["s"] = round(s * 100); + // Konvertiere zu 0-100% + webDoc["v"] = round(G.color[Foreground].B * 100); + // Konvertiere zu 0-100% char buffer[200]; serializeJson(webDoc, buffer); webSocket.broadcastTXT(buffer, strlen(buffer)); @@ -326,17 +329,22 @@ void Mqtt::init() { mqttClient.publish(availabilityTopic.c_str(), "online", true); // Hauptsteuerung - mqttClient.subscribe((std::string(G.mqtt.topic) + "/cmd").c_str()); + mqttClient.subscribe( + (std::string(G.mqtt.topic) + "/cmd").c_str()); delay(50); // Zusätzliche Topics - mqttClient.subscribe((std::string(G.mqtt.topic) + "/scrolltext/set").c_str()); + mqttClient.subscribe( + (std::string(G.mqtt.topic) + "/scrolltext/set").c_str()); delay(50); - mqttClient.subscribe((std::string(G.mqtt.topic) + "/bright_offset/set").c_str()); + mqttClient.subscribe( + (std::string(G.mqtt.topic) + "/bright_offset/set").c_str()); delay(50); - mqttClient.subscribe((std::string(G.mqtt.topic) + "/bright_slope/set").c_str()); + mqttClient.subscribe( + (std::string(G.mqtt.topic) + "/bright_slope/set").c_str()); delay(50); - mqttClient.subscribe((std::string(G.mqtt.topic) + "/scrolling_text_speed/set").c_str()); + mqttClient.subscribe( + (std::string(G.mqtt.topic) + "/scrolling_text_speed/set").c_str()); delay(50); if (isConnected()) { @@ -505,8 +513,10 @@ void Mqtt::callback(char *topic, byte *payload, unsigned int length) { parametersChanged = true; // Publish new state - String stateTopic = String(G.mqtt.topic) + "/scrolling_text_speed/state"; - mqttClient.publish(stateTopic.c_str(), String(G.effectSpeed).c_str(), + String stateTopic = String(G.mqtt.topic) + + "/scrolling_text_speed/state"; + mqttClient.publish(stateTopic.c_str(), + String(G.effectSpeed).c_str(), true); // Send update to web interface @@ -542,8 +552,8 @@ void Mqtt::sendState() { { StaticJsonDocument<200> doc; doc["state"] = (led.getState()) ? "ON" : "OFF"; - doc["brightness"] = - round(G.color[Foreground].B * 255); // Konvertiere von 0-1 zu 0-255 + doc["brightness"] = round(G.color[Foreground].B * 255); + // Konvertiere von 0-1 zu 0-255 doc["color_mode"] = "rgb"; // Konvertiere HSB zu RGB @@ -623,14 +633,19 @@ void Mqtt::sendState() { // Diagnose-Werte senden { StaticJsonDocument<200> doc; - doc["lux"] = round(clockWork.getLuxValue()); // Runde den Lux-Wert - doc["led_gain"] = round(ledGain); // Konvertiere zu Prozent und runde - doc["adc_value"] = clockWork.getAdcValue(); // Spannungswert (bereits gerundet) - doc["adc_raw"] = clockWork.getAdcRawValue(); // Roher ADC-Wert + doc["lux"] = round(clockWork.getLuxValue()); + // Runde den Lux-Wert + doc["led_gain"] = round(ledGain); + // Konvertiere zu Prozent und runde + doc["adc_value"] = clockWork.getAdcValue(); + // Spannungswert (bereits gerundet) + doc["adc_raw"] = clockWork.getAdcRawValue(); + // Roher ADC-Wert char buffer[200]; serializeJson(doc, buffer); - mqttClient.publish((std::string(G.mqtt.topic) + "/diagnostics").c_str(), - buffer, true); + mqttClient.publish( + (std::string(G.mqtt.topic) + "/diagnostics").c_str(), + buffer, true); } // Helligkeitsoffset Status @@ -732,7 +747,8 @@ None void Mqtt::sendDiscovery() { // Erstelle eine unique_id basierend auf MAC-Adresse String unique_id = WiFi.macAddress(); - unique_id.replace(":", ""); // Entferne die Doppelpunkte aus der MAC-Adresse + // Entferne die Doppelpunkte aus der MAC-Adresse + unique_id.replace(":", ""); // Hauptlicht-Entity { @@ -774,8 +790,8 @@ void Mqtt::sendDiscovery() { // Geräte-Information JsonObject device = root.createNestedObject("device"); - JsonArray identifiers = device.createNestedArray("identifiers"); - identifiers.add(unique_id); + JsonArray deviceIdentifiers = device.createNestedArray("identifiers"); + deviceIdentifiers.add(unique_id); device["name"] = G.mqtt.clientId; device["sw_version"] = VERSION; device["model"] = "Word Clock"; @@ -800,13 +816,15 @@ void Mqtt::sendDiscovery() { root["icon"] = "mdi:text"; JsonObject deviceCopy = root.createNestedObject("device"); - JsonArray deviceIdentifiers = deviceCopy.createNestedArray("identifiers"); + JsonArray deviceIdentifiers = + deviceCopy.createNestedArray("identifiers"); deviceIdentifiers.add(unique_id); deviceCopy["name"] = G.mqtt.clientId; deviceCopy["sw_version"] = VERSION; deviceCopy["model"] = "Word Clock"; deviceCopy["manufacturer"] = "ESPWortuhr"; - deviceCopy["configuration_url"] = "http://" + WiFi.localIP().toString(); + deviceCopy["configuration_url"] = + "http://" + WiFi.localIP().toString(); root["state_topic"] = std::string(G.mqtt.topic) + "/scrolltext/state"; root["command_topic"] = std::string(G.mqtt.topic) + "/scrolltext/set"; @@ -832,13 +850,15 @@ void Mqtt::sendDiscovery() { root["device_class"] = "speed"; // Korrekte Device Class JsonObject deviceCopy = root.createNestedObject("device"); - JsonArray deviceIdentifiers = deviceCopy.createNestedArray("identifiers"); + JsonArray deviceIdentifiers = + deviceCopy.createNestedArray("identifiers"); deviceIdentifiers.add(unique_id); deviceCopy["name"] = G.mqtt.clientId; deviceCopy["sw_version"] = VERSION; deviceCopy["model"] = "Word Clock"; deviceCopy["manufacturer"] = "ESPWortuhr"; - deviceCopy["configuration_url"] = "http://" + WiFi.localIP().toString(); + deviceCopy["configuration_url"] = + "http://" + WiFi.localIP().toString(); root["state_topic"] = std::string(G.mqtt.topic) + "/scrolling_text_speed/state"; @@ -869,13 +889,15 @@ void Mqtt::sendDiscovery() { root["state_class"] = "measurement"; JsonObject deviceCopy = root.createNestedObject("device"); - JsonArray deviceIdentifiers = deviceCopy.createNestedArray("identifiers"); + JsonArray deviceIdentifiers = + deviceCopy.createNestedArray("identifiers"); deviceIdentifiers.add(unique_id); deviceCopy["name"] = G.mqtt.clientId; deviceCopy["sw_version"] = VERSION; deviceCopy["model"] = "Word Clock"; deviceCopy["manufacturer"] = "ESPWortuhr"; - deviceCopy["configuration_url"] = "http://" + WiFi.localIP().toString(); + deviceCopy["configuration_url"] = + "http://" + WiFi.localIP().toString(); root["state_topic"] = std::string(G.mqtt.topic) + "/diagnostics"; root["value_template"] = "{{ value_json.lux }}"; @@ -900,13 +922,15 @@ void Mqtt::sendDiscovery() { root["state_class"] = "measurement"; JsonObject deviceCopy = root.createNestedObject("device"); - JsonArray deviceIdentifiers = deviceCopy.createNestedArray("identifiers"); + JsonArray deviceIdentifiers = + deviceCopy.createNestedArray("identifiers"); deviceIdentifiers.add(unique_id); deviceCopy["name"] = G.mqtt.clientId; deviceCopy["sw_version"] = VERSION; deviceCopy["model"] = "Word Clock"; deviceCopy["manufacturer"] = "ESPWortuhr"; - deviceCopy["configuration_url"] = "http://" + WiFi.localIP().toString(); + deviceCopy["configuration_url"] = + "http://" + WiFi.localIP().toString(); root["state_topic"] = std::string(G.mqtt.topic) + "/diagnostics"; root["value_template"] = "{{ value_json.led_gain }}"; @@ -932,13 +956,15 @@ void Mqtt::sendDiscovery() { root["value_template"] = "{{ value_json.adc_value | round(2) }}"; JsonObject deviceCopy = root.createNestedObject("device"); - JsonArray deviceIdentifiers = deviceCopy.createNestedArray("identifiers"); + JsonArray deviceIdentifiers = + deviceCopy.createNestedArray("identifiers"); deviceIdentifiers.add(unique_id); deviceCopy["name"] = G.mqtt.clientId; deviceCopy["sw_version"] = VERSION; deviceCopy["model"] = "Word Clock"; deviceCopy["manufacturer"] = "ESPWortuhr"; - deviceCopy["configuration_url"] = "http://" + WiFi.localIP().toString(); + deviceCopy["configuration_url"] = + "http://" + WiFi.localIP().toString(); root["state_topic"] = std::string(G.mqtt.topic) + "/diagnostics"; @@ -961,13 +987,15 @@ void Mqtt::sendDiscovery() { root["state_class"] = "measurement"; JsonObject deviceCopy = root.createNestedObject("device"); - JsonArray deviceIdentifiers = deviceCopy.createNestedArray("identifiers"); + JsonArray deviceIdentifiers = + deviceCopy.createNestedArray("identifiers"); deviceIdentifiers.add(unique_id); deviceCopy["name"] = G.mqtt.clientId; deviceCopy["sw_version"] = VERSION; deviceCopy["model"] = "Word Clock"; deviceCopy["manufacturer"] = "ESPWortuhr"; - deviceCopy["configuration_url"] = "http://" + WiFi.localIP().toString(); + deviceCopy["configuration_url"] = + "http://" + WiFi.localIP().toString(); root["state_topic"] = std::string(G.mqtt.topic) + "/diagnostics"; root["value_template"] = "{{ value_json.adc_raw | int }}"; From ac17a706436311d000206c121d0b0c4ff1118c7a Mon Sep 17 00:00:00 2001 From: sogidaned Date: Mon, 10 Mar 2025 14:32:32 +0100 Subject: [PATCH 21/23] test4 --- include/clockWork.h | 10 ++-- include/clockWork.hpp | 1 + include/mqtt.h | 6 +- include/mqtt.hpp | 125 ++++++++++++++++++++---------------------- src/Wortuhr.cpp | 2 +- 5 files changed, 69 insertions(+), 75 deletions(-) diff --git a/include/clockWork.h b/include/clockWork.h index a54e9743..51a8770d 100644 --- a/include/clockWork.h +++ b/include/clockWork.h @@ -13,6 +13,7 @@ class ClockWork { }; stateBH1750Type stateBH1750 = stateBH1750Type::toBeInitialized; float lux = 0.0; + float ledGain = 1.0; uint16_t adcValue0Lux = 10; // Hier wird der niedrigste LDR-ADC Wert getrackt, // für eine dynamische offset korrektur bei 0 LUX @@ -77,13 +78,14 @@ class ClockWork { iUhrType *getPointer(uint8_t type); void initLedStrip(uint8_t num); float getLuxValue() const { return lux; } - float getAdcValue() const { + float getLedGain() const { return ledGain; } + float getAdcValue() const { uint16_t adcRaw = analogRead(A0); float voltage = (adcRaw * 3.3f) / 1023.0f; - return round(voltage * 100.0f) / 100.0f; // Runde auf 2 Nachkommastellen + return round(voltage * 100.0f) / 100.0f; // Runde auf 2 Nachkommastellen } - uint16_t getAdcRawValue() const { - return analogRead(A0); // Roher ADC-Wert (0-1023) + uint16_t getAdcRawValue() const { + return analogRead(A0); // Roher ADC-Wert (0-1023) } //------------------------------------------------------------------------------ diff --git a/include/clockWork.hpp b/include/clockWork.hpp index 9e028870..132ed27e 100644 --- a/include/clockWork.hpp +++ b/include/clockWork.hpp @@ -46,6 +46,7 @@ void ClockWork::loopAutoBrightLogic() { adcValue0Lux = adcValue; float ldrValue = adcValue - adcValue0Lux; + // Derive LUX value from ldrValue via a second degree polinomial // based on LDR type if (G.ldrType == 0) { // 1 LDR Sensor diff --git a/include/mqtt.h b/include/mqtt.h index 920c6d4a..9ca956ad 100644 --- a/include/mqtt.h +++ b/include/mqtt.h @@ -1,13 +1,13 @@ #pragma once -#include + #include "Uhr.h" #include "clockWork.h" - +#include class Mqtt { private: ClockWork &clockWork; // Referenz auf ClockWork-Instanz - float lux = 0.0f; // Aktueller LUX-Wert + float lux = 0.0f; // Aktueller LUX-Wert float ledGain = 0.0f; // Aktueller LED-Gain void reInit(); static void callback(char *topic, byte *payload, unsigned int length); diff --git a/include/mqtt.hpp b/include/mqtt.hpp index 7a1ace21..819c2172 100644 --- a/include/mqtt.hpp +++ b/include/mqtt.hpp @@ -142,6 +142,7 @@ void Mqtt::processScrollingText(const JsonDocument &doc) { if (doc.containsKey("scrolling_text")) { strcpy(G.scrollingText, doc["scrolling_text"]); + // Send update to web interface StaticJsonDocument<200> webDoc; webDoc["command"] = "scrolltext"; @@ -309,18 +310,17 @@ void Mqtt::init() { String availabilityTopic = String(G.mqtt.topic) + "/availability"; if (checkIfMqttUserIsEmpty()) { - mqttClient.connect(G.mqtt.clientId, - availabilityTopic.c_str(), - 0, // QoS - true, // retain + mqttClient.connect(G.mqtt.clientId, availabilityTopic.c_str(), + 0, // QoS + true, // retain "offline"); // LWT Nachricht } else { mqttClient.connect(G.mqtt.clientId, G.mqtt.user, G.mqtt.password, availabilityTopic.c_str(), - 0, // QoS - true, // retain + 0, // QoS + true, // retain "offline"); // LWT Nachricht } delay(50); @@ -329,8 +329,7 @@ void Mqtt::init() { mqttClient.publish(availabilityTopic.c_str(), "online", true); // Hauptsteuerung - mqttClient.subscribe( - (std::string(G.mqtt.topic) + "/cmd").c_str()); + mqttClient.subscribe((std::string(G.mqtt.topic) + "/cmd").c_str()); delay(50); // Zusätzliche Topics @@ -513,11 +512,10 @@ void Mqtt::callback(char *topic, byte *payload, unsigned int length) { parametersChanged = true; // Publish new state - String stateTopic = String(G.mqtt.topic) + - "/scrolling_text_speed/state"; - mqttClient.publish(stateTopic.c_str(), - String(G.effectSpeed).c_str(), - true); + String stateTopic = + String(G.mqtt.topic) + "/scrolling_text_speed/state"; + mqttClient.publish(stateTopic.c_str(), String(G.effectSpeed).c_str(), + true); // Send update to web interface StaticJsonDocument<200> webDoc; @@ -554,6 +552,7 @@ void Mqtt::sendState() { doc["state"] = (led.getState()) ? "ON" : "OFF"; doc["brightness"] = round(G.color[Foreground].B * 255); // Konvertiere von 0-1 zu 0-255 + doc["color_mode"] = "rgb"; // Konvertiere HSB zu RGB @@ -627,7 +626,7 @@ void Mqtt::sendState() { char buffer[200]; serializeJson(doc, buffer); mqttClient.publish((std::string(G.mqtt.topic) + "/status").c_str(), - buffer, true); + buffer, true); } // Diagnose-Werte senden @@ -635,17 +634,20 @@ void Mqtt::sendState() { StaticJsonDocument<200> doc; doc["lux"] = round(clockWork.getLuxValue()); // Runde den Lux-Wert - doc["led_gain"] = round(ledGain); - // Konvertiere zu Prozent und runde + + doc["led_gain"] = round(clockWork.getLedGain()); + // Hole den LED-Gain-Wert von ClockWork + doc["adc_value"] = clockWork.getAdcValue(); // Spannungswert (bereits gerundet) + doc["adc_raw"] = clockWork.getAdcRawValue(); // Roher ADC-Wert + char buffer[200]; serializeJson(doc, buffer); - mqttClient.publish( - (std::string(G.mqtt.topic) + "/diagnostics").c_str(), - buffer, true); + mqttClient.publish((std::string(G.mqtt.topic) + "/diagnostics").c_str(), + buffer, true); } // Helligkeitsoffset Status @@ -816,15 +818,14 @@ void Mqtt::sendDiscovery() { root["icon"] = "mdi:text"; JsonObject deviceCopy = root.createNestedObject("device"); - JsonArray deviceIdentifiers = + JsonArray deviceIdentifiers = deviceCopy.createNestedArray("identifiers"); deviceIdentifiers.add(unique_id); deviceCopy["name"] = G.mqtt.clientId; deviceCopy["sw_version"] = VERSION; deviceCopy["model"] = "Word Clock"; deviceCopy["manufacturer"] = "ESPWortuhr"; - deviceCopy["configuration_url"] = - "http://" + WiFi.localIP().toString(); + deviceCopy["configuration_url"] = "http://" + WiFi.localIP().toString(); root["state_topic"] = std::string(G.mqtt.topic) + "/scrolltext/state"; root["command_topic"] = std::string(G.mqtt.topic) + "/scrolltext/set"; @@ -833,11 +834,10 @@ void Mqtt::sendDiscovery() { char buffer[512]; serializeJson(root, buffer); - mqttClient.publish( - (String(HOMEASSISTANT_DISCOVERY_TOPIC) + "/text/" + unique_id + - "_scrolltext/config") - .c_str(), - buffer, true); + mqttClient.publish((String(HOMEASSISTANT_DISCOVERY_TOPIC) + "/text/" + + unique_id + "_scrolltext/config") + .c_str(), + buffer, true); } // 3. Lauftext-Geschwindigkeit @@ -850,14 +850,14 @@ void Mqtt::sendDiscovery() { root["device_class"] = "speed"; // Korrekte Device Class JsonObject deviceCopy = root.createNestedObject("device"); - JsonArray deviceIdentifiers = + JsonArray deviceIdentifiers = deviceCopy.createNestedArray("identifiers"); deviceIdentifiers.add(unique_id); deviceCopy["name"] = G.mqtt.clientId; deviceCopy["sw_version"] = VERSION; deviceCopy["model"] = "Word Clock"; deviceCopy["manufacturer"] = "ESPWortuhr"; - deviceCopy["configuration_url"] = + deviceCopy["configuration_url"] = "http://" + WiFi.localIP().toString(); root["state_topic"] = @@ -870,11 +870,10 @@ void Mqtt::sendDiscovery() { char buffer[512]; serializeJson(root, buffer); - mqttClient.publish( - (String(HOMEASSISTANT_DISCOVERY_TOPIC) + "/number/" + unique_id + - "_scrolling_text_speed/config") - .c_str(), - buffer, true); + mqttClient.publish((String(HOMEASSISTANT_DISCOVERY_TOPIC) + "/number/" + + unique_id + "_scrolling_text_speed/config") + .c_str(), + buffer, true); } // Diagnose-Entities @@ -889,26 +888,24 @@ void Mqtt::sendDiscovery() { root["state_class"] = "measurement"; JsonObject deviceCopy = root.createNestedObject("device"); - JsonArray deviceIdentifiers = + JsonArray deviceIdentifiers = deviceCopy.createNestedArray("identifiers"); deviceIdentifiers.add(unique_id); deviceCopy["name"] = G.mqtt.clientId; deviceCopy["sw_version"] = VERSION; deviceCopy["model"] = "Word Clock"; deviceCopy["manufacturer"] = "ESPWortuhr"; - deviceCopy["configuration_url"] = - "http://" + WiFi.localIP().toString(); + deviceCopy["configuration_url"] = "http://" + WiFi.localIP().toString(); root["state_topic"] = std::string(G.mqtt.topic) + "/diagnostics"; root["value_template"] = "{{ value_json.lux }}"; char buffer[512]; serializeJson(root, buffer); - mqttClient.publish( - (String(HOMEASSISTANT_DISCOVERY_TOPIC) + "/sensor/" + unique_id + - "_lux/config") - .c_str(), - buffer, true); + mqttClient.publish((String(HOMEASSISTANT_DISCOVERY_TOPIC) + "/sensor/" + + unique_id + "_lux/config") + .c_str(), + buffer, true); } { @@ -922,26 +919,24 @@ void Mqtt::sendDiscovery() { root["state_class"] = "measurement"; JsonObject deviceCopy = root.createNestedObject("device"); - JsonArray deviceIdentifiers = + JsonArray deviceIdentifiers = deviceCopy.createNestedArray("identifiers"); deviceIdentifiers.add(unique_id); deviceCopy["name"] = G.mqtt.clientId; deviceCopy["sw_version"] = VERSION; deviceCopy["model"] = "Word Clock"; deviceCopy["manufacturer"] = "ESPWortuhr"; - deviceCopy["configuration_url"] = - "http://" + WiFi.localIP().toString(); + deviceCopy["configuration_url"] = "http://" + WiFi.localIP().toString(); root["state_topic"] = std::string(G.mqtt.topic) + "/diagnostics"; root["value_template"] = "{{ value_json.led_gain }}"; char buffer[512]; serializeJson(root, buffer); - mqttClient.publish( - (String(HOMEASSISTANT_DISCOVERY_TOPIC) + "/sensor/" + unique_id + - "_led_gain/config") - .c_str(), - buffer, true); + mqttClient.publish((String(HOMEASSISTANT_DISCOVERY_TOPIC) + "/sensor/" + + unique_id + "_led_gain/config") + .c_str(), + buffer, true); } { @@ -956,25 +951,23 @@ void Mqtt::sendDiscovery() { root["value_template"] = "{{ value_json.adc_value | round(2) }}"; JsonObject deviceCopy = root.createNestedObject("device"); - JsonArray deviceIdentifiers = + JsonArray deviceIdentifiers = deviceCopy.createNestedArray("identifiers"); deviceIdentifiers.add(unique_id); deviceCopy["name"] = G.mqtt.clientId; deviceCopy["sw_version"] = VERSION; deviceCopy["model"] = "Word Clock"; deviceCopy["manufacturer"] = "ESPWortuhr"; - deviceCopy["configuration_url"] = - "http://" + WiFi.localIP().toString(); + deviceCopy["configuration_url"] = "http://" + WiFi.localIP().toString(); root["state_topic"] = std::string(G.mqtt.topic) + "/diagnostics"; char buffer[512]; serializeJson(root, buffer); - mqttClient.publish( - (String(HOMEASSISTANT_DISCOVERY_TOPIC) + "/sensor/" + unique_id + - "_adc_value/config") - .c_str(), - buffer, true); + mqttClient.publish((String(HOMEASSISTANT_DISCOVERY_TOPIC) + "/sensor/" + + unique_id + "_adc_value/config") + .c_str(), + buffer, true); } { @@ -987,31 +980,29 @@ void Mqtt::sendDiscovery() { root["state_class"] = "measurement"; JsonObject deviceCopy = root.createNestedObject("device"); - JsonArray deviceIdentifiers = + JsonArray deviceIdentifiers = deviceCopy.createNestedArray("identifiers"); deviceIdentifiers.add(unique_id); deviceCopy["name"] = G.mqtt.clientId; deviceCopy["sw_version"] = VERSION; deviceCopy["model"] = "Word Clock"; deviceCopy["manufacturer"] = "ESPWortuhr"; - deviceCopy["configuration_url"] = - "http://" + WiFi.localIP().toString(); + deviceCopy["configuration_url"] = "http://" + WiFi.localIP().toString(); root["state_topic"] = std::string(G.mqtt.topic) + "/diagnostics"; root["value_template"] = "{{ value_json.adc_raw | int }}"; char buffer[512]; serializeJson(root, buffer); - mqttClient.publish( - (String(HOMEASSISTANT_DISCOVERY_TOPIC) + "/sensor/" + unique_id + - "_adc_raw/config") - .c_str(), - buffer, true); + mqttClient.publish((String(HOMEASSISTANT_DISCOVERY_TOPIC) + "/sensor/" + + unique_id + "_adc_raw/config") + .c_str(), + buffer, true); } // Sende Online-Status mqttClient.publish((std::string(G.mqtt.topic) + "/availability").c_str(), - "online", true); + "online", true); } // Neue Callback-Funktionen für die zusätzlichen Entitäten diff --git a/src/Wortuhr.cpp b/src/Wortuhr.cpp index eb5556d0..521d3d1b 100644 --- a/src/Wortuhr.cpp +++ b/src/Wortuhr.cpp @@ -220,7 +220,7 @@ void setup() { G.layoutVariant[ReverseMinDirection] = REVERSE_MINUTE_DIR; G.layoutVariant[MirrorVertical] = MIRROR_FRONT_VERTICAL; G.layoutVariant[MirrorHorizontal] = MIRROR_FRONT_HORIZONTAL; - G.ldrType = 0; // Standard: Einzelner LDR + G.ldrType = 0; // Standard: Einzelner LDR for (uint8_t i = 0; i < sizeof(G.languageVariant) / sizeof(G.languageVariant[0]); i++) { From 2c3968c5b8e89fd508a16e8b95b74699a773a8d4 Mon Sep 17 00:00:00 2001 From: sogidaned Date: Mon, 10 Mar 2025 19:39:42 +0100 Subject: [PATCH 22/23] Stufe 1 Fertig --- include/Uhr.h | 2 +- include/clockWork.h | 2 - include/clockWork.hpp | 3 +- include/mqtt.h | 5 +- include/mqtt.hpp | 229 +++++++++++++++++++++++++++++++----------- 5 files changed, 174 insertions(+), 67 deletions(-) diff --git a/include/Uhr.h b/include/Uhr.h index 1a63d266..de180f25 100644 --- a/include/Uhr.h +++ b/include/Uhr.h @@ -168,7 +168,7 @@ uint8_t lastMinute = 0; uint32_t frontMatrix[MAX_ROW_SIZE] = {0}; uint32_t lastFrontMatrix[MAX_ROW_SIZE] = {0}; -uint8_t minuteArray = 0; // Using a byte as a per bit array +uint8_t minuteArray = 0; /* Using a byte as a per bit array */ uint8_t lastMinuteArray = 0; uint16_t minutePixelArray[4] = {0}; bool frameArray[200] = {false}; diff --git a/include/clockWork.h b/include/clockWork.h index 51a8770d..3725891c 100644 --- a/include/clockWork.h +++ b/include/clockWork.h @@ -13,7 +13,6 @@ class ClockWork { }; stateBH1750Type stateBH1750 = stateBH1750Type::toBeInitialized; float lux = 0.0; - float ledGain = 1.0; uint16_t adcValue0Lux = 10; // Hier wird der niedrigste LDR-ADC Wert getrackt, // für eine dynamische offset korrektur bei 0 LUX @@ -78,7 +77,6 @@ class ClockWork { iUhrType *getPointer(uint8_t type); void initLedStrip(uint8_t num); float getLuxValue() const { return lux; } - float getLedGain() const { return ledGain; } float getAdcValue() const { uint16_t adcRaw = analogRead(A0); float voltage = (adcRaw * 3.3f) / 1023.0f; diff --git a/include/clockWork.hpp b/include/clockWork.hpp index 132ed27e..d4c59e07 100644 --- a/include/clockWork.hpp +++ b/include/clockWork.hpp @@ -45,8 +45,7 @@ void ClockWork::loopAutoBrightLogic() { if (adcValue < adcValue0Lux) adcValue0Lux = adcValue; float ldrValue = adcValue - adcValue0Lux; - - + // Derive LUX value from ldrValue via a second degree polinomial // based on LDR type if (G.ldrType == 0) { // 1 LDR Sensor diff --git a/include/mqtt.h b/include/mqtt.h index 9ca956ad..33b581ab 100644 --- a/include/mqtt.h +++ b/include/mqtt.h @@ -1,14 +1,12 @@ #pragma once - #include "Uhr.h" #include "clockWork.h" #include + class Mqtt { private: ClockWork &clockWork; // Referenz auf ClockWork-Instanz - float lux = 0.0f; // Aktueller LUX-Wert - float ledGain = 0.0f; // Aktueller LED-Gain void reInit(); static void callback(char *topic, byte *payload, unsigned int length); static void processState(const JsonDocument &doc); @@ -21,7 +19,6 @@ class Mqtt { static void processBrightSlope(const JsonDocument &doc); static void processScrollSpeed(const JsonDocument &doc); static void processEffectSpeed(const JsonDocument &doc); - static void processLuxCalculation(float ldrValue); static bool checkIfMqttUserIsEmpty(); public: diff --git a/include/mqtt.hpp b/include/mqtt.hpp index 819c2172..1722d3b5 100644 --- a/include/mqtt.hpp +++ b/include/mqtt.hpp @@ -141,8 +141,7 @@ None void Mqtt::processScrollingText(const JsonDocument &doc) { if (doc.containsKey("scrolling_text")) { strcpy(G.scrollingText, doc["scrolling_text"]); - - + // Send update to web interface StaticJsonDocument<200> webDoc; webDoc["command"] = "scrolltext"; @@ -160,7 +159,7 @@ void Mqtt::processScrollingText(const JsonDocument &doc) { This function processes the "color" key in the provided JSON document. If the "color" key is present, it updates the foreground color based on the hue (h) and saturation (s) values provided in the JSON document. The brightness component of -the color is preserved from the current foreground color. +the color remains unchanged. Input: @@ -209,12 +208,10 @@ void Mqtt::processColor(const JsonDocument &doc) { // Sende Update an die Weboberfläche StaticJsonDocument<200> webDoc; webDoc["command"] = "color"; - webDoc["h"] = round(h * 360); - // Konvertiere zu 0-360 Grad - webDoc["s"] = round(s * 100); - // Konvertiere zu 0-100% - webDoc["v"] = round(G.color[Foreground].B * 100); - // Konvertiere zu 0-100% + webDoc["h"] = round(h * 360); // Konvertiere zu 0-360 Grad + webDoc["s"] = round(s * 100); // Konvertiere zu 0-100% + webDoc["v"] = + round(G.color[Foreground].B * 100); // Konvertiere zu 0-100% char buffer[200]; serializeJson(webDoc, buffer); webSocket.broadcastTXT(buffer, strlen(buffer)); @@ -305,23 +302,21 @@ None void Mqtt::init() { mqttClient.setServer(G.mqtt.serverAdress, G.mqtt.port); mqttClient.setCallback(callback); - + // LWT (Last Will and Testament) konfigurieren String availabilityTopic = String(G.mqtt.topic) + "/availability"; - + if (checkIfMqttUserIsEmpty()) { - mqttClient.connect(G.mqtt.clientId, availabilityTopic.c_str(), - 0, // QoS - true, // retain - "offline"); // LWT Nachricht + mqttClient.connect(G.mqtt.clientId, availabilityTopic.c_str(), + 0, // QoS + true, // retain + "offline"); // LWT Nachricht } else { - mqttClient.connect(G.mqtt.clientId, - G.mqtt.user, - G.mqtt.password, - availabilityTopic.c_str(), - 0, // QoS - true, // retain - "offline"); // LWT Nachricht + mqttClient.connect(G.mqtt.clientId, G.mqtt.user, G.mqtt.password, + availabilityTopic.c_str(), + 0, // QoS + true, // retain + "offline"); // LWT Nachricht } delay(50); @@ -345,6 +340,9 @@ void Mqtt::init() { mqttClient.subscribe( (std::string(G.mqtt.topic) + "/scrolling_text_speed/set").c_str()); delay(50); + mqttClient.subscribe( + (std::string(G.mqtt.topic) + "/auto_brightness/set").c_str()); + delay(50); if (isConnected()) { Serial.println("MQTT Connected"); @@ -465,7 +463,9 @@ None */ void Mqtt::callback(char *topic, byte *payload, unsigned int length) { - StaticJsonDocument<512> doc; + // Bestimme den Nachrichtentyp anhand des Topics + String topicStr = String(topic); + String baseTopic = String(G.mqtt.topic); // Convert payload to a null-terminated string char msg[length + 1]; @@ -477,7 +477,51 @@ void Mqtt::callback(char *topic, byte *payload, unsigned int length) { Serial.print("] "); Serial.println(msg); - // Deserialize JSON + // Debug: Sende die empfangene Nachricht + StaticJsonDocument<200> debugDoc; + debugDoc["received_msg"] = msg; + debugDoc["msg_length"] = length; + debugDoc["topic"] = topic; + char debugBuffer[200]; + serializeJson(debugDoc, debugBuffer); + + // Debug-Publish für alle Nachrichten + mqttClient.publish( + (std::string(G.mqtt.topic) + "/debug/auto_brightness").c_str(), + debugBuffer, true); + + // Verarbeite Auto-Brightness-Nachrichten direkt als String + if (topicStr == baseTopic + "/auto_brightness/set") { + if (strcmp(msg, "ON") == 0) { + G.autoBrightEnabled = 1; + Serial.println("MQTT: Auto Brightness ON"); + } else if (strcmp(msg, "OFF") == 0) { + G.autoBrightEnabled = 0; + Serial.println("MQTT: Auto Brightness OFF"); + } else { + Serial.print("MQTT: Unknown command: "); + Serial.println(msg); + } + + parametersChanged = true; + + // Sende den neuen Status zurück + mqttClient.publish( + (std::string(G.mqtt.topic) + "/auto_brightness/state").c_str(), + G.autoBrightEnabled ? "ON" : "OFF", true); + + // Sende Update an die Weboberfläche + StaticJsonDocument<200> webDoc; + webDoc["command"] = "autobright"; + webDoc["value"] = G.autoBrightEnabled; + char buffer[200]; + serializeJson(webDoc, buffer); + webSocket.broadcastTXT(buffer, strlen(buffer)); + return; + } + + // Für alle anderen Nachrichten: JSON-Deserialisierung + StaticJsonDocument<512> doc; DeserializationError error = deserializeJson(doc, msg); if (error) { Serial.print(F("deserializeJson() failed: ")); @@ -485,12 +529,8 @@ void Mqtt::callback(char *topic, byte *payload, unsigned int length) { return; } - // Bestimme den Nachrichtentyp anhand des Topics - String topicStr = String(topic); - String baseTopic = String(G.mqtt.topic); - + // Verarbeite die restlichen Nachrichten if (topicStr == baseTopic + "/cmd") { - // Hauptsteuerung (wie bisher) processState(doc); processEffect(doc); processScrollingText(doc); @@ -507,17 +547,15 @@ void Mqtt::callback(char *topic, byte *payload, unsigned int length) { } else if (topicStr == baseTopic + "/effect_speed/set") { processEffectSpeed(doc); } else if (topicStr == baseTopic + "/scrolling_text_speed/set") { - int speed = atoi(msg); // Konvertiere den String direkt in eine Zahl + int speed = atoi(msg); G.effectSpeed = constrain(speed, 1, 10); parametersChanged = true; - // Publish new state String stateTopic = String(G.mqtt.topic) + "/scrolling_text_speed/state"; mqttClient.publish(stateTopic.c_str(), String(G.effectSpeed).c_str(), true); - // Send update to web interface StaticJsonDocument<200> webDoc; webDoc["command"] = "speed"; webDoc["value"] = G.effectSpeed; @@ -550,9 +588,21 @@ void Mqtt::sendState() { { StaticJsonDocument<200> doc; doc["state"] = (led.getState()) ? "ON" : "OFF"; - doc["brightness"] = round(G.color[Foreground].B * 255); - // Konvertiere von 0-1 zu 0-255 - + + // Berechne den tatsächlichen Helligkeitswert + float actualBrightness; + if (G.autoBrightEnabled) { + // Wenn automatische Helligkeit aktiv ist, verwende den aktuellen + // LED-Gain + actualBrightness = (ledGain / 100.0f) * G.color[Foreground].B; + } else { + // Wenn automatische Helligkeit deaktiviert ist, verwende nur die + // manuelle Helligkeit + actualBrightness = G.color[Foreground].B; + } + doc["brightness"] = + round(actualBrightness * 255); // Konvertiere von 0-1 zu 0-255 + doc["color_mode"] = "rgb"; // Konvertiere HSB zu RGB @@ -632,18 +682,11 @@ void Mqtt::sendState() { // Diagnose-Werte senden { StaticJsonDocument<200> doc; - doc["lux"] = round(clockWork.getLuxValue()); - // Runde den Lux-Wert - - doc["led_gain"] = round(clockWork.getLedGain()); - // Hole den LED-Gain-Wert von ClockWork - - doc["adc_value"] = clockWork.getAdcValue(); - // Spannungswert (bereits gerundet) - - doc["adc_raw"] = clockWork.getAdcRawValue(); - // Roher ADC-Wert - + doc["lux"] = round(clockWork.getLuxValue()); // Runde den Lux-Wert + doc["led_gain"] = round(ledGain); // Konvertiere zu Prozent und runde + doc["adc_value"] = + clockWork.getAdcValue(); // Spannungswert (bereits gerundet) + doc["adc_raw"] = clockWork.getAdcRawValue(); // Roher ADC-Wert char buffer[200]; serializeJson(doc, buffer); mqttClient.publish((std::string(G.mqtt.topic) + "/diagnostics").c_str(), @@ -689,9 +732,20 @@ void Mqtt::sendState() { String(G.effectSpeed).c_str(), true); } + // Helligkeitsautomatik Status + { + static bool lastAutoBrightState = G.autoBrightEnabled; + if (lastAutoBrightState != G.autoBrightEnabled) { + mqttClient.publish( + (std::string(G.mqtt.topic) + "/auto_brightness/state").c_str(), + G.autoBrightEnabled ? "ON" : "OFF", true); + lastAutoBrightState = G.autoBrightEnabled; + } + } + // Aktualisiere den Online-Status mqttClient.publish((std::string(G.mqtt.topic) + "/availability").c_str(), - "online", true); + "online", true); } //------------------------------------------------------------------------------ @@ -749,8 +803,7 @@ None void Mqtt::sendDiscovery() { // Erstelle eine unique_id basierend auf MAC-Adresse String unique_id = WiFi.macAddress(); - // Entferne die Doppelpunkte aus der MAC-Adresse - unique_id.replace(":", ""); + unique_id.replace(":", ""); // Entferne die Doppelpunkte aus der MAC-Adresse // Hauptlicht-Entity { @@ -792,8 +845,8 @@ void Mqtt::sendDiscovery() { // Geräte-Information JsonObject device = root.createNestedObject("device"); - JsonArray deviceIdentifiers = device.createNestedArray("identifiers"); - deviceIdentifiers.add(unique_id); + JsonArray identifiers = device.createNestedArray("identifiers"); + identifiers.add(unique_id); device["name"] = G.mqtt.clientId; device["sw_version"] = VERSION; device["model"] = "Word Clock"; @@ -803,9 +856,8 @@ void Mqtt::sendDiscovery() { char buffer[700]; serializeJson(root, buffer); - String discoveryTopic = - String(HOMEASSISTANT_DISCOVERY_TOPIC) + "/light/" + unique_id + - "/config"; + String discoveryTopic = String(HOMEASSISTANT_DISCOVERY_TOPIC) + + "/light/" + unique_id + "/config"; mqttClient.publish(discoveryTopic.c_str(), buffer, true); } @@ -857,8 +909,7 @@ void Mqtt::sendDiscovery() { deviceCopy["sw_version"] = VERSION; deviceCopy["model"] = "Word Clock"; deviceCopy["manufacturer"] = "ESPWortuhr"; - deviceCopy["configuration_url"] = - "http://" + WiFi.localIP().toString(); + deviceCopy["configuration_url"] = "http://" + WiFi.localIP().toString(); root["state_topic"] = std::string(G.mqtt.topic) + "/scrolling_text_speed/state"; @@ -1003,6 +1054,69 @@ void Mqtt::sendDiscovery() { // Sende Online-Status mqttClient.publish((std::string(G.mqtt.topic) + "/availability").c_str(), "online", true); + + // Helligkeitsautomatik Switch + { + StaticJsonDocument<512> root; + root["name"] = "Auto Brightness"; + root["unique_id"] = unique_id + "_auto_brightness"; + root["icon"] = "mdi:brightness-auto"; + root["device_class"] = "switch"; + + JsonObject deviceCopy = root.createNestedObject("device"); + JsonArray deviceIdentifiers = + deviceCopy.createNestedArray("identifiers"); + deviceIdentifiers.add(unique_id); + deviceCopy["name"] = G.mqtt.clientId; + deviceCopy["sw_version"] = VERSION; + deviceCopy["model"] = "Word Clock"; + deviceCopy["manufacturer"] = "ESPWortuhr"; + deviceCopy["configuration_url"] = "http://" + WiFi.localIP().toString(); + + root["state_topic"] = + std::string(G.mqtt.topic) + "/auto_brightness/state"; + root["command_topic"] = + std::string(G.mqtt.topic) + "/auto_brightness/set"; + root["payload_on"] = "ON"; + root["payload_off"] = "OFF"; + + char buffer[512]; + serializeJson(root, buffer); + mqttClient.publish((String(HOMEASSISTANT_DISCOVERY_TOPIC) + "/switch/" + + unique_id + "_auto_brightness/config") + .c_str(), + buffer, true); + } + + // Debug Sensor für Auto-Brightness + { + StaticJsonDocument<512> root; + root["name"] = "Auto Brightness Debug"; + root["unique_id"] = unique_id + "_auto_brightness_debug"; + root["icon"] = "mdi:bug"; + root["device_class"] = "diagnostic"; + + JsonObject deviceCopy = root.createNestedObject("device"); + JsonArray deviceIdentifiers = + deviceCopy.createNestedArray("identifiers"); + deviceIdentifiers.add(unique_id); + deviceCopy["name"] = G.mqtt.clientId; + deviceCopy["sw_version"] = VERSION; + deviceCopy["model"] = "Word Clock"; + deviceCopy["manufacturer"] = "ESPWortuhr"; + deviceCopy["configuration_url"] = "http://" + WiFi.localIP().toString(); + + root["state_topic"] = + std::string(G.mqtt.topic) + "/debug/auto_brightness"; + root["value_template"] = "{{ value_json.received_msg }}"; + + char buffer[512]; + serializeJson(root, buffer); + mqttClient.publish((String(HOMEASSISTANT_DISCOVERY_TOPIC) + "/sensor/" + + unique_id + "_auto_brightness_debug/config") + .c_str(), + buffer, true); + } } // Neue Callback-Funktionen für die zusätzlichen Entitäten @@ -1041,4 +1155,3 @@ void Mqtt::processEffectSpeed(const JsonDocument &doc) { mqttInstance->sendState(); } } - From 067c303adf71e914ed5a13b58d7ae10d7044a7c4 Mon Sep 17 00:00:00 2001 From: sogidaned Date: Tue, 11 Mar 2025 15:18:30 +0100 Subject: [PATCH 23/23] MQTT-Implementierung optimiert: Vereinfachung und Bereinigung der Funktionen - Entfernung redundanter MQTT-Callback-Funktionen - Vereinheitlichung der Farbverarbeitung mit Home Assistant - Verbesserung der Geschwindigkeitseinstellungen - Entfernung von Debug-Code und Vereinfachung der Konfiguration --- include/mqtt.h | 7 +- include/mqtt.hpp | 398 ++++++++++++----------------------------------- 2 files changed, 102 insertions(+), 303 deletions(-) diff --git a/include/mqtt.h b/include/mqtt.h index 33b581ab..2c4cbc8e 100644 --- a/include/mqtt.h +++ b/include/mqtt.h @@ -14,15 +14,10 @@ class Mqtt { static void processScrollingText(const JsonDocument &doc); static void processColor(const JsonDocument &doc); static void processBrightness(const JsonDocument &doc); - static void processAutobrightSwitch(const JsonDocument &doc); - static void processBrightOffset(const JsonDocument &doc); - static void processBrightSlope(const JsonDocument &doc); - static void processScrollSpeed(const JsonDocument &doc); - static void processEffectSpeed(const JsonDocument &doc); static bool checkIfMqttUserIsEmpty(); public: - Mqtt(ClockWork &cw); // Nur Deklaration + Mqtt(ClockWork &cw); ~Mqtt(); void init(); diff --git a/include/mqtt.hpp b/include/mqtt.hpp index 1722d3b5..14bca5f8 100644 --- a/include/mqtt.hpp +++ b/include/mqtt.hpp @@ -13,12 +13,12 @@ extern WiFiClient client; -// Globale Instanz +// Global Instance Mqtt *mqttInstance = nullptr; PubSubClient mqttClient(client); -// Konstruktor und Destruktor +// Constructor and Destructor Mqtt::Mqtt(ClockWork &cw) : clockWork(cw) { mqttInstance = this; } Mqtt::~Mqtt() { @@ -173,45 +173,24 @@ None void Mqtt::processColor(const JsonDocument &doc) { JsonObjectConst color = doc["color"]; if (!color.isNull()) { - uint8_t r = color["r"] | 0; - uint8_t g = color["g"] | 0; - uint8_t b = color["b"] | 0; - - // Direkte Umwandlung in HSB - float h, s; - float rf = r / 255.0f; - float gf = g / 255.0f; - float bf = b / 255.0f; - - float cmax = max(max(rf, gf), bf); - float cmin = min(min(rf, gf), bf); - float diff = cmax - cmin; - - // Berechne Hue - if (diff == 0) { - h = 0; - } else if (cmax == rf) { - h = fmod((60 * ((gf - bf) / diff) + 360), 360) / 360.0f; - } else if (cmax == gf) { - h = fmod((60 * ((bf - rf) / diff) + 120), 360) / 360.0f; - } else { - h = fmod((60 * ((rf - gf) / diff) + 240), 360) / 360.0f; - } - - // Berechne Saturation - s = (cmax == 0) ? 0 : (diff / cmax); + // Convert values from Home Assistant (0-360 for Hue, 0-100 for + // Saturation) to clock format (0-1 for both) + float h = constrain(color["h"].as(), 0, 360) / 360.0f; + float s = constrain(color["s"].as(), 0, 100) / 100.0f; - // Behalte aktuelle Helligkeit bei + // Keep current brightness G.color[Foreground] = HsbColor(h, s, G.color[Foreground].B); parametersChanged = true; - // Sende Update an die Weboberfläche + // Save color in EEPROM + eeprom::write(); + + // Send update to web interface StaticJsonDocument<200> webDoc; webDoc["command"] = "color"; - webDoc["h"] = round(h * 360); // Konvertiere zu 0-360 Grad - webDoc["s"] = round(s * 100); // Konvertiere zu 0-100% - webDoc["v"] = - round(G.color[Foreground].B * 100); // Konvertiere zu 0-100% + webDoc["h"] = round(h * 360); // Convert to 0-360 degrees + webDoc["s"] = round(s * 100); // Convert to 0-100% + webDoc["v"] = round(G.color[Foreground].B * 100); // Convert to 0-100% char buffer[200]; serializeJson(webDoc, buffer); webSocket.broadcastTXT(buffer, strlen(buffer)); @@ -244,11 +223,23 @@ None void Mqtt::processBrightness(const JsonDocument &doc) { if (doc.containsKey("brightness")) { + // If Auto-Brightness is enabled, ignore brightness changes from Home + // Assistant + if (G.autoBrightEnabled) { + Serial.println("MQTT: Ignoring brightness change - auto brightness " + "is enabled"); + return; + } + float brightness = float(uint8_t(doc["brightness"])) / 255.0f; brightness = max(0.0f, min(1.0f, brightness)); + // Set brightness directly G.color[Foreground] = HsbColor(G.color[Foreground].H, G.color[Foreground].S, brightness); + Serial.print("MQTT: Setting manual brightness: "); + Serial.println(brightness); + parametersChanged = true; if (mqttInstance) @@ -303,42 +294,36 @@ void Mqtt::init() { mqttClient.setServer(G.mqtt.serverAdress, G.mqtt.port); mqttClient.setCallback(callback); - // LWT (Last Will and Testament) konfigurieren + // Configure LWT (Last Will and Testament) String availabilityTopic = String(G.mqtt.topic) + "/availability"; if (checkIfMqttUserIsEmpty()) { mqttClient.connect(G.mqtt.clientId, availabilityTopic.c_str(), 0, // QoS true, // retain - "offline"); // LWT Nachricht + "offline"); // Last Will Message } else { mqttClient.connect(G.mqtt.clientId, G.mqtt.user, G.mqtt.password, availabilityTopic.c_str(), 0, // QoS true, // retain - "offline"); // LWT Nachricht + "offline"); // Last Will Message } delay(50); - // Sofort nach Verbindung den Online-Status senden + // Send online status immediately after connection mqttClient.publish(availabilityTopic.c_str(), "online", true); - // Hauptsteuerung + // Main control mqttClient.subscribe((std::string(G.mqtt.topic) + "/cmd").c_str()); delay(50); - // Zusätzliche Topics + // Additional Topics mqttClient.subscribe( (std::string(G.mqtt.topic) + "/scrolltext/set").c_str()); delay(50); mqttClient.subscribe( - (std::string(G.mqtt.topic) + "/bright_offset/set").c_str()); - delay(50); - mqttClient.subscribe( - (std::string(G.mqtt.topic) + "/bright_slope/set").c_str()); - delay(50); - mqttClient.subscribe( - (std::string(G.mqtt.topic) + "/scrolling_text_speed/set").c_str()); + (std::string(G.mqtt.topic) + "/effect_speed/set").c_str()); delay(50); mqttClient.subscribe( (std::string(G.mqtt.topic) + "/auto_brightness/set").c_str()); @@ -346,7 +331,7 @@ void Mqtt::init() { if (isConnected()) { Serial.println("MQTT Connected"); - sendState(); // Sende initialen Status + sendState(); // Send initial state } } @@ -437,7 +422,7 @@ void Mqtt::loop() { } mqttClient.loop(); - // Regelmäßiges Update des Status + // Regular status update if (millis() - lastStateUpdate >= STATE_UPDATE_INTERVAL) { sendState(); lastStateUpdate = millis(); @@ -463,7 +448,7 @@ None */ void Mqtt::callback(char *topic, byte *payload, unsigned int length) { - // Bestimme den Nachrichtentyp anhand des Topics + // Determine message type based on topic String topicStr = String(topic); String baseTopic = String(G.mqtt.topic); @@ -472,25 +457,7 @@ void Mqtt::callback(char *topic, byte *payload, unsigned int length) { memcpy(msg, payload, length); msg[length] = '\0'; - Serial.print("Received message ["); - Serial.print(topic); - Serial.print("] "); - Serial.println(msg); - - // Debug: Sende die empfangene Nachricht - StaticJsonDocument<200> debugDoc; - debugDoc["received_msg"] = msg; - debugDoc["msg_length"] = length; - debugDoc["topic"] = topic; - char debugBuffer[200]; - serializeJson(debugDoc, debugBuffer); - - // Debug-Publish für alle Nachrichten - mqttClient.publish( - (std::string(G.mqtt.topic) + "/debug/auto_brightness").c_str(), - debugBuffer, true); - - // Verarbeite Auto-Brightness-Nachrichten direkt als String + // Process Auto-Brightness messages directly as string if (topicStr == baseTopic + "/auto_brightness/set") { if (strcmp(msg, "ON") == 0) { G.autoBrightEnabled = 1; @@ -505,12 +472,12 @@ void Mqtt::callback(char *topic, byte *payload, unsigned int length) { parametersChanged = true; - // Sende den neuen Status zurück + // Send the new status back mqttClient.publish( (std::string(G.mqtt.topic) + "/auto_brightness/state").c_str(), G.autoBrightEnabled ? "ON" : "OFF", true); - // Sende Update an die Weboberfläche + // Send update to web interface StaticJsonDocument<200> webDoc; webDoc["command"] = "autobright"; webDoc["value"] = G.autoBrightEnabled; @@ -520,7 +487,7 @@ void Mqtt::callback(char *topic, byte *payload, unsigned int length) { return; } - // Für alle anderen Nachrichten: JSON-Deserialisierung + // For all other messages: JSON deserialization StaticJsonDocument<512> doc; DeserializationError error = deserializeJson(doc, msg); if (error) { @@ -529,7 +496,7 @@ void Mqtt::callback(char *topic, byte *payload, unsigned int length) { return; } - // Verarbeite die restlichen Nachrichten + // Process remaining messages if (topicStr == baseTopic + "/cmd") { processState(doc); processEffect(doc); @@ -538,30 +505,34 @@ void Mqtt::callback(char *topic, byte *payload, unsigned int length) { processBrightness(doc); } else if (topicStr == baseTopic + "/scrolltext/set") { processScrollingText(doc); - } else if (topicStr == baseTopic + "/bright_offset/set") { - processBrightOffset(doc); - } else if (topicStr == baseTopic + "/bright_slope/set") { - processBrightSlope(doc); - } else if (topicStr == baseTopic + "/scroll_speed/set") { - processScrollSpeed(doc); } else if (topicStr == baseTopic + "/effect_speed/set") { - processEffectSpeed(doc); - } else if (topicStr == baseTopic + "/scrolling_text_speed/set") { + // Process direct string value int speed = atoi(msg); - G.effectSpeed = constrain(speed, 1, 10); - parametersChanged = true; + if (speed >= 1 && speed <= 10) { + G.effectSpeed = speed; - String stateTopic = - String(G.mqtt.topic) + "/scrolling_text_speed/state"; - mqttClient.publish(stateTopic.c_str(), String(G.effectSpeed).c_str(), - true); + // Save changes to EEPROM + eeprom::write(); + parametersChanged = true; - StaticJsonDocument<200> webDoc; - webDoc["command"] = "speed"; - webDoc["value"] = G.effectSpeed; - char buffer[200]; - serializeJson(webDoc, buffer); - webSocket.broadcastTXT(buffer, strlen(buffer)); + // If we are in scrolltext mode, reinitialize the program + if (G.prog == COMMAND_MODE_SCROLLINGTEXT) { + G.progInit = true; + } + + // Send new state to Home Assistant + mqttClient.publish( + (std::string(G.mqtt.topic) + "/effect_speed/state").c_str(), + String(G.effectSpeed).c_str(), true); + + // Send update to web interface + StaticJsonDocument<200> webDoc; + webDoc["command"] = "speed"; + webDoc["value"] = G.effectSpeed; + char buffer[200]; + serializeJson(webDoc, buffer); + webSocket.broadcastTXT(buffer, strlen(buffer)); + } } } @@ -584,67 +555,25 @@ None */ void Mqtt::sendState() { - // Hauptstatus + // Main status { StaticJsonDocument<200> doc; doc["state"] = (led.getState()) ? "ON" : "OFF"; - // Berechne den tatsächlichen Helligkeitswert + // Calculate actual brightness value float actualBrightness; if (G.autoBrightEnabled) { - // Wenn automatische Helligkeit aktiv ist, verwende den aktuellen - // LED-Gain actualBrightness = (ledGain / 100.0f) * G.color[Foreground].B; } else { - // Wenn automatische Helligkeit deaktiviert ist, verwende nur die - // manuelle Helligkeit actualBrightness = G.color[Foreground].B; } - doc["brightness"] = - round(actualBrightness * 255); // Konvertiere von 0-1 zu 0-255 - - doc["color_mode"] = "rgb"; - - // Konvertiere HSB zu RGB - float h = G.color[Foreground].H; - float s = G.color[Foreground].S; - float v = 1.0f; // Volle Helligkeit für Farbe - - float c = v * s; - float x = c * (1 - abs(fmod(h * 6, 2) - 1)); - float m = v - c; - - float r, g, b; - if (h < 1.0f / 6.0f) { - r = c; - g = x; - b = 0; - } else if (h < 2.0f / 6.0f) { - r = x; - g = c; - b = 0; - } else if (h < 3.0f / 6.0f) { - r = 0; - g = c; - b = x; - } else if (h < 4.0f / 6.0f) { - r = 0; - g = x; - b = c; - } else if (h < 5.0f / 6.0f) { - r = x; - g = 0; - b = c; - } else { - r = c; - g = 0; - b = x; - } + doc["brightness"] = round(actualBrightness * 255); + doc["color_mode"] = "hs"; + // Color as object with h and s JsonObject color = doc.createNestedObject("color"); - color["r"] = round((r + m) * 255); - color["g"] = round((g + m) * 255); - color["b"] = round((b + m) * 255); + color["h"] = round(G.color[Foreground].H * 360); // Hue 0-360 + color["s"] = round(G.color[Foreground].S * 100); // Saturation 0-100 switch (G.prog) { case COMMAND_MODE_WORD_CLOCK: @@ -679,42 +608,28 @@ void Mqtt::sendState() { buffer, true); } - // Diagnose-Werte senden + // Send diagnostic values { StaticJsonDocument<200> doc; - doc["lux"] = round(clockWork.getLuxValue()); // Runde den Lux-Wert - doc["led_gain"] = round(ledGain); // Konvertiere zu Prozent und runde + doc["lux"] = round(clockWork.getLuxValue()); // Round lux value + doc["led_gain"] = round(ledGain); // Convert to percent and round doc["adc_value"] = - clockWork.getAdcValue(); // Spannungswert (bereits gerundet) - doc["adc_raw"] = clockWork.getAdcRawValue(); // Roher ADC-Wert + clockWork.getAdcValue(); // Voltage value (already rounded) + doc["adc_raw"] = clockWork.getAdcRawValue(); // Raw ADC value char buffer[200]; serializeJson(doc, buffer); mqttClient.publish((std::string(G.mqtt.topic) + "/diagnostics").c_str(), buffer, true); } - // Helligkeitsoffset Status - { - mqttClient.publish( - (std::string(G.mqtt.topic) + "/bright_offset/state").c_str(), - String(G.autoBrightOffset).c_str(), true); - } - - // Helligkeitssteigung Status + // Effect speed status - Always update current value { mqttClient.publish( - (std::string(G.mqtt.topic) + "/bright_slope/state").c_str(), - String(G.autoBrightSlope).c_str(), true); - } - - // Laufschriftgeschwindigkeit Status - { - mqttClient.publish( - (std::string(G.mqtt.topic) + "/scroll_speed/state").c_str(), + (std::string(G.mqtt.topic) + "/effect_speed/state").c_str(), String(G.effectSpeed).c_str(), true); } - // Scrollingtext Status + // Scrolling text status { StaticJsonDocument<200> doc; doc["scrolling_text"] = G.scrollingText; @@ -725,14 +640,7 @@ void Mqtt::sendState() { true); } - // Effektgeschwindigkeit Status - { - mqttClient.publish( - (std::string(G.mqtt.topic) + "/effect_speed/state").c_str(), - String(G.effectSpeed).c_str(), true); - } - - // Helligkeitsautomatik Status + // Auto brightness status { static bool lastAutoBrightState = G.autoBrightEnabled; if (lastAutoBrightState != G.autoBrightEnabled) { @@ -743,7 +651,7 @@ void Mqtt::sendState() { } } - // Aktualisiere den Online-Status + // Update online status mqttClient.publish((std::string(G.mqtt.topic) + "/availability").c_str(), "online", true); } @@ -763,54 +671,19 @@ None Output: None - */ - -/* Example MQTT Message -{ - "brightness": true, - "color_mode": true, - "supported_color_modes": [ - "hs" - ], - "schema": "json", - "name": "ESP", - "device": { - "identifiers": [ - "ESPBuro" - ], - "name": "ESP", - "sw_version": "3.3", - "configuration_url": "http://" - }, - "state_topic": "ESPBuro/status", - "command_topic": "ESPBuro/cmd", - "unique_id": "", - "plattform": "mqtt", - "effect": true, - "effect_list": [ - "Wordclock", - "Seconds", - "Digitalclock", - "Scrollingtext", - "Rainbowcycle", - "Rainbow", - "Color", - "Symbol" - ] -} */ void Mqtt::sendDiscovery() { - // Erstelle eine unique_id basierend auf MAC-Adresse + // Create unique_id based on MAC address String unique_id = WiFi.macAddress(); - unique_id.replace(":", ""); // Entferne die Doppelpunkte aus der MAC-Adresse + unique_id.replace(":", ""); // Remove colons from MAC address - // Hauptlicht-Entity + // Main light entity { StaticJsonDocument<700> root; mqttClient.setBufferSize(700); - // Basis-Konfiguration + // Base configuration root["name"] = String(G.mqtt.clientId); root["unique_id"] = unique_id; @@ -821,17 +694,17 @@ void Mqtt::sendDiscovery() { root["payload_available"] = "online"; root["payload_not_available"] = "offline"; - // Funktionen + // Functions root["brightness"] = true; root["brightness_scale"] = 255; JsonArray colorModes = root.createNestedArray("supported_color_modes"); - colorModes.add("rgb"); // Korrekter Color Mode + colorModes.add("hs"); // Correct color mode root["optimistic"] = false; // Schema root["schema"] = "json"; - // Effekte + // Effects root["effect"] = true; JsonArray effectList = root.createNestedArray("effect_list"); effectList.add("Wordclock"); @@ -843,7 +716,7 @@ void Mqtt::sendDiscovery() { effectList.add("Color"); effectList.add("Symbol"); - // Geräte-Information + // Device information JsonObject device = root.createNestedObject("device"); JsonArray identifiers = device.createNestedArray("identifiers"); identifiers.add(unique_id); @@ -861,7 +734,7 @@ void Mqtt::sendDiscovery() { mqttClient.publish(discoveryTopic.c_str(), buffer, true); } - // 2. Lauftext Switch + // 2. Scrolling Text Switch { StaticJsonDocument<512> root; @@ -892,14 +765,14 @@ void Mqtt::sendDiscovery() { buffer, true); } - // 3. Lauftext-Geschwindigkeit + // 3. Effect Speed { StaticJsonDocument<512> root; - root["name"] = "Scrolling Speed"; - root["unique_id"] = unique_id + "_scrolling_text_speed"; + root["name"] = "Effect Speed"; + root["unique_id"] = unique_id + "_effect_speed"; root["icon"] = "mdi:speedometer"; - root["device_class"] = "speed"; // Korrekte Device Class + root["device_class"] = "speed"; JsonObject deviceCopy = root.createNestedObject("device"); JsonArray deviceIdentifiers = @@ -911,10 +784,8 @@ void Mqtt::sendDiscovery() { deviceCopy["manufacturer"] = "ESPWortuhr"; deviceCopy["configuration_url"] = "http://" + WiFi.localIP().toString(); - root["state_topic"] = - std::string(G.mqtt.topic) + "/scrolling_text_speed/state"; - root["command_topic"] = - std::string(G.mqtt.topic) + "/scrolling_text_speed/set"; + root["state_topic"] = std::string(G.mqtt.topic) + "/effect_speed/state"; + root["command_topic"] = std::string(G.mqtt.topic) + "/effect_speed/set"; root["min"] = 1; root["max"] = 10; root["step"] = 1; @@ -922,12 +793,12 @@ void Mqtt::sendDiscovery() { char buffer[512]; serializeJson(root, buffer); mqttClient.publish((String(HOMEASSISTANT_DISCOVERY_TOPIC) + "/number/" + - unique_id + "_scrolling_text_speed/config") + unique_id + "_effect_speed/config") .c_str(), buffer, true); } - // Diagnose-Entities + // Diagnostic Entities { // Lux Sensor StaticJsonDocument<512> root; @@ -1051,11 +922,11 @@ void Mqtt::sendDiscovery() { buffer, true); } - // Sende Online-Status + // Send online status mqttClient.publish((std::string(G.mqtt.topic) + "/availability").c_str(), "online", true); - // Helligkeitsautomatik Switch + // Auto brightness switch { StaticJsonDocument<512> root; root["name"] = "Auto Brightness"; @@ -1087,71 +958,4 @@ void Mqtt::sendDiscovery() { .c_str(), buffer, true); } - - // Debug Sensor für Auto-Brightness - { - StaticJsonDocument<512> root; - root["name"] = "Auto Brightness Debug"; - root["unique_id"] = unique_id + "_auto_brightness_debug"; - root["icon"] = "mdi:bug"; - root["device_class"] = "diagnostic"; - - JsonObject deviceCopy = root.createNestedObject("device"); - JsonArray deviceIdentifiers = - deviceCopy.createNestedArray("identifiers"); - deviceIdentifiers.add(unique_id); - deviceCopy["name"] = G.mqtt.clientId; - deviceCopy["sw_version"] = VERSION; - deviceCopy["model"] = "Word Clock"; - deviceCopy["manufacturer"] = "ESPWortuhr"; - deviceCopy["configuration_url"] = "http://" + WiFi.localIP().toString(); - - root["state_topic"] = - std::string(G.mqtt.topic) + "/debug/auto_brightness"; - root["value_template"] = "{{ value_json.received_msg }}"; - - char buffer[512]; - serializeJson(root, buffer); - mqttClient.publish((String(HOMEASSISTANT_DISCOVERY_TOPIC) + "/sensor/" + - unique_id + "_auto_brightness_debug/config") - .c_str(), - buffer, true); - } -} - -// Neue Callback-Funktionen für die zusätzlichen Entitäten -void Mqtt::processBrightOffset(const JsonDocument &doc) { - if (doc.containsKey("value")) { - G.autoBrightOffset = doc["value"]; - parametersChanged = true; - if (mqttInstance) - mqttInstance->sendState(); - } -} - -void Mqtt::processBrightSlope(const JsonDocument &doc) { - if (doc.containsKey("value")) { - G.autoBrightSlope = doc["value"]; - parametersChanged = true; - if (mqttInstance) - mqttInstance->sendState(); - } -} - -void Mqtt::processScrollSpeed(const JsonDocument &doc) { - if (doc.containsKey("value")) { - G.effectSpeed = doc["value"]; - parametersChanged = true; - if (mqttInstance) - mqttInstance->sendState(); - } -} - -void Mqtt::processEffectSpeed(const JsonDocument &doc) { - if (doc.containsKey("value")) { - G.effectSpeed = doc["value"]; - parametersChanged = true; - if (mqttInstance) - mqttInstance->sendState(); - } }