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