diff --git a/README.md b/README.md
index 6134b44..db52f3d 100644
--- a/README.md
+++ b/README.md
@@ -85,8 +85,9 @@ HomeSpan requires version 2.0.0 or later of the [Arduino-ESP32 Board Manager](ht
* users should switch to the new constructor to avoid potential compatibility issues with future versions of HomeSpan
* added new method `boolean isRGBW()`
* returns *true* if Pixel was constructed as RGBW, else *false* if constructed as RGB only (i.e. no white LED)
- * created new PixelTester sketch (found under Other-> Examples) to aid in determining the *pixelType* for any LED Strip
-
+ * added new [PixelTester](examples/Other%20Examples/PixelTester) sketch (found under *Other Examples*) to aid in determining the *pixelType* for any LED Strip
+ * see the [Adressable RGB LEDs](docs/Pixels.md) page for details
+
* **New ability to read and set the IIDs of Services and Characteristics**
* adds new `SpanService` method `getIID()` that returns the IID of a Service
@@ -111,7 +112,7 @@ HomeSpan requires version 2.0.0 or later of the [Arduino-ESP32 Board Manager](ht
* a warning message is output on the Serial Monitor if `setVal()` is called to change the value of a Characteristic from within the `update()` method at the same time the Home App is sending an update request for that value
* does not apply if `setVal()` is called from within `update()` to change the value of a Characteristic in response to a *write-response* request from the Home App
-* **Fixed bug introduced in 1.9.0 that prevented `homeSpan.setPairingCode()` from saving (and subsequently using) the request Setup Pairing Code** (#786)
+* **Fixed bug introduced in 1.9.0 that prevented `homeSpan.setPairingCode()` from saving (and subsequently using) the request Setup Pairing Code**
* this method now operates silently, unless an invalid pairing code is provided, in which case an error is reported to the Serial Monitor and *the sketch is halted*
* the process for setting the Pairing Code using the CLI 'S' command or via the Access Point are unchanged - confirmation messages are still output to the Serial Monitor and errors do *not* cause the sketch to halt
diff --git a/docs/ServiceList.md b/docs/ServiceList.md
index abff6e9..fb3b681 100644
--- a/docs/ServiceList.md
+++ b/docs/ServiceList.md
@@ -428,6 +428,7 @@ The pre-defined constant expressions for enumerated Characteristics are in names
| Characteristic | Format | Perms | Min | Max | Constants/Defaults |
Active (B0) :small_blue_diamond:- indicates if the Service is active/on
| uint8 | PW+PR+EV | 0 | 1 | - INACTIVE (0) :heavy_check_mark:
- ACTIVE (1)
|
ActiveIdentifier (E7) - numerical Identifier of the InputSource selected in the Home App.
| uint32 | PW+PR+EV | 0 | 255 | 0 |
+DisplayOrder (136) - specifies the order in which the TV inputs are displayed for selection in the Home App
| tlv8 | PR+EV | 0 | 1 | "" |
RemoteKey (E1) - triggers an update when the corresponding key is pressed in the Remote Control widget on an iPhone
| uint8 | PW | 4 | 15 | - UP (4)
- DOWN (5)
- LEFT (6)
- RIGHT (7)
- CENTER (8)
- BACK (9)
- PLAY_PAUSE (11)
- INFO (15)
|
PowerModeSelection (DF) - when defined, creates a "View TV Settings" button in the Home App that triggers an update to this Characteristic when pressed
| uint8 | PW | 0 | 0 | |
ConfiguredName (E3) - default display name of this Service
| string | PW+PR+EV | - | - | "unnamed" |
diff --git a/docs/Tutorials.md b/docs/Tutorials.md
index 732c9ae..256cbd5 100644
--- a/docs/Tutorials.md
+++ b/docs/Tutorials.md
@@ -139,6 +139,9 @@ An example of HomeKit's *undocumented* Television Service showing how different
### [Pixel](../examples/Other%20Examples/Pixel)
Demonstrates how to use HomeSpan's *Pixel* and *Dot* classes to control one- and two-wire Addressable RGB and RGBW LEDs. See the [Addressable RGB LEDs](Pixels.md) page for full details
+### [PixelTester](../examples/Other%20Examples/PixelTester)
+A sketch to aid in determining the *pixelType* for any RGB(W) LED Strip. See the [Addressable RGB LEDs](Pixels.md) page for full details
+
### [CustomService](../examples/Other%20Examples/CustomService)
Demonstrates how to create Custom Services and Custom Characteristics in HomeSpan to implement an Atmospheric Pressure Sensor recognized by the *Eve for HomeKit* app. See [Custom Characteristics and Custom Services Macros](Reference.md#custom-characteristics-and-custom-services-macros) for full details
diff --git a/src/HAP.cpp b/src/HAP.cpp
index e5d6d07..ea031a3 100644
--- a/src/HAP.cpp
+++ b/src/HAP.cpp
@@ -324,7 +324,7 @@ int HAPClient::postPairSetupURL(uint8_t *content, size_t len){
iosTLV.print();
LOG2("------------ END TLVS! ------------\n");
- LOG1("In Pair Setup #%d (%s)...",conNum,client.remoteIP().toString().c_str());
+ LOG1("In Pair Setup #%d (%s)...",clientNumber,client.remoteIP().toString().c_str());
auto itState=iosTLV.find(kTLVType_State);
@@ -344,7 +344,7 @@ int HAPClient::postPairSetupURL(uint8_t *content, size_t len){
return(0);
};
- LOG2("Found . Expected .\n",tlvState,pairStatus);
+ LOG1("Found . Expected .\n",tlvState,pairStatus);
if(tlvState!=pairStatus){ // error: Device is not yet paired, but out-of-sequence pair-setup STATE was received
LOG0("\n*** ERROR: Out-of-Sequence Pair-Setup request!\n\n");
@@ -447,8 +447,7 @@ int HAPClient::postPairSetupURL(uint8_t *content, size_t len){
// Note the SALT and INFO text fields used by HKDF to create this Session Key are NOT the same as those for creating iosDeviceX.
// The iosDeviceX HKDF calculations are separate and will be performed further below with the SALT and INFO as specified in the HAP docs.
- TempBuffer sessionKey(crypto_box_PUBLICKEYBYTES); // temporary space - used only in this block
- HKDF::create(sessionKey,srp->K,64,"Pair-Setup-Encrypt-Salt","Pair-Setup-Encrypt-Info"); // create SessionKey
+ HKDF::create(temp.sessionKey,srp->K,64,"Pair-Setup-Encrypt-Salt","Pair-Setup-Encrypt-Info"); // create SessionKey
LOG2("------- DECRYPTING SUB-TLVS -------\n");
@@ -456,7 +455,7 @@ int HAPClient::postPairSetupURL(uint8_t *content, size_t len){
TempBuffer decrypted(itEncryptedData->getLen()-crypto_aead_chacha20poly1305_IETF_ABYTES); // temporary storage for decrypted data
- if(crypto_aead_chacha20poly1305_ietf_decrypt(decrypted, NULL, NULL, *itEncryptedData, itEncryptedData->getLen(), NULL, 0, (unsigned char *)"\x00\x00\x00\x00PS-Msg05", sessionKey)==-1){
+ if(crypto_aead_chacha20poly1305_ietf_decrypt(decrypted, NULL, NULL, *itEncryptedData, itEncryptedData->getLen(), NULL, 0, (unsigned char *)"\x00\x00\x00\x00PS-Msg05", temp.sessionKey)==-1){
LOG0("\n*** ERROR: Exchange-Request Authentication Failed\n\n");
responseTLV.add(kTLVType_Error,tagError_Authentication); // set Error=Authentication
tlvRespond(responseTLV); // send response to client
@@ -534,7 +533,7 @@ int HAPClient::postPairSetupURL(uint8_t *content, size_t len){
itEncryptedData=responseTLV.add(kTLVType_EncryptedData,subPack.len()+crypto_aead_chacha20poly1305_IETF_ABYTES,NULL); //create blank EncryptedData TLV with space for subTLV + Authentication Tag
- crypto_aead_chacha20poly1305_ietf_encrypt(*itEncryptedData,NULL,subPack,subPack.len(),NULL,0,NULL,(unsigned char *)"\x00\x00\x00\x00PS-Msg06",sessionKey);
+ crypto_aead_chacha20poly1305_ietf_encrypt(*itEncryptedData,NULL,subPack,subPack.len(),NULL,0,NULL,(unsigned char *)"\x00\x00\x00\x00PS-Msg06",temp.sessionKey);
LOG2("---------- END SUB-TLVS! ----------\n");
@@ -575,7 +574,7 @@ int HAPClient::postPairVerifyURL(uint8_t *content, size_t len){
iosTLV.print();
LOG2("------------ END TLVS! ------------\n");
- LOG1("In Pair Verify #%d (%s)...",conNum,client.remoteIP().toString().c_str());
+ LOG1("In Pair Verify #%d (%s)...",clientNumber,client.remoteIP().toString().c_str());
auto itState=iosTLV.find(kTLVType_State);
@@ -595,7 +594,7 @@ int HAPClient::postPairVerifyURL(uint8_t *content, size_t len){
return(0);
};
- LOG2("Found \n",tlvState); // unlike pair-setup, out-of-sequencing can be handled gracefully for pair-verify (HAP requirement). No need to keep track of pairStatus
+ LOG1("Found \n",tlvState); // unlike pair-setup, out-of-sequencing can be handled gracefully for pair-verify (HAP requirement). No need to keep track of pairStatus
switch(tlvState){ // Pair-Verify STATE received -- process request! (HAP Section 5.7)
@@ -611,16 +610,14 @@ int HAPClient::postPairVerifyURL(uint8_t *content, size_t len){
return(0);
}
- publicCurveKey=(uint8_t *)HS_MALLOC(crypto_box_PUBLICKEYBYTES); // temporary space - will be deleted at end of verification process
TempBuffer secretCurveKey(crypto_box_SECRETKEYBYTES); // temporary space - used only in this block
- crypto_box_keypair(publicCurveKey,secretCurveKey); // generate Accessory's random Curve25519 Public/Secret Key Pair
+ crypto_box_keypair(temp.publicCurveKey,secretCurveKey); // generate Accessory's random Curve25519 Public/Secret Key Pair
- iosCurveKey=(uint8_t *)HS_MALLOC(crypto_box_PUBLICKEYBYTES); // temporary space - will be deleted at end of verification process
- memcpy(iosCurveKey,*itPublicKey,crypto_box_PUBLICKEYBYTES); // save Controller's Curve25519 Public Key
+ memcpy(temp.iosCurveKey,*itPublicKey,crypto_box_PUBLICKEYBYTES); // save Controller's Curve25519 Public Key
// concatenate Accessory's Curve25519 Public Key, Accessory's Pairing ID, and Controller's Curve25519 Public Key into accessoryInfo
- TempBuffer accessoryInfo(publicCurveKey,crypto_box_PUBLICKEYBYTES,accessory.ID,hap_accessory_IDBYTES,iosCurveKey,crypto_box_PUBLICKEYBYTES,NULL);
+ TempBuffer accessoryInfo(temp.publicCurveKey,crypto_box_PUBLICKEYBYTES,accessory.ID,hap_accessory_IDBYTES,temp.iosCurveKey,crypto_box_PUBLICKEYBYTES,NULL);
subTLV.add(kTLVType_Identifier,hap_accessory_IDBYTES,accessory.ID); // set Identifier subTLV record as Accessory's Pairing ID
auto itSignature=subTLV.add(kTLVType_Signature,crypto_sign_BYTES,NULL); // create blank Signature subTLV
@@ -634,19 +631,17 @@ int HAPClient::postPairVerifyURL(uint8_t *content, size_t len){
TempBuffer subPack(subTLV.pack_size()); // create sub-TLV by packing Identifier and Signature TLV records together
subTLV.pack(subPack);
- sharedCurveKey=(uint8_t *)HS_MALLOC(crypto_box_PUBLICKEYBYTES); // temporary space - will be deleted at end of verification process
- crypto_scalarmult_curve25519(sharedCurveKey,secretCurveKey,iosCurveKey); // generate Shared-Secret Curve25519 Key from Accessory's Curve25519 Secret Key and Controller's Curve25519 Public Key
+ crypto_scalarmult_curve25519(temp.sharedCurveKey,secretCurveKey,temp.iosCurveKey); // generate Shared-Secret Curve25519 Key from Accessory's Curve25519 Secret Key and Controller's Curve25519 Public Key
- sessionKey=(uint8_t *)HS_MALLOC(crypto_box_PUBLICKEYBYTES); // temporary space - will be deleted at end of verification process
- HKDF::create(sessionKey,sharedCurveKey,crypto_box_PUBLICKEYBYTES,"Pair-Verify-Encrypt-Salt","Pair-Verify-Encrypt-Info"); // create Session Curve25519 Key from Shared-Secret Curve25519 Key using HKDF-SHA-512
+ HKDF::create(temp.sessionKey,temp.sharedCurveKey,crypto_box_PUBLICKEYBYTES,"Pair-Verify-Encrypt-Salt","Pair-Verify-Encrypt-Info"); // create Session Curve25519 Key from Shared-Secret Curve25519 Key using HKDF-SHA-512
- auto itEncryptedData=responseTLV.add(kTLVType_EncryptedData,subPack.len()+crypto_aead_chacha20poly1305_IETF_ABYTES,NULL); // create blank EncryptedData subTLV
- crypto_aead_chacha20poly1305_ietf_encrypt(*itEncryptedData,NULL,subPack,subPack.len(),NULL,0,NULL,(unsigned char *)"\x00\x00\x00\x00PV-Msg02",sessionKey); // encrypt data with Session Curve25519 Key and padded nonce="PV-Msg02"
+ auto itEncryptedData=responseTLV.add(kTLVType_EncryptedData,subPack.len()+crypto_aead_chacha20poly1305_IETF_ABYTES,NULL); // create blank EncryptedData subTLV
+ crypto_aead_chacha20poly1305_ietf_encrypt(*itEncryptedData,NULL,subPack,subPack.len(),NULL,0,NULL,(unsigned char *)"\x00\x00\x00\x00PV-Msg02",temp.sessionKey); // encrypt data with Session Curve25519 Key and padded nonce="PV-Msg02"
LOG2("---------- END SUB-TLVS! ----------\n");
responseTLV.add(kTLVType_State,pairState_M2); // set State=
- responseTLV.add(kTLVType_PublicKey,crypto_box_PUBLICKEYBYTES,publicCurveKey); // set PublicKey to Accessory's Curve25519 Public Key
+ responseTLV.add(kTLVType_PublicKey,crypto_box_PUBLICKEYBYTES,temp.publicCurveKey); // set PublicKey to Accessory's Curve25519 Public Key
tlvRespond(responseTLV); // send response to client
}
@@ -670,7 +665,7 @@ int HAPClient::postPairVerifyURL(uint8_t *content, size_t len){
TempBuffer decrypted((*itEncryptedData).getLen()-crypto_aead_chacha20poly1305_IETF_ABYTES); // temporary storage for decrypted data
- if(crypto_aead_chacha20poly1305_ietf_decrypt(decrypted, NULL, NULL, *itEncryptedData, itEncryptedData->getLen(), NULL, 0, (unsigned char *)"\x00\x00\x00\x00PV-Msg03", sessionKey)==-1){
+ if(crypto_aead_chacha20poly1305_ietf_decrypt(decrypted, NULL, NULL, *itEncryptedData, itEncryptedData->getLen(), NULL, 0, (unsigned char *)"\x00\x00\x00\x00PV-Msg03", temp.sessionKey)==-1){
LOG0("\n*** ERROR: Verify Authentication Failed\n\n");
responseTLV.add(kTLVType_State,pairState_M4); // set State=
responseTLV.add(kTLVType_Error,tagError_Authentication); // set Error=Authentication
@@ -713,7 +708,7 @@ int HAPClient::postPairVerifyURL(uint8_t *content, size_t len){
// concatenate Controller's Curve25519 Public Key (from previous step), Controller's Pairing ID, and Accessory's Curve25519 Public Key (from previous step) into iosDeviceInfo
- TempBuffer iosDeviceInfo(iosCurveKey,crypto_box_PUBLICKEYBYTES,tPair->ID,hap_controller_IDBYTES,publicCurveKey,crypto_box_PUBLICKEYBYTES,NULL);
+ TempBuffer iosDeviceInfo(temp.iosCurveKey,crypto_box_PUBLICKEYBYTES,tPair->ID,hap_controller_IDBYTES,temp.publicCurveKey,crypto_box_PUBLICKEYBYTES,NULL);
if(crypto_sign_verify_detached(*itSignature, iosDeviceInfo, iosDeviceInfo.len(), tPair->LTPK) != 0){ // verify signature of iosDeviceInfo using Controller's LTPK
LOG0("\n*** ERROR: LPTK Signature Verification Failed\n\n");
@@ -728,17 +723,12 @@ int HAPClient::postPairVerifyURL(uint8_t *content, size_t len){
cPair=tPair; // save Controller for this connection slot - connection is now verified and should be encrypted going forward
- HKDF::create(a2cKey,sharedCurveKey,32,"Control-Salt","Control-Read-Encryption-Key"); // create AccessoryToControllerKey from (previously-saved) Shared-Secret Curve25519 Key (HAP Section 6.5.2)
- HKDF::create(c2aKey,sharedCurveKey,32,"Control-Salt","Control-Write-Encryption-Key"); // create ControllerToAccessoryKey from (previously-saved) Shared-Secret Curve25519 Key (HAP Section 6.5.2)
+ HKDF::create(a2cKey,temp.sharedCurveKey,32,"Control-Salt","Control-Read-Encryption-Key"); // create AccessoryToControllerKey from (previously-saved) Shared-Secret Curve25519 Key (HAP Section 6.5.2)
+ HKDF::create(c2aKey,temp.sharedCurveKey,32,"Control-Salt","Control-Write-Encryption-Key"); // create ControllerToAccessoryKey from (previously-saved) Shared-Secret Curve25519 Key (HAP Section 6.5.2)
a2cNonce.zero(); // reset Nonces for this session to zero
c2aNonce.zero();
- free(publicCurveKey); // free storage of these temporary variables created in previous step
- free(sharedCurveKey);
- free(sessionKey);
- free(iosCurveKey);
-
LOG2("\n*** SESSION VERIFICATION COMPLETE *** \n");
}
break;
@@ -766,7 +756,7 @@ int HAPClient::postPairingsURL(uint8_t *content, size_t len){
iosTLV.print();
LOG2("------------ END TLVS! ------------\n");
- LOG1("In Post Pairings #%d (%s)...",conNum,client.remoteIP().toString().c_str());
+ LOG1("In Post Pairings #%d (%s)...",clientNumber,client.remoteIP().toString().c_str());
auto itState=iosTLV.find(kTLVType_State);
auto itMethod=iosTLV.find(kTLVType_Method);
@@ -892,7 +882,7 @@ int HAPClient::getAccessoriesURL(){
return(0);
}
- LOG1("In Get Accessories #%d (%s)...\n",conNum,client.remoteIP().toString().c_str());
+ LOG1("In Get Accessories #%d (%s)...\n",clientNumber,client.remoteIP().toString().c_str());
homeSpan.printfAttributes();
size_t nBytes=hapOut.getSize();
@@ -920,7 +910,7 @@ int HAPClient::getCharacteristicsURL(char *urlBuf){
return(0);
}
- LOG1("In Get Characteristics #%d (%s)...\n",conNum,client.remoteIP().toString().c_str());
+ LOG1("In Get Characteristics #%d (%s)...\n",clientNumber,client.remoteIP().toString().c_str());
int len=strlen(urlBuf); // determine number of IDs specified by counting commas in URL
int numIDs=1;
@@ -988,7 +978,7 @@ int HAPClient::putCharacteristicsURL(char *json){
return(0);
}
- LOG1("In Put Characteristics #%d (%s)...\n",conNum,client.remoteIP().toString().c_str());
+ LOG1("In Put Characteristics #%d (%s)...\n",clientNumber,client.remoteIP().toString().c_str());
int n=homeSpan.countCharacteristics(json); // count number of objects in JSON request
if(n==0) // if no objects found, return
@@ -1027,7 +1017,7 @@ int HAPClient::putCharacteristicsURL(char *json){
// Create and send Event Notifications if needed
- eventNotify(pObj,n,HAPClient::conNum); // transmit EVENT Notification for "n" pObj objects, except DO NOT notify client making request
+ eventNotify(pObj,n,this); // transmit EVENT Notification for "n" pObj objects, except DO NOT notify client making request
return(1);
}
@@ -1041,7 +1031,7 @@ int HAPClient::putPrepareURL(char *json){
return(0);
}
- LOG1("In Put Prepare #%d (%s)...\n",conNum,client.remoteIP().toString().c_str());
+ LOG1("In Put Prepare #%d (%s)...\n",clientNumber,client.remoteIP().toString().c_str());
char ttlToken[]="\"ttl\":";
char pidToken[]="\"pid\":";
@@ -1217,6 +1207,7 @@ void HAPClient::getStatusURL(HAPClient *hapClient, void (*callBack)(const char *
if(hapClient){
hapClient->client.stop();
+ delay(1);
LOG2("------------ SENT! --------------\n");
}
}
@@ -1246,27 +1237,26 @@ void HAPClient::checkTimedWrites(){
else
tw++;
}
-
}
//////////////////////////////////////
-void HAPClient::eventNotify(SpanBuf *pObj, int nObj, int ignoreClient){
-
- for(int cNum=0;cNumclient && 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)
+void HAPClient::eventNotify(SpanBuf *pObj, int nObj, HAPClient *ignore){
- homeSpan.printfNotify(pObj,nObj,cNum); // create JSON (which may be of zero length if there are no applicable notifications for this cNum)
+ for(auto it=homeSpan.hapList.begin(); it!=homeSpan.hapList.end(); ++it){ // loop over all connection slots
+ if(&(*it)!=ignore){ // if NOT flagged to be ignored (in cases where it is the client making a PUT request)
+
+ homeSpan.printfNotify(pObj,nObj,&(*it)); // create JSON (which may be of zero length if there are no applicable notifications for this cNum)
size_t nBytes=hapOut.getSize();
hapOut.flush();
if(nBytes>0){ // if there ARE notifications to send to client cNum
- LOG2("\n>>>>>>>>>> %s >>>>>>>>>>\n",hap[cNum]->client.remoteIP().toString().c_str());
+ LOG2("\n>>>>>>>>>> %s >>>>>>>>>>\n",it->client.remoteIP().toString().c_str());
- hapOut.setLogLevel(2).setHapClient(hap[cNum]);
+ hapOut.setLogLevel(2).setHapClient(&(*it));
hapOut << "EVENT/1.0 200 OK\r\nContent-Type: application/hap+json\r\nContent-Length: " << nBytes << "\r\n\r\n";
- homeSpan.printfNotify(pObj,nObj,cNum);
+ homeSpan.printfNotify(pObj,nObj,&(*it));
hapOut.flush();
LOG2("\n-------- SENT ENCRYPTED! --------\n");
@@ -1301,6 +1291,8 @@ void HAPClient::tlvRespond(TLV8 &tlv8){
LOG2("------------ SENT! --------------\n");
else
LOG2("-------- SENT ENCRYPTED! --------\n");
+
+ free(body);
} // tlvRespond
@@ -1469,11 +1461,11 @@ void HAPClient::removeController(uint8_t *id){
//////////////////////////////////////
void HAPClient::tearDown(uint8_t *id){
-
- for(int i=0;iclient && (id==NULL || (hap[i]->cPair && !memcmp(id,hap[i]->cPair->ID,hap_controller_IDBYTES)))){
- LOG1("*** Terminating Client #%d\n",i);
- hap[i]->client.stop();
+
+ for(HAPClient &hc : homeSpan.hapList){
+ if(id==NULL || (hc.cPair && !memcmp(id,hc.cPair->ID,hap_controller_IDBYTES))){
+ LOG1("*** Terminating Client #%d\n",hc.clientNumber);
+ hc.client.stop();
}
}
}
@@ -1569,9 +1561,12 @@ HapOut::HapStreamBuffer::HapStreamBuffer(){
//////////////////////////////////////
HapOut::HapStreamBuffer::~HapStreamBuffer(){
-
+
sync();
free(buffer);
+ free(encBuf);
+ free(hash);
+ free(ctx);
}
//////////////////////////////////////
@@ -1696,5 +1691,4 @@ void HapOut::HapStreamBuffer::printFormatted(char *buf, size_t nChars, size_t ns
pairState HAPClient::pairStatus;
Accessory HAPClient::accessory;
list> HAPClient::controllerList;
-int HAPClient::conNum;
diff --git a/src/HAP.h b/src/HAP.h
index 8fd6f15..9532741 100644
--- a/src/HAP.h
+++ b/src/HAP.h
@@ -72,6 +72,34 @@ struct Accessory {
uint8_t LTPK[crypto_sign_PUBLICKEYBYTES]; // Long Term Ed2519 Public Key
};
+//////////////////////////////////////////////////////////
+// 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);}
+
+};
+
/////////////////////////////////////////////////
// HAPClient Structure
// Reads and Writes from each HAP Client connection
@@ -87,20 +115,22 @@ struct HAPClient {
static pairState pairStatus; // tracks pair-setup status
static Accessory accessory; // Accessory ID and Ed25519 public and secret keys - permanently stored
static list> controllerList; // linked-list of Paired Controller IDs and ED25519 long-term public keys - permanently stored
- static int conNum; // connection number - used to keep track of per-connection EV notifications
// individual structures and data defined for each Hap Client connection
WiFiClient client; // handle to client
+ int clientNumber; // client number
Controller *cPair=NULL; // pointer to info on current, session-verified Paired Controller (NULL=un-verified, and therefore un-encrypted, connection)
// These temporary Curve25519 keys are generated in the first call to pair-verify and used in the second call to pair-verify so must persist for a short period
-
- uint8_t *publicCurveKey; // Accessory's Curve25519 Public Key
- uint8_t *sharedCurveKey; // Shared-Secret Curve25519 Key derived from Accessory's Secret Key and Controller's Public Key
- uint8_t *sessionKey; // Session Key Curve25519 (derived with various HKDF calls)
- uint8_t *iosCurveKey; // Controller's Curve25519 Public Key
+ struct tempKeys_t {
+ uint8_t publicCurveKey[crypto_box_PUBLICKEYBYTES]; // Accessory's Curve25519 Public Key
+ uint8_t sharedCurveKey[crypto_box_PUBLICKEYBYTES]; // Shared-Secret Curve25519 Key derived from Accessory's Secret Key and Controller's Public Key
+ uint8_t sessionKey[crypto_box_PUBLICKEYBYTES]; // Session Key Curve25519 (derived with various HKDF calls)
+ uint8_t iosCurveKey[crypto_box_PUBLICKEYBYTES]; // Controller's Curve25519 Public Key
+ } temp;
+
// CurveKey and CurveKey Nonces are created once each new session is verified in /pair-verify. Keys persist for as long as connection is open
uint8_t a2cKey[32]; // AccessoryToControllerKey derived from HKDF-SHA-512 of sharedCurveKey (HAP Section 6.5.2)
@@ -143,7 +173,7 @@ struct HAPClient {
static void tearDown(uint8_t *id); // tears down connections using Controller with ID=id; tears down all connections if id=NULL
static void checkNotifications(); // checks for Event Notifications and reports to controllers as needed (HAP Section 6.8)
static void checkTimedWrites(); // checks for expired Timed Write PIDs, and clears any found (HAP Section 6.7.2.4)
- static void eventNotify(SpanBuf *pObj, int nObj, int ignoreClient=-1); // transmits EVENT Notifications for nObj SpanBuf objects, pObj, with optional flag to ignore a specific client
+ static void eventNotify(SpanBuf *pObj, int nObj, HAPClient *ignore=NULL); // transmits EVENT Notifications for nObj SpanBuf objects, pObj, with optional flag to ignore a specific client
static void getStatusURL(HAPClient *, void (*)(const char *, void *), void *); // GET / status (an optional, non-HAP feature)
@@ -206,5 +236,4 @@ class HapOut : public std::ostream {
/////////////////////////////////////////////////
// Extern Variables
-extern HAPClient **hap;
extern HapOut hapOut;
diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp
index e815af0..d5d7398 100644
--- a/src/HomeSpan.cpp
+++ b/src/HomeSpan.cpp
@@ -46,7 +46,6 @@ const __attribute__((section(".rodata_custom_desc"))) SpanPartition spanPartitio
using namespace Utils;
HapOut hapOut; // Specialized output stream that can both print to serial monitor and encrypt/transmit to HAP Clients with minimal memory usage (global-scoped variable)
-HAPClient **hap; // HAP Client structure containing HTTP client connections, parsing routines, and state variables (global-scoped variable)
Span homeSpan; // HAP Attributes database and all related control functions for this Accessory (global-scoped variable)
HapCharacteristics hapChars; // Instantiation of all HAP Characteristics used to create SpanCharacteristics (global-scoped variable)
@@ -69,7 +68,6 @@ Span::Span(){
rebootCount++;
nvs_set_u8(wifiNVS,"REBOOTS",rebootCount);
nvs_commit(wifiNVS);
-
}
///////////////////////////////
@@ -89,14 +87,7 @@ void Span::begin(Category catID, const char *displayName, const char *hostNameBa
esp_task_wdt_delete(xTaskGetIdleTaskHandleForCPU(0)); // required to avoid watchdog timeout messages from ESP32-C3
- if(requestedMaxConavailable()){ // found a new HTTP client
- int freeSlot=getFreeSlot(); // get next free slot
-
- if(freeSlot==-1){ // no available free slots
- freeSlot=randombytes_uniform(maxConnections);
- LOG2("=======================================\n");
- LOG1("** Freeing Client #");
- LOG1(freeSlot);
- LOG1(" (");
- LOG1(millis()/1000);
- LOG1(" sec) ");
- LOG1(hap[freeSlot]->client.remoteIP());
- LOG1("\n");
- hap[freeSlot]->client.stop(); // disconnect client from first slot and re-use
- }
-
- hap[freeSlot]->client=newClient; // copy new client handle into free slot
+ if(hapServer->hasClient()){
+
+ auto it=hapList.emplace(hapList.begin()); // create new HAPClient connection
+ it->client=hapServer->available();
+ it->clientNumber=it->client.fd()-LWIP_SOCKET_OFFSET;
+
+ HAPClient::pairStatus=pairState_M1; // reset starting PAIR STATE (which may be needed if Accessory failed in middle of pair-setup)
LOG2("=======================================\n");
- LOG1("** Client #");
- LOG1(freeSlot);
- LOG1(" Connected: (");
- LOG1(millis()/1000);
- LOG1(" sec) ");
- LOG1(hap[freeSlot]->client.remoteIP());
- LOG1(" on Socket ");
- LOG1(hap[freeSlot]->client.fd()-LWIP_SOCKET_OFFSET+1);
- LOG1("/");
- LOG1(CONFIG_LWIP_MAX_SOCKETS);
- LOG1("\n");
+ LOG1("** Client #%d Connected (%lu sec): %s\n",it->clientNumber,millis()/1000,it->client.remoteIP().toString().c_str());
LOG2("\n");
-
- hap[freeSlot]->cPair=NULL; // reset pointer to verified ID
- homeSpan.clearNotify(freeSlot); // clear all notification requests for this connection
- HAPClient::pairStatus=pairState_M1; // reset starting PAIR STATE (which may be needed if Accessory failed in middle of pair-setup)
}
- for(int i=0;iclient && hap[i]->client.available()){ // if connection exists and data is available
+ currentClient=hapList.begin();
+ while(currentClient!=hapList.end()){
- HAPClient::conNum=i; // set connection number
- homeSpan.lastClientIP=hap[i]->client.remoteIP().toString(); // store IP Address for web logging
- hap[i]->processRequest(); // process HAP request
- homeSpan.lastClientIP="0.0.0.0"; // reset stored IP address to show "0.0.0.0" if homeSpan.getClientIP() is used in any other context
-
- if(!hap[i]->client){ // client disconnected by server
- LOG1("** Disconnected Client #");
- LOG1(i);
- LOG1(" (");
- LOG1(millis()/1000);
- LOG1(" sec)\n");
+ if(currentClient->client.connected()){ // if the client is connected
+ if(currentClient->client.available()){ // if client has data available
+ homeSpan.lastClientIP=currentClient->client.remoteIP().toString(); // store IP Address for web logging
+ currentClient->processRequest(); // PROCESS HAP REQUEST
+ homeSpan.lastClientIP="0.0.0.0"; // reset stored IP address to show "0.0.0.0" if homeSpan.getClientIP() is used in any other context
}
-
- LOG2("\n");
-
- } // process HAP Client
- } // for-loop over connection slots
-
+ currentClient++;
+ } else {
+ LOG1("** Client #%d DISCONNECTED (%lu sec)\n",currentClient->clientNumber,millis()/1000);
+ clearNotify(&*currentClient); // clear all notification requests for this connection
+ currentClient=hapList.erase(currentClient); // remove HAPClient connection
+ }
+ }
+
snapTime=millis(); // snap the current time for use in ALL loop routines
for(auto it=Loops.begin();it!=Loops.end();it++) // call loop() for all Services with over-ridden loop() methods
@@ -334,18 +294,6 @@ void Span::pollTask() {
} // poll
-///////////////////////////////
-
-int Span::getFreeSlot(){
-
- for(int i=0;iclient)
- return(i);
- }
-
- return(-1);
-}
-
//////////////////////////////////////
void Span::commandMode(){
@@ -550,7 +498,7 @@ void Span::checkConnect(){
if(webLog.timeServer)
xTaskCreateUniversal(webLog.initTime, "timeSeverTaskHandle", 8096, &webLog, 1, NULL, 0);
- LOG0("Starting HAP Server on port %d supporting %d simultaneous HomeKit Controller Connections...\n\n",tcpPortNum,maxConnections);
+ LOG0("Starting HAP Server on port %d...\n\n",tcpPortNum);
hapServer->begin();
@@ -587,6 +535,15 @@ void Span::processSerialCommand(const char *c){
switch(c[0]){
+ case 'Z': {
+ for(auto it=hapList.begin(); it!=hapList.end(); ++it){
+ (*it).client.stop();
+ delay(5);
+
+ }
+ }
+ break;
+
case 's': {
LOG0("\n*** HomeSpan Status ***\n\n");
@@ -603,27 +560,20 @@ void Span::processSerialCommand(const char *c){
HAPClient::printControllers();
LOG0("\n");
- for(int i=0;iclient){
-
- LOG0("%s on Socket %d/%d",hap[i]->client.remoteIP().toString().c_str(),hap[i]->client.fd()-LWIP_SOCKET_OFFSET+1,CONFIG_LWIP_MAX_SOCKETS);
-
- if(hap[i]->cPair){
- LOG0(" ID=");
- HAPClient::charPrintRow(hap[i]->cPair->getID(),36);
- LOG0(hap[i]->cPair->isAdmin()?" (admin)":" (regular)");
- } else {
- LOG0(" (unverified)");
- }
-
+ for(auto it=hapList.begin(); it!=hapList.end(); ++it){
+ LOG0("Client #%d: %s",(*it).clientNumber,(*it).client.remoteIP().toString().c_str());
+ if((*it).cPair){
+ LOG0(" ID=");
+ HAPClient::charPrintRow((*it).cPair->getID(),36);
+ LOG0((*it).cPair->isAdmin()?" (admin)\n":" (regular)\n");
} else {
- LOG0("(unconnected)");
+ LOG0(" (unverified)\n");
}
+ }
- LOG0("\n");
- }
-
+ if(hapList.empty())
+ LOG0("No Client Connections!\n");
+
LOG0("\n*** End Status ***\n\n");
}
break;
@@ -914,11 +864,9 @@ void Span::processSerialCommand(const char *c){
if(((*chr)->perms)&EV){
LOG0(", EV=(");
boolean addComma=false;
- for(int i=0;iev[i] && hap[i]->client){
- LOG0("%s%d",addComma?",":"",i);
- addComma=true;
- }
+ for(auto const &hc : (*chr)->evList){
+ LOG0("%s%d",addComma?",":"",hc->clientNumber);
+ addComma=true;
}
LOG0(")");
}
@@ -1491,29 +1439,25 @@ int Span::updateCharacteristics(char *buf, SpanBuf *pObj){
///////////////////////////////
-void Span::clearNotify(int slotNum){
-
- for(int i=0;iServices.size();j++){
- for(int k=0;kServices[j]->Characteristics.size();k++){
- Accessories[i]->Services[j]->Characteristics[k]->ev[slotNum]=false;
- }
- }
- }
-}
+void Span::clearNotify(HAPClient *hc){
+
+ for(auto const &acc : Accessories)
+ for(auto const &svc : acc->Services)
+ for(auto const &chr : svc->Characteristics)
+ chr->evList.remove(hc);
+}
///////////////////////////////
-void Span::printfNotify(SpanBuf *pObj, int nObj, int conNum){
+void Span::printfNotify(SpanBuf *pObj, int nObj, HAPClient *hc){
boolean notifyFlag=false;
for(int i=0;ievList.has(hc)){ // if connection hc is subscribed to EV notifications for this characteristic
- if(pObj[i].characteristic->ev[conNum]){ // if notifications requested for this characteristic by specified connection number
-
if(!notifyFlag) // this is first notification for any characteristic
hapOut << "{\"characteristics\":["; // print start of JSON array
else // else already printed at least one other characteristic
@@ -1857,8 +1801,6 @@ SpanCharacteristic::SpanCharacteristic(HapChar *hapChar, boolean isCustom){
iid=++(homeSpan.Accessories.back()->iidCount);
service=homeSpan.Accessories.back()->Services.back();
aid=homeSpan.Accessories.back()->aid;
-
- ev=(boolean *)HS_CALLOC(homeSpan.maxConnections,sizeof(boolean));
}
///////////////////////////////
@@ -1870,7 +1812,6 @@ SpanCharacteristic::~SpanCharacteristic(){
chr++;
service->Characteristics.erase(chr);
- free(ev);
free(desc);
free(unit);
free(validValues);
@@ -1886,6 +1827,207 @@ SpanCharacteristic::~SpanCharacteristic(){
///////////////////////////////
+String SpanCharacteristic::uvPrint(UVal &u){
+ char c[64];
+ switch(format){
+ case FORMAT::BOOL:
+ return(String(u.BOOL));
+ case FORMAT::INT:
+ return(String(u.INT));
+ case FORMAT::UINT8:
+ return(String(u.UINT8));
+ case FORMAT::UINT16:
+ return(String(u.UINT16));
+ case FORMAT::UINT32:
+ return(String(u.UINT32));
+ case FORMAT::UINT64:
+ sprintf(c,"%llu",u.UINT64);
+ return(String(c));
+ case FORMAT::FLOAT:
+ sprintf(c,"%g",u.FLOAT);
+ return(String(c));
+ case FORMAT::STRING:
+ case FORMAT::DATA:
+ case FORMAT::TLV_ENC:
+ return(String("\"") + String(u.STRING) + String("\""));
+ } // switch
+ return(String()); // included to prevent compiler warnings
+}
+
+///////////////////////////////
+
+void SpanCharacteristic::uvSet(UVal &dest, UVal &src){
+ if(format>=FORMAT::STRING)
+ uvSet(dest,(const char *)src.STRING);
+ else
+ dest=src;
+}
+
+///////////////////////////////
+
+void SpanCharacteristic::uvSet(UVal &u, const char *val){
+ u.STRING = (char *)HS_REALLOC(u.STRING, strlen(val) + 1);
+ strcpy(u.STRING, val);
+}
+
+///////////////////////////////
+
+char *SpanCharacteristic::getStringGeneric(UVal &val){
+ if(format>=FORMAT::STRING)
+ return val.STRING;
+
+ return NULL;
+}
+
+///////////////////////////////
+
+void SpanCharacteristic::setString(const char *val, boolean notify){
+
+ setValCheck();
+ uvSet(value,val);
+ setValFinish(notify);
+}
+
+///////////////////////////////
+
+size_t SpanCharacteristic::getDataGeneric(uint8_t *data, size_t len, UVal &val){
+ if(format0){
+ size_t olen;
+ mbedtls_base64_encode(NULL,0,&olen,NULL,len); // get length of string buffer needed (mbedtls includes the trailing null in this size)
+ value.STRING = (char *)HS_REALLOC(value.STRING,olen); // allocate sufficient size for storing value
+ mbedtls_base64_encode((uint8_t*)value.STRING,olen,&olen,data,len ); // encode data into string buf
+ } else {
+ value.STRING = (char *)HS_REALLOC(value.STRING,1); // allocate sufficient size for just trailing null character
+ *value.STRING ='\0';
+ }
+
+ setValFinish(notify);
+}
+
+///////////////////////////////
+
+size_t SpanCharacteristic::getTLVGeneric(TLV8 &tlv, UVal &val){
+
+ if(format tBuf(bufSize); // create fixed-size buffer to store decoded bytes
+ tlv.wipe(); // clear TLV completely
+
+ size_t nChars=strlen(val.STRING); // total characters to decode
+ uint8_t *p=(uint8_t *)val.STRING; // set pointer to beginning of value
+ const size_t decodeSize=bufSize/3*4; // number of characters to decode in each pass
+ int status=0;
+
+ while(nChars>0){
+ size_t olen;
+ size_t n=nChars0){
+ LOG0("\n*** WARNING: Can't unpack Characteristic::%s with getTLV(). TLV record is incomplete or corrupted!\n\n",hapName);
+ tlv.wipe();
+ return(0);
+ }
+return(tlv.pack_size());
+}
+
+///////////////////////////////
+
+void SpanCharacteristic::setTLV(TLV8 &tlv, boolean notify){
+
+ setValCheck();
+
+ const size_t bufSize=36; // maximum size of buffer to store packed TLV bytes before encoding directly into value; must be multiple of 3
+ size_t nBytes=tlv.pack_size(); // total size of packed TLV in bytes
+
+ if(nBytes>0){
+ size_t nChars;
+ mbedtls_base64_encode(NULL,0,&nChars,NULL,nBytes); // get length of string buffer needed (mbedtls includes the trailing null in this size)
+ value.STRING = (char *)HS_REALLOC(value.STRING,nChars); // allocate sufficient size for storing value
+ TempBuffer tBuf(bufSize); // create fixed-size buffer to store packed TLV bytes
+ tlv.pack_init(); // initialize TLV packing
+ uint8_t *p=(uint8_t *)value.STRING; // set pointer to beginning of value
+ while((nBytes=tlv.pack(tBuf,bufSize))>0){ // pack the next set of TLV bytes, up to a maximum of bufSize, into tBuf
+ size_t olen; // number of characters written (excludes null character)
+ mbedtls_base64_encode(p,nChars,&olen,tBuf,nBytes); // encode data directly into value
+ p+=olen; // advance pointer to null character
+ nChars-=olen; // subtract number of characters remaining
+ }
+ } else {
+ value.STRING = (char *)HS_REALLOC(value.STRING,1); // allocate sufficient size for just trailing null character
+ *value.STRING ='\0';
+ }
+
+ setValFinish(notify);
+}
+
+///////////////////////////////
+
+void SpanCharacteristic::setValCheck(){
+ if(updateFlag==1)
+ LOG0("\n*** WARNING: Attempt to set value of Characteristic::%s within update() while it is being simultaneously updated by Home App. This may cause device to become non-responsive!\n\n",hapName);
+}
+
+///////////////////////////////
+
+void SpanCharacteristic::setValFinish(boolean notify){
+
+ uvSet(newValue,value);
+ updateTime=homeSpan.snapTime;
+
+ if(notify){
+ if((perms&EV) && (updateFlag!=2)){ // only broadcast notification if EV permission is set AND update is NOT being done in context of write-response
+ SpanBuf sb; // create SpanBuf object
+ sb.characteristic=this; // set characteristic
+ sb.status=StatusCode::OK; // set status
+ 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){
+ nvs_set_str(homeSpan.charNVS,nvsKey,value.STRING); // store data
+ nvs_commit(homeSpan.charNVS);
+ }
+ }
+}
+
+///////////////////////////////
+
void SpanCharacteristic::printfAttributes(int flags){
const char permCodes[][7]={"pr","pw","ev","aa","tw","hd","wr"};
@@ -1943,9 +2085,11 @@ void SpanCharacteristic::printfAttributes(int flags){
if(flags&GET_AID)
hapOut << ",\"aid\":" << aid;
+
+ HAPClient *hc=&(*(homeSpan.currentClient));
if(flags&GET_EV)
- hapOut << ",\"ev\":" << (ev[HAPClient::conNum]?"true":"false");
+ hapOut << ",\"ev\":" << (evList.has(hc)?"true":"false");
if(flags&GET_STATUS)
hapOut << ",\"status\":0";
@@ -1971,7 +2115,12 @@ StatusCode SpanCharacteristic::loadUpdate(char *val, char *ev, boolean wr){
return(StatusCode::NotifyNotAllowed);
LOG1("Notification Request for aid=%u iid=%u: %s\n",aid,iid,evFlag?"true":"false");
- this->ev[HAPClient::conNum]=evFlag;
+ HAPClient *hc=&(*(homeSpan.currentClient));
+
+ if(evFlag)
+ evList.add(hc);
+ else
+ evList.remove(hc);
}
if(!val) // no request to update value
@@ -2066,6 +2215,57 @@ unsigned long SpanCharacteristic::timeVal(){
///////////////////////////////
+boolean SpanCharacteristic::updated(){
+
+ return(updateFlag>0);
+}
+
+///////////////////////////////
+
+uint32_t SpanCharacteristic::getIID(){
+
+ return(iid);
+}
+
+///////////////////////////////
+
+SpanCharacteristic *SpanCharacteristic::setPerms(uint8_t perms){
+ perms&=0x7F;
+ if(perms>0)
+ this->perms=perms;
+ return(this);
+}
+
+///////////////////////////////
+
+SpanCharacteristic *SpanCharacteristic::addPerms(uint8_t dPerms){
+ return(setPerms(perms|dPerms));
+}
+
+///////////////////////////////
+
+SpanCharacteristic *SpanCharacteristic::removePerms(uint8_t dPerms){
+ return(setPerms(perms&(~dPerms)));
+}
+
+///////////////////////////////
+
+SpanCharacteristic *SpanCharacteristic::setDescription(const char *c){
+ desc = (char *)HS_REALLOC(desc, strlen(c) + 1);
+ strcpy(desc, c);
+ return(this);
+}
+
+///////////////////////////////
+
+SpanCharacteristic *SpanCharacteristic::setUnit(const char *c){
+ unit = (char *)HS_REALLOC(unit, strlen(c) + 1);
+ strcpy(unit, c);
+ return(this);
+}
+
+///////////////////////////////
+
SpanCharacteristic *SpanCharacteristic::setValidValues(int n, ...){
String s="[";
@@ -2102,18 +2302,23 @@ SpanCharacteristic *SpanCharacteristic::setValidValues(int n, ...){
}
///////////////////////////////
-// SpanRange //
+
+boolean SpanCharacteristic::EVLIST::has(HAPClient *hc){
+ return(find_if(begin(), end(), [hc](const HAPClient *hcTemp){return(hc==hcTemp);}) != end());
+}
+
///////////////////////////////
-SpanRange::SpanRange(int min, int max, int step){
+void SpanCharacteristic::EVLIST::add(HAPClient *hc){
+ if(!has(hc))
+ push_back(hc);
+}
- if(homeSpan.Accessories.empty() || homeSpan.Accessories.back()->Services.empty() || homeSpan.Accessories.back()->Services.back()->Characteristics.empty() ){
- LOG0("\nFATAL ERROR! Can't create new SpanRange(%d,%d,%d) without a defined Characteristic ***\n",min,max,step);
- LOG0("\n=== PROGRAM HALTED ===");
- while(1);
- } else {
- homeSpan.Accessories.back()->Services.back()->Characteristics.back()->setRange(min,max,step);
- }
+///////////////////////////////
+
+void SpanCharacteristic::EVLIST::remove(HAPClient *hc){
+ auto it=remove_if(begin(), end(), [hc](const HAPClient *hcTemp){return(hc==hcTemp);});
+ erase(it,end());
}
///////////////////////////////
@@ -2183,8 +2388,6 @@ void SpanWebLog::init(uint16_t maxEntries, const char *serv, const char *tz, con
isEnabled=true;
}
log = (log_t *)HS_CALLOC(maxEntries,sizeof(log_t));
- if(timeServer)
- homeSpan.reserveSocketConnections(1);
}
///////////////////////////////
@@ -2251,7 +2454,6 @@ int SpanOTA::init(boolean _auth, boolean _safeLoad, const char *pwd){
enabled=true;
safeLoad=_safeLoad;
auth=_auth;
- homeSpan.reserveSocketConnections(1);
if(pwd==NULL)
return(0);
return(setPassword(pwd));
diff --git a/src/HomeSpan.h b/src/HomeSpan.h
index d5b656b..94ad261 100644
--- a/src/HomeSpan.h
+++ b/src/HomeSpan.h
@@ -109,10 +109,11 @@ struct Span;
struct SpanAccessory;
struct SpanService;
struct SpanCharacteristic;
-struct SpanRange;
struct SpanBuf;
struct SpanButton;
struct SpanUserCommand;
+
+struct HAPClient;
class Controller;
extern Span homeSpan;
@@ -190,35 +191,6 @@ struct SpanOTA{ // manages OTA process
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 //
//////////////////////////////////////
@@ -230,7 +202,6 @@ class Span{
friend class SpanCharacteristic;
friend class SpanUserCommand;
friend class SpanButton;
- friend class SpanRange;
friend class SpanWebLog;
friend class SpanOTA;
friend class Network;
@@ -267,8 +238,6 @@ class Span{
const char *defaultSetupCode=DEFAULT_SETUP_CODE; // Setup Code used for pairing
uint16_t autoOffLED=0; // automatic turn-off duration (in seconds) for Status LED
int logLevel=DEFAULT_LOG_LEVEL; // level for writing out log messages to serial monitor
- uint8_t maxConnections=CONFIG_LWIP_MAX_SOCKETS-2; // maximum number of allowed simultaneous HAP connections
- uint8_t requestedMaxCon=CONFIG_LWIP_MAX_SOCKETS-2; // requested maximum 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
uint16_t tcpPortNum=DEFAULT_TCP_PORT; // port for TCP communications between HomeKit and HomeSpan
char qrID[5]=""; // Setup ID used for pairing with QR Code
@@ -294,16 +263,17 @@ class Span{
SpanOTA spanOTA; // manages OTA process
SpanConfig hapConfig; // track configuration changes to the HAP Accessory database; used to increment the configuration number (c#) when changes found
- vector> Accessories; // vector of pointers to all Accessories
- vector> Loops; // vector of pointer to all Services that have over-ridden loop() methods
+
+ list> hapList; // linked-list of HAPClient structures containing HTTP client connections, parsing routines, and state variables
+ list>::iterator currentClient; // iterator to current client
+ vector> Accessories; // vector of pointers to all Accessories
+ vector> Loops; // vector of pointer to all Services that have over-ridden loop() methods
vector> Notifications; // vector of SpanBuf objects that store info for Characteristics that are updated with setVal() and require a Notification Event
- vector> PushButtons; // vector of pointer to all PushButtons
- unordered_map TimedWrites; // map of timed-write PIDs and Alarm Times (based on TTLs)
-
- unordered_map UserCommands; // map of pointers to all UserCommands
+ vector> PushButtons; // vector of pointer to all PushButtons
+ unordered_map TimedWrites; // map of timed-write PIDs and Alarm Times (based on TTLs)
+ unordered_map UserCommands; // map of pointers to all UserCommands
void pollTask(); // poll HAP Clients and process any new HAP requests
- int getFreeSlot(); // returns free HAPClient slot number. HAPClients slot keep track of each active HAPClient connection
void checkConnect(); // check WiFi connection; connect if needed
void commandMode(); // allows user to control and reset HomeSpan settings with the control button
void resetStatus(); // resets statusLED and calls statusCallback based on current HomeSpan status
@@ -316,8 +286,8 @@ class Span{
int updateCharacteristics(char *buf, SpanBuf *pObj); // parses PUT /characteristics JSON request 'buf into 'pObj' and updates referenced characteristics; returns 1 on success, 0 on fail
void printfAttributes(SpanBuf *pObj, int nObj); // writes SpanBuf objects to hapOut stream
boolean printfAttributes(char **ids, int numIDs, int flags); // writes accessory requested characteristic ids to hapOut stream - returns true if all characteristics are found and readable, else returns false
- void clearNotify(int slotNum); // set ev notification flags for connection 'slotNum' to false across all characteristics
- void printfNotify(SpanBuf *pObj, int nObj, int conNum); // writes notification JSON to hapOut stream based on SpanBuf objects and specified connection number
+ void clearNotify(HAPClient *hc); // clear all notifications related to specific client connection
+ void printfNotify(SpanBuf *pObj, int nObj, HAPClient *hc); // writes notification JSON to hapOut stream based on SpanBuf objects and specified connection
static boolean invalidUUID(const char *uuid){
int x=0;
@@ -369,7 +339,6 @@ class Span{
int getLogLevel(){return(logLevel);} // get Log Level
Span& setSerialInputDisable(boolean val){serialInputDisabled=val;return(*this);} // sets whether serial input is disabled (true) or enabled (false)
boolean getSerialInputDisable(){return(serialInputDisabled);} // returns true if serial input is disabled, or false if serial input in enabled
- Span& reserveSocketConnections(uint8_t n){maxConnections-=n;return(*this);} // reserves n socket connections *not* to be used for HAP
Span& setHostNameSuffix(const char *suffix){hostNameSuffix=suffix;return(*this);} // sets the hostName suffix to be used instead of the 6-byte AccessoryID
Span& setPortNum(uint16_t port){tcpPortNum=port;return(*this);} // sets the TCP port number to use for communications between HomeKit and HomeSpan
Span& setQRID(const char *id); // sets the Setup ID for optional pairing with a QR Code
@@ -427,10 +396,11 @@ class Span{
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>::const_iterator controllerListBegin();
- list>::const_iterator controllerListEnd();
-
- [[deprecated("Please use reserveSocketConnections(n) method instead.")]]
- void setMaxConnections(uint8_t n){requestedMaxCon=n;} // sets maximum number of simultaneous HAP connections
+ list>::const_iterator controllerListEnd();
+
+ [[deprecated("This function has been deprecated (it is not needed) and no longer does anything. Please remove from sketch to ensure backwards compatilibilty with future versions.")]]
+ Span& reserveSocketConnections(uint8_t n){return(*this);}
+
};
///////////////////////////////
@@ -441,7 +411,6 @@ class SpanAccessory{
friend class SpanService;
friend class SpanCharacteristic;
friend class SpanButton;
- friend class SpanRange;
uint32_t aid=0; // Accessory Instance ID (HAP Table 6-1)
uint32_t iidCount=0; // running count of iid to use for Services and Characteristics associated with this Accessory
@@ -466,7 +435,6 @@ class SpanService{
friend class Span;
friend class SpanAccessory;
friend class SpanCharacteristic;
- friend class SpanRange;
uint32_t iid=0; // Instance ID (HAP Table 6-2)
const char *type; // Service Type
@@ -520,6 +488,13 @@ class SpanCharacteristic{
STRING_t STRING = NULL;
};
+ class EVLIST : public vector>{ // vector of current connections that have subscribed to EV notifications for this Characteristic
+ public:
+ boolean has(HAPClient *hc); // returns true if pointer to connection hc is subscribed, else returns false
+ void add(HAPClient *hc); // adds connection hc as new subscriber, IF not already a subscriber
+ void remove(HAPClient *hc); // removes connection hc as a subscriber; okay to remove even if hc was not already a subscriber
+ };
+
uint32_t iid=0; // Instance ID (HAP Table 6-3)
HapChar *hapChar; // pointer to HAP Characteristic structure
const char *type; // Characteristic Type
@@ -535,7 +510,6 @@ class SpanCharacteristic{
boolean staticRange; // Flag that indicates whether Range is static and cannot be changed with setRange()
boolean customRange=false; // Flag for custom ranges
char *validValues=NULL; // Optional JSON array of valid values. Applicable only to uint8 Characteristics
- boolean *ev; // Characteristic Event Notify Enable (per-connection)
char *nvsKey=NULL; // key for NVS storage of Characteristic value
boolean isCustom; // flag to indicate this is a Custom Characteristic
boolean setRangeError=false; // flag to indicate attempt to set Range on Characteristic that does not support changes to Range
@@ -546,50 +520,16 @@ class SpanCharacteristic{
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
SpanService *service=NULL; // pointer to Service containing this Characteristic
-
+ EVLIST evList; // vector of current connections that have subscribed to EV notifications for this Characteristic
+
void printfAttributes(int flags); // writes Characteristic JSON to hapOut stream
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){
- char c[64];
- switch(format){
- case FORMAT::BOOL:
- return(String(u.BOOL));
- case FORMAT::INT:
- return(String(u.INT));
- case FORMAT::UINT8:
- return(String(u.UINT8));
- case FORMAT::UINT16:
- return(String(u.UINT16));
- case FORMAT::UINT32:
- return(String(u.UINT32));
- case FORMAT::UINT64:
- sprintf(c,"%llu",u.UINT64);
- return(String(c));
- case FORMAT::FLOAT:
- sprintf(c,"%g",u.FLOAT);
- return(String(c));
- case FORMAT::STRING:
- case FORMAT::DATA:
- case FORMAT::TLV_ENC:
- return(String("\"") + String(u.STRING) + String("\""));
- } // switch
- return(String()); // included to prevent compiler warnings
- }
+ String uvPrint(UVal &u); // returns "printable" String for any type of Characteristic
+
+ void uvSet(UVal &dest, UVal &src); // copies UVal src into UVal dest
+ void uvSet(UVal &u, const char *val); // copies string val into UVal u
- void uvSet(UVal &dest, UVal &src){
- if(format>=FORMAT::STRING)
- uvSet(dest,(const char *)src.STRING);
- else
- dest=src;
- }
-
- void uvSet(UVal &u, const char *val){
- u.STRING = (char *)HS_REALLOC(u.STRING, strlen(val) + 1);
- strcpy(u.STRING, val);
- }
-
- template void uvSet(UVal &u, T val){
+ template void uvSet(UVal &u, T val){ // copies numeric val into UVal u
switch(format){
case FORMAT::BOOL:
u.BOOL=(boolean)val;
@@ -619,7 +559,11 @@ class SpanCharacteristic{
} // switch
}
- template T uvGet(UVal &u){
+ char *getStringGeneric(UVal &val); // gets the specified UVal for string-based Characteristics
+ size_t getDataGeneric(uint8_t *data, size_t len, UVal &val); // gets the specified UVal for data-based Characteristics
+ size_t getTLVGeneric(TLV8 &tlv, UVal &val); // gets the specified UVal for tlv8-based Characteristics
+
+ template T uvGet(UVal &u){ // gets the specified UVal for numeric-based Characteristics
switch(format){
case FORMAT::BOOL:
@@ -643,10 +587,13 @@ class SpanCharacteristic{
}
return((T)0); // included to prevent compiler warnings
}
-
+
+ void setValCheck(); // initial check before setting value of any Characteristic
+ void setValFinish(boolean notify); // final processing after setting value of any Characteristic
+
protected:
- ~SpanCharacteristic(); // destructor
+ ~SpanCharacteristic(); // destructor
template void init(T val, boolean nvsStore, A min=0, B max=1){
@@ -688,169 +635,24 @@ class SpanCharacteristic{
public:
- void *operator new(size_t size){return(HS_MALLOC(size));} // override new operator to use PSRAM when available
- SpanCharacteristic(HapChar *hapChar, boolean isCustom=false); // constructor
+ SpanCharacteristic(HapChar *hapChar, boolean isCustom=false); // SpanCharacteristic constructor
+ void *operator new(size_t size){return(HS_MALLOC(size));} // override new operator to use PSRAM when available
- uint32_t getIID(){return(iid);} // returns IID of Characteristic
+ template T getVal(){return(uvGet(value));} // gets the value for numeric-based Characteristics
+ char *getString(){return(getStringGeneric(value));} // gets the value for string-based Characteristics
+ size_t getData(uint8_t *data, size_t len){return(getDataGeneric(data,len,value));} // gets the value for data-based Characteristics
+ size_t getTLV(TLV8 &tlv){return(getTLVGeneric(tlv,value));} // gets the value for tlv8-based Characteristics
- template T getVal(){
- return(uvGet(value));
- }
+ template T getNewVal(){return(uvGet(newValue));} // gets the newValue for numeric-based Characteristics
+ char *getNewString(){return(getStringGeneric(newValue));} // gets the newValue for string-based Characteristics
+ size_t getNewData(uint8_t *data, size_t len){return(getDataGeneric(data,len,newValue));} // gets the newValue for data-based Characteristics
+ size_t getNewTLV(TLV8 &tlv){return(getTLVGeneric(tlv,newValue));} // gets the newValue for tlv8-based Characteristics
- template T getNewVal(){
- return(uvGet(newValue));
- }
-
- char *getStringGeneric(UVal &val){
- if(format>=FORMAT::STRING)
- return val.STRING;
-
- return NULL;
- }
-
- char *getString(){return(getStringGeneric(value));}
- char *getNewString(){return(getStringGeneric(newValue));}
-
- void setString(const char *val, boolean notify=true){
-
- setValCheck();
- uvSet(value,val);
- setValFinish(notify);
- }
-
- size_t getDataGeneric(uint8_t *data, size_t len, UVal &val){
- if(format0){
- size_t olen;
- mbedtls_base64_encode(NULL,0,&olen,NULL,len); // get length of string buffer needed (mbedtls includes the trailing null in this size)
- value.STRING = (char *)HS_REALLOC(value.STRING,olen); // allocate sufficient size for storing value
- mbedtls_base64_encode((uint8_t*)value.STRING,olen,&olen,data,len ); // encode data into string buf
- } else {
- value.STRING = (char *)HS_REALLOC(value.STRING,1); // allocate sufficient size for just trailing null character
- *value.STRING ='\0';
- }
-
- setValFinish(notify);
- }
-
- size_t getTLVGeneric(TLV8 &tlv, UVal &val){
-
- if(format tBuf(bufSize); // create fixed-size buffer to store decoded bytes
- tlv.wipe(); // clear TLV completely
-
- size_t nChars=strlen(val.STRING); // total characters to decode
- uint8_t *p=(uint8_t *)val.STRING; // set pointer to beginning of value
- const size_t decodeSize=bufSize/3*4; // number of characters to decode in each pass
- int status=0;
-
- while(nChars>0){
- size_t olen;
- size_t n=nChars0){
- LOG0("\n*** WARNING: Can't unpack Characteristic::%s with getTLV(). TLV record is incomplete or corrupted!\n\n",hapName);
- tlv.wipe();
- return(0);
- }
- return(tlv.pack_size());
- }
-
- size_t getTLV(TLV8 &tlv){return(getTLVGeneric(tlv,value));}
- size_t getNewTLV(TLV8 &tlv){return(getTLVGeneric(tlv,newValue));}
-
- void setTLV(TLV8 &tlv, boolean notify=true){
-
- setValCheck();
-
- const size_t bufSize=36; // maximum size of buffer to store packed TLV bytes before encoding directly into value; must be multiple of 3
- size_t nBytes=tlv.pack_size(); // total size of packed TLV in bytes
-
- if(nBytes>0){
- size_t nChars;
- mbedtls_base64_encode(NULL,0,&nChars,NULL,nBytes); // get length of string buffer needed (mbedtls includes the trailing null in this size)
- value.STRING = (char *)HS_REALLOC(value.STRING,nChars); // allocate sufficient size for storing value
- TempBuffer tBuf(bufSize); // create fixed-size buffer to store packed TLV bytes
- tlv.pack_init(); // initialize TLV packing
- uint8_t *p=(uint8_t *)value.STRING; // set pointer to beginning of value
- while((nBytes=tlv.pack(tBuf,bufSize))>0){ // pack the next set of TLV bytes, up to a maximum of bufSize, into tBuf
- size_t olen; // number of characters written (excludes null character)
- mbedtls_base64_encode(p,nChars,&olen,tBuf,nBytes); // encode data directly into value
- p+=olen; // advance pointer to null character
- nChars-=olen; // subtract number of characters remaining
- }
- } else {
- value.STRING = (char *)HS_REALLOC(value.STRING,1); // allocate sufficient size for just trailing null character
- *value.STRING ='\0';
- }
-
- setValFinish(notify);
- }
-
- void setValCheck(){
- if(updateFlag==1)
- LOG0("\n*** WARNING: Attempt to set value of Characteristic::%s within update() while it is being simultaneously updated by Home App. This may cause device to become non-responsive!\n\n",hapName);
- }
-
- void setValFinish(boolean notify){
-
- uvSet(newValue,value);
- updateTime=homeSpan.snapTime;
-
- if(notify){
- if((perms&EV) && (updateFlag!=2)){ // only broadcast notification if EV permission is set AND update is NOT being done in context of write-response
- SpanBuf sb; // create SpanBuf object
- sb.characteristic=this; // set characteristic
- sb.status=StatusCode::OK; // set status
- 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
- }
+ void setString(const char *val, boolean notify=true); // sets the value and newValue for string-based Characteristic
+ void setData(uint8_t *data, size_t len, boolean notify=true); // sets the value and newValue for data-based Characteristic
+ void setTLV(TLV8 &tlv, boolean notify=true); // sets the value and newValue for tlv8-based Characteristic
- if(nvsKey){
- nvs_set_str(homeSpan.charNVS,nvsKey,value.STRING); // store data
- nvs_commit(homeSpan.charNVS);
- }
- }
- }
-
- template void setVal(T val, boolean notify=true){
+ template void setVal(T val, boolean notify=true){ // sets the value and newValue for numeric-based Characteristics
setValCheck();
@@ -880,14 +682,20 @@ class SpanCharacteristic{
}
}
- } // setVal()
+ } // setVal()
+
+ boolean updated(); // returns true within update() if Characteristic was updated by Home App
+ unsigned long timeVal(); // returns time elapsed (in millis) since value was last updated, either by Home App or by using setVal()
+ uint32_t getIID(); // returns IID of Characteristic
- 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, 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 *setPerms(uint8_t perms); // sets permissions of a Characteristic
+ SpanCharacteristic *addPerms(uint8_t dPerms); // add permissions of a Characteristic
+ SpanCharacteristic *removePerms(uint8_t dPerms); // removes permissions of a Characteristic
+ SpanCharacteristic *setDescription(const char *c); // sets description of a Characteristic
+ SpanCharacteristic *setUnit(const char *c); // set unit of a Characteristic
+ SpanCharacteristic *setValidValues(int n, ...); // sets a list of 'n' valid values allowed for a Characteristic - only applicable if format=INT, UINT8, UINT16, or UINT32
- template SpanCharacteristic *setRange(A min, B max, S step=0){
+ template SpanCharacteristic *setRange(A min, B max, S step=0){ // sets the allowed range of a Characteristic
if(!staticRange){
uvSet(minValue,min);
@@ -900,40 +708,6 @@ class SpanCharacteristic{
return(this);
} // setRange()
-
- SpanCharacteristic *setPerms(uint8_t perms){
- perms&=0x7F;
- if(perms>0)
- this->perms=perms;
- return(this);
- }
-
- SpanCharacteristic *addPerms(uint8_t dPerms){
- return(setPerms(perms|dPerms));
- }
-
- SpanCharacteristic *removePerms(uint8_t dPerms){
- return(setPerms(perms&(~dPerms)));
- }
-
- SpanCharacteristic *setDescription(const char *c){
- desc = (char *)HS_REALLOC(desc, strlen(c) + 1);
- strcpy(desc, c);
- return(this);
- }
-
- SpanCharacteristic *setUnit(const char *c){
- unit = (char *)HS_REALLOC(unit, strlen(c) + 1);
- strcpy(unit, c);
- return(this);
- }
-
-};
-
-///////////////////////////////
-
-struct [[deprecated("Please use Characteristic::setRange() method instead.")]] SpanRange{
- SpanRange(int min, int max, int step);
};
///////////////////////////////
diff --git a/src/PSRAM.h b/src/PSRAM.h
index dd9e03a..d5b0cb6 100644
--- a/src/PSRAM.h
+++ b/src/PSRAM.h
@@ -45,11 +45,14 @@ template
struct Mallocator {
typedef T value_type;
Mallocator() = default;
- template constexpr Mallocator(const Mallocator&) noexcept {}
+ template constexpr Mallocator(const Mallocator&) {}
[[nodiscard]] T* allocate(std::size_t n) {
- if(n > std::size_t(-1) / sizeof(T)) throw std::bad_alloc();
- if(auto p = static_cast(HS_MALLOC(n*sizeof(T)))) return p;
- throw std::bad_alloc();
+ auto p = static_cast(HS_MALLOC(n*sizeof(T)));
+ if(p==NULL){
+ Serial.printf("\n\n*** FATAL ERROR: Requested allocation of %d bytes failed. Program Halting.\n\n",n*sizeof(T));
+ while(1);
+ }
+ return p;
}
void deallocate(T* p, std::size_t) noexcept { std::free(p); }
};
diff --git a/src/src.ino b/src/src.ino
index 13e2f91..98d9733 100644
--- a/src/src.ino
+++ b/src/src.ino
@@ -30,8 +30,13 @@
void setup() {
Serial.begin(115200);
+
+ homeSpan.setLogLevel(2);
+ homeSpan.enableWebLog();
- homeSpan.begin(Category::Lighting,"HomeSpan Light");
+ homeSpan.begin(Category::Lighting,"HomeSpan LightBulb");
+
+ new SpanUserCommand('D', " - disconnect WiFi", [](const char *buf){WiFi.disconnect();});
new SpanAccessory();
new Service::AccessoryInformation();
@@ -39,8 +44,6 @@ void setup() {
new Service::LightBulb();
new Characteristic::On();
-// new SpanUserCommand('k',"- list controllers",list_controllers);
- homeSpan.setControllerCallback(list_controllers);
}
@@ -52,17 +55,3 @@ 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");
- }
-}
diff --git a/tools/makeServices b/tools/makeServices
index 279262b..8bc6c5d 100755
--- a/tools/makeServices
+++ b/tools/makeServices
@@ -54,6 +54,8 @@ BEGIN {
uuid[char]=x[3]
perms[char]=x[4]
format[char]=tolower(x[5])
+ if(format[char]=="tlv_enc")
+ format[char]="tlv8"
static[char]=x[6]
}