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] 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"];