Extended Exmaple 16 to use two Stateless Programmable Switches

Added ServiceLabelIndex Characteristic, which seems to be required as noted in HAP specifications.  However, linking to a ServiceLabel Service does not appear to be required, so there is no need to build any Service-Linking logic into homeSpan - will wait for use-case to be identified.

To do:  Add support for double-click to SpanButton, then include in Example 16.
This commit is contained in:
Gregg 2020-10-31 15:53:06 -05:00
parent 3979498b3c
commit f0f761c143
4 changed files with 38 additions and 121 deletions

View File

@ -16,7 +16,6 @@
void setup() {
Serial.begin(115200);
homeSpan.begin(Category::Bridges,"HomeSpan Bridge");
@ -27,9 +26,10 @@ void setup() {
new Characteristic::Version("1.1.0");
new SpanAccessory();
new DEV_Identify("PushButton Switch","HomeSpan","123-ABC","20mA LED","0.9",0);
new DEV_Identify("PushButton Switches","HomeSpan","123-ABC","Prog Switches","0.9",0);
new DEV_ProgButton(23);
new DEV_ProgButton(23,2);
new DEV_ProgButton(5,7);
} // end of setup()

View File

@ -8,15 +8,18 @@ struct DEV_ProgButton : Service::StatelessProgrammableSwitch { // Stateles
int buttonPin; // pin with programmable pushbutton
SpanCharacteristic *switchEvent; // reference to the ProgrammableSwitchEvent Characteristic
DEV_ProgButton(int buttonPin) : Service::StatelessProgrammableSwitch(){
DEV_ProgButton(int buttonPin, int index) : Service::StatelessProgrammableSwitch(){
switchEvent=new Characteristic::ProgrammableSwitchEvent(); // ProgrammableSwitchEvent Characteristic
new Characteristic::ServiceLabelIndex(index); // set service label index
new SpanButton(buttonPin); // create new SpanButton
this->buttonPin=buttonPin; // save button pin number
Serial.print("Configuring Programmable Pushbuton: Pin="); // initialization message
Serial.print("Configuring Programmable Pushbutton: Pin="); // initialization message
Serial.print(buttonPin);
Serial.print(" Index=");
Serial.print(index);
Serial.print("\n");
} // end constructor

View File

@ -80,10 +80,7 @@ void Span::poll() {
homeSpan.Accessories.back()->validate();
}
Serial.print(configLog);
Serial.print("\nConfigured as Bridge: ");
Serial.print(homeSpan.isBridge?"YES":"NO");
Serial.print("\n\n*** End Config Log ***\n");
processSerialCommand("i"); // print homeSpan configuration info
if(nFatalErrors>0){
Serial.print("\n*** PROGRAM HALTED DUE TO ");
@ -617,8 +614,14 @@ void Span::processSerialCommand(char *c){
case 'i':{
char d[]="------------------------------";
Serial.print("\n*** HomeSpan Info ***\n\n");
Serial.print(configLog);
Serial.print("\nConfigured as Bridge: ");
Serial.print(homeSpan.isBridge?"YES":"NO");
Serial.print("\n\n");
char d[]="------------------------------";
char cBuf[256];
sprintf(cBuf,"%-30s %s %s %s %s %s %s\n","Service","Type","AID","IID","Update","Loop","Button");
Serial.print(cBuf);
@ -655,7 +658,7 @@ void Span::processSerialCommand(char *c){
Serial.print("\n");
Serial.print(" R - restart device\n");
Serial.print(" F - factory reset and restart\n");
Serial.print(" E - delete all stored data and restart\n");
Serial.print(" E - erase ALL stored data and restart\n");
Serial.print("\n");
Serial.print(" ? - print this list of commands\n");
Serial.print("\n");
@ -1026,7 +1029,7 @@ SpanAccessory::SpanAccessory(){
homeSpan.Accessories.push_back(this);
aid=homeSpan.Accessories.size();
homeSpan.configLog+="+Accessory " + String(aid) + "\n";
homeSpan.configLog+="+Accessory-" + String(aid) + "\n";
}
///////////////////////////////
@ -1082,7 +1085,7 @@ int SpanAccessory::sprintfAttributes(char *cBuf){
SpanService::SpanService(const char *type, const char *hapName){
if(!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.empty()) // this is not the first Service to be defined for this Accessory
homeSpan.Accessories.back()->Services.back()->validate();
this->type=type;
@ -1096,9 +1099,10 @@ SpanService::SpanService(const char *type, const char *hapName){
return;
}
homeSpan.configLog+="\n";
homeSpan.Accessories.back()->Services.push_back(this);
iid=++(homeSpan.Accessories.back()->iidCount);
homeSpan.configLog+=+"-" + String(iid) + String(" (") + String(type) + String(")\n");
}
///////////////////////////////
@ -1175,6 +1179,14 @@ SpanCharacteristic::SpanCharacteristic(char *type, uint8_t perms, char *hapName)
return;
}
iid=++(homeSpan.Accessories.back()->iidCount);
service=homeSpan.Accessories.back()->Services.back();
aid=homeSpan.Accessories.back()->aid;
ev=(boolean *)calloc(homeSpan.maxConnections,sizeof(boolean));
homeSpan.configLog+="-" + String(iid) + String(" (") + String(type) + String(") ");
boolean valid=false;
for(int i=0; !valid && i<homeSpan.Accessories.back()->Services.back()->req.size(); i++)
@ -1188,23 +1200,20 @@ SpanCharacteristic::SpanCharacteristic(char *type, uint8_t perms, char *hapName)
homeSpan.nFatalErrors++;
}
valid=true;
boolean repeated=false;
for(int i=0; valid && i<homeSpan.Accessories.back()->Services.back()->Characteristics.size(); i++)
valid=strcmp(type,homeSpan.Accessories.back()->Services.back()->Characteristics[i]->type);
for(int i=0; !repeated && i<homeSpan.Accessories.back()->Services.back()->Characteristics.size(); i++)
repeated=!strcmp(type,homeSpan.Accessories.back()->Services.back()->Characteristics[i]->type);
if(!valid){
if(valid && repeated){
homeSpan.configLog+=" *** ERROR! Characteristic already defined for this Service. ***";
homeSpan.nFatalErrors++;
}
homeSpan.configLog+="\n";
homeSpan.Accessories.back()->Services.back()->Characteristics.push_back(this);
iid=++(homeSpan.Accessories.back()->iidCount);
service=homeSpan.Accessories.back()->Services.back();
aid=homeSpan.Accessories.back()->aid;
ev=(boolean *)calloc(homeSpan.maxConnections,sizeof(boolean));
homeSpan.configLog+="\n";
}
///////////////////////////////
@ -1327,101 +1336,6 @@ int SpanCharacteristic::sprintfAttributes(char *cBuf, int flags){
nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?128:0,",\"minValue\":%d,\"maxValue\":%d,\"minStep\":%d",range->min,range->max,range->step);
}
/*
switch(format){
case BOOL:
if(perms&PR){
if(!(perms&NV))
nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?64:0,",\"value\":%s",value.BOOL?"true":"false");
else
nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?64:0,",\"value\":null");
}
if(flags&GET_META)
nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?64:0,",\"format\":\"bool\"");
break;
case INT:
if(perms&PR){
if(!(perms&NV))
nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?64:0,",\"value\":%d",value.INT);
else
nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?64:0,",\"value\":null");
}
if(flags&GET_META)
nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?64:0,",\"format\":\"int\"");
break;
case UINT8:
if(perms&PR){
if(!(perms&NV))
nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?64:0,",\"value\":%u",value.UINT8);
else
nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?64:0,",\"value\":null");
}
if(flags&GET_META)
nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?64:0,",\"format\":\"uint8\"");
break;
case UINT16:
if(perms&PR){
if(!(perms&NV))
nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?64:0,",\"value\":%u",value.UINT16);
else
nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?64:0,",\"value\":null");
}
if(flags&GET_META)
nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?64:0,",\"format\":\"uint16\"");
break;
case UINT32:
if(perms&PR){
if(!(perms&NV))
nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?64:0,",\"value\":%lu",value.UINT32);
else
nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?64:0,",\"value\":null");
}
if(flags&GET_META)
nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?64:0,",\"format\":\"uint32\"");
break;
case UINT64:
if(perms&PR){
if(!(perms&NV))
nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?64:0,",\"value\":%llu",value.UINT64);
else
nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?64:0,",\"value\":null");
}
if(flags&GET_META)
nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?64:0,",\"format\":\"uint64\"");
break;
case FLOAT:
if(perms&PR){
if(!(perms&NV))
nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?64:0,",\"value\":%lg",value.FLOAT);
else
nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?64:0,",\"value\":null");
}
if(flags&GET_META)
nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?64:0,",\"format\":\"float\"");
break;
case STRING:
if(perms&PR){
if(!(perms&NV))
nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?64:0,",\"value\":\"%s\"",value.STRING);
else
nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?64:0,",\"value\":null");
}
if(flags&GET_META)
nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?64:0,",\"format\":\"string\"");
break;
} // switch
*/
if(desc && (flags&GET_DESC)){
nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?128:0,",\"description\":\"%s\"",desc);
}

View File

@ -56,7 +56,7 @@ struct Span{
unsigned long snapTime; // current time (in millis) snapped before entering Service loops() or updates()
boolean isInitialized=false; // flag indicating HomeSpan has been initialized
int nFatalErrors=0; // number of fatal errors in user-defined configuration
String configLog="\n*** Config Log ***\n\n"; // log of configuration process, including any errors
String configLog; // log of configuration process, including any errors
boolean isBridge=true; // flag indicating whether device is configured as a bridge (i.e. first Accessory contains nothing but AccessoryInformation and HAPProtocolInformation)
char *defaultSetupCode=DEFAULT_SETUP_CODE; // Setup Code used for pairing