From 170e0b61b168197a51dd65116465c32f906b0a98 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 27 Feb 2022 09:51:27 -0600 Subject: [PATCH] Add new macro CUSTOM_SERV() to created custom services Also, updated error checking so that the UUID for both custom Services and custom Characteristics are checked for syntax. A fatal error is thrown if an ill-formatted UUID is found, since this will definitely prevent pairing with the HomeApp. The UUID for HAP Services and Characteristics are NOT error checked, since these are fixed in HomeSpan. Also, the custom Characteristics are not validated against the optional list for a service. If the user adds a custom Characteristic to a HAP Service, it is assumed to be valid. Similarly, none of the Characteristics (HAP of Custom) in a Custom Service are validated at all. --- src/HomeSpan.cpp | 19 +++++++++++++------ src/HomeSpan.h | 26 +++++++++++++++++--------- src/Span.h | 21 +++++++++++++++------ 3 files changed, 45 insertions(+), 21 deletions(-) diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index b945744..6f523cf 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -917,12 +917,12 @@ void Span::processSerialCommand(const char *c){ Serial.print("\n\n"); char d[]="------------------------------"; - Serial.printf("%-30s %s %10s %s %s %s %s %s\n","Service","UUID","AID","IID","Update","Loop","Button","Linked Services"); - Serial.printf("%.30s %.4s %.10s %.3s %.6s %.4s %.6s %.15s\n",d,d,d,d,d,d,d,d); + Serial.printf("%-30s %8s %10s %s %s %s %s %s\n","Service","UUID","AID","IID","Update","Loop","Button","Linked Services"); + Serial.printf("%.30s %.8s %.10s %.3s %.6s %.4s %.6s %.15s\n",d,d,d,d,d,d,d,d); for(int i=0;iServices.size();j++){ SpanService *s=Accessories[i]->Services[j]; - Serial.printf("%-30s %4s %10u %3d %6s %4s %6s ",s->hapName,s->type,Accessories[i]->aid,s->iid, + Serial.printf("%-30s %8.8s %10u %3d %6s %4s %6s ",s->hapName,s->type,Accessories[i]->aid,s->iid, (void(*)())(s->*(&SpanService::update))!=(void(*)())(&SpanService::update)?"YES":"NO", (void(*)())(s->*(&SpanService::loop))!=(void(*)())(&SpanService::loop)?"YES":"NO", (void(*)(int,boolean))(s->*(&SpanService::button))!=(void(*)(int,boolean))(&SpanService::button)?"YES":"NO" @@ -1489,13 +1489,14 @@ int SpanAccessory::sprintfAttributes(char *cBuf){ // SpanService // /////////////////////////////// -SpanService::SpanService(const char *type, const char *hapName){ +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 homeSpan.Accessories.back()->Services.back()->validate(); this->type=type; this->hapName=hapName; + this->isCustom=isCustom; homeSpan.configLog+=" \u279f Service " + String(hapName); @@ -1508,7 +1509,12 @@ SpanService::SpanService(const char *type, const char *hapName){ homeSpan.Accessories.back()->Services.push_back(this); iid=++(homeSpan.Accessories.back()->iidCount); - homeSpan.configLog+=": IID=" + String(iid) + ", UUID=\"" + String(type) + "\""; + homeSpan.configLog+=": IID=" + String(iid) + ", " + (isCustom?"Custom-":"") + "UUID=\"" + String(type) + "\""; + + if(Span::invalidUUID(type,isCustom)){ + homeSpan.configLog+=" *** ERROR! Format of UUID is invalid. ***"; + homeSpan.nFatalErrors++; + } if(!strcmp(this->type,"3E") && iid!=1){ homeSpan.configLog+=" *** ERROR! The AccessoryInformation Service must be defined before any other Services in an Accessory. ***"; @@ -1600,12 +1606,13 @@ void SpanService::validate(){ // SpanCharacteristic // /////////////////////////////// -SpanCharacteristic::SpanCharacteristic(HapChar *hapChar){ +SpanCharacteristic::SpanCharacteristic(HapChar *hapChar, boolean isCustom){ type=hapChar->type; perms=hapChar->perms; hapName=hapChar->hapName; format=hapChar->format; staticRange=hapChar->staticRange; + this->isCustom=isCustom; homeSpan.configLog+=" \u21e8 Characteristic " + String(hapName); diff --git a/src/HomeSpan.h b/src/HomeSpan.h index d692064..79ca2d2 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -202,7 +202,13 @@ struct Span{ void enableOTA(boolean auth=true){otaEnabled=true;otaAuth=auth;reserveSocketConnections(1);} // enables Over-the-Air updates, with (auth=true) or without (auth=false) authorization password [[deprecated("Please use reserveSocketConnections(n) method instead.")]] - void setMaxConnections(uint8_t n){requestedMaxCon=n;} // sets maximum number of simultaneous HAP connections + void setMaxConnections(uint8_t n){requestedMaxCon=n;} // sets maximum number of simultaneous HAP connections + + static boolean invalidUUID(const char *uuid, boolean isCustom){ + int x=0; + sscanf(uuid,"%*8[0-9a-fA-F]-%*4[0-9a-fA-F]-%*4[0-9a-fA-F]-%*4[0-9a-fA-F]-%*12[0-9a-fA-F]%n",&x); + return(isCustom && (strlen(uuid)!=36 || x!=36)); + } }; @@ -233,8 +239,9 @@ struct SpanService{ vector req; // vector of pointers to all required HAP Characteristic Types for this Service vector opt; // vector of pointers to all optional HAP Characteristic Types for this Service vector linkedServices; // vector of pointers to any optional linked Services + boolean isCustom; // flag to indicate this is a Custom Service - SpanService(const char *type, const char *hapName); + SpanService(const char *type, const char *hapName, boolean isCustom=false); // constructor SpanService *setPrimary(); // sets the Service Type to be primary and returns pointer to self SpanService *setHidden(); // sets the Service Type to be hidden and returns pointer to self @@ -280,6 +287,7 @@ struct SpanCharacteristic{ const char *validValues=NULL; // Optional JSON array of valid values. Applicable only to uint8 Characteristics boolean *ev; // Characteristic Event Notify Enable (per-connection) char *nvsKey=NULL; // key for NVS storage of Characteristic value + boolean isCustom; // flag to indicate this is a Custom Characteristic uint32_t aid=0; // Accessory ID - passed through from Service containing this Characteristic boolean isUpdated=false; // set to true when new value has been requested by PUT /characteristic @@ -287,7 +295,7 @@ struct SpanCharacteristic{ UVal newValue; // the updated value requested by PUT /characteristic SpanService *service=NULL; // pointer to Service containing this Characteristic - SpanCharacteristic(HapChar *hapChar); // contructor + SpanCharacteristic(HapChar *hapChar, boolean isCustom=false); // contructor int sprintfAttributes(char *cBuf, int flags); // prints Characteristic JSON records into buf, according to flags mask; return number of characters printed, excluding null terminator StatusCode loadUpdate(char *val, char *ev); // load updated val/ev from PUT /characteristic JSON request. Return intiial HAP status code (checks to see if characteristic is found, is writable, etc.) @@ -457,11 +465,6 @@ struct SpanCharacteristic{ uvSet(stepValue,0); } - int x=0; - sscanf(type,"%*8[0-9a-fA-F]-%*4[0-9a-fA-F]-%*4[0-9a-fA-F]-%*4[0-9a-fA-F]-%*12[0-9a-fA-F]%n",&x); - - boolean isCustom=(strlen(type)==36 && x==36); - homeSpan.configLog+="(" + uvPrint(value) + ")" + ": IID=" + String(iid) + ", " + (isCustom?"Custom-":"") + "UUID=\"" + String(type) + "\""; if(format!=FORMAT::STRING && format!=FORMAT::BOOL) homeSpan.configLog+= ", Range=[" + String(uvPrint(minValue)) + "," + String(uvPrint(maxValue)) + "]"; @@ -471,7 +474,12 @@ struct SpanCharacteristic{ else if(nvsFlag==1) homeSpan.configLog+=" (storing)"; - boolean valid=isCustom; + if(Span::invalidUUID(type,isCustom)){ + homeSpan.configLog+=" *** ERROR! Format of UUID is invalid. ***"; + homeSpan.nFatalErrors++; + } + + boolean valid=isCustom|service->isCustom; // automatically set valid if either Characteristic or containing Service is Custom for(int i=0; !valid && iServices.back()->req.size(); i++) valid=!strcmp(type,homeSpan.Accessories.back()->Services.back()->req[i]->type); diff --git a/src/Span.h b/src/Span.h index 03fd31a..c0155f7 100644 --- a/src/Span.h +++ b/src/Span.h @@ -43,7 +43,7 @@ namespace Service { REQ(Model); REQ(Name); REQ(SerialNumber); - OPT(HardwareRevision); + OPT(HardwareRevision); }}; struct AirPurifier : SpanService { AirPurifier() : SpanService{"BB","AirPurifier"}{ @@ -521,14 +521,23 @@ namespace Characteristic { } -////////////////////////////////////////// -// MACRO TO ADD CUSTOM CHARACTERISTICS // -////////////////////////////////////////// +//////////////////////////////////////////////////////// +// MACROS TO ADD CUSTOM SERVICES AND CHARACTERISTICS // +//////////////////////////////////////////////////////// #define CUSTOM_CHAR(NAME,UUID,PERMISISONS,FORMAT,DEFVAL,MINVAL,MAXVAL,STATIC_RANGE) \ HapChar _CUSTOM_##NAME {#UUID,#NAME,(PERMS)(PERMISISONS),FORMAT,STATIC_RANGE}; \ - namespace Characteristic { struct NAME : SpanCharacteristic { NAME(FORMAT##_t val=DEFVAL, boolean nvsStore=false) : SpanCharacteristic {&_CUSTOM_##NAME} { init(val,nvsStore,(FORMAT##_t)MINVAL,(FORMAT##_t)MAXVAL); } }; } + namespace Characteristic { struct NAME : SpanCharacteristic { NAME(FORMAT##_t val=DEFVAL, boolean nvsStore=false) : SpanCharacteristic {&_CUSTOM_##NAME,true} { init(val,nvsStore,(FORMAT##_t)MINVAL,(FORMAT##_t)MAXVAL); } }; } #define CUSTOM_CHAR_STRING(NAME,UUID,PERMISISONS,DEFVAL) \ HapChar _CUSTOM_##NAME {#UUID,#NAME,(PERMS)(PERMISISONS),STRING,true}; \ - namespace Characteristic { struct NAME : SpanCharacteristic { NAME(const char * val=DEFVAL, boolean nvsStore=false) : SpanCharacteristic {&_CUSTOM_##NAME} { init(val,nvsStore); } }; } + namespace Characteristic { struct NAME : SpanCharacteristic { NAME(const char * val=DEFVAL, boolean nvsStore=false) : SpanCharacteristic {&_CUSTOM_##NAME,true} { init(val,nvsStore); } }; } + +#define CUSTOM_SERV(NAME,UUID) \ + namespace Service { struct NAME : SpanService { NAME() : SpanService{#UUID,#NAME,true}{} }; } + + + + + +