Report helpers for every tier-1 property, asset ids, AlexaCapability::isValid()
AlexaStatusMessage gets a helper for color, percentage, power level, range, mode, lock state, contact, motion, humidity and the thermostat (target, lower and upper setpoint, mode), and addProperty(row, place, value) for a property without one. Each is a function of its own on one shared builder; a value outside of the range of its property is reported as the nearest of the range with an error. AlexaResources.h holds the 103 asset ids of resources-and-assets.html (AlexaAssets, AlexaUnits); AlexaState has the seven states that were missing; addStateMapping() takes a number of any type. A setter that refuses its argument makes isValid() false, and the capability is left out of discovery with an error instead of being announced without what was refused. basicLight: static RAM 30,520 B (was 30,540), flash 333,389 B (was 334,981); 130 host tests (was 112). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
3215457f91
commit
77156ffbe5
10 changed files with 995 additions and 100 deletions
|
|
@ -28,6 +28,7 @@
|
|||
#include "AlexaInterface.h"
|
||||
#include "AlexaLimits.h"
|
||||
#include "AlexaLog.h"
|
||||
#include "AlexaResources.h"
|
||||
#include "AlexaTransport.h"
|
||||
#include "AlexaUtils.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -16,9 +16,21 @@ static const uint8_t ACTION_COUNT = sizeof(ACTION_NAMES) / sizeof(ACTION_NAMES[0
|
|||
|
||||
static const char STATE_OPEN[] PROGMEM = "Alexa.States.Open";
|
||||
static const char STATE_CLOSED[] PROGMEM = "Alexa.States.Closed";
|
||||
static const char *const STATE_NAMES[] PROGMEM = {STATE_OPEN, STATE_CLOSED};
|
||||
static const char STATE_ECO_ON[] PROGMEM = "Alexa.States.EcoOn";
|
||||
static const char STATE_ECO_OFF[] PROGMEM = "Alexa.States.EcoOff";
|
||||
static const char STATE_LOW[] PROGMEM = "Alexa.States.Low";
|
||||
static const char STATE_EMPTY[] PROGMEM = "Alexa.States.Empty";
|
||||
static const char STATE_FULL[] PROGMEM = "Alexa.States.Full";
|
||||
static const char STATE_DONE[] PROGMEM = "Alexa.States.Done";
|
||||
static const char STATE_STUCK[] PROGMEM = "Alexa.States.Stuck";
|
||||
static const char *const STATE_NAMES[] PROGMEM = {STATE_OPEN, STATE_CLOSED, STATE_ECO_ON, STATE_ECO_OFF, STATE_LOW,
|
||||
STATE_EMPTY, STATE_FULL, STATE_DONE, STATE_STUCK};
|
||||
static const uint8_t STATE_COUNT = sizeof(STATE_NAMES) / sizeof(STATE_NAMES[0]);
|
||||
|
||||
static_assert(static_cast<uint8_t>(AlexaAction::SetEcoOff) + 1 == ACTION_COUNT, "an action has no name");
|
||||
static_assert(static_cast<uint8_t>(AlexaState::Stuck) + 1 == STATE_COUNT, "a state has no name");
|
||||
static_assert(ACTION_COUNT <= 8 && STATE_COUNT <= 16, "a mapping has no bit for every action or state");
|
||||
|
||||
static PGM_P nameAt(const char *const *names, uint8_t place)
|
||||
{
|
||||
return static_cast<PGM_P>(pgm_read_ptr(&names[place]));
|
||||
|
|
@ -37,7 +49,7 @@ PGM_P alexaStateName(AlexaState state)
|
|||
}
|
||||
|
||||
// The names of the bits that are set, in the order of the table
|
||||
static void addNames(JsonArray target, uint8_t bits, const char *const *names, uint8_t count)
|
||||
static void addNames(JsonArray target, uint16_t bits, const char *const *names, uint8_t count)
|
||||
{
|
||||
for (uint8_t place = 0; place < count; place++)
|
||||
{
|
||||
|
|
@ -76,7 +88,7 @@ AlexaCapabilityName::AlexaCapabilityName(const AlexaInterfaceDesc &row, const ch
|
|||
}
|
||||
|
||||
AlexaCapability::AlexaCapability(const AlexaInterfaceDesc &row, const char *instance)
|
||||
: row(&row), retrievable(true), proactivelyReported(false), nonControllable(false)
|
||||
: row(&row), retrievable(true), proactivelyReported(false), nonControllable(false), valid(true)
|
||||
{
|
||||
setInstance(instance);
|
||||
}
|
||||
|
|
@ -93,6 +105,12 @@ AlexaCapability::~AlexaCapability()
|
|||
}
|
||||
}
|
||||
|
||||
AlexaCapability &AlexaCapability::refused()
|
||||
{
|
||||
valid = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
AlexaCapability &AlexaCapability::setInstance(const char *newInstance)
|
||||
{
|
||||
free(instance);
|
||||
|
|
@ -101,6 +119,7 @@ AlexaCapability &AlexaCapability::setInstance(const char *newInstance)
|
|||
{
|
||||
ALEX2ESP_LOGE("%s: instance not set: the heap has no room for it",
|
||||
AlexaCapabilityName(*row, nullptr).text);
|
||||
return refused();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
|
@ -112,19 +131,19 @@ AlexaCapability &AlexaCapability::addFriendlyName(const char *text, const char *
|
|||
{
|
||||
ALEX2ESP_LOGE("%s: friendly name not added: pass a text and a locale, addFriendlyName(\"Lift\", \"en-US\")",
|
||||
name.text);
|
||||
return *this;
|
||||
return refused();
|
||||
}
|
||||
if (nameCount >= ALEX2ESP_MAX_FRIENDLY_NAMES)
|
||||
{
|
||||
ALEX2ESP_LOGE("%s: friendly name \"%s\" not added: it has %d already (ALEX2ESP_MAX_FRIENDLY_NAMES)",
|
||||
name.text, text, ALEX2ESP_MAX_FRIENDLY_NAMES);
|
||||
return *this;
|
||||
return refused();
|
||||
}
|
||||
const char *copy = copyText(text);
|
||||
if (copy == nullptr)
|
||||
{
|
||||
ALEX2ESP_LOGE("%s: friendly name \"%s\" not added: the heap has no room for it", name.text, text);
|
||||
return *this;
|
||||
return refused();
|
||||
}
|
||||
names[nameCount++] = {copy, locale};
|
||||
return *this;
|
||||
|
|
@ -135,14 +154,14 @@ AlexaCapability &AlexaCapability::addFriendlyAsset(PGM_P assetId)
|
|||
const AlexaCapabilityName name(*row, instance);
|
||||
if (assetId == nullptr)
|
||||
{
|
||||
ALEX2ESP_LOGE("%s: asset not added: pass its id, addFriendlyAsset(PSTR(\"Alexa.Setting.Opening\"))", name.text);
|
||||
return *this;
|
||||
ALEX2ESP_LOGE("%s: asset not added: pass its id, addFriendlyAsset(AlexaAssets::Setting_Opening)", name.text);
|
||||
return refused();
|
||||
}
|
||||
if (nameCount >= ALEX2ESP_MAX_FRIENDLY_NAMES)
|
||||
{
|
||||
ALEX2ESP_LOGE("%s: asset not added: it has %d names already (ALEX2ESP_MAX_FRIENDLY_NAMES)",
|
||||
name.text, ALEX2ESP_MAX_FRIENDLY_NAMES);
|
||||
return *this;
|
||||
return refused();
|
||||
}
|
||||
names[nameCount++] = {assetId, nullptr};
|
||||
return *this;
|
||||
|
|
@ -180,19 +199,19 @@ AlexaCapability &AlexaCapability::addActionMapping(const ActionMapping &mapping)
|
|||
{
|
||||
ALEX2ESP_LOGE("%s: action mapping not added: only RangeController, ModeController and ToggleController take one",
|
||||
name.text);
|
||||
return *this;
|
||||
return refused();
|
||||
}
|
||||
if (mapping.actions == 0 || mapping.directiveName == nullptr || mapping.directiveName[0] == '\0')
|
||||
{
|
||||
ALEX2ESP_LOGE("%s: action mapping not added: it needs an action and a directive, ActionMapping({AlexaAction::Open}, \"TurnOn\")",
|
||||
name.text);
|
||||
return *this;
|
||||
return refused();
|
||||
}
|
||||
if (actionCount >= ALEX2ESP_MAX_ACTION_MAPPINGS)
|
||||
{
|
||||
ALEX2ESP_LOGE("%s: action mapping to %s not added: it has %d already (ALEX2ESP_MAX_ACTION_MAPPINGS)",
|
||||
name.text, mapping.directiveName, ALEX2ESP_MAX_ACTION_MAPPINGS);
|
||||
return *this;
|
||||
return refused();
|
||||
}
|
||||
actionMappings[actionCount++] = mapping;
|
||||
return *this;
|
||||
|
|
@ -206,25 +225,28 @@ AlexaCapability::StateMapping *AlexaCapability::newStateMapping(std::initializer
|
|||
{
|
||||
ALEX2ESP_LOGE("%s: state mapping not added: only RangeController, ModeController and ToggleController take one",
|
||||
name.text);
|
||||
valid = false;
|
||||
return nullptr;
|
||||
}
|
||||
if (states.size() == 0)
|
||||
{
|
||||
ALEX2ESP_LOGE("%s: state mapping not added: it needs a state, addStateMapping({AlexaState::Closed}, 0.0f)",
|
||||
ALEX2ESP_LOGE("%s: state mapping not added: it needs a state, addStateMapping({AlexaState::Closed}, 0)",
|
||||
name.text);
|
||||
valid = false;
|
||||
return nullptr;
|
||||
}
|
||||
if (stateCount >= ALEX2ESP_MAX_STATE_MAPPINGS)
|
||||
{
|
||||
ALEX2ESP_LOGE("%s: state mapping not added: it has %d already (ALEX2ESP_MAX_STATE_MAPPINGS)",
|
||||
name.text, ALEX2ESP_MAX_STATE_MAPPINGS);
|
||||
valid = false;
|
||||
return nullptr;
|
||||
}
|
||||
StateMapping *mapping = &stateMappings[stateCount++];
|
||||
mapping->states = 0;
|
||||
for (AlexaState state : states)
|
||||
{
|
||||
mapping->states |= static_cast<uint8_t>(1u << static_cast<uint8_t>(state));
|
||||
mapping->states |= static_cast<uint16_t>(1u << static_cast<uint8_t>(state));
|
||||
}
|
||||
mapping->kind = kind;
|
||||
mapping->maximum = 0;
|
||||
|
|
@ -258,7 +280,7 @@ AlexaCapability &AlexaCapability::addStateMapping(std::initializer_list<AlexaSta
|
|||
{
|
||||
ALEX2ESP_LOGE("%s: state mapping not added: it needs a value, addStateMapping({AlexaState::Closed}, \"OFF\")",
|
||||
AlexaCapabilityName(*row, instance).text);
|
||||
return *this;
|
||||
return refused();
|
||||
}
|
||||
StateMapping *mapping = newStateMapping(states, STATES_TO_TEXT);
|
||||
if (mapping != nullptr)
|
||||
|
|
@ -285,6 +307,12 @@ bool AlexaCapability::matches(const char *ns, const char *directiveInstance) con
|
|||
// of a capability that has them. A plain literal takes static RAM in every sketch.
|
||||
bool AlexaCapability::toJson(JsonArray capabilities, const char *endpointId) const
|
||||
{
|
||||
if (!valid)
|
||||
{
|
||||
ALEX2ESP_LOGE("%s: %s left out of discovery: a call that set it up was refused, correct what the error of that call names",
|
||||
endpointId, AlexaCapabilityName(*row, instance).text);
|
||||
return false;
|
||||
}
|
||||
if (row->has(AIF_INSTANCED) && (instance == nullptr || nameCount == 0))
|
||||
{
|
||||
const AlexaCapabilityName name(*row, instance);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include <Arduino.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <initializer_list>
|
||||
#include <type_traits>
|
||||
#include "AlexaInterfaces.h"
|
||||
#include "AlexaLimits.h"
|
||||
|
||||
|
|
@ -23,11 +24,19 @@ enum class AlexaAction : uint8_t
|
|||
SetEcoOff
|
||||
};
|
||||
|
||||
// What Alexa says about the device: "the blind is open" (alexa-discovery-objects.html#state-mapping)
|
||||
// What Alexa says about the device: "the blind is open" (alexa-discovery-objects.html#state-mapping). EcoOn and
|
||||
// EcoOff are for a ToggleController; Low, Empty, Full, Done and Stuck are what Alexa announces a device for.
|
||||
enum class AlexaState : uint8_t
|
||||
{
|
||||
Open,
|
||||
Closed
|
||||
Closed,
|
||||
EcoOn,
|
||||
EcoOff,
|
||||
Low,
|
||||
Empty,
|
||||
Full,
|
||||
Done,
|
||||
Stuck
|
||||
};
|
||||
|
||||
// "Alexa.Actions.Open", in program memory
|
||||
|
|
@ -74,7 +83,9 @@ struct AlexaCapabilityName
|
|||
// 116 bytes on the heap of an ESP8266, whatever the capability holds. Every setter returns the capability:
|
||||
// device->addCapability(AlexaInterfaces::RangeController, "Blind.Lift")
|
||||
// ->addFriendlyName("Lift", "en-US").setConfiguration(fillLift).addStateMapping({AlexaState::Closed}, 0.0f);
|
||||
// What a capability refuses is printed at ERROR with what to change, and leaves the capability as it was.
|
||||
// What a capability refuses is printed at ERROR with what to change, and leaves the capability as it was. The
|
||||
// chain goes on after a call that was refused; isValid() is false from then on, and the device leaves the
|
||||
// capability out of discovery, where Alexa would otherwise learn half of it.
|
||||
class AlexaCapability
|
||||
{
|
||||
public:
|
||||
|
|
@ -97,6 +108,9 @@ public:
|
|||
bool isProactivelyReported() const { return proactivelyReported; }
|
||||
bool isNonControllable() const { return nonControllable; }
|
||||
|
||||
// false when a setter has refused what it was given. The reason was printed when it happened.
|
||||
bool isValid() const { return valid; }
|
||||
|
||||
// The instance tells the capabilities of one interface on a device apart ("Blind.Lift", "Blind.Tilt"). It is
|
||||
// copied. addCapability(row, instance) sets it; this is for a capability that was added by its type.
|
||||
AlexaCapability &setInstance(const char *instance);
|
||||
|
|
@ -104,7 +118,8 @@ public:
|
|||
// A name of the capability in words. The text is copied, the locale ("en-US") is not: pass a literal.
|
||||
AlexaCapability &addFriendlyName(const char *text, const char *locale);
|
||||
|
||||
// A name from the catalog of Alexa, which Alexa translates: PSTR("Alexa.Setting.Opening"). Not copied.
|
||||
// A name from the catalog of Alexa, which Alexa translates: AlexaAssets::Setting_Opening of AlexaResources.h,
|
||||
// or PSTR("Alexa.Setting.Opening"). Not copied.
|
||||
AlexaCapability &addFriendlyAsset(PGM_P assetId);
|
||||
|
||||
AlexaCapability &setRetrievable(bool value);
|
||||
|
|
@ -124,13 +139,21 @@ public:
|
|||
AlexaCapability &addStateMapping(std::initializer_list<AlexaState> states, float minimum, float maximum);
|
||||
AlexaCapability &addStateMapping(std::initializer_list<AlexaState> states, const char *value);
|
||||
|
||||
// A number of another type, addStateMapping({AlexaState::Closed}, 0): without this a 0 is as much a text
|
||||
// that is missing as it is a number, and the call does not compile
|
||||
template <typename Number, typename = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
|
||||
AlexaCapability &addStateMapping(std::initializer_list<AlexaState> states, Number value)
|
||||
{
|
||||
return addStateMapping(states, static_cast<float>(value));
|
||||
}
|
||||
|
||||
// Whether a directive with this namespace and instance is for this capability. The instance counts only
|
||||
// for an interface that has instances.
|
||||
bool matches(const char *ns, const char *directiveInstance) const;
|
||||
|
||||
// Adds the discovery object of the capability to the capabilities of its endpoint. A capability that Alexa
|
||||
// would reject the endpoint for, a generic controller without an instance or without a name, adds nothing,
|
||||
// prints what is missing and returns false.
|
||||
// prints what is missing and returns false. So does one that is not valid.
|
||||
bool toJson(JsonArray capabilities, const char *endpointId) const;
|
||||
|
||||
private:
|
||||
|
|
@ -149,8 +172,8 @@ private:
|
|||
|
||||
struct StateMapping
|
||||
{
|
||||
uint8_t states; // one bit per AlexaState
|
||||
uint8_t kind; // StateMappingKind
|
||||
uint16_t states; // one bit per AlexaState
|
||||
uint8_t kind; // StateMappingKind
|
||||
union
|
||||
{
|
||||
float value; // the value, or the lowest of a range
|
||||
|
|
@ -160,6 +183,7 @@ private:
|
|||
};
|
||||
|
||||
StateMapping *newStateMapping(std::initializer_list<AlexaState> states, StateMappingKind kind);
|
||||
AlexaCapability &refused();
|
||||
void writeSemantics(JsonObject capability) const;
|
||||
void writeNames(JsonObject capability) const;
|
||||
|
||||
|
|
@ -176,6 +200,7 @@ private:
|
|||
bool retrievable : 1;
|
||||
bool proactivelyReported : 1;
|
||||
bool nonControllable : 1;
|
||||
bool valid : 1;
|
||||
};
|
||||
|
||||
#endif // ALEXA_CAPABILITY_H
|
||||
|
|
|
|||
141
src/AlexaResources.h
Normal file
141
src/AlexaResources.h
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
// The names Alexa has in its own catalog, which it translates into the language of the user: the assets of
|
||||
// resources-and-assets.html under https://developer.amazon.com/en-US/docs/alexa/device-apis/
|
||||
//
|
||||
// capability->addFriendlyAsset(AlexaAssets::Setting_Opening);
|
||||
// configuration["unitOfMeasure"] = FPSTR(AlexaUnits::Percent);
|
||||
// labelOfAMode["value"]["assetId"] = FPSTR(AlexaAssets::Value_Open);
|
||||
//
|
||||
// Every id is a string in program memory that belongs to the file which includes this one: an id that the sketch
|
||||
// does not name takes no room in it. The words a user says and the states Alexa names, "Alexa.Actions.Open" and
|
||||
// "Alexa.States.Closed", are AlexaAction and AlexaState in AlexaCapability.h.
|
||||
#ifndef ALEXA_RESOURCES_H
|
||||
#define ALEXA_RESOURCES_H
|
||||
|
||||
#include "AlexaCompat.h"
|
||||
|
||||
#define ALEXA_RESOURCE(name, id) static const char name[] PROGMEM = id;
|
||||
|
||||
namespace AlexaAssets
|
||||
{
|
||||
// What a device does, as the name of a mode or a preset. Not the actions of an action mapping.
|
||||
ALEXA_RESOURCE(Actions_Charge, "Alexa.Actions.Charge")
|
||||
ALEXA_RESOURCE(Actions_Clean, "Alexa.Actions.Clean")
|
||||
ALEXA_RESOURCE(Actions_Dispense, "Alexa.Actions.Dispense")
|
||||
ALEXA_RESOURCE(Actions_Dock, "Alexa.Actions.Dock")
|
||||
ALEXA_RESOURCE(Actions_Empty, "Alexa.Actions.Empty")
|
||||
ALEXA_RESOURCE(Actions_Mop, "Alexa.Actions.Mop")
|
||||
ALEXA_RESOURCE(Actions_Skip, "Alexa.Actions.Skip")
|
||||
ALEXA_RESOURCE(Actions_Sweep, "Alexa.Actions.Sweep")
|
||||
ALEXA_RESOURCE(Actions_Vacuum, "Alexa.Actions.Vacuum")
|
||||
|
||||
// The buttons of a remote and how they are pressed, for Alexa.SimpleEventSource
|
||||
ALEXA_RESOURCE(Button_OffButton, "Alexa.Button.OffButton")
|
||||
ALEXA_RESOURCE(Button_OnButton, "Alexa.Button.OnButton")
|
||||
ALEXA_RESOURCE(Button_BrightenButton, "Alexa.Button.BrightenButton")
|
||||
ALEXA_RESOURCE(Button_DimButton, "Alexa.Button.DimButton")
|
||||
ALEXA_RESOURCE(Button_MainButton, "Alexa.Button.MainButton")
|
||||
ALEXA_RESOURCE(Button_TopButton, "Alexa.Button.TopButton")
|
||||
ALEXA_RESOURCE(Button_BottomButton, "Alexa.Button.BottomButton")
|
||||
ALEXA_RESOURCE(Button_CenterButton, "Alexa.Button.CenterButton")
|
||||
ALEXA_RESOURCE(Button_MiddleButton, "Alexa.Button.MiddleButton")
|
||||
ALEXA_RESOURCE(Button_UpButton, "Alexa.Button.UpButton")
|
||||
ALEXA_RESOURCE(Button_DownButton, "Alexa.Button.DownButton")
|
||||
ALEXA_RESOURCE(Button_LeftButton, "Alexa.Button.LeftButton")
|
||||
ALEXA_RESOURCE(Button_RightButton, "Alexa.Button.RightButton")
|
||||
ALEXA_RESOURCE(Button_FirstButton, "Alexa.Button.FirstButton")
|
||||
ALEXA_RESOURCE(Button_SecondButton, "Alexa.Button.SecondButton")
|
||||
ALEXA_RESOURCE(Button_ThirdButton, "Alexa.Button.ThirdButton")
|
||||
ALEXA_RESOURCE(Button_FourthButton, "Alexa.Button.FourthButton")
|
||||
ALEXA_RESOURCE(Button_FifthButton, "Alexa.Button.FifthButton")
|
||||
ALEXA_RESOURCE(Button_SixthButton, "Alexa.Button.SixthButton")
|
||||
ALEXA_RESOURCE(Button_SeventhButton, "Alexa.Button.SeventhButton")
|
||||
ALEXA_RESOURCE(Button_EighthButton, "Alexa.Button.EighthButton")
|
||||
ALEXA_RESOURCE(Button_SinglePress, "Alexa.Button.SinglePress")
|
||||
ALEXA_RESOURCE(Button_SinglePush, "Alexa.Button.SinglePush")
|
||||
ALEXA_RESOURCE(Button_DoublePress, "Alexa.Button.DoublePress")
|
||||
ALEXA_RESOURCE(Button_DoublePush, "Alexa.Button.DoublePush")
|
||||
ALEXA_RESOURCE(Button_LongPress, "Alexa.Button.LongPress")
|
||||
ALEXA_RESOURCE(Button_LongPush, "Alexa.Button.LongPush")
|
||||
|
||||
ALEXA_RESOURCE(DeviceName_AirPurifier, "Alexa.DeviceName.AirPurifier")
|
||||
ALEXA_RESOURCE(DeviceName_Camera, "Alexa.DeviceName.Camera")
|
||||
ALEXA_RESOURCE(DeviceName_Fan, "Alexa.DeviceName.Fan")
|
||||
ALEXA_RESOURCE(DeviceName_Router, "Alexa.DeviceName.Router")
|
||||
ALEXA_RESOURCE(DeviceName_Shade, "Alexa.DeviceName.Shade")
|
||||
ALEXA_RESOURCE(DeviceName_Shower, "Alexa.DeviceName.Shower")
|
||||
ALEXA_RESOURCE(DeviceName_SpaceHeater, "Alexa.DeviceName.SpaceHeater")
|
||||
ALEXA_RESOURCE(DeviceName_Washer, "Alexa.DeviceName.Washer")
|
||||
|
||||
ALEXA_RESOURCE(Gesture_SingleClick, "Alexa.Gesture.SingleClick")
|
||||
ALEXA_RESOURCE(Gesture_DoubleClick, "Alexa.Gesture.DoubleClick")
|
||||
ALEXA_RESOURCE(Gesture_Tap, "Alexa.Gesture.Tap")
|
||||
ALEXA_RESOURCE(Gesture_DoubleTap, "Alexa.Gestures.DoubleTap") // "Gestures": the id as the page has it
|
||||
ALEXA_RESOURCE(Gesture_SwipeUp, "Alexa.Gesture.SwipeUp")
|
||||
ALEXA_RESOURCE(Gesture_SwipeDown, "Alexa.Gesture.SwipeDown")
|
||||
ALEXA_RESOURCE(Gesture_SwipeLeft, "Alexa.Gesture.SwipeLeft")
|
||||
ALEXA_RESOURCE(Gesture_SwipeRight, "Alexa.Gesture.SwipeRight")
|
||||
|
||||
ALEXA_RESOURCE(Setting_2GGuestWiFi, "Alexa.Setting.2GGuestWiFi")
|
||||
ALEXA_RESOURCE(Setting_5GGuestWiFi, "Alexa.Setting.5GGuestWiFi")
|
||||
ALEXA_RESOURCE(Setting_Auto, "Alexa.Setting.Auto")
|
||||
ALEXA_RESOURCE(Setting_Direction, "Alexa.Setting.Direction")
|
||||
ALEXA_RESOURCE(Setting_DryCycle, "Alexa.Setting.DryCycle")
|
||||
ALEXA_RESOURCE(Setting_FanSpeed, "Alexa.Setting.FanSpeed")
|
||||
ALEXA_RESOURCE(Setting_GuestWiFi, "Alexa.Setting.GuestWiFi")
|
||||
ALEXA_RESOURCE(Setting_Heat, "Alexa.Setting.Heat")
|
||||
ALEXA_RESOURCE(Setting_Mode, "Alexa.Setting.Mode")
|
||||
ALEXA_RESOURCE(Setting_Night, "Alexa.Setting.Night")
|
||||
ALEXA_RESOURCE(Setting_Opening, "Alexa.Setting.Opening")
|
||||
ALEXA_RESOURCE(Setting_Oscillate, "Alexa.Setting.Oscillate")
|
||||
ALEXA_RESOURCE(Setting_Preset, "Alexa.Setting.Preset")
|
||||
ALEXA_RESOURCE(Setting_Quiet, "Alexa.Setting.Quiet")
|
||||
ALEXA_RESOURCE(Setting_Temperature, "Alexa.Setting.Temperature")
|
||||
ALEXA_RESOURCE(Setting_WashCycle, "Alexa.Setting.WashCycle")
|
||||
ALEXA_RESOURCE(Setting_WaterTemperature, "Alexa.Setting.WaterTemperature")
|
||||
|
||||
ALEXA_RESOURCE(Shower_HandHeld, "Alexa.Shower.HandHeld")
|
||||
ALEXA_RESOURCE(Shower_RainHead, "Alexa.Shower.RainHead")
|
||||
|
||||
// The names of modes and presets
|
||||
ALEXA_RESOURCE(Value_Open, "Alexa.Value.Open")
|
||||
ALEXA_RESOURCE(Value_Close, "Alexa.Value.Close")
|
||||
ALEXA_RESOURCE(Value_Minimum, "Alexa.Value.Minimum")
|
||||
ALEXA_RESOURCE(Value_Low, "Alexa.Value.Low")
|
||||
ALEXA_RESOURCE(Value_Medium, "Alexa.Value.Medium")
|
||||
ALEXA_RESOURCE(Value_High, "Alexa.Value.High")
|
||||
ALEXA_RESOURCE(Value_Maximum, "Alexa.Value.Maximum")
|
||||
ALEXA_RESOURCE(Value_Delicate, "Alexa.Value.Delicate")
|
||||
ALEXA_RESOURCE(Value_QuickWash, "Alexa.Value.QuickWash")
|
||||
}
|
||||
|
||||
// What the value of a range is measured in: "unitOfMeasure" in the configuration of Alexa.RangeController
|
||||
namespace AlexaUnits
|
||||
{
|
||||
ALEXA_RESOURCE(Angle_Degrees, "Alexa.Unit.Angle.Degrees")
|
||||
ALEXA_RESOURCE(Angle_Radians, "Alexa.Unit.Angle.Radians")
|
||||
ALEXA_RESOURCE(Distance_Feet, "Alexa.Unit.Distance.Feet")
|
||||
ALEXA_RESOURCE(Distance_Inches, "Alexa.Unit.Distance.Inches")
|
||||
ALEXA_RESOURCE(Distance_Kilometers, "Alexa.Unit.Distance.Kilometers")
|
||||
ALEXA_RESOURCE(Distance_Meters, "Alexa.Unit.Distance.Meters")
|
||||
ALEXA_RESOURCE(Distance_Miles, "Alexa.Unit.Distance.Miles")
|
||||
ALEXA_RESOURCE(Distance_Yards, "Alexa.Unit.Distance.Yards")
|
||||
ALEXA_RESOURCE(Mass_Grams, "Alexa.Unit.Mass.Grams")
|
||||
ALEXA_RESOURCE(Mass_Kilograms, "Alexa.Unit.Mass.Kilograms")
|
||||
ALEXA_RESOURCE(Percent, "Alexa.Unit.Percent")
|
||||
ALEXA_RESOURCE(Temperature_Celsius, "Alexa.Unit.Temperature.Celsius")
|
||||
ALEXA_RESOURCE(Temperature_Degrees, "Alexa.Unit.Temperature.Degrees")
|
||||
ALEXA_RESOURCE(Temperature_Fahrenheit, "Alexa.Unit.Temperature.Fahrenheit")
|
||||
ALEXA_RESOURCE(Temperature_Kelvin, "Alexa.Unit.Temperature.Kelvin")
|
||||
ALEXA_RESOURCE(Volume_CubicFeet, "Alexa.Unit.Volume.CubicFeet")
|
||||
ALEXA_RESOURCE(Volume_CubicMeters, "Alexa.Unit.Volume.CubicMeters")
|
||||
ALEXA_RESOURCE(Volume_Gallons, "Alexa.Unit.Volume.Gallons")
|
||||
ALEXA_RESOURCE(Volume_Liters, "Alexa.Unit.Volume.Liters")
|
||||
ALEXA_RESOURCE(Volume_Pints, "Alexa.Unit.Volume.Pints")
|
||||
ALEXA_RESOURCE(Volume_Quarts, "Alexa.Unit.Volume.Quarts")
|
||||
ALEXA_RESOURCE(Weight_Ounces, "Alexa.Unit.Weight.Ounces")
|
||||
ALEXA_RESOURCE(Weight_Pounds, "Alexa.Unit.Weight.Pounds")
|
||||
}
|
||||
|
||||
#undef ALEXA_RESOURCE
|
||||
|
||||
#endif // ALEXA_RESOURCES_H
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
#include "AlexaStatusMessage.h"
|
||||
#include "AlexaBridgeLogic.h"
|
||||
#include "AlexaCapability.h"
|
||||
#include "AlexaLog.h"
|
||||
|
||||
// What a 1.x sketch wrote where the time belongs when it built a property by hand: the backend's HTTP route
|
||||
|
|
@ -82,9 +83,69 @@ JsonObject AlexaStatusMessage::payload()
|
|||
return doc["event"]["payload"].as<JsonObject>();
|
||||
}
|
||||
|
||||
AlexaStatusMessage &AlexaStatusMessage::addTemperatureSensorProp(float value, TemperatureSensorScale scale, unsigned int uncertaintyInMs)
|
||||
JsonVariant AlexaStatusMessage::newProperty(const AlexaInterfaceDesc &row, uint8_t place, unsigned int uncertaintyInMs, const String &instance)
|
||||
{
|
||||
JsonDocument temperature;
|
||||
JsonObject prop = properties().add<JsonObject>();
|
||||
prop["namespace"] = FPSTR(row.ns);
|
||||
prop["name"] = FPSTR(row.property(place));
|
||||
if (!instance.isEmpty())
|
||||
{
|
||||
prop["instance"] = instance;
|
||||
}
|
||||
// Its place is between the name and the time, where the pages of Alexa show it
|
||||
JsonVariant value = prop["value"].to<JsonVariant>();
|
||||
setTimeOfSample(prop);
|
||||
prop["uncertaintyInMilliseconds"] = uncertaintyInMs;
|
||||
return value;
|
||||
}
|
||||
|
||||
int AlexaStatusMessage::withinRange(int value, int lowest, int highest, const AlexaInterfaceDesc &row) const
|
||||
{
|
||||
if (value >= lowest && value <= highest)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
return nearestOf(value < lowest, lowest, highest, row);
|
||||
}
|
||||
|
||||
float AlexaStatusMessage::withinRange(float value, int lowest, int highest, const AlexaInterfaceDesc &row) const
|
||||
{
|
||||
if (value >= lowest && value <= highest)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
return nearestOf(value < lowest, lowest, highest, row);
|
||||
}
|
||||
|
||||
int AlexaStatusMessage::nearestOf(bool below, int lowest, int highest, const AlexaInterfaceDesc &row) const
|
||||
{
|
||||
const int nearest = below ? lowest : highest;
|
||||
ALEX2ESP_LOGE("%s: %s reported as %d: the value was outside of %d to %d, the range Alexa gives for it",
|
||||
endpointId.c_str(), AlexaCapabilityName(row, nullptr).text, nearest, lowest, highest);
|
||||
return nearest;
|
||||
}
|
||||
|
||||
AlexaStatusMessage &AlexaStatusMessage::addSwitchProp(const AlexaInterfaceDesc &row, PowerController state, unsigned int uncertaintyInMs, const String &instance)
|
||||
{
|
||||
newProperty(row, 0, uncertaintyInMs, instance).set(state == PowerController::ON ? F("ON") : F("OFF"));
|
||||
return *this;
|
||||
}
|
||||
|
||||
AlexaStatusMessage &AlexaStatusMessage::addNumberProp(const AlexaInterfaceDesc &row, int value, int lowest, int highest, unsigned int uncertaintyInMs)
|
||||
{
|
||||
newProperty(row, 0, uncertaintyInMs).set(withinRange(value, lowest, highest, row));
|
||||
return *this;
|
||||
}
|
||||
|
||||
AlexaStatusMessage &AlexaStatusMessage::addDetectionProp(const AlexaInterfaceDesc &row, bool detected, unsigned int uncertaintyInMs)
|
||||
{
|
||||
newProperty(row, 0, uncertaintyInMs).set(detected ? F("DETECTED") : F("NOT_DETECTED"));
|
||||
return *this;
|
||||
}
|
||||
|
||||
AlexaStatusMessage &AlexaStatusMessage::addTemperatureProp(const AlexaInterfaceDesc &row, uint8_t place, float value, TemperatureSensorScale scale, unsigned int uncertaintyInMs)
|
||||
{
|
||||
JsonObject temperature = newProperty(row, place, uncertaintyInMs).to<JsonObject>();
|
||||
temperature["value"] = value;
|
||||
if (scale == TemperatureSensorScale::FAHRENHEIT)
|
||||
{
|
||||
|
|
@ -98,7 +159,170 @@ AlexaStatusMessage &AlexaStatusMessage::addTemperatureSensorProp(float value, Te
|
|||
{
|
||||
temperature["scale"] = F("CELSIUS");
|
||||
}
|
||||
return AddProperty(AlexaInterfaces::TemperatureSensor, temperature.as<JsonObject>(), uncertaintyInMs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
AlexaStatusMessage &AlexaStatusMessage::addHealthProp(EndpointHealth endpointHealth, unsigned int uncertaintyInMs)
|
||||
{
|
||||
JsonObject connectivity = newProperty(AlexaInterfaces::EndpointHealth, 0, uncertaintyInMs).to<JsonObject>();
|
||||
connectivity["value"] = endpointHealth == EndpointHealth::OK ? F("OK") : F("UNREACHABLE");
|
||||
return *this;
|
||||
}
|
||||
|
||||
AlexaStatusMessage &AlexaStatusMessage::addPowerControllerProp(PowerController powerController, unsigned int uncertaintyInMs)
|
||||
{
|
||||
return addSwitchProp(AlexaInterfaces::PowerController, powerController, uncertaintyInMs);
|
||||
}
|
||||
|
||||
AlexaStatusMessage &AlexaStatusMessage::addBrightnessControllerProp(int brightness, unsigned int uncertaintyInMs)
|
||||
{
|
||||
return addNumberProp(AlexaInterfaces::BrightnessController, brightness, 0, 100, uncertaintyInMs);
|
||||
}
|
||||
|
||||
AlexaStatusMessage &AlexaStatusMessage::addPercentageControllerProp(int percentage, unsigned int uncertaintyInMs)
|
||||
{
|
||||
return addNumberProp(AlexaInterfaces::PercentageController, percentage, 0, 100, uncertaintyInMs);
|
||||
}
|
||||
|
||||
AlexaStatusMessage &AlexaStatusMessage::addPowerLevelControllerProp(int powerLevel, unsigned int uncertaintyInMs)
|
||||
{
|
||||
return addNumberProp(AlexaInterfaces::PowerLevelController, powerLevel, 0, 100, uncertaintyInMs);
|
||||
}
|
||||
|
||||
AlexaStatusMessage &AlexaStatusMessage::addColorTemperatureControllerProp(int colorTemperature, unsigned int uncertaintyInMs)
|
||||
{
|
||||
return addNumberProp(AlexaInterfaces::ColorTemperatureController, colorTemperature, 1000, 10000, uncertaintyInMs);
|
||||
}
|
||||
|
||||
AlexaStatusMessage &AlexaStatusMessage::addColorControllerProp(float hue, float saturation, float brightness, unsigned int uncertaintyInMs)
|
||||
{
|
||||
const AlexaInterfaceDesc &row = AlexaInterfaces::ColorController;
|
||||
JsonObject color = newProperty(row, 0, uncertaintyInMs).to<JsonObject>();
|
||||
color[F("hue")] = withinRange(hue, 0, 360, row);
|
||||
color[F("saturation")] = withinRange(saturation, 0, 1, row);
|
||||
color[F("brightness")] = withinRange(brightness, 0, 1, row);
|
||||
return *this;
|
||||
}
|
||||
|
||||
AlexaStatusMessage &AlexaStatusMessage::addRangeControllerProp(const String &instance, float value, unsigned int uncertaintyInMs)
|
||||
{
|
||||
newProperty(AlexaInterfaces::RangeController, 0, uncertaintyInMs, instance).set(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
AlexaStatusMessage &AlexaStatusMessage::addModeControllerProp(const String &instance, const String &mode, unsigned int uncertaintyInMs)
|
||||
{
|
||||
newProperty(AlexaInterfaces::ModeController, 0, uncertaintyInMs, instance).set(mode);
|
||||
return *this;
|
||||
}
|
||||
|
||||
AlexaStatusMessage &AlexaStatusMessage::addToggleControllerProp(const String &instance, PowerController toggleState, unsigned int uncertaintyInMs)
|
||||
{
|
||||
return addSwitchProp(AlexaInterfaces::ToggleController, toggleState, uncertaintyInMs, instance);
|
||||
}
|
||||
|
||||
AlexaStatusMessage &AlexaStatusMessage::addLockControllerProp(AlexaLockState lockState, unsigned int uncertaintyInMs)
|
||||
{
|
||||
JsonVariant value = newProperty(AlexaInterfaces::LockController, 0, uncertaintyInMs);
|
||||
switch (lockState)
|
||||
{
|
||||
case AlexaLockState::LOCKED:
|
||||
value.set(F("LOCKED"));
|
||||
break;
|
||||
case AlexaLockState::UNLOCKED:
|
||||
value.set(F("UNLOCKED"));
|
||||
break;
|
||||
case AlexaLockState::JAMMED:
|
||||
value.set(F("JAMMED"));
|
||||
break;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
AlexaStatusMessage &AlexaStatusMessage::addContactSensorProp(bool detected, unsigned int uncertaintyInMs)
|
||||
{
|
||||
return addDetectionProp(AlexaInterfaces::ContactSensor, detected, uncertaintyInMs);
|
||||
}
|
||||
|
||||
AlexaStatusMessage &AlexaStatusMessage::addMotionSensorProp(bool detected, unsigned int uncertaintyInMs)
|
||||
{
|
||||
return addDetectionProp(AlexaInterfaces::MotionSensor, detected, uncertaintyInMs);
|
||||
}
|
||||
|
||||
AlexaStatusMessage &AlexaStatusMessage::addTemperatureSensorProp(float value, TemperatureSensorScale scale, unsigned int uncertaintyInMs)
|
||||
{
|
||||
return addTemperatureProp(AlexaInterfaces::TemperatureSensor, 0, value, scale, uncertaintyInMs);
|
||||
}
|
||||
|
||||
AlexaStatusMessage &AlexaStatusMessage::addHumiditySensorProp(float humidity, unsigned int uncertaintyInMs)
|
||||
{
|
||||
const AlexaInterfaceDesc &row = AlexaInterfaces::HumiditySensor;
|
||||
JsonObject relativeHumidity = newProperty(row, 0, uncertaintyInMs).to<JsonObject>();
|
||||
relativeHumidity["value"] = withinRange(humidity, 0, 100, row);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// The places of the properties in the row of the thermostat (src/AlexaInterfaces.cpp)
|
||||
enum ThermostatProperty : uint8_t
|
||||
{
|
||||
THERMOSTAT_TARGET_SETPOINT,
|
||||
THERMOSTAT_LOWER_SETPOINT,
|
||||
THERMOSTAT_UPPER_SETPOINT,
|
||||
THERMOSTAT_MODE
|
||||
};
|
||||
|
||||
AlexaStatusMessage &AlexaStatusMessage::addThermostatSetpointProp(float value, TemperatureSensorScale scale, unsigned int uncertaintyInMs)
|
||||
{
|
||||
return addTemperatureProp(AlexaInterfaces::ThermostatController, THERMOSTAT_TARGET_SETPOINT, value, scale, uncertaintyInMs);
|
||||
}
|
||||
|
||||
AlexaStatusMessage &AlexaStatusMessage::addThermostatLowerSetpointProp(float value, TemperatureSensorScale scale, unsigned int uncertaintyInMs)
|
||||
{
|
||||
return addTemperatureProp(AlexaInterfaces::ThermostatController, THERMOSTAT_LOWER_SETPOINT, value, scale, uncertaintyInMs);
|
||||
}
|
||||
|
||||
AlexaStatusMessage &AlexaStatusMessage::addThermostatUpperSetpointProp(float value, TemperatureSensorScale scale, unsigned int uncertaintyInMs)
|
||||
{
|
||||
return addTemperatureProp(AlexaInterfaces::ThermostatController, THERMOSTAT_UPPER_SETPOINT, value, scale, uncertaintyInMs);
|
||||
}
|
||||
|
||||
AlexaStatusMessage &AlexaStatusMessage::addThermostatModeProp(AlexaThermostatMode mode, unsigned int uncertaintyInMs)
|
||||
{
|
||||
JsonVariant value = newProperty(AlexaInterfaces::ThermostatController, THERMOSTAT_MODE, uncertaintyInMs);
|
||||
switch (mode)
|
||||
{
|
||||
case AlexaThermostatMode::AUTO:
|
||||
value.set(F("AUTO"));
|
||||
break;
|
||||
case AlexaThermostatMode::COOL:
|
||||
value.set(F("COOL"));
|
||||
break;
|
||||
case AlexaThermostatMode::ECO:
|
||||
value.set(F("ECO"));
|
||||
break;
|
||||
case AlexaThermostatMode::EM_HEAT:
|
||||
value.set(F("EM_HEAT"));
|
||||
break;
|
||||
case AlexaThermostatMode::HEAT:
|
||||
value.set(F("HEAT"));
|
||||
break;
|
||||
case AlexaThermostatMode::OFF:
|
||||
value.set(F("OFF"));
|
||||
break;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
AlexaStatusMessage &AlexaStatusMessage::addProperty(const AlexaInterfaceDesc &row, uint8_t place, JsonVariantConst value, unsigned int uncertaintyInMs, const String &instance)
|
||||
{
|
||||
if (place >= row.propertyCount())
|
||||
{
|
||||
ALEX2ESP_LOGE("%s: property %d of %s not added: the interface has %d, the first of them is 0",
|
||||
endpointId.c_str(), place, AlexaCapabilityName(row, nullptr).text, row.propertyCount());
|
||||
return *this;
|
||||
}
|
||||
newProperty(row, place, uncertaintyInMs, instance).set(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
AlexaStatusMessage &AlexaStatusMessage::addContextProp(JsonObjectConst property)
|
||||
|
|
|
|||
|
|
@ -36,6 +36,24 @@ enum class TemperatureSensorScale
|
|||
KELVIN
|
||||
};
|
||||
|
||||
enum class AlexaLockState : uint8_t
|
||||
{
|
||||
LOCKED,
|
||||
UNLOCKED,
|
||||
JAMMED // the lock cannot reach either state
|
||||
};
|
||||
|
||||
// The modes of alexa-thermostatcontroller.html. A thermostat announces the ones it has in its configuration.
|
||||
enum class AlexaThermostatMode : uint8_t
|
||||
{
|
||||
AUTO,
|
||||
COOL,
|
||||
ECO,
|
||||
EM_HEAT, // the emergency or auxiliary heat of a heat pump
|
||||
HEAT,
|
||||
OFF
|
||||
};
|
||||
|
||||
enum class AlexaMessageKind : uint8_t
|
||||
{
|
||||
RESPONSE, // the answer to a directive that was carried out
|
||||
|
|
@ -88,40 +106,63 @@ public:
|
|||
|
||||
AlexaMessageKind getKind() const { return kind; }
|
||||
|
||||
// The properties of the device. In a ChangeReport the properties added before context() are those that
|
||||
// changed and the ones after it the others; every other kind has one list. An ErrorResponse, a
|
||||
// DeferredResponse and an event carry no properties: what is added to them is not sent.
|
||||
AlexaStatusMessage &addHealthProp(EndpointHealth endpointHealth, unsigned int uncertaintyInMs = 0)
|
||||
{
|
||||
JsonDocument healthValue;
|
||||
healthValue["value"] = (endpointHealth == EndpointHealth::OK) ? "OK" : "UNREACHABLE";
|
||||
return AddProperty(AlexaInterfaces::EndpointHealth, healthValue.as<JsonObject>(), uncertaintyInMs);
|
||||
}
|
||||
// The properties of the device, one function for each. In a ChangeReport the properties added before
|
||||
// context() are those that changed and the ones after it the others; every other kind has one list. An
|
||||
// ErrorResponse, a DeferredResponse and an event carry no properties: what is added to them is not sent.
|
||||
//
|
||||
// uncertaintyInMs is the time since the value was read from the hardware. A value outside of the range
|
||||
// that Alexa gives for its property is reported as the nearest one of the range, and the log says so at
|
||||
// ERROR. A sketch links the functions it calls and no others.
|
||||
AlexaStatusMessage &addHealthProp(EndpointHealth endpointHealth, unsigned int uncertaintyInMs = 0);
|
||||
AlexaStatusMessage &addPowerControllerProp(PowerController powerController, unsigned int uncertaintyInMs = 0);
|
||||
|
||||
AlexaStatusMessage &addPowerControllerProp(PowerController powerController, unsigned int uncertaintyInMs = 0)
|
||||
{
|
||||
String value = (powerController == PowerController::ON) ? "ON" : "OFF";
|
||||
return AddProperty(AlexaInterfaces::PowerController, value, uncertaintyInMs);
|
||||
}
|
||||
// 0 to 100
|
||||
AlexaStatusMessage &addBrightnessControllerProp(int brightness, unsigned int uncertaintyInMs = 0);
|
||||
AlexaStatusMessage &addPercentageControllerProp(int percentage, unsigned int uncertaintyInMs = 0);
|
||||
AlexaStatusMessage &addPowerLevelControllerProp(int powerLevel, unsigned int uncertaintyInMs = 0);
|
||||
|
||||
// 1000 to 10000 K
|
||||
AlexaStatusMessage &addColorTemperatureControllerProp(int colorTemperature, unsigned int uncertaintyInMs = 0);
|
||||
|
||||
// Hue 0 to 360 degrees, saturation and brightness 0 to 1
|
||||
AlexaStatusMessage &addColorControllerProp(float hue, float saturation, float brightness, unsigned int uncertaintyInMs = 0);
|
||||
|
||||
// The generic controllers, with the instance of the capability. The range of a value and the modes are what
|
||||
// the configuration of the capability announced; the library does not check them.
|
||||
AlexaStatusMessage &addRangeControllerProp(const String &instance, float value, unsigned int uncertaintyInMs = 0);
|
||||
AlexaStatusMessage &addModeControllerProp(const String &instance, const String &mode, unsigned int uncertaintyInMs = 0);
|
||||
AlexaStatusMessage &addToggleControllerProp(const String &instance, PowerController toggleState, unsigned int uncertaintyInMs = 0);
|
||||
|
||||
AlexaStatusMessage &addLockControllerProp(AlexaLockState lockState, unsigned int uncertaintyInMs = 0);
|
||||
|
||||
// true: the contact is open, there is motion
|
||||
AlexaStatusMessage &addContactSensorProp(bool detected, unsigned int uncertaintyInMs = 0);
|
||||
AlexaStatusMessage &addMotionSensorProp(bool detected, unsigned int uncertaintyInMs = 0);
|
||||
|
||||
// The temperature is reported in the scale it is given in
|
||||
AlexaStatusMessage &addTemperatureSensorProp(float value, TemperatureSensorScale scale = TemperatureSensorScale::CELSIUS, unsigned int uncertaintyInMs = 0);
|
||||
|
||||
AlexaStatusMessage &addBrightnessControllerProp(unsigned int brightness, unsigned int uncertaintyInMs = 0)
|
||||
{
|
||||
return AddProperty(AlexaInterfaces::BrightnessController, brightness, uncertaintyInMs);
|
||||
}
|
||||
// Relative humidity, 0 to 100 percent
|
||||
AlexaStatusMessage &addHumiditySensorProp(float humidity, unsigned int uncertaintyInMs = 0);
|
||||
|
||||
AlexaStatusMessage &addColorTemperatureControllerProp(unsigned int colorTemperature, unsigned int uncertaintyInMs = 0)
|
||||
// A thermostat with one setpoint reports targetSetpoint. One that heats below a temperature and cools above
|
||||
// another reports lowerSetpoint and upperSetpoint, which addThermostatDualSetpointProp() adds together.
|
||||
AlexaStatusMessage &addThermostatSetpointProp(float value, TemperatureSensorScale scale = TemperatureSensorScale::CELSIUS, unsigned int uncertaintyInMs = 0);
|
||||
AlexaStatusMessage &addThermostatLowerSetpointProp(float value, TemperatureSensorScale scale = TemperatureSensorScale::CELSIUS, unsigned int uncertaintyInMs = 0);
|
||||
AlexaStatusMessage &addThermostatUpperSetpointProp(float value, TemperatureSensorScale scale = TemperatureSensorScale::CELSIUS, unsigned int uncertaintyInMs = 0);
|
||||
AlexaStatusMessage &addThermostatDualSetpointProp(float lower, float upper, TemperatureSensorScale scale = TemperatureSensorScale::CELSIUS, unsigned int uncertaintyInMs = 0)
|
||||
{
|
||||
return AddProperty(AlexaInterfaces::ColorTemperatureController, colorTemperature, uncertaintyInMs);
|
||||
return addThermostatLowerSetpointProp(lower, scale, uncertaintyInMs).addThermostatUpperSetpointProp(upper, scale, uncertaintyInMs);
|
||||
}
|
||||
AlexaStatusMessage &addThermostatModeProp(AlexaThermostatMode mode, unsigned int uncertaintyInMs = 0);
|
||||
|
||||
AlexaStatusMessage &addToggleControllerProp(const String &instance, PowerController toggleState, unsigned int uncertaintyInMs = 0)
|
||||
{
|
||||
String value = (toggleState == PowerController::ON) ? "ON" : "OFF";
|
||||
return AddProperty(AlexaInterfaces::ToggleController, value, uncertaintyInMs, instance);
|
||||
}
|
||||
// A property without a function of its own: the row of its interface, the place of the property in the row
|
||||
// (0 for the first) and its value, which is copied.
|
||||
// JsonDocument volume;
|
||||
// volume.set(40);
|
||||
// message.addProperty(AlexaInterfaces::Speaker, 0, volume.as<JsonVariantConst>());
|
||||
// A place the row does not have adds nothing and prints an error.
|
||||
AlexaStatusMessage &addProperty(const AlexaInterfaceDesc &row, uint8_t place, JsonVariantConst value, unsigned int uncertaintyInMs = 0, const String &instance = "");
|
||||
|
||||
// Adds a property the sketch built itself. Its timeOfSample is set to the current time when the object has
|
||||
// none or still carries the 1.x placeholder "{REPLACE_WITH_DATETIME}".
|
||||
|
|
@ -140,11 +181,11 @@ public:
|
|||
{
|
||||
return addTemperatureSensorProp(value, tempSensor, uncertaintyInMs);
|
||||
}
|
||||
AlexaStatusMessage &AddBrightnessControllerProp(unsigned int brightness, unsigned int uncertaintyInMs = 0)
|
||||
AlexaStatusMessage &AddBrightnessControllerProp(int brightness, unsigned int uncertaintyInMs = 0)
|
||||
{
|
||||
return addBrightnessControllerProp(brightness, uncertaintyInMs);
|
||||
}
|
||||
AlexaStatusMessage &AddColorTemperatureControllerProp(unsigned int colorTemperature, unsigned int uncertaintyInMs = 0)
|
||||
AlexaStatusMessage &AddColorTemperatureControllerProp(int colorTemperature, unsigned int uncertaintyInMs = 0)
|
||||
{
|
||||
return addColorTemperatureControllerProp(colorTemperature, uncertaintyInMs);
|
||||
}
|
||||
|
|
@ -207,22 +248,20 @@ private:
|
|||
void setTimeOfSample(JsonObject property);
|
||||
bool publishOn(PGM_P topicEnd, bool ofEndpoint);
|
||||
|
||||
// Adds the first property of the row: every interface with a helper above reports one
|
||||
template <typename T>
|
||||
AlexaStatusMessage &AddProperty(const AlexaInterfaceDesc &row, const T &value, unsigned int uncertaintyInMs = 0,const String &instanceName="")
|
||||
{
|
||||
JsonObject prop = properties().add<JsonObject>();
|
||||
prop["namespace"] = FPSTR(row.ns);
|
||||
prop["name"] = FPSTR(row.property(0));
|
||||
// Adds a property to the list and returns its value, which is still empty, for the caller to set. The
|
||||
// place has to be one the row has.
|
||||
JsonVariant newProperty(const AlexaInterfaceDesc &row, uint8_t place, unsigned int uncertaintyInMs, const String &instance = "");
|
||||
|
||||
if (!instanceName.isEmpty()) {
|
||||
prop["instance"] = instanceName;
|
||||
}
|
||||
prop["value"] = value;
|
||||
// What several properties have in common
|
||||
AlexaStatusMessage &addSwitchProp(const AlexaInterfaceDesc &row, PowerController state, unsigned int uncertaintyInMs, const String &instance = "");
|
||||
AlexaStatusMessage &addNumberProp(const AlexaInterfaceDesc &row, int value, int lowest, int highest, unsigned int uncertaintyInMs);
|
||||
AlexaStatusMessage &addDetectionProp(const AlexaInterfaceDesc &row, bool detected, unsigned int uncertaintyInMs);
|
||||
AlexaStatusMessage &addTemperatureProp(const AlexaInterfaceDesc &row, uint8_t place, float value, TemperatureSensorScale scale, unsigned int uncertaintyInMs);
|
||||
|
||||
setTimeOfSample(prop);
|
||||
prop["uncertaintyInMilliseconds"] = uncertaintyInMs;
|
||||
return *this;
|
||||
}
|
||||
// The value when it is within the range, the nearest of the range with a line in the log when it is not.
|
||||
// Whole numbers have a function of their own: a lamp that reports its brightness links no floating point.
|
||||
int withinRange(int value, int lowest, int highest, const AlexaInterfaceDesc &row) const;
|
||||
float withinRange(float value, int lowest, int highest, const AlexaInterfaceDesc &row) const;
|
||||
int nearestOf(bool below, int lowest, int highest, const AlexaInterfaceDesc &row) const;
|
||||
};
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue