mirror of
https://github.com/rstrouse/ESPSomfy-RTS.git
synced 2026-10-31 22:02:17 +01:00
fix: command op replies that said ok when nothing was done
From the review of #100: - setMyPosition on a moving shade replies busy ("The shade is moving"), and setPositions with only a tilt, for a shade without one, replies bad request ("The shade has no tilt"). Both said ok before. - PROTOCOL.md lists where the ops differ from the routes, and how sendRemoteCommand is answered. - The LoopQueue hold comment covers protocol 2 replies and an unpair held through a frequency scan. - One shadeRequestIsForGroup() for the group cases; ProtoOps shares one reader for "one or both" fields and one for ids, and the radio refused text. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
598d708b12
commit
5737a58790
12 changed files with 103 additions and 49 deletions
|
|
@ -64,9 +64,10 @@ class OpReader {
|
|||
return v.is<const char *>() ? v.as<const char *>() : nullptr;
|
||||
}
|
||||
};
|
||||
// What the reply's desc points at once parseShadeOp has returned.
|
||||
// The reader's problem, kept once the reader has gone so the reply can send
|
||||
// it. One buffer for every parse: they all run on async_tcp, one at a time.
|
||||
static char lastError[64];
|
||||
static const char *result(OpReader &r) {
|
||||
static const char *errorOf(OpReader &r) {
|
||||
if(!r.error) return nullptr;
|
||||
snprintf(lastError, sizeof(lastError), "%s", r.error);
|
||||
return lastError;
|
||||
|
|
@ -116,17 +117,17 @@ static void readStep(OpReader &r, shade_request_t &req) {
|
|||
// Steps are 7 bits on air.
|
||||
if(r.get(proto_field_t::stepSize, 0, 127, n)) req.stepSize = static_cast<uint8_t>(n);
|
||||
}
|
||||
static void readShade(OpReader &r, shade_request_t &req) {
|
||||
static void readId(OpReader &r, proto_field_t field, shade_request_t &req) {
|
||||
int32_t n;
|
||||
if(r.need(proto_field_t::shadeId, 0, 255, n)) req.id = static_cast<uint8_t>(n);
|
||||
if(r.need(field, 0, 255, n)) req.id = static_cast<uint8_t>(n);
|
||||
}
|
||||
static void readShade(OpReader &r, shade_request_t &req) { readId(r, proto_field_t::shadeId, req); }
|
||||
// A shade or a group; req.type becomes the one that was given.
|
||||
static void readShadeOrGroup(OpReader &r, shade_request_t &req, shade_request_type_t shade, shade_request_type_t group) {
|
||||
if(!r.either(proto_field_t::shadeId, proto_field_t::groupId)) return;
|
||||
int32_t n;
|
||||
const bool isShade = r.has(proto_field_t::shadeId);
|
||||
req.type = isShade ? shade : group;
|
||||
if(r.need(isShade ? proto_field_t::shadeId : proto_field_t::groupId, 0, 255, n)) req.id = static_cast<uint8_t>(n);
|
||||
readId(r, isShade ? proto_field_t::shadeId : proto_field_t::groupId, req);
|
||||
}
|
||||
// A command, or a position 0-100 to move to.
|
||||
static void readCommandOrTarget(OpReader &r, shade_request_t &req) {
|
||||
|
|
@ -135,15 +136,16 @@ static void readCommandOrTarget(OpReader &r, shade_request_t &req) {
|
|||
if(r.has(proto_field_t::command)) r.command(req.command);
|
||||
else if(r.get(proto_field_t::target, 0, 100, n)) req.target = static_cast<int8_t>(n);
|
||||
}
|
||||
// Two positions 0-100 of which at least one is given; -1 for the other.
|
||||
static void readPositions(OpReader &r, proto_field_t lift, proto_field_t tilt, int8_t &liftOut, int8_t &tiltOut) {
|
||||
if(!r.has(lift) && !r.has(tilt)) {
|
||||
r.fail("Give %s, %s or both", protoField(lift).name, protoField(tilt).name);
|
||||
// Two values in lo..hi of which at least one is given; the other is left as
|
||||
// it was.
|
||||
static void readOneOrBoth(OpReader &r, proto_field_t a, proto_field_t b, int32_t lo, int32_t hi, int8_t &aOut, int8_t &bOut) {
|
||||
if(!r.has(a) && !r.has(b)) {
|
||||
r.fail("Give %s, %s or both", protoField(a).name, protoField(b).name);
|
||||
return;
|
||||
}
|
||||
int32_t n;
|
||||
if(r.get(lift, 0, 100, n)) liftOut = static_cast<int8_t>(n);
|
||||
if(r.get(tilt, 0, 100, n)) tiltOut = static_cast<int8_t>(n);
|
||||
if(r.get(a, lo, hi, n)) aOut = static_cast<int8_t>(n);
|
||||
if(r.get(b, lo, hi, n)) bOut = static_cast<int8_t>(n);
|
||||
}
|
||||
|
||||
const char *parseShadeOp(uint8_t type, JsonObjectConst d, shade_request_t &req) {
|
||||
|
|
@ -165,7 +167,7 @@ const char *parseShadeOp(uint8_t type, JsonObjectConst d, shade_request_t &req)
|
|||
break;
|
||||
case proto_type_t::groupCommand:
|
||||
req.type = shade_request_type_t::group_command;
|
||||
if(r.need(proto_field_t::groupId, 0, 255, n)) req.id = static_cast<uint8_t>(n);
|
||||
readId(r, proto_field_t::groupId, req);
|
||||
r.command(req.command);
|
||||
readRepeat(r, req);
|
||||
readStep(r, req);
|
||||
|
|
@ -179,19 +181,17 @@ const char *parseShadeOp(uint8_t type, JsonObjectConst d, shade_request_t &req)
|
|||
case proto_type_t::setPositions:
|
||||
req.type = shade_request_type_t::set_positions;
|
||||
readShade(r, req);
|
||||
readPositions(r, proto_field_t::position, proto_field_t::tiltPosition, req.position, req.tiltPosition);
|
||||
readOneOrBoth(r, proto_field_t::position, proto_field_t::tiltPosition, 0, 100, req.position, req.tiltPosition);
|
||||
break;
|
||||
case proto_type_t::setSensor:
|
||||
readShadeOrGroup(r, req, shade_request_type_t::shade_sensor, shade_request_type_t::group_sensor);
|
||||
if(!r.has(proto_field_t::sunny) && !r.has(proto_field_t::windy)) r.fail("Give sunny, windy or both");
|
||||
if(r.get(proto_field_t::sunny, 0, 1, n)) req.sunny = static_cast<int8_t>(n);
|
||||
if(r.get(proto_field_t::windy, 0, 1, n)) req.windy = static_cast<int8_t>(n);
|
||||
readOneOrBoth(r, proto_field_t::sunny, proto_field_t::windy, 0, 1, req.sunny, req.windy);
|
||||
readRepeat(r, req);
|
||||
break;
|
||||
case proto_type_t::setMyPosition:
|
||||
req.type = shade_request_type_t::set_favorite;
|
||||
readShade(r, req);
|
||||
readPositions(r, proto_field_t::myPos, proto_field_t::myTiltPos, req.position, req.tiltPosition);
|
||||
readOneOrBoth(r, proto_field_t::myPos, proto_field_t::myTiltPos, 0, 100, req.position, req.tiltPosition);
|
||||
break;
|
||||
case proto_type_t::unpairShade:
|
||||
req.type = shade_request_type_t::unpair;
|
||||
|
|
@ -206,7 +206,7 @@ const char *parseShadeOp(uint8_t type, JsonObjectConst d, shade_request_t &req)
|
|||
r.fail("Not a shade op");
|
||||
break;
|
||||
}
|
||||
return result(r);
|
||||
return errorOf(r);
|
||||
}
|
||||
|
||||
const char *parseRemoteCommandOp(JsonObjectConst d, somfy_frame_t &frame, uint8_t &repeats) {
|
||||
|
|
@ -220,28 +220,25 @@ const char *parseRemoteCommandOp(JsonObjectConst d, somfy_frame_t &frame, uint8_
|
|||
if(r.need(proto_field_t::lastRollingCode, 1, UINT16_MAX, n)) frame.rollingCode = static_cast<uint16_t>(n);
|
||||
if(r.get(proto_field_t::encKey, 0, UINT8_MAX, n)) frame.encKey = static_cast<uint8_t>(n);
|
||||
if(r.get(proto_field_t::repeats, 0, UINT8_MAX, n)) repeats = static_cast<uint8_t>(n);
|
||||
return result(r);
|
||||
return errorOf(r);
|
||||
}
|
||||
|
||||
proto_error_t protoRequestError(shade_request_result_t result) {
|
||||
switch(result) {
|
||||
case shade_request_result_t::not_found: return proto_error_t::notFound;
|
||||
case shade_request_result_t::radio_refused: return proto_error_t::radioUnavailable;
|
||||
// At rest it would take it: the client may try again.
|
||||
case shade_request_result_t::shade_moving: return proto_error_t::busy;
|
||||
case shade_request_result_t::no_tilt: return proto_error_t::badRequest;
|
||||
default: return proto_error_t::none;
|
||||
}
|
||||
}
|
||||
const char *protoRequestErrorDesc(const shade_request_t &req, shade_request_result_t result) {
|
||||
switch(result) {
|
||||
case shade_request_result_t::not_found:
|
||||
switch(req.type) {
|
||||
case shade_request_type_t::group_command:
|
||||
case shade_request_type_t::group_repeat:
|
||||
case shade_request_type_t::group_sensor:
|
||||
return "No group with that id";
|
||||
default:
|
||||
return "No shade with that id";
|
||||
}
|
||||
case shade_request_result_t::radio_refused: return "The radio didn't take the command";
|
||||
case shade_request_result_t::not_found: return shadeRequestIsForGroup(req.type) ? "No group with that id" : "No shade with that id";
|
||||
case shade_request_result_t::radio_refused: return PROTO_RADIO_REFUSED;
|
||||
case shade_request_result_t::shade_moving: return "The shade is moving";
|
||||
case shade_request_result_t::no_tilt: return "The shade has no tilt";
|
||||
default: return nullptr;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue