SinricPro Library
Loading...
Searching...
No Matches
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#pragma once
9
10#if defined(ESP8266)
11 #include <ESP8266WiFi.h>
12#elif defined(ESP32) || defined(ARDUINO_ARCH_RP2040)
13 #include <WiFi.h>
14#endif
15
16#include <ArduinoJson.h>
17#include <WebSocketsClient.h>
18
19#include "SinricProConfig.h"
20#include "SinricProDebug.h"
21#include "SinricProInterface.h"
22#include "SinricProNamespace.h"
23#include "SinricProQueue.h"
24namespace SINRICPRO_NAMESPACE {
25
26enum class ConnectionState {
27 disconnected,
28 connecting,
29 connected
30};
31
32#if !defined(WEBSOCKETS_VERSION_INT) || (WEBSOCKETS_VERSION_INT < 2003005)
33#error "Wrong WebSockets Version! Minimum Version is 2.3.5!!!"
34#endif
35
36using wsConnectedCallback = std::function<void(void)>;
37using wsDisconnectedCallback = std::function<void(void)>;
38using wsPongCallback = std::function<void(uint32_t)>;
39
40class WebsocketListener : protected WebSocketsClient {
41 public:
42 WebsocketListener();
43 ~WebsocketListener();
44
45 void begin(String server, String appKey, String deviceIds, SinricProQueue_t* receiveQueue);
46 void handle();
47 void stop();
48 void setRestoreDeviceStates(bool flag);
49
50 void sendMessage(String& message);
51
52 void onConnected(wsConnectedCallback callback);
53 void onDisconnected(wsDisconnectedCallback callback);
54 void onPong(wsPongCallback callback);
55
56 using WebSocketsClient::disconnect;
57 using WebSocketsClient::isConnected;
58
59 protected:
60 bool _begin;
61 bool restoreDeviceStates;
62 ConnectionState connectionState;
63
64 wsConnectedCallback _wsConnectedCb;
65 wsDisconnectedCallback _wsDisconnectedCb;
66 wsPongCallback _wsPongCb;
67
68 virtual void runCbEvent(WStype_t type, uint8_t* payload, size_t length) override;
69
70 void setExtraHeaders();
71 SinricProQueue_t* receiveQueue;
72 String deviceIds;
73 String appKey;
74};
75
76WebsocketListener::WebsocketListener()
77 : _begin(false)
78 , restoreDeviceStates(false)
79 , connectionState(ConnectionState::disconnected)
80 , _wsConnectedCb(nullptr)
81 , _wsDisconnectedCb(nullptr)
82 , _wsPongCb(nullptr) {}
83
84WebsocketListener::~WebsocketListener() {
85 stop();
86}
87
88void WebsocketListener::setExtraHeaders() {
89#if defined(ESP8266)
90 const char* platform = "ESP8266";
91#elif defined(ESP32)
92 const char* platform = "ESP32";
93#elif defined(ARDUINO_ARCH_RP2040)
94 const char* platform = "RP2040";
95#endif
96
97 String headers = "appkey:" + appKey;
98 headers += "\r\ndeviceids:" + deviceIds;
99 headers += "\r\nrestoredevicestates:" + String(restoreDeviceStates ? "true" : "false");
100 headers += "\r\nip:" + WiFi.localIP().toString();
101 headers += "\r\nmac:" + WiFi.macAddress();
102 headers += "\r\nplatform:" + String(platform);
103 headers += "\r\nSDKVersion:" + String(SINRICPRO_VERSION);
104
105#ifdef FIRMWARE_VERSION
106 headers += "\r\nfirmwareVersion:" + String(FIRMWARE_VERSION);
107#endif
108
109 DEBUG_SINRIC("[SinricPro:Websocket]: headers: \r\n%s\r\n", headers.c_str());
110 WebSocketsClient::setExtraHeaders(headers.c_str());
111}
112
113void WebsocketListener::begin(String server, String appKey, String deviceIds, SinricProQueue_t* receiveQueue) {
114 if (_begin) return;
115 _begin = true;
116 connectionState = ConnectionState::connecting;
117
118 this->receiveQueue = receiveQueue;
119 this->appKey = appKey;
120 this->deviceIds = deviceIds;
121
122#ifdef WEBSOCKET_SSL
123 DEBUG_SINRIC("[SinricPro:Websocket]: Connecting to WebSocket Server using SSL (%s)\r\n", server.c_str());
124#else
125 DEBUG_SINRIC("[SinricPro:Websocket]: Connecting to WebSocket Server (%s)\r\n", server.c_str());
126#endif
127
128 if (isConnected()) stop();
129 setExtraHeaders();
130 enableHeartbeat(WEBSOCKET_PING_INTERVAL, WEBSOCKET_PING_TIMEOUT, WEBSOCKET_RETRY_COUNT);
131#ifdef WEBSOCKET_SSL
132 WebSocketsClient::beginSSL(server.c_str(), SINRICPRO_SERVER_SSL_PORT, "/");
133#else
134 WebSocketsClient::begin(server.c_str(), SINRICPRO_SERVER_PORT, "/"); // server address, port and URL
135#endif
136}
137
138void WebsocketListener::handle() {
139 loop();
140}
141
142void WebsocketListener::stop() {
143 disconnect();
144 _begin = false;
145 connectionState = ConnectionState::disconnected;
146}
147
148void WebsocketListener::setRestoreDeviceStates(bool flag) {
149 this->restoreDeviceStates = flag;
150};
151
152void WebsocketListener::sendMessage(String& message) {
153 sendTXT(message);
154}
155
156void WebsocketListener::onConnected(wsConnectedCallback callback) {
157 _wsConnectedCb = callback;
158}
159
160void WebsocketListener::onDisconnected(wsDisconnectedCallback callback) {
161 _wsDisconnectedCb = callback;
162}
163
164void WebsocketListener::onPong(wsPongCallback callback) {
165 _wsPongCb = callback;
166}
167
168void WebsocketListener::runCbEvent(WStype_t type, uint8_t* payload, size_t length) {
169 (void)length;
170
171 switch (type) {
172 case WStype_DISCONNECTED: {
173 DEBUG_SINRIC("[SinricPro:Websocket]: disconnected\r\n");
174 if (connectionState == ConnectionState::connected && _wsDisconnectedCb) _wsDisconnectedCb();
175 connectionState = ConnectionState::disconnected;
176 }
177 break;
178
179 case WStype_CONNECTED:
180 DEBUG_SINRIC("[SinricPro:Websocket]: connected\r\n");
181 if (_wsConnectedCb) _wsConnectedCb();
182 if (restoreDeviceStates) {
183 restoreDeviceStates = false;
184 setExtraHeaders();
185 }
186 connectionState = ConnectionState::connected;
187 break;
188
189 case WStype_TEXT: {
190 SinricProMessage* request = new SinricProMessage(IF_WEBSOCKET, (char*)payload);
191 DEBUG_SINRIC("[SinricPro:Websocket]: receiving data\r\n");
192 receiveQueue->push(request);
193 break;
194 }
195
196 case WStype_PONG: {
197 if (_wsPongCb) _wsPongCb(millis() - _client.lastPing);
198 break;
199 }
200
201 default:
202 break;
203 }
204}
205
206} // namespace SINRICPRO_NAMESPACE