From 9381b2a33ba496a6ef70a0c60475e2c554cabf37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20M=C3=BChl?= Date: Wed, 29 Mar 2023 15:13:21 +0200 Subject: [PATCH 1/2] progmem chars --- .../src/ArduinoHADefines.h | 2 +- src/Dictionary.cpp | 102 ++++++++++ src/Dictionary.h | 104 ++++++++++ src/DisplayManager.cpp | 33 ++- src/MQTTManager.cpp | 190 +++++++++++------- src/MQTTManager.h | 1 + src/PeripheryManager.cpp | 40 ++-- src/ServerManager.cpp | 14 +- 8 files changed, 372 insertions(+), 114 deletions(-) create mode 100644 src/Dictionary.cpp create mode 100644 src/Dictionary.h diff --git a/lib/home-assistant-integration/src/ArduinoHADefines.h b/lib/home-assistant-integration/src/ArduinoHADefines.h index 186a0b5..0188002 100644 --- a/lib/home-assistant-integration/src/ArduinoHADefines.h +++ b/lib/home-assistant-integration/src/ArduinoHADefines.h @@ -4,7 +4,7 @@ //#define ARDUINOHA_DEBUG // These macros allow to exclude some parts of the library to save more resources. - #define EX_ARDUINOHA_BINARY_SENSOR + //#define EX_ARDUINOHA_BINARY_SENSOR //#define EX_ARDUINOHA_BUTTON #define EX_ARDUINOHA_CAMERA #define EX_ARDUINOHA_COVER diff --git a/src/Dictionary.cpp b/src/Dictionary.cpp new file mode 100644 index 0000000..d32ccc2 --- /dev/null +++ b/src/Dictionary.cpp @@ -0,0 +1,102 @@ +#include +#include "Dictionary.h" + +// MQTT +const char StatsTopic[] PROGMEM = {"stats"}; +const char ButtonLeftTopic[] PROGMEM = {"stats/buttonLeft"}; +const char ButtonSelectTopic[] PROGMEM = {"stats/buttonSelect"}; +const char ButtonRightTopic[] PROGMEM = {"stats/buttonRight"}; + +// Generic +const char State0[] PROGMEM = {"0"}; +const char State1[] PROGMEM = {"1"}; + +// HA +const char HAmanufacturer[] PROGMEM = {"Blueforcer"}; +const char HAmodel[] PROGMEM = {"AWTRIX Light"}; +const char HAmatID[] PROGMEM = {"%s_mat"}; +const char HAmatIcon[] PROGMEM = {"mdi:lightbulb"}; +const char HAmatName[] PROGMEM = {"Matrix"}; +const char HAbriID[] PROGMEM = {"%s_bri"}; +const char HAbriIcon[] PROGMEM = {"mdi:brightness-auto"}; +const char HAbriName[] PROGMEM = {"Brightness mode"}; +const char HAbriOptions[] PROGMEM = {"Manual;Auto"}; + +const char HAbtnaID[] PROGMEM = {"%s_btna"}; +const char HAbtnaIcon[] PROGMEM = {"mdi:bell-off"}; +const char HAbtnaName[] PROGMEM = {"Dismiss notification"}; + +const char HAbtnbID[] PROGMEM = {"%s_btnb"}; +const char HAbtnbIcon[] PROGMEM = {"mdi:arrow-right-bold"}; +const char HAbtnbName[] PROGMEM = {"Next app"}; + +const char HAbtncID[] PROGMEM = {"%s_btnc"}; +const char HAbtncIcon[] PROGMEM = {"mdi:arrow-left-bold"}; +const char HAbtncName[] PROGMEM = {"Previous app"}; + +const char HAappID[] PROGMEM = {"%s_app"}; +const char HAappIcon[] PROGMEM = {"mdi:apps"}; +const char HAappName[] PROGMEM = {"Current app"}; + +const char HAtempID[] PROGMEM = {"%s_temp"}; +const char HAtempIcon[] PROGMEM = {"mdi:thermometer"}; +const char HAtempName[] PROGMEM = {"Temperature"}; +const char HAtempClass[] PROGMEM = {"temperature"}; +const char HAtempUnit[] PROGMEM = {"°C"}; + +const char HAhumID[] PROGMEM = {"%s_hum"}; +const char HAhumIcon[] PROGMEM = {"mdi:water-percent"}; +const char HAhumName[] PROGMEM = {"Humidity"}; +const char HAhumClass[] PROGMEM = {"humidity"}; +const char HAhumUnit[] PROGMEM = {"%"}; + +const char HAbatID[] PROGMEM = {"%s_bat"}; +const char HAbatIcon[] PROGMEM = {"mdi:battery-90"}; +const char HAbatName[] PROGMEM = {"Battery"}; +const char HAbatClass[] PROGMEM = {"battery"}; +const char HAbatUnit[] PROGMEM = {"%"}; + +const char HAluxID[] PROGMEM = {"%s_lux"}; +const char HAluxIcon[] PROGMEM = {"mdi:sun-wireless"}; +const char HAluxName[] PROGMEM = {"Illuminance"}; +const char HAluxClass[] PROGMEM = {"illuminance"}; +const char HAluxUnit[] PROGMEM = {"lx"}; + +const char HAverID[] PROGMEM = {"%s_ver"}; +const char HAverName[] PROGMEM = {"Version"}; + +const char HAsigID[] PROGMEM = {"%s_sig"}; +const char HAsigIcon[] PROGMEM = {"mdi:sun-wireless"}; +const char HAsigName[] PROGMEM = {"WiFi strength"}; +const char HAsigClass[] PROGMEM = {"signal_strength"}; +const char HAsigUnit[] PROGMEM = {"dB"}; + +const char HAupID[] PROGMEM = {"%s_up"}; +const char HAupName[] PROGMEM = {"Uptime"}; +const char HAupClass[] PROGMEM = {"duration"}; + +const char HAbtnLID[] PROGMEM = {"%s_btnL"}; +const char HAbtnLName[] PROGMEM = {"Button left"}; + +const char HAbtnMID[] PROGMEM = {"%s_btnM"}; +const char HAbtnMName[] PROGMEM = {"Button select"}; + +const char HAbtnRID[] PROGMEM = {"%s_btnR"}; +const char HAbtnRName[] PROGMEM = {"Button right"}; + +const char HAramRID[] PROGMEM = {"%s_btnR"}; +const char HAramIcon[] PROGMEM = {"mdi:application-cog"}; +const char HAramName[] PROGMEM = {"Free ram"}; +const char HAramClass[] PROGMEM = {"data_size"}; +const char HAramUnit[] PROGMEM = {"B"}; + +// JSON properites +const char BatKey[] PROGMEM = {"bat"}; +const char BatRawKey[] PROGMEM = {"bat_raw"}; +const char LuxKey[] PROGMEM = {"lux"}; +const char LDRRawKey[] PROGMEM = {"ldr_raw"}; +const char BrightnessKey[] PROGMEM = {"bri"}; +const char TempKey[] PROGMEM = {"temp"}; +const char HumKey[] PROGMEM = {"hum"}; +const char UpTimeKey[] PROGMEM = {"uptime"}; +const char SignalStrengthKey[] PROGMEM = {"wifi_signal"}; diff --git a/src/Dictionary.h b/src/Dictionary.h new file mode 100644 index 0000000..143a69b --- /dev/null +++ b/src/Dictionary.h @@ -0,0 +1,104 @@ +#ifndef DICTIONARY_H +#define DICTIONARY_H + +// MQTT +extern const char StatsTopic[]; +extern const char ButtonLeftTopic[]; +extern const char ButtonSelectTopic[]; +extern const char ButtonRightTopic[]; + +// Generic +extern const char State0[]; +extern const char State1[]; + +// HA +extern const char HAmanufacturer[]; +extern const char HAmodel[]; +extern const char HAmatID[]; +extern const char HAmatIcon[]; +extern const char HAmatName[]; +extern const char HAbriID[]; +extern const char HAbriIcon[]; +extern const char HAbriName[]; +extern const char HAbriOptions[]; + +extern const char HAbtnaID[]; +extern const char HAbtnaIcon[]; +extern const char HAbtnaName[]; + +extern const char HAbtnbID[]; +extern const char HAbtnbIcon[]; +extern const char HAbtnbName[]; + +extern const char HAbtncID[]; +extern const char HAbtncIcon[]; +extern const char HAbtncName[]; + +extern const char HAappID[]; +extern const char HAappIcon[]; +extern const char HAappName[]; + +extern const char HAtempID[]; +extern const char HAtempIcon[]; +extern const char HAtempName[]; +extern const char HAtempClass[]; +extern const char HAtempUnit[]; + +extern const char HAhumID[]; +extern const char HAhumIcon[]; +extern const char HAhumName[]; +extern const char HAhumClass[]; +extern const char HAhumUnit[]; + +extern const char HAbatID[]; +extern const char HAbatIcon[]; +extern const char HAbatName[]; +extern const char HAbatClass[]; +extern const char HAbatUnit[]; + +extern const char HAluxID[]; +extern const char HAluxIcon[]; +extern const char HAluxName[]; +extern const char HAluxClass[]; +extern const char HAluxUnit[]; + +extern const char HAverID[]; +extern const char HAverName[]; + +extern const char HAsigID[]; +extern const char HAsigIcon[]; +extern const char HAsigName[]; +extern const char HAsigClass[]; +extern const char HAsigUnit[]; + +extern const char HAupID[]; +extern const char HAupName[]; +extern const char HAupClass[]; + +extern const char HAbtnLID[]; +extern const char HAbtnLName[]; + +extern const char HAbtnMID[]; +extern const char HAbtnMName[]; + +extern const char HAbtnRID[]; +extern const char HAbtnRName[]; + +extern const char HAramRID[]; +extern const char HAramIcon[]; +extern const char HAramName[]; +extern const char HAramClass[]; +extern const char HAramUnit[]; + +// JSON properites +extern const char BatKey[]; +extern const char BatRawKey[]; +extern const char LuxKey[]; +extern const char LDRRawKey[]; +extern const char BrightnessKey[]; +extern const char TempKey[]; +extern const char HumKey[]; +extern const char UpTimeKey[]; +extern const char SignalStrengthKey[]; + +#endif diff --git a/src/DisplayManager.cpp b/src/DisplayManager.cpp index eefb3f0..1e54928 100644 --- a/src/DisplayManager.cpp +++ b/src/DisplayManager.cpp @@ -260,6 +260,11 @@ void DisplayManager_::generateCustomPage(String name, String payload) CustomApp customApp; + if (customApps.find(name) != customApps.end()) + { + customApp = customApps[name]; + } + if (doc.containsKey("sound")) { customApp.sound = ("/" + doc["sound"].as() + ".txt"); @@ -308,21 +313,27 @@ void DisplayManager_::generateCustomPage(String name, String payload) if (doc.containsKey("icon")) { String iconFileName = String(doc["icon"].as()); - - if (LittleFS.exists("/ICONS/" + iconFileName + ".jpg")) + if (customApp.icon && String(customApp.icon.name()).startsWith(iconFileName)) { - customApp.isGif = false; - customApp.icon = LittleFS.open("/ICONS/" + iconFileName + ".jpg"); - } - else if (LittleFS.exists("/ICONS/" + iconFileName + ".gif")) - { - customApp.isGif = true; - customApp.icon = LittleFS.open("/ICONS/" + iconFileName + ".gif"); + } else { - fs::File nullPointer; - customApp.icon = nullPointer; + if (LittleFS.exists("/ICONS/" + iconFileName + ".jpg")) + { + customApp.isGif = false; + customApp.icon = LittleFS.open("/ICONS/" + iconFileName + ".jpg"); + } + else if (LittleFS.exists("/ICONS/" + iconFileName + ".gif")) + { + customApp.isGif = true; + customApp.icon = LittleFS.open("/ICONS/" + iconFileName + ".gif"); + } + else + { + fs::File nullPointer; + customApp.icon = nullPointer; + } } } else diff --git a/src/MQTTManager.cpp b/src/MQTTManager.cpp index 52a31da..1ce9665 100644 --- a/src/MQTTManager.cpp +++ b/src/MQTTManager.cpp @@ -5,6 +5,7 @@ #include #include #include +#include "Dictionary.h" unsigned long startTime; @@ -29,9 +30,11 @@ HASensor *humidity = nullptr; HASensor *illuminance = nullptr; HASensor *uptime = nullptr; HASensor *strength = nullptr; - HASensor *version = nullptr; HASensor *ram = nullptr; +HABinarySensor *btnleft = nullptr; +HABinarySensor *btnmid = nullptr; +HABinarySensor *btnright = nullptr; // The getter for the instantiated singleton instance MQTTManager_ &MQTTManager_::getInstance() @@ -212,14 +215,14 @@ void connect() } char matID[40], briID[40]; -char btnAID[40], btnBID[40], btnCID[40], appID[40], tempID[40], humID[40], luxID[40], verID[40], batID[40], ramID[40], upID[40], sigID[40]; +char btnAID[40], btnBID[40], btnCID[40], appID[40], tempID[40], humID[40], luxID[40], verID[40], batID[40], ramID[40], upID[40], sigID[40], btnLID[40], btnMID[40], btnRID[40]; void MQTTManager_::setup() { startTime = millis(); if (HA_DISCOVERY) { - Serial.println("Starting Homeassistant discorvery"); + Serial.println(F("Starting Homeassistant discorvery")); uint8_t mac[6]; WiFi.macAddress(mac); @@ -228,17 +231,17 @@ void MQTTManager_::setup() device.setUniqueId(mac, sizeof(mac)); device.setName(uniqueID); device.setSoftwareVersion(VERSION); - device.setManufacturer("Blueforcer"); - device.setModel("AWTRIX Light"); + device.setManufacturer(HAmanufacturer); + device.setModel(HAmodel); device.setAvailability(true); String uniqueIDWithSuffix; - sprintf(matID, "%s_mat", macStr); + sprintf(matID, HAmatID, macStr); Matrix = new HALight(matID, HALight::BrightnessFeature | HALight::RGBFeature); - Matrix->setIcon("mdi:lightbulb"); - Matrix->setName("Matrix"); + Matrix->setIcon(HAmatIcon); + Matrix->setName(HAmatName); Matrix->onStateCommand(onStateCommand); Matrix->onBrightnessCommand(onBrightnessCommand); Matrix->onRGBColorCommand(onRGBColorCommand); @@ -252,87 +255,99 @@ void MQTTManager_::setup() Matrix->setCurrentRGBColor(color); Matrix->setState(true, true); - sprintf(briID, "%s_bri", macStr); + sprintf(briID, HAbriID, macStr); BriMode = new HASelect(briID); - BriMode->setOptions("Manual;Auto"); // use semicolons as separator of options + BriMode->setOptions(HAbriOptions); // use semicolons as separator of options BriMode->onCommand(onSelectCommand); - BriMode->setIcon("mdi:brightness-auto"); // optional - BriMode->setName("Brightness mode"); // optional + BriMode->setIcon(HAbriIcon); // optional + BriMode->setName(HAbriName); // optional BriMode->setState(AUTO_BRIGHTNESS, true); - sprintf(btnAID, "%s_btna", macStr); + sprintf(btnAID, HAbtnaID, macStr); dismiss = new HAButton(btnAID); - dismiss->setIcon("mdi:bell-off"); - dismiss->setName("Dismiss notification"); + dismiss->setIcon(HAbtnaIcon); + dismiss->setName(HAbtnaName); - sprintf(btnBID, "%s_btnb", macStr); + sprintf(btnBID, HAbtnbID, macStr); nextApp = new HAButton(btnBID); - nextApp->setIcon("mdi:arrow-right-bold"); - nextApp->setName("Next app"); + nextApp->setIcon(HAbtnbIcon); + nextApp->setName(HAbtnbName); - sprintf(btnCID, "%s_btnc", macStr); + sprintf(btnCID, HAbtncID, macStr); prevApp = new HAButton(btnCID); - prevApp->setIcon("mdi:arrow-left-bold"); - prevApp->setName("Previous app"); + prevApp->setIcon(HAbtncIcon); + prevApp->setName(HAbtncName); dismiss->onCommand(onButtonCommand); nextApp->onCommand(onButtonCommand); prevApp->onCommand(onButtonCommand); - sprintf(appID, "%s_app", macStr); + sprintf(appID, HAappID, macStr); curApp = new HASensor(appID); - curApp->setIcon("mdi:apps"); - curApp->setName("Current app"); + curApp->setIcon(HAappIcon); + curApp->setName(HAappName); - sprintf(tempID, "%s_temp", macStr); + sprintf(tempID, HAtempID, macStr); temperature = new HASensor(tempID); - temperature->setIcon("mdi:thermometer"); - temperature->setName("Temperature"); - temperature->setDeviceClass("temperature"); - temperature->setUnitOfMeasurement("°C"); + temperature->setIcon(HAtempIcon); + temperature->setName(HAtempName); + temperature->setDeviceClass(HAtempClass); + temperature->setUnitOfMeasurement(HAtempUnit); - sprintf(humID, "%s_hum", macStr); + sprintf(humID, HAhumID, macStr); humidity = new HASensor(humID); - humidity->setIcon("mdi:water-percent"); - humidity->setName("Humidity"); - humidity->setDeviceClass("humidity"); - humidity->setUnitOfMeasurement("%"); + humidity->setIcon(HAhumIcon); + humidity->setName(HAhumName); + humidity->setDeviceClass(HAhumClass); + humidity->setUnitOfMeasurement(HAhumUnit); - sprintf(batID, "%s_bat", macStr); + sprintf(batID, HAbatID, macStr); battery = new HASensor(batID); - battery->setIcon("mdi:battery-90"); - battery->setName("Battery"); - battery->setDeviceClass("battery"); - battery->setUnitOfMeasurement("%"); + battery->setIcon(HAbatIcon); + battery->setName(HAbatName); + battery->setDeviceClass(HAbatClass); + battery->setUnitOfMeasurement(HAbatUnit); - sprintf(luxID, "%s_lux", macStr); + sprintf(luxID, HAluxID, macStr); illuminance = new HASensor(luxID); - illuminance->setIcon("mdi:sun-wireless"); - illuminance->setName("Illuminance"); - illuminance->setDeviceClass("illuminance"); - illuminance->setUnitOfMeasurement("lx"); + illuminance->setIcon(HAluxIcon); + illuminance->setName(HAluxName); + illuminance->setDeviceClass(HAluxClass); + illuminance->setUnitOfMeasurement(HAluxUnit); - sprintf(verID, "%s_ver", macStr); + sprintf(verID, HAverID, macStr); version = new HASensor(verID); - version->setName("Version"); + version->setName(HAverName); - sprintf(sigID, "%s_sig", macStr); + sprintf(sigID, HAsigID, macStr); strength = new HASensor(sigID); - strength->setName("WiFi strength"); - strength->setDeviceClass("signal_strength"); - strength->setUnitOfMeasurement("dB"); + strength->setName(HAsigName); + strength->setDeviceClass(HAsigClass); + strength->setUnitOfMeasurement(HAsigUnit); - sprintf(upID, "%s_up", macStr); + sprintf(upID, HAupID, macStr); uptime = new HASensor(upID); - uptime->setName("Uptime"); - uptime->setDeviceClass("duration"); + uptime->setName(HAupName); + uptime->setDeviceClass(HAupClass); - sprintf(ramID, "%s_ram", macStr); + sprintf(btnLID, HAbtnLID, macStr); + btnleft = new HABinarySensor(btnLID); + btnleft->setName(HAbtnLName); + + sprintf(btnMID, HAbtnMID, macStr); + btnmid = new HABinarySensor(btnMID); + btnmid->setName(HAbtnMName); + + sprintf(btnRID, HAbtnRID, macStr); + btnright = new HABinarySensor(btnRID); + btnright->setName(HAbtnRName); + + sprintf(ramID, HAramRID, macStr); ram = new HASensor(ramID); - ram->setDeviceClass("data_size"); - ram->setIcon("mdi:application-cog"); - ram->setName("Free ram"); - ram->setUnitOfMeasurement("B"); + ram->setDeviceClass(HAramClass); + ram->setIcon(HAramIcon); + ram->setName(HAramName); + ram->setUnitOfMeasurement(HAramUnit); } else { @@ -419,22 +434,55 @@ void MQTTManager_::sendStats() StaticJsonDocument<200> doc; char buffer[5]; - doc["bat"] = BATTERY_PERCENT; - doc["batraw"] = BATTERY_RAW; + doc[BatKey] = BATTERY_PERCENT; + doc[BatRawKey] = BATTERY_RAW; snprintf(buffer, 5, "%.0f", CURRENT_LUX); - doc["lux"] = buffer; - doc["ldrraw"] = LDR_RAW; - doc["bri"] = BRIGHTNESS; + doc[LuxKey] = buffer; + doc[LDRRawKey] = LDR_RAW; + doc[BrightnessKey] = BRIGHTNESS; snprintf(buffer, 5, "%.0f", CURRENT_TEMP); - doc["temp"] = buffer; + doc[TempKey] = buffer; snprintf(buffer, 5, "%.0f", CURRENT_HUM); - doc["hum"] = buffer; - doc["uptime"] = readUptime(); - doc["wifi"] = WiFi.RSSI(); + doc[HumKey] = buffer; + doc[UpTimeKey] = readUptime(); + doc[SignalStrengthKey] = WiFi.RSSI(); String jsonString; serializeJson(doc, jsonString); - char topic[50]; - strcpy(topic, MQTT_PREFIX.c_str()); - strcat(topic, "/stats"); - mqtt.publish(topic, jsonString.c_str()); + publish(StatsTopic, jsonString.c_str()); +} + +void MQTTManager_::sendButton(byte btn, bool state) +{ + static bool btn0State, btn1State, btn2State; + + switch (btn) + { + case 0: + if (btn0State != state) + { + btnleft->setState(state, false); + btn0State = state; + publish(ButtonLeftTopic, state ? State1 : State0); + } + break; + case 1: + if (btn1State != state) + { + btnmid->setState(state, false); + btn1State = state; + publish(ButtonSelectTopic, state ? State1 : State0); + } + + break; + case 2: + if (btn2State != state) + { + btnright->setState(state, false); + btn2State = state; + publish(ButtonRightTopic, state ? State1 : State0); + } + break; + default: + break; + } } diff --git a/src/MQTTManager.h b/src/MQTTManager.h index aa1a5c3..312ed7a 100644 --- a/src/MQTTManager.h +++ b/src/MQTTManager.h @@ -15,6 +15,7 @@ public: void publish(const char *topic, const char *payload); void setCurrentApp(String value); void sendStats(); + void sendButton(byte btn, bool state); }; extern MQTTManager_ &MQTTManager; diff --git a/src/PeripheryManager.cpp b/src/PeripheryManager.cpp index 4090edf..544024c 100644 --- a/src/PeripheryManager.cpp +++ b/src/PeripheryManager.cpp @@ -9,27 +9,26 @@ #include #include -#define SOUND_OFF false +#define SOUND_OFF true #ifdef ULANZI // Pinouts für das ULANZI-Environment - #define BATTERY_PIN 34 - #define BUZZER_PIN 15 - #define LDR_PIN 35 - #define BUTTON_UP_PIN 26 - #define BUTTON_DOWN_PIN 14 - #define BUTTON_SELECT_PIN 27 +#define BATTERY_PIN 34 +#define BUZZER_PIN 15 +#define LDR_PIN 35 +#define BUTTON_UP_PIN 26 +#define BUTTON_DOWN_PIN 14 +#define BUTTON_SELECT_PIN 27 #else // Pinouts für das WEMOS_D1_MINI32-Environment - #define BATTERY_PIN -1 - #define BUZZER_PIN -1 - #define LDR_PIN A0 - #define BUTTON_UP_PIN D0 - #define BUTTON_DOWN_PIN D4 - #define BUTTON_SELECT_PIN D8 +#define BATTERY_PIN -1 +#define BUZZER_PIN -1 +#define LDR_PIN A0 +#define BUTTON_UP_PIN D0 +#define BUTTON_DOWN_PIN D4 +#define BUTTON_SELECT_PIN D8 #endif - Adafruit_SHT31 sht31; EasyButton button_left(BUTTON_UP_PIN); EasyButton button_right(BUTTON_DOWN_PIN); @@ -58,8 +57,6 @@ float sampleSum = 0.0; float sampleAverage = 0.0; float brightnessPercent = 0.0; - - // The getter for the instantiated singleton instance PeripheryManager_ &PeripheryManager_::getInstance() { @@ -140,7 +137,7 @@ void fistStart() uint16_t ADCVALUE = analogRead(BATTERY_PIN); - BATTERY_PERCENT = min((int)map(ADCVALUE, 490, 660, 0, 100), 100); + BATTERY_PERCENT = min((int)map(ADCVALUE, 490, 690, 0, 100), 100); sht31.readBoth(&CURRENT_TEMP, &CURRENT_HUM); uint16_t LDRVALUE = analogRead(LDR_PIN); @@ -170,9 +167,10 @@ void PeripheryManager_::setup() void PeripheryManager_::tick() { - button_left.read(); - button_right.read(); - button_select.read(); + + MQTTManager.sendButton(0, button_left.read()); + MQTTManager.sendButton(1, button_select.read()); + MQTTManager.sendButton(2, button_right.read()); unsigned long currentMillis_BatTempHum = millis(); if (currentMillis_BatTempHum - previousMillis_BatTempHum >= interval_BatTempHum) @@ -212,8 +210,6 @@ void PeripheryManager_::tick() } } - - const int MIN_ALARM_INTERVAL = 60; // 1 Minute time_t lastAlarmTime = 0; diff --git a/src/ServerManager.cpp b/src/ServerManager.cpp index 2f2776e..f676a5a 100644 --- a/src/ServerManager.cpp +++ b/src/ServerManager.cpp @@ -63,7 +63,7 @@ void ServerManager_::setup() { WiFi.config(local_IP, gateway, subnet, primaryDNS, secondaryDNS); } - IPAddress myIP = mws.startWiFi(150000, uniqueID, "12345678"); + IPAddress myIP = mws.startWiFi(10000, uniqueID, "12345678"); isConnected = !(myIP == IPAddress(192, 168, 4, 1)); Serial.println(myIP.toString()); @@ -117,7 +117,7 @@ void ServerManager_::tick() uint16_t stringToColor(const String &str) { - // Aufteilen des Strings in seine Bestandteile + int comma1 = str.indexOf(','); int comma2 = str.lastIndexOf(','); if (comma1 < 0 || comma2 < 0 || comma2 == comma1) @@ -129,36 +129,33 @@ uint16_t stringToColor(const String &str) String gStr = str.substring(comma1 + 1, comma2); String bStr = str.substring(comma2 + 1); - // Konvertieren der Werte von Strings zu Zahlen + int r = rStr.toInt(); int g = gStr.toInt(); int b = bStr.toInt(); - // Sicherheitsabfrage: Werte müssen zwischen 0 und 255 liegen + if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) { return 0xFFFF; } - // Konvertieren der Werte zu 5-6-5 Bitformat uint16_t color = ((r >> 3) << 11) | ((g >> 2) & 0x3F) << 5 | (b >> 3); return color; } String colorToString(uint16_t color) { - // Konvertieren der Farbwerte von 5-6-5 Bitformat zu 8 Bit + uint8_t r = (color >> 11) << 3; uint8_t g = ((color >> 5) & 0x3F) << 2; uint8_t b = (color & 0x1F) << 3; - // Sicherheitsabfrage: Werte müssen zwischen 0 und 255 liegen if (r > 255 || g > 255 || b > 255) { return "#FFFFFF"; } - // Konvertieren der Farbwerte zu Strings und Zusammenführen String rStr = String(r); String gStr = String(g); String bStr = String(b); @@ -202,4 +199,3 @@ void ServerManager_::loadSettings() Serial.println(F("Configuration file not exist")); return; } - From 249074d0220e204b36550eb9988a1435c5383593 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20M=C3=BChl?= <31169771+Blueforcer@users.noreply.github.com> Date: Thu, 30 Mar 2023 12:34:53 +0200 Subject: [PATCH 2/2] v0.43 - Expose buttons to HA - HA Switch to control the transition - Icons are not reloaded if its the same as before --- docs/_sidebar.md | 1 + docs/apps.md | 38 ++++++++++++++++++++++++++++++++ docs/icons.md | 2 +- docs/index.html | 13 ++++++++++- lib/MatrixUI/MatrixDisplayUi.cpp | 12 ---------- lib/MatrixUI/MatrixDisplayUi.h | 6 ++--- src/Dictionary.cpp | 6 ++++- src/Dictionary.h | 4 ++++ src/DisplayManager.cpp | 2 +- src/Globals.cpp | 2 +- src/MQTTManager.cpp | 34 +++++++++++++++++++++------- src/PeripheryManager.cpp | 2 +- 12 files changed, 92 insertions(+), 30 deletions(-) create mode 100644 docs/apps.md diff --git a/docs/_sidebar.md b/docs/_sidebar.md index 8d32213..f654dfb 100644 --- a/docs/_sidebar.md +++ b/docs/_sidebar.md @@ -9,6 +9,7 @@ - [Onscreen](onscreen.md) - Features + - [Apps](apps.md) - [Custom Pages & Notifications](custom.md) - [Alarm clock](alarm.md) - [Timer](timer.md) diff --git a/docs/apps.md b/docs/apps.md new file mode 100644 index 0000000..235be7e --- /dev/null +++ b/docs/apps.md @@ -0,0 +1,38 @@ +# Apps + +The AWTRIX Light system comes equipped with several built-in applications, including Time, Date, Temperature, Humidity, and Battery status. +As it is designed to integrate seamlessly with your smart home ecosystem, additional applications can be created using MQTT. + +There are numerous benefits to this approach: + +- **Personalization:** Customize each application to suit your preferences and needs. +- **Flexibility:** Develop your own applications without the need to modify the firmware. +- **Efficient resource management:** Save valuable flash memory space on the ESP module. +- **Adaptability:** No need to rewrite the firmware if an API undergoes changes. + + +You can use any system you like wich is able to build json strings and send them to a mqtt topic. + +[Node-RED](https://nodered.org/) serves as an ideal software solution for creating these applications. +It is available as a standalone program or as a plugin for Home Assistant and ioBroker, allowing you to further enhance the capabilities of your AWTRIX Light system. + + +Here is a demo of an Youtube App as NodeRED Flow: +```json +[{"id":"2a59d30d07abe14f","type":"group","z":"54b42d8d.cda474","style":{"stroke":"#999999","stroke-opacity":"1","fill":"none","fill-opacity":"1","label":true,"label-position":"nw","color":"#a4a4a4"},"nodes":["f0f17299.3736c","dc7878f9.4756c8","f234aae371d72680","555bb8624b88c9c3","69c388146e28049d","a349ade5a57f7537"],"x":34,"y":39,"w":892,"h":122},{"id":"f0f17299.3736c","type":"inject","z":"54b42d8d.cda474","g":"2a59d30d07abe14f","name":"","props":[],"repeat":"3600","crontab":"","once":true,"onceDelay":0.1,"topic":"","x":130,"y":120,"wires":[["a349ade5a57f7537"]]},{"id":"dc7878f9.4756c8","type":"http request","z":"54b42d8d.cda474","g":"2a59d30d07abe14f","name":"","method":"GET","ret":"obj","paytoqs":"query","url":"https://youtube.googleapis.com/youtube/v3/channels","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":430,"y":120,"wires":[["f234aae371d72680"]]},{"id":"f234aae371d72680","type":"function","z":"54b42d8d.cda474","g":"2a59d30d07abe14f","name":"parser","func":"var json = msg.payload;\nvar subscriberCount = json.items[0].statistics.subscriberCount;\n\nmsg.payload = { \"text\": subscriberCount, \"icon\": 5029};\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":590,"y":120,"wires":[["555bb8624b88c9c3"]]},{"id":"555bb8624b88c9c3","type":"mqtt out","z":"54b42d8d.cda474","g":"2a59d30d07abe14f","name":"","topic":"ulanzi/custom/youtube","qos":"","retain":"","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"346df2a95aac5785","x":800,"y":120,"wires":[]},{"id":"69c388146e28049d","type":"comment","z":"54b42d8d.cda474","g":"2a59d30d07abe14f","name":"Youtube Follower","info":"Just enter your channelID and Youtube API key in the \"Data\" node and set your AWTRIX MQTT prefix.\nUses Icon 5029 (LM)","x":140,"y":80,"wires":[]},{"id":"a349ade5a57f7537","type":"function","z":"54b42d8d.cda474","g":"2a59d30d07abe14f","name":"Data","func":"msg.payload = { \"id\": \"UCpGLALzRO0uaasWTsm9M99w\", \"key\": \"XXX\", \"part\":\"statistics\"}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":270,"y":120,"wires":[["dc7878f9.4756c8"]]},{"id":"346df2a95aac5785","type":"mqtt-broker","name":"","broker":"localhost","port":"1883","clientid":"","autoConnect":true,"usetls":false,"protocolVersion":"4","keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","birthMsg":{},"closeTopic":"","closeQos":"0","closePayload":"","closeMsg":{},"willTopic":"","willQos":"0","willPayload":"","willMsg":{},"userProps":"","sessionExpiry":""}] +``` + +This Node-RED flow retrieves and displays the subscriber count of a specified YouTube channel on an AWTRIX light device. The flow consists of the following nodes: + +1. **Inject**: This node triggers the flow periodically (every hour) or manually. +2. **Data (Function)**: This node contains the YouTube channel ID and the YouTube API key. Replace "XXX" with your YouTube API key and Youtube ID. The node constructs a payload containing the channel ID, API key, and required statistics and sends it to the "HTTP request" node. +3. **HTTP request**: This node sends a GET request to the YouTube API to retrieve the channel's statistics. The response is returned as a JavaScript object and passed to the "parser" node. +4. **parser (Function)**: This node extracts the subscriber count from the received channel statistics and constructs a payload containing the count and an icon (Icon 5029). The payload is sent to the "MQTT out" node. +5. **MQTT out**: This node publishes the payload to the MQTT topic "ulanzi/custom/youtube" on a local MQTT broker. You also have to change the topic in this node to fit your mqtt prefix. +6. **Comment (Youtube Follower)**: This node contains additional information about the flow. It does not affect the flow's functionality. + +To use this flow, replace the "XXX" in the "Data" node with your YouTube API key and ensure that the MQTT broker settings in the "MQTT out" node are correct. +The flow will then retrieve the subscriber count of the specified YouTube channel and display it on your AWTRIX device along with the icon. +This Flow uses icon 5029 from LM (Just download it from the awtrix webinterface). You can change the icon in the flow to your favorite one. + + diff --git a/docs/icons.md b/docs/icons.md index c307b8b..95cc0b5 100644 --- a/docs/icons.md +++ b/docs/icons.md @@ -11,4 +11,4 @@ To download an icon, simply enter the ID of a LaMetric or AWTRIX 2.0 icon in the AWTRIX 2.0 icons can be found in the respective software. In a future update, non-AWTRIX 2.0 users will also get access to the database. You can also create your own icon and place it in the "ICONS" folder via the web interface file browser. -The icon needs to be a GIF or JPEG with a resolution of 8x8. \ No newline at end of file +The icon needs to be a GIF (.gif) or JPG (.jpg) with a resolution of 8x8. \ No newline at end of file diff --git a/docs/index.html b/docs/index.html index ba73f6a..e5632d2 100644 --- a/docs/index.html +++ b/docs/index.html @@ -20,11 +20,22 @@ coverpage: true, loadNavbar: true, mergeNavbar: true, - repo: 'https://github.com/Blueforcer/awtrix-light' + repo: 'https://github.com/Blueforcer/awtrix-light', + share: { + reddit: true, + linkedin: true, + facebook: true, + twitter: true, + whatsapp: true, + telegram: true, + } } + + + \ No newline at end of file diff --git a/lib/MatrixUI/MatrixDisplayUi.cpp b/lib/MatrixUI/MatrixDisplayUi.cpp index 1cd9739..473a81e 100644 --- a/lib/MatrixUI/MatrixDisplayUi.cpp +++ b/lib/MatrixUI/MatrixDisplayUi.cpp @@ -236,18 +236,6 @@ void MatrixDisplayUi::drawApp() int16_t x, y, x1, y1; switch (this->appAnimationDirection) { - case SLIDE_LEFT: - x = -32 * progress; - y = 0; - x1 = x + 32; - y1 = 0; - break; - case SLIDE_RIGHT: - x = 32 * progress; - y = 0; - x1 = x - 32; - y1 = 0; - break; case SLIDE_UP: x = 0; y = -8 * progress; diff --git a/lib/MatrixUI/MatrixDisplayUi.h b/lib/MatrixUI/MatrixDisplayUi.h index bb8a5fc..485060f 100644 --- a/lib/MatrixUI/MatrixDisplayUi.h +++ b/lib/MatrixUI/MatrixDisplayUi.h @@ -41,9 +41,7 @@ enum AnimationDirection { SLIDE_UP, - SLIDE_DOWN, - SLIDE_LEFT, - SLIDE_RIGHT + SLIDE_DOWN }; enum AppState @@ -79,7 +77,7 @@ private: FastLED_NeoMatrix *matrix; // Values for the Apps - AnimationDirection appAnimationDirection = SLIDE_RIGHT; + AnimationDirection appAnimationDirection = SLIDE_DOWN; int8_t lastTransitionDirection = 1; diff --git a/src/Dictionary.cpp b/src/Dictionary.cpp index d32ccc2..b90798c 100644 --- a/src/Dictionary.cpp +++ b/src/Dictionary.cpp @@ -65,6 +65,10 @@ const char HAluxUnit[] PROGMEM = {"lx"}; const char HAverID[] PROGMEM = {"%s_ver"}; const char HAverName[] PROGMEM = {"Version"}; +const char HAtransID[] PROGMEM = {"%s_tra"}; +const char HAtransName[] PROGMEM = {"Transition"}; +const char HAtransIcon[] PROGMEM = {"mdi:swap-vertical"}; + const char HAsigID[] PROGMEM = {"%s_sig"}; const char HAsigIcon[] PROGMEM = {"mdi:sun-wireless"}; const char HAsigName[] PROGMEM = {"WiFi strength"}; @@ -84,7 +88,7 @@ const char HAbtnMName[] PROGMEM = {"Button select"}; const char HAbtnRID[] PROGMEM = {"%s_btnR"}; const char HAbtnRName[] PROGMEM = {"Button right"}; -const char HAramRID[] PROGMEM = {"%s_btnR"}; +const char HAramRID[] PROGMEM = {"%s_ram"}; const char HAramIcon[] PROGMEM = {"mdi:application-cog"}; const char HAramName[] PROGMEM = {"Free ram"}; const char HAramClass[] PROGMEM = {"data_size"}; diff --git a/src/Dictionary.h b/src/Dictionary.h index 143a69b..467f053 100644 --- a/src/Dictionary.h +++ b/src/Dictionary.h @@ -75,6 +75,10 @@ extern const char HAupID[]; extern const char HAupName[]; extern const char HAupClass[]; +extern const char HAtransID[]; +extern const char HAtransName[]; +extern const char HAtransIcon[]; + extern const char HAbtnLID[]; extern const char HAbtnLName[]; diff --git a/src/DisplayManager.cpp b/src/DisplayManager.cpp index 1e54928..93e2092 100644 --- a/src/DisplayManager.cpp +++ b/src/DisplayManager.cpp @@ -73,6 +73,7 @@ void DisplayManager_::MatrixState(bool on) bool DisplayManager_::setAutoTransition(bool active) { + if (ui.AppCount < 2) { ui.disablesetAutoTransition(); @@ -315,7 +316,6 @@ void DisplayManager_::generateCustomPage(String name, String payload) String iconFileName = String(doc["icon"].as()); if (customApp.icon && String(customApp.icon.name()).startsWith(iconFileName)) { - } else { diff --git a/src/Globals.cpp b/src/Globals.cpp index e8ceea7..b86fc02 100644 --- a/src/Globals.cpp +++ b/src/Globals.cpp @@ -65,7 +65,7 @@ IPAddress gateway; IPAddress subnet; IPAddress primaryDNS; IPAddress secondaryDNS; -const char *VERSION = "0.42"; +const char *VERSION = "0.43"; String MQTT_HOST = ""; uint16_t MQTT_PORT = 1883; String MQTT_USER; diff --git a/src/MQTTManager.cpp b/src/MQTTManager.cpp index 1ce9665..3c81717 100644 --- a/src/MQTTManager.cpp +++ b/src/MQTTManager.cpp @@ -12,7 +12,7 @@ unsigned long startTime; WiFiClient espClient; uint8_t lastBrightness; HADevice device; -HAMqtt mqtt(espClient, device, 18); +HAMqtt mqtt(espClient, device, 19); unsigned long reconnectTimer = 0; const unsigned long reconnectInterval = 30000; // 30 Sekunden @@ -22,7 +22,7 @@ HASelect *BriMode = nullptr; HAButton *dismiss = nullptr; HAButton *nextApp = nullptr; HAButton *prevApp = nullptr; - +HASwitch *transition = nullptr; HASensor *curApp = nullptr; HASensor *battery = nullptr; HASensor *temperature = nullptr; @@ -62,6 +62,14 @@ void onButtonCommand(HAButton *sender) } } +void onSwitchCommand(bool state, HASwitch *sender) +{ + AUTO_TRANSITION = state; + DisplayManager.setAutoTransition(state); + saveSettings(); + sender->setState(state); +} + void onSelectCommand(int8_t index, HASelect *sender) { switch (index) @@ -215,7 +223,7 @@ void connect() } char matID[40], briID[40]; -char btnAID[40], btnBID[40], btnCID[40], appID[40], tempID[40], humID[40], luxID[40], verID[40], batID[40], ramID[40], upID[40], sigID[40], btnLID[40], btnMID[40], btnRID[40]; +char btnAID[40], btnBID[40], btnCID[40], appID[40], tempID[40], humID[40], luxID[40], verID[40], batID[40], ramID[40], upID[40], sigID[40], btnLID[40], btnMID[40], btnRID[40], transID[40]; void MQTTManager_::setup() { @@ -268,6 +276,17 @@ void MQTTManager_::setup() dismiss->setIcon(HAbtnaIcon); dismiss->setName(HAbtnaName); + sprintf(transID, HAtransID, macStr); + transition = new HASwitch(transID); + transition->setIcon(HAtransIcon); + transition->setName(HAtransName); + transition->onCommand(onSwitchCommand); + + sprintf(appID, HAappID, macStr); + curApp = new HASensor(appID); + curApp->setIcon(HAappIcon); + curApp->setName(HAappName); + sprintf(btnBID, HAbtnbID, macStr); nextApp = new HAButton(btnBID); nextApp->setIcon(HAbtnbIcon); @@ -282,11 +301,6 @@ void MQTTManager_::setup() nextApp->onCommand(onButtonCommand); prevApp->onCommand(onButtonCommand); - sprintf(appID, HAappID, macStr); - curApp = new HASensor(appID); - curApp->setIcon(HAappIcon); - curApp->setName(HAappName); - sprintf(tempID, HAtempID, macStr); temperature = new HASensor(tempID); temperature->setIcon(HAtempIcon); @@ -430,6 +444,10 @@ void MQTTManager_::sendStats() ram->setValue(rambuffer); uptime->setValue(readUptime()); version->setValue(VERSION); + transition->setState(AUTO_TRANSITION, false); + } + else + { } StaticJsonDocument<200> doc; diff --git a/src/PeripheryManager.cpp b/src/PeripheryManager.cpp index 544024c..f7a183a 100644 --- a/src/PeripheryManager.cpp +++ b/src/PeripheryManager.cpp @@ -225,7 +225,7 @@ void PeripheryManager_::checkAlarms() DeserializationError error = deserializeJson(doc, file); if (error) { - Serial.println("Failed to read Alarm file"); + Serial.println(F("Failed to read Alarm file")); return; } JsonArray alarms = doc["alarms"];