- Adds notification indicators
- Adds the possibility to modify the [color correction](https://blueforcer.github.io/awtrix-light/#/dev) 
- Adds the possibility to turn the Matrix on and off via MQTT or HTTP
This commit is contained in:
Stephan Mühl
2023-04-10 18:32:23 +02:00
committed by GitHub
15 changed files with 433 additions and 52 deletions

View File

@@ -1,25 +1,41 @@
# MQTT / HTTP API # MQTT / HTTP API
## Status ## Status
In MQTT awtrix send its stats every 10s to `[PREFIX]/stats` In MQTT awtrix send its stats every 10s to `[PREFIX]/stats`
With HTTP, make GET request to `http://[IP]/api/stats` With HTTP, make GET request to `http://[IP]/api/stats`
## Update ## Turn display on or off
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 | | Topic | URL | Payload/Body | HTTP method |
| --- | --- | --- | --- | --- | | --- | --- | --- | --- |
| `[PREFIX]/doupdate` |`http://[IP]/api/doupdate` | JSON | empty payload/body | POST | | `[PREFIX]/power` | `http://[IP]/api/power` | `{"state":value}` | POST |
Valid values are:
- `0` => off
- `1` => on
## Colored indicators
A colored indicator is like a small notification sign wich will be shown on the upper right or lower right corner.
| Topic | URL | Payload/Body | HTTP method |
| --- | --- | --- | --- |
| `[PREFIX]/indicator1` | `http://[IP]/api/indicator1` | `{"color":[255,0,0]}` | POST |
| `[PREFIX]/indicator2` | `http://[IP]/api/indicator2` | `{"color":[0,255,0]}` | POST |
Instead of a RGB array you can also sent HEX color strings like `{"color":"#32a852"}`
Send the color black `{"color":[0,0,0]}` or `{"color":"0"}` to hide the indicators.
Optionally you can make the indicator blinking by adding the key `"blink":true/false`.
## Custom Apps and Notifications ## Custom Apps and Notifications
With AWTRIX Light, you can create custom apps or notifications to display your own text and icons. With AWTRIX Light, you can create custom apps or notifications to display your own text and icons.
With MQTT simply send a JSON object to the topic `[PREFIX]/custom/[page]` where [page] is a the name of your page (without spaces). With MQTT simply send a JSON object to the topic `[PREFIX]/custom/[app]` where [app] is a the name of your app (without spaces).
With the [HTTP API](api?id=add-custom-app) you have to set the appname in the request header (`name = Appname`) With the HTTP API you have to set the appname in the request header (`name = [appname]`)
You can also send a one-time notification with the same JSON format. Simply send your JSON object to `[PREFIX]/notify` or `http://[IP]/api/notify`.
| Topic | URL | Payload/Body | Query parameters | HTTP method | | Topic | URL | Payload/Body | Query parameters | HTTP method |
| --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- |
@@ -43,14 +59,14 @@ 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 as it 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.
To update a custom page, simply send a modified JSON object to the same topic. The display will be updated immediately. To update a custom page, simply send a modified JSON object to the same topic. The display will be updated immediately.
You can also send a one-time notification with the same JSON format. Simply send your JSON object to "awtrixlight/notify".
### Example ### Example
@@ -222,7 +238,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 +251,10 @@ Here's an example JSON object to start a timer for 1 hour, 30 minutes, and 10 se
} }
``` ```
## 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 |

View File

@@ -12,5 +12,6 @@ The JSON object has the following properties:
| Key | Type | Description | Default | | Key | Type | Description | Default |
| --- | ---- | ----------- | ------- | | --- | ---- | ----------- | ------- |
| `bootsound` | string | Uses a custom melodie while booting | | | `bootsound` | string | Uses a custom melodie while booting | |
| `uppercase` | boolean | Print every character in uppercase | true | | `uppercase` | boolean | Print every character in uppercase | `true` |
| `temp_dec_places` | int | Number of decimal places for temperature measurements | 0 | | `temp_dec_places` | int | Number of decimal places for temperature measurements | `0` |
| `color_correction` | array of int | Sets the colorcorrection of the matrix | `[255,255,255]` |

View File

@@ -223,12 +223,44 @@ void MatrixDisplayUi::tick()
} }
this->matrix->clear(); this->matrix->clear();
if (this->AppCount > 0) if (this->AppCount > 0)
this->drawApp(); this->drawApp();
this->drawOverlays(); this->drawOverlays();
this->drawIndicators();
this->matrix->show(); this->matrix->show();
} }
void MatrixDisplayUi::drawIndicators()
{
if (indicator1State && !indicator1Blink)
{
matrix->drawPixel(31, 0, indicator1Color);
matrix->drawPixel(30, 0, indicator1Color);
matrix->drawPixel(31, 1, indicator1Color);
}
if (indicator2State && !indicator2Blink)
{
matrix->drawPixel(31, 7, indicator2Color);
matrix->drawPixel(31, 6, indicator2Color);
matrix->drawPixel(30, 7, indicator2Color);
}
if (indicator1State && indicator1Blink && (millis() % 1000) < 500)
{
matrix->drawPixel(31, 0, indicator1Color);
matrix->drawPixel(30, 0, indicator1Color);
matrix->drawPixel(31, 1, indicator1Color);
}
if (indicator2State && indicator2Blink && (millis() % 1000) < 500)
{
matrix->drawPixel(31, 7, indicator2Color);
matrix->drawPixel(31, 6, indicator2Color);
matrix->drawPixel(30, 7, indicator2Color);
}
}
void MatrixDisplayUi::drawApp() void MatrixDisplayUi::drawApp()
{ {
switch (this->state.appState) switch (this->state.appState)
@@ -293,3 +325,33 @@ uint8_t MatrixDisplayUi::getnextAppNumber()
return this->nextAppNumber; return this->nextAppNumber;
return (this->state.currentApp + this->AppCount + this->state.appTransitionDirection) % this->AppCount; return (this->state.currentApp + this->AppCount + this->state.appTransitionDirection) % this->AppCount;
} }
void MatrixDisplayUi::setIndicator1Color(uint16_t color)
{
this->indicator1Color = color;
}
void MatrixDisplayUi::setIndicator1State(bool state)
{
this->indicator1State = state;
}
void MatrixDisplayUi::setIndicator2Color(uint16_t color)
{
this->indicator2Color = color;
}
void MatrixDisplayUi::setIndicator2State(bool state)
{
this->indicator2State = state;
}
void MatrixDisplayUi::setIndicator1Blink(bool blink)
{
this->indicator1Blink = blink;
}
void MatrixDisplayUi::setIndicator2Blink(bool blink)
{
this->indicator2Blink = blink;
}

View File

@@ -77,7 +77,6 @@ class MatrixDisplayUi
private: private:
FastLED_NeoMatrix *matrix; FastLED_NeoMatrix *matrix;
// Values for the Apps // Values for the Apps
AnimationDirection appAnimationDirection = SLIDE_DOWN; AnimationDirection appAnimationDirection = SLIDE_DOWN;
int8_t lastTransitionDirection = 1; int8_t lastTransitionDirection = 1;
@@ -102,6 +101,14 @@ private:
// Bookeeping for update // Bookeeping for update
uint8_t updateInterval = 33; uint8_t updateInterval = 33;
uint16_t indicator1Color = 63488;
uint16_t indicator2Color = 31;
bool indicator1State = false;
bool indicator2State = false;
bool indicator1Blink = false;
bool indicator2Blink = false;
uint8_t getnextAppNumber(); uint8_t getnextAppNumber();
void drawApp(); void drawApp();
void drawOverlays(); void drawOverlays();
@@ -148,6 +155,15 @@ public:
*/ */
void setTimePerTransition(uint16_t time); void setTimePerTransition(uint16_t time);
void setIndicator1Color(uint16_t color);
void setIndicator1State(bool state);
void setIndicator2Color(uint16_t color);
void setIndicator2State(bool state);
void setIndicator1Blink(bool Blink);
void setIndicator2Blink(bool Blink);
void drawIndicators();
// Customize indicator position and style // Customize indicator position and style
// App settings // App settings

View File

@@ -17,6 +17,15 @@ const char HAmodel[] PROGMEM = {"AWTRIX Light"};
const char HAmatID[] PROGMEM = {"%s_mat"}; const char HAmatID[] PROGMEM = {"%s_mat"};
const char HAmatIcon[] PROGMEM = {"mdi:lightbulb"}; const char HAmatIcon[] PROGMEM = {"mdi:lightbulb"};
const char HAmatName[] PROGMEM = {"Matrix"}; const char HAmatName[] PROGMEM = {"Matrix"};
const char HAi1ID[] PROGMEM = {"%s_ind1"};
const char HAi1Icon[] PROGMEM = {"mdi:arrow-top-right-thick"};
const char HAi1Name[] PROGMEM = {"Indicator 1"};
const char HAi2ID[] PROGMEM = {"%s_ind2"};
const char HAi2Icon[] PROGMEM = {"mdi:arrow-bottom-right-thick"};
const char HAi2Name[] PROGMEM = {"Indicator 2"};
const char HAbriID[] PROGMEM = {"%s_bri"}; const char HAbriID[] PROGMEM = {"%s_bri"};
const char HAbriIcon[] PROGMEM = {"mdi:brightness-auto"}; const char HAbriIcon[] PROGMEM = {"mdi:brightness-auto"};
const char HAbriName[] PROGMEM = {"Brightness mode"}; const char HAbriName[] PROGMEM = {"Brightness mode"};
@@ -74,12 +83,10 @@ const char HAupdateName[] PROGMEM = {"Update"};
const char HAupdateClass[] PROGMEM = {"update"}; const char HAupdateClass[] PROGMEM = {"update"};
const char HAupdateIcon[] PROGMEM = {"mdi:update"}; const char HAupdateIcon[] PROGMEM = {"mdi:update"};
const char HAdoUpID[] PROGMEM = {"%s_doupd"}; const char HAdoUpID[] PROGMEM = {"%s_doupd"};
const char HAdoUpName[] PROGMEM = {"Start Update"}; const char HAdoUpName[] PROGMEM = {"Start Update"};
const char HAdoUpIcon[] PROGMEM = {"mdi:update"}; const char HAdoUpIcon[] PROGMEM = {"mdi:update"};
const char HAsigID[] PROGMEM = {"%s_sig"}; const char HAsigID[] PROGMEM = {"%s_sig"};
const char HAsigIcon[] PROGMEM = {"mdi:sun-wireless"}; const char HAsigIcon[] PROGMEM = {"mdi:sun-wireless"};
const char HAsigName[] PROGMEM = {"WiFi strength"}; const char HAsigName[] PROGMEM = {"WiFi strength"};

View File

@@ -17,6 +17,15 @@ extern const char HAmodel[];
extern const char HAmatID[]; extern const char HAmatID[];
extern const char HAmatIcon[]; extern const char HAmatIcon[];
extern const char HAmatName[]; extern const char HAmatName[];
extern const char HAi1ID[];
extern const char HAi1Icon[];
extern const char HAi1Name[];
extern const char HAi2ID[];
extern const char HAi2Icon[];
extern const char HAi2Name[];
extern const char HAbriID[]; extern const char HAbriID[];
extern const char HAbriIcon[]; extern const char HAbriIcon[];
extern const char HAbriName[]; extern const char HAbriName[];

View File

@@ -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;
@@ -54,6 +56,11 @@ void DisplayManager_::setBrightness(uint8_t bri)
else else
{ {
matrix->setBrightness(bri); matrix->setBrightness(bri);
if (GAMMA > 0)
{
Serial.println(GAMMA);
napplyGamma_video(&leds[256], 256, GAMMA);
}
} }
} }
@@ -140,6 +147,7 @@ bool jpg_output(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t *bitmap)
void DisplayManager_::printText(int16_t x, int16_t y, const char *text, bool centered, byte textCase) void DisplayManager_::printText(int16_t x, int16_t y, const char *text, bool centered, byte textCase)
{ {
if (centered) if (centered)
{ {
uint16_t textWidth = getTextWidth(text, textCase); uint16_t textWidth = getTextWidth(text, textCase);
@@ -533,9 +541,12 @@ void DisplayManager_::setup()
TJpgDec.setCallback(jpg_output); TJpgDec.setCallback(jpg_output);
TJpgDec.setJpgScale(1); TJpgDec.setJpgScale(1);
FastLED.addLeds<NEOPIXEL, MATRIX_PIN>(leds, MATRIX_WIDTH * MATRIX_HEIGHT).setTemperature(OvercastSky); FastLED.addLeds<NEOPIXEL, MATRIX_PIN>(leds, MATRIX_WIDTH * MATRIX_HEIGHT);
setMatrixLayout(MATRIX_LAYOUT); setMatrixLayout(MATRIX_LAYOUT);
if (COLOR_CORRECTION)
{
FastLED.setCorrection(COLOR_CORRECTION);
}
gif.setMatrix(matrix); gif.setMatrix(matrix);
ui->setAppAnimation(SLIDE_DOWN); ui->setAppAnimation(SLIDE_DOWN);
ui->setTimePerApp(TIME_PER_APP); ui->setTimePerApp(TIME_PER_APP);
@@ -555,6 +566,7 @@ void DisplayManager_::tick()
else else
{ {
ui->update(); ui->update();
if (ui->getUiState()->appState == IN_TRANSITION && !appIsSwitching) if (ui->getUiState()->appState == IN_TRANSITION && !appIsSwitching)
@@ -800,10 +812,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);
} }
@@ -941,3 +955,145 @@ String DisplayManager_::getAppsAsJson()
serializeJson(appsObject, json); serializeJson(appsObject, json);
return json; return json;
} }
void DisplayManager_::powerStateParse(const char *json)
{
DynamicJsonDocument doc(128);
DeserializationError error = deserializeJson(doc, json);
if (error)
return;
if (doc.containsKey("state"))
{
bool state = doc["state"].as<bool>();
setPower(state);
}
}
void DisplayManager_::setPower(bool state)
{
if (state)
{
MATRIX_OFF = false;
setBrightness(lastBrightness);
}
else
{
MATRIX_OFF = true;
lastBrightness = BRIGHTNESS;
setBrightness(0);
}
}
void DisplayManager_::setIndicator1Color(uint16_t color)
{
ui->setIndicator1Color(color);
}
void DisplayManager_::setIndicator1State(bool state)
{
ui->setIndicator1State(state);
}
void DisplayManager_::setIndicator2Color(uint16_t color)
{
ui->setIndicator2Color(color);
}
void DisplayManager_::setIndicator2State(bool state)
{
ui->setIndicator2State(state);
}
void DisplayManager_::indicatorParser(uint8_t indicator, const char *json)
{
DynamicJsonDocument doc(128);
DeserializationError error = deserializeJson(doc, json);
if (error)
return;
if (doc.containsKey("color"))
{
auto color = doc["color"];
if (color.is<String>())
{
uint16_t col = hexToRgb565(color.as<String>());
if (col > 0)
{
if (indicator == 1)
{
ui->setIndicator1State(true);
ui->setIndicator1Color(col);
}
else
{
ui->setIndicator2State(true);
ui->setIndicator2Color(col);
}
}
else
{
if (indicator == 1)
{
ui->setIndicator1State(false);
}
else
{
ui->setIndicator2State(false);
}
}
}
else if (color.is<JsonArray>() && color.size() == 3)
{
uint8_t r = color[0];
uint8_t g = color[1];
uint8_t b = color[2];
if (r == 0 && g == 0 && b == 0)
{
if (indicator == 1)
{
ui->setIndicator1State(false);
}
else
{
ui->setIndicator2State(false);
}
}
else
{
if (indicator == 1)
{
ui->setIndicator1State(true);
ui->setIndicator1Color((r << 11) | (g << 5) | b);
}
else
{
ui->setIndicator2State(true);
ui->setIndicator2Color((r << 11) | (g << 5) | b);
}
}
}
}
if (doc.containsKey("blink"))
{
if (indicator == 1)
{
ui->setIndicator1Blink(doc["blink"].as<bool>());
}
else
{
ui->setIndicator2Blink(doc["blink"].as<bool>());
}
}
else
{
if (indicator == 1)
{
ui->setIndicator1Blink(false);
}
else
{
ui->setIndicator2Blink(false);
}
}
}

View File

@@ -55,7 +55,6 @@ public:
bool setAutoTransition(bool active); bool setAutoTransition(bool active);
void switchToApp(const char *json); void switchToApp(const char *json);
void setNewSettings(const char *json); void setNewSettings(const char *json);
void drawGIF(uint16_t x, uint16_t y, fs::File gifFile);
void drawJPG(uint16_t x, uint16_t y, fs::File jpgFile); void drawJPG(uint16_t x, uint16_t y, fs::File jpgFile);
void drawProgressBar(int cur, int total); void drawProgressBar(int cur, int total);
void drawMenuIndicator(int cur, int total, uint16_t color); void drawMenuIndicator(int cur, int total, uint16_t color);
@@ -66,6 +65,13 @@ public:
void setAppTime(uint16_t duration); void setAppTime(uint16_t duration);
String getAppsAsJson(); String getAppsAsJson();
String getStat(); String getStat();
void setPower(bool state);
void powerStateParse(const char *json);
void setIndicator1Color(uint16_t color);
void setIndicator1State(bool state);
void setIndicator2Color(uint16_t color);
void setIndicator2State(bool state);
void indicatorParser(uint8_t indicator, const char *json);
}; };
extern DisplayManager_ &DisplayManager; extern DisplayManager_ &DisplayManager;

View File

@@ -8,6 +8,38 @@
std::map<char, uint16_t> CharMap = { std::map<char, uint16_t> CharMap = {
{32, 2}, {33, 2}, {34, 4}, {35, 4}, {36, 4}, {37, 4}, {38, 4}, {39, 2}, {40, 3}, {41, 3}, {42, 4}, {43, 4}, {44, 3}, {45, 4}, {46, 2}, {47, 4}, {48, 4}, {49, 4}, {50, 4}, {51, 4}, {52, 4}, {53, 4}, {54, 4}, {55, 4}, {56, 4}, {57, 4}, {58, 2}, {59, 3}, {60, 4}, {61, 4}, {62, 4}, {63, 4}, {64, 4}, {65, 4}, {66, 4}, {67, 4}, {68, 4}, {69, 4}, {70, 4}, {71, 4}, {72, 4}, {73, 2}, {74, 4}, {75, 4}, {76, 4}, {77, 6}, {78, 5}, {79, 4}, {80, 4}, {81, 5}, {82, 4}, {83, 4}, {84, 4}, {85, 4}, {86, 4}, {87, 6}, {88, 4}, {89, 4}, {90, 4}, {91, 4}, {92, 4}, {93, 4}, {94, 4}, {95, 4}, {96, 3}, {97, 4}, {98, 4}, {99, 4}, {100, 4}, {101, 4}, {102, 4}, {103, 4}, {104, 4}, {105, 2}, {106, 4}, {107, 4}, {108, 4}, {109, 4}, {110, 4}, {111, 4}, {112, 4}, {113, 4}, {114, 4}, {115, 4}, {116, 4}, {117, 4}, {118, 4}, {119, 4}, {120, 4}, {121, 4}, {122, 4}, {123, 4}, {124, 2}, {125, 4}, {126, 4}, {161, 2}, {162, 4}, {163, 4}, {164, 4}, {165, 4}, {166, 2}, {167, 4}, {168, 4}, {169, 4}, {170, 4}, {171, 3}, {172, 4}, {173, 3}, {174, 4}, {175, 4}, {176, 4}, {177, 4}, {178, 4}, {179, 4}, {180, 3}, {181, 4}, {182, 4}, {183, 4}, {184, 4}, {185, 2}, {186, 4}, {187, 3}, {188, 4}, {189, 4}, {190, 4}, {191, 4}, {192, 4}, {193, 4}, {194, 4}, {195, 4}, {196, 4}, {197, 4}, {198, 4}, {199, 4}, {200, 4}, {201, 4}, {202, 4}, {203, 4}, {204, 4}, {205, 4}, {206, 4}, {207, 4}, {208, 4}, {209, 4}, {210, 4}, {211, 4}, {212, 4}, {213, 4}, {214, 4}, {215, 4}, {216, 4}, {217, 4}, {218, 4}, {219, 4}, {220, 4}, {221, 4}, {222, 4}, {223, 4}, {224, 4}, {225, 4}, {226, 4}, {227, 4}, {228, 4}, {229, 4}, {230, 4}, {231, 4}, {232, 4}, {233, 4}, {234, 4}, {235, 4}, {236, 3}, {237, 3}, {238, 4}, {239, 4}, {240, 4}, {241, 4}, {242, 4}, {243, 4}, {244, 4}, {245, 4}, {246, 4}, {247, 4}, {248, 4}, {249, 4}, {250, 4}, {251, 4}, {252, 4}, {253, 4}, {254, 4}, {255, 4}, {285, 2}, {338, 4}, {339, 4}, {352, 4}, {353, 4}, {376, 4}, {381, 4}, {382, 4}, {3748, 2}, {5024, 2}, {8226, 2}, {8230, 4}, {8364, 4}, {65533, 4}}; {32, 2}, {33, 2}, {34, 4}, {35, 4}, {36, 4}, {37, 4}, {38, 4}, {39, 2}, {40, 3}, {41, 3}, {42, 4}, {43, 4}, {44, 3}, {45, 4}, {46, 2}, {47, 4}, {48, 4}, {49, 4}, {50, 4}, {51, 4}, {52, 4}, {53, 4}, {54, 4}, {55, 4}, {56, 4}, {57, 4}, {58, 2}, {59, 3}, {60, 4}, {61, 4}, {62, 4}, {63, 4}, {64, 4}, {65, 4}, {66, 4}, {67, 4}, {68, 4}, {69, 4}, {70, 4}, {71, 4}, {72, 4}, {73, 2}, {74, 4}, {75, 4}, {76, 4}, {77, 6}, {78, 5}, {79, 4}, {80, 4}, {81, 5}, {82, 4}, {83, 4}, {84, 4}, {85, 4}, {86, 4}, {87, 6}, {88, 4}, {89, 4}, {90, 4}, {91, 4}, {92, 4}, {93, 4}, {94, 4}, {95, 4}, {96, 3}, {97, 4}, {98, 4}, {99, 4}, {100, 4}, {101, 4}, {102, 4}, {103, 4}, {104, 4}, {105, 2}, {106, 4}, {107, 4}, {108, 4}, {109, 4}, {110, 4}, {111, 4}, {112, 4}, {113, 4}, {114, 4}, {115, 4}, {116, 4}, {117, 4}, {118, 4}, {119, 4}, {120, 4}, {121, 4}, {122, 4}, {123, 4}, {124, 2}, {125, 4}, {126, 4}, {161, 2}, {162, 4}, {163, 4}, {164, 4}, {165, 4}, {166, 2}, {167, 4}, {168, 4}, {169, 4}, {170, 4}, {171, 3}, {172, 4}, {173, 3}, {174, 4}, {175, 4}, {176, 4}, {177, 4}, {178, 4}, {179, 4}, {180, 3}, {181, 4}, {182, 4}, {183, 4}, {184, 4}, {185, 2}, {186, 4}, {187, 3}, {188, 4}, {189, 4}, {190, 4}, {191, 4}, {192, 4}, {193, 4}, {194, 4}, {195, 4}, {196, 4}, {197, 4}, {198, 4}, {199, 4}, {200, 4}, {201, 4}, {202, 4}, {203, 4}, {204, 4}, {205, 4}, {206, 4}, {207, 4}, {208, 4}, {209, 4}, {210, 4}, {211, 4}, {212, 4}, {213, 4}, {214, 4}, {215, 4}, {216, 4}, {217, 4}, {218, 4}, {219, 4}, {220, 4}, {221, 4}, {222, 4}, {223, 4}, {224, 4}, {225, 4}, {226, 4}, {227, 4}, {228, 4}, {229, 4}, {230, 4}, {231, 4}, {232, 4}, {233, 4}, {234, 4}, {235, 4}, {236, 3}, {237, 3}, {238, 4}, {239, 4}, {240, 4}, {241, 4}, {242, 4}, {243, 4}, {244, 4}, {245, 4}, {246, 4}, {247, 4}, {248, 4}, {249, 4}, {250, 4}, {251, 4}, {252, 4}, {253, 4}, {254, 4}, {255, 4}, {285, 2}, {338, 4}, {339, 4}, {352, 4}, {353, 4}, {376, 4}, {381, 4}, {382, 4}, {3748, 2}, {5024, 2}, {8226, 2}, {8230, 4}, {8364, 4}, {65533, 4}};
//---------------------------------------------------------------
// This is the gamma lookup for mapping 255 brightness levels
// The lookup table would be similar but have slightly shifted
// numbers for different gammas (gamma 2.0, 2.2, 2.5, etc.)
const uint8_t PROGMEM gamma8[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2,
2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5,
5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10,
10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16,
17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25,
25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36,
37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50,
51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68,
69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89,
90, 92, 93, 95, 96, 98, 99,101,102,104,105,107,109,110,112,114,
115,117,119,120,122,124,126,127,129,131,133,135,137,138,140,142,
144,146,148,150,152,154,156,158,160,162,164,167,169,171,173,175,
177,180,182,184,186,189,191,193,196,198,200,203,205,208,210,213,
215,218,220,223,225,228,231,233,236,239,241,244,247,249,252,255 };
CRGB applyGammaCorrection(const CRGB& color) {
CRGB correctedColor;
correctedColor.r = pgm_read_byte(&gamma8[color.r]);
correctedColor.g = pgm_read_byte(&gamma8[color.g]);
correctedColor.b = pgm_read_byte(&gamma8[color.b]);
return correctedColor;
}
uint32_t hsvToRgb(uint8_t h, uint8_t s, uint8_t v) uint32_t hsvToRgb(uint8_t h, uint8_t s, uint8_t v)
{ {
CHSV hsv(h, s, v); CHSV hsv(h, s, v);

View File

@@ -2,6 +2,7 @@
#include "Preferences.h" #include "Preferences.h"
#include <WiFi.h> #include <WiFi.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <LittleFS.h> #include <LittleFS.h>
Preferences Settings; Preferences Settings;
@@ -66,6 +67,22 @@ void loadDevSettings()
TEMP_DECIMAL_PLACES = doc["temp_dec_places"].as<int>(); TEMP_DECIMAL_PLACES = doc["temp_dec_places"].as<int>();
} }
if (doc.containsKey("gamma"))
{
GAMMA = doc["gamma"].as<float>();
}
if (doc.containsKey("color_correction"))
{
auto correction = doc["color_correction"];
if (correction.is<JsonArray>() && correction.size() == 3)
{
uint8_t r = correction[0];
uint8_t g = correction[1];
uint8_t b = correction[2];
COLOR_CORRECTION.setRGB(r, g, b);
}
}
file.close(); file.close();
} }
} }
@@ -139,7 +156,7 @@ IPAddress gateway;
IPAddress subnet; IPAddress subnet;
IPAddress primaryDNS; IPAddress primaryDNS;
IPAddress secondaryDNS; IPAddress secondaryDNS;
const char *VERSION = "0.48"; const char *VERSION = "0.49";
String MQTT_HOST = ""; String MQTT_HOST = "";
uint16_t MQTT_PORT = 1883; uint16_t MQTT_PORT = 1883;
String MQTT_USER; String MQTT_USER;
@@ -211,3 +228,5 @@ uint8_t VOLUME;
int MATRIX_LAYOUT; int MATRIX_LAYOUT;
bool UPDATE_AVAILABLE = false; bool UPDATE_AVAILABLE = false;
long RECEIVED_MESSAGES; long RECEIVED_MESSAGES;
CRGB COLOR_CORRECTION;
float GAMMA = 0;

View File

@@ -1,6 +1,7 @@
#ifndef GLOBALS_H #ifndef GLOBALS_H
#define GLOBALS_H #define GLOBALS_H
#include <Arduino.h> #include <Arduino.h>
#include <FastLED.h>
extern const char *uniqueID; extern const char *uniqueID;
extern const char *VERSION; extern const char *VERSION;
@@ -76,6 +77,8 @@ extern uint8_t VOLUME;
extern int MATRIX_LAYOUT; extern int MATRIX_LAYOUT;
extern bool UPDATE_AVAILABLE; extern bool UPDATE_AVAILABLE;
extern long RECEIVED_MESSAGES; extern long RECEIVED_MESSAGES;
extern CRGB COLOR_CORRECTION;
extern float GAMMA;
void loadSettings(); void loadSettings();
void saveSettings(); void saveSettings();
#endif // Globals_H #endif // Globals_H

View File

@@ -10,14 +10,15 @@
#include "UpdateManager.h" #include "UpdateManager.h"
WiFiClient espClient; WiFiClient espClient;
uint8_t lastBrightness;
HADevice device; HADevice device;
HAMqtt mqtt(espClient, device, 22); HAMqtt mqtt(espClient, device, 25);
unsigned long reconnectTimer = 0; unsigned long reconnectTimer = 0;
const unsigned long reconnectInterval = 30000; // 30 Sekunden const unsigned long reconnectInterval = 30000; // 30 Sekunden
HALight *Matrix = nullptr; HALight *Matrix = nullptr;
HALight *Indikator1 = nullptr;
HALight *Indikator2 = nullptr;
HASelect *BriMode = nullptr; HASelect *BriMode = nullptr;
HAButton *dismiss = nullptr; HAButton *dismiss = nullptr;
HAButton *nextApp = nullptr; HAButton *nextApp = nullptr;
@@ -100,26 +101,37 @@ void onSelectCommand(int8_t index, HASelect *sender)
} }
void onRGBColorCommand(HALight::RGBColor color, HALight *sender) void onRGBColorCommand(HALight::RGBColor color, HALight *sender)
{
if (sender == Matrix)
{ {
TEXTCOLOR_565 = ((color.red & 0x1F) << 11) | ((color.green & 0x3F) << 5) | (color.blue & 0x1F); TEXTCOLOR_565 = ((color.red & 0x1F) << 11) | ((color.green & 0x3F) << 5) | (color.blue & 0x1F);
saveSettings(); saveSettings();
}
else if (sender == Indikator1)
{
DisplayManager.setIndicator1Color(((color.red & 0x1F) << 11) | ((color.green & 0x3F) << 5) | (color.blue & 0x1F));
}
else if (sender == Indikator2)
{
DisplayManager.setIndicator2Color(((color.red & 0x1F) << 11) | ((color.green & 0x3F) << 5) | (color.blue & 0x1F));
}
sender->setRGBColor(color); // report color back to the Home Assistant sender->setRGBColor(color); // report color back to the Home Assistant
} }
void onStateCommand(bool state, HALight *sender) void onStateCommand(bool state, HALight *sender)
{ {
if (state) if (sender == Matrix)
{ {
MATRIX_OFF = false; DisplayManager.setPower(state);
DisplayManager.setBrightness(lastBrightness);
} }
else else if (sender == Indikator1)
{ {
MATRIX_OFF = true; DisplayManager.setIndicator1State(state);
lastBrightness = BRIGHTNESS; }
DisplayManager.setBrightness(0); else if (sender == Indikator2)
{
DisplayManager.setIndicator2State(state);
} }
sender->setState(state); sender->setState(state);
} }
@@ -129,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);
} }
@@ -208,7 +219,24 @@ void onMqttMessage(const char *topic, const uint8_t *payload, uint16_t length)
delete[] payloadCopy; delete[] payloadCopy;
return; return;
} }
if (strTopic.equals(MQTT_PREFIX + "/power"))
{
DisplayManager.powerStateParse(payloadCopy);
delete[] payloadCopy;
return;
}
if (strTopic.equals(MQTT_PREFIX + "/indicator1"))
{
DisplayManager.indicatorParser(1, payloadCopy);
delete[] payloadCopy;
return;
}
if (strTopic.equals(MQTT_PREFIX + "/indicator2"))
{
DisplayManager.indicatorParser(2, 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;
@@ -239,7 +267,10 @@ void onMqttConnected()
"/nextapp", "/nextapp",
"/doupdate", "/doupdate",
"/nextapp", "/nextapp",
"/apps"}; "/apps",
"/power",
"/indicator1",
"/indicator2"};
for (const char *topic : topics) for (const char *topic : topics)
{ {
String fullTopic = prefix + topic; String fullTopic = prefix + topic;
@@ -265,7 +296,7 @@ void connect()
} }
} }
char matID[40], briID[40]; char matID[40], ind1ID[40], ind2ID[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], updateID[40], doUpdateID[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 #ifdef ULANZI
char batID[40]; char batID[40];
@@ -273,7 +304,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"));
@@ -304,6 +334,20 @@ void MQTTManager_::setup()
Matrix->setCurrentState(true); Matrix->setCurrentState(true);
Matrix->setBRIGHTNESS(BRIGHTNESS); Matrix->setBRIGHTNESS(BRIGHTNESS);
sprintf(ind1ID, HAi1ID, macStr);
Indikator1 = new HALight(ind1ID, HALight::RGBFeature);
Indikator1->setIcon(HAi1Icon);
Indikator1->setName(HAi1Name);
Indikator1->onStateCommand(onStateCommand);
Indikator1->onRGBColorCommand(onRGBColorCommand);
sprintf(ind2ID, HAi2ID, macStr);
Indikator2 = new HALight(ind2ID, HALight::RGBFeature);
Indikator2->setIcon(HAi2Icon);
Indikator2->setName(HAi2Name);
Indikator2->onStateCommand(onStateCommand);
Indikator2->onRGBColorCommand(onRGBColorCommand);
HALight::RGBColor color; HALight::RGBColor color;
color.red = (TEXTCOLOR_565 >> 11) << 3; color.red = (TEXTCOLOR_565 >> 11) << 3;
color.green = ((TEXTCOLOR_565 >> 5) & 0x3F) << 2; color.green = ((TEXTCOLOR_565 >> 5) & 0x3F) << 2;
@@ -368,8 +412,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);

View File

@@ -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();
} }

View File

@@ -99,9 +99,15 @@ void ServerManager_::setup()
{ DisplayManager.generateCustomPage(mws.webserver->arg("name"),mws.webserver->arg("plain").c_str()); mws.webserver->send(200,"OK"); }); { DisplayManager.generateCustomPage(mws.webserver->arg("name"),mws.webserver->arg("plain").c_str()); mws.webserver->send(200,"OK"); });
mws.addHandler("/api/stats", HTTP_GET, []() mws.addHandler("/api/stats", HTTP_GET, []()
{ mws.webserver->sendContent(DisplayManager.getStat()); }); { mws.webserver->sendContent(DisplayManager.getStat()); });
mws.addHandler("/api/indicator1", HTTP_POST, []()
{ DisplayManager.indicatorParser(1,mws.webserver->arg("plain").c_str()); mws.webserver->send(200,"OK"); });
mws.addHandler("/api/indicator2", HTTP_POST, []()
{ DisplayManager.indicatorParser(2,mws.webserver->arg("plain").c_str()); mws.webserver->send(200,"OK"); });
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/power", HTTP_POST, []()
{ DisplayManager.powerStateParse(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);

View File

@@ -94,7 +94,6 @@ void setup()
delay(200); delay(200);
DisplayManager.setBrightness(BRIGHTNESS); DisplayManager.setBrightness(BRIGHTNESS);
DisplayManager.clearMatrix();
} }
void loop() void loop()