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