Created Example 16 - Programmable Pushbutton
Modified SpanCharacteristic::sprintfAttributes to streamline logic and add special handling for the ProgrammableSwitchEvent Characteristic as required by HAP: the value returned for the database or any read request must be set to null (i.e. "value":null). The only time the real value should be returned is when the device sends a EV/Notify message (when the button is pressed). Verified that the example works as expected! TO DO: Add functionality to allow for Service Namespace and Index label so that you can have multiple programmable pushbuttons in one service - this requires logic for HAP LINKED SERVICE functionality.
This commit is contained in:
parent
2dd30e0a51
commit
3979498b3c
|
|
@ -0,0 +1,42 @@
|
|||
|
||||
////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// HomeSpan: A HomeKit implementation for the ESP32 //
|
||||
// ------------------------------------------------ //
|
||||
// //
|
||||
// Example 16: Stateless Programmable Switches //
|
||||
// * using linked-services //
|
||||
// //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
#include "HomeSpan.h"
|
||||
#include "DEV_ProgButton.h"
|
||||
#include "DEV_Identify.h"
|
||||
|
||||
void setup() {
|
||||
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.begin(Category::Bridges,"HomeSpan Bridge");
|
||||
|
||||
new SpanAccessory();
|
||||
new DEV_Identify("Bridge #1","HomeSpan","123-ABC","HS Bridge","0.9",3);
|
||||
new Service::HAPProtocolInformation();
|
||||
new Characteristic::Version("1.1.0");
|
||||
|
||||
new SpanAccessory();
|
||||
new DEV_Identify("PushButton Switch","HomeSpan","123-ABC","20mA LED","0.9",0);
|
||||
|
||||
new DEV_ProgButton(23);
|
||||
|
||||
} // end of setup()
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
homeSpan.poll();
|
||||
|
||||
} // end of loop()
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
|
||||
//////////////////////////////////
|
||||
// DEVICE-SPECIFIC SERVICES //
|
||||
//////////////////////////////////
|
||||
|
||||
struct DEV_Identify : Service::AccessoryInformation {
|
||||
|
||||
int nBlinks; // number of times to blink built-in LED in identify routine
|
||||
SpanCharacteristic *identify; // reference to the Identify Characteristic
|
||||
|
||||
DEV_Identify(char *name, char *manu, char *sn, char *model, char *version, int nBlinks) : Service::AccessoryInformation(){
|
||||
|
||||
new Characteristic::Name(name); // create all the required Characteristics with values set based on above arguments
|
||||
new Characteristic::Manufacturer(manu);
|
||||
new Characteristic::SerialNumber(sn);
|
||||
new Characteristic::Model(model);
|
||||
new Characteristic::FirmwareRevision(version);
|
||||
identify=new Characteristic::Identify(); // store a reference to the Identify Characteristic for use below
|
||||
|
||||
this->nBlinks=nBlinks; // store the number of times to blink the built-in LED
|
||||
|
||||
pinMode(LED_BUILTIN,OUTPUT); // make sure built-in LED is set for output
|
||||
}
|
||||
|
||||
boolean update(){
|
||||
|
||||
for(int i=0;i<nBlinks;i++){
|
||||
digitalWrite(LED_BUILTIN,LOW);
|
||||
delay(250);
|
||||
digitalWrite(LED_BUILTIN,HIGH);
|
||||
delay(250);
|
||||
}
|
||||
|
||||
return(true); // return true
|
||||
|
||||
} // update
|
||||
|
||||
};
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
|
||||
////////////////////////////////////////////////////
|
||||
// DEVICE-SPECIFIC PROGRAMMABLE SWITCH SERVICES //
|
||||
////////////////////////////////////////////////////
|
||||
|
||||
struct DEV_ProgButton : Service::StatelessProgrammableSwitch { // Stateless Programmable Switch
|
||||
|
||||
int buttonPin; // pin with programmable pushbutton
|
||||
SpanCharacteristic *switchEvent; // reference to the ProgrammableSwitchEvent Characteristic
|
||||
|
||||
DEV_ProgButton(int buttonPin) : Service::StatelessProgrammableSwitch(){
|
||||
|
||||
switchEvent=new Characteristic::ProgrammableSwitchEvent(); // ProgrammableSwitchEvent Characteristic
|
||||
|
||||
new SpanButton(buttonPin); // create new SpanButton
|
||||
this->buttonPin=buttonPin; // save button pin number
|
||||
|
||||
Serial.print("Configuring Programmable Pushbuton: Pin="); // initialization message
|
||||
Serial.print(buttonPin);
|
||||
Serial.print("\n");
|
||||
|
||||
} // end constructor
|
||||
|
||||
void button(int pin, boolean isLong) override {
|
||||
|
||||
LOG1("Found button press on pin: "); // always a good idea to log messages
|
||||
LOG1(pin);
|
||||
LOG1(" type: ");
|
||||
LOG1(isLong?"LONG":"SHORT");
|
||||
LOG1("\n");
|
||||
|
||||
switchEvent->setVal(isLong?2:0); // set the value of the switchEvent Characteristic to 2 for long press or 0 for short press
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
//////////////////////////////////
|
||||
|
|
@ -1251,7 +1251,7 @@ void HAPClient::checkTimedWrites(){
|
|||
void HAPClient::eventNotify(SpanBuf *pObj, int nObj, int ignoreClient){
|
||||
|
||||
for(int cNum=0;cNum<homeSpan.maxConnections;cNum++){ // loop over all connection slots
|
||||
if(hap[cNum]->client && cNum!=ignoreClient){ // if there is a client connected to this slot and it is NOT flagged to be ignored (in cases where it is the client making a PUT request
|
||||
if(hap[cNum]->client && cNum!=ignoreClient){ // if there is a client connected to this slot and it is NOT flagged to be ignored (in cases where it is the client making a PUT request)
|
||||
|
||||
int nBytes=homeSpan.sprintfNotify(pObj,nObj,NULL,cNum); // get JSON response for notifications to client cNum - includes terminating null (will be recast to uint8_t* below)
|
||||
|
||||
|
|
|
|||
|
|
@ -80,7 +80,8 @@ struct HapCharType {
|
|||
AA=8,
|
||||
TW=16,
|
||||
HD=32,
|
||||
WR=64
|
||||
WR=64,
|
||||
NV=128
|
||||
};
|
||||
|
||||
struct HapCharList {
|
||||
|
|
@ -142,7 +143,7 @@ struct HapCharList {
|
|||
HAPCHAR( PM10Density, C7, PR+EV );
|
||||
HAPCHAR( PositionState, 72, PR+EV );
|
||||
HAPCHAR( ProgramMode, D1, PR+EV );
|
||||
HAPCHAR( ProgrammableSwitchEvent, 73, PR+EV ); // this characteristic requires specical handling
|
||||
HAPCHAR( ProgrammableSwitchEvent, 73, PR+EV+NV ); // NV = flag to indicate that HomeSpan should always return a null value, as required by HAP for this Characteristic
|
||||
HAPCHAR( RelativeHumidityDehumidifierThreshold, C9, PR+PW+EV );
|
||||
HAPCHAR( RelativeHumidityHumidifierThreshold, CA, PR+PW+EV );
|
||||
HAPCHAR( RemainingDuration, D4, PR+EV );
|
||||
|
|
|
|||
109
src/HomeSpan.cpp
109
src/HomeSpan.cpp
|
|
@ -923,7 +923,7 @@ int Span::sprintfNotify(SpanBuf *pObj, int nObj, char *cBuf, int conNum){
|
|||
if(notifyFlag) // already printed at least one other characteristic
|
||||
nChars+=snprintf(cBuf?(cBuf+nChars):NULL,cBuf?64:0,","); // add preceeding comma before printing next characteristic
|
||||
|
||||
nChars+=pObj[i].characteristic->sprintfAttributes(cBuf?(cBuf+nChars):NULL,GET_AID); // get JSON attributes for characteristic
|
||||
nChars+=pObj[i].characteristic->sprintfAttributes(cBuf?(cBuf+nChars):NULL,GET_AID+GET_NV); // get JSON attributes for characteristic
|
||||
notifyFlag=true;
|
||||
|
||||
} // notification requested
|
||||
|
|
@ -1270,74 +1270,157 @@ int SpanCharacteristic::sprintfAttributes(char *cBuf, int flags){
|
|||
|
||||
const char permCodes[][7]={"pr","pw","ev","aa","tw","hd","wr"};
|
||||
|
||||
const char formatCodes[][8]={"bool","uint8","uint16","uint32","uint64","int","float","string"};
|
||||
|
||||
nBytes+=snprintf(cBuf,cBuf?64:0,"{\"iid\":%d",iid);
|
||||
|
||||
if(flags&GET_TYPE)
|
||||
nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?64:0,",\"type\":\"%s\"",type);
|
||||
|
||||
if(perms&PR){
|
||||
|
||||
if(perms&NV && !(flags&GET_NV)){
|
||||
nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?64:0,",\"value\":null");
|
||||
} else {
|
||||
|
||||
switch(format){
|
||||
case BOOL:
|
||||
nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?64:0,",\"value\":%s",value.BOOL?"true":"false");
|
||||
break;
|
||||
|
||||
case INT:
|
||||
nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?64:0,",\"value\":%d",value.INT);
|
||||
break;
|
||||
|
||||
case UINT8:
|
||||
nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?64:0,",\"value\":%u",value.UINT8);
|
||||
break;
|
||||
|
||||
case UINT16:
|
||||
nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?64:0,",\"value\":%u",value.UINT16);
|
||||
break;
|
||||
|
||||
case UINT32:
|
||||
nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?64:0,",\"value\":%lu",value.UINT32);
|
||||
break;
|
||||
|
||||
case UINT64:
|
||||
nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?64:0,",\"value\":%llu",value.UINT64);
|
||||
break;
|
||||
|
||||
case FLOAT:
|
||||
nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?64:0,",\"value\":%lg",value.FLOAT);
|
||||
break;
|
||||
|
||||
case STRING:
|
||||
nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?64:0,",\"value\":\"%s\"",value.STRING);
|
||||
break;
|
||||
|
||||
} // switch
|
||||
} // print Characteristic value
|
||||
} // permissions=PR
|
||||
|
||||
if(flags&GET_META){
|
||||
nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?64:0,",\"format\":\"%s\"",formatCodes[format]);
|
||||
|
||||
if(range && (flags&GET_META))
|
||||
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&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&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&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&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&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&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&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&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(range && (flags&GET_META)){
|
||||
nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?128:0,",\"minValue\":%d,\"maxValue\":%d,\"minStep\":%d",range->min,range->max,range->step);
|
||||
}
|
||||
*/
|
||||
|
||||
if(desc && (flags&GET_DESC)){
|
||||
nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?128:0,",\"description\":\"%s\"",desc);
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ enum {
|
|||
GET_TYPE=8,
|
||||
GET_EV=16,
|
||||
GET_DESC=32,
|
||||
GET_NV=64,
|
||||
GET_ALL=255
|
||||
};
|
||||
|
||||
|
|
@ -162,18 +163,19 @@ struct SpanCharacteristic{
|
|||
AA=8,
|
||||
TW=16,
|
||||
HD=32,
|
||||
WR=64
|
||||
WR=64,
|
||||
NV=128
|
||||
};
|
||||
|
||||
enum FORMAT { // HAP Table 6-5
|
||||
BOOL,
|
||||
UINT8,
|
||||
UINT16,
|
||||
UINT32,
|
||||
UINT64,
|
||||
INT,
|
||||
FLOAT,
|
||||
STRING
|
||||
BOOL=0,
|
||||
UINT8=1,
|
||||
UINT16=2,
|
||||
UINT32=3,
|
||||
UINT64=4,
|
||||
INT=5,
|
||||
FLOAT=6,
|
||||
STRING=7
|
||||
};
|
||||
|
||||
union UVal {
|
||||
|
|
|
|||
Loading…
Reference in New Issue