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"
26 void begin(AppKey socketAuthToken, AppSecret signingKey, String serverURL = SINRICPRO_SERVER_URL);
28 template <
typename DeviceType>
29 DeviceType& add(DeviceId deviceId);
31 void add(SinricProDeviceInterface& newDevice);
32 void add(SinricProDeviceInterface* newDevice);
55 void onPong(std::function<
void(uint32_t)> cb) { _websocketListener.onPong(cb); }
59 DynamicJsonDocument prepareResponse(JsonDocument& requestMessage);
60 DynamicJsonDocument prepareEvent(DeviceId deviceId,
const char* action,
const char* cause)
override;
61 void sendMessage(JsonDocument& jsonMessage)
override;
64 proxy(
SinricProClass* ptr, DeviceId deviceId) : ptr(ptr), deviceId(deviceId) {}
67 template <
typename DeviceType>
68 operator DeviceType&() {
return as<DeviceType>(); }
69 template <
typename DeviceType>
70 DeviceType& as() {
return ptr->getDeviceInstance<DeviceType>(deviceId); }
88 proxy
operator[](
const DeviceId deviceId) {
return proxy(
this, deviceId); }
91 void setResponseMessage(String &&message) { responseMessageStr = message; }
98 unsigned long getTimestamp()
override {
return baseTimestamp + (millis()/1000); }
100 void handleReceiveQueue();
101 void handleSendQueue();
103 void handleRequest(DynamicJsonDocument& requestMessage, interface_t Interface);
104 void handleResponse(DynamicJsonDocument& responseMessage);
106 DynamicJsonDocument prepareRequest(DeviceId deviceId,
const char* action);
112 void onConnect() { DEBUG_SINRIC(
"[SinricPro]: Connected to \"%s\"!]\r\n", serverURL.c_str()); }
113 void onDisconnect() { DEBUG_SINRIC(
"[SinricPro]: Disconnect\r\n"); }
115 void extractTimestamp(JsonDocument &message);
117 SinricProDeviceInterface* getDevice(DeviceId deviceId);
119 template <
typename DeviceType>
120 DeviceType& getDeviceInstance(DeviceId deviceId);
122 std::vector<SinricProDeviceInterface*> devices;
124 AppKey socketAuthToken;
125 AppSecret signingKey;
128 websocketListener _websocketListener;
129 udpListener _udpListener;
130 SinricProQueue_t receiveQueue;
131 SinricProQueue_t sendQueue;
133 unsigned long baseTimestamp = 0;
136 String responseMessageStr =
"";
139 SinricProDeviceInterface* SinricProClass::getDevice(DeviceId deviceId) {
140 for (
auto& device : devices) {
141 if (deviceId == device->getDeviceId())
return device;
146 template <
typename DeviceType>
147 DeviceType& SinricProClass::getDeviceInstance(DeviceId deviceId) {
148 DeviceType* tmp_device = (DeviceType*) getDevice(deviceId);
149 if (tmp_device)
return *tmp_device;
151 DEBUG_SINRIC(
"[SinricPro]: Device \"%s\" does not exist. Creating new device\r\n", deviceId.toString().c_str());
152 DeviceType& tmp_deviceInstance = add<DeviceType>(deviceId);
155 DEBUG_SINRIC(
"[SinricPro]: Reconnecting to server.\r\n");
159 return tmp_deviceInstance;
180 if (!socketAuthToken.isValid()) {
181 DEBUG_SINRIC(
"[SinricPro:begin()]: App-Key \"%s\" is invalid!! Please check your app-key!! SinricPro will not work!\r\n", socketAuthToken.toString().c_str());
184 if (!signingKey.isValid()) {
185 DEBUG_SINRIC(
"[SinricPro:begin()]: App-Secret \"%s\" is invalid!! Please check your app-secret!! SinricPro will not work!\r\n", signingKey.toString().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(DeviceId deviceId) {
203 DeviceType* newDevice =
new DeviceType(deviceId);
204 if (DeviceId(deviceId).isValid()){
205 DEBUG_SINRIC(
"[SinricPro:add()]: Adding device with id \"%s\".\r\n", deviceId.toString().c_str());
206 newDevice->begin(
this);
208 if (socketAuthToken.isValid() && signingKey.isValid()) _begin =
true;
210 DEBUG_SINRIC(
"[SinricPro:add()]: DeviceId \"%s\" is invalid!! Device will be ignored and will NOT WORK!\r\n", deviceId.toString().c_str());
212 devices.push_back(newDevice);
216 __attribute__ ((deprecated(
"Please use DeviceType& myDevice = SinricPro.add<DeviceType>(DeviceId);")))
218 if (!newDevice->getDeviceId().isValid())
return;
219 newDevice->
begin(
this);
220 devices.push_back(newDevice);
223 __attribute__ ((deprecated(
"Please use DeviceType& myDevice = SinricPro.add<DeviceType>(DeviceId);")))
225 if (!newDevice.getDeviceId().isValid())
return;
226 newDevice.
begin(
this);
227 devices.push_back(&newDevice);
246 static bool begin_error =
false;
249 DEBUG_SINRIC(
"[SinricPro:handle()]: ERROR! SinricPro.begin() failed or was not called prior to event handler\r\n");
250 DEBUG_SINRIC(
"[SinricPro:handle()]: -Reasons include an invalid app-key, invalid app-secret or no valid deviceIds)\r\n");
251 DEBUG_SINRIC(
"[SinricPro:handle()]: -SinricPro is disabled! Check earlier log messages for details.\r\n");
258 if (!isConnected()) connect();
259 _websocketListener.handle();
260 _udpListener.handle();
262 handleReceiveQueue();
266 DynamicJsonDocument SinricProClass::prepareRequest(DeviceId deviceId,
const char* action) {
267 DynamicJsonDocument requestMessage(1024);
268 JsonObject header = requestMessage.createNestedObject(
"header");
269 header[
"payloadVersion"] = 2;
270 header[
"signatureVersion"] = 1;
272 JsonObject payload = requestMessage.createNestedObject(
"payload");
273 payload[
"action"] = action;
274 payload[
"createdAt"] = 0;
275 payload[
"deviceId"] = deviceId.toString();
276 payload[
"replyToken"] = MessageID().getID();
277 payload[
"type"] =
"request";
278 payload.createNestedObject(
"value");
279 return requestMessage;
282 void SinricProClass::handleResponse(DynamicJsonDocument& responseMessage) {
283 DEBUG_SINRIC(
"[SinricPro.handleResponse()]:\r\n");
285 #ifndef NODEBUG_SINRIC
286 serializeJsonPretty(responseMessage, DEBUG_ESP_PORT);
291 void SinricProClass::handleRequest(DynamicJsonDocument& requestMessage, interface_t Interface) {
292 DEBUG_SINRIC(
"[SinricPro.handleRequest()]: handling request\r\n");
293 #ifndef NODEBUG_SINRIC
294 serializeJsonPretty(requestMessage, DEBUG_ESP_PORT);
297 DynamicJsonDocument responseMessage = prepareResponse(requestMessage);
300 bool success =
false;
301 const char* deviceId = requestMessage[
"payload"][
"deviceId"];
302 const char* action = requestMessage[
"payload"][
"action"];
303 JsonObject request_value = requestMessage[
"payload"][
"value"];
304 JsonObject response_value = responseMessage[
"payload"][
"value"];
306 for (
auto& device : devices) {
307 if (device->getDeviceId() == deviceId && success ==
false) {
308 success = device->handleRequest(deviceId, action, request_value, response_value);
309 responseMessage[
"payload"][
"success"] = success;
311 if (responseMessageStr.length() > 0){
312 responseMessage[
"payload"][
"message"] = responseMessageStr;
313 responseMessageStr =
"";
315 responseMessage[
"payload"][
"message"] =
"Device returned an error while processing the request!";
321 String responseString;
322 serializeJson(responseMessage, responseString);
323 sendQueue.push(
new SinricProMessage(Interface, responseString.c_str()));
326 void SinricProClass::handleReceiveQueue() {
327 if (receiveQueue.count() == 0)
return;
329 DEBUG_SINRIC(
"[SinricPro.handleReceiveQueue()]: %i message(s) in receiveQueue\r\n", receiveQueue.count());
330 while (receiveQueue.count() > 0) {
331 SinricProMessage* rawMessage = receiveQueue.pop();
332 DynamicJsonDocument jsonMessage(1024);
333 deserializeJson(jsonMessage, rawMessage->getMessage());
335 bool sigMatch =
false;
337 if (strncmp(rawMessage->getMessage(),
"{\"timestamp\":", 13) == 0 && strlen(rawMessage->getMessage()) <= 26) {
340 sigMatch = verifyMessage(signingKey.toString(), jsonMessage);
343 String messageType = jsonMessage[
"payload"][
"type"];
346 DEBUG_SINRIC(
"[SinricPro.handleReceiveQueue()]: Signature is valid. Processing message...\r\n");
347 extractTimestamp(jsonMessage);
348 if (messageType ==
"response") handleResponse(jsonMessage);
349 if (messageType ==
"request") handleRequest(jsonMessage, rawMessage->getInterface());
351 DEBUG_SINRIC(
"[SinricPro.handleReceiveQueue()]: Signature is invalid! Sending messsage to [dev/null] ;)\r\n");
357 void SinricProClass::handleSendQueue() {
358 if (!isConnected())
return;
359 if (!baseTimestamp)
return;
360 while (sendQueue.count() > 0) {
361 DEBUG_SINRIC(
"[SinricPro:handleSendQueue()]: %i message(s) in sendQueue\r\n", sendQueue.count());
362 DEBUG_SINRIC(
"[SinricPro:handleSendQueue()]: Sending message...\r\n");
364 SinricProMessage* rawMessage = sendQueue.pop();
366 DynamicJsonDocument jsonMessage(1024);
367 deserializeJson(jsonMessage, rawMessage->getMessage());
369 signMessage(signingKey.toString(), jsonMessage);
373 serializeJson(jsonMessage, messageStr);
374 #ifndef NODEBUG_SINRIC
375 serializeJsonPretty(jsonMessage, DEBUG_ESP_PORT);
379 switch (rawMessage->getInterface()) {
380 case IF_WEBSOCKET: DEBUG_SINRIC(
"[SinricPro:handleSendQueue]: Sending to websocket\r\n"); _websocketListener.sendMessage(messageStr);
break;
381 case IF_UDP: DEBUG_SINRIC(
"[SinricPro:handleSendQueue]: Sending to UDP\r\n");_udpListener.sendMessage(messageStr);
break;
385 DEBUG_SINRIC(
"[SinricPro:handleSendQueue()]: message sent.\r\n");
389 void SinricProClass::connect() {
392 for (
auto& device : devices) {
393 DeviceId deviceId = device->getDeviceId();
394 if (deviceId.isValid()) {
395 if (i>0) deviceList +=
';';
396 deviceList += deviceId.toString();
402 DEBUG_SINRIC(
"[SinricPro]: ERROR! No valid devices available. Please add a valid device first!\r\n");
406 _websocketListener.begin(serverURL, socketAuthToken.toString(), deviceList, &receiveQueue);
410 void SinricProClass::stop() {
412 DEBUG_SINRIC(
"[SinricPro:stop()\r\n");
413 _websocketListener.stop();
416 bool SinricProClass::isConnected() {
417 return _websocketListener.isConnected();
430 _websocketListener.onConnected(cb);
443 _websocketListener.onDisconnected(cb);
447 void SinricProClass::reconnect() {
448 DEBUG_SINRIC(
"SinricPro:reconnect(): disconnecting\r\n");
450 DEBUG_SINRIC(
"SinricPro:reconnect(): connecting\r\n");
454 void SinricProClass::extractTimestamp(JsonDocument &message) {
455 unsigned long tempTimestamp = 0;
457 tempTimestamp = message[
"timestamp"] | 0;
459 baseTimestamp = tempTimestamp - (millis() / 1000);
460 DEBUG_SINRIC(
"[SinricPro:extractTimestamp(): Got Timestamp %lu\r\n", tempTimestamp);
465 tempTimestamp = message[
"payload"][
"createdAt"] | 0;
467 DEBUG_SINRIC(
"[SinricPro:extractTimestamp(): Got Timestamp %lu\r\n", tempTimestamp);
468 baseTimestamp = tempTimestamp - (millis() / 1000);
474 void SinricProClass::sendMessage(JsonDocument& jsonMessage) {
475 if (!isConnected()) {
476 DEBUG_SINRIC(
"[SinricPro:sendMessage()]: device is offline, message has been dropped\r\n");
479 DEBUG_SINRIC(
"[SinricPro:sendMessage()]: pushing message into sendQueue\r\n");
480 String messageString;
481 serializeJson(jsonMessage, messageString);
482 sendQueue.push(
new SinricProMessage(IF_WEBSOCKET, messageString.c_str()));
495 _websocketListener.setRestoreDeviceStates(flag);
498 DynamicJsonDocument SinricProClass::prepareResponse(JsonDocument& requestMessage) {
499 DynamicJsonDocument responseMessage(1024);
500 JsonObject header = responseMessage.createNestedObject(
"header");
501 header[
"payloadVersion"] = 2;
502 header[
"signatureVersion"] = 1;
504 JsonObject payload = responseMessage.createNestedObject(
"payload");
505 payload[
"action"] = requestMessage[
"payload"][
"action"];
506 payload[
"clientId"] = requestMessage[
"payload"][
"clientId"];
507 payload[
"createdAt"] = 0;
508 payload[
"deviceId"] = requestMessage[
"payload"][
"deviceId"];
509 payload[
"message"] =
"OK";
510 payload[
"replyToken"] = requestMessage[
"payload"][
"replyToken"];
511 payload[
"success"] =
false;
512 payload[
"type"] =
"response";
513 payload.createNestedObject(
"value");
514 return responseMessage;
518 DynamicJsonDocument SinricProClass::prepareEvent(DeviceId deviceId,
const char* action,
const char* cause) {
519 DynamicJsonDocument eventMessage(1024);
520 JsonObject header = eventMessage.createNestedObject(
"header");
521 header[
"payloadVersion"] = 2;
522 header[
"signatureVersion"] = 1;
524 JsonObject payload = eventMessage.createNestedObject(
"payload");
525 payload[
"action"] = action;
526 payload[
"cause"].createNestedObject(
"type");
527 payload[
"cause"][
"type"] = cause;
528 payload[
"createdAt"] = 0;
529 payload[
"deviceId"] = deviceId.toString();
530 payload[
"replyToken"] = MessageID().getID();
531 payload[
"type"] =
"event";
532 payload.createNestedObject(
"value");
536 #ifndef NOSINRIC_INSTANCE