9#include "rbwebserver.h"
23#ifdef RBGRIDUI_USING_ESP_IDF
25extern const uint8_t index_html_start[]
asm(
"_binary_index_html_gz_start");
26extern const uint8_t index_html_end[]
asm(
"_binary_index_html_gz_end");
28extern const uint8_t combined_js_start[]
asm(
"_binary_combined_js_gz_start");
29extern const uint8_t combined_js_end[]
asm(
"_binary_combined_js_gz_end");
32 not_found_response_t resp = {};
33 if(strcmp(request_path,
"/") == 0 || strcmp(request_path,
"/index.html") == 0) {
34 resp.data = (uint8_t*)index_html_start;
35 resp.size = index_html_end - index_html_start;
37 }
else if(strcmp(request_path,
"/combined.js") == 0) {
38 resp.data = (uint8_t*)combined_js_start;
39 resp.size = combined_js_end - combined_js_start;
46 not_found_response_t resp = {};
53 , m_protocol_ours(false)
54 , m_update_timer(nullptr)
55 , m_web_server_task(nullptr)
56 , m_state_mustarrive_id(UINT32_MAX)
57 , m_states_modified(false) {
63void _GridUi::begin(rb::Protocol* protocol,
int cols,
int rows,
bool enableSplitting) {
64 std::lock_guard<std::mutex> l(m_states_mu);
65 if (m_protocol !=
nullptr) {
66 ESP_LOGE(
"GridUI",
"begin() called more than once!");
71 m_protocol_ours =
false;
73 m_layout.reset(
new rbjson::Object);
74 m_layout->set(
"cols", cols);
75 m_layout->set(
"rows", rows);
76 m_layout->set(
"enableSplitting",
new rbjson::Bool(enableSplitting));
81 m_web_server_task = rb_web_start(80);
82 if(m_web_server_task == NULL) {
83 ESP_LOGE(
"GridUI",
"failed to call rb_web_start");
87#ifdef RBGRIDUI_USING_ESP_IDF
95 m_protocol_ours =
true;
99rb::Protocol*
_GridUi::beginConnect(
const char* owner,
const char* deviceName,
const char* wifiSSID,
const char* wifiPassword) {
100 rb::WiFi::connect(wifiSSID, wifiPassword);
101 return begin(owner, deviceName);
104rb::Protocol*
_GridUi::beginStartAp(
const char* owner,
const char* deviceName,
const char* wifiSSID,
const char* wifiPassword,
bool withCaptivePortal) {
105 rb::WiFi::startAp(wifiSSID, wifiPassword);
106 if (withCaptivePortal) {
107 rb::DnsServer::get().start();
109 return begin(owner, deviceName);
112uint16_t _GridUi::generateUuidLocked()
const {
114 const uint32_t rnd = esp_random();
115 if (checkUuidFreeLocked(rnd & 0xFFFF))
117 if (checkUuidFreeLocked(rnd >> 16))
125 ESP_LOGE(
"GridUI",
"end() called when not initialized!");
129 if(m_protocol_ours) {
133 m_protocol =
nullptr;
136 esp_timer_stop(m_update_timer);
137 esp_timer_delete(m_update_timer);
138 m_update_timer =
nullptr;
141 if(m_web_server_task) {
142 rb_web_stop(m_web_server_task);
143 m_web_server_task =
nullptr;
147 std::lock_guard<std::mutex> guard(m_states_mu);
149 m_states.shrink_to_fit();
151 m_widgets.shrink_to_fit();
155 m_state_mustarrive_id = 0;
156 m_states_modified =
false;
157 m_tab_changed =
false;
162 std::lock_guard<std::mutex> l(m_states_mu);
164 ESP_LOGE(
"GridUI",
"commit() called with no layout prepared!");
172 snprintf(buf,
sizeof(buf),
"%s/layout.json", rb_web_get_files_root());
174 std::ofstream stream;
175 stream.rdbuf()->pubsetbuf(buf,
sizeof(buf));
176 stream.open(buf, std::ofstream::trunc);
179 ESP_LOGE(
"GridUI",
"failed to open %s", buf);
183 m_states.shrink_to_fit();
185 m_layout->serialize(stream);
188 stream.seekp(-1, std::ofstream::cur);
190 stream <<
",\"widgets\": [";
191 for (
size_t i = 0; i < m_widgets.size(); ++i) {
196 auto& w = m_widgets[i];
197 w->serialize(stream);
198 if(strcmp(w->m_type,
"Arm") == 0) {
199 w->extra().remove(
"info");
204 m_widgets.shrink_to_fit();
209 ESP_LOGE(
"GridUI",
"failed to serialize layout");
215 ESP_LOGE(
"GridUI",
"failed to write layout");
220 esp_timer_create_args_t args = {
221 .callback = stateChangeTask,
223 .dispatch_method = ESP_TIMER_TASK,
224 .name =
"gridui_state",
225#ifdef ESP_IDF_VERSION
226 .skip_unhandled_events =
false,
230 esp_timer_create(&args, &m_update_timer);
231 esp_timer_start_periodic(m_update_timer, 50 * 1000);
237 auto* state = stateByUuidLocked(pkt->getInt(
"id"));
238 if (state ==
nullptr) {
239 m_states_mu.unlock();
243 auto* st = pkt->getObject(
"st");
247 m_states_mu.unlock();
249 state->call(pkt->getString(
"ev"));
250 }
else if (cmd ==
"_gall") {
251 bool changed =
false;
252 std::lock_guard<std::mutex> l(m_states_mu);
253 for (
auto& itr : m_states) {
254 if (itr->remarkAllChanges())
258 m_states_modified =
true;
260 m_tab_changed =
true;
268 std::lock_guard<std::mutex> k(m_tab_mu);
269 m_tab_changed =
true;
273void _GridUi::stateChangeTask(
void* selfVoid) {
274 auto* self = (
_GridUi*)selfVoid;
276 auto* prot = self->protocol();
277 if (prot ==
nullptr || !prot->is_possessed())
280 if (!prot->is_mustarrive_complete(self->m_state_mustarrive_id))
283 if (self->m_states_modified.exchange(
false)) {
284 std::unique_ptr<rbjson::Object> pkt(
new rbjson::Object);
286 std::lock_guard<std::mutex> guard(self->m_states_mu);
288 std::unique_ptr<rbjson::Object> state(
new rbjson::Object);
290 const size_t size = self->m_states.size();
291 for (
size_t i = 0; i < size; ++i) {
292 auto& s = self->m_states[i];
293 if (s->popChanges(*state.get())) {
294 snprintf(buf,
sizeof(buf),
"%d", (
int)s->uuid());
295 pkt->set(buf, state.release());
296 state.reset(
new rbjson::Object);
300 self->m_state_mustarrive_id = prot->send_mustarrive(
"_gst", pkt.release());
303 if (self->m_tab_changed.exchange(
false)) {
304 std::lock_guard<std::mutex> lock(self->m_tab_mu);
305 std::unique_ptr<rbjson::Object> pkt(
new rbjson::Object);
307 pkt->set(
"tab", self->m_tab);
309 self->m_state_mustarrive_id = prot->send_mustarrive(
"_gtb", pkt.release());
rb::Protocol * protocol() const
rb::Protocol * beginConnect(const char *owner, const char *deviceName, const char *wifiSSID, const char *wifiPassword="")
void begin(rb::Protocol *protocol, int cols=12, int rows=18, bool enableSplitting=true)
bool handleRbPacket(const std::string &command, rbjson::Object *pkt)
void changeTab(uint16_t index)
rb::Protocol * beginStartAp(const char *owner, const char *deviceName, const char *wifiSSID, const char *wifiPassword="", bool withCaptivePortal=true)
static void defaultOnPacketReceived(const std::string &cmd, rbjson::Object *pkt)
not_found_response_t webserverNotFoundCallback(const char *request_path)