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 "SinricProUDP.h" 
   19#include "SinricProWebsocket.h" 
   21namespace SINRICPRO_NAMESPACE {
 
   82using PongCallback = std::function<
void(uint32_t)>;
 
   96    void           begin(String appKey, String appSecret, String serverURL = SINRICPRO_SERVER_URL);
 
  102    void           onPong(PongCallback 
cb);
 
  103    void           restoreDeviceStates(
bool flag);
 
  104    void           setResponseMessage(String&& 
message);
 
  105    unsigned long  getTimestamp() 
override;
 
  106    virtual String sign(
const String& 
message);
 
  107    Proxy          operator[](
const String deviceId);
 
  113    template <
typename DeviceType>
 
  116    void add(SinricProDeviceInterface& 
newDevice);
 
  117    void add(SinricProDeviceInterface* 
newDevice);
 
  120    JsonDocument prepareEvent(String deviceId, 
const char* action, 
const char* 
cause) 
override;
 
  124    void handleReceiveQueue();
 
  125    void handleSendQueue();
 
  132    JsonDocument prepareRequest(String deviceId, 
const char* action);
 
  143    SinricProDeviceInterface* getDevice(String deviceId);
 
  145    template <
typename DeviceType>
 
  146    DeviceType& getDeviceInstance(String deviceId);
 
  148    std::vector<SinricProDeviceInterface*> devices;
 
  154    WebsocketListener _websocketListener;
 
  155    UdpListener       _udpListener;
 
  156    SinricProQueue_t  receiveQueue;
 
  157    SinricProQueue_t  sendQueue;
 
  162    String responseMessageStr = 
"";
 
  164    SinricProModuleCommandHandler _moduleCommandHandler;
 
 
  167class SinricProClass::Proxy {
 
  171    template <
typename DeviceType>
 
  179SinricProClass::Proxy::Proxy(SinricProClass* ptr, 
const String& deviceId)
 
  180    : ptr(ptr), deviceId(deviceId) {}
 
  182template <
typename DeviceType>
 
  183SinricProClass::Proxy::operator DeviceType&() {
 
  184    return ptr->getDeviceInstance<DeviceType>(deviceId);
 
  187SinricProDeviceInterface* SinricProClass::getDevice(String deviceId) {
 
  188    for (
auto& device : devices) {
 
  189        if (deviceId == device->getDeviceId()) 
return device;
 
  194template <
typename DeviceType>
 
  195DeviceType& SinricProClass::getDeviceInstance(String deviceId) {
 
  196    DeviceType* tmp_device = (DeviceType*)getDevice(deviceId);
 
  197    if (tmp_device) 
return *tmp_device;
 
  199    DEBUG_SINRIC(
"[SinricPro]: Device \"%s\" does not exist in the internal device list. creating new device\r\n", deviceId.c_str());
 
  200    DeviceType& tmp_deviceInstance = add<DeviceType>(deviceId);
 
  203        DEBUG_SINRIC(
"[SinricPro]: Reconnecting to server.\r\n");
 
  207    return tmp_deviceInstance;
 
  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());
 
  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    this->appKey    = appKey;
 
  243    this->appSecret = appSecret;
 
  244    this->serverURL = serverURL;
 
  246    _udpListener.begin(&receiveQueue);
 
 
  249template <
typename DeviceType>
 
  250DeviceType& SinricProClass::add(String deviceId) {
 
  252    DEBUG_SINRIC(
"[SinricPro:add()]: Adding device with id \"%s\".\r\n", deviceId.c_str());
 
  259__attribute__((deprecated(
"Please use DeviceType& myDevice = SinricPro.add<DeviceType>(String);"))) void SinricProClass::add(SinricProDeviceInterface* newDevice) {
 
  260    newDevice->begin(
this);
 
  261    devices.push_back(newDevice);
 
  264__attribute__((deprecated(
"Please use DeviceType& myDevice = SinricPro.add<DeviceType>(String);"))) void SinricProClass::add(SinricProDeviceInterface& newDevice) {
 
  265    newDevice.begin(
this);
 
  266    devices.push_back(&newDevice);
 
  288            DEBUG_SINRIC(
"[SinricPro:handle()]: ERROR! SinricPro.begin() failed or was not called prior to event handler\r\n");
 
  289            DEBUG_SINRIC(
"[SinricPro:handle()]:    -Reasons include an invalid app-key, invalid app-secret or no valid deviceIds)\r\n");
 
  290            DEBUG_SINRIC(
"[SinricPro:handle()]:    -SinricPro is disabled! Check earlier log messages for details.\r\n");
 
  296    if (!isConnected()) connect();
 
  297    _websocketListener.handle();
 
  298    _udpListener.handle();
 
  300    handleReceiveQueue();
 
 
  304JsonDocument SinricProClass::prepareRequest(String deviceId, 
const char* action) {
 
  320void SinricProClass::handleResponse(JsonDocument& responseMessage) {
 
  321    (void)responseMessage;
 
  322    DEBUG_SINRIC(
"[SinricPro.handleResponse()]:\r\n");
 
  324#ifndef NODEBUG_SINRIC 
  325    serializeJsonPretty(responseMessage, DEBUG_ESP_PORT);
 
  330void SinricProClass::handleModuleRequest(JsonDocument& requestMessage, interface_t Interface) {
 
  331    DEBUG_SINRIC(
"[SinricPro.handleModuleScopeRequest()]: handling module scope request\r\n");
 
  332#ifndef NODEBUG_SINRIC 
  333    serializeJsonPretty(requestMessage, DEBUG_ESP_PORT);
 
  336    JsonDocument responseMessage = prepareResponse(requestMessage);
 
  338    String           action         = requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_action] | 
"";
 
  339    JsonObject       request_value  = requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_value];
 
  340    JsonObject       response_value = responseMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_value];
 
  341    SinricProRequest request{action, 
"", request_value, response_value};
 
  343    bool success = _moduleCommandHandler.handleRequest(request);
 
  345    responseMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_success] = success;
 
  346    responseMessage[FSTR_SINRICPRO_payload].remove(FSTR_SINRICPRO_deviceId);
 
  348        if (responseMessageStr.length() > 0) {
 
  349            responseMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_message] = responseMessageStr;
 
  350            responseMessageStr                                              = 
"";
 
  352            responseMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_message] = 
"Module did not handle \"" + action + 
"\"";
 
  356    String responseString;
 
  357    serializeJson(responseMessage, responseString);
 
  358    sendQueue.push(
new SinricProMessage(Interface, responseString.c_str()));
 
  361void SinricProClass::handleDeviceRequest(JsonDocument& requestMessage, interface_t Interface) {
 
  362    DEBUG_SINRIC(
"[SinricPro.handleDeviceRequest()]: handling device sope request\r\n");
 
  363#ifndef NODEBUG_SINRIC 
  364    serializeJsonPretty(requestMessage, DEBUG_ESP_PORT);
 
  367    JsonDocument responseMessage = prepareResponse(requestMessage);
 
  370    bool        success        = 
false;
 
  371    const char* deviceId       = requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_deviceId];
 
  372    String      action         = requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_action] | 
"";
 
  373    String      instance       = requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_instanceId] | 
"";
 
  374    JsonObject  request_value  = requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_value];
 
  375    JsonObject  response_value = responseMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_value];
 
  377    for (
auto& device : devices) {
 
  378        if (device->getDeviceId() == deviceId && success == 
false) {
 
  379            SinricProRequest request{
 
  384            success                                                         = device->handleRequest(request);
 
  385            responseMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_success] = success;
 
  387                if (responseMessageStr.length() > 0) {
 
  388                    responseMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_message] = responseMessageStr;
 
  389                    responseMessageStr                                              = 
"";
 
  391                    responseMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_message] = 
"Device did not handle \"" + action + 
"\"";
 
  397    String responseString;
 
  398    serializeJson(responseMessage, responseString);
 
  399    sendQueue.push(
new SinricProMessage(Interface, responseString.c_str()));
 
  402void SinricProClass::handleReceiveQueue() {
 
  403    if (receiveQueue.size() == 0) 
return;
 
  405    DEBUG_SINRIC(
"[SinricPro.handleReceiveQueue()]: %i message(s) in receiveQueue\r\n", receiveQueue.size());
 
  406    while (receiveQueue.size() > 0) {
 
  407        SinricProMessage* rawMessage = receiveQueue.front();
 
  409        JsonDocument jsonMessage;
 
  410        deserializeJson(jsonMessage, rawMessage->getMessage());
 
  412        bool sigMatch = 
false;
 
  414        if (strncmp(rawMessage->getMessage(), 
"{\"timestamp\":", 13) == 0 && strlen(rawMessage->getMessage()) <= 26) {
 
  417            String signature           = jsonMessage[FSTR_SINRICPRO_signature][FSTR_SINRICPRO_HMAC] | 
"";
 
  418            String payload             = extractPayload(rawMessage->getMessage());
 
  419            String calculatedSignature = calculateSignature(appSecret.c_str(), payload);
 
  420            sigMatch                   = (calculatedSignature == signature);
 
  423        String messageType = jsonMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_type];
 
  426            DEBUG_SINRIC(
"[SinricPro.handleReceiveQueue()]: Signature is valid. Processing message...\r\n");
 
  427            extractTimestamp(jsonMessage);
 
  428            if (messageType == FSTR_SINRICPRO_response) handleResponse(jsonMessage);
 
  429            if (messageType == FSTR_SINRICPRO_request) {
 
  430                String scope = jsonMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_scope] | FSTR_SINRICPRO_device;
 
  431                if (strcmp(FSTR_SINRICPRO_module, scope.c_str()) == 0) {
 
  432                    handleModuleRequest(jsonMessage, rawMessage->getInterface());
 
  434                    handleDeviceRequest(jsonMessage, rawMessage->getInterface());
 
  438            handleInvalidSignatureRequest(jsonMessage, rawMessage->getInterface());
 
  444void SinricProClass::handleInvalidSignatureRequest(JsonDocument& requestMessage, interface_t Interface) { 
 
  445    DEBUG_SINRIC(
"[SinricPro.handleInvalidSignatureRequest()]: Signature is invalid!\r\n");
 
  447#ifndef NODEBUG_SINRIC 
  448    serializeJsonPretty(requestMessage, DEBUG_ESP_PORT);
 
  451    JsonDocument responseMessage = prepareResponse(requestMessage);    
 
  452    responseMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_success] = 
false;
 
  453    responseMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_message] = 
"Signature is invalid";
 
  455    String responseString;
 
  456    serializeJson(responseMessage, responseString);
 
  457    sendQueue.push(
new SinricProMessage(Interface, responseString.c_str()));
 
  460void SinricProClass::handleSendQueue() {
 
  461    if (!isConnected()) 
return;
 
  462    if (!timestamp.getTimestamp()) 
return;
 
  463    while (sendQueue.size() > 0) {
 
  464        DEBUG_SINRIC(
"[SinricPro:handleSendQueue()]: %i message(s) in sendQueue\r\n", sendQueue.size());
 
  465        DEBUG_SINRIC(
"[SinricPro:handleSendQueue()]: Sending message...\r\n");
 
  467        SinricProMessage* rawMessage = sendQueue.front();
 
  470        JsonDocument jsonMessage;
 
  471        deserializeJson(jsonMessage, rawMessage->getMessage());
 
  472        jsonMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_createdAt] = timestamp.getTimestamp();
 
  473        signMessage(appSecret, jsonMessage);
 
  477        serializeJson(jsonMessage, messageStr);
 
  478#ifndef NODEBUG_SINRIC 
  479        serializeJsonPretty(jsonMessage, DEBUG_ESP_PORT);
 
  483        switch (rawMessage->getInterface()) {
 
  485                DEBUG_SINRIC(
"[SinricPro:handleSendQueue]: Sending to websocket\r\n");
 
  486                _websocketListener.sendMessage(messageStr);
 
  489                DEBUG_SINRIC(
"[SinricPro:handleSendQueue]: Sending to UDP\r\n");
 
  490                _udpListener.sendMessage(messageStr);
 
  496        DEBUG_SINRIC(
"[SinricPro:handleSendQueue()]: message sent.\r\n");
 
  500void SinricProClass::connect() {
 
  503    for (
auto& device : devices) {
 
  504        String deviceId = device->getDeviceId();
 
  505        if (i > 0) deviceList += 
';';
 
  506        deviceList += device->getDeviceId();
 
  510    _websocketListener.begin(serverURL, appKey, deviceList, &receiveQueue);
 
  513void SinricProClass::stop() {
 
  515    DEBUG_SINRIC(
"[SinricPro:stop()\r\n");
 
  516    _websocketListener.stop();
 
  519bool SinricProClass::isConnected() {
 
  520    return _websocketListener.isConnected();
 
  534    _moduleCommandHandler.onOTAUpdate(
cb);
 
 
  548    _moduleCommandHandler.onSetSetting(
cb);
 
 
  564    _moduleCommandHandler.onReportHealth(
cb);
 
 
  577    _websocketListener.onConnected(
cb);
 
 
  590    _websocketListener.onDisconnected(
cb);
 
 
  593void SinricProClass::onPong(PongCallback 
cb) {
 
  594    _websocketListener.onPong(
cb);
 
  597void SinricProClass::reconnect() {
 
  598    DEBUG_SINRIC(
"SinricPro:reconnect(): disconnecting\r\n");
 
  600    DEBUG_SINRIC(
"SinricPro:reconnect(): connecting\r\n");
 
  604void SinricProClass::onConnect() {
 
  605    DEBUG_SINRIC(
"[SinricPro]: Connected to \"%s\"!]\r\n", serverURL.c_str());
 
  608void SinricProClass::onDisconnect() {
 
  609    DEBUG_SINRIC(
"[SinricPro]: Disconnect\r\n");
 
  612void SinricProClass::extractTimestamp(JsonDocument& message) {
 
  613    unsigned long tempTimestamp = 0;
 
  615    tempTimestamp = message[
"timestamp"] | 0;
 
  617        timestamp.setTimestamp(tempTimestamp);
 
  618        DEBUG_SINRIC(
"[SinricPro:extractTimestamp(): Got Timestamp %lu\r\n", tempTimestamp);
 
  623    tempTimestamp = message[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_createdAt] | 0;
 
  625        DEBUG_SINRIC(
"[SinricPro:extractTimestamp(): Got Timestamp %lu\r\n", tempTimestamp);
 
  626        timestamp.setTimestamp(tempTimestamp);
 
  631void SinricProClass::sendMessage(JsonDocument& jsonMessage) {
 
  632    if (!isConnected()) {
 
  633        DEBUG_SINRIC(
"[SinricPro:sendMessage()]: device is offline, message has been dropped\r\n");
 
  636    DEBUG_SINRIC(
"[SinricPro:sendMessage()]: pushing message into sendQueue\r\n");
 
  637    String messageString;
 
  638    serializeJson(jsonMessage, messageString);
 
  639    sendQueue.push(
new SinricProMessage(IF_WEBSOCKET, messageString.c_str()));
 
  652    _websocketListener.setRestoreDeviceStates(
flag);
 
 
  672    return Proxy(
this, deviceId);
 
 
  675void SinricProClass::setResponseMessage(String&& 
message) {
 
  685    return timestamp.getTimestamp();
 
 
  688String SinricProClass::sign(
const String& 
message) {
 
  689    return HMACbase64(
message, appSecret);
 
  692JsonDocument SinricProClass::prepareResponse(JsonDocument& requestMessage) {
 
  693    JsonDocument responseMessage;
 
  694    JsonObject   header                     = responseMessage[FSTR_SINRICPRO_header].to<JsonObject>();
 
  695    header[FSTR_SINRICPRO_payloadVersion]   = 2;
 
  696    header[FSTR_SINRICPRO_signatureVersion] = 1;
 
  698    JsonObject payload                = responseMessage[FSTR_SINRICPRO_payload].to<JsonObject>();
 
  699    payload[FSTR_SINRICPRO_action]    = requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_action];
 
  700    payload[FSTR_SINRICPRO_clientId]  = requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_clientId];
 
  701    payload[FSTR_SINRICPRO_scope]     = requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_scope];
 
  702    payload[FSTR_SINRICPRO_createdAt] = 0;
 
  703    payload[FSTR_SINRICPRO_deviceId]  = requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_deviceId];
 
  704    if (requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_instanceId].is<String>()) payload[FSTR_SINRICPRO_instanceId] = requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_instanceId];
 
  705    payload[FSTR_SINRICPRO_message]    = FSTR_SINRICPRO_OK;
 
  706    payload[FSTR_SINRICPRO_replyToken] = requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_replyToken];
 
  707    payload[FSTR_SINRICPRO_success]    = 
false;
 
  708    payload[FSTR_SINRICPRO_type]       = FSTR_SINRICPRO_response;
 
  709    payload[FSTR_SINRICPRO_value].to<JsonObject>();
 
  710    return responseMessage;
 
  713JsonDocument SinricProClass::prepareEvent(String deviceId, 
const char* action, 
const char* cause) {
 
  714    JsonDocument eventMessage;
 
  715    JsonObject   header                     = eventMessage[FSTR_SINRICPRO_header].to<JsonObject>();
 
  716    header[FSTR_SINRICPRO_payloadVersion]   = 2;
 
  717    header[FSTR_SINRICPRO_signatureVersion] = 1;
 
  719    JsonObject payload             = eventMessage[FSTR_SINRICPRO_payload].to<JsonObject>();
 
  720    payload[FSTR_SINRICPRO_action] = action;
 
  721    payload[FSTR_SINRICPRO_cause][FSTR_SINRICPRO_type].to<JsonObject>();
 
  722    payload[FSTR_SINRICPRO_cause][FSTR_SINRICPRO_type] = cause;
 
  723    payload[FSTR_SINRICPRO_createdAt]                  = 0;
 
  724    payload[FSTR_SINRICPRO_deviceId]                   = deviceId;
 
  725    payload[FSTR_SINRICPRO_replyToken]                 = MessageID().getID();
 
  726    payload[FSTR_SINRICPRO_type]                       = FSTR_SINRICPRO_event;
 
  727    payload[FSTR_SINRICPRO_value].to<JsonObject>();
 
  733SINRICPRO_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:89
 
void begin(String appKey, String appSecret, String serverURL="ws.sinric.pro")
Initializing SinricProClass to be able to connect to SinricPro Server.
Definition SinricPro.h:226
 
void restoreDeviceStates(bool flag)
Enable / disable restore device states function.
Definition SinricPro.h:651
 
void handle()
Handles communication between device and SinricPro Server.
Definition SinricPro.h:284
 
void onDisconnected(DisconnectedCallbackHandler cb)
Set callback function for websocket disconnected event.
Definition SinricPro.h:589
 
void onSetSetting(SetSettingCallbackHandler cb)
Set callback function for setting a module setting.
Definition SinricPro.h:547
 
Proxy operator[](const String deviceId)
operator[] is used tor create a new device instance or get an existing device instance
Definition SinricPro.h:671
 
void onConnected(ConnectedCallbackHandler cb)
Set callback function for websocket connected event.
Definition SinricPro.h:576
 
void onOTAUpdate(OTAUpdateCallbackHandler cb)
Set callback function for OTA (Over-The-Air) updates.
Definition SinricPro.h:533
 
void onReportHealth(ReportHealthCallbackHandler cb)
Sets the callback function for reporting device health status.
Definition SinricPro.h:563
 
unsigned long getTimestamp() override
return unsigned long current timestamp(unix epoch time) * /
Definition SinricPro.h:684
 
Base class for all device types.
Definition SinricProDevice.h:24
 
std::function< bool(const String &url, int major, int minor, int patch, bool enforce)> OTAUpdateCallbackHandler
Function signature for OTA update callback.
Definition SinricPro.h:54
 
std::function< bool(String &healthReport)> ReportHealthCallbackHandler
Defines a function type for reporting device health status.
Definition SinricPro.h:80
 
std::function< void(void)> DisconnectedCallbackHandler
Callback definition for onDisconnected function.
Definition SinricPro.h:39
 
std::function< bool(const String &id, const String &value)> SetSettingCallbackHandler
Function signature for setting a module setting.
Definition SinricPro.h:65
 
std::function< void(void)> ConnectedCallbackHandler
Callback definition for onConnected function.
Definition SinricPro.h:30