Leave room for the answer: ALEX2ESP_MAX_MESSAGE defaults to ALEX2ESP_MAX_DIRECTIVE + 1024
Both limits were 2047 bytes. An answer repeats the correlationToken of its directive, which is most of a large directive, and adds 140 to 170 bytes for every property. So a directive between about 1,750 and 2,047 bytes was accepted, the sketch acted on it, and the answer was refused: the lamp switched and Alexa reported a device that does not respond.
The limit for messages now follows the limit for directives unless it is set: 2047 + 1024 = 3071 bytes, room for six properties on top of the largest directive. Both defines moved to src/AlexaLimits.h, next to each other, with the relation in the comment. The limit also applies to the discovery object of a device, which may now be 3071 bytes.
For a sketch: a report between 2,048 and 3,071 bytes is sent, where it was refused with an error. A project that sets ALEX2ESP_MAX_MESSAGE keeps its value.
Measured with the bridge built for the host against a fake MQTT client (not in the repository), a TurnOn of 2,047 bytes answered with two properties:
8ea7778 handler ran, send() false, "2374 bytes not sent, the limit is 2047"
this commit handler ran, send() true, 2,374 bytes published
Tests: test_largest_directive_can_be_answered puts a directive of ALEX2ESP_MAX_DIRECTIVE bytes through the receive buffer, builds the Response of a lamp to it and checks it against ALEX2ESP_MAX_MESSAGE, also with six properties. 33 host tests pass.
examples/basicLight.cpp for d1_mini, static RAM / flash in bytes: 34,116 / 336,757 -> 34,116 / 336,757, no warnings.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
46a27c26e9
commit
e48f8580f9
5 changed files with 102 additions and 17 deletions
|
|
@ -7,17 +7,19 @@
|
|||
#include <string.h>
|
||||
#include <string>
|
||||
#include "AlexaBridgeLogic.h"
|
||||
#include "AlexaLimits.h"
|
||||
|
||||
typedef AlexaDirectiveBuffer::Result Result;
|
||||
|
||||
#define ASSERT_RESULT(expected, actual) TEST_ASSERT_EQUAL_INT(static_cast<int>(expected), static_cast<int>(actual))
|
||||
|
||||
// A directive as Alex2MQTT publishes it on <root>/<endpointId>/alexaDirective
|
||||
static std::string directive(const char *name, const char *messageId)
|
||||
static std::string directive(const char *name, const char *messageId,
|
||||
const std::string &correlationToken = "AAAAAAAAAQBnlqNbYnB0dHNmYW5zbGF0ZQ==")
|
||||
{
|
||||
return std::string("{\"header\":{\"namespace\":\"Alexa.PowerController\",\"name\":\"") + name +
|
||||
"\",\"payloadVersion\":\"3\",\"messageId\":\"" + messageId +
|
||||
"\",\"correlationToken\":\"AAAAAAAAAQBnlqNbYnB0dHNmYW5zbGF0ZQ==\"},"
|
||||
"\",\"correlationToken\":\"" + correlationToken + "\"},"
|
||||
"\"endpoint\":{\"endpointId\":\"ESP-01\",\"cookie\":{}},\"payload\":{}}";
|
||||
}
|
||||
|
||||
|
|
@ -444,6 +446,74 @@ void test_document_that_ran_out_of_memory_is_not_published()
|
|||
TEST_ASSERT_EQUAL_UINT(0, length); // 0 tells the two reasons for TOO_LARGE apart
|
||||
}
|
||||
|
||||
// --- the limit of a directive and the limit of its answer ---
|
||||
|
||||
// One property of a report as AlexaStatusMessage::AddProperty writes it
|
||||
static void addProperty(JsonArray properties, const char *interfaceName, const char *name, const char *value)
|
||||
{
|
||||
JsonObject property = properties.add<JsonObject>();
|
||||
property["namespace"] = interfaceName;
|
||||
property["name"] = name;
|
||||
property["value"] = value;
|
||||
property["timeOfSample"] = "2026-09-28T13:05:09Z";
|
||||
property["uncertaintyInMilliseconds"] = 0;
|
||||
}
|
||||
|
||||
// The Response of a lamp as AlexaStatusMessage builds it (src/AlexaStatusMessage.cpp): the correlationToken of the
|
||||
// directive, the health of the endpoint and the power state
|
||||
static void buildResponse(JsonDocument &answer, const JsonDocument &received)
|
||||
{
|
||||
JsonObject header = answer["event"]["header"].to<JsonObject>();
|
||||
header["namespace"] = "Alexa";
|
||||
header["name"] = "Response";
|
||||
header["payloadVersion"] = "3";
|
||||
header["messageId"] = "OQpZQ2l8Pr2f9kkS8g6ffwpx7bJgJARngGUEQ"; // 37 characters, as generateMessageId() returns
|
||||
header["correlationToken"] = received["header"]["correlationToken"];
|
||||
answer["event"]["endpoint"]["endpointId"] = received["endpoint"]["endpointId"];
|
||||
answer["event"]["payload"].to<JsonObject>();
|
||||
|
||||
JsonArray properties = answer["context"]["properties"].to<JsonArray>();
|
||||
JsonObject health = properties.add<JsonObject>();
|
||||
health["namespace"] = "Alexa.EndpointHealth";
|
||||
health["name"] = "connectivity";
|
||||
health["value"]["value"] = "OK";
|
||||
health["timeOfSample"] = "2026-09-28T13:05:09Z";
|
||||
health["uncertaintyInMilliseconds"] = 0;
|
||||
addProperty(properties, "Alexa.PowerController", "powerState", "ON");
|
||||
}
|
||||
|
||||
void test_largest_directive_can_be_answered()
|
||||
{
|
||||
// The correlationToken is what makes a directive large, and the answer repeats it
|
||||
std::string withoutToken = directive("TurnOn", ID_1, "");
|
||||
std::string largest = directive("TurnOn", ID_1, std::string(ALEX2ESP_MAX_DIRECTIVE - withoutToken.size(), 'T'));
|
||||
TEST_ASSERT_EQUAL_UINT(ALEX2ESP_MAX_DIRECTIVE, largest.size());
|
||||
|
||||
AlexaDirectiveBuffer buffer(ALEX2ESP_MAX_DIRECTIVE);
|
||||
ASSERT_RESULT(Result::COMPLETE, deliver(buffer, largest, 536));
|
||||
JsonDocument received;
|
||||
TEST_ASSERT_TRUE(deserializeJson(received, buffer.data(), buffer.length()) == DeserializationError::Ok);
|
||||
buffer.release();
|
||||
|
||||
JsonDocument answer;
|
||||
buildResponse(answer, received);
|
||||
size_t length = 0;
|
||||
ASSERT_RESULT(AlexaSendResult::OK, AlexaBridgeLogic::checkMessage(answer, ALEX2ESP_MAX_MESSAGE, &length));
|
||||
// The answer is the larger of the two: a limit for messages as low as the limit for directives refuses it,
|
||||
// after the sketch has switched the lamp
|
||||
TEST_ASSERT_GREATER_THAN_UINT(ALEX2ESP_MAX_DIRECTIVE, length);
|
||||
ASSERT_RESULT(AlexaSendResult::TOO_LARGE, AlexaBridgeLogic::checkMessage(answer, ALEX2ESP_MAX_DIRECTIVE, &length));
|
||||
|
||||
// The default leaves room for six properties
|
||||
JsonArray properties = answer["context"]["properties"];
|
||||
addProperty(properties, "Alexa.BrightnessController", "brightness", "100");
|
||||
addProperty(properties, "Alexa.ColorTemperatureController", "colorTemperatureInKelvin", "2700");
|
||||
addProperty(properties, "Alexa.ToggleController", "toggleState", "ON");
|
||||
addProperty(properties, "Alexa.ToggleController", "toggleState", "OFF");
|
||||
TEST_ASSERT_EQUAL_UINT(6, properties.size());
|
||||
ASSERT_RESULT(AlexaSendResult::OK, AlexaBridgeLogic::checkMessage(answer, ALEX2ESP_MAX_MESSAGE, &length));
|
||||
}
|
||||
|
||||
// --- topics ---
|
||||
|
||||
void test_endpoint_is_taken_from_the_directive_topic()
|
||||
|
|
@ -564,6 +634,8 @@ int main(int, char **)
|
|||
RUN_TEST(test_empty_document_is_not_published);
|
||||
RUN_TEST(test_document_that_ran_out_of_memory_is_not_published);
|
||||
|
||||
RUN_TEST(test_largest_directive_can_be_answered);
|
||||
|
||||
RUN_TEST(test_endpoint_is_taken_from_the_directive_topic);
|
||||
RUN_TEST(test_other_topics_are_not_directive_topics);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue