mirror of
https://github.com/rstrouse/ESPSomfy-RTS.git
synced 2026-11-01 14:22:12 +01:00
feat: settings ops, the WiFi and frequency scans, and reboot for socket protocol 2
Step 1d of the protocol 2 core (docs/SECURITY_PLAN.md). - getSettings/saveSettings by section: general, network, mqtt, security, radio. A save is the settings route's request, so loop() applies it with the settings' own fromJSON(). ProtoSettings checks every field first: kind, range, string length, IP addresses. - Secrets are only reported as has*. A security save replies with the client's new token and closes the other sockets; other saves send settingsChanged to the other clients. - scanWifi runs an async scan from ESPNetwork, kept apart from the roaming scan, and wifiNetworks goes to the clients that asked. - beginFrequencyScan, endFrequencyScan and reboot run at once on async_tcp, as their routes do: a scan's end mustn't wait in the loop queue behind an unpair that waits for the scan to end (new op kind 'a'). - e2e: test_settings.py and a --reboot flag. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
97a4e98183
commit
2517f40709
22 changed files with 1221 additions and 49 deletions
|
|
@ -1,6 +1,7 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "ProtoEdits.h"
|
||||
#include "ProtoParse.h"
|
||||
|
||||
struct edit_op_t {
|
||||
proto_type_t type;
|
||||
|
|
@ -96,41 +97,9 @@ static const char *routeName(model_edit_type_t type, proto_field_t f) {
|
|||
if(type == model_edit_type_t::link_remote && f == proto_field_t::lastRollingCode) return "rollingCode";
|
||||
return protoField(f).name;
|
||||
}
|
||||
static const char *kindName(char kind) {
|
||||
switch(kind) {
|
||||
case 'b': return "boolean (1 or 0)";
|
||||
case 'i': return "whole number";
|
||||
case 'n': return "number";
|
||||
case 's': return "string";
|
||||
case 'l': return "list";
|
||||
default: return "object";
|
||||
}
|
||||
}
|
||||
|
||||
// What the reply's desc points at; every parse runs on async_tcp, one at a
|
||||
// time.
|
||||
static char why[64];
|
||||
// One value under its field's kind: false when it isn't one.
|
||||
static bool expandValue(const proto_field_info_t &f, JsonVariantConst v, JsonVariant out) {
|
||||
switch(f.kind) {
|
||||
case 'b':
|
||||
// Booleans are 1 and 0; the edit routes read true and false.
|
||||
if(!v.is<int>() || (v.as<int>() != 0 && v.as<int>() != 1)) return false;
|
||||
out.set(v.as<int>() == 1);
|
||||
return true;
|
||||
case 'i':
|
||||
if(v.is<int64_t>() || v.is<uint64_t>()) return out.set(v);
|
||||
return false;
|
||||
case 'n':
|
||||
if(v.is<float>()) return out.set(v);
|
||||
return false;
|
||||
case 's':
|
||||
if(v.is<const char *>()) return out.set(v);
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const char *expandEditPayload(model_edit_type_t type, JsonObjectConst d, JsonDocument &doc) {
|
||||
doc.clear();
|
||||
switch(type) {
|
||||
|
|
@ -164,8 +133,8 @@ const char *expandEditPayload(model_edit_type_t type, JsonObjectConst d, JsonDoc
|
|||
snprintf(why, sizeof(why), "%s can't be set by this edit", f.name);
|
||||
return why;
|
||||
}
|
||||
if(!expandValue(f, kv.value(), obj[routeName(type, field)].to<JsonVariant>())) {
|
||||
snprintf(why, sizeof(why), "%s is not a %s", f.name, kindName(f.kind));
|
||||
if(!protoRouteValue(f, kv.value(), obj[routeName(type, field)].to<JsonVariant>())) {
|
||||
snprintf(why, sizeof(why), "%s is not a %s", f.name, protoKindName(f.kind));
|
||||
return why;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue