mirror of
https://github.com/rstrouse/ESPSomfy-RTS.git
synced 2026-10-31 13:52:15 +01:00
The /ws endpoint runs next to the old socket on port 8080. The handshake checks the token under security, the device sends hello, then a sync of the schema (unless the client has it), header, rooms, shades, groups, repeaters and status, paced by the client's next. Events go out with the old ones: shadeState or the whole shade on a config change, groups, rooms, removals, orders, repeaters, shadeCommand, memory, wifi, ethernet, and the frames and frequencyScan feeds. The table of types, fields, errors and feeds is defined once and is sent as the schema. The writer, item writers, sync and client frame parser are pure and natively tested. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
167 lines
6.4 KiB
C++
167 lines
6.4 KiB
C++
#include "JsonFormatter.h"
|
|
#include "esp_log.h"
|
|
|
|
static const char *TAG = "JSON";
|
|
|
|
void BufferedJsonFormatter::setBuffer(char *b, size_t sz) {
|
|
this->buff = b;
|
|
this->buffSize = sz;
|
|
this->buff[0] = 0;
|
|
this->_len = 0;
|
|
this->_nocomma = true;
|
|
this->_overflow = false;
|
|
this->_objects = 0;
|
|
this->_arrays = 0;
|
|
}
|
|
size_t BufferedJsonFormatter::length() const {
|
|
return this->_len;
|
|
}
|
|
void JsonFormatter::discard() {
|
|
// With no room every _safecat() takes the overflow path, and as it has
|
|
// already overflowed that doesn't log either.
|
|
this->buff = nullptr;
|
|
this->buffSize = 0;
|
|
this->_len = 0;
|
|
this->_nocomma = true;
|
|
this->_overflow = true;
|
|
this->_objects = 0;
|
|
this->_arrays = 0;
|
|
}
|
|
void JsonFormatter::rollback(const json_mark_t &m) {
|
|
this->_len = m.len;
|
|
if(this->buff) this->buff[this->_len] = 0;
|
|
this->_objects = m.objects;
|
|
this->_arrays = m.arrays;
|
|
this->_nocomma = m.nocomma;
|
|
this->_overflow = false;
|
|
}
|
|
void JsonFormatter::beginObject(const char *name) {
|
|
if(name && strlen(name) > 0) this->appendElem(name);
|
|
else if(!this->_nocomma) this->_safecat(",");
|
|
this->_safecat("{");
|
|
this->_objects++;
|
|
this->_nocomma = true;
|
|
}
|
|
void JsonFormatter::endObject() {
|
|
//if(strlen(this->buff) + 1 > this->buffSize - 1) this->send();
|
|
this->_safecat("}");
|
|
this->_objects--;
|
|
this->_nocomma = false;
|
|
}
|
|
void JsonFormatter::beginArray(const char *name) {
|
|
if(name && strlen(name) > 0) this->appendElem(name);
|
|
else if(!this->_nocomma) this->_safecat(",");
|
|
this->_safecat("[");
|
|
this->_arrays++;
|
|
this->_nocomma = true;
|
|
}
|
|
void JsonFormatter::endArray() {
|
|
//if(strlen(this->buff) + 1 > this->buffSize - 1) this->send();
|
|
this->_safecat("]");
|
|
this->_arrays--;
|
|
this->_nocomma = false;
|
|
}
|
|
|
|
void JsonFormatter::appendElem(const char *name) {
|
|
if(!this->_nocomma) this->_safecat(",");
|
|
if(name && strlen(name) > 0) {
|
|
this->_safecat(name, true);
|
|
this->_safecat(":");
|
|
}
|
|
this->_nocomma = false;
|
|
}
|
|
|
|
void JsonFormatter::addElem(const char *name, const char *val) {
|
|
if(!val) return;
|
|
this->appendElem(name);
|
|
this->_safecat(val, true);
|
|
}
|
|
void JsonFormatter::addElem(const char *val) { this->addElem(nullptr, val); }
|
|
void JsonFormatter::addElem(float fval) { sprintf(this->_numbuff, "%.4f", fval); this->_appendNumber(nullptr); }
|
|
void JsonFormatter::addElem(int8_t nval) { sprintf(this->_numbuff, "%d", nval); this->_appendNumber(nullptr); }
|
|
void JsonFormatter::addElem(uint8_t nval) { sprintf(this->_numbuff, "%u", nval); this->_appendNumber(nullptr); }
|
|
void JsonFormatter::addElem(int32_t nval) { sprintf(this->_numbuff, "%ld", (long)nval); this->_appendNumber(nullptr); }
|
|
void JsonFormatter::addElem(uint32_t nval) { sprintf(this->_numbuff, "%lu", (unsigned long)nval); this->_appendNumber(nullptr); }
|
|
|
|
/*
|
|
void JsonFormatter::addElem(int16_t nval) { sprintf(this->_numbuff, "%d", nval); this->_appendNumber(nullptr); }
|
|
void JsonFormatter::addElem(uint16_t nval) { sprintf(this->_numbuff, "%u", nval); this->_appendNumber(nullptr); }
|
|
void JsonFormatter::addElem(int64_t lval) { sprintf(this->_numbuff, "%lld", (long long)lval); this->_appendNumber(nullptr); }
|
|
void JsonFormatter::addElem(uint64_t lval) { sprintf(this->_numbuff, "%llu", (unsigned long long)lval); this->_appendNumber(nullptr); }
|
|
*/
|
|
void JsonFormatter::addElem(bool bval) { strcpy(this->_numbuff, bval ? "true" : "false"); this->_appendNumber(nullptr); }
|
|
|
|
void JsonFormatter::addElem(const char *name, float fval) { sprintf(this->_numbuff, "%.4f", fval); this->_appendNumber(name); }
|
|
void JsonFormatter::addElem(const char *name, int8_t nval) { sprintf(this->_numbuff, "%d", nval); this->_appendNumber(name); }
|
|
void JsonFormatter::addElem(const char *name, uint8_t nval) { sprintf(this->_numbuff, "%u", nval); this->_appendNumber(name); }
|
|
void JsonFormatter::addElem(const char *name, int32_t nval) { sprintf(this->_numbuff, "%ld", (long)nval); this->_appendNumber(name); }
|
|
void JsonFormatter::addElem(const char *name, uint32_t nval) { sprintf(this->_numbuff, "%lu", (unsigned long)nval); this->_appendNumber(name); }
|
|
|
|
/*
|
|
void JsonFormatter::addElem(const char *name, int16_t nval) { sprintf(this->_numbuff, "%d", nval); this->_appendNumber(name); }
|
|
void JsonFormatter::addElem(const char *name, uint16_t nval) { sprintf(this->_numbuff, "%u", nval); this->_appendNumber(name); }
|
|
void JsonFormatter::addElem(const char *name, int64_t lval) { sprintf(this->_numbuff, "%lld", (long long)lval); this->_appendNumber(name); }
|
|
*/
|
|
void JsonFormatter::addElem(const char *name, uint64_t lval) { sprintf(this->_numbuff, "%llu", (unsigned long long)lval); this->_appendNumber(name); }
|
|
|
|
void JsonFormatter::addElem(const char *name, bool bval) { strcpy(this->_numbuff, bval ? "true" : "false"); this->_appendNumber(name); }
|
|
|
|
void JsonFormatter::_safecat(const char *val, bool escape) {
|
|
const size_t vlen = escape ? this->calcEscapedLength(val) + 2 : strlen(val);
|
|
if(this->_len + vlen >= this->buffSize) {
|
|
if(!this->_overflow && this->_logOverflow) ESP_LOGE(TAG, "JSON exceeded buffer size %u", (unsigned)this->buffSize);
|
|
this->_overflow = true;
|
|
return;
|
|
}
|
|
this->_appendAt(val, escape, vlen);
|
|
}
|
|
void JsonFormatter::_appendAt(const char *val, bool escape, size_t vlen) {
|
|
char *p = this->buff + this->_len;
|
|
if(escape) {
|
|
*p++ = '"';
|
|
*p = 0;
|
|
this->escapeString(val, p);
|
|
p += vlen - 2;
|
|
*p++ = '"';
|
|
}
|
|
else {
|
|
memcpy(p, val, vlen);
|
|
p += vlen;
|
|
}
|
|
*p = 0;
|
|
this->_len += vlen;
|
|
}
|
|
void JsonFormatter::_appendNumber(const char *name) { this->appendElem(name); this->_safecat(this->_numbuff); }
|
|
size_t JsonFormatter::escapeChar(char c, char *out) {
|
|
const char *esc = nullptr;
|
|
switch(c) {
|
|
case '"': esc = "\\\""; break;
|
|
case '/': esc = "\\/"; break;
|
|
case '\b': esc = "\\b"; break;
|
|
case '\f': esc = "\\f"; break;
|
|
case '\n': esc = "\\n"; break;
|
|
case '\r': esc = "\\r"; break;
|
|
case '\t': esc = "\\t"; break;
|
|
case '\\': esc = "\\\\"; break;
|
|
default: break;
|
|
}
|
|
if(esc) {
|
|
strcpy(out, esc);
|
|
return 2;
|
|
}
|
|
// Any other control character is not allowed raw in a JSON string.
|
|
if((unsigned char)c < 0x20) return snprintf(out, 7, "\\u%04x", (unsigned char)c);
|
|
out[0] = c;
|
|
out[1] = 0x00;
|
|
return 1;
|
|
}
|
|
uint32_t JsonFormatter::calcEscapedLength(const char *raw) {
|
|
char tmp[7];
|
|
uint32_t len = 0;
|
|
for(size_t i = 0; raw[i]; i++) len += escapeChar(raw[i], tmp);
|
|
return len;
|
|
}
|
|
void JsonFormatter::escapeString(const char *raw, char *escaped) {
|
|
char *out = escaped + strlen(escaped);
|
|
for(size_t i = 0; raw[i]; i++) out += escapeChar(raw[i], out);
|
|
}
|