SinricPro Library
SinricProWebsocket.h
1 /*
2  * Copyright (c) 2019 Sinric. All rights reserved.
3  * Licensed under Creative Commons Attribution-Share Alike (CC BY-SA)
4  *
5  * This file is part of the Sinric Pro (https://github.com/sinricpro/)
6  */
7 
8 
9 #ifndef _SINRICPRO_WEBSOCKET_H__
10 #define _SINRICPRO_WEBSOCKET_H__
11 
12 #if defined ESP8266
13  #include <ESP8266WiFi.h>
14 #endif
15 #if defined ESP32
16  #include <WiFi.h>
17 #endif
18 
19 //#include <WebSockets.h>
20 #include <WebSocketsClient.h>
21 
22 #include <ArduinoJson.h>
23 #include "SinricProDebug.h"
24 #include "SinricProConfig.h"
25 #include "SinricProQueue.h"
26 #include "SinricProInterface.h"
27 
28 
29 #if !defined(WEBSOCKETS_VERSION_INT) || (WEBSOCKETS_VERSION_INT < 2003003)
30 #error "Wrong WebSockets Version! Minimum Version is 2.3.3!!!"
31 #endif
32 
33 class AdvWebSocketsClient : public WebSocketsClient {
34  public:
35  void onPong(std::function<void(uint32_t)> cb) { _rttCb = cb; }
36  protected:
37  void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin) {
38  if ((opcode == WSop_pong)&& (_rttCb)) {
39  _rttCb(millis()-_client.lastPing);
40  }
41  WebSocketsClient::messageReceived(client, opcode, payload, length, fin);
42  }
43  private:
44  std::function<void(uint32_t)> _rttCb = nullptr;
45 };
46 
47 class websocketListener
48 {
49  public:
50  typedef std::function<void(void)> wsConnectedCallback;
51  typedef std::function<void(void)> wsDisconnectedCallback;
52 
53  websocketListener();
54  ~websocketListener();
55 
56  void begin(String server, String socketAuthToken, String deviceIds, SinricProQueue_t* receiveQueue);
57  void handle();
58  void stop();
59  bool isConnected() { return _isConnected; }
60  void setRestoreDeviceStates(bool flag) { this->restoreDeviceStates = flag; };
61 
62  void sendMessage(String &message);
63 
64  void onConnected(wsConnectedCallback callback) { _wsConnectedCb = callback; }
65  void onDisconnected(wsDisconnectedCallback callback) { _wsDisconnectedCb = callback; }
66  void onPong(std::function<void(uint32_t)> cb) { webSocket.onPong(cb); }
67 
68  void disconnect() { webSocket.disconnect(); }
69  private:
70  bool _begin = false;
71  bool _isConnected = false;
72  bool restoreDeviceStates = false;
73 
74  AdvWebSocketsClient webSocket;
75 
76  wsConnectedCallback _wsConnectedCb;
77  wsDisconnectedCallback _wsDisconnectedCb;
78 
79  void webSocketEvent(WStype_t type, uint8_t * payload, size_t length);
80  void setExtraHeaders();
81  SinricProQueue_t* receiveQueue;
82  String deviceIds;
83  String socketAuthToken;
84 };
85 
86 void websocketListener::setExtraHeaders() {
87  String headers = "appkey:" + socketAuthToken + "\r\n";
88  headers += "deviceids:" + deviceIds + "\r\n";
89  headers += "restoredevicestates:" + String(restoreDeviceStates?"true":"false") + "\r\n";
90  headers += "ip:" + WiFi.localIP().toString() + "\r\n";
91  headers += "mac:" + WiFi.macAddress() + "\r\n";
92  #ifdef ESP8266
93  headers += "platform:ESP8266\r\n";
94  #endif
95  #ifdef ESP32
96  headers += "platform:ESP32\r\n";
97  #endif
98  headers += "version:" + String(SINRICPRO_VERSION);
99  DEBUG_SINRIC("[SinricPro:Websocket]: headers: \r\n%s\r\n", headers.c_str());
100  webSocket.setExtraHeaders(headers.c_str());
101 }
102 
103 websocketListener::websocketListener() : _isConnected(false) {}
104 
105 websocketListener::~websocketListener() {
106  stop();
107 }
108 
109 void websocketListener::begin(String server, String socketAuthToken, String deviceIds, SinricProQueue_t* receiveQueue) {
110  if (_begin) return;
111  _begin = true;
112 
113  this->receiveQueue = receiveQueue;
114  this->socketAuthToken = socketAuthToken;
115  this->deviceIds = deviceIds;
116 
117 #ifdef WEBSOCKET_SSL
118  DEBUG_SINRIC("[SinricPro:Websocket]: Connecting to WebSocket Server using SSL (%s)\r\n", server.c_str());
119 #else
120  DEBUG_SINRIC("[SinricPro:Websocket]: Connecting to WebSocket Server (%s)\r\n", server.c_str());
121 #endif
122 
123  if (_isConnected) {
124  stop();
125  }
126  setExtraHeaders();
127  webSocket.onEvent([&](WStype_t type, uint8_t * payload, size_t length) { webSocketEvent(type, payload, length); });
128  webSocket.enableHeartbeat(WEBSOCKET_PING_INTERVAL, WEBSOCKET_PING_TIMEOUT, WEBSOCKET_RETRY_COUNT);
129 #ifdef WEBSOCKET_SSL
130  webSocket.beginSSL(server.c_str(), SINRICPRO_SERVER_SSL_PORT, "/");
131 #else
132  webSocket.begin(server.c_str(), SINRICPRO_SERVER_PORT, "/"); // server address, port and URL
133 #endif
134 }
135 
136 void websocketListener::handle() {
137  webSocket.loop();
138 }
139 
140 void websocketListener::stop() {
141  if (_isConnected) {
142  webSocket.disconnect();
143  }
144  _begin = false;
145 }
146 
147 void websocketListener::sendMessage(String &message) {
148  webSocket.sendTXT(message);
149 }
150 
151 
152 void websocketListener::webSocketEvent(WStype_t type, uint8_t * payload, size_t length)
153 {
154  (void) length;
155  switch (type) {
156  case WStype_DISCONNECTED:
157  if (_isConnected) {
158  DEBUG_SINRIC("[SinricPro:Websocket]: disconnected\r\n");
159  if (_wsDisconnectedCb) _wsDisconnectedCb();
160  _isConnected = false;
161  }
162  break;
163  case WStype_CONNECTED:
164  _isConnected = true;
165  DEBUG_SINRIC("[SinricPro:Websocket]: connected\r\n");
166  if (_wsConnectedCb) _wsConnectedCb();
167  if (restoreDeviceStates) {
168  restoreDeviceStates=false;
169  setExtraHeaders();
170  }
171  break;
172  case WStype_TEXT: {
173  SinricProMessage* request = new SinricProMessage(IF_WEBSOCKET, (char*)payload);
174  DEBUG_SINRIC("[SinricPro:Websocket]: receiving data\r\n");
175  receiveQueue->push(request);
176  break;
177  }
178  default: break;
179  }
180 }
181 
182 #endif