Updated logic for required/optional Characteristics

Logic now embedded in 'i' CLI command.
This commit is contained in:
Gregg 2022-04-30 22:20:42 -05:00
parent 0e6f7d27f9
commit 9f71c67f14
3 changed files with 32 additions and 77 deletions

View File

@ -179,17 +179,6 @@ void Span::pollTask() {
if(!isInitialized){ if(!isInitialized){
if(!homeSpan.Accessories.empty()){
if(!homeSpan.Accessories.back()->Services.empty())
homeSpan.Accessories.back()->Services.back()->validate();
}
if(nWarnings>0){
configLog+="\n*** CAUTION: There " + String((nWarnings>1?"are ":"is ")) + String(nWarnings) + " WARNING" + (nWarnings>1?"S":"") + " associated with this configuration that may lead to the device becoming non-responsive, or operating in an unexpected manner. ***\n";
}
processSerialCommand("i"); // print homeSpan configuration info processSerialCommand("i"); // print homeSpan configuration info
if(nFatalErrors>0){ if(nFatalErrors>0){
@ -926,7 +915,7 @@ void Span::processSerialCommand(const char *c){
Serial.print("\n*** HomeSpan Info ***\n\n"); Serial.print("\n*** HomeSpan Info ***\n\n");
std::set<uint32_t> aidValues; unordered_set<uint32_t> aidValues;
for(auto acc=Accessories.begin(); acc!=Accessories.end(); acc++){ for(auto acc=Accessories.begin(); acc!=Accessories.end(); acc++){
Serial.printf("\u27a4 Accessory: AID=%d\n",(*acc)->aid); Serial.printf("\u27a4 Accessory: AID=%d\n",(*acc)->aid);
@ -951,7 +940,9 @@ void Span::processSerialCommand(const char *c){
} }
else if((*acc)->aid==1) // this is an Accessory with aid=1, but it has more than just AccessoryInfo. So... else if((*acc)->aid==1) // this is an Accessory with aid=1, but it has more than just AccessoryInfo. So...
isBridge=false; // ...this is not a bridge device isBridge=false; // ...this is not a bridge device
unordered_set<HapChar *> hapChar;
for(auto chr=(*svc)->Characteristics.begin(); chr!=(*svc)->Characteristics.end(); chr++){ for(auto chr=(*svc)->Characteristics.begin(); chr!=(*svc)->Characteristics.end(); chr++){
Serial.printf(" \u21e8 Characteristic %s(%s): IID=%d, %sUUID=\"%s\"",(*chr)->hapName,(*chr)->uvPrint((*chr)->value).c_str(),(*chr)->iid,(*chr)->isCustom?"Custom-":"",(*chr)->type); Serial.printf(" \u21e8 Characteristic %s(%s): IID=%d, %sUUID=\"%s\"",(*chr)->hapName,(*chr)->uvPrint((*chr)->value).c_str(),(*chr)->iid,(*chr)->isCustom?"Custom-":"",(*chr)->type);
@ -966,13 +957,13 @@ void Span::processSerialCommand(const char *c){
Serial.printf(" (nvs)"); Serial.printf(" (nvs)");
Serial.printf("\n"); Serial.printf("\n");
if(!(*chr)->isSupported) if(!(*chr)->isCustom && !(*svc)->isCustom && (*svc)->req.find((*chr)->hapChar)==(*svc)->req.end() && (*svc)->opt.find((*chr)->hapChar)==(*svc)->opt.end())
Serial.printf(" *** WARNING! Service does not support this Characteristic ***\n"); Serial.printf(" *** WARNING! Service does not support this Characteristic ***\n");
else else
if(invalidUUID((*chr)->type,(*chr)->isCustom)) if(invalidUUID((*chr)->type,(*chr)->isCustom))
Serial.printf(" *** ERROR! Format of UUID is invalid ***\n"); Serial.printf(" *** ERROR! Format of UUID is invalid ***\n");
else else
if((*chr)->isRepeated) if(hapChar.find((*chr)->hapChar)!=hapChar.end())
Serial.printf(" *** ERROR! Characteristic already defined for this Service ***\n"); Serial.printf(" *** ERROR! Characteristic already defined for this Service ***\n");
if((*chr)->setRangeError) if((*chr)->setRangeError)
@ -981,15 +972,19 @@ void Span::processSerialCommand(const char *c){
if((*chr)->format!=STRING && ((*chr)->uvGet<double>((*chr)->value) < (*chr)->uvGet<double>((*chr)->minValue) || (*chr)->uvGet<double>((*chr)->value) > (*chr)->uvGet<double>((*chr)->maxValue))) if((*chr)->format!=STRING && ((*chr)->uvGet<double>((*chr)->value) < (*chr)->uvGet<double>((*chr)->minValue) || (*chr)->uvGet<double>((*chr)->value) > (*chr)->uvGet<double>((*chr)->maxValue)))
Serial.printf(" *** WARNING! Value of %llg is out of range [%llg,%llg] ***\n",(*chr)->uvGet<double>((*chr)->value),(*chr)->uvGet<double>((*chr)->minValue),(*chr)->uvGet<double>((*chr)->maxValue)); Serial.printf(" *** WARNING! Value of %llg is out of range [%llg,%llg] ***\n",(*chr)->uvGet<double>((*chr)->value),(*chr)->uvGet<double>((*chr)->minValue),(*chr)->uvGet<double>((*chr)->maxValue));
// 2. MISSING REQUIRED CHARACTERISTICS hapChar.insert((*chr)->hapChar);
// 3. AID CHECK
} // Characteristics } // Characteristics
for(auto req=(*svc)->req.begin(); req!=(*svc)->req.end(); req++){
if(hapChar.find(*req)==hapChar.end())
Serial.printf(" *** WARNING! Required '%s' Characteristic for this Service not found ***\n",(*req)->hapName);
}
} // Services } // Services
if(!foundInfo) if(!foundInfo)
Serial.printf(" *** ERROR! Required Accessory Information Service not found ***\n"); Serial.printf(" *** ERROR! Required 'AccessoryInformation' Service not found ***\n");
} // Accessories } // Accessories
@ -1458,10 +1453,7 @@ SpanAccessory::SpanAccessory(uint32_t aid){
} }
this->aid=homeSpan.Accessories.back()->aid+1; this->aid=homeSpan.Accessories.back()->aid+1;
if(!homeSpan.Accessories.back()->Services.empty())
homeSpan.Accessories.back()->Services.back()->validate();
} else { } else {
this->aid=1; this->aid=1;
} }
@ -1498,22 +1490,18 @@ int SpanAccessory::sprintfAttributes(char *cBuf){
SpanService::SpanService(const char *type, const char *hapName, boolean isCustom){ SpanService::SpanService(const char *type, const char *hapName, boolean isCustom){
// if(!homeSpan.Accessories.empty() && !homeSpan.Accessories.back()->Services.empty()) // this is not the first Service to be defined for this Accessory if(homeSpan.Accessories.empty()){
// homeSpan.Accessories.back()->Services.back()->validate(); Serial.printf("\nFATAL ERROR! Can't create new Service '%s' without a defined Accessory ***\n",hapName);
Serial.printf("\n=== PROGRAM HALTED ===");
while(1);
}
this->type=type; this->type=type;
this->hapName=hapName; this->hapName=hapName;
this->isCustom=isCustom; this->isCustom=isCustom;
if(homeSpan.Accessories.empty()){
homeSpan.configLog+=" *** ERROR! Can't create new Service without a defined Accessory! ***\n";
homeSpan.nFatalErrors++;
return;
}
homeSpan.Accessories.back()->Services.push_back(this); homeSpan.Accessories.back()->Services.push_back(this);
iid=++(homeSpan.Accessories.back()->iidCount); iid=++(homeSpan.Accessories.back()->iidCount);
} }
/////////////////////////////// ///////////////////////////////
@ -1573,26 +1561,6 @@ int SpanService::sprintfAttributes(char *cBuf){
return(nBytes); return(nBytes);
} }
///////////////////////////////
void SpanService::validate(){
for(int i=0;i<req.size();i++){
boolean valid=false;
for(int j=0;!valid && j<Characteristics.size();j++)
valid=!strcmp(req[i]->type,Characteristics[j]->type);
if(!valid){
homeSpan.configLog+=" \u2718 Characteristic " + String(req[i]->hapName);
homeSpan.configLog+=" *** WARNING! Required Characteristic for this Service not found. ***\n";
homeSpan.nWarnings++;
}
}
vector<HapChar *>().swap(opt);
vector<HapChar *>().swap(req);
}
/////////////////////////////// ///////////////////////////////
// SpanCharacteristic // // SpanCharacteristic //
/////////////////////////////// ///////////////////////////////
@ -1604,13 +1572,12 @@ SpanCharacteristic::SpanCharacteristic(HapChar *hapChar, boolean isCustom){
format=hapChar->format; format=hapChar->format;
staticRange=hapChar->staticRange; staticRange=hapChar->staticRange;
this->isCustom=isCustom; this->isCustom=isCustom;
this->hapChar=hapChar;
homeSpan.configLog+=" \u21e8 Characteristic " + String(hapName);
if(homeSpan.Accessories.empty() || homeSpan.Accessories.back()->Services.empty()){ if(homeSpan.Accessories.empty() || homeSpan.Accessories.back()->Services.empty()){
homeSpan.configLog+=" *** ERROR! Can't create new Characteristic without a defined Service! ***\n"; Serial.printf("\nFATAL ERROR! Can't create new Characteristic '%s' without a defined Service ***\n",hapName);
homeSpan.nFatalErrors++; Serial.printf("\n=== PROGRAM HALTED ===");
return; while(1);
} }
iid=++(homeSpan.Accessories.back()->iidCount); iid=++(homeSpan.Accessories.back()->iidCount);

View File

@ -36,7 +36,7 @@
#include <Arduino.h> #include <Arduino.h>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
#include <set> #include <unordered_set>
#include <nvs.h> #include <nvs.h>
#include <ArduinoOTA.h> #include <ArduinoOTA.h>
@ -49,6 +49,7 @@
using std::vector; using std::vector;
using std::unordered_map; using std::unordered_map;
using std::unordered_set;
enum { enum {
GET_AID=1, GET_AID=1,
@ -297,8 +298,8 @@ struct SpanService{
boolean hidden=false; // optional property indicating service is hidden boolean hidden=false; // optional property indicating service is hidden
boolean primary=false; // optional property indicating service is primary boolean primary=false; // optional property indicating service is primary
vector<SpanCharacteristic *> Characteristics; // vector of pointers to all Characteristics in this Service vector<SpanCharacteristic *> Characteristics; // vector of pointers to all Characteristics in this Service
vector<HapChar *> req; // vector of pointers to all required HAP Characteristic Types for this Service unordered_set<HapChar *> req; // unordered set of pointers to all required HAP Characteristic Types for this Service
vector<HapChar *> opt; // vector of pointers to all optional HAP Characteristic Types for this Service unordered_set<HapChar *> opt; // unordered set of pointers to all optional HAP Characteristic Types for this Service
vector<SpanService *> linkedServices; // vector of pointers to any optional linked Services vector<SpanService *> linkedServices; // vector of pointers to any optional linked Services
boolean isCustom; // flag to indicate this is a Custom Service boolean isCustom; // flag to indicate this is a Custom Service
@ -310,7 +311,6 @@ struct SpanService{
vector<SpanService *> getLinks(){return(linkedServices);} // returns linkedServices vector for use as range in "for-each" loops vector<SpanService *> getLinks(){return(linkedServices);} // returns linkedServices vector for use as range in "for-each" loops
int sprintfAttributes(char *cBuf); // prints Service JSON records into buf; return number of characters printed, excluding null terminator int sprintfAttributes(char *cBuf); // prints Service JSON records into buf; return number of characters printed, excluding null terminator
void validate(); // error-checks Service
virtual boolean update() {return(true);} // placeholder for code that is called when a Service is updated via a Controller. Must return true/false depending on success of update virtual boolean update() {return(true);} // placeholder for code that is called when a Service is updated via a Controller. Must return true/false depending on success of update
virtual void loop(){} // loops for each Service - called every cycle and can be over-ridden with user-defined code virtual void loop(){} // loops for each Service - called every cycle and can be over-ridden with user-defined code
@ -333,6 +333,7 @@ struct SpanCharacteristic{
}; };
int iid=0; // Instance ID (HAP Table 6-3) int iid=0; // Instance ID (HAP Table 6-3)
HapChar *hapChar; // pointer to HAP Characteristic structure
const char *type; // Characteristic Type const char *type; // Characteristic Type
const char *hapName; // HAP Name const char *hapName; // HAP Name
UVal value; // Characteristic Value UVal value; // Characteristic Value
@ -349,8 +350,6 @@ struct SpanCharacteristic{
boolean *ev; // Characteristic Event Notify Enable (per-connection) boolean *ev; // Characteristic Event Notify Enable (per-connection)
char *nvsKey=NULL; // key for NVS storage of Characteristic value char *nvsKey=NULL; // key for NVS storage of Characteristic value
boolean isCustom; // flag to indicate this is a Custom Characteristic boolean isCustom; // flag to indicate this is a Custom Characteristic
boolean isSupported; // flag to indicate this Characteristic is supported by the containing Service (it's either required or optional)
boolean isRepeated=false; // flag to indicate this Characteristic is defined repeated times within the same Service (reports an error)
boolean setRangeError=false; // flag to indicate attempt to set range on Characteristic that does not support changes to range boolean setRangeError=false; // flag to indicate attempt to set range on Characteristic that does not support changes to range
uint32_t aid=0; // Accessory ID - passed through from Service containing this Characteristic uint32_t aid=0; // Accessory ID - passed through from Service containing this Characteristic
@ -512,18 +511,7 @@ struct SpanCharacteristic{
uvSet(maxValue,max); uvSet(maxValue,max);
uvSet(stepValue,0); uvSet(stepValue,0);
} }
isSupported=isCustom|service->isCustom; // automatically set valid if either Characteristic or containing Service is Custom
for(int i=0; !isSupported && i<homeSpan.Accessories.back()->Services.back()->req.size(); i++)
isSupported=!strcmp(type,homeSpan.Accessories.back()->Services.back()->req[i]->type);
for(int i=0; !isSupported && i<homeSpan.Accessories.back()->Services.back()->opt.size(); i++)
isSupported=!strcmp(type,homeSpan.Accessories.back()->Services.back()->opt[i]->type);
for(int i=0; !isRepeated && i<homeSpan.Accessories.back()->Services.back()->Characteristics.size(); i++)
isRepeated=!strcmp(type,homeSpan.Accessories.back()->Services.back()->Characteristics[i]->type);
homeSpan.Accessories.back()->Services.back()->Characteristics.push_back(this); homeSpan.Accessories.back()->Services.back()->Characteristics.push_back(this);
} // init() } // init()

View File

@ -31,8 +31,8 @@
// Macros to define vectors of required and optional characteristics for each Span Service structure // Macros to define vectors of required and optional characteristics for each Span Service structure
#define REQ(HAPCHAR) req.push_back(&hapChars.HAPCHAR) #define REQ(HAPCHAR) req.insert(&hapChars.HAPCHAR)
#define OPT(HAPCHAR) opt.push_back(&hapChars.HAPCHAR) #define OPT(HAPCHAR) opt.insert(&hapChars.HAPCHAR)
namespace Service { namespace Service {