Added QR Code logic

HomeSpan now broadcasts a Hashed Setup ID as MDNS "sh", which is used when pairing with a QR Code instead of a Setup Code.  A text version of the resulting QR code is output to the Serial Monitor whenever the 9-digit Setup Code is generated or changed.  The text version of the QR code can then be input into any QR Code Generator to create a pairable QR Code.

The default Setup ID used to create the Hashed Setup ID is "HSPN".  This can be changed with homeSpan.setQRCode(const char *id), where id is exactly 4 alphanumeric characters.  If not, the request to change the Setup ID is silently ignored and remains "HSPN."
This commit is contained in:
Gregg 2021-01-24 18:46:55 -06:00
parent 78cbd926f1
commit 9b71d6928a
6 changed files with 67 additions and 6 deletions

View File

@ -58,11 +58,14 @@ void HAPClient::init(){
} else { } else {
char c[128]; char c[128];
sprintf(c,"Generating SRP verification data for default Setup Code: %.3s-%.2s-%.3s\n\n",homeSpan.defaultSetupCode,homeSpan.defaultSetupCode+3,homeSpan.defaultSetupCode+5); sprintf(c,"Generating SRP verification data for default Setup Code: %.3s-%.2s-%.3s\n",homeSpan.defaultSetupCode,homeSpan.defaultSetupCode+3,homeSpan.defaultSetupCode+5);
Serial.print(c); Serial.print(c);
srp.createVerifyCode(homeSpan.defaultSetupCode,verifyData.verifyCode,verifyData.salt); // create verification code from default Setup Code and random salt srp.createVerifyCode(homeSpan.defaultSetupCode,verifyData.verifyCode,verifyData.salt); // create verification code from default Setup Code and random salt
nvs_set_blob(srpNVS,"VERIFYDATA",&verifyData,sizeof(verifyData)); // update data nvs_set_blob(srpNVS,"VERIFYDATA",&verifyData,sizeof(verifyData)); // update data
nvs_commit(srpNVS); // commit to NVS nvs_commit(srpNVS); // commit to NVS
Serial.print("Optional QR Code: ");
Serial.print(homeSpan.getQRCode(homeSpan.defaultSetupCode));
Serial.print("\n\n");
} }
if(!nvs_get_blob(hapNVS,"ACCESSORY",NULL,&len)){ // if found long-term Accessory data in NVS if(!nvs_get_blob(hapNVS,"ACCESSORY",NULL,&len)){ // if found long-term Accessory data in NVS

View File

@ -410,6 +410,17 @@ void Span::checkConnect(){
else else
mdns_service_txt_item_set("_hap","_tcp","sf","0"); // set Status Flag = 0 mdns_service_txt_item_set("_hap","_tcp","sf","0"); // set Status Flag = 0
uint8_t hashInput[22];
uint8_t hashOutput[64];
char setupHash[9];
size_t len;
memcpy(hashInput,qrID,4); // Create the Seup ID for use with optional QR Codes. This is an undocumented feature of HAP R2!
memcpy(hashInput+4,id,17); // Step 1: Concatenate 4-character Setup ID and 17-character Accessory ID into hashInput
mbedtls_sha512_ret(hashInput,21,hashOutput,0); // Step 2: Perform SHA-512 hash on combined 21-byte hashInput to create 64-byte hashOutput
mbedtls_base64_encode((uint8_t *)setupHash,9,&len,hashOutput,4); // Step 3: Encode the first 4 bytes of hashOutput in base64, which results in an 8-character, null-terminated, setupHash
mdns_service_txt_item_set("_hap","_tcp","sh",setupHash); // Step 4: broadcast the resulting Setup Hash
Serial.print("\nStarting Web (HTTP) Server supporting up to "); Serial.print("\nStarting Web (HTTP) Server supporting up to ");
Serial.print(maxConnections); Serial.print(maxConnections);
Serial.print(" simultaneous connections...\n\n"); Serial.print(" simultaneous connections...\n\n");
@ -426,6 +437,19 @@ void Span::checkConnect(){
/////////////////////////////// ///////////////////////////////
void Span::setQRID(const char *id){
char tBuf[5];
sscanf(id,"%4[0-9A-Za-z]",tBuf);
if(strlen(id)==4 && strlen(tBuf)==4){
qrID=id;
}
} // setQRID
///////////////////////////////
void Span::processSerialCommand(const char *c){ void Span::processSerialCommand(const char *c){
switch(c[0]){ switch(c[0]){
@ -515,6 +539,10 @@ void Span::processSerialCommand(const char *c){
nvs_set_blob(HAPClient::srpNVS,"VERIFYDATA",&verifyData,sizeof(verifyData)); // update data nvs_set_blob(HAPClient::srpNVS,"VERIFYDATA",&verifyData,sizeof(verifyData)); // update data
nvs_commit(HAPClient::srpNVS); // commit to NVS nvs_commit(HAPClient::srpNVS); // commit to NVS
Serial.print("New Code Saved!\n"); Serial.print("New Code Saved!\n");
Serial.print("Optional QR Code: ");
Serial.print(getQRCode(setupCode));
Serial.print("\n\n");
} }
} }
break; break;
@ -725,6 +753,32 @@ void Span::processSerialCommand(const char *c){
/////////////////////////////// ///////////////////////////////
const char *Span::getQRCode(const char *setupCode){
uint64_t n;
uint64_t iSetupCode;
uint64_t iCategory;
sscanf(category,"%llu",&iCategory);
sscanf(setupCode,"%llu",&iSetupCode);
n=(iCategory << 31) | (0xA << 27) | iSetupCode;
qrCode="";
while(n>0){
char c=n%36+48;
if(c>57)
c+=7;
qrCode=c+qrCode;
n/=36;
}
qrCode="X-HM://00" + qrCode + qrID;
return(qrCode.c_str());
}
///////////////////////////////
int Span::sprintfAttributes(char *cBuf){ int Span::sprintfAttributes(char *cBuf){
int nBytes=0; int nBytes=0;

View File

@ -85,6 +85,7 @@ struct Span{
int nFatalErrors=0; // number of fatal errors in user-defined configuration int nFatalErrors=0; // number of fatal errors in user-defined configuration
String configLog; // 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) boolean isBridge=true; // flag indicating whether device is configured as a bridge (i.e. first Accessory contains nothing but AccessoryInformation and HAPProtocolInformation)
String qrCode; // optional QR Code to use for pairing
boolean connected=false; // WiFi connection status boolean connected=false; // WiFi connection status
unsigned long waitTime=60000; // time to wait (in milliseconds) between WiFi connection attempts unsigned long waitTime=60000; // time to wait (in milliseconds) between WiFi connection attempts
@ -97,6 +98,7 @@ struct Span{
uint8_t maxConnections=DEFAULT_MAX_CONNECTIONS; // number of simultaneous HAP connections uint8_t maxConnections=DEFAULT_MAX_CONNECTIONS; // number of simultaneous HAP connections
unsigned long comModeLife=DEFAULT_COMMAND_TIMEOUT*1000; // length of time (in milliseconds) to keep Command Mode alive before resuming normal operations unsigned long comModeLife=DEFAULT_COMMAND_TIMEOUT*1000; // length of time (in milliseconds) to keep Command Mode alive before resuming normal operations
uint16_t tcpPortNum=DEFAULT_TCP_PORT; // port for TCP communications between HomeKit and HomeSpan uint16_t tcpPortNum=DEFAULT_TCP_PORT; // port for TCP communications between HomeKit and HomeSpan
const char *qrID=DEFAULT_QR_ID; // optional Setup ID used to pair with QR Code
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
@ -146,6 +148,8 @@ struct Span{
void setMaxConnections(uint8_t nCon){maxConnections=nCon;} // sets maximum number of simultaneous HAP connections (HAP requires devices support at least 8) void setMaxConnections(uint8_t nCon){maxConnections=nCon;} // sets maximum number of simultaneous HAP connections (HAP requires devices support at least 8)
void setHostNameSuffix(const char *suffix){hostNameSuffix=suffix;} // sets the hostName suffix to be used instead of the 6-byte AccessoryID void setHostNameSuffix(const char *suffix){hostNameSuffix=suffix;} // sets the hostName suffix to be used instead of the 6-byte AccessoryID
void setPortNum(uint16_t port){tcpPortNum=port;} // sets the TCP port number to use for communications between HomeKit and HomeSpan void setPortNum(uint16_t port){tcpPortNum=port;} // sets the TCP port number to use for communications between HomeKit and HomeSpan
void setQRID(const char *id); // sets the Setup ID for optional pairing with a QR Code
const char *getQRCode(const char *setupCode); // gets an optional QR code from setupCode
}; };
/////////////////////////////// ///////////////////////////////

View File

@ -29,6 +29,7 @@
#include <mbedtls/sha512.h> #include <mbedtls/sha512.h>
#include <mbedtls/bignum.h> #include <mbedtls/bignum.h>
#include <mbedtls/base64.h>
#include "HAPConstants.h" #include "HAPConstants.h"

View File

@ -61,6 +61,8 @@
#define DEFAULT_SETUP_CODE "46637726" // changed during network setup or with 'S' command #define DEFAULT_SETUP_CODE "46637726" // changed during network setup or with 'S' command
#define DEFAULT_QR_ID "HSPN" // change with homeSpan.setQRID(qrID);
#define DEFAULT_CONTROL_PIN 21 // change with homeSpan.setControlPin(pin) #define DEFAULT_CONTROL_PIN 21 // change with homeSpan.setControlPin(pin)
#define DEFAULT_STATUS_PIN 13 // change with homeSpan.setStatusPin(pin) #define DEFAULT_STATUS_PIN 13 // change with homeSpan.setStatusPin(pin)

View File

@ -8,10 +8,7 @@ void setup() {
Serial.begin(115200); Serial.begin(115200);
homeSpan.setLogLevel(2); homeSpan.setLogLevel(1);
homeSpan.setHostNameSuffix("");
homeSpan.setPortNum(1200);
homeSpan.begin(Category::Lighting,"HomeSpanTest"); homeSpan.begin(Category::Lighting,"HomeSpanTest");