Compare commits
4 Commits
84bc7f5d40
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 9e00821802 | |||
| 025cd56500 | |||
| 147fa762c8 | |||
| eca47d1596 |
85
TRMNL.cpp
85
TRMNL.cpp
@@ -1,4 +1,5 @@
|
|||||||
#include "TRMNL.h"
|
#include "TRMNL.h"
|
||||||
|
#include "helpers.h"
|
||||||
|
|
||||||
using std::list;
|
using std::list;
|
||||||
using std::map;
|
using std::map;
|
||||||
@@ -18,6 +19,19 @@ m_refresh_rate(refresh_rate),
|
|||||||
m_update_handler()
|
m_update_handler()
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
TRMNL::TRMNL(const nlohmann::json& j)
|
||||||
|
: m_id(""),
|
||||||
|
m_api_key(""),
|
||||||
|
m_friendly_id(""),
|
||||||
|
m_refresh_rate(DEFAULT_REFRESH_RATE),
|
||||||
|
m_update_handler()
|
||||||
|
{
|
||||||
|
json_extract(j, "ID", m_id);
|
||||||
|
json_extract(j, "api_key", m_api_key);
|
||||||
|
json_extract(j, "friendly_id", m_friendly_id);
|
||||||
|
json_extract(j, "refresh_rate", m_refresh_rate);
|
||||||
|
}
|
||||||
|
|
||||||
const string& TRMNL::id() const
|
const string& TRMNL::id() const
|
||||||
{
|
{
|
||||||
return m_id;
|
return m_id;
|
||||||
@@ -108,47 +122,6 @@ void to_json(json& j, const TRMNL& trmnl)
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void from_json(const json& j, TRMNL& trmnl)
|
|
||||||
{
|
|
||||||
bool updated = false;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
j.at("ID").get_to(trmnl.m_id);
|
|
||||||
updated = true;
|
|
||||||
}
|
|
||||||
catch (const std::exception& e)
|
|
||||||
{}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
j.at("api_key").get_to(trmnl.m_api_key);
|
|
||||||
updated = true;
|
|
||||||
}
|
|
||||||
catch (const std::exception& e)
|
|
||||||
{}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
j.at("friendly_id").get_to(trmnl.m_friendly_id);
|
|
||||||
updated = true;
|
|
||||||
}
|
|
||||||
catch (const std::exception& e)
|
|
||||||
{}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
j.at("refresh_rate").get_to(trmnl.m_refresh_rate);
|
|
||||||
updated = true;
|
|
||||||
}
|
|
||||||
catch (const std::exception& e)
|
|
||||||
{}
|
|
||||||
|
|
||||||
if (updated && trmnl.m_update_handler)
|
|
||||||
{
|
|
||||||
trmnl.m_update_handler(trmnl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TRMNLContainer::TRMNL_update_handler(const TRMNL& trmnl)
|
void TRMNLContainer::TRMNL_update_handler(const TRMNL& trmnl)
|
||||||
{
|
{
|
||||||
auto it = m_by_id.find(trmnl.m_id);
|
auto it = m_by_id.find(trmnl.m_id);
|
||||||
@@ -164,6 +137,20 @@ void TRMNLContainer::TRMNL_update_handler(const TRMNL& trmnl)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TRMNLContainer& TRMNLContainer::operator=(const TRMNLContainer& other)
|
||||||
|
{
|
||||||
|
m_devices = other.m_devices;
|
||||||
|
m_by_id = other.m_by_id;
|
||||||
|
m_by_friendly = other.m_by_friendly;
|
||||||
|
|
||||||
|
for (TRMNL& device : m_devices)
|
||||||
|
{
|
||||||
|
device.set_update_handler([this](const TRMNL& trmnl){ TRMNL_update_handler(trmnl); });
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
void TRMNLContainer::add_device(TRMNL trmnl)
|
void TRMNLContainer::add_device(TRMNL trmnl)
|
||||||
{
|
{
|
||||||
if (trmnl.m_id.empty())
|
if (trmnl.m_id.empty())
|
||||||
@@ -171,7 +158,6 @@ void TRMNLContainer::add_device(TRMNL trmnl)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
trmnl.set_update_handler([this](const TRMNL& trmnl){ TRMNL_update_handler(trmnl); });
|
|
||||||
auto it = m_devices.insert(m_devices.end(), trmnl);
|
auto it = m_devices.insert(m_devices.end(), trmnl);
|
||||||
|
|
||||||
m_by_id[trmnl.m_id] = it;
|
m_by_id[trmnl.m_id] = it;
|
||||||
@@ -180,6 +166,8 @@ void TRMNLContainer::add_device(TRMNL trmnl)
|
|||||||
{
|
{
|
||||||
m_by_friendly[trmnl.m_friendly_id] = it;
|
m_by_friendly[trmnl.m_friendly_id] = it;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
it->set_update_handler([this](const TRMNL& trmnl){ TRMNL_update_handler(trmnl); });
|
||||||
}
|
}
|
||||||
|
|
||||||
TRMNL* TRMNLContainer::get_device_by_id(const string& id)
|
TRMNL* TRMNLContainer::get_device_by_id(const string& id)
|
||||||
@@ -217,16 +205,3 @@ void to_json(json& j, const TRMNLContainer& cont)
|
|||||||
{
|
{
|
||||||
j = cont.m_devices;
|
j = cont.m_devices;
|
||||||
}
|
}
|
||||||
|
|
||||||
void from_json(const json& j, TRMNLContainer& cont)
|
|
||||||
{
|
|
||||||
if (!j.is_array())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < j.size(); ++i)
|
|
||||||
{
|
|
||||||
cont.add_device(j[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
11
TRMNL.h
11
TRMNL.h
@@ -9,6 +9,8 @@
|
|||||||
|
|
||||||
#include "json.hpp"
|
#include "json.hpp"
|
||||||
|
|
||||||
|
constexpr int DEFAULT_REFRESH_RATE = 300;
|
||||||
|
|
||||||
class TRMNL
|
class TRMNL
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
@@ -20,11 +22,13 @@ private:
|
|||||||
std::function<void (const TRMNL& trmnl)> m_update_handler;
|
std::function<void (const TRMNL& trmnl)> m_update_handler;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Constructor
|
// Constructors
|
||||||
TRMNL(const std::string& id = "",
|
TRMNL(const std::string& id = "",
|
||||||
const std::string& api_key = "",
|
const std::string& api_key = "",
|
||||||
const std::string& friendly_id = "",
|
const std::string& friendly_id = "",
|
||||||
int refresh_rate = 600);
|
int refresh_rate = DEFAULT_REFRESH_RATE);
|
||||||
|
|
||||||
|
TRMNL(const nlohmann::json& j);
|
||||||
|
|
||||||
// Getters
|
// Getters
|
||||||
const std::string& id() const;
|
const std::string& id() const;
|
||||||
@@ -43,7 +47,6 @@ public:
|
|||||||
static std::string friendly_from_id(std::string id);
|
static std::string friendly_from_id(std::string id);
|
||||||
|
|
||||||
friend void to_json(nlohmann::json& j, const TRMNL& trmnl);
|
friend void to_json(nlohmann::json& j, const TRMNL& trmnl);
|
||||||
friend void from_json(const nlohmann::json& j, TRMNL& trmnl);
|
|
||||||
friend class TRMNLContainer;
|
friend class TRMNLContainer;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -58,6 +61,7 @@ private:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
TRMNLContainer() = default;
|
TRMNLContainer() = default;
|
||||||
|
TRMNLContainer& operator=(const TRMNLContainer& other);
|
||||||
|
|
||||||
void add_device(TRMNL trmnl);
|
void add_device(TRMNL trmnl);
|
||||||
|
|
||||||
@@ -67,7 +71,6 @@ public:
|
|||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
friend void to_json(nlohmann::json& j, const TRMNLContainer& cont);
|
friend void to_json(nlohmann::json& j, const TRMNLContainer& cont);
|
||||||
friend void from_json(const nlohmann::json& j, TRMNLContainer& cont);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // TRMNL_H_
|
#endif // TRMNL_H_
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// Required
|
// Required
|
||||||
"host": "localhost",
|
"host": "localhost",
|
||||||
"port": 0,
|
"port": 0,
|
||||||
|
"base_url": "https://trmnl.com/",
|
||||||
|
|
||||||
// Optional
|
// Optional
|
||||||
"devices_filename": "devices.json",
|
"devices_filename": "devices.json",
|
||||||
|
|||||||
222
main.cpp
222
main.cpp
@@ -1,12 +1,23 @@
|
|||||||
|
// Start using chrono when C++20 becomes available
|
||||||
|
// #include <chrono>
|
||||||
|
#include <filesystem>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <random>
|
#include <random>
|
||||||
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
// Stop using these when C++20 becomes available
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include "httplib.h"
|
#include "httplib.h"
|
||||||
#include "json.hpp"
|
#include "json.hpp"
|
||||||
@@ -17,28 +28,168 @@
|
|||||||
using nlohmann::json;
|
using nlohmann::json;
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
void reload_container(TRMNLContainer& container, const string& filename)
|
constexpr int API_KEY_LENGHT = 16;
|
||||||
|
constexpr char DEFAULT_IMAGE_URL[] = "https://trmnl.com/images/setup/setup-logo.bmp";
|
||||||
|
constexpr char DEFAULT_IMAGE_FNAME[] = "2024-09-20T00:00:00";
|
||||||
|
|
||||||
|
string generate_api_key(int len = API_KEY_LENGHT)
|
||||||
|
{
|
||||||
|
string result = "";
|
||||||
|
std::random_device rand;
|
||||||
|
std::uniform_int_distribution distribution(0, 63);
|
||||||
|
int single;
|
||||||
|
char symbol;
|
||||||
|
|
||||||
|
if (len < 0)
|
||||||
|
{
|
||||||
|
len = API_KEY_LENGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < len; ++i)
|
||||||
|
{
|
||||||
|
single = distribution(rand);
|
||||||
|
if (single <= 9)
|
||||||
|
{
|
||||||
|
symbol = '0' + single;
|
||||||
|
}
|
||||||
|
else if ((single >= 10) && (single <= 35))
|
||||||
|
{
|
||||||
|
symbol = 'A' + single - 10;
|
||||||
|
}
|
||||||
|
else if ((single >= 36) && (single <= 61))
|
||||||
|
{
|
||||||
|
symbol = 'a' + single - 36;
|
||||||
|
}
|
||||||
|
else if (62 == single)
|
||||||
|
{
|
||||||
|
symbol = '+';
|
||||||
|
}
|
||||||
|
else if (63 == single)
|
||||||
|
{
|
||||||
|
symbol = '/';
|
||||||
|
}
|
||||||
|
// This should never happen
|
||||||
|
else
|
||||||
|
{
|
||||||
|
symbol = '?';
|
||||||
|
}
|
||||||
|
|
||||||
|
result += symbol;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool reload_container(TRMNLContainer& container, const string& filename)
|
||||||
{
|
{
|
||||||
json j;
|
json j;
|
||||||
bool ok = read_file_json(j, filename, &cout);
|
bool ok = read_file_json(j, filename, &cout);
|
||||||
if (!ok)
|
if (!ok)
|
||||||
{
|
{
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
container.clear();
|
container.clear();
|
||||||
container = j;
|
if (j.is_array())
|
||||||
|
{
|
||||||
|
for (int i = 0; i < j.size(); ++i)
|
||||||
|
{
|
||||||
|
container.add_device(j[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
string get_timestamp_from_filename(const string& filepath)
|
||||||
|
{
|
||||||
|
char result[128] = "";
|
||||||
|
|
||||||
|
struct stat status;
|
||||||
|
stat(filepath.c_str(), &status);
|
||||||
|
|
||||||
|
// Time of last modification
|
||||||
|
time_t file_time = status.st_mtim.tv_sec;
|
||||||
|
|
||||||
|
tm* file_tm;
|
||||||
|
file_tm = gmtime(&file_time);
|
||||||
|
|
||||||
|
snprintf(result, sizeof(result) - 1, "%04d-%02d-%02dT%02d:%02d:%02d",
|
||||||
|
file_tm->tm_year + 1900,
|
||||||
|
file_tm->tm_mon + 1,
|
||||||
|
file_tm->tm_mday,
|
||||||
|
file_tm->tm_hour,
|
||||||
|
file_tm->tm_min,
|
||||||
|
file_tm->tm_sec
|
||||||
|
);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use this when C++20 becomes available
|
||||||
|
#if 0
|
||||||
|
string get_timestamp_from_file_time(const fs::file_time_type& ftime)
|
||||||
|
{
|
||||||
|
char result[128] = "";
|
||||||
|
time_t file_time = std::chrono::system_clock::to_time_t(std::chrono::file_clock::to_sys(ftime));
|
||||||
|
|
||||||
|
tm* file_tm;
|
||||||
|
file_tm = gmtime(&file_time);
|
||||||
|
|
||||||
|
snprintf(result, sizeof(result) - 1, "%04d-%02d-%02dT%02d:%02d:%02d",
|
||||||
|
file_tm->tm_year + 1900,
|
||||||
|
file_tm->tm_mon + 1,
|
||||||
|
file_tm->tm_mday,
|
||||||
|
file_tm->tm_hour,
|
||||||
|
file_tm->tm_min,
|
||||||
|
file_tm->tm_sec
|
||||||
|
);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// First string is image filename for URL
|
||||||
|
// Second string is timestamp for TRMNL filename
|
||||||
|
pair<string, string> find_image_for_friendly(const string& folder_images, const string& friendly_id)
|
||||||
|
{
|
||||||
|
pair<string, string> result = {"", ""};
|
||||||
|
set<string> permitted_extensions = { ".bmp", ".png" };
|
||||||
|
|
||||||
|
if (!fs::is_directory(folder_images))
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const fs::directory_entry& entry : fs::directory_iterator(folder_images))
|
||||||
|
{
|
||||||
|
if ((entry.path().stem() == friendly_id) &&
|
||||||
|
(0 != permitted_extensions.count(entry.path().extension())))
|
||||||
|
{
|
||||||
|
result.first = entry.path().filename();
|
||||||
|
result.second = get_timestamp_from_filename(entry.path());
|
||||||
|
// Change to this when C++20 becomes available
|
||||||
|
// result.second = get_timestamp_from_file_time(entry.last_write_time());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
string config_filename = "config.json";
|
string config_filename = "config.json";
|
||||||
string devices_filename = "devices.json";
|
string devices_filename = "devices.json";
|
||||||
string host = "";
|
|
||||||
string folder_images = "images";
|
string folder_images = "images";
|
||||||
|
|
||||||
|
string host = "";
|
||||||
|
uint16_t port = 0;
|
||||||
|
string base_url = "";
|
||||||
|
|
||||||
string cert_file = "";
|
string cert_file = "";
|
||||||
string key_file = "";
|
string key_file = "";
|
||||||
uint16_t port = 0;
|
|
||||||
shared_ptr<httplib::Server> server = nullptr;
|
shared_ptr<httplib::Server> server = nullptr;
|
||||||
|
|
||||||
bool ok;
|
bool ok;
|
||||||
@@ -57,10 +208,14 @@ int main(int argc, char **argv)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
json_extract(cfg, "devices_filename", devices_filename);
|
|
||||||
json_extract(cfg, "host", host);
|
json_extract(cfg, "host", host);
|
||||||
json_extract(cfg, "port", port);
|
json_extract(cfg, "port", port);
|
||||||
|
json_extract(cfg, "base_url", base_url);
|
||||||
|
|
||||||
|
json_extract(cfg, "devices_filename", devices_filename);
|
||||||
json_extract(cfg, "folder_images", folder_images);
|
json_extract(cfg, "folder_images", folder_images);
|
||||||
|
|
||||||
|
// TODO: Extract when SSL is ready
|
||||||
// json_extract(cfg, "cert_file", cert_file);
|
// json_extract(cfg, "cert_file", cert_file);
|
||||||
// json_extract(cfg, "key_file", cert_file);
|
// json_extract(cfg, "key_file", cert_file);
|
||||||
|
|
||||||
@@ -76,12 +231,24 @@ int main(int argc, char **argv)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (base_url.empty())
|
||||||
|
{
|
||||||
|
cout << "base url not provided" << endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (folder_images.empty())
|
if (folder_images.empty())
|
||||||
{
|
{
|
||||||
cout << "folder for images is empty" << endl;
|
cout << "folder for images is empty" << endl;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!fs::is_directory(folder_images))
|
||||||
|
{
|
||||||
|
cout << "filepath for images is not a folder" << endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (!cert_file.empty() && !key_file.empty())
|
if (!cert_file.empty() && !key_file.empty())
|
||||||
{
|
{
|
||||||
// TODO: Implement SSL Server Properly
|
// TODO: Implement SSL Server Properly
|
||||||
@@ -91,15 +258,13 @@ int main(int argc, char **argv)
|
|||||||
server = make_shared<httplib::Server>();
|
server = make_shared<httplib::Server>();
|
||||||
}
|
}
|
||||||
|
|
||||||
ok = read_file_json(devs, devices_filename, &cout);
|
ok = reload_container(container, devices_filename);
|
||||||
if (!ok)
|
if (!ok)
|
||||||
{
|
{
|
||||||
|
cout << "Could not read devices file" << endl;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
container.clear();
|
|
||||||
container = devs;
|
|
||||||
|
|
||||||
auto setup_handler = [&container, &devices_filename](const httplib::Request& req, httplib::Response& res)
|
auto setup_handler = [&container, &devices_filename](const httplib::Request& req, httplib::Response& res)
|
||||||
{
|
{
|
||||||
json response;
|
json response;
|
||||||
@@ -152,16 +317,14 @@ int main(int argc, char **argv)
|
|||||||
if (trmnl->api_key().empty())
|
if (trmnl->api_key().empty())
|
||||||
{
|
{
|
||||||
should_dump = true;
|
should_dump = true;
|
||||||
// TODO: Randomly generate key here
|
trmnl->api_key(generate_api_key());
|
||||||
trmnl->api_key("nullptr");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
response["status"] = 200;
|
response["status"] = 200;
|
||||||
response["api_key"] = trmnl->api_key();
|
response["api_key"] = trmnl->api_key();
|
||||||
response["friendly_id"] = trmnl->friendly_id();
|
response["friendly_id"] = trmnl->friendly_id();
|
||||||
// TODO: Check for image in folder
|
response["image_url"] = DEFAULT_IMAGE_URL;
|
||||||
response["image_url"] = "https://trmnl.com/images/setup/setup-logo.bmp";
|
response["filename"] = DEFAULT_IMAGE_FNAME;
|
||||||
response["filename"] = "welcome";
|
|
||||||
|
|
||||||
res.set_header("Content-Type", "application/json");
|
res.set_header("Content-Type", "application/json");
|
||||||
res.body = response.dump();
|
res.body = response.dump();
|
||||||
@@ -172,7 +335,8 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
auto display_handler = [&container, &devices_filename](const httplib::Request& req, httplib::Response& res)
|
auto display_handler = [&container, &devices_filename, &folder_images, &base_url]
|
||||||
|
(const httplib::Request& req, httplib::Response& res)
|
||||||
{
|
{
|
||||||
json response;
|
json response;
|
||||||
|
|
||||||
@@ -223,19 +387,35 @@ int main(int argc, char **argv)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Check for image in folder
|
string image_fname;
|
||||||
// string image;
|
string image_timestamp;
|
||||||
|
tie(image_fname, image_timestamp) = find_image_for_friendly(folder_images, trmnl->friendly_id());
|
||||||
|
|
||||||
// The ID and api_key match here
|
// The ID and api_key match here
|
||||||
res.status = 200;
|
res.status = 200;
|
||||||
|
|
||||||
// From docs: will be 202 if no user_id is attached to device
|
// From docs: will be 202 if no user_id is attached to device
|
||||||
response["status"] = 0;
|
response["status"] = 0;
|
||||||
response["image_url"] = "https://trmnl.com/images/setup/setup-logo.bmp";
|
|
||||||
response["filename"] = "2024-09-20T00:00:00";
|
if (image_fname.empty())
|
||||||
|
{
|
||||||
|
response["image_url"] = DEFAULT_IMAGE_URL;
|
||||||
|
response["filename"] = DEFAULT_IMAGE_FNAME;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
string full_url = base_url;
|
||||||
|
full_url += "/images/";
|
||||||
|
full_url += image_fname;
|
||||||
|
response["image_url"] = full_url;
|
||||||
|
response["filename"] = image_timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
response["refresh_rate"] = to_string(trmnl->refresh_rate());
|
||||||
|
|
||||||
|
// TODO: Handle firmware updating
|
||||||
response["update_firmware"] = false;
|
response["update_firmware"] = false;
|
||||||
response["firmware_url"] = nullptr;
|
response["firmware_url"] = nullptr;
|
||||||
response["refresh_rate"] = to_string(trmnl->refresh_rate());
|
|
||||||
response["reset_firmware"] = false;
|
response["reset_firmware"] = false;
|
||||||
|
|
||||||
res.set_header("Content-Type", "application/json");
|
res.set_header("Content-Type", "application/json");
|
||||||
|
|||||||
Reference in New Issue
Block a user