-Add update sensor to HA. AWL search for an update every hour.
- Fixes  duration in custom apps
This commit is contained in:
Stephan Mühl
2023-04-02 00:56:24 +02:00
parent 3c27f09287
commit b08e38f06c
12 changed files with 299 additions and 132 deletions

View File

@@ -6,6 +6,13 @@ In MQTT awtrix send its stats every 10s to `[PREFIX]/stats`
With HTTP, make GET request to `http://[IP]/api/stats`
## Update
Awtrix searches for an update every 1 Hour. If a new one is found it will be published to HA and in the stats.
You can start the update with update button in HA or:
| Topic | URL | Payload/Body | HTTP Header | HTTP method |
| --- | --- | --- |--- |--- |
| `[PREFIX]/doupdate` |`http://[IP]/api/doupdate` | JSON | empty payload/body | POST |
## Add custom app
create custom apps or notifications to display your own text and icons.
Have a look at [this section](custom?id=custom-apps-and-notifications)
@@ -64,10 +71,14 @@ This provides flexibility in organizing apps according to personal preference.
The JSON payload is an array of objects, where each object represents an app to be displayed on awtrix. Each app object contains the following fields:
`"name"`: The name of the app ("time", "date", "temp", "hum", "bat") are the native apps.
For custom apps, use the name you set in the topic. For example, if `[PREFIX]/custom/test` is your topic, then `test` is the name.
`"show"`: A boolean indicating whether the app should be shown on the screen or not. If not present, the app is considered active by default.
`"pos"`: An integer indicating the position of the app in the list. If not present, the app will be added to the end of the list.
### JSON Properties
| Property | Description |Default |
|----------|-------------|-------------|
| name | The name of the app. If it's a native app, it can be one of "time", "date", "temp", "hum", or "bat". For custom apps, use the name you set in the topic. For example, if `[PREFIX]/custom/test` is your topic, then `test` is the name. | |
| show | A boolean indicating whether the app should be shown on the screen or not. If not present, the app is considered active by default. | true |
| pos | An integer indicating the position of the app in the list. If not present, the app will be added to the end of the list. | Last Item |
> You can also just send the information for one app.
@@ -129,6 +140,8 @@ Change various settings related to the app display.
| --- | --- | --- |--- |
| `[PREFIX]/settings` |`http://[IP]/api/settings`| JSON | POST |
#### JSON Properties
Each property is optional; you do not need to send all.
| Key | Type | Description | Value Range | Default |
@@ -140,3 +153,37 @@ Each property is optional; you do not need to send all.
| `brightness` | number | Determines the brightness of the matrix. | An integer between 0 and 255. | N/A |
| `autobrightness` | boolean | Determines if automatic brightness control is active. | `true` or `false`. | N/A |
| `autotransition` | boolean | Determines if automatic switching to the next app is active. | `true` or `false`. | N/A |
## Timer
With AWTRIX Light, you can set a timer using MQTT. Simply send a JSON object to the topic **[PREFIX]/timer** to start a timer.
When the timer goes off, the display will show a notification, and you can dismiss the timer by pressing the middle button.
#### JSON Properties
The JSON object has the following properties:
| Key | Type | Description |
| --- | ---- | ----------- |
| `hours` | number | The number of hours after midnight when the timer should be triggered. |
| `minutes` | number | The number of minutes after the hour when the timer should be triggered. |
| `seconds` | number | The number of seconds after the minute when the timer should be triggered. |
| `sound` | string | The name of the sound file (without extension) to play when the timer is triggered. |
Each value is optional, so you can set a timer for just minutes, or any combination of hours, minutes, and seconds. If you only want to start a timer in some minutes, just send the minutes.
## Example
Here's an example JSON object to start a timer for 1 hour, 30 minutes, and 10 seconds, with the sound "friends":
```json
{
"hours": 1,
"minutes": 30,
"seconds": 10,
"sound": "friends"
}
```

View File

@@ -69,6 +69,17 @@ const char HAtransID[] PROGMEM = {"%s_tra"};
const char HAtransName[] PROGMEM = {"Transition"};
const char HAtransIcon[] PROGMEM = {"mdi:swap-vertical"};
const char HAupdateID[] PROGMEM = {"%s_upd"};
const char HAupdateName[] PROGMEM = {"Update"};
const char HAupdateClass[] PROGMEM = {"update"};
const char HAupdateIcon[] PROGMEM = {"mdi:update"};
const char HAdoUpID[] PROGMEM = {"%s_doupd"};
const char HAdoUpName[] PROGMEM = {"Start Update"};
const char HAdoUpIcon[] PROGMEM = {"mdi:update"};
const char HAsigID[] PROGMEM = {"%s_sig"};
const char HAsigIcon[] PROGMEM = {"mdi:sun-wireless"};
const char HAsigName[] PROGMEM = {"WiFi strength"};
@@ -104,3 +115,4 @@ const char TempKey[] PROGMEM = {"temp"};
const char HumKey[] PROGMEM = {"hum"};
const char UpTimeKey[] PROGMEM = {"uptime"};
const char SignalStrengthKey[] PROGMEM = {"wifi_signal"};
const char UpdateKey[] PROGMEM = {"up_available"};

View File

@@ -77,10 +77,19 @@ extern const char HAupID[];
extern const char HAupName[];
extern const char HAupClass[];
extern const char HAdoUpID[];
extern const char HAdoUpName[];
extern const char HAdoUpIcon[];
extern const char HAtransID[];
extern const char HAtransName[];
extern const char HAtransIcon[];
extern const char HAupdateID[];
extern const char HAupdateName[];
extern const char HAupdateClass[];
extern const char HAupdateIcon[];
extern const char HAbtnLID[];
extern const char HAbtnLName[];
@@ -106,5 +115,5 @@ extern const char TempKey[];
extern const char HumKey[];
extern const char UpTimeKey[];
extern const char SignalStrengthKey[];
extern const char UpdateKey[];
#endif

View File

@@ -293,7 +293,7 @@ void DisplayManager_::generateCustomPage(String name, const char *json)
customApp.barSize = 0;
}
customApp.duration = doc.containsKey("duration") ? doc["duration"].as<int>() * 1000 : -1;
customApp.duration = doc.containsKey("duration") ? doc["duration"].as<int>() * 1000 : 0;
int pos = doc.containsKey("pos") ? doc["pos"].as<uint8_t>() : -1;
customApp.rainbow = doc.containsKey("rainbow") ? doc["rainbow"] : false;
customApp.pushIcon = doc.containsKey("pushIcon") ? doc["pushIcon"] : 0;
@@ -514,7 +514,7 @@ void DisplayManager_::loadNativeApps()
void DisplayManager_::setup()
{
TJpgDec.setCallback(jpg_output);
FastLED.addLeds<NEOPIXEL, MATRIX_PIN>(leds, MATRIX_WIDTH * MATRIX_HEIGHT);
gif.setMatrix(&matrix);
@@ -891,8 +891,10 @@ String DisplayManager_::getStat()
{
StaticJsonDocument<200> doc;
char buffer[5];
#ifdef ULANZI
doc[BatKey] = BATTERY_PERCENT;
doc[BatRawKey] = BATTERY_RAW;
#endif
snprintf(buffer, 5, "%.0f", CURRENT_LUX);
doc[LuxKey] = buffer;
doc[LDRRawKey] = LDR_RAW;
@@ -903,6 +905,7 @@ String DisplayManager_::getStat()
doc[HumKey] = buffer;
doc[UpTimeKey] = PeripheryManager.readUptime();
doc[SignalStrengthKey] = WiFi.RSSI();
doc[UpdateKey] = UPDATE_AVAILABLE;
String jsonString;
serializeJson(doc, jsonString);
return jsonString;

View File

@@ -121,7 +121,7 @@ IPAddress gateway;
IPAddress subnet;
IPAddress primaryDNS;
IPAddress secondaryDNS;
const char *VERSION = "0.46";
const char *VERSION = "0.47";
String MQTT_HOST = "";
uint16_t MQTT_PORT = 1883;
String MQTT_USER;
@@ -187,4 +187,5 @@ bool SOUND_ACTIVE;
String BOOT_SOUND = "";
uint8_t VOLUME;
uint8_t VOLUME_PERCENT;
int MATRIX_LAYOUT;
int MATRIX_LAYOUT;
bool UPDATE_AVAILABLE = false;

View File

@@ -71,6 +71,7 @@ extern String BOOT_SOUND;
extern uint8_t VOLUME;
extern uint8_t VOLUME_PERCENT;
extern int MATRIX_LAYOUT;
extern bool UPDATE_AVAILABLE;
void loadSettings();
void saveSettings();
#endif // Globals_H

View File

@@ -7,6 +7,7 @@
#include <ArduinoJson.h>
#include "Dictionary.h"
#include "PeripheryManager.h"
#include "Updater.h"
WiFiClient espClient;
uint8_t lastBrightness;
@@ -36,6 +37,8 @@ HASensor *ram = nullptr;
HABinarySensor *btnleft = nullptr;
HABinarySensor *btnmid = nullptr;
HABinarySensor *btnright = nullptr;
HABinarySensor *update = nullptr;
HAButton *doUpdate = nullptr;
// The getter for the instantiated singleton instance
MQTTManager_ &MQTTManager_::getInstance()
@@ -61,6 +64,11 @@ void onButtonCommand(HAButton *sender)
{
DisplayManager.previousApp();
}
else if (sender == doUpdate)
{
if (UPDATE_AVAILABLE)
Updater.updateFirmware();
}
}
void onSwitchCommand(bool state, HASwitch *sender)
@@ -183,6 +191,12 @@ void onMqttMessage(const char *topic, const uint8_t *payload, uint16_t length)
DisplayManager.previousApp();
return;
}
if (strTopic == MQTT_PREFIX + "/doupdate")
{
if (UPDATE_AVAILABLE)
Updater.updateFirmware();
return;
}
else if (strTopic.startsWith(MQTT_PREFIX + "/custom"))
{
@@ -212,6 +226,7 @@ void onMqttConnected()
"/settings",
"/previousapp",
"/nextapp",
"/doupdate",
"/nextapp",
"/apps"};
for (const char *topic : topics)
@@ -222,7 +237,6 @@ void onMqttConnected()
Serial.println(F("MQTT Connected"));
}
void connect()
{
mqtt.onMessage(onMqttMessage);
@@ -241,7 +255,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], ramID[40], upID[40], sigID[40], btnLID[40], btnMID[40], btnRID[40], transID[40];
char btnAID[40], btnBID[40], btnCID[40], appID[40], tempID[40], humID[40], luxID[40], verID[40], ramID[40], upID[40], sigID[40], btnLID[40], btnMID[40], btnRID[40], transID[40], updateID[40], doUpdateID[40];
#ifdef ULANZI
char batID[40];
#endif
@@ -299,6 +313,12 @@ void MQTTManager_::setup()
dismiss->setIcon(HAbtnaIcon);
dismiss->setName(HAbtnaName);
sprintf(doUpdateID, HAdoUpID, macStr);
doUpdate = new HAButton(doUpdateID);
doUpdate->setIcon(HAdoUpIcon);
doUpdate->setName(HAdoUpName);
doUpdate->onCommand(onButtonCommand);
sprintf(transID, HAtransID, macStr);
transition = new HASwitch(transID);
transition->setIcon(HAtransIcon);
@@ -373,6 +393,12 @@ void MQTTManager_::setup()
btnleft = new HABinarySensor(btnLID);
btnleft->setName(HAbtnLName);
sprintf(updateID, HAupdateID, macStr);
update = new HABinarySensor(updateID);
update->setIcon(HAupdateIcon);
update->setName(HAupdateName);
update->setDeviceClass(HAupdateClass);
sprintf(btnMID, HAbtnMID, macStr);
btnmid = new HABinarySensor(btnMID);
btnmid->setName(HAbtnMName);
@@ -456,6 +482,8 @@ void MQTTManager_::sendStats()
uptime->setValue(PeripheryManager.readUptime());
version->setValue(VERSION);
transition->setState(AUTO_TRANSITION, false);
update->setState(UPDATE_AVAILABLE, false);
}
else
{

View File

@@ -6,6 +6,7 @@
#include <PeripheryManager.h>
#include <updater.h>
#include <icons.h>
#include <Updater.h>
String menuText;
int menuSelection;
@@ -377,9 +378,9 @@ void MenuManager_::selectButton()
#endif
case 13:
if (FirmwareVersionCheck())
if (Updater.checkUpdate(true))
{
updateFirmware();
Updater.updateFirmware();
}
break;
default:

View File

@@ -9,6 +9,7 @@
#include <LittleFS.h>
#include <WiFi.h>
#include "DisplayManager.h"
#include "Updater.h"
WebServer server(80);
FSWebServer mws(LittleFS, server);
@@ -37,7 +38,6 @@ void saveHandler()
webRequest->send(200);
}
void ServerManager_::setup()
{
if (!local_IP.fromString(NET_IP) || !gateway.fromString(NET_GW) || !subnet.fromString(NET_SN) || !primaryDNS.fromString(NET_PDNS) || !secondaryDNS.fromString(NET_SDNS))
@@ -53,7 +53,7 @@ void ServerManager_::setup()
if (isConnected)
{
mws.addOptionBox("Network");
mws.addOption("Static IP", NET_STATIC);
mws.addOption("Local IP", NET_IP);
@@ -97,6 +97,9 @@ void ServerManager_::setup()
{ DisplayManager.generateCustomPage(mws.webserver->arg("name"),mws.webserver->arg("plain").c_str()); mws.webserver->send(200,"OK"); });
mws.addHandler("/api/stats", HTTP_GET, []()
{ mws.webserver->sendContent(DisplayManager.getStat()); });
mws.addHandler("/api/doupdate", HTTP_POST, []()
{ if (UPDATE_AVAILABLE)
Updater.updateFirmware(); mws.webserver->send(200,"OK"); });
Serial.println("Webserver loaded");
}
mws.addHandler("/version", HTTP_GET, versionHandler);

152
src/Updater.cpp Normal file
View File

@@ -0,0 +1,152 @@
#include <updater.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <HTTPUpdate.h>
#include <WiFiClientSecure.h>
#include "cert.h"
#include "DisplayManager.h"
#include <Ticker.h>
#include "Globals.h"
#define URL_fw_Version "https://raw.githubusercontent.com/Blueforcer/awtrix-light/main/version"
#define URL_fw_Bin "https://raw.githubusercontent.com/Blueforcer/awtrix-light/main/docs/flasher/firmware/firmware.bin"
Ticker UpdateTicker;
// The getter for the instantiated singleton instance
Updater_ &Updater_::getInstance()
{
static Updater_ instance;
return instance;
}
// Initialize the global shared instance
Updater_ &Updater = Updater.getInstance();
void update_started()
{
}
void update_finished()
{
}
void update_progress(int cur, int total)
{
DisplayManager.drawProgressBar(cur, total);
}
void update_error(int err)
{
DisplayManager.clear();
DisplayManager.printText(0, 6, "FAIL", true, true);
DisplayManager.show();
}
void Updater_::updateFirmware()
{
WiFiClientSecure client;
client.setCACert(rootCACertificate);
httpUpdate.onStart(update_started);
httpUpdate.onEnd(update_finished);
httpUpdate.onProgress(update_progress);
httpUpdate.onError(update_error);
t_httpUpdate_return ret = httpUpdate.update(client, URL_fw_Bin);
switch (ret)
{
case HTTP_UPDATE_FAILED:
Serial.printf("HTTP_UPDATE_FAILD Error (%d): %s\n", httpUpdate.getLastError(), httpUpdate.getLastErrorString().c_str());
break;
case HTTP_UPDATE_NO_UPDATES:
Serial.println("HTTP_UPDATE_NO_UPDATES");
break;
case HTTP_UPDATE_OK:
Serial.println("HTTP_UPDATE_OK");
break;
}
}
bool Updater_::checkUpdate(bool withScreen)
{
if (withScreen)
{
DisplayManager.clear();
DisplayManager.printText(0, 6, "CHECK", true, true);
DisplayManager.show();
}
String payload;
int httpCode;
String fwurl = "";
fwurl += URL_fw_Version;
fwurl += "?";
fwurl += String(rand());
Serial.println(fwurl);
WiFiClientSecure *client = new WiFiClientSecure;
if (client)
{
client->setCACert(rootCACertificate);
HTTPClient https;
if (https.begin(*client, fwurl))
{ // HTTPS
Serial.print("[HTTPS] GET...\n");
// start connection and send HTTP header
delay(100);
httpCode = https.GET();
delay(100);
if (httpCode == HTTP_CODE_OK) // if version received
{
payload = https.getString(); // save received version
}
else
{
Serial.print("error in downloading version file:");
Serial.println(httpCode);
}
https.end();
}
delete client;
}
if (httpCode == HTTP_CODE_OK) // if version received
{
payload.trim();
if (payload.equals(VERSION))
{
UPDATE_AVAILABLE = false;
Serial.printf("\nDevice already on latest firmware version: %s\n", VERSION);
if (withScreen)
{
DisplayManager.clear();
DisplayManager.printText(0, 6, "NO UP :(", true, true);
DisplayManager.show();
delay(1000);
}
return 0;
}
else
{
UPDATE_AVAILABLE = true;
return 1;
}
}
UPDATE_AVAILABLE = false;
return 0;
}
void checkUpdateNoReturn()
{
Serial.println("Check Update");
Updater.getInstance().checkUpdate(false);
}
void Updater_::setup()
{
UpdateTicker.attach(3600, checkUpdateNoReturn);
}

View File

@@ -36,6 +36,7 @@
#include "MQTTManager.h"
#include "ServerManager.h"
#include "Globals.h"
#include "Updater.h"
TaskHandle_t taskHandle;
volatile bool StopTask = false;
@@ -58,7 +59,7 @@ void BootAnimation(void *parameter)
void setup()
{
PeripheryManager.setup();
delay(500);
delay(1000);
Serial.begin(9600);
loadSettings();
ServerManager.loadSettings();
@@ -73,6 +74,8 @@ void setup()
{
MQTTManager.setup();
DisplayManager.loadNativeApps();
Updater.setup();
Updater.checkUpdate(false);
StopTask = true;
float x = 4;
while (x >= -85)
@@ -80,6 +83,7 @@ void setup()
DisplayManager.HSVtext(x, 6, ("AWTRIX " + ServerManager.myIP.toString()).c_str(), true);
x -= 0.18;
}
}
else
{
@@ -98,7 +102,6 @@ void loop()
PeripheryManager.tick();
if (ServerManager.isConnected)
{
MQTTManager.tick();
}
}

View File

@@ -1,122 +1,29 @@
#include <WiFi.h>
#include <HTTPClient.h>
#include <HTTPUpdate.h>
#include <WiFiClientSecure.h>
#include "cert.h"
#include "DisplayManager.h"
#ifndef UPDATER_h
#define UPDATER_h
#define URL_fw_Version "https://raw.githubusercontent.com/Blueforcer/awtrix-light/main/version"
#define URL_fw_Bin "https://raw.githubusercontent.com/Blueforcer/awtrix-light/main/docs/flasher/firmware/firmware.bin"
#include <Arduino.h>
#include <EasyButton.h>
#ifdef ULANZI
#include "Adafruit_SHT31.h"
#else
#include "Adafruit_BME280.h"
#include "SoftwareSerial.h"
#include <DFMiniMp3.h>
#endif
void update_started()
class Updater_
{
}
void update_finished()
{
}
void update_progress(int cur, int total)
{
DisplayManager.drawProgressBar(cur, total);
}
void update_error(int err)
{
DisplayManager.clear();
DisplayManager.printText(0, 6, "FAIL", true, true);
DisplayManager.show();
}
void updateFirmware()
{
WiFiClientSecure client;
client.setCACert(rootCACertificate);
httpUpdate.onStart(update_started);
httpUpdate.onEnd(update_finished);
httpUpdate.onProgress(update_progress);
httpUpdate.onError(update_error);
t_httpUpdate_return ret = httpUpdate.update(client, URL_fw_Bin);
switch (ret)
{
case HTTP_UPDATE_FAILED:
Serial.printf("HTTP_UPDATE_FAILD Error (%d): %s\n", httpUpdate.getLastError(), httpUpdate.getLastErrorString().c_str());
break;
case HTTP_UPDATE_NO_UPDATES:
Serial.println("HTTP_UPDATE_NO_UPDATES");
break;
case HTTP_UPDATE_OK:
Serial.println("HTTP_UPDATE_OK");
break;
}
}
private:
Updater_() = default;
bool FirmwareVersionCheck()
{
DisplayManager.clear();
DisplayManager.printText(0, 6, "CHECK", true, true);
DisplayManager.show();
String payload;
int httpCode;
String fwurl = "";
fwurl += URL_fw_Version;
fwurl += "?";
fwurl += String(rand());
Serial.println(fwurl);
WiFiClientSecure *client = new WiFiClientSecure;
public:
static Updater_ &getInstance();
void setup();
bool checkUpdate(bool);
void updateFirmware();
};
if (client)
{
client->setCACert(rootCACertificate);
HTTPClient https;
if (https.begin(*client, fwurl))
{ // HTTPS
Serial.print("[HTTPS] GET...\n");
// start connection and send HTTP header
delay(100);
httpCode = https.GET();
delay(100);
if (httpCode == HTTP_CODE_OK) // if version received
{
payload = https.getString(); // save received version
}
else
{
Serial.print("error in downloading version file:");
Serial.println(httpCode);
}
https.end();
}
delete client;
}
if (httpCode == HTTP_CODE_OK) // if version received
{
payload.trim();
if (payload.equals(VERSION))
{
Serial.printf("\nDevice already on latest firmware version:%s\n", VERSION);
DisplayManager.clear();
DisplayManager.printText(0, 6, "NO UP :(", true, true);
DisplayManager.show();
delay(1000);
return 0;
}
else
{
Serial.println(payload);
Serial.println("New firmware detected");
DisplayManager.printText(0, 6, payload.c_str(), true, true);
return 1;
}
}
return 0;
}
extern Updater_ &Updater;
#endif