11 #include "SinricProInterface.h"
12 #include "SinricProDeviceInterface.h"
13 #include "SinricProWebsocket.h"
14 #include "SinricProUDP.h"
15 #include "SinricProSignature.h"
16 #include "SinricProMessageid.h"
17 #include "SinricProQueue.h"
18 #include "SinricProId.h"
28 void begin(AppKey socketAuthToken, AppSecret signingKey, String serverURL = SINRICPRO_SERVER_URL);
52 void onPong(std::function<
void(uint32_t)> cb) { _websocketListener.onPong(cb); }
57 proxy(
SinricProClass* ptr, DeviceId deviceId) : ptr(ptr), deviceId(deviceId) {}
60 template <
typename DeviceType>
61 operator DeviceType&() {
return as<DeviceType>(); }
62 template <
typename DeviceType>
63 DeviceType& as() {
return ptr->getDeviceInstance<DeviceType>(deviceId); }
81 proxy
operator[](
const DeviceId deviceId) {
return proxy(
this, deviceId); }
84 void setResponseMessage(String &&message) { responseMessageStr = message; }
91 unsigned long getTimestamp()
override {
return baseTimestamp + (millis()/1000); }
93 template <
typename DeviceType>
94 DeviceType &add(DeviceId deviceId);
96 void add(SinricProDeviceInterface &newDevice);
97 void add(SinricProDeviceInterface *newDevice);
99 DynamicJsonDocument prepareResponse(JsonDocument &requestMessage);
100 DynamicJsonDocument prepareEvent(DeviceId deviceId,
const char *action,
const char *cause)
override;
101 void sendMessage(JsonDocument &jsonMessage)
override;
104 void handleReceiveQueue();
105 void handleSendQueue();
107 void handleRequest(DynamicJsonDocument& requestMessage, interface_t Interface);
108 void handleResponse(DynamicJsonDocument& responseMessage);
110 DynamicJsonDocument prepareRequest(DeviceId deviceId,
const char* action);
116 void onConnect() { DEBUG_SINRIC(
"[SinricPro]: Connected to \"%s\"!]\r\n", serverURL.c_str()); }
117 void onDisconnect() { DEBUG_SINRIC(
"[SinricPro]: Disconnect\r\n"); }
119 void extractTimestamp(JsonDocument &message);
121 SinricProDeviceInterface* getDevice(DeviceId deviceId);
123 template <
typename DeviceType>
124 DeviceType& getDeviceInstance(DeviceId deviceId);
126 std::vector<SinricProDeviceInterface*> devices;
128 AppKey socketAuthToken;
129 AppSecret signingKey;
132 websocketListener _websocketListener;
133 udpListener _udpListener;
134 SinricProQueue_t receiveQueue;
135 SinricProQueue_t sendQueue;
137 unsigned long baseTimestamp = 0;
140 String responseMessageStr =
"";
143 SinricProDeviceInterface* SinricProClass::getDevice(DeviceId deviceId) {
144 for (
auto& device : devices) {
145 if (deviceId == device->getDeviceId())
return device;
150 template <
typename DeviceType>
151 DeviceType& SinricProClass::getDeviceInstance(DeviceId deviceId) {
152 DeviceType* tmp_device = (DeviceType*) getDevice(deviceId);
153 if (tmp_device)
return *tmp_device;
155 DEBUG_SINRIC(
"[SinricPro]: Device \"%s\" does not exist. Creating new device\r\n", deviceId.toString().c_str());
156 DeviceType& tmp_deviceInstance = add<DeviceType>(deviceId);
159 DEBUG_SINRIC(
"[SinricPro]: Reconnecting to server.\r\n");
163 return tmp_deviceInstance;
184 if (!socketAuthToken.isValid()) {
185 DEBUG_SINRIC(
"[SinricPro:begin()]: App-Key \"%s\" is invalid!! Please check your app-key!! SinricPro will not work!\r\n", socketAuthToken.toString().c_str());
188 if (!signingKey.isValid()) {
189 DEBUG_SINRIC(
"[SinricPro:begin()]: App-Secret \"%s\" is invalid!! Please check your app-secret!! SinricPro will not work!\r\n", signingKey.toString().c_str());
198 this->socketAuthToken = socketAuthToken;
199 this->signingKey = signingKey;
200 this->serverURL = serverURL;
202 _udpListener.begin(&receiveQueue);
205 template <
typename DeviceType>
206 DeviceType& SinricProClass::add(DeviceId deviceId) {
207 DeviceType* newDevice =
new DeviceType(deviceId);
208 if (DeviceId(deviceId).isValid()){
209 DEBUG_SINRIC(
"[SinricPro:add()]: Adding device with id \"%s\".\r\n", deviceId.toString().c_str());
210 newDevice->begin(
this);
212 if (socketAuthToken.isValid() && signingKey.isValid()) _begin =
true;
214 DEBUG_SINRIC(
"[SinricPro:add()]: DeviceId \"%s\" is invalid!! Device will be ignored and will NOT WORK!\r\n", deviceId.toString().c_str());
216 devices.push_back(newDevice);
220 __attribute__ ((deprecated(
"Please use DeviceType& myDevice = SinricPro.add<DeviceType>(DeviceId);")))
222 if (!newDevice->getDeviceId().isValid())
return;
223 newDevice->
begin(
this);
224 devices.push_back(newDevice);
227 __attribute__ ((deprecated(
"Please use DeviceType& myDevice = SinricPro.add<DeviceType>(DeviceId);")))
229 if (!newDevice.getDeviceId().isValid())
return;
230 newDevice.
begin(
this);
231 devices.push_back(&newDevice);
250 static bool begin_error =
false;
253 DEBUG_SINRIC(
"[SinricPro:handle()]: ERROR! SinricPro.begin() failed or was not called prior to event handler\r\n");
254 DEBUG_SINRIC(
"[SinricPro:handle()]: -Reasons include an invalid app-key, invalid app-secret or no valid deviceIds)\r\n");
255 DEBUG_SINRIC(
"[SinricPro:handle()]: -SinricPro is disabled! Check earlier log messages for details.\r\n");
262 if (!isConnected()) connect();
263 _websocketListener.handle();
264 _udpListener.handle();
266 handleReceiveQueue();
270 DynamicJsonDocument SinricProClass::prepareRequest(DeviceId deviceId,
const char* action) {
271 DynamicJsonDocument requestMessage(1024);
272 JsonObject header = requestMessage.createNestedObject(
"header");
273 header[
"payloadVersion"] = 2;
274 header[
"signatureVersion"] = 1;
276 JsonObject payload = requestMessage.createNestedObject(
"payload");
277 payload[
"action"] = action;
278 payload[
"createdAt"] = 0;
279 payload[
"deviceId"] = deviceId.toString();
280 payload[
"replyToken"] = MessageID().getID();
281 payload[
"type"] =
"request";
282 payload.createNestedObject(
"value");
283 return requestMessage;
286 void SinricProClass::handleResponse(DynamicJsonDocument& responseMessage) {
287 (void) responseMessage;
288 DEBUG_SINRIC(
"[SinricPro.handleResponse()]:\r\n");
290 #ifndef NODEBUG_SINRIC
291 serializeJsonPretty(responseMessage, DEBUG_ESP_PORT);
296 void SinricProClass::handleRequest(DynamicJsonDocument& requestMessage, interface_t Interface) {
297 DEBUG_SINRIC(
"[SinricPro.handleRequest()]: handling request\r\n");
298 #ifndef NODEBUG_SINRIC
299 serializeJsonPretty(requestMessage, DEBUG_ESP_PORT);
302 DynamicJsonDocument responseMessage = prepareResponse(requestMessage);
305 bool success =
false;
306 const char* deviceId = requestMessage[
"payload"][
"deviceId"];
307 String action = requestMessage[
"payload"][
"action"] |
"";
308 String instance = requestMessage[
"payload"][
"instanceId"] |
"";
309 JsonObject request_value = requestMessage[
"payload"][
"value"];
310 JsonObject response_value = responseMessage[
"payload"][
"value"];
312 for (
auto& device : devices) {
313 if (device->getDeviceId() == deviceId && success ==
false) {
314 SinricProRequest request {
320 success = device->handleRequest(request);
321 responseMessage[
"payload"][
"success"] = success;
323 if (responseMessageStr.length() > 0){
324 responseMessage[
"payload"][
"message"] = responseMessageStr;
325 responseMessageStr =
"";
327 responseMessage[
"payload"][
"message"] =
"Device returned an error while processing the request!";
333 String responseString;
334 serializeJson(responseMessage, responseString);
335 sendQueue.push(
new SinricProMessage(Interface, responseString.c_str()));
338 void SinricProClass::handleReceiveQueue() {
339 if (receiveQueue.size() == 0)
return;
341 DEBUG_SINRIC(
"[SinricPro.handleReceiveQueue()]: %i message(s) in receiveQueue\r\n", receiveQueue.size());
342 while (receiveQueue.size() > 0) {
343 SinricProMessage* rawMessage = receiveQueue.front();
345 DynamicJsonDocument jsonMessage(1024);
346 deserializeJson(jsonMessage, rawMessage->getMessage());
348 bool sigMatch =
false;
350 if (strncmp(rawMessage->getMessage(),
"{\"timestamp\":", 13) == 0 && strlen(rawMessage->getMessage()) <= 26) {
353 sigMatch = verifyMessage(signingKey.toString(), jsonMessage);
356 String messageType = jsonMessage[
"payload"][
"type"];
359 DEBUG_SINRIC(
"[SinricPro.handleReceiveQueue()]: Signature is valid. Processing message...\r\n");
360 extractTimestamp(jsonMessage);
361 if (messageType ==
"response") handleResponse(jsonMessage);
362 if (messageType ==
"request") handleRequest(jsonMessage, rawMessage->getInterface());
364 DEBUG_SINRIC(
"[SinricPro.handleReceiveQueue()]: Signature is invalid! Sending messsage to [dev/null] ;)\r\n");
370 void SinricProClass::handleSendQueue() {
371 if (!isConnected())
return;
372 if (!baseTimestamp)
return;
373 while (sendQueue.size() > 0) {
374 DEBUG_SINRIC(
"[SinricPro:handleSendQueue()]: %i message(s) in sendQueue\r\n", sendQueue.size());
375 DEBUG_SINRIC(
"[SinricPro:handleSendQueue()]: Sending message...\r\n");
377 SinricProMessage* rawMessage = sendQueue.front(); sendQueue.pop();
379 DynamicJsonDocument jsonMessage(1024);
380 deserializeJson(jsonMessage, rawMessage->getMessage());
382 signMessage(signingKey.toString(), jsonMessage);
386 serializeJson(jsonMessage, messageStr);
387 #ifndef NODEBUG_SINRIC
388 serializeJsonPretty(jsonMessage, DEBUG_ESP_PORT);
392 switch (rawMessage->getInterface()) {
393 case IF_WEBSOCKET: DEBUG_SINRIC(
"[SinricPro:handleSendQueue]: Sending to websocket\r\n"); _websocketListener.sendMessage(messageStr);
break;
394 case IF_UDP: DEBUG_SINRIC(
"[SinricPro:handleSendQueue]: Sending to UDP\r\n");_udpListener.sendMessage(messageStr);
break;
398 DEBUG_SINRIC(
"[SinricPro:handleSendQueue()]: message sent.\r\n");
402 void SinricProClass::connect() {
405 for (
auto& device : devices) {
406 DeviceId deviceId = device->getDeviceId();
407 if (deviceId.isValid()) {
408 if (i>0) deviceList +=
';';
409 deviceList += deviceId.toString();
415 DEBUG_SINRIC(
"[SinricPro]: ERROR! No valid devices available. Please add a valid device first!\r\n");
419 _websocketListener.begin(serverURL, socketAuthToken.toString(), deviceList, &receiveQueue);
423 void SinricProClass::stop() {
425 DEBUG_SINRIC(
"[SinricPro:stop()\r\n");
426 _websocketListener.stop();
429 bool SinricProClass::isConnected() {
430 return _websocketListener.isConnected();
443 _websocketListener.onConnected(cb);
456 _websocketListener.onDisconnected(cb);
460 void SinricProClass::reconnect() {
461 DEBUG_SINRIC(
"SinricPro:reconnect(): disconnecting\r\n");
463 DEBUG_SINRIC(
"SinricPro:reconnect(): connecting\r\n");
467 void SinricProClass::extractTimestamp(JsonDocument &message) {
468 unsigned long tempTimestamp = 0;
470 tempTimestamp = message[
"timestamp"] | 0;
472 baseTimestamp = tempTimestamp - (millis() / 1000);
473 DEBUG_SINRIC(
"[SinricPro:extractTimestamp(): Got Timestamp %lu\r\n", tempTimestamp);
478 tempTimestamp = message[
"payload"][
"createdAt"] | 0;
480 DEBUG_SINRIC(
"[SinricPro:extractTimestamp(): Got Timestamp %lu\r\n", tempTimestamp);
481 baseTimestamp = tempTimestamp - (millis() / 1000);
487 void SinricProClass::sendMessage(JsonDocument& jsonMessage) {
488 if (!isConnected()) {
489 DEBUG_SINRIC(
"[SinricPro:sendMessage()]: device is offline, message has been dropped\r\n");
492 DEBUG_SINRIC(
"[SinricPro:sendMessage()]: pushing message into sendQueue\r\n");
493 String messageString;
494 serializeJson(jsonMessage, messageString);
495 sendQueue.push(
new SinricProMessage(IF_WEBSOCKET, messageString.c_str()));
508 _websocketListener.setRestoreDeviceStates(flag);
511 DynamicJsonDocument SinricProClass::prepareResponse(JsonDocument& requestMessage) {
512 DynamicJsonDocument responseMessage(1024);
513 JsonObject header = responseMessage.createNestedObject(
"header");
514 header[
"payloadVersion"] = 2;
515 header[
"signatureVersion"] = 1;
517 JsonObject payload = responseMessage.createNestedObject(
"payload");
518 payload[
"action"] = requestMessage[
"payload"][
"action"];
519 payload[
"clientId"] = requestMessage[
"payload"][
"clientId"];
520 payload[
"createdAt"] = 0;
521 payload[
"deviceId"] = requestMessage[
"payload"][
"deviceId"];
522 if (requestMessage[
"payload"].containsKey(
"instanceId")) payload[
"instanceId"] = requestMessage[
"payload"][
"instanceId"];
523 payload[
"message"] =
"OK";
524 payload[
"replyToken"] = requestMessage[
"payload"][
"replyToken"];
525 payload[
"success"] =
false;
526 payload[
"type"] =
"response";
527 payload.createNestedObject(
"value");
528 return responseMessage;
532 DynamicJsonDocument SinricProClass::prepareEvent(DeviceId deviceId,
const char* action,
const char* cause) {
533 DynamicJsonDocument eventMessage(1024);
534 JsonObject header = eventMessage.createNestedObject(
"header");
535 header[
"payloadVersion"] = 2;
536 header[
"signatureVersion"] = 1;
538 JsonObject payload = eventMessage.createNestedObject(
"payload");
539 payload[
"action"] = action;
540 payload[
"cause"].createNestedObject(
"type");
541 payload[
"cause"][
"type"] = cause;
542 payload[
"createdAt"] = 0;
543 payload[
"deviceId"] = deviceId.toString();
544 payload[
"replyToken"] = MessageID().getID();
545 payload[
"type"] =
"event";
546 payload.createNestedObject(
"value");
550 #ifndef NOSINRIC_INSTANCE
The main class of this library, handling communication between SinricPro Server and your devices.
Definition: SinricPro.h:25
void restoreDeviceStates(bool flag)
Enable / disable restore device states function.
Definition: SinricPro.h:507
void handle()
Handles communication between device and SinricPro Server.
Definition: SinricPro.h:249
std::function< void(void)> ConnectedCallbackHandler
Callback definition for onConnected function.
Definition: SinricPro.h:41
void onDisconnected(DisconnectedCallbackHandler cb)
Set callback function for websocket disconnected event.
Definition: SinricPro.h:455
proxy operator[](const DeviceId deviceId)
operator[] is used tor create a new device instance or get an existing device instance
Definition: SinricPro.h:81
void begin(AppKey socketAuthToken, AppSecret signingKey, String serverURL=SINRICPRO_SERVER_URL)
Initializing SinricProClass to be able to connect to SinricPro Server.
Definition: SinricPro.h:182
void onConnected(ConnectedCallbackHandler cb)
Set callback function for websocket connected event.
Definition: SinricPro.h:442
std::function< void(void)> DisconnectedCallbackHandler
Callback definition for onDisconnected function.
Definition: SinricPro.h:49
unsigned long getTimestamp() override
Get the current timestamp.
Definition: SinricPro.h:91
Base class for all device types.
Definition: SinricProDevice.h:25
The main instance of SinricProClass.