Merge pull request #49 from Elfish/main
awtrix_upgrade fix + MQTT onstate added + import missing commits from main branch
This commit is contained in:
13
README.md
13
README.md
@@ -16,11 +16,12 @@
|
|||||||

|

|
||||||
[](https://github.com/Blueforcer/awtrix-light/actions/workflows/main.yml)
|
[](https://github.com/Blueforcer/awtrix-light/actions/workflows/main.yml)
|
||||||
|
|
||||||
|
<a href="http://www.youtube.com/watch?feature=player_embedded&v=N0NKPJzGHuA" target="_blank">
|
||||||
|
<img src="http://img.youtube.com/vi/N0NKPJzGHuA/mqdefault.jpg" alt="Watch the video" height="280" border="10" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||

|
AWTRIX Light is a open‑source custom firmware for the [Ulanzi Smart Pixel clock TC001](https://www.ulanzi.com/products/ulanzi-pixel-smart-clock-2882)
|
||||||
</div>
|
|
||||||
|
|
||||||
AWTRIX Light is an open‑source custom firmware for the [Ulanzi Smart Pixel clock TC001](https://www.ulanzi.com/products/ulanzi-pixel-smart-clock-2882)
|
|
||||||
|
|
||||||
AWL meant to be a companion for your smarthome like HomeAssistant, IOBroker, NodeRed and so on.
|
AWL meant to be a companion for your smarthome like HomeAssistant, IOBroker, NodeRed and so on.
|
||||||
Even if you don't have a Smarthome system, but would like to experiment with NodeRed or N8N, you are still welcome to join us.
|
Even if you don't have a Smarthome system, but would like to experiment with NodeRed or N8N, you are still welcome to join us.
|
||||||
@@ -40,7 +41,9 @@ Join the thousands of satisfied awtrix users who have already chosen Awtrix 2 an
|
|||||||
- Onscreen menu where you can change your settings directly on the device
|
- Onscreen menu where you can change your settings directly on the device
|
||||||
- Pre-installed Apps like time, date, temperature, humidity and battery
|
- Pre-installed Apps like time, date, temperature, humidity and battery
|
||||||
- Add customapps without recompiling straight from your Smarthome.
|
- Add customapps without recompiling straight from your Smarthome.
|
||||||
- Noitification support
|
- Notification support
|
||||||
|
- Animated icons
|
||||||
|
- Custom icons without recompiling
|
||||||
- Easy to use icon system
|
- Easy to use icon system
|
||||||
- Powerful MQTT commands
|
- Powerful MQTT commands
|
||||||
- HTTP API
|
- HTTP API
|
||||||
|
|||||||
12
docs/api.md
12
docs/api.md
@@ -43,7 +43,7 @@ The JSON object has the following properties:
|
|||||||
| `sound` | string | The filename of your RTTTL ringtone file (without extension). | |
|
| `sound` | string | The filename of your RTTTL ringtone file (without extension). | |
|
||||||
| `pushIcon` | number | 0 = Icon doesn't move. 1 = Icon moves with text and will not appear again. 2 = Icon moves with text but appears again when the text starts to scroll again. | 0 |
|
| `pushIcon` | number | 0 = Icon doesn't move. 1 = Icon moves with text and will not appear again. 2 = Icon moves with text but appears again when the text starts to scroll again. | 0 |
|
||||||
| `bar` | array of integers | draws a bargraph. Without icon maximum 16 values, with icon 11 values | |
|
| `bar` | array of integers | draws a bargraph. Without icon maximum 16 values, with icon 11 values | |
|
||||||
| `textCase` | integer | Changes teh Uppercase setting. 0=global setting, 1=forces uppercase; 2=shows what is sent. | 0 |
|
| `textCase` | integer | Changes the Uppercase setting. 0=global setting, 1=forces uppercase; 2=shows what is sent. | 0 |
|
||||||
|
|
||||||
|
|
||||||
All keys are optional, so you can send just the properties you want to use.
|
All keys are optional, so you can send just the properties you want to use.
|
||||||
@@ -222,7 +222,7 @@ The JSON object has the following properties:
|
|||||||
|
|
||||||
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.
|
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
|
#### Example
|
||||||
|
|
||||||
Here's an example JSON object to start a timer for 1 hour, 30 minutes, and 10 seconds, with the sound "friends":
|
Here's an example JSON object to start a timer for 1 hour, 30 minutes, and 10 seconds, with the sound "friends":
|
||||||
|
|
||||||
@@ -235,3 +235,11 @@ Here's an example JSON object to start a timer for 1 hour, 30 minutes, and 10 se
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Turn display on or off
|
||||||
|
| Topic | URL | Payload/Body | HTTP method |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `[PREFIX]/onstate` | `http://[IP]/api/onstate` | `{"state":value}` | POST |
|
||||||
|
|
||||||
|
Valid values are:
|
||||||
|
- `0` => off
|
||||||
|
- `1` => on
|
||||||
|
|||||||
@@ -2,6 +2,6 @@
|
|||||||
# Online flasher
|
# Online flasher
|
||||||
|
|
||||||
Available in Google Chrome and Microsoft Edge browsers.
|
Available in Google Chrome and Microsoft Edge browsers.
|
||||||
If you falsh your Ulanzi Clock the first time you need to check "erase".
|
If you flash your Ulanzi Clock the first time you need to check "erase".
|
||||||
|
|
||||||
[filename](flasher/index.html ':include :type=iframe')
|
[filename](flasher/index.html ':include :type=iframe')
|
||||||
|
|||||||
Binary file not shown.
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "AWTRIX Light",
|
"name": "AWTRIX Light",
|
||||||
"version": "0.47",
|
"version": "0.48",
|
||||||
"home_assistant_domain": "AwtrixLight",
|
"home_assistant_domain": "AwtrixLight",
|
||||||
"funding_url": "https://blueforcer.de",
|
"funding_url": "https://blueforcer.de",
|
||||||
"new_install_prompt_erase": true,
|
"new_install_prompt_erase": true,
|
||||||
|
|||||||
@@ -75,7 +75,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<script type="module" src="https://unpkg.com/esp-web-tools@8.0.1/dist/web/install-button.js?module"></script>
|
<script type="module" src="https://esm.run/esp-web-tools@8.0.1/dist/web/install-button.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ CRGB leds[MATRIX_WIDTH * MATRIX_HEIGHT];
|
|||||||
FastLED_NeoMatrix *matrix = new FastLED_NeoMatrix(leds, 8, 8, 4, 1, NEO_MATRIX_TOP + NEO_MATRIX_LEFT + NEO_MATRIX_ROWS + NEO_MATRIX_PROGRESSIVE);
|
FastLED_NeoMatrix *matrix = new FastLED_NeoMatrix(leds, 8, 8, 4, 1, NEO_MATRIX_TOP + NEO_MATRIX_LEFT + NEO_MATRIX_ROWS + NEO_MATRIX_PROGRESSIVE);
|
||||||
MatrixDisplayUi *ui = new MatrixDisplayUi(matrix);
|
MatrixDisplayUi *ui = new MatrixDisplayUi(matrix);
|
||||||
|
|
||||||
|
uint8_t lastBrightness;
|
||||||
|
|
||||||
DisplayManager_ &DisplayManager_::getInstance()
|
DisplayManager_ &DisplayManager_::getInstance()
|
||||||
{
|
{
|
||||||
static DisplayManager_ instance;
|
static DisplayManager_ instance;
|
||||||
@@ -421,7 +423,7 @@ void DisplayManager_::generateNotification(const char *json)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
notify.barSize = 0;
|
notify.barSize = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (doc.containsKey("color"))
|
if (doc.containsKey("color"))
|
||||||
{
|
{
|
||||||
@@ -473,7 +475,7 @@ void DisplayManager_::generateNotification(const char *json)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fs::File nullPointer;
|
fs::File nullPointer;
|
||||||
notify.icon = nullPointer;
|
notify.icon = nullPointer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -802,10 +804,12 @@ std::pair<String, AppCallback> getNativeAppByName(const String &appName)
|
|||||||
{
|
{
|
||||||
return std::make_pair("hum", HumApp);
|
return std::make_pair("hum", HumApp);
|
||||||
}
|
}
|
||||||
|
#ifdef ULANZI
|
||||||
else if (appName == "bat")
|
else if (appName == "bat")
|
||||||
{
|
{
|
||||||
return std::make_pair("bat", BatApp);
|
return std::make_pair("bat", BatApp);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
return std::make_pair("", nullptr);
|
return std::make_pair("", nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -944,6 +948,32 @@ String DisplayManager_::getAppsAsJson()
|
|||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DisplayManager_::onStateParse(const char *json)
|
||||||
|
{
|
||||||
|
DynamicJsonDocument doc(512);
|
||||||
|
DeserializationError error = deserializeJson(doc, json);
|
||||||
|
if (error)
|
||||||
|
return;
|
||||||
|
bool state = doc["state"].as<bool>();
|
||||||
|
onState(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DisplayManager_::onState(bool state)
|
||||||
|
{
|
||||||
|
if (state)
|
||||||
|
{
|
||||||
|
MATRIX_OFF = false;
|
||||||
|
setBrightness(lastBrightness);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MATRIX_OFF = true;
|
||||||
|
lastBrightness = BRIGHTNESS;
|
||||||
|
setBrightness(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void DisplayManager_::setIndicator1(bool state, uint16_t color)
|
void DisplayManager_::setIndicator1(bool state, uint16_t color)
|
||||||
{
|
{
|
||||||
ui->setIndicator1(state, color);
|
ui->setIndicator1(state, color);
|
||||||
@@ -953,3 +983,4 @@ void DisplayManager_::setIndicator2(bool state, uint16_t color)
|
|||||||
{
|
{
|
||||||
ui->setIndicator2(state, color);
|
ui->setIndicator2(state, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -66,8 +66,13 @@ public:
|
|||||||
void setAppTime(uint16_t duration);
|
void setAppTime(uint16_t duration);
|
||||||
String getAppsAsJson();
|
String getAppsAsJson();
|
||||||
String getStat();
|
String getStat();
|
||||||
|
|
||||||
|
void onState(bool state);
|
||||||
|
void onStateParse(const char *json);
|
||||||
|
|
||||||
void setIndicator1(bool state, uint16_t color);
|
void setIndicator1(bool state, uint16_t color);
|
||||||
void setIndicator2(bool state, uint16_t color);
|
void setIndicator2(bool state, uint16_t color);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern DisplayManager_ &DisplayManager;
|
extern DisplayManager_ &DisplayManager;
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
#include "UpdateManager.h"
|
#include "UpdateManager.h"
|
||||||
|
|
||||||
WiFiClient espClient;
|
WiFiClient espClient;
|
||||||
uint8_t lastBrightness;
|
|
||||||
HADevice device;
|
HADevice device;
|
||||||
HAMqtt mqtt(espClient, device, 25);
|
HAMqtt mqtt(espClient, device, 25);
|
||||||
|
|
||||||
@@ -123,17 +122,7 @@ void onStateCommand(bool state, HALight *sender)
|
|||||||
{
|
{
|
||||||
if (sender == Matrix)
|
if (sender == Matrix)
|
||||||
{
|
{
|
||||||
if (state)
|
DisplayManager.onState(state);
|
||||||
{
|
|
||||||
MATRIX_OFF = false;
|
|
||||||
DisplayManager.setBrightness(lastBrightness);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
MATRIX_OFF = true;
|
|
||||||
lastBrightness = BRIGHTNESS;
|
|
||||||
DisplayManager.setBrightness(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (sender == Indikator1)
|
else if (sender == Indikator1)
|
||||||
{
|
{
|
||||||
@@ -143,7 +132,6 @@ void onStateCommand(bool state, HALight *sender)
|
|||||||
{
|
{
|
||||||
DisplayManager.setIndicator2(state, 0);
|
DisplayManager.setIndicator2(state, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
sender->setState(state);
|
sender->setState(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,7 +141,6 @@ void onBrightnessCommand(uint8_t brightness, HALight *sender)
|
|||||||
if (AUTO_BRIGHTNESS)
|
if (AUTO_BRIGHTNESS)
|
||||||
return;
|
return;
|
||||||
BRIGHTNESS = brightness;
|
BRIGHTNESS = brightness;
|
||||||
lastBrightness = brightness;
|
|
||||||
saveSettings();
|
saveSettings();
|
||||||
DisplayManager.setBrightness(brightness);
|
DisplayManager.setBrightness(brightness);
|
||||||
}
|
}
|
||||||
@@ -232,7 +219,13 @@ void onMqttMessage(const char *topic, const uint8_t *payload, uint16_t length)
|
|||||||
delete[] payloadCopy;
|
delete[] payloadCopy;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (strTopic.equals(MQTT_PREFIX + "/onstate"))
|
||||||
|
{
|
||||||
|
DisplayManager.onStateParse(payloadCopy);
|
||||||
|
Serial.println(payloadCopy);
|
||||||
|
delete[] payloadCopy;
|
||||||
|
return;
|
||||||
|
}
|
||||||
else if (strTopic.startsWith(MQTT_PREFIX + "/custom"))
|
else if (strTopic.startsWith(MQTT_PREFIX + "/custom"))
|
||||||
{
|
{
|
||||||
String topic_str = topic;
|
String topic_str = topic;
|
||||||
@@ -263,7 +256,8 @@ void onMqttConnected()
|
|||||||
"/nextapp",
|
"/nextapp",
|
||||||
"/doupdate",
|
"/doupdate",
|
||||||
"/nextapp",
|
"/nextapp",
|
||||||
"/apps"};
|
"/apps",
|
||||||
|
"/onstate"};
|
||||||
for (const char *topic : topics)
|
for (const char *topic : topics)
|
||||||
{
|
{
|
||||||
String fullTopic = prefix + topic;
|
String fullTopic = prefix + topic;
|
||||||
@@ -297,7 +291,6 @@ char batID[40];
|
|||||||
|
|
||||||
void MQTTManager_::setup()
|
void MQTTManager_::setup()
|
||||||
{
|
{
|
||||||
|
|
||||||
if (HA_DISCOVERY)
|
if (HA_DISCOVERY)
|
||||||
{
|
{
|
||||||
Serial.println(F("Starting Homeassistant discorvery"));
|
Serial.println(F("Starting Homeassistant discorvery"));
|
||||||
@@ -406,8 +399,8 @@ void MQTTManager_::setup()
|
|||||||
humidity->setName(HAhumName);
|
humidity->setName(HAhumName);
|
||||||
humidity->setDeviceClass(HAhumClass);
|
humidity->setDeviceClass(HAhumClass);
|
||||||
humidity->setUnitOfMeasurement(HAhumUnit);
|
humidity->setUnitOfMeasurement(HAhumUnit);
|
||||||
#ifdef ULANZI
|
|
||||||
|
|
||||||
|
#ifdef ULANZI
|
||||||
sprintf(batID, HAbatID, macStr);
|
sprintf(batID, HAbatID, macStr);
|
||||||
battery = new HASensor(batID);
|
battery = new HASensor(batID);
|
||||||
battery->setIcon(HAbatIcon);
|
battery->setIcon(HAbatIcon);
|
||||||
|
|||||||
@@ -96,7 +96,6 @@ void left_button_pressed()
|
|||||||
#ifndef ULANZI
|
#ifndef ULANZI
|
||||||
PeripheryManager.playFromFile(DFMINI_MP3_CLICK);
|
PeripheryManager.playFromFile(DFMINI_MP3_CLICK);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
DisplayManager.leftButton();
|
DisplayManager.leftButton();
|
||||||
MenuManager.leftButton();
|
MenuManager.leftButton();
|
||||||
}
|
}
|
||||||
@@ -106,7 +105,6 @@ void right_button_pressed()
|
|||||||
#ifndef ULANZI
|
#ifndef ULANZI
|
||||||
PeripheryManager.playFromFile(DFMINI_MP3_CLICK);
|
PeripheryManager.playFromFile(DFMINI_MP3_CLICK);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
DisplayManager.rightButton();
|
DisplayManager.rightButton();
|
||||||
MenuManager.rightButton();
|
MenuManager.rightButton();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,6 +102,8 @@ void ServerManager_::setup()
|
|||||||
mws.addHandler("/api/doupdate", HTTP_POST, []()
|
mws.addHandler("/api/doupdate", HTTP_POST, []()
|
||||||
{ if (UPDATE_AVAILABLE)
|
{ if (UPDATE_AVAILABLE)
|
||||||
UpdateManager.updateFirmware(); mws.webserver->send(200,"OK"); });
|
UpdateManager.updateFirmware(); mws.webserver->send(200,"OK"); });
|
||||||
|
mws.addHandler("/api/onstate", HTTP_POST, []()
|
||||||
|
{ DisplayManager.onStateParse(mws.webserver->arg("plain").c_str()); mws.webserver->send(200,"OK"); });
|
||||||
Serial.println("Webserver loaded");
|
Serial.println("Webserver loaded");
|
||||||
}
|
}
|
||||||
mws.addHandler("/version", HTTP_GET, versionHandler);
|
mws.addHandler("/version", HTTP_GET, versionHandler);
|
||||||
|
|||||||
Reference in New Issue
Block a user