1818#include " SinricProUDP.h"
1919#include " SinricProWebsocket.h"
2020#include " Timestamp.h"
21+ #include " EventLimiter.h"
2122namespace SINRICPRO_NAMESPACE {
2223
2324/* *
@@ -108,6 +109,7 @@ class SinricProClass : public SinricProInterface {
108109 void onOTAUpdate (OTAUpdateCallbackHandler cb);
109110 void onSetSetting (SetSettingCallbackHandler cb);
110111 void onReportHealth (ReportHealthCallbackHandler cb);
112+ bool sendSettingEvent (String settingId, SettingValue settingValue, String cause = FSTR_SINRICPRO_PHYSICAL_INTERACTION );
111113
112114 protected:
113115 template <typename DeviceType>
@@ -162,6 +164,7 @@ class SinricProClass : public SinricProInterface {
162164 String responseMessageStr = " " ;
163165
164166 SinricProModuleCommandHandler _moduleCommandHandler;
167+ EventLimiter _settingEventLimiter{EVENT_LIMIT_STATE };
165168};
166169
167170class SinricProClass ::Proxy {
@@ -225,12 +228,27 @@ DeviceType& SinricProClass::getDeviceInstance(String deviceId) {
225228 **/
226229void SinricProClass::begin (String appKey, String appSecret, String serverURL) {
227230 bool success = true ;
228- if (!appKey.length ()) {
229- DEBUG_SINRIC (" [SinricPro:begin()]: App-Key \" %s\" is invalid!! Please check your app-key!! SinricPro will not work!\r\n " , appKey.c_str ());
231+
232+ // Validate APP_KEY
233+ // - Must be 36 characters
234+ // - UUID format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
235+ if (appKey.length () != 36 ) {
236+ Serial.printf (" [SinricPro:begin()]: Invalid App Key '%s' detected (Length: %d; Expected: 36). Initialization aborted! Please check your SinricPro credentials.\r\n " , appKey.c_str (), appKey.length ());
237+ success = false ;
238+ } else if (appKey.charAt (8 ) != ' -' || appKey.charAt (13 ) != ' -' || appKey.charAt (18 ) != ' -' || appKey.charAt (23 ) != ' -' ) {
239+ Serial.printf (" [SinricPro:begin()]: App Key '%s' is in an invalid format. Initialization aborted!\r\n " , appKey.c_str ());
230240 success = false ;
231241 }
232- if (!appSecret.length ()) {
233- DEBUG_SINRIC (" [SinricPro:begin()]: App-Secret \" %s\" is invalid!! Please check your app-secret!! SinricPro will not work!\r\n " , appSecret.c_str ());
242+
243+ // Validate APP_SECRET (73 characters)
244+ // - Must be 73 characters
245+ // - Double UUID format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
246+ if (appSecret.length () != 73 ) {
247+ Serial.printf (" [SinricPro:begin()]: Invalid App Secret '%s' detected (Length: %d; Expected: 73). Initialization aborted! Please check your SinricPro credentials.\r\n " , appSecret.c_str (), appSecret.length ());
248+ success = false ;
249+ } else if (appSecret.charAt (8 ) != ' -' || appSecret.charAt (13 ) != ' -' || appSecret.charAt (18 ) != ' -' || appSecret.charAt (23 ) != ' -' ||
250+ appSecret.charAt (36 ) != ' -' || appSecret.charAt (45 ) != ' -' || appSecret.charAt (50 ) != ' -' || appSecret.charAt (55 ) != ' -' || appSecret.charAt (60 ) != ' -' ) {
251+ Serial.printf (" [SinricPro:begin()]: App Secret '%s' is in an invalid format. Initialization aborted!\r\n " , appSecret.c_str ());
234252 success = false ;
235253 }
236254
@@ -248,6 +266,14 @@ void SinricProClass::begin(String appKey, String appSecret, String serverURL) {
248266
249267template <typename DeviceType>
250268DeviceType& SinricProClass::add (String deviceId) {
269+ // Validate DEVICE_ID
270+ // - Must be 24 characters
271+ // - Hexadecimal only (0-9, a-f, A-F)
272+ // - Format: 695b4624f6e5944047661b8a
273+ if (deviceId.length () != 24 ) {
274+ Serial.printf (" [SinricPro:add()]: Device Id \" %s\" is invalid (wrong length: expected 24, got %d)!! Please check your device-id!!\r\n " , deviceId.c_str (), deviceId.length ());
275+ }
276+
251277 DeviceType* newDevice = new DeviceType (deviceId);
252278 DEBUG_SINRIC (" [SinricPro:add()]: Adding device with id \" %s\" .\r\n " , deviceId.c_str ());
253279 newDevice->begin (this );
@@ -564,6 +590,42 @@ void SinricProClass::onReportHealth(ReportHealthCallbackHandler cb) {
564590 _moduleCommandHandler.onReportHealth (cb);
565591}
566592
593+ /* *
594+ * @brief Send `setSetting` event to SinricPro Server at module level
595+ *
596+ * @param settingId `String` the setting identifier
597+ * @param settingValue `SettingValue` (int, float, bool, or String) the setting value
598+ * @param cause (optional) `String` reason why event is sent (default = `"PHYSICAL_INTERACTION"`)
599+ * @return the success of sending the event
600+ * @retval true event has been sent successfully
601+ * @retval false event has not been sent, maybe you sent too many events in a short distance of time
602+ **/
603+ bool SinricProClass::sendSettingEvent (String settingId, SettingValue settingValue, String cause) {
604+ if (_settingEventLimiter) return false ;
605+
606+ JsonDocument eventMessage = prepareEvent (" " , FSTR_SETTING_setSetting, cause.c_str ());
607+ JsonObject payload = eventMessage[FSTR_SINRICPRO_payload];
608+
609+ payload.remove (FSTR_SINRICPRO_deviceId);
610+ payload[FSTR_SINRICPRO_scope] = FSTR_SINRICPRO_module;
611+
612+ JsonObject event_value = payload[FSTR_SINRICPRO_value];
613+ event_value[FSTR_SETTING_id] = settingId;
614+
615+ if (std::holds_alternative<int >(settingValue)) {
616+ event_value[FSTR_SETTING_value] = std::get<int >(settingValue);
617+ } else if (std::holds_alternative<float >(settingValue)) {
618+ event_value[FSTR_SETTING_value] = std::get<float >(settingValue);
619+ } else if (std::holds_alternative<bool >(settingValue)) {
620+ event_value[FSTR_SETTING_value] = std::get<bool >(settingValue);
621+ } else if (std::holds_alternative<String>(settingValue)) {
622+ event_value[FSTR_SETTING_value] = std::get<String>(settingValue);
623+ }
624+
625+ sendMessage (eventMessage);
626+ return true ;
627+ }
628+
567629/* *
568630 * @brief Set callback function for websocket connected event
569631 *
0 commit comments