From 8e016ee4dc01522348e862669b30ded46d7cfd5e Mon Sep 17 00:00:00 2001
From: BlueAndi
Date: Sun, 18 Jun 2023 23:26:50 +0200
Subject: [PATCH 001/105] Set "homeassistant" as discovery prefix default.
---
lib/MqttApiTopicHandler/src/HomeAssistantMqtt.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/MqttApiTopicHandler/src/HomeAssistantMqtt.cpp b/lib/MqttApiTopicHandler/src/HomeAssistantMqtt.cpp
index fe50b489..ec84222e 100644
--- a/lib/MqttApiTopicHandler/src/HomeAssistantMqtt.cpp
+++ b/lib/MqttApiTopicHandler/src/HomeAssistantMqtt.cpp
@@ -72,7 +72,7 @@ const char* HomeAssistantMqtt::KEY_HA_DISCOVERY_PREFIX = "ha_dp";
const char* HomeAssistantMqtt::NAME_HA_DISCOVERY_PREFIX = "Home Assistant Discovery Prefix";
/* Initialize Home Assistant discovery prefix default value. */
-const char* HomeAssistantMqtt::DEFAULT_HA_DISCOVERY_PREFIX = "";
+const char* HomeAssistantMqtt::DEFAULT_HA_DISCOVERY_PREFIX = "homeassistant";
/******************************************************************************
* Public Methods
From 8ed795aba0d6c933b8d9fa9f9eaa9b07cf436eda Mon Sep 17 00:00:00 2001
From: BlueAndi
Date: Sun, 18 Jun 2023 23:27:25 +0200
Subject: [PATCH 002/105] MQTT service for Ulanzi TC001 was missing.
---
config/configNormal.ini | 2 +-
config/configSmall.ini | 2 +-
config/configSmallNoI2s.ini | 2 +-
config/configSmallUlanzi.ini | 4 ++--
4 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/config/configNormal.ini b/config/configNormal.ini
index e96bf216..125534c9 100644
--- a/config/configNormal.ini
+++ b/config/configNormal.ini
@@ -12,7 +12,7 @@ lib_deps =
MqttService @ ~0.1.0
# ********** Topic handlers **********
RestApiTopicHandler @ ~0.1.0 # Mandatory, can not be removed. Used by webinterface.
- MqttApiTopicHandler @ ~0.1.0
+ MqttApiTopicHandler @ ~0.1.0 # Requires MqttService
# ********** Plugins **********
;BatteryPlugin @ ~0.1.0
BTCQuotePlugin @ ~0.1.0
diff --git a/config/configSmall.ini b/config/configSmall.ini
index b844f118..725be8d4 100644
--- a/config/configSmall.ini
+++ b/config/configSmall.ini
@@ -12,7 +12,7 @@ lib_deps =
;MqttService @ ~0.1.0
# ********** Topic handlers **********
RestApiTopicHandler @ ~0.1.0 # Mandatory, can not be removed. Used by webinterface.
- ;MqttApiTopicHandler @ ~0.1.0
+ ;MqttApiTopicHandler @ ~0.1.0 # Requires MqttService
# ********** Plugins **********
;BatteryPlugin @ ~0.1.0
BTCQuotePlugin @ ~0.1.0
diff --git a/config/configSmallNoI2s.ini b/config/configSmallNoI2s.ini
index d2ad23ff..a4a6a425 100644
--- a/config/configSmallNoI2s.ini
+++ b/config/configSmallNoI2s.ini
@@ -12,7 +12,7 @@ lib_deps =
;MqttService @ ~0.1.0
# ********** Topic handlers **********
RestApiTopicHandler @ ~0.1.0 # Mandatory, can not be removed. Used by webinterface.
- ;MqttApiTopicHandler @ ~0.1.0
+ ;MqttApiTopicHandler @ ~0.1.0 # Requires MqttService
# ********** Plugins **********
;BatteryPlugin @ ~0.1.0
BTCQuotePlugin @ ~0.1.0
diff --git a/config/configSmallUlanzi.ini b/config/configSmallUlanzi.ini
index 0edc695e..59f4f678 100644
--- a/config/configSmallUlanzi.ini
+++ b/config/configSmallUlanzi.ini
@@ -9,10 +9,10 @@ lib_deps =
TopicHandlerService @ ~0.1.0 # Mandatory, can not be removed.
SettingsService @ ~0.1.0 # Mandatory, can not be removed.
;AudioService @ ~0.1.0 # No I2S interface available
- ;MqttService @ ~0.1.0
+ MqttService @ ~0.1.0
# ********** Topic handlers **********
RestApiTopicHandler @ ~0.1.0 # Mandatory, can not be removed. Used by webinterface.
- MqttApiTopicHandler @ ~0.1.0
+ MqttApiTopicHandler @ ~0.1.0 # Requires MqttService
# ********** Plugins **********
BatteryPlugin @ ~0.1.0
;BTCQuotePlugin @ ~0.1.0
From 144b6536c40e7e6cc0c0bb11b99eb03048c25df3 Mon Sep 17 00:00:00 2001
From: BlueAndi
Date: Thu, 22 Jun 2023 21:19:55 +0200
Subject: [PATCH 003/105] Home Assistant discovery by default disabled. It can
be enabled in the settings.
---
.../src/HomeAssistantMqtt.cpp | 45 ++++++++++++++-----
.../src/HomeAssistantMqtt.h | 22 +++++++--
2 files changed, 51 insertions(+), 16 deletions(-)
diff --git a/lib/MqttApiTopicHandler/src/HomeAssistantMqtt.cpp b/lib/MqttApiTopicHandler/src/HomeAssistantMqtt.cpp
index ec84222e..fee3b18e 100644
--- a/lib/MqttApiTopicHandler/src/HomeAssistantMqtt.cpp
+++ b/lib/MqttApiTopicHandler/src/HomeAssistantMqtt.cpp
@@ -74,6 +74,15 @@ const char* HomeAssistantMqtt::NAME_HA_DISCOVERY_PREFIX = "Home Assistant Di
/* Initialize Home Assistant discovery prefix default value. */
const char* HomeAssistantMqtt::DEFAULT_HA_DISCOVERY_PREFIX = "homeassistant";
+/* Initialize Home Assistant discovery enable flag key */
+const char* HomeAssistantMqtt::KEY_HA_DISCOVERY_ENABLE = "ha_ena";
+
+/* Initialize Home Assistant discovery enable flag name */
+const char* HomeAssistantMqtt::NAME_HA_DISCOVERY_ENABLE = "Enable Home Assistant Discovery";
+
+/* Initialize Home Assistant discovery enable flag default value */
+const bool HomeAssistantMqtt::DEFAULT_HA_DISCOVERY_ENABLE = false;
+
/******************************************************************************
* Public Methods
*****************************************************************************/
@@ -86,13 +95,18 @@ void HomeAssistantMqtt::start()
{
LOG_ERROR("Couldn't register HA discovery prefix setting.");
}
+ else if (false == settings.registerSetting(&m_haDiscoveryEnabledSetting))
+ {
+ LOG_ERROR("Couldn't register HA discovery enable setting.");
+ }
else if (false == settings.open(true))
{
LOG_ERROR("Couldn't open settings.");
}
else
{
- m_haDiscoveryPrefix = m_haDiscoveryPrefixSetting.getValue();
+ m_haDiscoveryPrefix = m_haDiscoveryPrefixSetting.getValue();
+ m_haDiscoveryEnabled = m_haDiscoveryEnabledSetting.getValue();
settings.close();
}
@@ -103,21 +117,26 @@ void HomeAssistantMqtt::stop()
SettingsService& settings = SettingsService::getInstance();
settings.unregisterSetting(&m_haDiscoveryPrefixSetting);
+ settings.unregisterSetting(&m_haDiscoveryEnabledSetting);
clearMqttDiscoveryInfoList();
}
void HomeAssistantMqtt::process(bool isConnected)
{
- if (true == isConnected)
+ /* The Home Assistant discovery must be enabled. */
+ if (true == m_haDiscoveryEnabled)
{
- if (false == m_isConnected)
+ if (true == isConnected)
{
- publishAllAutoDiscoveryInfo(true);
- }
- else
- {
- publishAllAutoDiscoveryInfo(false);
+ if (false == m_isConnected)
+ {
+ publishAllAutoDiscoveryInfo(true);
+ }
+ else
+ {
+ publishAllAutoDiscoveryInfo(false);
+ }
}
}
@@ -126,10 +145,11 @@ void HomeAssistantMqtt::process(bool isConnected)
void HomeAssistantMqtt::registerMqttDiscovery(const String& nodeId, const String& objectId, const String& stateTopic, const String& cmdTopic, const String& availabilityTopic, JsonObjectConst& extra)
{
- /* The Home Assistant discovery prefix must be available, otherwise this
+ /* The Home Assistant discovery must be enabled and the prefix must be available, otherwise this
* feature is disabled.
*/
- if (false == m_haDiscoveryPrefix.isEmpty())
+ if ((true == m_haDiscoveryEnabled) &&
+ (false == m_haDiscoveryPrefix.isEmpty()))
{
JsonVariantConst jsonHomeAssistant = extra["ha"];
@@ -181,10 +201,11 @@ void HomeAssistantMqtt::registerMqttDiscovery(const String& nodeId, const String
void HomeAssistantMqtt::unregisterMqttDiscovery(const String& nodeId, const String& objectId, const String& stateTopic, const String& cmdTopic)
{
- /* The Home Assistant discovery prefix must be available, otherwise this
+ /* The Home Assistant discovery must be enabled and the prefix must be available, otherwise this
* feature is disabled.
*/
- if (false == m_haDiscoveryPrefix.isEmpty())
+ if ((true == m_haDiscoveryEnabled) &&
+ (false == m_haDiscoveryPrefix.isEmpty()))
{
ListOfMqttDiscoveryInfo::iterator listOfMqttDiscoveryInfoIt = m_mqttDiscoveryInfoList.begin();
diff --git a/lib/MqttApiTopicHandler/src/HomeAssistantMqtt.h b/lib/MqttApiTopicHandler/src/HomeAssistantMqtt.h
index 693f178e..766f9546 100644
--- a/lib/MqttApiTopicHandler/src/HomeAssistantMqtt.h
+++ b/lib/MqttApiTopicHandler/src/HomeAssistantMqtt.h
@@ -45,6 +45,7 @@
*****************************************************************************/
#include
#include
+#include
#include
#include
@@ -69,7 +70,9 @@ class HomeAssistantMqtt
*/
HomeAssistantMqtt() :
m_haDiscoveryPrefixSetting(KEY_HA_DISCOVERY_PREFIX, NAME_HA_DISCOVERY_PREFIX, DEFAULT_HA_DISCOVERY_PREFIX, MIN_VALUE_HA_DISCOVERY_PREFIX, MAX_VALUE_HA_DISCOVERY_PREFIX),
+ m_haDiscoveryEnabledSetting(KEY_HA_DISCOVERY_ENABLE, NAME_HA_DISCOVERY_ENABLE, DEFAULT_HA_DISCOVERY_ENABLE),
m_haDiscoveryPrefix(),
+ m_haDiscoveryEnabled(false),
m_isConnected(false)
{
}
@@ -140,6 +143,15 @@ class HomeAssistantMqtt
/** Home Assistant discovery prefix max. length */
static const size_t MAX_VALUE_HA_DISCOVERY_PREFIX = 64U;
+ /** Home Assistant discovery enable flag key */
+ static const char* KEY_HA_DISCOVERY_ENABLE;
+
+ /** Home Assistant discovery enable flag name */
+ static const char* NAME_HA_DISCOVERY_ENABLE;
+
+ /** Home Assistant discovery enable flag default value */
+ static const bool DEFAULT_HA_DISCOVERY_ENABLE;
+
/** Information necessary for Home Assistant MQTT discovery. */
struct MqttDiscoveryInfo
{
@@ -173,10 +185,12 @@ class HomeAssistantMqtt
/** List of Home Assistant MQTT discovery information. */
typedef std::vector ListOfMqttDiscoveryInfo;
- KeyValueString m_haDiscoveryPrefixSetting; /**< Setting for the Home Assistant MQTT discovery prefix. */
- String m_haDiscoveryPrefix; /**< Home Assistant MQTT discovery prefix. */
- ListOfMqttDiscoveryInfo m_mqttDiscoveryInfoList; /**< List of Home Assistant MQTT discovery informations. */
- bool m_isConnected; /**< Is MQTT broker connection established? */
+ KeyValueString m_haDiscoveryPrefixSetting; /**< Setting for the Home Assistant MQTT discovery prefix. */
+ KeyValueBool m_haDiscoveryEnabledSetting; /**< Setting for the Home Assistant MQTT discovery enable flag. */
+ String m_haDiscoveryPrefix; /**< Home Assistant MQTT discovery prefix. */
+ bool m_haDiscoveryEnabled; /**< Is the Home Assistant MQTT discovery enabled or not. */
+ ListOfMqttDiscoveryInfo m_mqttDiscoveryInfoList; /**< List of Home Assistant MQTT discovery informations. */
+ bool m_isConnected; /**< Is MQTT broker connection established? */
HomeAssistantMqtt(const HomeAssistantMqtt& ext);
HomeAssistantMqtt& operator=(const HomeAssistantMqtt& ext);
From 8c1bf56e8b2b461a1ff8fbf8e350e58238ac65dd Mon Sep 17 00:00:00 2001
From: BlueAndi
Date: Sun, 25 Jun 2023 01:04:11 +0200
Subject: [PATCH 004/105] With the sequence \xHH a character code in hex can be
specified. This is useful e.g. to get the degree character 0x8E by using \x8E
---
lib/YAWidgets/src/TextWidget.cpp | 34 +++++++++++++++++++++++++++++++-
lib/YAWidgets/src/TextWidget.h | 12 +++++++++++
2 files changed, 45 insertions(+), 1 deletion(-)
diff --git a/lib/YAWidgets/src/TextWidget.cpp b/lib/YAWidgets/src/TextWidget.cpp
index 7cba00f2..9811dcee 100644
--- a/lib/YAWidgets/src/TextWidget.cpp
+++ b/lib/YAWidgets/src/TextWidget.cpp
@@ -68,7 +68,8 @@ const YAFont& TextWidget::DEFAULT_FONT = Fonts::getFontByTy
TextWidget::KeywordHandler TextWidget::m_keywordHandlers[] =
{
&TextWidget::handleColor,
- &TextWidget::handleAlignment
+ &TextWidget::handleAlignment,
+ &TextWidget::handleCharCode
};
/* Set default scroll pause in ms. */
@@ -539,6 +540,37 @@ bool TextWidget::handleAlignment(YAGfx* gfx, YAGfxText* gfxText, bool noAction,
return status;
}
+bool TextWidget::handleCharCode(YAGfx* gfx, YAGfxText* gfxText, bool noAction, const String& formatStr, bool isScrolling, uint8_t& overstep) const
+{
+ bool status = false;
+
+ UTIL_NOT_USED(isScrolling);
+
+ if (('x' == formatStr[0]) ||
+ ('X' == formatStr[0]))
+ {
+ const uint8_t CHAR_CODE_LEN = 2U;
+ String charCodeStr = String("0x") + formatStr.substring(1, 1 + CHAR_CODE_LEN);
+ uint8_t charCode = 0U;
+ bool convStatus = Util::strToUInt8(charCodeStr, charCode);
+
+ if (true == convStatus)
+ {
+ if ((false == noAction) &&
+ (nullptr != gfx) &&
+ (nullptr != gfxText))
+ {
+ gfxText->drawChar(*gfx, static_cast(charCode));
+ }
+
+ overstep = 1U + CHAR_CODE_LEN;
+ status = true;
+ }
+ }
+
+ return status;
+}
+
/******************************************************************************
* External Functions
*****************************************************************************/
diff --git a/lib/YAWidgets/src/TextWidget.h b/lib/YAWidgets/src/TextWidget.h
index af0f67c4..9bd55bab 100644
--- a/lib/YAWidgets/src/TextWidget.h
+++ b/lib/YAWidgets/src/TextWidget.h
@@ -452,6 +452,18 @@ class TextWidget : public Widget
* @return If keyword is handled successful, it returns true otherwise false.
*/
bool handleAlignment(YAGfx* gfx, YAGfxText* gfxText, bool noAction, const String& formatStr, bool isScrolling, uint8_t& overstep) const;
+
+ /**
+ * Handles the keyword for character code.
+ *
+ * @param[in] gfx Graphics interface, only necessary if actions shall take place.
+ * @param[in] noAction The handler shall take no action. This is only used to get rid of the keywords in the text.
+ * @param[in] formatStr String which may contain keywords.
+ * @param[out] overstep Number of characters, which must be overstepped before the next normal character comes.
+ *
+ * @return If keyword is handled successful, it returns true otherwise false.
+ */
+ bool handleCharCode(YAGfx* gfx, YAGfxText* gfxText, bool noAction, const String& formatStr, bool isScrolling, uint8_t& overstep) const;
};
/******************************************************************************
From b02a84e92ddb98c22f911360ab2799e2ab29a122 Mon Sep 17 00:00:00 2001
From: BlueAndi
Date: Sun, 25 Jun 2023 01:12:13 +0200
Subject: [PATCH 005/105] Debug log removed and max. payload size for sending
MQTT data increased to 2k.
---
lib/MqttService/src/MqttService.cpp | 2 --
lib/MqttService/src/MqttService.h | 2 +-
2 files changed, 1 insertion(+), 3 deletions(-)
diff --git a/lib/MqttService/src/MqttService.cpp b/lib/MqttService/src/MqttService.cpp
index 27399757..ad41b48f 100644
--- a/lib/MqttService/src/MqttService.cpp
+++ b/lib/MqttService/src/MqttService.cpp
@@ -367,8 +367,6 @@ void MqttService::rxCallback(char* topic, uint8_t* payload, uint32_t length)
{
SubscriberList::const_iterator it;
- LOG_DEBUG("MQTT Rx: %s", topic);
-
for(it = m_subscriberList.begin(); it != m_subscriberList.end(); ++it)
{
if (nullptr != (*it))
diff --git a/lib/MqttService/src/MqttService.h b/lib/MqttService/src/MqttService.h
index 40b76e0c..eff53d25 100644
--- a/lib/MqttService/src/MqttService.h
+++ b/lib/MqttService/src/MqttService.h
@@ -215,7 +215,7 @@ class MqttService : public IService
* Max. MQTT client buffer size in byte.
* Received MQTT messages greather than this will be skipped.
*/
- static const size_t MAX_BUFFER_SIZE = 1024U;
+ static const size_t MAX_BUFFER_SIZE = 2048U;
KeyValueString m_mqttBrokerUrlSetting; /**< URL of the MQTT broker setting */
String m_url; /**< URL of the MQTT broker */
From 45e5ce4707f0890d80cf3b35e0c69bba9673c1a8 Mon Sep 17 00:00:00 2001
From: BlueAndi
Date: Sun, 25 Jun 2023 01:12:48 +0200
Subject: [PATCH 006/105] Push URL moved in a separate function.
---
src/StateMachine/ConnectedState.cpp | 79 +++++++++++++++--------------
src/StateMachine/ConnectedState.h | 6 +++
2 files changed, 48 insertions(+), 37 deletions(-)
diff --git a/src/StateMachine/ConnectedState.cpp b/src/StateMachine/ConnectedState.cpp
index bea2b777..ffc8b4fb 100644
--- a/src/StateMachine/ConnectedState.cpp
+++ b/src/StateMachine/ConnectedState.cpp
@@ -138,43 +138,7 @@ void ConnectedState::entry(StateMachine& sm)
SysMsg::getInstance().show(infoStr, DURATION_NON_SCROLLING, SCROLLING_REPEAT_NUM);
}
- /* If a push URL is set, notify about the online status. */
- if (false == notifyURL.isEmpty())
- {
- String url = notifyURL;
- const char* GET_CMD = "get ";
- const char* POST_CMD = "post ";
- bool isGet = true;
-
- /* URL prefix might indicate the kind of request. */
- url.toLowerCase();
- if (0U != url.startsWith(GET_CMD))
- {
- url = url.substring(strlen(GET_CMD));
- isGet = true;
- }
- else if (0U != url.startsWith(POST_CMD))
- {
- url = url.substring(strlen(POST_CMD));
- isGet = false;
- }
- else
- {
- ;
- }
-
- if (true == m_client.begin(url))
- {
- if (false == m_client.GET())
- {
- LOG_WARNING("GET %s failed.", url.c_str());
- }
- else
- {
- LOG_INFO("Notification triggered.");
- }
- }
- }
+ pushUrl(notifyURL);
}
}
@@ -237,6 +201,47 @@ void ConnectedState::exit(StateMachine& sm)
* Private Methods
*****************************************************************************/
+void ConnectedState::pushUrl(const String& pushUrl)
+{
+ /* If a push URL is set, notify about the online status. */
+ if (false == pushUrl.isEmpty())
+ {
+ String url = pushUrl;
+ const char* GET_CMD = "get ";
+ const char* POST_CMD = "post ";
+ bool isGet = true;
+
+ /* URL prefix might indicate the kind of request. */
+ url.toLowerCase();
+ if (0U != url.startsWith(GET_CMD))
+ {
+ url = url.substring(strlen(GET_CMD));
+ isGet = true;
+ }
+ else if (0U != url.startsWith(POST_CMD))
+ {
+ url = url.substring(strlen(POST_CMD));
+ isGet = false;
+ }
+ else
+ {
+ ;
+ }
+
+ if (true == m_client.begin(url))
+ {
+ if (false == m_client.GET())
+ {
+ LOG_WARNING("GET %s failed.", url.c_str());
+ }
+ else
+ {
+ LOG_INFO("Notification triggered.");
+ }
+ }
+ }
+}
+
/******************************************************************************
* External Functions
*****************************************************************************/
diff --git a/src/StateMachine/ConnectedState.h b/src/StateMachine/ConnectedState.h
index 4dc7439a..011404fd 100644
--- a/src/StateMachine/ConnectedState.h
+++ b/src/StateMachine/ConnectedState.h
@@ -125,6 +125,12 @@ class ConnectedState : public AbstractState
*/
void initHttpClient(void);
+ /**
+ * Notify via URL that the system is online.
+ *
+ * @param[in] pushUrl Push URL
+ */
+ void pushUrl(const String& pushUrl);
};
/******************************************************************************
From a5d70f4b477418b94f35c66308d5900e0c3c9d21 Mon Sep 17 00:00:00 2001
From: BlueAndi
Date: Sun, 25 Jun 2023 01:14:34 +0200
Subject: [PATCH 007/105] Page time limited to min. 10s and fixed the problem,
that the slot time is only considered in the plugin activation. But if the
slot time was changed during active period, nothing happened.
---
lib/TempHumidPlugin/src/TempHumidPlugin.cpp | 15 ++++++++++-----
lib/TempHumidPlugin/src/TempHumidPlugin.h | 9 +++++++--
2 files changed, 17 insertions(+), 7 deletions(-)
diff --git a/lib/TempHumidPlugin/src/TempHumidPlugin.cpp b/lib/TempHumidPlugin/src/TempHumidPlugin.cpp
index d74d15f9..733ce71c 100644
--- a/lib/TempHumidPlugin/src/TempHumidPlugin.cpp
+++ b/lib/TempHumidPlugin/src/TempHumidPlugin.cpp
@@ -172,18 +172,23 @@ void TempHumidPlugin::process(bool isConnected)
m_sensorUpdateTimer.start(SENSOR_UPDATE_PERIOD);
}
-}
-
-void TempHumidPlugin::active(YAGfx& gfx)
-{
- MutexGuard guard(m_mutex);
/* Set time to show page - either 10s or slot_time / 4
* read here because otherwise we do not get config changes during runtime in slot_time.
*/
if (nullptr != m_slotInterf) {
m_pageTime = m_slotInterf->getDuration() / 4U;
+
+ if (DEFAULT_PAGE_TIME > m_pageTime)
+ {
+ m_pageTime = DEFAULT_PAGE_TIME;
+ }
}
+}
+
+void TempHumidPlugin::active(YAGfx& gfx)
+{
+ MutexGuard guard(m_mutex);
gfx.fillScreen(ColorDef::BLACK);
m_iconCanvas.update(gfx);
diff --git a/lib/TempHumidPlugin/src/TempHumidPlugin.h b/lib/TempHumidPlugin/src/TempHumidPlugin.h
index 9728fc9d..25602095 100644
--- a/lib/TempHumidPlugin/src/TempHumidPlugin.h
+++ b/lib/TempHumidPlugin/src/TempHumidPlugin.h
@@ -83,7 +83,7 @@ class TempHumidPlugin : public Plugin
m_bitmapWidget(),
m_textWidget("\\calign?"),
m_page(TEMPERATURE),
- m_pageTime(10000U),
+ m_pageTime(DEFAULT_PAGE_TIME),
m_timer(),
m_mutex(),
m_humidity(0.0F),
@@ -240,7 +240,12 @@ class TempHumidPlugin : public Plugin
/**
* Read sensor only every N milliseconds (currently 90 seconds)
*/
- static const uint32_t SENSOR_UPDATE_PERIOD = SIMPLE_TIMER_SECONDS(90U);
+ static const uint32_t SENSOR_UPDATE_PERIOD = SIMPLE_TIMER_SECONDS(90U);
+
+ /**
+ * Default time in ms how long one page will be shown until the next page.
+ */
+ static const uint32_t DEFAULT_PAGE_TIME = SIMPLE_TIMER_SECONDS(10U);
Fonts::FontType m_fontType; /**< Font type which shall be used if there is no conflict with the layout. */
WidgetGroup m_textCanvas; /**< Canvas used for the text widget. */
From 4baa88be5cb32015adb778d677c34d543e2403b0 Mon Sep 17 00:00:00 2001
From: BlueAndi
Date: Sun, 25 Jun 2023 21:04:10 +0200
Subject: [PATCH 008/105] Bumblebee, github and plug icon added. Copied most of
the icons to /data/configuration to provide them in the filesystem.
---
data/configuration/bumblebee.bmp | Bin 0 -> 246 bytes
data/configuration/earth.bmp | Bin 0 -> 246 bytes
.../web => data/configuration}/github.bmp | Bin
data/configuration/hacker.bmp | Bin 0 -> 246 bytes
data/configuration/lightning.bmp | Bin 0 -> 246 bytes
.../web => data/configuration}/plug.bmp | Bin
data/configuration/santaclaus.bmp | Bin 0 -> 246 bytes
data/configuration/sun.bmp | Bin 0 -> 246 bytes
doc/images/icons/bumblebee.bmp | Bin 0 -> 246 bytes
doc/images/icons/github.bmp | Bin 0 -> 246 bytes
doc/images/icons/plug.bmp | Bin 0 -> 246 bytes
11 files changed, 0 insertions(+), 0 deletions(-)
create mode 100644 data/configuration/bumblebee.bmp
create mode 100644 data/configuration/earth.bmp
rename {lib/GithubPlugin/web => data/configuration}/github.bmp (100%)
create mode 100644 data/configuration/hacker.bmp
create mode 100644 data/configuration/lightning.bmp
rename {lib/ShellyPlugSPlugin/web => data/configuration}/plug.bmp (100%)
create mode 100644 data/configuration/santaclaus.bmp
create mode 100644 data/configuration/sun.bmp
create mode 100644 doc/images/icons/bumblebee.bmp
create mode 100644 doc/images/icons/github.bmp
create mode 100644 doc/images/icons/plug.bmp
diff --git a/data/configuration/bumblebee.bmp b/data/configuration/bumblebee.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..b04e2f10d6981e0716559ffcd35ceacf47c65301
GIT binary patch
literal 246
zcmZ?r{l)+RWp?JB0GZIa`X7!U;t1~2{|sP+ECOUh1c4-k
gjZA_}0E
X5OKH^kO2X3aUd7jd|cu%eOPD!O?4kJ
literal 0
HcmV?d00001
diff --git a/data/configuration/lightning.bmp b/data/configuration/lightning.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..2fd726bcded0d910b4ced7dab35bc4ad8aeed992
GIT binary patch
literal 246
zcmZ?r{l)+RWe1O
y?uH8j8DK6xaiCmsUN2BP1c8*o08kvrgqsXx|NjrP6hy)SOf8T`I0noH$p8Qr^gnX|
literal 0
HcmV?d00001
diff --git a/data/configuration/sun.bmp b/data/configuration/sun.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..aa54ce18a92f0ad492efc7e46a2f4c86ad6caa90
GIT binary patch
literal 246
zcmZ?r{l)+RWzjv#6vT;5af;RvD;
oA`28$-Fgv-fDE`sAOlStVm?G1*&K)#WQ%|z5XEpU5Lq}00F$Ff+W-In
literal 0
HcmV?d00001
diff --git a/doc/images/icons/bumblebee.bmp b/doc/images/icons/bumblebee.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..b04e2f10d6981e0716559ffcd35ceacf47c65301
GIT binary patch
literal 246
zcmZ?r{l)+RWp?JB0GZIa`X7!U;t1~2{|sP+ECOUh1c4-k
gjZA_}0EuGU
literal 0
HcmV?d00001
diff --git a/doc/images/icons/plug.bmp b/doc/images/icons/plug.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..9d40fbb4c1003943e0b3b43e860e0c53b6d09bf3
GIT binary patch
literal 246
zcmZ?r{l)+RW
Date: Sun, 25 Jun 2023 21:05:34 +0200
Subject: [PATCH 009/105] GithubPlugin and ShellyPlugSPlugin replaced by
GrabViaRestPlugin. The new plugin can be configured. For the MQTT the
GrabViaMqttPlugin can be used.
---
config/configNormal.ini | 4 +-
config/configSmall.ini | 4 +-
config/configSmallNoI2s.ini | 4 +-
config/configSmallUlanzi.ini | 6 +-
doc/PLUGINS.md | 43 +-
lib/GithubPlugin/web/GithubPlugin.jpg | Bin 6741 -> 0 bytes
.../library.json | 2 +-
.../src/GrabViaMqttPlugin.cpp | 557 +++++++++++++
.../src/GrabViaMqttPlugin.h} | 778 ++++++++----------
.../web/GrabViaMqttPlugin.html} | 74 +-
.../web/GrabViaMqttPlugin.jpg | Bin 0 -> 15282 bytes
.../library.json | 2 +-
.../src/GrabViaRestPlugin.cpp} | 370 +++++++--
.../src/GrabViaRestPlugin.h} | 108 +--
.../web/GrabViaRestPlugin.html} | 428 +++++-----
.../web/GrabViaRestPlugin.jpg | Bin 0 -> 15282 bytes
.../src/ShellyPlugSPlugin.cpp | 508 ------------
.../web/ShellyPlugSPlugin.jpg | Bin 9258 -> 0 bytes
18 files changed, 1573 insertions(+), 1315 deletions(-)
delete mode 100644 lib/GithubPlugin/web/GithubPlugin.jpg
rename lib/{GithubPlugin => GrabViaMqttPlugin}/library.json (88%)
create mode 100644 lib/GrabViaMqttPlugin/src/GrabViaMqttPlugin.cpp
rename lib/{ShellyPlugSPlugin/src/ShellyPlugSPlugin.h => GrabViaMqttPlugin/src/GrabViaMqttPlugin.h} (65%)
rename lib/{GithubPlugin/web/GithubPlugin.html => GrabViaMqttPlugin/web/GrabViaMqttPlugin.html} (67%)
create mode 100644 lib/GrabViaMqttPlugin/web/GrabViaMqttPlugin.jpg
rename lib/{ShellyPlugSPlugin => GrabViaRestPlugin}/library.json (88%)
rename lib/{GithubPlugin/src/GithubPlugin.cpp => GrabViaRestPlugin/src/GrabViaRestPlugin.cpp} (50%)
rename lib/{GithubPlugin/src/GithubPlugin.h => GrabViaRestPlugin/src/GrabViaRestPlugin.h} (73%)
rename lib/{ShellyPlugSPlugin/web/ShellyPlugSPlugin.html => GrabViaRestPlugin/web/GrabViaRestPlugin.html} (63%)
create mode 100644 lib/GrabViaRestPlugin/web/GrabViaRestPlugin.jpg
delete mode 100644 lib/ShellyPlugSPlugin/src/ShellyPlugSPlugin.cpp
delete mode 100644 lib/ShellyPlugSPlugin/web/ShellyPlugSPlugin.jpg
diff --git a/config/configNormal.ini b/config/configNormal.ini
index 125534c9..b6f3ecea 100644
--- a/config/configNormal.ini
+++ b/config/configNormal.ini
@@ -21,7 +21,8 @@ lib_deps =
DDPPlugin @ ~0.1.0
FirePlugin @ ~0.1.0
GameOfLifePlugin @ ~0.1.0
- GithubPlugin @ ~0.1.0
+ GrabViaMqttPlugin @ ~0.1.0 # Requires MqttService
+ GrabViaRestPlugin @ ~0.1.0
GruenbeckPlugin @ ~0.1.0
IconTextLampPlugin @ ~0.1.0
IconTextPlugin @ ~0.1.0
@@ -30,7 +31,6 @@ lib_deps =
OpenWeatherPlugin @ ~0.1.0
RainbowPlugin @ ~0.1.0
SensorPlugin @ ~0.1.0
- ShellyPlugSPlugin @ ~0.1.0
SignalDetectorPlugin @ ~0.1.0 # Requires AudioService
SoundReactivePlugin @ ~0.1.0 # Requires AudioService
SunrisePlugin @ ~0.1.0
diff --git a/config/configSmall.ini b/config/configSmall.ini
index 725be8d4..3986fa71 100644
--- a/config/configSmall.ini
+++ b/config/configSmall.ini
@@ -21,7 +21,8 @@ lib_deps =
;DDPPlugin @ ~0.1.0
FirePlugin @ ~0.1.0
;GameOfLifePlugin @ ~0.1.0
- GithubPlugin @ ~0.1.0
+ ;GrabViaMqttPlugin @ ~0.1.0 # Requires MqttService
+ GrabViaRestPlugin @ ~0.1.0
;GruenbeckPlugin @ ~0.1.0
IconTextLampPlugin @ ~0.1.0
IconTextPlugin @ ~0.1.0
@@ -30,7 +31,6 @@ lib_deps =
OpenWeatherPlugin @ ~0.1.0
;RainbowPlugin @ ~0.1.0
SensorPlugin @ ~0.1.0
- ShellyPlugSPlugin @ ~0.1.0
;SignalDetectorPlugin @ ~0.1.0 # Requires AudioService
;SoundReactivePlugin @ ~0.1.0 # Requires AudioService
SunrisePlugin @ ~0.1.0
diff --git a/config/configSmallNoI2s.ini b/config/configSmallNoI2s.ini
index a4a6a425..5cb124db 100644
--- a/config/configSmallNoI2s.ini
+++ b/config/configSmallNoI2s.ini
@@ -21,7 +21,8 @@ lib_deps =
;DDPPlugin @ ~0.1.0
FirePlugin @ ~0.1.0
GameOfLifePlugin @ ~0.1.0
- GithubPlugin @ ~0.1.0
+ ;GrabViaMqttPlugin @ ~0.1.0 # Requires MqttService
+ GrabViaRestPlugin @ ~0.1.0
;GruenbeckPlugin @ ~0.1.0
IconTextLampPlugin @ ~0.1.0
IconTextPlugin @ ~0.1.0
@@ -30,7 +31,6 @@ lib_deps =
OpenWeatherPlugin @ ~0.1.0
;RainbowPlugin @ ~0.1.0
SensorPlugin @ ~0.1.0
- ;ShellyPlugSPlugin @ ~0.1.0
;SignalDetectorPlugin @ ~0.1.0 # Requires AudioService
;SoundReactivePlugin @ ~0.1.0 # Requires AudioService
SunrisePlugin @ ~0.1.0
diff --git a/config/configSmallUlanzi.ini b/config/configSmallUlanzi.ini
index 59f4f678..ebe78388 100644
--- a/config/configSmallUlanzi.ini
+++ b/config/configSmallUlanzi.ini
@@ -21,7 +21,8 @@ lib_deps =
;DDPPlugin @ ~0.1.0
FirePlugin @ ~0.1.0
GameOfLifePlugin @ ~0.1.0
- GithubPlugin @ ~0.1.0
+ GrabViaMqttPlugin @ ~0.1.0 # Requires MqttService
+ GrabViaRestPlugin @ ~0.1.0
;GruenbeckPlugin @ ~0.1.0
;IconTextLampPlugin @ ~0.1.0
IconTextPlugin @ ~0.1.0
@@ -30,13 +31,12 @@ lib_deps =
OpenWeatherPlugin @ ~0.1.0
;RainbowPlugin @ ~0.1.0
SensorPlugin @ ~0.1.0
- ;ShellyPlugSPlugin @ ~0.1.0
;SignalDetectorPlugin @ ~0.1.0 # Requires AudioService
;SoundReactivePlugin @ ~0.1.0 # Requires AudioService
SunrisePlugin @ ~0.1.0
SysMsgPlugin @ ~0.1.0 # Mandatory, can not be removed.
TempHumidPlugin @ ~0.1.0
- ThreeIconPlugin @ ~0.1.0
+ ;ThreeIconPlugin @ ~0.1.0
;VolumioPlugin @ ~0.1.0
;WifiStatusPlugin @ ~0.1.0
WormPlugin @ ~0.1.0
diff --git a/doc/PLUGINS.md b/doc/PLUGINS.md
index e7a635cb..6222a816 100644
--- a/doc/PLUGINS.md
+++ b/doc/PLUGINS.md
@@ -8,6 +8,8 @@ The content of the display can be configured by installing an individual set of
Each plugin is identified by its unique UID.
* [Generic plugins](#generic-plugins)
+ * [GrabViaMqttPlugin](#grabviamqttplugin)
+ * [GrabViaRestPlugin](#grabviarestplugin)
* [IconTextPlugin](#icontextplugin)
* [IconTextLampPlugin](#icontextlampplugin)
* [JustTextPlugin](#justtextplugin)
@@ -21,13 +23,11 @@ Each plugin is identified by its unique UID.
* [xlights Configuration](#xlights-configuration)
* [FirePlugin](#fireplugin)
* [GameOfLifePlugin](#gameoflifeplugin)
- * [GithubPlugin](#githubplugin)
* [GruenbeckPlugin](#gruenbeckplugin)
* [MatrixPlugin](#matrixplugin)
* [OpenWeatherPlugin](#openweatherplugin)
* [RainbowPlugin](#rainbowplugin)
* [SensorPlugin](#sensorplugin)
- * [ShellyPlugSPlugin](#shellyplugsplugin)
* [SignalDetectorPlugin](#signaldetectorplugin)
* [SoundReactivePlugin](#soundreactiveplugin)
* [SunrisePlugin](#sunriseplugin)
@@ -44,27 +44,35 @@ Each plugin is identified by its unique UID.
# Generic plugins
The generic plugins allow the user to control the different UI elements described in the plugin name via the [REST API](https://app.swaggerhub.com/apis/BlueAndi/Pixelix/1.3.0).
+## GrabViaMqttPlugin
+The plugin can grab information in JSON format via MQTT and shows it on the display.
+Each part can be set separately via the [REST API](https://app.swaggerhub.com/apis/BlueAndi/Pixelix/1.4.0#/GrabViaMqttPlugin).
+
+## GrabViaRestPlugin
+The plugin can grab information in JSON format via REST API and shows it on the display.
+Each part can be set separately via the [REST API](https://app.swaggerhub.com/apis/BlueAndi/Pixelix/1.4.0#/GrabViaRestPlugin).
+
## IconTextPlugin
The IconTextPlugin shows an icon on left side, text on right side. If no text is set, the plugin will be skipped in the slot.\
-Each part can be set separately via the [REST API](https://app.swaggerhub.com/apis/BlueAndi/Pixelix/1.3.0#/IconTextPlugin).
+Each part can be set separately via the [REST API](https://app.swaggerhub.com/apis/BlueAndi/Pixelix/1.4.0#/IconTextPlugin).
If MQTT is built in and enabled, it will support Home Assistant MQTT discovery.
## IconTextLampPlugin
The IconTextLampPlugin shows an icon on left side, text on right side and lamps at the bottom.\
-Each part can be set separately via the [REST API](https://app.swaggerhub.com/apis/BlueAndi/Pixelix/1.3.0#/IconTextLampPlugin).
+Each part can be set separately via the [REST API](https://app.swaggerhub.com/apis/BlueAndi/Pixelix/1.4.0#/IconTextLampPlugin).
If MQTT is built in and enabled, it will support Home Assistant MQTT discovery.
## JustTextPlugin
The JustTextPlugin shows only text on the whole display. If no text is set, the plugin will be skipped in the slot.\
-The text to be displayed can be set via the [REST API](https://app.swaggerhub.com/apis/BlueAndi/Pixelix/1.3.0#/JustTextPlugin).
+The text to be displayed can be set via the [REST API](https://app.swaggerhub.com/apis/BlueAndi/Pixelix/1.4.0#/JustTextPlugin).
If MQTT is built in and enabled, it will support Home Assistant MQTT discovery.
## ThreeIconPlugin
The ThreeIconPlugin shows three icons on the display.\
-Each icon can be set separately via the [REST API](https://app.swaggerhub.com/apis/BlueAndi/Pixelix/1.3.0#/ThreeIconPlugin).
+Each icon can be set separately via the [REST API](https://app.swaggerhub.com/apis/BlueAndi/Pixelix/1.4.0#/ThreeIconPlugin).
# Dedicated plugins
Dedicated plugins are plugins which only serves one single purpose thy are only internally configurable.
@@ -78,7 +86,7 @@ Powered by [CoinDesk](https://www.coindesk.com/price/bitcoin).
## CountdownPlugin
The CountdownPlugin shows the remaining days until a configured target date.\
-Target date and the description of the target day (plural/singular form) can be set via the [REST API](https://app.swaggerhub.com/apis/BlueAndi/Pixelix/1.3.0#/CountdownPlugin).
+Target date and the description of the target day (plural/singular form) can be set via the [REST API](https://app.swaggerhub.com/apis/BlueAndi/Pixelix/1.4.0#/CountdownPlugin).
## DateTimePlugin
The plugin shows the current time and/or date.
@@ -91,7 +99,7 @@ Configure the date and time format in the plugin configuration JSON file. The fo
By default the local time (see timezone in the settings) is used. It can be overwritten by the plugin configuration.
-It can be set what shall be shown via the [REST API](https://app.swaggerhub.com/apis/BlueAndi/Pixelix/1.3.0#/DateTimePlugin).
+It can be set what shall be shown via the [REST API](https://app.swaggerhub.com/apis/BlueAndi/Pixelix/1.4.0#/DateTimePlugin).
## DDPPlugin
The plugin setup a server supporting the Distributed Display Protocol (DDP), which is used e.g. by [xlights](https://www.xlights.org) or [LedFx](https://www.ledfx.app).
@@ -128,12 +136,9 @@ The FirePlugin shows a animated fire on the display.
## GameOfLifePlugin
The GameOfLifePlugin shows the game of life game on the display.
-## GithubPlugin
-The plugin shows the stargazers count of a github repository.
-
## GruenbeckPlugin
The GruenbeckPlugin shows the remaining system capacity (parameter = D_Y_10_1 ) of the Gruenbeck softliQ SC18 via the system's RESTful webservice.\
-The IP address of the Gruenbeck webserver can be set via the [REST API](https://app.swaggerhub.com/apis/BlueAndi/Pixelix/1.3.0#/GruenbeckPlugin).
+The IP address of the Gruenbeck webserver can be set via the [REST API](https://app.swaggerhub.com/apis/BlueAndi/Pixelix/1.4.0#/GruenbeckPlugin).
## MatrixPlugin
The plugin shows the effect from the film "Matrix" over the whole display.
@@ -142,7 +147,7 @@ The plugin shows the effect from the film "Matrix" over the whole display.
The OpenWeatherPlugin shows the current weather condition (icon and temperature) and one additional information (uvIndex, humidity or windspeed) .\
Information provided by [OpenWeather](https://openweathermap.org/).\
In order to use the plugin an API key is necessary, see https://openweathermap.org/appid for further information.\
-The coordinates (latitude & longitude) of your location, your API key and the desired additional information to be displayed can be set via the [REST API](https://app.swaggerhub.com/apis/BlueAndi/Pixelix/1.3.0#/OpenWeatherPlugin).
+The coordinates (latitude & longitude) of your location, your API key and the desired additional information to be displayed can be set via the [REST API](https://app.swaggerhub.com/apis/BlueAndi/Pixelix/1.4.0#/OpenWeatherPlugin).
## RainbowPlugin
The RainbowPlugin shows an animated rainbow on the display.
@@ -150,26 +155,22 @@ The RainbowPlugin shows an animated rainbow on the display.
## SensorPlugin
The plugin shows sensor values of the selected sensor channel.
-## ShellyPlugSPlugin
-The ShellyPlugSPlugin shows the current AC power being drawn via a Shelly PlugS, in watts.\
-The IP address of the Shelly PlugS webserver can be set via the [REST API](https://app.swaggerhub.com/apis/BlueAndi/Pixelix/1.3.0#/ShellyPlugSPlugin).
-
## SignalDetectorPlugin
The plugin is able to detect a signal, which can be combined with up to 2 frequencies.\
Each frequency must be detected for a specific configureable time.\
As long as nothing is detected, the plugin will disable itself.\
If a signal is detected, it will be shown on the display for the configured slot duration. After slot duration timeout or user changed the slot, the plugin will be disabled until next signal detection. \
Additional a push notification can be configured. By default a GET is triggered. Using "GET" or "POST" as prefix its configureable. Example: "POST http://..."
-Each part can be set separately via the [REST API](https://app.swaggerhub.com/apis/BlueAndi/Pixelix/1.3.0#/SignalDetectorPlugin).
+Each part can be set separately via the [REST API](https://app.swaggerhub.com/apis/BlueAndi/Pixelix/1.4.0#/SignalDetectorPlugin).
## SoundReactivePlugin
The plugin shows octave frequency bands, depended on the environment sound.
Required: A digital microphone (INMP441) is required, connected to the I2S port.
-The number of shown frequency bands can be set via the [REST API](https://app.swaggerhub.com/apis/BlueAndi/Pixelix/1.3.0#/SoundReactivePlugin).
+The number of shown frequency bands can be set via the [REST API](https://app.swaggerhub.com/apis/BlueAndi/Pixelix/1.4.0#/SoundReactivePlugin).
## SunrisePlugin
The SunrisePlugin shows the current sunrise / sunset times for a configured location.\
-The coordinates (latitude & longitude) of your location can be set via the [REST API]([REST.md#endpoint-base-uridisplayuidplugin-uidlocation](https://app.swaggerhub.com/apis/BlueAndi/Pixelix/1.3.0#/SunrisePlugin)).\
+The coordinates (latitude & longitude) of your location can be set via the [REST API]([REST.md#endpoint-base-uridisplayuidplugin-uidlocation](https://app.swaggerhub.com/apis/BlueAndi/Pixelix/1.4.0#/SunrisePlugin)).\
Powered by sunrise-sunset.org
Configure the time format in the plugin configuration JSON file. The format itself is according to strftime(). For colorization text properties can be added.
@@ -186,7 +187,7 @@ The TestPlugin can be used to check whether the LED matrix topology (layout) is
## VolumioPlugin
The VolumioPlugin shows the current VOLUMIO state as icon and the played artist/title.\
If the VOLUMIO server is offline, the plugin gets automatically disabled, otherwise enabled.\
-The host address of the Volumio webserver can be set via the [REST API](https://app.swaggerhub.com/apis/BlueAndi/Pixelix/1.3.0#/VolumioPlugin).
+The host address of the Volumio webserver can be set via the [REST API](https://app.swaggerhub.com/apis/BlueAndi/Pixelix/1.4.0#/VolumioPlugin).
## WifiStatusPlugin
The WifiStatusPlugin shows the current wireless signal strength.
diff --git a/lib/GithubPlugin/web/GithubPlugin.jpg b/lib/GithubPlugin/web/GithubPlugin.jpg
deleted file mode 100644
index 1605cf605986c4d92013a26345934e88b788eba5..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 6741
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
Hf%^XoJ80WC
diff --git a/lib/GithubPlugin/library.json b/lib/GrabViaMqttPlugin/library.json
similarity index 88%
rename from lib/GithubPlugin/library.json
rename to lib/GrabViaMqttPlugin/library.json
index 38605d28..250cd88d 100644
--- a/lib/GithubPlugin/library.json
+++ b/lib/GrabViaMqttPlugin/library.json
@@ -1,5 +1,5 @@
{
- "name": "GithubPlugin",
+ "name": "GrabViaMqttPlugin",
"version": "0.1.0",
"description": "....",
"authors": [{
diff --git a/lib/GrabViaMqttPlugin/src/GrabViaMqttPlugin.cpp b/lib/GrabViaMqttPlugin/src/GrabViaMqttPlugin.cpp
new file mode 100644
index 00000000..19d08395
--- /dev/null
+++ b/lib/GrabViaMqttPlugin/src/GrabViaMqttPlugin.cpp
@@ -0,0 +1,557 @@
+/* MIT License
+ *
+ * Copyright (c) 2019 - 2023 Andreas Merkle
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*******************************************************************************
+ DESCRIPTION
+*******************************************************************************/
+/**
+ * @brief Grab information via REST API plugin
+ * @author Andreas Merkle
+ */
+
+/******************************************************************************
+ * Includes
+ *****************************************************************************/
+#include "GrabViaMqttPlugin.h"
+
+#include
+#include
+#include
+
+/******************************************************************************
+ * Compiler Switches
+ *****************************************************************************/
+
+/******************************************************************************
+ * Macros
+ *****************************************************************************/
+
+/******************************************************************************
+ * Types and classes
+ *****************************************************************************/
+
+/******************************************************************************
+ * Prototypes
+ *****************************************************************************/
+
+/******************************************************************************
+ * Local Variables
+ *****************************************************************************/
+
+/* Initialize plugin topic. */
+const char* GrabViaMqttPlugin::TOPIC_CONFIG = "/config";
+
+/******************************************************************************
+ * Public Methods
+ *****************************************************************************/
+
+void GrabViaMqttPlugin::getTopics(JsonArray& topics) const
+{
+ (void)topics.add(TOPIC_CONFIG);
+}
+
+bool GrabViaMqttPlugin::getTopic(const String& topic, JsonObject& value) const
+{
+ bool isSuccessful = false;
+
+ if (0U != topic.equals(TOPIC_CONFIG))
+ {
+ getConfiguration(value);
+ isSuccessful = true;
+ }
+
+ return isSuccessful;
+}
+
+bool GrabViaMqttPlugin::setTopic(const String& topic, const JsonObject& value)
+{
+ bool isSuccessful = false;
+
+ if (0U != topic.equals(TOPIC_CONFIG))
+ {
+ const size_t JSON_DOC_SIZE = 1024U;
+ DynamicJsonDocument jsonDoc(JSON_DOC_SIZE);
+ JsonObject jsonCfg = jsonDoc.to();
+ JsonVariantConst jsonPath = value["path"];
+ JsonVariantConst jsonFilter = value["filter"];
+ JsonVariantConst jsonIconPath = value["iconPath"];
+ JsonVariantConst jsonFormat = value["format"];
+ JsonVariantConst jsonMultiplier = value["multiplier"];
+ JsonVariantConst jsonOffset = value["offset"];
+
+ /* The received configuration may not contain all single key/value pair.
+ * Therefore read first the complete internal configuration and
+ * overwrite them with the received ones.
+ */
+ getConfiguration(jsonCfg);
+
+ /* Note:
+ * Check only for the key/value pair availability.
+ * The type check will follow in the setConfiguration().
+ */
+
+ if (false == jsonPath.isNull())
+ {
+ jsonCfg["path"] = jsonPath.as();
+ isSuccessful = true;
+ }
+
+ if (false == jsonFilter.isNull())
+ {
+ if (true == jsonFilter.is())
+ {
+ jsonCfg["filter"] = jsonFilter.as();
+ isSuccessful = true;
+ }
+ else if (true == jsonFilter.is())
+ {
+ const size_t JSON_DOC_FILTER_SIZE = 256U;
+ DynamicJsonDocument jsonDocFilter(JSON_DOC_SIZE);
+ DeserializationError result = deserializeJson(jsonDocFilter, jsonFilter.as());
+
+ if (DeserializationError::Ok == result)
+ {
+ jsonCfg["filter"] = jsonDocFilter.as();
+ isSuccessful = true;
+ }
+ }
+ else
+ {
+ ;
+ }
+ }
+
+ if (false == jsonIconPath.isNull())
+ {
+ jsonCfg["iconPath"] = jsonIconPath.as();
+ isSuccessful = true;
+ }
+
+ if (false == jsonFormat.isNull())
+ {
+ jsonCfg["format"] = jsonFormat.as();
+ isSuccessful = true;
+ }
+
+ if (false == jsonMultiplier.isNull())
+ {
+ jsonCfg["multiplier"] = jsonMultiplier.as();
+ isSuccessful = true;
+ }
+
+ if (false == jsonOffset.isNull())
+ {
+ jsonCfg["offset"] = jsonOffset.as();
+ isSuccessful = true;
+ }
+
+ if (true == isSuccessful)
+ {
+ JsonObjectConst jsonCfgConst = jsonCfg;
+
+ isSuccessful = setConfiguration(jsonCfgConst);
+
+ if (true == isSuccessful)
+ {
+ requestStoreToPersistentMemory();
+ }
+ }
+ }
+
+ return isSuccessful;
+}
+
+bool GrabViaMqttPlugin::hasTopicChanged(const String& topic)
+{
+ MutexGuard guard(m_mutex);
+ bool hasTopicChanged = m_hasTopicChanged;
+
+ /* Only a single topic, therefore its not necessary to check. */
+ PLUGIN_NOT_USED(topic);
+
+ m_hasTopicChanged = false;
+
+ return hasTopicChanged;
+}
+
+void GrabViaMqttPlugin::start(uint16_t width, uint16_t height)
+{
+ MutexGuard guard(m_mutex);
+
+ m_layoutLeft.setPosAndSize(0, 0, ICON_WIDTH, ICON_HEIGHT);
+ (void)m_layoutLeft.addWidget(m_iconWidget);
+
+ /* The text canvas is left aligned to the icon canvas and it spans over
+ * the whole display height.
+ */
+ m_layoutRight.setPosAndSize(ICON_WIDTH, 0, width - ICON_WIDTH, height);
+ (void)m_layoutRight.addWidget(m_textWidgetRight);
+
+ /* If only text is used, it will span over the whole display. */
+ m_layoutTextOnly.setPosAndSize(0, 0, width, height);
+ (void)m_layoutTextOnly.addWidget(m_textWidgetTextOnly);
+
+ /* Choose font. */
+ m_textWidgetRight.setFont(Fonts::getFontByType(m_fontType));
+ m_textWidgetTextOnly.setFont(Fonts::getFontByType(m_fontType));
+
+ /* The text widget inside the text canvas is left aligned on x-axis and
+ * aligned to the center of y-axis.
+ */
+ if (height > m_textWidgetRight.getFont().getHeight())
+ {
+ uint16_t diffY = height - m_textWidgetRight.getFont().getHeight();
+ uint16_t offsY = diffY / 2U;
+
+ m_textWidgetRight.move(0, offsY);
+ m_textWidgetTextOnly.move(0, offsY);
+ }
+
+ /* Try to load configuration. If there is no configuration available, a default configuration
+ * will be created.
+ */
+ if (false == loadConfiguration())
+ {
+ if (false == saveConfiguration())
+ {
+ LOG_WARNING("Failed to create initial configuration file %s.", getFullPathToConfiguration().c_str());
+ }
+ }
+ else
+ {
+ /* Remember current timestamp to detect updates of the configuration in the
+ * filesystem without using the plugin API.
+ */
+ updateTimestampLastUpdate();
+ }
+
+ if (false == m_iconPath.isEmpty())
+ {
+ (void)m_iconWidget.load(FILESYSTEM, m_iconPath);
+ }
+
+ m_cfgReloadTimer.start(CFG_RELOAD_PERIOD);
+
+ subscribe();
+}
+
+void GrabViaMqttPlugin::stop()
+{
+ String configurationFilename = getFullPathToConfiguration();
+ MutexGuard guard(m_mutex);
+
+ m_cfgReloadTimer.stop();
+ unsubscribe();
+
+ if (false != FILESYSTEM.remove(configurationFilename))
+ {
+ LOG_INFO("File %s removed", configurationFilename.c_str());
+ }
+}
+
+void GrabViaMqttPlugin::process(bool isConnected)
+{
+ MutexGuard guard(m_mutex);
+
+ /* Configuration in persistent memory updated? */
+ if ((true == m_cfgReloadTimer.isTimerRunning()) &&
+ (true == m_cfgReloadTimer.isTimeout()))
+ {
+ if (true == isConfigurationUpdated())
+ {
+ m_reloadConfigReq = true;
+ }
+
+ m_cfgReloadTimer.restart();
+ }
+
+ if (true == m_storeConfigReq)
+ {
+ if (false == saveConfiguration())
+ {
+ LOG_WARNING("Failed to save configuration: %s", getFullPathToConfiguration().c_str());
+ }
+
+ m_storeConfigReq = false;
+ }
+ else if (true == m_reloadConfigReq)
+ {
+ LOG_INFO("Reload configuration: %s", getFullPathToConfiguration().c_str());
+
+ if (true == loadConfiguration())
+ {
+ updateTimestampLastUpdate();
+ }
+
+ m_reloadConfigReq = false;
+ }
+ else
+ {
+ ;
+ }
+}
+
+void GrabViaMqttPlugin::update(YAGfx& gfx)
+{
+ MutexGuard guard(m_mutex);
+
+ gfx.fillScreen(ColorDef::BLACK);
+
+ /* If a icon is available, the icon/text layout will be used otherwise the text only layout. */
+ if (false == m_iconPath.isEmpty())
+ {
+ m_layoutLeft.update(gfx);
+ m_layoutRight.update(gfx);
+ }
+ else
+ {
+ m_layoutTextOnly.update(gfx);
+ }
+}
+
+/******************************************************************************
+ * Protected Methods
+ *****************************************************************************/
+
+/******************************************************************************
+ * Private Methods
+ *****************************************************************************/
+
+void GrabViaMqttPlugin::requestStoreToPersistentMemory()
+{
+ MutexGuard guard(m_mutex);
+
+ m_storeConfigReq = true;
+}
+
+void GrabViaMqttPlugin::getConfiguration(JsonObject& jsonCfg) const
+{
+ MutexGuard guard(m_mutex);
+
+ jsonCfg["path"] = m_path;
+ jsonCfg["filter"] = m_filter;
+ jsonCfg["iconPath"] = m_iconPath;
+ jsonCfg["format"] = m_format;
+ jsonCfg["multiplier"] = m_multiplier;
+ jsonCfg["offset"] = m_offset;
+}
+
+bool GrabViaMqttPlugin::setConfiguration(JsonObjectConst& jsonCfg)
+{
+ bool status = false;
+ JsonVariantConst jsonPath = jsonCfg["path"];
+ JsonVariantConst jsonFilter = jsonCfg["filter"];
+ JsonVariantConst jsonIconPath = jsonCfg["iconPath"];
+ JsonVariantConst jsonFormat = jsonCfg["format"];
+ JsonVariantConst jsonMultiplier = jsonCfg["multiplier"];
+ JsonVariantConst jsonOffset = jsonCfg["offset"];
+
+ if (false == jsonPath.is())
+ {
+ LOG_WARNING("JSON path not found or invalid type.");
+ }
+ else if (false == jsonFilter.is())
+ {
+ LOG_WARNING("JSON filter not found or invalid type.");
+ }
+ else if (false == jsonIconPath.is())
+ {
+ LOG_WARNING("JSON icon path not found or invalid type.");
+ }
+ else if (false == jsonFormat.is())
+ {
+ LOG_WARNING("JSON format not found or invalid type.");
+ }
+ else if (false == jsonMultiplier.is())
+ {
+ LOG_WARNING("JSON multiplier not found or invalid type.");
+ }
+ else if (false == jsonOffset.is())
+ {
+ LOG_WARNING("JSON offset not found or invalid type.");
+ }
+ else
+ {
+ bool reqInit = false;
+ bool reqIcon = false;
+ MutexGuard guard(m_mutex);
+
+ if (m_path != jsonPath.as())
+ {
+ unsubscribe();
+ reqInit = true;
+ }
+
+ if (m_iconPath != jsonIconPath.as())
+ {
+ reqIcon = true;
+ }
+
+ m_path = jsonPath.as();
+ m_filter = jsonFilter.as();
+ m_iconPath = jsonIconPath.as();
+ m_format = jsonFormat.as();
+ m_multiplier = jsonMultiplier.as();
+ m_offset = jsonOffset.as();
+
+ if (true == reqInit)
+ {
+ subscribe();
+ }
+
+ /* Load icon immediately */
+ if (true == reqIcon)
+ {
+ if (true == m_iconPath.endsWith(".sprite"))
+ {
+ String textureFileName = m_iconPath;
+
+ textureFileName.replace(".sprite", ".bmp");
+
+ if (false == m_iconWidget.loadSpriteSheet(FILESYSTEM, m_iconPath, textureFileName))
+ {
+ LOG_WARNING("Failed to load animation %s / %s.", m_iconPath.c_str(), textureFileName.c_str());
+ }
+ }
+ else if (true == m_iconPath.endsWith(".bmp"))
+ {
+ if (false == m_iconWidget.load(FILESYSTEM, m_iconPath))
+ {
+ LOG_WARNING("Failed to load bitmap %s.", m_iconPath.c_str());
+ }
+ }
+ else
+ {
+ m_iconWidget.clear(ColorDef::BLACK);
+ }
+ }
+
+ m_hasTopicChanged = true;
+
+ status = true;
+ }
+
+ return status;
+}
+
+void GrabViaMqttPlugin::getJsonValueByFilter(JsonObjectConst src, JsonObjectConst filter, JsonVariantConst& value)
+{
+ for (JsonPairConst pair : filter)
+ {
+ if (true == pair.value().is())
+ {
+ getJsonValueByFilter(src[pair.key()], filter[pair.key()], value);
+ }
+ else
+ {
+ value = src[pair.key()];
+ }
+
+ /* Break immediately as its assumed that the filter only contains one
+ * single object.
+ */
+ break;
+ }
+}
+
+void GrabViaMqttPlugin::subscribe()
+{
+ MqttService& mqttService = MqttService::getInstance();
+
+ if (false == m_path.isEmpty())
+ {
+ (void)mqttService.subscribe(m_path,
+ [this](const String& topic, const uint8_t* payload, size_t size)
+ {
+ this->mqttTopicCallback(topic, payload, size);
+ });
+ }
+}
+
+void GrabViaMqttPlugin::unsubscribe()
+{
+ MqttService& mqttService = MqttService::getInstance();
+
+ if (false == m_path.isEmpty())
+ {
+ mqttService.unsubscribe(m_path);
+ }
+}
+
+void GrabViaMqttPlugin::mqttTopicCallback(const String& topic, const uint8_t* payload, size_t size)
+{
+ const size_t JSON_DOC_SIZE = 1024U;
+ DynamicJsonDocument jsonDoc(JSON_DOC_SIZE);
+ DeserializationError error = deserializeJson(jsonDoc, payload, size);
+
+ if (DeserializationError::Ok != error)
+ {
+ LOG_WARNING("MQTT payload contains invalid JSON.");
+ }
+ else
+ {
+ JsonVariantConst jsonValue;
+
+ getJsonValueByFilter(jsonDoc.as(), m_filter.as(), jsonValue);
+
+ /* Is it a number? */
+ if (true == jsonValue.is())
+ {
+ const size_t BUFFER_SIZE = 128U;
+ char buffer[BUFFER_SIZE];
+ float value = jsonValue.as();
+
+ value *= m_multiplier;
+ value += m_offset;
+
+ (void)snprintf(buffer, sizeof(buffer), m_format.c_str(), value);
+
+ m_textWidgetRight.setFormatStr(buffer);
+ m_textWidgetTextOnly.setFormatStr(buffer);
+ }
+ /* Is it a string? */
+ else if (true == jsonValue.is())
+ {
+ const size_t BUFFER_SIZE = 40U;
+ char buffer[BUFFER_SIZE];
+
+ (void)snprintf(buffer, sizeof(buffer), m_format.c_str(), jsonValue.as().c_str());
+
+ m_textWidgetRight.setFormatStr(buffer);
+ m_textWidgetTextOnly.setFormatStr(buffer);
+ }
+ else
+ {
+ m_textWidgetRight.setFormatStr("\\calign-");
+ m_textWidgetTextOnly.setFormatStr("\\calign-");
+ }
+ }
+}
+
+/******************************************************************************
+ * External Functions
+ *****************************************************************************/
+
+/******************************************************************************
+ * Local Functions
+ *****************************************************************************/
diff --git a/lib/ShellyPlugSPlugin/src/ShellyPlugSPlugin.h b/lib/GrabViaMqttPlugin/src/GrabViaMqttPlugin.h
similarity index 65%
rename from lib/ShellyPlugSPlugin/src/ShellyPlugSPlugin.h
rename to lib/GrabViaMqttPlugin/src/GrabViaMqttPlugin.h
index b6a27cd4..d4596eef 100644
--- a/lib/ShellyPlugSPlugin/src/ShellyPlugSPlugin.h
+++ b/lib/GrabViaMqttPlugin/src/GrabViaMqttPlugin.h
@@ -1,415 +1,363 @@
-/* MIT License
- *
- * Copyright (c) 2019 - 2023 Andreas Merkle
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-/*******************************************************************************
- DESCRIPTION
-*******************************************************************************/
-/**
- * @brief ShellyPlugSPlugin plugin
- * @author Yann Le Glaz
-
- *
- * @addtogroup plugin
- *
- * @{
- */
-
-#ifndef SHELLYPLUGSPLUGIN_H
-#define SHELLYPLUGSPLUGIN_H
-
-/******************************************************************************
- * Compile Switches
- *****************************************************************************/
-
-/******************************************************************************
- * Includes
- *****************************************************************************/
-#include "AsyncHttpClient.h"
-#include "Plugin.hpp"
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-/******************************************************************************
- * Macros
- *****************************************************************************/
-
-/******************************************************************************
- * Types and Classes
- *****************************************************************************/
-
-/**
- * Shows the current AC power being drawn via a Shelly PlugS, in watts.
- */
-class ShellyPlugSPlugin : public Plugin, private PluginConfigFsHandler
-{
-public:
-
- /**
- * Constructs the plugin.
- *
- * @param[in] name Plugin name
- * @param[in] uid Unique id
- */
- ShellyPlugSPlugin(const String& name, uint16_t uid) :
- Plugin(name, uid),
- PluginConfigFsHandler(uid, FILESYSTEM),
- m_fontType(Fonts::FONT_TYPE_DEFAULT),
- m_textCanvas(),
- m_iconCanvas(),
- m_bitmapWidget(),
- m_textWidget("?"),
- m_ipAddress("192.168.1.123"), /* Example data */
- m_client(),
- m_mutex(),
- m_requestTimer(),
- m_cfgReloadTimer(),
- m_storeConfigReq(false),
- m_reloadConfigReq(false),
- m_hasTopicChanged(false),
- m_taskProxy()
- {
- (void)m_mutex.create();
- }
-
- /**
- * Destroys the plugin.
- */
- ~ShellyPlugSPlugin()
- {
- m_client.regOnResponse(nullptr);
- m_client.regOnClosed(nullptr);
- m_client.regOnError(nullptr);
-
- /* Abort any pending TCP request to avoid getting a callback after the
- * object is destroyed.
- */
- m_client.end();
-
- clearQueue();
-
- m_mutex.destroy();
- }
-
- /**
- * Plugin creation method, used to register on the plugin manager.
- *
- * @param[in] name Plugin name
- * @param[in] uid Unique id
- *
- * @return If successful, it will return the pointer to the plugin instance, otherwise nullptr.
- */
- static IPluginMaintenance* create(const String& name, uint16_t uid)
- {
- return new ShellyPlugSPlugin(name, uid);
- }
-
- /**
- * Get font type.
- *
- * @return The font type the plugin uses.
- */
- Fonts::FontType getFontType() const final
- {
- return m_fontType;
- }
-
- /**
- * Set font type.
- * The plugin may skip the font type in case it gets conflicts with the layout.
- *
- * A font type change will only be considered if it is set before the start()
- * method is called!
- *
- * @param[in] fontType The font type which the plugin shall use.
- */
- void setFontType(Fonts::FontType fontType) final
- {
- m_fontType = fontType;
- return;
- }
-
- /**
- * Get plugin topics, which can be get/set via different communication
- * interfaces like REST, websocket, MQTT, etc.
- *
- * Example:
- * {
- * "topics": [
- * "/text"
- * ]
- * }
- *
- * By default a topic is readable and writeable.
- * This can be set explicit with the "access" key with the following possible
- * values:
- * - Only readable: "r"
- * - Only writeable: "w"
- * - Readable and writeable: "rw"
- *
- * Example:
- * {
- * "topics": [{
- * "name": "/text",
- * "access": "r"
- * }]
- * }
- *
- * @param[out] topics Topis in JSON format
- */
- void getTopics(JsonArray& topics) const final;
-
- /**
- * Get a topic data.
- * Note, currently only JSON format is supported.
- *
- * @param[in] topic The topic which data shall be retrieved.
- * @param[out] value The topic value in JSON format.
- *
- * @return If successful it will return true otherwise false.
- */
- bool getTopic(const String& topic, JsonObject& value) const final;
-
- /**
- * Set a topic data.
- * Note, currently only JSON format is supported.
- *
- * @param[in] topic The topic which data shall be retrieved.
- * @param[in] value The topic value in JSON format.
- *
- * @return If successful it will return true otherwise false.
- */
- bool setTopic(const String& topic, const JsonObject& value) final;
-
- /**
- * Is the topic content changed since last time?
- * Every readable volatile topic shall support this. Otherwise the topic
- * handlers might not be able to provide updated information.
- *
- * @param[in] topic The topic which to check.
- *
- * @return If the topic content changed since last time, it will return true otherwise false.
- */
- bool hasTopicChanged(const String& topic) final;
-
- /**
- * Start the plugin. This is called only once during plugin lifetime.
- * It can be used as deferred initialization (after the constructor)
- * and provides the canvas size.
- *
- * If your display layout depends on canvas or font size, calculate it
- * here.
- *
- * Overwrite it if your plugin needs to know that it was installed.
- *
- * @param[in] width Display width in pixel
- * @param[in] height Display height in pixel
- */
- void start(uint16_t width, uint16_t height) final;
-
- /**
- * Stop the plugin. This is called only once during plugin lifetime.
- * It can be used as a first clean-up, before the plugin will be destroyed.
- *
- * Overwrite it if your plugin needs to know that it will be uninstalled.
- */
- void stop() final;
-
- /**
- * Process the plugin.
- * Overwrite it if your plugin has cyclic stuff to do without being in a
- * active slot.
- *
- * @param[in] isConnected The network connection status. If network
- * connection is established, it will be true otherwise false.
- */
- void process(bool isConnected) final;
-
- /**
- * Update the display.
- * The scheduler will call this method periodically.
- *
- * @param[in] gfx Display graphics interface
- */
- void update(YAGfx& gfx) final;
-
- /**
- * Get ip-address.
- *
- * @return IP-address
- */
- String getIPAddress() const;
-
- /**
- * Set ip-address.
- *
- * @param[in] ipAddress IP-address
- */
- void setIPAddress(const String& ipAddress);
-
-private:
-
- /**
- * Icon width in pixels.
- */
- static const int16_t ICON_WIDTH = 8;
-
- /**
- * Icon height in pixels.
- */
- static const int16_t ICON_HEIGHT = 8;
-
- /**
- * Image path within the filesystem.
- */
- static const char* IMAGE_PATH;
-
- /**
- * Plugin topic, used to read/write the configuration.
- */
- static const char* TOPIC_CONFIG;
-
- /**
- * Period in ms for requesting power consumption from the Shelly PlugS.
- * This is used in case the last request to the server was successful.
- */
- static const uint32_t UPDATE_PERIOD = SIMPLE_TIMER_SECONDS(15U);
-
- /**
- * Short period in ms for requesting power consumption from the Shelly PlugS.
- * This is used in case the request to the server failed.
- */
- static const uint32_t UPDATE_PERIOD_SHORT = SIMPLE_TIMER_SECONDS(10U);
-
- /**
- * The configuration in the persistent memory shall be cyclic loaded.
- * This mechanism ensure that manual changes in the file are considered.
- * This is the reload period in ms.
- */
- static const uint32_t CFG_RELOAD_PERIOD = SIMPLE_TIMER_SECONDS(30U);
-
- Fonts::FontType m_fontType; /**< Font type which shall be used if there is no conflict with the layout. */
- WidgetGroup m_textCanvas; /**< Canvas used for the text widget. */
- WidgetGroup m_iconCanvas; /**< Canvas used for the bitmap widget. */
- BitmapWidget m_bitmapWidget; /**< Bitmap widget, used to show the icon. */
- TextWidget m_textWidget; /**< Text widget, used for showing the text. */
- String m_ipAddress; /**< IP-address of the ShellyPlugS server. */
- AsyncHttpClient m_client; /**< Asynchronous HTTP client. */
- mutable MutexRecursive m_mutex; /**< Mutex to protect against concurrent access. */
- SimpleTimer m_requestTimer; /**< Timer is used for cyclic ShellyPlugS http request. */
- SimpleTimer m_cfgReloadTimer; /**< Timer is used to cyclic reload the configuration from persistent memory. */
- bool m_storeConfigReq; /**< Is requested to store the configuration in persistent memory? */
- bool m_reloadConfigReq; /**< Is requested to reload the configuration from persistent memory? */
- bool m_hasTopicChanged; /**< Has the topic content changed? */
-
- /**
- * Defines the message types, which are necessary for HTTP client/server handling.
- */
- enum MsgType
- {
- MSG_TYPE_INVALID = 0, /**< Invalid message type. */
- MSG_TYPE_RSP /**< A response, caused by a previous request. */
- };
-
- /**
- * A message for HTTP client/server handling.
- */
- struct Msg
- {
- MsgType type; /**< Message type */
- DynamicJsonDocument* rsp; /**< Response, only valid if message type is a response. */
-
- /**
- * Constructs a message.
- */
- Msg() :
- type(MSG_TYPE_INVALID),
- rsp(nullptr)
- {
- }
- };
-
- /**
- * Task proxy used to decouple server responses, which happen in a different task context.
- */
- TaskProxy m_taskProxy;
-
- /**
- * Request to store configuration to persistent memory.
- */
- void requestStoreToPersistentMemory();
-
- /**
- * Get configuration in JSON.
- *
- * @param[out] cfg Configuration
- */
- void getConfiguration(JsonObject& cfg) const final;
-
- /**
- * Set configuration in JSON.
- *
- * @param[in] cfg Configuration
- *
- * @return If successful set, it will return true otherwise false.
- */
- bool setConfiguration(JsonObjectConst& cfg) final;
-
- /**
- * Request new data.
- *
- * @return If successful it will return true otherwise false.
- */
- bool startHttpRequest(void);
-
- /**
- * Register callback function on response reception.
- */
- void initHttpClient(void);
-
- /**
- * Handle a web response from the server.
- *
- * @param[in] jsonDoc Web response as JSON document
- */
- void handleWebResponse(DynamicJsonDocument& jsonDoc);
-
- /**
- * Clear the task proxy queue.
- */
- void clearQueue();
-};
-
-/******************************************************************************
- * Functions
- *****************************************************************************/
-
-#endif /* SHELLYPLUGSPLUGIN_H */
-
-/** @} */
\ No newline at end of file
+/* MIT License
+ *
+ * Copyright (c) 2019 - 2023 Andreas Merkle
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*******************************************************************************
+ DESCRIPTION
+*******************************************************************************/
+/**
+ * @brief Grab information via REST API plugin
+ * @author Andreas Merkle
+ *
+ * @addtogroup plugin
+ *
+ * @{
+ */
+
+#ifndef GRAB_VIA_REST_PLUGIN_H
+#define GRAB_VIA_REST_PLUGIN_H
+
+/******************************************************************************
+ * Compile Switches
+ *****************************************************************************/
+
+/******************************************************************************
+ * Includes
+ *****************************************************************************/
+#include
+#include "Plugin.hpp"
+
+#include
+#include
+#include
+#include
+#include
+
+/******************************************************************************
+ * Macros
+ *****************************************************************************/
+
+/******************************************************************************
+ * Types and Classes
+ *****************************************************************************/
+
+/**
+ * Grab information from a MQTT broker and display it.
+ */
+class GrabViaMqttPlugin : public Plugin, private PluginConfigFsHandler
+{
+public:
+
+ /**
+ * Constructs the plugin.
+ *
+ * @param[in] name Plugin name
+ * @param[in] uid Unique id
+ */
+ GrabViaMqttPlugin(const String& name, uint16_t uid) :
+ Plugin(name, uid),
+ PluginConfigFsHandler(uid, FILESYSTEM),
+ m_fontType(Fonts::FONT_TYPE_DEFAULT),
+ m_layoutRight(),
+ m_layoutLeft(),
+ m_layoutTextOnly(),
+ m_iconWidget(),
+ m_textWidgetRight("\\calign?"),
+ m_textWidgetTextOnly("\\calign?"),
+ m_path(),
+ m_filter(1024U),
+ m_iconPath(),
+ m_format("%s"),
+ m_multiplier(1.0f),
+ m_offset(0.0f),
+ m_mutex(),
+ m_cfgReloadTimer(),
+ m_storeConfigReq(false),
+ m_reloadConfigReq(false),
+ m_hasTopicChanged(false)
+ {
+ (void)m_mutex.create();
+ }
+
+ /**
+ * Destroys the plugin.
+ */
+ ~GrabViaMqttPlugin()
+ {
+ m_mutex.destroy();
+ }
+
+ /**
+ * Plugin creation method, used to register on the plugin manager.
+ *
+ * @param[in] name Plugin name
+ * @param[in] uid Unique id
+ *
+ * @return If successful, it will return the pointer to the plugin instance, otherwise nullptr.
+ */
+ static IPluginMaintenance* create(const String& name, uint16_t uid)
+ {
+ return new GrabViaMqttPlugin(name, uid);
+ }
+
+ /**
+ * Get font type.
+ *
+ * @return The font type the plugin uses.
+ */
+ Fonts::FontType getFontType() const final
+ {
+ return m_fontType;
+ }
+
+ /**
+ * Set font type.
+ * The plugin may skip the font type in case it gets conflicts with the layout.
+ *
+ * A font type change will only be considered if it is set before the start()
+ * method is called!
+ *
+ * @param[in] fontType The font type which the plugin shall use.
+ */
+ void setFontType(Fonts::FontType fontType) final
+ {
+ m_fontType = fontType;
+ return;
+ }
+
+ /**
+ * Get plugin topics, which can be get/set via different communication
+ * interfaces like REST, websocket, MQTT, etc.
+ *
+ * Example:
+ * {
+ * "topics": [
+ * "/text"
+ * ]
+ * }
+ *
+ * By default a topic is readable and writeable.
+ * This can be set explicit with the "access" key with the following possible
+ * values:
+ * - Only readable: "r"
+ * - Only writeable: "w"
+ * - Readable and writeable: "rw"
+ *
+ * Example:
+ * {
+ * "topics": [{
+ * "name": "/text",
+ * "access": "r"
+ * }]
+ * }
+ *
+ * @param[out] topics Topis in JSON format
+ */
+ void getTopics(JsonArray& topics) const final;
+
+ /**
+ * Get a topic data.
+ * Note, currently only JSON format is supported.
+ *
+ * @param[in] topic The topic which data shall be retrieved.
+ * @param[out] value The topic value in JSON format.
+ *
+ * @return If successful it will return true otherwise false.
+ */
+ bool getTopic(const String& topic, JsonObject& value) const final;
+
+ /**
+ * Set a topic data.
+ * Note, currently only JSON format is supported.
+ *
+ * @param[in] topic The topic which data shall be retrieved.
+ * @param[in] value The topic value in JSON format.
+ *
+ * @return If successful it will return true otherwise false.
+ */
+ bool setTopic(const String& topic, const JsonObject& value) final;
+
+ /**
+ * Is the topic content changed since last time?
+ * Every readable volatile topic shall support this. Otherwise the topic
+ * handlers might not be able to provide updated information.
+ *
+ * @param[in] topic The topic which to check.
+ *
+ * @return If the topic content changed since last time, it will return true otherwise false.
+ */
+ bool hasTopicChanged(const String& topic) final;
+
+ /**
+ * Start the plugin. This is called only once during plugin lifetime.
+ * It can be used as deferred initialization (after the constructor)
+ * and provides the canvas size.
+ *
+ * If your display layout depends on canvas or font size, calculate it
+ * here.
+ *
+ * Overwrite it if your plugin needs to know that it was installed.
+ *
+ * @param[in] width Display width in pixel
+ * @param[in] height Display height in pixel
+ */
+ void start(uint16_t width, uint16_t height) final;
+
+ /**
+ * Stop the plugin. This is called only once during plugin lifetime.
+ * It can be used as a first clean-up, before the plugin will be destroyed.
+ *
+ * Overwrite it if your plugin needs to know that it will be uninstalled.
+ */
+ void stop() final;
+
+ /**
+ * Process the plugin.
+ * Overwrite it if your plugin has cyclic stuff to do without being in a
+ * active slot.
+ *
+ * @param[in] isConnected The network connection status. If network
+ * connection is established, it will be true otherwise false.
+ */
+ void process(bool isConnected) final;
+
+ /**
+ * Update the display.
+ * The scheduler will call this method periodically.
+ *
+ * @param[in] gfx Display graphics interface
+ */
+ void update(YAGfx& gfx) final;
+
+private:
+
+ /**
+ * Icon width in pixels.
+ */
+ static const uint16_t ICON_WIDTH = 8U;
+
+ /**
+ * Icon height in pixels.
+ */
+ static const uint16_t ICON_HEIGHT = 8U;
+
+ /**
+ * Plugin topic, used to read/write the configuration.
+ */
+ static const char* TOPIC_CONFIG;
+
+ /**
+ * The configuration in the persistent memory shall be cyclic loaded.
+ * This mechanism ensure that manual changes in the file are considered.
+ * This is the reload period in ms.
+ */
+ static const uint32_t CFG_RELOAD_PERIOD = SIMPLE_TIMER_SECONDS(30U);
+
+ Fonts::FontType m_fontType; /**< Font type which shall be used if there is no conflict with the layout. */
+ WidgetGroup m_layoutRight; /**< Canvas used for the text widget in a layout with icon on the left side. */
+ WidgetGroup m_layoutLeft; /**< Canvas used for the bitmap widget in a layout with text on the right side. */
+ WidgetGroup m_layoutTextOnly; /**< Canvas used in case only text is shown. */
+ BitmapWidget m_iconWidget; /**< Bitmap widget, used to show the icon. */
+ TextWidget m_textWidgetRight; /**< Text widget, used in layout with icon. */
+ TextWidget m_textWidgetTextOnly; /**< Text widget, used in layout without icon. */
+ String m_path; /**< MQTT topic path */
+ DynamicJsonDocument m_filter; /**< Filter used for the response in JSON format. */
+ String m_iconPath; /**< Icon filename with path. */
+ String m_format; /**< Format used to embed the retrieved filtered value. */
+ float m_multiplier; /**< If grabbed value is a number, it will be multiplied with the multiplier. */
+ float m_offset; /**< If grabbed value is a number, the offset will be added after the multiplication with the multiplier. */
+ mutable MutexRecursive m_mutex; /**< Mutex to protect against concurrent access. */
+ SimpleTimer m_cfgReloadTimer; /**< Timer is used to cyclic reload the configuration from persistent memory. */
+ bool m_storeConfigReq; /**< Is requested to store the configuration in persistent memory? */
+ bool m_reloadConfigReq; /**< Is requested to reload the configuration from persistent memory? */
+ bool m_hasTopicChanged; /**< Has the topic content changed? */
+
+ /**
+ * Request to store configuration to persistent memory.
+ */
+ void requestStoreToPersistentMemory();
+
+ /**
+ * Get configuration in JSON.
+ *
+ * @param[out] cfg Configuration
+ */
+ void getConfiguration(JsonObject& cfg) const final;
+
+ /**
+ * Set configuration in JSON.
+ *
+ * @param[in] cfg Configuration
+ *
+ * @return If successful set, it will return true otherwise false.
+ */
+ bool setConfiguration(JsonObjectConst& cfg) final;
+
+ /**
+ * Request new data.
+ *
+ * @return If successful it will return true otherwise false.
+ */
+ bool startHttpRequest(void);
+
+ /**
+ * Get value from JSON source by the filter.
+ *
+ * @param[in] src Source in JSON format
+ * @param[in] filter Filter in JSON format
+ * @param[out] value Value in JSON format
+ */
+ void getJsonValueByFilter(JsonObjectConst src, JsonObjectConst filter, JsonVariantConst& value);
+
+ /**
+ * Clear the task proxy queue.
+ */
+ void clearQueue();
+
+ /**
+ * Subscribe MQTT topic to be informed about value changes.
+ */
+ void subscribe();
+
+ /**
+ * Unsubscribe MQTT topic to stop on change notifications.
+ */
+ void unsubscribe();
+
+ /**
+ * The MQTT callback is registered by subscription and will be called on change by
+ * the MQTT service.
+ *
+ * @param[in] topic Topic
+ * @param[in] payload Topic payload
+ * @param[in] size Topic payload size in byte
+ */
+ void mqttTopicCallback(const String& topic, const uint8_t* payload, size_t size);
+};
+
+/******************************************************************************
+ * Functions
+ *****************************************************************************/
+
+#endif /* GRAB_VIA_REST_PLUGIN_H */
+
+/** @} */
diff --git a/lib/GithubPlugin/web/GithubPlugin.html b/lib/GrabViaMqttPlugin/web/GrabViaMqttPlugin.html
similarity index 67%
rename from lib/GithubPlugin/web/GithubPlugin.html
rename to lib/GrabViaMqttPlugin/web/GrabViaMqttPlugin.html
index 98c0de08..bea11250 100644
--- a/lib/GithubPlugin/web/GithubPlugin.html
+++ b/lib/GrabViaMqttPlugin/web/GrabViaMqttPlugin.html
@@ -32,20 +32,24 @@
-
GithubPlugin
-
-
The plugin shows the stargazers count of a github repository.
+
GrabViaMqttPlugin
+
+
The plugin can grab information in JSON format via MQTT and shows it on the display.
REST API
-
Get github user and repository name
-
GET {{ORIGIN}}/rest/api/v1/display/uid/<PLUGIN-UID>/github
-
Set github user and/or repository name
-
POST {{ORIGIN}}/rest/api/v1/display/uid/<PLUGIN-UID>/github?user=<USER>&repository=<REPOSITORY>
-
POST {{ORIGIN}}/rest/api/v1/display/alias/<PLUGIN-ALIAS>/github?user=<USER>&repository=<REPOSITORY>
+
Get configuration
+
GET {{ORIGIN}}/rest/api/v1/display/uid/<PLUGIN-UID>/config
+
Set configuration
+
POST {{ORIGIN}}/rest/api/v1/display/uid/<PLUGIN-UID>/config?path=<PATH>&filter=<FILTER>&iconPath=<ICON-PATH>&format=<FORMAT>&MULTIPLIER=<MULTIPLIER>&OFFSET=<OFFSET>
+
POST {{ORIGIN}}/rest/api/v1/display/alias/<PLUGIN-ALIAS>/config?path=<PATH>&filter=<FILTER>&iconPath=<ICON-PATH>&format=<FORMAT>&MULTIPLIER=<MULTIPLIER>&OFFSET=<OFFSET>
PLUGIN-UID: The plugin unique id.
PLUGIN-ALIAS: The plugin alias name.
-
USER: The name of the user who owns the repository.
-
REPOSITORY: The name of the repository.
+
PATH: MQTT topic path.
+
FILTER: Filter to identify the value in the JSON response.
+
ICON-PATH: Filename of the icon including the path.
+
FORMAT: Format specifier, e.g. "%s" for strings or "%f" for numbers.
+
MULTIPLIER: Number which is multiplied with a number value. Not used for string values.
+
OFFSET: Number which is added to a number value. Not used for string values.
Configuration
User
@@ -56,15 +60,31 @@
User
-
-
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
@@ -90,7 +110,7 @@
User
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
GrabViaRestPlugin
+
+
The plugin can grab information in JSON format via REST API and shows it on the display.
+
REST API
+
Get configuration
+
GET {{ORIGIN}}/rest/api/v1/display/uid/<PLUGIN-UID>/config
+
Set configuration
+
POST {{ORIGIN}}/rest/api/v1/display/uid/<PLUGIN-UID>/config?method=<METHOD>&url=<URL>&filter=<FILTER>&iconPath=<ICON-PATH>&format=<FORMAT>&MULTIPLIER=<MULTIPLIER>&OFFSET=<OFFSET>
+
POST {{ORIGIN}}/rest/api/v1/display/alias/<PLUGIN-ALIAS>/config?method=<METHOD>&url=<URL>&filter=<FILTER>&iconPath=<ICON-PATH>&format=<FORMAT>&MULTIPLIER=<MULTIPLIER>&OFFSET=<OFFSET>
+
+
PLUGIN-UID: The plugin unique id.
+
PLUGIN-ALIAS: The plugin alias name.
+
METHOD: HTTP method, supported are "GET" and "POST".
+
URL: REST API URL.
+
FILTER: Filter to identify the value in the JSON response.
+
ICON-PATH: Filename of the icon including the path.
+
FORMAT: Format specifier, e.g. "%s" for strings or "%f" for numbers.
+
MULTIPLIER: Number which is multiplied with a number value. Not used for string values.
+
OFFSET: Number which is added to a number value. Not used for string values.