Added checks for use of setVal()

Throws warning, but still performs change, if setVal() is used within update() to change the value of a Characteristic that is being updated by the Home App, UNLESS the Characteristics has requested a write-response (in which case setVal() is expected).

Throw warning, and DOES NOT perform change, if setVal() is used on a Characteristic without EV permission, UNLESS it has been used in update() in response to a write-response request.

Also added diagnostic to 'i' CLI command that indicates which connections have currently asked to EV Notifications for each Characteristic.  Only applies if EV permission is set for a Characteristic.
This commit is contained in:
Gregg 2024-03-16 13:56:04 -05:00
parent 8ce80157b3
commit faa99e0fbb
2 changed files with 67 additions and 42 deletions

View File

@ -905,8 +905,21 @@ void Span::processSerialCommand(const char *c){
LOG0(", %sRange=[%s,%s]",(*chr)->customRange?"Custom-":"",(*chr)->uvPrint((*chr)->minValue).c_str(),(*chr)->uvPrint((*chr)->maxValue).c_str()); LOG0(", %sRange=[%s,%s]",(*chr)->customRange?"Custom-":"",(*chr)->uvPrint((*chr)->minValue).c_str(),(*chr)->uvPrint((*chr)->maxValue).c_str());
} }
if(((*chr)->perms)&EV){
LOG0(", EV=(");
boolean addComma=false;
for(int i=0;i<homeSpan.maxConnections;i++){
if((*chr)->ev[i] && hap[i]->client){
LOG0("%s%d",addComma?",":"",i);
addComma=true;
}
}
LOG0(")");
}
if((*chr)->nvsKey) if((*chr)->nvsKey)
LOG0(" (nvs)"); LOG0(" (nvs)");
LOG0("\n"); LOG0("\n");
if(!(*chr)->isCustom && !(*svc)->isCustom && std::find((*svc)->req.begin(),(*svc)->req.end(),(*chr)->hapChar)==(*svc)->req.end() && std::find((*svc)->opt.begin(),(*svc)->opt.end(),(*chr)->hapChar)==(*svc)->opt.end()) if(!(*chr)->isCustom && !(*svc)->isCustom && std::find((*svc)->req.begin(),(*svc)->req.end(),(*chr)->hapChar)==(*svc)->req.end() && std::find((*svc)->opt.begin(),(*svc)->opt.end(),(*chr)->hapChar)==(*svc)->opt.end())
@ -1420,10 +1433,10 @@ int Span::updateCharacteristics(char *buf, SpanBuf *pObj){
} else { } else {
pObj[i].characteristic = find(pObj[i].aid,pObj[i].iid); // find characteristic with matching aid/iid and store pointer pObj[i].characteristic = find(pObj[i].aid,pObj[i].iid); // find characteristic with matching aid/iid and store pointer
if(pObj[i].characteristic) // if found, initialize characterstic update with new val/ev if(pObj[i].characteristic) // if found, initialize characterstic update with new val/ev
pObj[i].status=pObj[i].characteristic->loadUpdate(pObj[i].val,pObj[i].ev); // save status code, which is either an error, or TBD (in which case isUpdated for the characteristic has been set to true) pObj[i].status=pObj[i].characteristic->loadUpdate(pObj[i].val,pObj[i].ev,pObj[i].wr); // save status code, which is either an error, or TBD (in which case updateFlag for the characteristic has been set to either 1 or 2)
else else
pObj[i].status=StatusCode::UnknownResource; // if not found, set HAP error pObj[i].status=StatusCode::UnknownResource; // if not found, set HAP error
} }
} // first pass } // first pass
@ -1455,7 +1468,7 @@ int Span::updateCharacteristics(char *buf, SpanBuf *pObj){
pObj[j].characteristic->uvSet(pObj[j].characteristic->newValue,pObj[j].characteristic->value); // replace characteristic new value with original value pObj[j].characteristic->uvSet(pObj[j].characteristic->newValue,pObj[j].characteristic->value); // replace characteristic new value with original value
LOG1(" (failed)\n"); LOG1(" (failed)\n");
} }
pObj[j].characteristic->isUpdated=false; // reset isUpdated flag for characteristic pObj[j].characteristic->updateFlag=0; // reset updateFlag for characteristic
} }
} }
@ -1899,7 +1912,7 @@ void SpanCharacteristic::printfAttributes(int flags){
/////////////////////////////// ///////////////////////////////
StatusCode SpanCharacteristic::loadUpdate(char *val, char *ev){ StatusCode SpanCharacteristic::loadUpdate(char *val, char *ev, boolean wr){
if(ev){ // request for notification if(ev){ // request for notification
boolean evFlag; boolean evFlag;
@ -2000,7 +2013,7 @@ StatusCode SpanCharacteristic::loadUpdate(char *val, char *ev){
} // switch } // switch
isUpdated=true; updateFlag=1+wr; // set flag to 1 if successful update or 2 if successful AND write-response flag is set
updateTime=homeSpan.snapTime; updateTime=homeSpan.snapTime;
return(StatusCode::TBD); return(StatusCode::TBD);
} }

View File

@ -500,13 +500,13 @@ class SpanCharacteristic{
boolean setValidValuesError=false; // flag to indicate attempt to set Valid Values on Characteristic that does not support changes to Valid Values boolean setValidValuesError=false; // flag to indicate attempt to set Valid Values on Characteristic that does not support changes to Valid Values
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
boolean isUpdated=false; // set to true when new value has been requested by PUT /characteristic uint8_t updateFlag=0; // set to either 1 (for normal write) or 2 (for write-response) inside update() when Characteristic is successfully updated via Home App
unsigned long updateTime=0; // last time value was updated (in millis) either by PUT /characteristic OR by setVal() unsigned long updateTime=0; // last time value was updated (in millis) either by PUT /characteristic OR by setVal()
UVal newValue; // the updated value requested by PUT /characteristic UVal newValue; // the updated value requested by PUT /characteristic
SpanService *service=NULL; // pointer to Service containing this Characteristic SpanService *service=NULL; // pointer to Service containing this Characteristic
void printfAttributes(int flags); // writes Characteristic JSON to hapOut stream void printfAttributes(int flags); // writes Characteristic JSON to hapOut stream
StatusCode loadUpdate(char *val, char *ev); // load updated val/ev from PUT /characteristic JSON request. Return intitial HAP status code (checks to see if characteristic is found, is writable, etc.) StatusCode loadUpdate(char *val, char *ev, boolean wr); // load updated val/ev from PUT /characteristic JSON request. Return intitial HAP status code (checks to see if characteristic is found, is writable, etc.)
String uvPrint(UVal &u){ String uvPrint(UVal &u){
char c[67]; // space for 64 characters + surrounding quotes + terminating null char c[67]; // space for 64 characters + surrounding quotes + terminating null
@ -669,28 +669,35 @@ class SpanCharacteristic{
return NULL; return NULL;
} }
void setString(const char *val){ void setString(const char *val, boolean notify=true){
if((perms & EV) == 0){ if(!((perms&EV) || (updateFlag==2))){
LOG0("\n*** WARNING: Attempt to update Characteristic::%s with setString() ignored. No NOTIFICATION permission on this characteristic\n\n",hapName); LOG0("\n*** WARNING: Attempt to update Characteristic::%s with setData() ignored. No EVENT NOTIFICATION (EV) permission on this characteristic\n\n",hapName);
return; return;
} }
if(updateFlag==1)
LOG0("\n*** WARNING: Attempt to update Characteristic::%s with setVal() within update() while it is being updated by Home App. This may cause device to become non-responsive!\n\n",hapName);
uvSet(value,val); uvSet(value,val);
uvSet(newValue,value); uvSet(newValue,value);
updateTime=homeSpan.snapTime; updateTime=homeSpan.snapTime;
SpanBuf sb; // create SpanBuf object if(notify){
sb.characteristic=this; // set characteristic if(updateFlag!=2){ // do not broadcast EV if update is being done in context of write-response
sb.status=StatusCode::OK; // set status SpanBuf sb; // create SpanBuf object
char dummy[]=""; sb.characteristic=this; // set characteristic
sb.val=dummy; // set dummy "val" so that printfNotify knows to consider this "update" sb.status=StatusCode::OK; // set status
homeSpan.Notifications.push_back(sb); // store SpanBuf in Notifications vector char dummy[]="";
sb.val=dummy; // set dummy "val" so that printfNotify knows to consider this "update"
homeSpan.Notifications.push_back(sb); // store SpanBuf in Notifications vector
}
if(nvsKey){ if(nvsKey){
nvs_set_str(homeSpan.charNVS,nvsKey,value.STRING); // store data nvs_set_str(homeSpan.charNVS,nvsKey,value.STRING); // store data
nvs_commit(homeSpan.charNVS); nvs_commit(homeSpan.charNVS);
}
} }
} // setString() } // setString()
@ -731,10 +738,10 @@ class SpanCharacteristic{
return(olen); return(olen);
} }
void setData(uint8_t *data, size_t len){ void setData(uint8_t *data, size_t len, boolean notify=true){
if((perms & EV) == 0){ if(!((perms&EV) || (updateFlag==2))){
LOG0("\n*** WARNING: Attempt to update Characteristic::%s with setData() ignored. No NOTIFICATION permission on this characteristic\n\n",hapName); LOG0("\n*** WARNING: Attempt to update Characteristic::%s with setData() ignored. No EVENT NOTIFICATION (EV) permission on this characteristic\n\n",hapName);
return; return;
} }
@ -744,19 +751,22 @@ class SpanCharacteristic{
} }
size_t olen; size_t olen;
mbedtls_base64_encode(NULL,0,&olen,data,len); // get length of string buffer needed (mbedtls includes the trailing null in this size) mbedtls_base64_encode(NULL,0,&olen,data,len); // get length of string buffer needed (mbedtls includes the trailing null in this size)
TempBuffer<char> tBuf(olen); // create temporary string buffer, with room for trailing null TempBuffer<char> tBuf(olen); // create temporary string buffer, with room for trailing null
mbedtls_base64_encode((uint8_t*)tBuf.get(),olen,&olen,data,len ); // encode data into string buf mbedtls_base64_encode((uint8_t*)tBuf.get(),olen,&olen,data,len ); // encode data into string buf
setString(tBuf); // call setString to continue processing as if characteristic was a string setString(tBuf,notify); // call setString to continue processing as if characteristic was a string
} }
template <typename T> void setVal(T val, boolean notify=true){ template <typename T> void setVal(T val, boolean notify=true){
if((perms & EV) == 0){ if(!((perms&EV) || (updateFlag==2))){
LOG0("\n*** WARNING: Attempt to update Characteristic::%s with setVal() ignored. No NOTIFICATION permission on this characteristic\n\n",hapName); LOG0("\n*** WARNING: Attempt to update Characteristic::%s with setData() ignored. No EVENT NOTIFICATION (EV) permission on this characteristic\n\n",hapName);
return; return;
} }
if(updateFlag==1)
LOG0("\n*** WARNING: Attempt to update Characteristic::%s with setVal() within update() while it is being updated by Home App. This may cause device to become non-responsive!\n\n",hapName);
if(!((val >= uvGet<T>(minValue)) && (val <= uvGet<T>(maxValue)))){ if(!((val >= uvGet<T>(minValue)) && (val <= uvGet<T>(maxValue)))){
LOG0("\n*** WARNING: Attempt to update Characteristic::%s with setVal(%g) is out of range [%g,%g]. This may cause device to become non-responsive!\n\n", LOG0("\n*** WARNING: Attempt to update Characteristic::%s with setVal(%g) is out of range [%g,%g]. This may cause device to become non-responsive!\n\n",
hapName,(double)val,uvGet<double>(minValue),uvGet<double>(maxValue)); hapName,(double)val,uvGet<double>(minValue),uvGet<double>(maxValue));
@ -768,12 +778,14 @@ class SpanCharacteristic{
updateTime=homeSpan.snapTime; updateTime=homeSpan.snapTime;
if(notify){ if(notify){
SpanBuf sb; // create SpanBuf object if(updateFlag!=2){ // do not broadcast EV if update is being done in context of write-response
sb.characteristic=this; // set characteristic SpanBuf sb; // create SpanBuf object
sb.status=StatusCode::OK; // set status sb.characteristic=this; // set characteristic
char dummy[]=""; sb.status=StatusCode::OK; // set status
sb.val=dummy; // set dummy "val" so that printfNotify knows to consider this "update" char dummy[]="";
homeSpan.Notifications.push_back(sb); // store SpanBuf in Notifications vector sb.val=dummy; // set dummy "val" so that printfNotify knows to consider this "update"
homeSpan.Notifications.push_back(sb); // store SpanBuf in Notifications vector
}
if(nvsKey){ if(nvsKey){
nvs_set_u64(homeSpan.charNVS,nvsKey,value.UINT64); // store data as uint64_t regardless of actual type (it will be read correctly when access through uvGet()) nvs_set_u64(homeSpan.charNVS,nvsKey,value.UINT64); // store data as uint64_t regardless of actual type (it will be read correctly when access through uvGet())
@ -783,8 +795,8 @@ class SpanCharacteristic{
} // setVal() } // setVal()
boolean updated(){return(isUpdated);} // returns isUpdated boolean updated(){return(updateFlag>0);} // returns true within update() if Characteristic was updated by Home App
unsigned long timeVal(); // returns time elapsed (in millis) since value was last updated unsigned long timeVal(); // returns time elapsed (in millis) since value was last updated, either by Home App or by using setVal()
SpanCharacteristic *setValidValues(int n, ...); // sets a list of 'n' valid values allowed for a Characteristic and returns pointer to self. Only applicable if format=INT, UINT8, UINT16, or UINT32 SpanCharacteristic *setValidValues(int n, ...); // sets a list of 'n' valid values allowed for a Characteristic and returns pointer to self. Only applicable if format=INT, UINT8, UINT16, or UINT32