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"
25 void begin(String socketAuthToken, String signingKey, String serverURL = SINRICPRO_SERVER_URL);
26 template <
typename DeviceType>
27 DeviceType& add(
const char* deviceId,
unsigned long eventWaitTime = 1000);
29 void add(SinricProDeviceInterface& newDevice);
30 void add(SinricProDeviceInterface* newDevice);
56 DynamicJsonDocument prepareResponse(JsonDocument& requestMessage);
57 DynamicJsonDocument prepareEvent(
const char* deviceId,
const char* action,
const char* cause)
override;
58 void sendMessage(JsonDocument& jsonMessage)
override;
61 proxy(
SinricProClass* ptr, String deviceId) : ptr(ptr), deviceId(deviceId) {}
64 template <
typename DeviceType>
65 operator DeviceType&() {
return as<DeviceType>(); }
66 template <
typename DeviceType>
67 DeviceType& as() {
return ptr->getDeviceInstance<DeviceType>(deviceId); }
85 proxy
operator[](
const String deviceId) {
return proxy(
this, deviceId); }
88 void setResponseMessage(String &&message) { responseMessageStr = message; }
95 unsigned long getTimestamp()
override {
return baseTimestamp + (millis()/1000); }
97 void handleReceiveQueue();
98 void handleSendQueue();
100 void handleRequest(DynamicJsonDocument& requestMessage, interface_t Interface);
101 void handleResponse(DynamicJsonDocument& responseMessage);
103 DynamicJsonDocument prepareRequest(
const char* deviceId,
const char* action);
109 void onConnect() { DEBUG_SINRIC(
"[SinricPro]: Connected to \"%s\"!]\r\n", serverURL.c_str()); }
110 void onDisconnect() { DEBUG_SINRIC(
"[SinricPro]: Disconnect\r\n"); }
112 bool verifyDeviceId(
const char*
id);
113 bool verifyAppKey(
const char* key);
114 bool verifyAppSecret(
const char* secret);
115 void extractTimestamp(JsonDocument &message);
117 SinricProDeviceInterface* getDevice(String deviceId);
119 template <
typename DeviceType>
120 DeviceType& getDeviceInstance(String deviceId);
122 std::vector<SinricProDeviceInterface*> devices;
123 String socketAuthToken;
127 websocketListener _websocketListener;
128 udpListener _udpListener;
129 SinricProQueue_t receiveQueue;
130 SinricProQueue_t sendQueue;
132 unsigned long baseTimestamp = 0;
135 String responseMessageStr =
"";
138 SinricProDeviceInterface* SinricProClass::getDevice(String deviceId) {
139 for (
auto& device : devices) {
140 if (deviceId == String(device->getDeviceId()))
return device;
145 template <
typename DeviceType>
146 DeviceType& SinricProClass::getDeviceInstance(String deviceId) {
147 DeviceType* tmp_device = (DeviceType*) getDevice(deviceId);
148 if (tmp_device)
return *tmp_device;
150 DEBUG_SINRIC(
"[SinricPro]: Device \"%s\" does not exist. Creating new device\r\n", deviceId.c_str());
151 DeviceType& tmp_deviceInstance = add<DeviceType>(deviceId.c_str());
154 DEBUG_SINRIC(
"[SinricPro]: Reconnecting to server.\r\n");
158 return tmp_deviceInstance;
179 if (!verifyAppKey(socketAuthToken.c_str())) {
180 DEBUG_SINRIC(
"[SinricPro:begin()]: App-Key \"%s\" is invalid!! Please check your app-key!! SinricPro will not work!\r\n", socketAuthToken.c_str());
183 if (!verifyAppSecret(signingKey.c_str())) {
184 DEBUG_SINRIC(
"[SinricPro:begin()]: App-Secret \"%s\" is invalid!! Please check your app-secret!! SinricPro will not work!\r\n", signingKey.c_str());
194 this->socketAuthToken = socketAuthToken;
195 this->signingKey = signingKey;
196 this->serverURL = serverURL;
198 _udpListener.begin(&receiveQueue);
201 template <
typename DeviceType>
202 DeviceType& SinricProClass::add(
const char* deviceId,
unsigned long eventWaitTime) {
203 DeviceType* newDevice =
new DeviceType(deviceId, eventWaitTime);
204 if (verifyDeviceId(deviceId)){
205 DEBUG_SINRIC(
"[SinricPro:add()]: Adding device with id \"%s\".\r\n", deviceId);
206 newDevice->begin(
this);
207 if (verifyAppKey(socketAuthToken.c_str()) && verifyAppSecret(signingKey.c_str())) _begin =
true;
209 DEBUG_SINRIC(
"[SinricPro:add()]: DeviceId \"%s\" is invalid!! Device will be ignored and will NOT WORK!\r\n", deviceId);
211 devices.push_back(newDevice);
215 __attribute__ ((deprecated(
"Please use DeviceType& myDevice = SinricPro.add<DeviceType>(DeviceId);")))
217 if (!verifyDeviceId(newDevice->getDeviceId()))
return;
218 newDevice->
begin(
this);
219 devices.push_back(newDevice);
222 __attribute__ ((deprecated(
"Please use DeviceType& myDevice = SinricPro.add<DeviceType>(DeviceId);")))
224 if (!verifyDeviceId(newDevice.getDeviceId()))
return;
225 newDevice.
begin(
this);
226 devices.push_back(&newDevice);
245 static bool begin_error =
false;
248 DEBUG_SINRIC(
"[SinricPro:handle()]: ERROR! SinricPro.begin() failed or was not called prior to event handler\r\n");
249 DEBUG_SINRIC(
"[SinricPro:handle()]: -Reasons include an invalid app-key, invalid app-secret or no valid deviceIds)\r\n");
250 DEBUG_SINRIC(
"[SinricPro:handle()]: -SinricPro is disabled! Check earlier log messages for details.\r\n");
257 if (!isConnected()) connect();
258 _websocketListener.handle();
259 _udpListener.handle();
261 handleReceiveQueue();
265 DynamicJsonDocument SinricProClass::prepareRequest(
const char* deviceId,
const char* action) {
266 DynamicJsonDocument requestMessage(1024);
267 JsonObject header = requestMessage.createNestedObject(
"header");
268 header[
"payloadVersion"] = 2;
269 header[
"signatureVersion"] = 1;
271 JsonObject payload = requestMessage.createNestedObject(
"payload");
272 payload[
"action"] = action;
273 payload[
"createdAt"] = 0;
274 payload[
"deviceId"] = deviceId;
275 payload[
"replyToken"] = MessageID().getID();
276 payload[
"type"] =
"request";
277 payload.createNestedObject(
"value");
278 return requestMessage;
281 void SinricProClass::handleResponse(DynamicJsonDocument& responseMessage) {
282 DEBUG_SINRIC(
"[SinricPro.handleResponse()]:\r\n");
284 #ifndef NODEBUG_SINRIC
285 serializeJsonPretty(responseMessage, DEBUG_ESP_PORT);
290 void SinricProClass::handleRequest(DynamicJsonDocument& requestMessage, interface_t Interface) {
291 DEBUG_SINRIC(
"[SinricPro.handleRequest()]: handling request\r\n");
292 #ifndef NODEBUG_SINRIC
293 serializeJsonPretty(requestMessage, DEBUG_ESP_PORT);
296 DynamicJsonDocument responseMessage = prepareResponse(requestMessage);
299 bool success =
false;
300 const char* deviceId = requestMessage[
"payload"][
"deviceId"];
301 const char* action = requestMessage[
"payload"][
"action"];
302 JsonObject request_value = requestMessage[
"payload"][
"value"];
303 JsonObject response_value = responseMessage[
"payload"][
"value"];
305 for (
auto& device : devices) {
306 if (strcmp(deviceId, device->getDeviceId()) == 0 && success ==
false) {
307 success = device->handleRequest(deviceId, action, request_value, response_value);
308 responseMessage[
"payload"][
"success"] = success;
310 if (responseMessageStr.length() > 0){
311 responseMessage[
"payload"][
"message"] = responseMessageStr;
312 responseMessageStr =
"";
314 responseMessage[
"payload"][
"message"] =
"Device returned an error while processing the request!";
320 String responseString;
321 serializeJson(responseMessage, responseString);
322 sendQueue.push(
new SinricProMessage(Interface, responseString.c_str()));
325 void SinricProClass::handleReceiveQueue() {
326 if (receiveQueue.count() == 0)
return;
328 DEBUG_SINRIC(
"[SinricPro.handleReceiveQueue()]: %i message(s) in receiveQueue\r\n", receiveQueue.count());
329 while (receiveQueue.count() > 0) {
330 SinricProMessage* rawMessage = receiveQueue.pop();
331 DynamicJsonDocument jsonMessage(1024);
332 deserializeJson(jsonMessage, rawMessage->getMessage());
334 bool sigMatch =
false;
336 if (strncmp(rawMessage->getMessage(),
"{\"timestamp\":", 13) == 0 && strlen(rawMessage->getMessage()) <= 26) {
339 sigMatch = verifyMessage(signingKey, jsonMessage);
342 String messageType = jsonMessage[
"payload"][
"type"];
345 DEBUG_SINRIC(
"[SinricPro.handleReceiveQueue()]: Signature is valid. Processing message...\r\n");
346 extractTimestamp(jsonMessage);
347 if (messageType ==
"response") handleResponse(jsonMessage);
348 if (messageType ==
"request") handleRequest(jsonMessage, rawMessage->getInterface());
350 DEBUG_SINRIC(
"[SinricPro.handleReceiveQueue()]: Signature is invalid! Sending messsage to [dev/null] ;)\r\n");
356 void SinricProClass::handleSendQueue() {
357 if (!isConnected())
return;
358 if (!baseTimestamp)
return;
359 while (sendQueue.count() > 0) {
360 DEBUG_SINRIC(
"[SinricPro:handleSendQueue()]: %i message(s) in sendQueue\r\n", sendQueue.count());
361 DEBUG_SINRIC(
"[SinricPro:handleSendQueue()]: Sending message...\r\n");
363 SinricProMessage* rawMessage = sendQueue.pop();
365 DynamicJsonDocument jsonMessage(1024);
366 deserializeJson(jsonMessage, rawMessage->getMessage());
368 signMessage(signingKey, jsonMessage);
372 serializeJson(jsonMessage, messageStr);
373 #ifndef NODEBUG_SINRIC
374 serializeJsonPretty(jsonMessage, DEBUG_ESP_PORT);
378 switch (rawMessage->getInterface()) {
379 case IF_WEBSOCKET: DEBUG_SINRIC(
"[SinricPro:handleSendQueue]: Sending to websocket\r\n"); _websocketListener.sendMessage(messageStr);
break;
380 case IF_UDP: DEBUG_SINRIC(
"[SinricPro:handleSendQueue]: Sending to UDP\r\n");_udpListener.sendMessage(messageStr);
break;
384 DEBUG_SINRIC(
"[SinricPro:handleSendQueue()]: message sent.\r\n");
388 void SinricProClass::connect() {
391 for (
auto& device : devices) {
392 const char* deviceId = device->getDeviceId();
393 if (verifyDeviceId(deviceId)) {
394 if (i>0) deviceList +=
';';
395 deviceList += String(deviceId);
401 DEBUG_SINRIC(
"[SinricPro]: ERROR! No valid devices available. Please add a valid device first!\r\n");
405 _websocketListener.begin(serverURL, socketAuthToken, deviceList, &receiveQueue);
409 void SinricProClass::stop() {
410 DEBUG_SINRIC(
"[SinricPro:stop()\r\n");
411 _websocketListener.stop();
414 bool SinricProClass::isConnected() {
415 return _websocketListener.isConnected();
428 _websocketListener.onConnected(cb);
441 _websocketListener.onDisconnected(cb);
445 void SinricProClass::reconnect() {
446 DEBUG_SINRIC(
"SinricPro:reconnect(): disconnecting\r\n");
448 DEBUG_SINRIC(
"SinricPro:reconnect(): connecting\r\n");
452 bool SinricProClass::verifyDeviceId(
const char*
id) {
453 if (strlen(
id) != 24)
return false;
455 return sscanf(
id,
"%4x%4x%4x%4x%4x%4x%c",
456 &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &tmp_c) == 6;
459 bool SinricProClass::verifyAppKey(
const char* key) {
460 if (strlen(key) != 36)
return false;
462 return sscanf(key,
"%4x%4x-%4x-%4x-%4x-%4x%4x%4x%c",
463 &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &tmp_c) == 8;
466 bool SinricProClass::verifyAppSecret(
const char* secret) {
467 if (strlen(secret) != 73)
return false;
469 return sscanf(secret,
"%4x%4x-%4x-%4x-%4x-%4x%4x%4x-%4x%4x-%4x-%4x-%4x-%4x%4x%4x%c",
470 &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &tmp_c) == 16;
474 void SinricProClass::extractTimestamp(JsonDocument &message) {
475 unsigned long tempTimestamp = 0;
477 tempTimestamp = message[
"timestamp"] | 0;
479 baseTimestamp = tempTimestamp - (millis() / 1000);
480 DEBUG_SINRIC(
"[SinricPro:extractTimestamp(): Got Timestamp %lu\r\n", tempTimestamp);
485 tempTimestamp = message[
"payload"][
"createdAt"] | 0;
487 DEBUG_SINRIC(
"[SinricPro:extractTimestamp(): Got Timestamp %lu\r\n", tempTimestamp);
488 baseTimestamp = tempTimestamp - (millis() / 1000);
494 void SinricProClass::sendMessage(JsonDocument& jsonMessage) {
495 DEBUG_SINRIC(
"[SinricPro:sendMessage()]: pushing message into sendQueue\r\n");
496 String messageString;
497 serializeJson(jsonMessage, messageString);
498 sendQueue.push(
new SinricProMessage(IF_WEBSOCKET, messageString.c_str()));
511 _websocketListener.setRestoreDeviceStates(flag);
514 DynamicJsonDocument SinricProClass::prepareResponse(JsonDocument& requestMessage) {
515 DynamicJsonDocument responseMessage(1024);
516 JsonObject header = responseMessage.createNestedObject(
"header");
517 header[
"payloadVersion"] = 2;
518 header[
"signatureVersion"] = 1;
520 JsonObject payload = responseMessage.createNestedObject(
"payload");
521 payload[
"action"] = requestMessage[
"payload"][
"action"];
522 payload[
"clientId"] = requestMessage[
"payload"][
"clientId"];
523 payload[
"createdAt"] = 0;
524 payload[
"deviceId"] = requestMessage[
"payload"][
"deviceId"];
525 payload[
"message"] =
"OK";
526 payload[
"replyToken"] = requestMessage[
"payload"][
"replyToken"];
527 payload[
"success"] =
false;
528 payload[
"type"] =
"response";
529 payload.createNestedObject(
"value");
530 return responseMessage;
534 DynamicJsonDocument SinricProClass::prepareEvent(
const char* deviceId,
const char* action,
const char* cause) {
535 DynamicJsonDocument eventMessage(1024);
536 JsonObject header = eventMessage.createNestedObject(
"header");
537 header[
"payloadVersion"] = 2;
538 header[
"signatureVersion"] = 1;
540 JsonObject payload = eventMessage.createNestedObject(
"payload");
541 payload[
"action"] = action;
542 payload[
"cause"].createNestedObject(
"type");
543 payload[
"cause"][
"type"] = cause;
544 payload[
"createdAt"] = 0;
545 payload[
"deviceId"] = deviceId;
546 payload[
"replyToken"] = MessageID().getID();
547 payload[
"type"] =
"event";
548 payload.createNestedObject(
"value");
552 #ifndef NOSINRIC_INSTANCE