EmlaLockSafe
EmlaLockApi.h
Go to the documentation of this file.
1
5#pragma once
6
7#include "../configuration/Configuration.h"
8#define ARDUINOJSON_USE_LONG_LONG 1
9#define ARDUINOJSON_DECODE_UNICODE 1
10
11#include <Arduino.h>
12#include <ArduinoJson.h>
13#include <HTTPClient.h>
14#include <Thread.h>
15#include <WiFiClientSecure.h>
16#include <chrono>
17#include <condition_variable>
18#include <limits>
19#include <mutex>
20
21namespace emlalock {
27protected:
31 const String host = "api.emlalock.com";
32
33protected:
37 StaticJsonDocument<10000> jsonDocument;
38
39protected:
43 std::mutex mtx;
44
45protected:
50 std::condition_variable condVar;
51
52protected:
57
58protected:
63
64public:
71 static EmlaLockApi& getSingleton(bool offlineMode = false) {
72 static EmlaLockApi* instance = nullptr;
73 if (!instance) {
74 instance = new EmlaLockApi(offlineMode);
75 }
76
77 return *instance;
78 }
79
80protected:
89 : triggered(true)
91 if (offlineMode) {
92 Serial.println("Starting EmlaLock API in offline mode..");
93 }
94 else {
95 Serial.println("Starting EmlaLock API in online mode..");
96 esp32::Thread::create("ElmaApiThread", 8192, 1, 1, *this, &EmlaLockApi::threadFunction);
97 }
98 }
99
100public:
106 std::unique_lock<std::mutex> lock(mtx);
107 if (offlineMode) {
109 }
110 else {
111 triggered = true;
112 lock.unlock();
113 condVar.notify_all();
114 }
115 }
116
117protected:
123 // This thread runs forever
124 std::unique_lock<std::mutex> lock(mtx);
125 while (true) {
126 // wait until the thread will be triggered or every ten minutes...
127 condVar.wait_for(lock, std::chrono::seconds(600), [this]() -> bool {
128 return triggered;
129 }); // use lambda to avoid spurious wakeups
130 triggered = false;
131
132 // ensure we are not in the manual mode!
134 continue;
135 }
136 else {
138 }
139
140 if (requestUrl("/info/?userid=" + configuration::Configuration::getSingleton().getUserId() +
141 "&apikey=" + configuration::Configuration::getSingleton().getApiKey())) {
142 uint32_t numberOfFailedSessionOld = LockState::getNumberOfFailedSessions();
143 uint32_t numberOfFailedSessionNew = jsonDocument["user"]["failedsessions"].as<uint32_t>();
144 if (numberOfFailedSessionOld != numberOfFailedSessionNew) {
145 LockState::setNumberOfFailedSessions(numberOfFailedSessionNew);
146 // Check if a session with an known end date was aborted:
148 // that's not allowed if this happened during an active
149 // emlalock-session! Switch to manual mode with the last known end-time
150 Serial.println("Abort rejected");
152 return;
153 }
154 }
155
156 if (jsonDocument["chastitysession"].size() != 0) {
158 (LockState::DisplayTimePassed)jsonDocument["chastitysession"]["displaymode"]["timepassed"].as<int>());
160 (LockState::DisplayTimeLeft)jsonDocument["chastitysession"]["displaymode"]["timeleft"].as<int>());
161
163 LockState::setStartDate(jsonDocument["chastitysession"]["startdate"].as<time_t>());
164 }
165 else {
167 }
168
170 LockState::setEndDate(jsonDocument["chastitysession"]["enddate"].as<time_t>());
171 char buf[21];
172 tm tmBuf;
173 strftime(buf, 21, "%d.%m.%Y %T", localtime_r(&LockState::getEndDate(), &tmBuf));
174 Serial.printf("End-date: %s\n", buf);
175 }
176 else {
178 String s = jsonDocument["chastitysession"]["enddate"].as<String>();
179 s.replace("{{localization.", "");
180 s.replace("}}", "");
182 }
183 LockState::setEndDate(std::numeric_limits<time_t>::max());
184 }
185
186 if (jsonDocument["chastitysession"]["incleaning"].as<int>() != 0) {
187 LockState::setCleaningEndDate(jsonDocument["chastitysession"]["cleaningstarted"].as<time_t>() +
188 jsonDocument["chastitysession"]["timeforcleaning"].as<time_t>());
189 }
190 else {
192 }
193 }
194 else {
195 // Session was closed on server ---> reset if the enddate was not
196 // displayed...
198 }
199 }
200 else {
201 Serial.println("Connection failed.");
202 }
204 }
205 }
206
207protected:
214 bool requestUrl(String& url) {
215 WiFiClientSecure client;
216 client.setInsecure();
217 url = String("https://") + host + url;
218 Serial.println(url);
219
220 // Your Domain name with URL path or IP address with path
221 HTTPClient http;
222 http.useHTTP10(true);
223 http.begin(client, url);
224
225 // Send HTTP GET request
226 int httpResponseCode = http.GET();
227
228 String data;
229 if (httpResponseCode > 0) {
230 DeserializationError error = deserializeJson(jsonDocument, http.getStream());
231 http.end();
232 if (error) {
233 Serial.print(F("deserializeJson() failed: "));
234 Serial.println(error.c_str());
235 return false;
236 }
237 }
238 else {
239 Serial.print("Error code: ");
240 Serial.println(httpResponseCode);
241 // Free resources
242 http.end();
243 return false;
244 }
245
246 return true;
247 }
248};
249
250} // namespace emlalock
static void setStartDate(const time_t &startDate)
Set the Start Date.
Definition: LockState.h:311
static const time_t & getEndDate()
Get the End Date.
Definition: LockState.h:325
DisplayTimeLeft
States for the selection how the time left should be displayed.
Definition: LockState.h:47
static const Mode & getMode()
Get the mode.
Definition: LockState.h:233
static const uint32_t & getNumberOfFailedSessions()
Get the number of failed sessions.
Definition: LockState.h:348
static void setLastUpdateTime(const time_t &lastUpdateTime)
Set the time of the last update over the emlalock api.
Definition: LockState.h:447
static const DisplayTimePassed & getDisplayTimePassed()
Get how the time passed should be displayed.
Definition: LockState.h:256
static void setEndDate(const time_t &endDate)
Set the End Date.
Definition: LockState.h:334
static void setTemperatureString(const String &temperatureString)
Set the temperature string.
Definition: LockState.h:380
static void setNumberOfFailedSessions(const uint32_t &numberOfFailedSessions)
Set the number of failed sessions.
Definition: LockState.h:357
static void setMode(const Mode &mode)
Set the mode.
Definition: LockState.h:242
static void setDisplayTimePassed(const DisplayTimePassed &displayTimePassed)
Set how the time passed should be displayed.
Definition: LockState.h:265
static void setDisplayTimeLeft(const DisplayTimeLeft &displayTimeLeft)
Set how the time left should be displayed.
Definition: LockState.h:288
static void setCleaningEndDate(const time_t &cleaningEndDate)
Set the End Date of the current cleaning opening.
Definition: LockState.h:425
static const DisplayTimeLeft & getDisplayTimeLeft()
Get how the time left should be displayed.
Definition: LockState.h:279
DisplayTimePassed
States for the selection how the passed time should be displayed.
Definition: LockState.h:41
static Configuration & getSingleton()
Get the Singleton object.
Definition: Configuration.h:162
Class interfacing Arduino to the EmlaLock API.
Definition: EmlaLockApi.h:26
void threadFunction()
The thread functions which is communicating asynchronously with the Emlalock API.
Definition: EmlaLockApi.h:122
bool triggered
Helper variable to detect spurious wakeups of thread.
Definition: EmlaLockApi.h:56
EmlaLockApi(bool offlineMode)
Construct a new EmlaLock Api Object. Use the singleton EmlaLock or getInstance() instead of creating ...
Definition: EmlaLockApi.h:88
bool requestUrl(String &url)
Loads the requested url and parses the json result.
Definition: EmlaLockApi.h:214
StaticJsonDocument< 10000 > jsonDocument
JSON parser.
Definition: EmlaLockApi.h:37
const String host
The base address to the API.
Definition: EmlaLockApi.h:31
bool offlineMode
If true the thread requesting data from emlalock won't be started.
Definition: EmlaLockApi.h:62
std::mutex mtx
Mutex used to ensure thread safety of the API.
Definition: EmlaLockApi.h:43
void triggerRefresh()
Triggers the client to reload the current state from the EmlaLock server.
Definition: EmlaLockApi.h:105
static EmlaLockApi & getSingleton(bool offlineMode=false)
Get the singleton instance of the API handler.
Definition: EmlaLockApi.h:71
std::condition_variable condVar
Condition variable to which can be triggered to immediately request the status of the EmlaLock Sessio...
Definition: EmlaLockApi.h:50
Definition: EmlaLockApi.h:21