Added homeSpan.setControllerCallback()

Also adds: homeSpan.controllerListBegin(), homeSpan.controllerListEnd()
Also adds: Controller::isAdmin(), Controller::getID(), Controller::getLTPK()

To accomplish this, needed to move Controller definition from HAP.h to HomeSpan.h

Also, converted Controller from struct to class to ensure Controller data is protected from being modified now that it is exposed through condstant iterators.
This commit is contained in:
Gregg 2024-04-28 08:15:04 -05:00
parent ee2e850505
commit 74d27485d5
5 changed files with 84 additions and 246 deletions

View File

@ -1345,7 +1345,7 @@ int HAPClient::receiveEncrypted(uint8_t *httpBuf, int messageSize){
///////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////
void HAPClient::hexPrintColumn(uint8_t *buf, int n, int minLogLevel){ void HAPClient::hexPrintColumn(const uint8_t *buf, int n, int minLogLevel){
if(homeSpan.logLevel<minLogLevel) if(homeSpan.logLevel<minLogLevel)
return; return;
@ -1356,7 +1356,7 @@ void HAPClient::hexPrintColumn(uint8_t *buf, int n, int minLogLevel){
////////////////////////////////////// //////////////////////////////////////
void HAPClient::hexPrintRow(uint8_t *buf, int n, int minLogLevel){ void HAPClient::hexPrintRow(const uint8_t *buf, int n, int minLogLevel){
if(homeSpan.logLevel<minLogLevel) if(homeSpan.logLevel<minLogLevel)
return; return;
@ -1367,7 +1367,7 @@ void HAPClient::hexPrintRow(uint8_t *buf, int n, int minLogLevel){
////////////////////////////////////// //////////////////////////////////////
void HAPClient::charPrintRow(uint8_t *buf, int n, int minLogLevel){ void HAPClient::charPrintRow(const uint8_t *buf, int n, int minLogLevel){
if(homeSpan.logLevel<minLogLevel) if(homeSpan.logLevel<minLogLevel)
return; return;
@ -1503,6 +1503,9 @@ void HAPClient::printControllers(int minLogLevel){
void HAPClient::saveControllers(){ void HAPClient::saveControllers(){
if(homeSpan.controllerCallback)
homeSpan.controllerCallback();
if(controllerList.empty()){ if(controllerList.empty()){
nvs_erase_key(homeSpan.hapNVS,"CONTROLLERS"); nvs_erase_key(homeSpan.hapNVS,"CONTROLLERS");
return; return;

View File

@ -63,26 +63,6 @@ struct Nonce {
void inc(); void inc();
}; };
/////////////////////////////////////////////////
// Paired Controller Structure for Permanently-Stored Data
struct Controller {
boolean allocated=false; // DEPRECATED (but needed for backwards compatability with original NVS storage of Controller info)
boolean admin; // Controller has admin privileges
uint8_t ID[hap_controller_IDBYTES]; // Pairing ID
uint8_t LTPK[crypto_sign_PUBLICKEYBYTES]; // Long Term Ed2519 Public Key
Controller(){}
Controller(uint8_t *id, uint8_t *ltpk, boolean ad){
allocated=true;
admin=ad;
memcpy(ID,id,hap_controller_IDBYTES);
memcpy(LTPK,ltpk,crypto_sign_PUBLICKEYBYTES);
}
};
///////////////////////////////////////////////// /////////////////////////////////////////////////
// Accessory Structure for Permanently-Stored Data // Accessory Structure for Permanently-Stored Data
@ -150,9 +130,9 @@ struct HAPClient {
static void init(); // initialize HAP after start-up static void init(); // initialize HAP after start-up
static void hexPrintColumn(uint8_t *buf, int n, int minLogLevel=0); // prints 'n' bytes of *buf as HEX, one byte per row, subject to specified minimum log level static void hexPrintColumn(const uint8_t *buf, int n, int minLogLevel=0); // prints 'n' bytes of *buf as HEX, one byte per row, subject to specified minimum log level
static void hexPrintRow(uint8_t *buf, int n, int minLogLevel=0); // prints 'n' bytes of *buf as HEX, all on one row, subject to specified minimum log level static void hexPrintRow(const uint8_t *buf, int n, int minLogLevel=0); // prints 'n' bytes of *buf as HEX, all on one row, subject to specified minimum log level
static void charPrintRow(uint8_t *buf, int n, int minLogLevel=0); // prints 'n' bytes of *buf as CHAR, all on one row, subject to specified minimum log level static void charPrintRow(const uint8_t *buf, int n, int minLogLevel=0); // prints 'n' bytes of *buf as CHAR, all on one row, subject to specified minimum log level
static Controller *findController(uint8_t *id); // returns pointer to controller with matching ID (or NULL if no match) static Controller *findController(uint8_t *id); // returns pointer to controller with matching ID (or NULL if no match)
static tagError addController(uint8_t *id, uint8_t *ltpk, boolean admin); // stores data for new Controller with specified data. Returns tagError (if any) static tagError addController(uint8_t *id, uint8_t *ltpk, boolean admin); // stores data for new Controller with specified data. Returns tagError (if any)

View File

@ -611,8 +611,8 @@ void Span::processSerialCommand(const char *c){
if(hap[i]->cPair){ if(hap[i]->cPair){
LOG0(" ID="); LOG0(" ID=");
HAPClient::charPrintRow(hap[i]->cPair->ID,36); HAPClient::charPrintRow(hap[i]->cPair->getID(),36);
LOG0(hap[i]->cPair->admin?" (admin)":" (regular)"); LOG0(hap[i]->cPair->isAdmin()?" (admin)":" (regular)");
} else { } else {
LOG0(" (unverified)"); LOG0(" (unverified)");
} }
@ -1092,7 +1092,7 @@ void Span::processSerialCommand(const char *c){
reboot(); reboot();
} else { } else {
HAPClient::controllerList.push_back(tCont); HAPClient::controllerList.push_back(tCont);
HAPClient::charPrintRow(tCont.ID,36); HAPClient::charPrintRow(tCont.getID(),36);
LOG0("\n"); LOG0("\n");
} }
} }
@ -1658,6 +1658,18 @@ boolean Span::updateDatabase(boolean updateMDNS){
return(changed); return(changed);
} }
///////////////////////////////
list<Controller, Mallocator<Controller>>::const_iterator Span::controllerListBegin(){
return(HAPClient::controllerList.cbegin());
}
///////////////////////////////
list<Controller, Mallocator<Controller>>::const_iterator Span::controllerListEnd(){
return(HAPClient::controllerList.cend());
}
/////////////////////////////// ///////////////////////////////
// SpanAccessory // // SpanAccessory //
/////////////////////////////// ///////////////////////////////

View File

@ -113,6 +113,7 @@ struct SpanRange;
struct SpanBuf; struct SpanBuf;
struct SpanButton; struct SpanButton;
struct SpanUserCommand; struct SpanUserCommand;
class Controller;
extern Span homeSpan; extern Span homeSpan;
@ -189,6 +190,35 @@ struct SpanOTA{ // manages OTA process
static void error(ota_error_t err); static void error(ota_error_t err);
}; };
//////////////////////////////////////////////////////////
// Paired Controller Structure for Permanently-Stored Data
class Controller {
friend class HAPClient;
boolean allocated=false; // DEPRECATED (but needed for backwards compatability with original NVS storage of Controller info)
boolean admin; // Controller has admin privileges
uint8_t ID[36]; // Pairing ID
uint8_t LTPK[32]; // Long Term Ed2519 Public Key
public:
Controller(uint8_t *id, uint8_t *ltpk, boolean ad){
allocated=true;
admin=ad;
memcpy(ID,id,36);
memcpy(LTPK,ltpk,32);
}
Controller(){}
const uint8_t *getID() const {return(ID);}
const uint8_t *getLTPK() const {return(LTPK);}
boolean isAdmin() const {return(admin);}
};
////////////////////////////////////// //////////////////////////////////////
// USER API CLASSES BEGINS HERE // // USER API CLASSES BEGINS HERE //
////////////////////////////////////// //////////////////////////////////////
@ -250,6 +280,7 @@ class Span{
void (*apFunction)()=NULL; // optional function to invoke when starting Access Point void (*apFunction)()=NULL; // optional function to invoke when starting Access Point
void (*statusCallback)(HS_STATUS status)=NULL; // optional callback when HomeSpan status changes void (*statusCallback)(HS_STATUS status)=NULL; // optional callback when HomeSpan status changes
void (*rebootCallback)(uint8_t)=NULL; // optional callback when device reboots void (*rebootCallback)(uint8_t)=NULL; // optional callback when device reboots
void (*controllerCallback)()=NULL; // optional callback when Controller is added/removed/changed
WiFiServer *hapServer; // pointer to the HAP Server connection WiFiServer *hapServer; // pointer to the HAP Server connection
Blinker *statusLED; // indicates HomeSpan status Blinker *statusLED; // indicates HomeSpan status
@ -355,6 +386,7 @@ class Span{
Span& setPairingCode(const char *s, boolean progCall=true); // sets the Pairing Code - use is NOT recommended. Use 'S' from CLI instead Span& setPairingCode(const char *s, boolean progCall=true); // sets the Pairing Code - use is NOT recommended. Use 'S' from CLI instead
void deleteStoredValues(){processSerialCommand("V");} // deletes stored Characteristic values from NVS void deleteStoredValues(){processSerialCommand("V");} // deletes stored Characteristic values from NVS
Span& resetIID(uint32_t newIID); // resets the IID count for the current Accessory to start at newIID Span& resetIID(uint32_t newIID); // resets the IID count for the current Accessory to start at newIID
Span& setControllerCallback(void (*f)()){controllerCallback=f;return(*this);} // sets an optional user-defined function to call whenever a Controller is added/removed/changed
int enableOTA(boolean auth=true, boolean safeLoad=true){return(spanOTA.init(auth, safeLoad, NULL));} // enables Over-the-Air updates, with (auth=true) or without (auth=false) authorization password int enableOTA(boolean auth=true, boolean safeLoad=true){return(spanOTA.init(auth, safeLoad, NULL));} // enables Over-the-Air updates, with (auth=true) or without (auth=false) authorization password
int enableOTA(const char *pwd, boolean safeLoad=true){return(spanOTA.init(true, safeLoad, pwd));} // enables Over-the-Air updates, with custom authorization password (overrides any password stored with the 'O' command) int enableOTA(const char *pwd, boolean safeLoad=true){return(spanOTA.init(true, safeLoad, pwd));} // enables Over-the-Air updates, with custom authorization password (overrides any password stored with the 'O' command)
@ -393,6 +425,9 @@ class Span{
TaskHandle_t getAutoPollTask(){return(pollTaskHandle);} TaskHandle_t getAutoPollTask(){return(pollTaskHandle);}
Span& setTimeServerTimeout(uint32_t tSec){webLog.waitTime=tSec*1000;return(*this);} // sets wait time (in seconds) for optional web log time server to connect Span& setTimeServerTimeout(uint32_t tSec){webLog.waitTime=tSec*1000;return(*this);} // sets wait time (in seconds) for optional web log time server to connect
list<Controller, Mallocator<Controller>>::const_iterator controllerListBegin();
list<Controller, Mallocator<Controller>>::const_iterator controllerListEnd();
[[deprecated("Please use reserveSocketConnections(n) method instead.")]] [[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

View File

@ -27,227 +27,21 @@
#include "HomeSpan.h" #include "HomeSpan.h"
CUSTOM_CHAR_DATA(TestData,333,PR+EV);
struct HomeSpanTV : Service::Television {
SpanCharacteristic *active = new Characteristic::Active(0); // TV On/Off (set to Off at start-up)
SpanCharacteristic *activeID = new Characteristic::ActiveIdentifier(3); // Sets HDMI 3 on start-up
SpanCharacteristic *remoteKey = new Characteristic::RemoteKey(); // Used to receive button presses from the Remote Control widget
SpanCharacteristic *settingsKey = new Characteristic::PowerModeSelection(); // Adds "View TV Setting" option to Selection Screen
SpanCharacteristic *displayOrder = new Characteristic::DisplayOrder();
SpanCharacteristic *testData = new Characteristic::TestData();
SpanCharacteristic *tvname;
HomeSpanTV(const char *name) : Service::Television() {
tvname = new Characteristic::ConfiguredName(name); // Name of TV
Serial.printf("Configured TV: %s\n",name);
TLV8 orderTLV;
uint32_t order[]={5,10,6,2,1,9,11,3,18,12};
for(int i=0;i<sizeof(order)/sizeof(uint32_t);i++){
if(i>0)
orderTLV.add(6);
orderTLV.add(1,sizeof(uint32_t),(uint8_t*)(order+i));
}
orderTLV.print();
displayOrder->setTLV(orderTLV);
uint8_t blob[]={1,2,3,4,5,6,7,8,9,10,11,12};
// testData->setData(blob,sizeof(blob));
testData->setData(blob,1);
new SpanUserCommand('P', "- change order of inputs", changeOrder, this);
new SpanUserCommand('C', "- change name of TV", setTVName, this);
}
boolean update() override {
if(active->updated()){
Serial.printf("Set TV Power to: %s\n",active->getNewVal()?"ON":"OFF");
}
if(activeID->updated()){
Serial.printf("Set Input Source to HDMI-%d\n",activeID->getNewVal());
}
if(settingsKey->updated()){
Serial.printf("Received request to \"View TV Settings\"\n");
}
if(remoteKey->updated()){
Serial.printf("Remote Control key pressed: ");
switch(remoteKey->getNewVal()){
case 4:
Serial.printf("UP ARROW\n");
break;
case 5:
Serial.printf("DOWN ARROW\n");
break;
case 6:
Serial.printf("LEFT ARROW\n");
break;
case 7:
Serial.printf("RIGHT ARROW\n");
break;
case 8:
Serial.printf("SELECT\n");
break;
case 9:
Serial.printf("BACK\n");
break;
case 11:
Serial.printf("PLAY/PAUSE\n");
break;
case 15:
Serial.printf("INFO\n");
break;
default:
Serial.print("UNKNOWN KEY\n");
}
}
return(true);
}
static void setTVName(const char *buf, void *arg){
HomeSpanTV *hsTV=(HomeSpanTV *)arg;
hsTV->tvname->setString("New Name");
Serial.printf("Reset TV Name to '%s'\n",hsTV->tvname->getString());
Serial.printf("Showing displayOrder '%s'\n",hsTV->displayOrder->getString());
}
static void changeOrder(const char *buf, void *arg){
HomeSpanTV *hsTV=(HomeSpanTV *)arg;
TLV8 orderTLV;
hsTV->displayOrder->getTLV(orderTLV);
orderTLV.print();
orderTLV.wipe();
uint8_t order[]={12,10,6,2,1,9,11,3,18,5};
for(int i=0;i<sizeof(order)/sizeof(uint8_t);i++){
if(i>0)
orderTLV.add(0);
orderTLV.add(1,sizeof(uint8_t),(uint8_t*)(order+i));
}
Serial.printf("AFTER:\n");
orderTLV.print();
size_t n=orderTLV.pack_size();
Serial.printf("Size=%d\n",n);
uint8_t c[n];
orderTLV.pack(c);
hsTV->displayOrder->setData(c,n);
}
};
///////////////////////////////
void setup() { void setup() {
Serial.begin(115200); Serial.begin(115200);
homeSpan.setLogLevel(2); homeSpan.begin(Category::Lighting,"HomeSpan Light");
homeSpan.begin(Category::Television,"HomeSpan Television"); new SpanAccessory();
new Service::AccessoryInformation();
new Characteristic::Identify();
new Service::LightBulb();
new Characteristic::On();
SPAN_ACCESSORY(); // new SpanUserCommand('k',"- list controllers",list_controllers);
homeSpan.setControllerCallback(list_controllers);
SpanService *hdmi1 = new Service::InputSource(); // Source included in Selection List, but excluded from Settings Screen }
new Characteristic::ConfiguredName("Alpha");
new Characteristic::Identifier(5);
new Characteristic::IsConfigured(1);
new Characteristic::CurrentVisibilityState(0);
new Characteristic::TargetVisibilityState(0);
SpanService *hdmi2 = new Service::InputSource();
new Characteristic::ConfiguredName("Gamma");
new Characteristic::Identifier(10);
new Characteristic::IsConfigured(1);
new Characteristic::CurrentVisibilityState(0);
new Characteristic::TargetVisibilityState(0);
SpanService *hdmi3 = new Service::InputSource();
new Characteristic::ConfiguredName("Beta");
new Characteristic::Identifier(6);
new Characteristic::IsConfigured(1);
new Characteristic::CurrentVisibilityState(0);
new Characteristic::TargetVisibilityState(0);
SpanService *hdmi4 = new Service::InputSource();
new Characteristic::ConfiguredName("Zebra");
new Characteristic::Identifier(2);
new Characteristic::IsConfigured(1);
new Characteristic::CurrentVisibilityState(0);
new Characteristic::TargetVisibilityState(0);
SpanService *hdmi5 = new Service::InputSource();
new Characteristic::ConfiguredName("Delta");
new Characteristic::Identifier(1);
new Characteristic::IsConfigured(1);
new Characteristic::CurrentVisibilityState(0);
new Characteristic::TargetVisibilityState(0);
SpanService *hdmi6 = new Service::InputSource();
new Characteristic::ConfiguredName("Trident");
new Characteristic::Identifier(9);
new Characteristic::IsConfigured(1);
new Characteristic::CurrentVisibilityState(0);
new Characteristic::TargetVisibilityState(0);
SpanService *hdmi7 = new Service::InputSource();
new Characteristic::ConfiguredName("Netflix");
new Characteristic::Identifier(11);
new Characteristic::IsConfigured(1);
new Characteristic::CurrentVisibilityState(0);
new Characteristic::TargetVisibilityState(0);
SpanService *hdmi8 = new Service::InputSource();
new Characteristic::ConfiguredName("Alpha2");
new Characteristic::Identifier(3);
new Characteristic::IsConfigured(1);
new Characteristic::CurrentVisibilityState(0);
new Characteristic::TargetVisibilityState(0);
SpanService *hdmi9 = new Service::InputSource();
new Characteristic::ConfiguredName("Moon");
new Characteristic::Identifier(18);
new Characteristic::IsConfigured(1);
new Characteristic::CurrentVisibilityState(0);
new Characteristic::TargetVisibilityState(0);
SpanService *hdmi10 = new Service::InputSource();
new Characteristic::ConfiguredName("Gamba");
new Characteristic::Identifier(12);
new Characteristic::IsConfigured(1);
new Characteristic::CurrentVisibilityState(0);
new Characteristic::TargetVisibilityState(0);
SpanService *speaker = new Service::TelevisionSpeaker();
new Characteristic::VolumeSelector();
new Characteristic::VolumeControlType(3);
(new HomeSpanTV("Test TV")) // Define a Television Service. Must link in InputSources!
->addLink(hdmi1)
->addLink(hdmi2)
->addLink(hdmi3)
->addLink(hdmi4)
->addLink(hdmi5)
->addLink(hdmi6)
->addLink(hdmi7)
->addLink(hdmi8)
->addLink(hdmi9)
->addLink(hdmi10)
->addLink(speaker)
;
}
////////////////////////////////////// //////////////////////////////////////
@ -258,3 +52,17 @@ void loop(){
} }
////////////////////////////////////// //////////////////////////////////////
void list_controllers(){
Serial.printf("\nControllers\n");
for(auto it=homeSpan.controllerListBegin(); it!=homeSpan.controllerListEnd(); ++it){
Serial.printf("Admin=%d ID=",it->isAdmin());
for(int i=0;i<36;i++)
Serial.printf("%02X",it->getID()[i]);
Serial.printf(" LTPK=");
for(int i=0;i<32;i++)
Serial.printf("%02X",it->getLTPK()[i]);
Serial.printf("\n");
}
}