10#include "SinricProDeviceInterface.h"
11#include "SinricProInterface.h"
12#include "SinricProMessageid.h"
13#include "SinricProModuleCommandHandler.h"
14#include "SinricProNamespace.h"
15#include "SinricProQueue.h"
16#include "SinricProSignature.h"
17#include "SinricProStrings.h"
18#include "SinricProWebsocket.h"
20#include "EventLimiter.h"
21#ifndef SINRICPRO_NO_LOCAL_CONTROL
22#include "SinricProUDP.h"
24#include "SinricProMDNS.h"
25namespace SINRICPRO_NAMESPACE {
86using PongCallback = std::function<
void(uint32_t)>;
100 void begin(String appKey, String appSecret, String serverURL = SINRICPRO_SERVER_URL);
106 void onPong(PongCallback
cb);
107 void restoreDeviceStates(
bool flag);
108 void setResponseMessage(String&&
message);
109 unsigned long getTimestamp()
override;
110 virtual String sign(
const String&
message);
111 Proxy operator[](
const String deviceId);
118 template <
typename DeviceType>
121 void add(SinricProDeviceInterface&
newDevice);
122 void add(SinricProDeviceInterface*
newDevice);
125 JsonDocument prepareEvent(String deviceId,
const char* action,
const char*
cause)
override;
129 void handleReceiveQueue();
130 void handleSendQueue();
137 JsonDocument prepareRequest(String deviceId,
const char* action);
148 SinricProDeviceInterface* getDevice(String deviceId);
152 template <
typename DeviceType>
153 DeviceType& getDeviceInstance(String deviceId);
155 std::vector<SinricProDeviceInterface*> devices;
161 WebsocketListener _websocketListener;
162#ifndef SINRICPRO_NO_LOCAL_CONTROL
163 UdpListener _udpListener;
165#ifdef SINRICPRO_MDNS_ENABLED
167 size_t _mdnsDeviceCount = 0;
169 SinricProQueue_t receiveQueue;
170 SinricProQueue_t sendQueue;
175 String responseMessageStr =
"";
177 SinricProModuleCommandHandler _moduleCommandHandler;
178 EventLimiter _settingEventLimiter{EVENT_LIMIT_STATE};
181class SinricProClass::Proxy {
185 template <
typename DeviceType>
193SinricProClass::Proxy::Proxy(SinricProClass* ptr,
const String& deviceId)
194 : ptr(ptr), deviceId(deviceId) {}
196template <
typename DeviceType>
197SinricProClass::Proxy::operator DeviceType&() {
198 return ptr->getDeviceInstance<DeviceType>(deviceId);
201SinricProDeviceInterface* SinricProClass::getDevice(String deviceId) {
202 for (
auto& device : devices) {
203 if (deviceId == device->getDeviceId())
return device;
208template <
typename DeviceType>
209DeviceType& SinricProClass::getDeviceInstance(String deviceId) {
210 DeviceType* tmp_device = (DeviceType*)getDevice(deviceId);
211 if (tmp_device)
return *tmp_device;
213 DEBUG_SINRIC(
"[SinricPro]: Device \"%s\" does not exist in the internal device list. creating new device\r\n", deviceId.c_str());
214 DeviceType& tmp_deviceInstance = add<DeviceType>(deviceId);
217 DEBUG_SINRIC(
"[SinricPro]: Reconnecting to server.\r\n");
221 return tmp_deviceInstance;
246 if (appKey.length() != 36) {
247 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());
249 }
else if (appKey.charAt(8) !=
'-' || appKey.charAt(13) !=
'-' || appKey.charAt(18) !=
'-' || appKey.charAt(23) !=
'-') {
250 Serial.printf(
"[SinricPro:begin()]: App Key '%s' is in an invalid format. Initialization aborted!\r\n", appKey.c_str());
257 if (appSecret.length() != 73) {
258 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());
260 }
else if (appSecret.charAt(8) !=
'-' || appSecret.charAt(13) !=
'-' || appSecret.charAt(18) !=
'-' || appSecret.charAt(23) !=
'-' ||
261 appSecret.charAt(36) !=
'-' || appSecret.charAt(45) !=
'-' || appSecret.charAt(50) !=
'-' || appSecret.charAt(55) !=
'-' || appSecret.charAt(60) !=
'-') {
262 Serial.printf(
"[SinricPro:begin()]: App Secret '%s' is in an invalid format. Initialization aborted!\r\n", appSecret.c_str());
271 this->appKey = appKey;
272 this->appSecret = appSecret;
273 this->serverURL = serverURL;
275#ifndef SINRICPRO_NO_LOCAL_CONTROL
276 _udpListener.begin(&receiveQueue);
278#ifdef SINRICPRO_MDNS_ENABLED
283 _mdnsListener.begin(
hostName, joinDeviceIds(
','));
284 _mdnsDeviceCount = devices.size();
289template <
typename DeviceType>
290DeviceType& SinricProClass::add(String deviceId) {
295 if (deviceId.length() != 24) {
296 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());
299 DeviceType* newDevice =
new DeviceType(deviceId);
300 DEBUG_SINRIC(
"[SinricPro:add()]: Adding device with id \"%s\".\r\n", deviceId.c_str());
301 newDevice->begin(
this);
303 devices.push_back(newDevice);
307__attribute__((deprecated(
"Please use DeviceType& myDevice = SinricPro.add<DeviceType>(String);"))) void SinricProClass::add(SinricProDeviceInterface* newDevice) {
308 newDevice->begin(
this);
309 devices.push_back(newDevice);
312__attribute__((deprecated(
"Please use DeviceType& myDevice = SinricPro.add<DeviceType>(String);"))) void SinricProClass::add(SinricProDeviceInterface& newDevice) {
313 newDevice.begin(
this);
314 devices.push_back(&newDevice);
336 DEBUG_SINRIC(
"[SinricPro:handle()]: ERROR! SinricPro.begin() failed or was not called prior to event handler\r\n");
337 DEBUG_SINRIC(
"[SinricPro:handle()]: -Reasons include an invalid app-key, invalid app-secret or no valid deviceIds)\r\n");
338 DEBUG_SINRIC(
"[SinricPro:handle()]: -SinricPro is disabled! Check earlier log messages for details.\r\n");
344 if (!isConnected()) connect();
345 _websocketListener.handle();
346#ifndef SINRICPRO_NO_LOCAL_CONTROL
347 _udpListener.handle();
349#ifdef SINRICPRO_MDNS_ENABLED
350 _mdnsListener.handle();
353 handleReceiveQueue();
357JsonDocument SinricProClass::prepareRequest(String deviceId,
const char* action) {
373void SinricProClass::handleResponse(JsonDocument& responseMessage) {
374 (void)responseMessage;
375 DEBUG_SINRIC(
"[SinricPro.handleResponse()]:\r\n");
377#ifndef NODEBUG_SINRIC
378 serializeJsonPretty(responseMessage, DEBUG_ESP_PORT);
383void SinricProClass::handleModuleRequest(JsonDocument& requestMessage,
const SinricProMessage* origin) {
384 DEBUG_SINRIC(
"[SinricPro.handleModuleScopeRequest()]: handling module scope request\r\n");
385#ifndef NODEBUG_SINRIC
386 serializeJsonPretty(requestMessage, DEBUG_ESP_PORT);
389 JsonDocument responseMessage = prepareResponse(requestMessage);
391 String action = requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_action] |
"";
392 JsonObject request_value = requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_value];
393 JsonObject response_value = responseMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_value];
394 SinricProRequest request{action,
"", request_value, response_value};
396 bool success = _moduleCommandHandler.handleRequest(request);
398 responseMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_success] = success;
399 responseMessage[FSTR_SINRICPRO_payload].remove(FSTR_SINRICPRO_deviceId);
401 if (responseMessageStr.length() > 0) {
402 responseMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_message] = responseMessageStr;
403 responseMessageStr =
"";
405 responseMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_message] =
"Module did not handle \"" + action +
"\"";
409 String responseString;
410 serializeJson(responseMessage, responseString);
411 sendQueue.push(
new SinricProMessage(origin->getInterface(), responseString.c_str(),
412 origin->getRemoteIP(), origin->getRemotePort()));
415void SinricProClass::handleDeviceRequest(JsonDocument& requestMessage,
const SinricProMessage* origin) {
416 DEBUG_SINRIC(
"[SinricPro.handleDeviceRequest()]: handling device sope request\r\n");
417#ifndef NODEBUG_SINRIC
418 serializeJsonPretty(requestMessage, DEBUG_ESP_PORT);
421 JsonDocument responseMessage = prepareResponse(requestMessage);
424 bool success =
false;
425 const char* deviceId = requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_deviceId];
426 String action = requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_action] |
"";
427 String instance = requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_instanceId] |
"";
428 JsonObject request_value = requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_value];
429 JsonObject response_value = responseMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_value];
431 bool deviceIsOurs =
false;
433 for (
auto& device : devices) {
434 if (device->getDeviceId() == deviceId && success ==
false) {
436 SinricProRequest request{
441 success = device->handleRequest(request);
442 responseMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_success] = success;
444 if (responseMessageStr.length() > 0) {
445 responseMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_message] = responseMessageStr;
446 responseMessageStr =
"";
448 responseMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_message] =
"Device did not handle \"" + action +
"\"";
454 if (!deviceIsOurs && origin->getInterface() == IF_UDP) {
455 DEBUG_SINRIC(
"[SinricPro.handleDeviceRequest()]: ignoring LAN request for unknown device \"%s\"\r\n", deviceId);
459 String responseString;
460 serializeJson(responseMessage, responseString);
461 sendQueue.push(
new SinricProMessage(origin->getInterface(), responseString.c_str(),
462 origin->getRemoteIP(), origin->getRemotePort()));
465void SinricProClass::handleReceiveQueue() {
466 if (receiveQueue.size() == 0)
return;
468 DEBUG_SINRIC(
"[SinricPro.handleReceiveQueue()]: %i message(s) in receiveQueue\r\n", receiveQueue.size());
469 while (receiveQueue.size() > 0) {
470 SinricProMessage* rawMessage = receiveQueue.front();
472 JsonDocument jsonMessage;
473 deserializeJson(jsonMessage, rawMessage->getMessage());
475 bool sigMatch =
false;
477 if (strncmp(rawMessage->getMessage(),
"{\"timestamp\":", 13) == 0 && strlen(rawMessage->getMessage()) <= 26) {
480 String signature = jsonMessage[FSTR_SINRICPRO_signature][FSTR_SINRICPRO_HMAC] |
"";
481 String payload = extractPayload(rawMessage->getMessage());
482 String calculatedSignature = calculateSignature(appSecret.c_str(), payload);
483 sigMatch = (calculatedSignature == signature);
486 String messageType = jsonMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_type];
489 DEBUG_SINRIC(
"[SinricPro.handleReceiveQueue()]: Signature is valid. Processing message...\r\n");
490 extractTimestamp(jsonMessage);
491 if (messageType == FSTR_SINRICPRO_response) handleResponse(jsonMessage);
492 if (messageType == FSTR_SINRICPRO_request) {
493 String scope = jsonMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_scope] | FSTR_SINRICPRO_device;
494 if (strcmp(FSTR_SINRICPRO_module, scope.c_str()) == 0) {
495 handleModuleRequest(jsonMessage, rawMessage);
497 handleDeviceRequest(jsonMessage, rawMessage);
501 handleInvalidSignatureRequest(jsonMessage, rawMessage);
507void SinricProClass::handleInvalidSignatureRequest(JsonDocument& requestMessage,
const SinricProMessage* origin) {
508 DEBUG_SINRIC(
"[SinricPro.handleInvalidSignatureRequest()]: Signature is invalid!\r\n");
510#ifndef NODEBUG_SINRIC
511 serializeJsonPretty(requestMessage, DEBUG_ESP_PORT);
514 JsonDocument responseMessage = prepareResponse(requestMessage);
515 responseMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_success] =
false;
516 responseMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_message] =
"Signature is invalid";
518 String responseString;
519 serializeJson(responseMessage, responseString);
520 sendQueue.push(
new SinricProMessage(origin->getInterface(), responseString.c_str(),
521 origin->getRemoteIP(), origin->getRemotePort()));
524void SinricProClass::handleSendQueue() {
526 size_t pending = sendQueue.size();
527 while (pending-- > 0) {
528 DEBUG_SINRIC(
"[SinricPro:handleSendQueue()]: %i message(s) in sendQueue\r\n", sendQueue.size());
530 SinricProMessage* rawMessage = sendQueue.front();
532 if (rawMessage->getInterface() == IF_WEBSOCKET &&
533 (!isConnected() || !timestamp.getTimestamp())) {
535 sendQueue.push(rawMessage);
539 DEBUG_SINRIC(
"[SinricPro:handleSendQueue()]: Sending message...\r\n");
542 JsonDocument jsonMessage;
543 deserializeJson(jsonMessage, rawMessage->getMessage());
544 jsonMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_createdAt] = timestamp.getTimestamp();
545 signMessage(appSecret, jsonMessage);
549 serializeJson(jsonMessage, messageStr);
550#ifndef NODEBUG_SINRIC
551 serializeJsonPretty(jsonMessage, DEBUG_ESP_PORT);
555 switch (rawMessage->getInterface()) {
558 DEBUG_SINRIC(
"[SinricPro:handleSendQueue]: Sending to websocket\r\n");
559 _websocketListener.sendMessage(messageStr);
561 DEBUG_SINRIC(
"[SinricPro:handleSendQueue]: Dropping WS message — not connected\r\n");
565#ifndef SINRICPRO_NO_LOCAL_CONTROL
566 DEBUG_SINRIC(
"[SinricPro:handleSendQueue]: Sending to UDP\r\n");
567 _udpListener.sendMessage(messageStr, rawMessage->getRemoteIP(), rawMessage->getRemotePort());
574 DEBUG_SINRIC(
"[SinricPro:handleSendQueue()]: message sent.\r\n");
578String SinricProClass::joinDeviceIds(
char separator) {
581 for (
auto& device : devices) {
582 if (i++ > 0) out += separator;
583 out += device->getDeviceId();
588void SinricProClass::connect() {
589 _websocketListener.begin(serverURL, appKey, joinDeviceIds(
';'), &receiveQueue);
591#ifdef SINRICPRO_MDNS_ENABLED
592 if (devices.size() != _mdnsDeviceCount) {
593 _mdnsDeviceCount = devices.size();
594 _mdnsListener.update(joinDeviceIds(
','));
599void SinricProClass::stop() {
601 DEBUG_SINRIC(
"[SinricPro:stop()\r\n");
602 _websocketListener.stop();
605bool SinricProClass::isConnected() {
606 return _websocketListener.isConnected();
620 _moduleCommandHandler.onOTAUpdate(
cb);
634 _moduleCommandHandler.onSetSetting(
cb);
650 _moduleCommandHandler.onReportHealth(
cb);
664 if (_settingEventLimiter)
return false;
677 }
else if (std::holds_alternative<float>(
settingValue)) {
679 }
else if (std::holds_alternative<bool>(
settingValue)) {
681 }
else if (std::holds_alternative<String>(
settingValue)) {
699 _websocketListener.onConnected(
cb);
712 _websocketListener.onDisconnected(
cb);
715void SinricProClass::onPong(PongCallback
cb) {
716 _websocketListener.onPong(
cb);
719void SinricProClass::reconnect() {
720 DEBUG_SINRIC(
"SinricPro:reconnect(): disconnecting\r\n");
722 DEBUG_SINRIC(
"SinricPro:reconnect(): connecting\r\n");
726void SinricProClass::onConnect() {
727 DEBUG_SINRIC(
"[SinricPro]: Connected to \"%s\"!]\r\n", serverURL.c_str());
730void SinricProClass::onDisconnect() {
731 DEBUG_SINRIC(
"[SinricPro]: Disconnect\r\n");
734void SinricProClass::extractTimestamp(JsonDocument& message) {
735 unsigned long tempTimestamp = 0;
737 tempTimestamp = message[
"timestamp"] | 0;
739 timestamp.setTimestamp(tempTimestamp);
740 DEBUG_SINRIC(
"[SinricPro:extractTimestamp(): Got Timestamp %lu\r\n", tempTimestamp);
745 tempTimestamp = message[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_createdAt] | 0;
747 DEBUG_SINRIC(
"[SinricPro:extractTimestamp(): Got Timestamp %lu\r\n", tempTimestamp);
748 timestamp.setTimestamp(tempTimestamp);
753void SinricProClass::sendMessage(JsonDocument& jsonMessage) {
754 if (!isConnected()) {
755 DEBUG_SINRIC(
"[SinricPro:sendMessage()]: device is offline, message has been dropped\r\n");
758 DEBUG_SINRIC(
"[SinricPro:sendMessage()]: pushing message into sendQueue\r\n");
759 String messageString;
760 serializeJson(jsonMessage, messageString);
761 sendQueue.push(
new SinricProMessage(IF_WEBSOCKET, messageString.c_str()));
775__attribute__((deprecated(
"Use SinricPro Portal: Edit Device > Other > 'Restore Device State When Connect' instead")))
void SinricProClass::restoreDeviceStates(
bool flag) {
776 _websocketListener.setRestoreDeviceStates(
flag);
796 return Proxy(
this, deviceId);
799void SinricProClass::setResponseMessage(String&&
message) {
809 return timestamp.getTimestamp();
812String SinricProClass::sign(
const String&
message) {
813 return HMACbase64(
message, appSecret);
816JsonDocument SinricProClass::prepareResponse(JsonDocument& requestMessage) {
817 JsonDocument responseMessage;
818 JsonObject header = responseMessage[FSTR_SINRICPRO_header].to<JsonObject>();
819 header[FSTR_SINRICPRO_payloadVersion] = 2;
820 header[FSTR_SINRICPRO_signatureVersion] = 1;
822 JsonObject payload = responseMessage[FSTR_SINRICPRO_payload].to<JsonObject>();
823 payload[FSTR_SINRICPRO_action] = requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_action];
824 payload[FSTR_SINRICPRO_clientId] = requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_clientId];
825 payload[FSTR_SINRICPRO_scope] = requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_scope];
826 payload[FSTR_SINRICPRO_createdAt] = 0;
827 payload[FSTR_SINRICPRO_deviceId] = requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_deviceId];
828 if (requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_instanceId].is<String>()) payload[FSTR_SINRICPRO_instanceId] = requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_instanceId];
829 payload[FSTR_SINRICPRO_message] = FSTR_SINRICPRO_OK;
830 payload[FSTR_SINRICPRO_replyToken] = requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_replyToken];
831 payload[FSTR_SINRICPRO_success] =
false;
832 payload[FSTR_SINRICPRO_type] = FSTR_SINRICPRO_response;
833 payload[FSTR_SINRICPRO_value].to<JsonObject>();
834 return responseMessage;
837JsonDocument SinricProClass::prepareEvent(String deviceId,
const char* action,
const char* cause) {
838 JsonDocument eventMessage;
839 JsonObject header = eventMessage[FSTR_SINRICPRO_header].to<JsonObject>();
840 header[FSTR_SINRICPRO_payloadVersion] = 2;
841 header[FSTR_SINRICPRO_signatureVersion] = 1;
843 JsonObject payload = eventMessage[FSTR_SINRICPRO_payload].to<JsonObject>();
844 payload[FSTR_SINRICPRO_action] = action;
845 payload[FSTR_SINRICPRO_cause][FSTR_SINRICPRO_type].to<JsonObject>();
846 payload[FSTR_SINRICPRO_cause][FSTR_SINRICPRO_type] = cause;
847 payload[FSTR_SINRICPRO_createdAt] = 0;
848 payload[FSTR_SINRICPRO_deviceId] = deviceId;
849 payload[FSTR_SINRICPRO_replyToken] = MessageID().getID();
850 payload[FSTR_SINRICPRO_type] = FSTR_SINRICPRO_event;
851 payload[FSTR_SINRICPRO_value].to<JsonObject>();
857SINRICPRO_NAMESPACE::SinricProClass SinricPro;
AirQuality.
Definition AirQualitySensor.h:19
The main class of this library, handling communication between SinricPro Server and your devices.
Definition SinricPro.h:93
void begin(String appKey, String appSecret, String serverURL="ws.sinric.pro")
Initializing SinricProClass to be able to connect to SinricPro Server.
Definition SinricPro.h:240
void handle()
Handles communication between device and SinricPro Server.
Definition SinricPro.h:332
void onDisconnected(DisconnectedCallbackHandler cb)
Set callback function for websocket disconnected event.
Definition SinricPro.h:711
void onSetSetting(SetSettingCallbackHandler cb)
Set callback function for setting a module setting.
Definition SinricPro.h:633
bool sendSettingEvent(String settingId, SettingValue settingValue, String cause=FSTR_SINRICPRO_PHYSICAL_INTERACTION)
Send setSetting event to SinricPro Server at module level.
Definition SinricPro.h:663
Proxy operator[](const String deviceId)
operator[] is used tor create a new device instance or get an existing device instance
Definition SinricPro.h:795
void onConnected(ConnectedCallbackHandler cb)
Set callback function for websocket connected event.
Definition SinricPro.h:698
void onOTAUpdate(OTAUpdateCallbackHandler cb)
Set callback function for OTA (Over-The-Air) updates.
Definition SinricPro.h:619
void onReportHealth(ReportHealthCallbackHandler cb)
Sets the callback function for reporting device health status.
Definition SinricPro.h:649
unsigned long getTimestamp() override
return unsigned long current timestamp(unix epoch time) * /
Definition SinricPro.h:808
Base class for all device types.
Definition SinricProDevice.h:24
Announces this device on the LAN via mDNS.
Definition SinricProMDNS.h:59
std::function< bool(const String &url, int major, int minor, int patch, bool enforce)> OTAUpdateCallbackHandler
Function signature for OTA update callback.
Definition SinricPro.h:58
std::function< bool(String &healthReport)> ReportHealthCallbackHandler
Defines a function type for reporting device health status.
Definition SinricPro.h:84
std::function< void(void)> DisconnectedCallbackHandler
Callback definition for onDisconnected function.
Definition SinricPro.h:43
std::function< void(void)> ConnectedCallbackHandler
Callback definition for onConnected function.
Definition SinricPro.h:34
std::variant< int, float, bool, String > SettingValue
Variant type for setting values that can hold int, float, bool, or String.
Definition SettingController.h:19
std::function< bool(const String &id, SettingValue &value)> SetSettingCallbackHandler
Function signature for setting a module setting.
Definition SinricPro.h:69