Created new version of ProgrammableHub with Dynamic Changes
This commit is contained in:
parent
df2154d048
commit
999eb45e8a
|
|
@ -1,38 +0,0 @@
|
|||
|
||||
//////////////////////////////////
|
||||
// DEVICE-SPECIFIC SERVICES //
|
||||
//////////////////////////////////
|
||||
|
||||
struct DEV_Identify : Service::AccessoryInformation {
|
||||
|
||||
int nBlinks; // number of times to blink built-in LED in identify routine
|
||||
SpanCharacteristic *identify; // reference to the Identify Characteristic
|
||||
|
||||
DEV_Identify(const char *name, const char *manu, const char *sn, const char *model, const char *version, int nBlinks) : Service::AccessoryInformation(){
|
||||
|
||||
new Characteristic::Name(name); // create all the required Characteristics with values set based on above arguments
|
||||
new Characteristic::Manufacturer(manu);
|
||||
new Characteristic::SerialNumber(sn);
|
||||
new Characteristic::Model(model);
|
||||
new Characteristic::FirmwareRevision(version);
|
||||
identify=new Characteristic::Identify(); // store a reference to the Identify Characteristic for use below
|
||||
|
||||
this->nBlinks=nBlinks; // store the number of times to blink the LED
|
||||
|
||||
pinMode(homeSpan.getStatusPin(),OUTPUT); // make sure LED is set for output
|
||||
}
|
||||
|
||||
boolean update(){
|
||||
|
||||
for(int i=0;i<nBlinks;i++){
|
||||
digitalWrite(homeSpan.getStatusPin(),LOW);
|
||||
delay(250);
|
||||
digitalWrite(homeSpan.getStatusPin(),HIGH);
|
||||
delay(250);
|
||||
}
|
||||
|
||||
return(true); // return true
|
||||
|
||||
} // update
|
||||
|
||||
};
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
|
||||
////////////////////////////////////
|
||||
// DEVICE-SPECIFIC LED SERVICES //
|
||||
////////////////////////////////////
|
||||
|
||||
#include "extras/PwmPin.h" // library of various PWM functions
|
||||
|
||||
////////////////////////////////////
|
||||
|
||||
struct DEV_GenericLED : Service::LightBulb { // Generic LED
|
||||
|
||||
// This version of the LED Service allows for dimmable and non-dimmable devices. Status of both the
|
||||
// power state and the brightness of the LED are stored in NVS for restoration if the device reboots.
|
||||
|
||||
LedPin *LED; // reference to an LedPin
|
||||
SpanCharacteristic *power; // reference to the On Characteristic
|
||||
SpanCharacteristic *level; // reference to the Brightness Characteristic
|
||||
boolean isDimmable; // flag to indicate whether light is dimmable
|
||||
|
||||
DEV_GenericLED(int ledPin, uint8_t dimmable=0) : Service::LightBulb(){
|
||||
|
||||
power=new Characteristic::On(0,true); // second argument is true, so the value of the On Characteristic (initially set to 0) will be saved in NVS
|
||||
isDimmable=dimmable;
|
||||
|
||||
if(isDimmable){
|
||||
level=new Characteristic::Brightness(50,true); // second argument is true, so the value of the Brightness Characteristic (initially set to 50) will be saved in NVS
|
||||
level->setRange(5,100,1); // sets the range of the Brightness to be from a min of 5%, to a max of 100%, in steps of 1%
|
||||
}
|
||||
|
||||
this->LED=new LedPin(ledPin); // configures a PWM LED for output to pin number "ledPin"
|
||||
|
||||
Serial.printf("Configuring LED: Pin=%d %s\n",LED->getPin(),isDimmable?"(Dimmable)":""); // initialization message
|
||||
|
||||
LED->set(power->getVal()*(isDimmable?(level->getVal()):100)); // set the LED to its initial state at startup.
|
||||
|
||||
} // end constructor
|
||||
|
||||
boolean update(){ // update() method
|
||||
|
||||
LOG1("Updating LED on pin=");
|
||||
LOG1(LED->getPin());
|
||||
LOG1(": Current Power=");
|
||||
LOG1(power->getVal()?"true":"false");
|
||||
if(isDimmable){
|
||||
LOG1(" Current Brightness=");
|
||||
LOG1(level->getVal());
|
||||
}
|
||||
|
||||
if(power->updated()){
|
||||
LOG1(" New Power=");
|
||||
LOG1(power->getNewVal()?"true":"false");
|
||||
}
|
||||
|
||||
if(isDimmable && level->updated()){
|
||||
LOG1(" New Brightness=");
|
||||
LOG1(level->getNewVal());
|
||||
}
|
||||
|
||||
LOG1("\n");
|
||||
|
||||
LED->set(power->getNewVal()*(isDimmable?(level->getNewVal()):100)); // update the physical LED to reflect the new values
|
||||
|
||||
return(true); // return true
|
||||
|
||||
} // update
|
||||
|
||||
};
|
||||
|
||||
//////////////////////////////////
|
||||
|
|
@ -25,242 +25,363 @@
|
|||
*
|
||||
********************************************************************************/
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// HomeSpan: A HomeKit implementation for the ESP32 //
|
||||
// ------------------------------------------------ //
|
||||
// //
|
||||
// Demonstrates how to implement a Web Server alongside //
|
||||
// of HomeSpan to create a Programmable Hub serving up to //
|
||||
// 16 Configurable Lights. //
|
||||
// 24 Configurable Lights. Allows for dynamic changes //
|
||||
// to Accessories without needing to reboot //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "HomeSpan.h"
|
||||
#include "DEV_LED.h"
|
||||
#include "DEV_Identify.h"
|
||||
|
||||
#include <WebServer.h> // include WebServer library
|
||||
|
||||
WebServer webServer(80); // create WebServer on port 80
|
||||
|
||||
#define NLIGHTS 16 // maximum number of Lightbulb Accessories
|
||||
#define MAX_LIGHTS 24
|
||||
#define MAX_NAME_LENGTH 32
|
||||
#define HUB_NAME "lighthub"
|
||||
|
||||
uint8_t pinList[]={0,4,5,12,14,15,16,17,18,19,22,23,25,26,27,32,33}; // list of allowed pins
|
||||
char lightNames[NLIGHTS][9]; // storage for default light names
|
||||
enum colorType_t : uint8_t {
|
||||
NO_COLOR,
|
||||
TEMPERATURE_ONLY,
|
||||
FULL_RGB
|
||||
};
|
||||
|
||||
nvs_handle lightNVS; // handle for NVS storage
|
||||
uint32_t aidStore=2; // keep track of unique AID numbers - start with AID=2
|
||||
|
||||
struct { // structure to store pin numbers and dimmable flag
|
||||
uint8_t pin=0;
|
||||
uint8_t dimmable=0;
|
||||
} lightData[NLIGHTS];
|
||||
struct lightData_t {
|
||||
char name[MAX_NAME_LENGTH+1]="";
|
||||
uint32_t aid=0;
|
||||
boolean isDimmable:1;
|
||||
colorType_t colorType:2;
|
||||
} lightData[MAX_LIGHTS];
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
nvs_handle savedData;
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
size_t len;
|
||||
nvs_open("SAVED_DATA",NVS_READWRITE,&savedData); // open a new namespace called SAVED_DATA in the NVS
|
||||
|
||||
if(!nvs_get_blob(savedData,"LIGHTDATA",NULL,&len)) // if LIGHTDATA data found
|
||||
nvs_get_blob(savedData,"LIGHTDATA",&lightData,&len); // retrieve data
|
||||
|
||||
nvs_get_u32(savedData,"AID",&aidStore); // get AID, if it exists
|
||||
|
||||
homeSpan.setLogLevel(1);
|
||||
|
||||
homeSpan.setHostNameSuffix(""); // use null string for suffix (rather than the HomeSpan device ID)
|
||||
homeSpan.setPortNum(1201); // change port number for HomeSpan so we can use port 80 for the Web Server
|
||||
homeSpan.enableOTA(); // enable OTA updates
|
||||
homeSpan.setMaxConnections(5); // reduce max connection to 5 (default is 8) since WebServer and a connecting client will need 2, and OTA needs 1
|
||||
homeSpan.setWifiCallback(setupWeb); // need to start Web Server after WiFi is established
|
||||
|
||||
homeSpan.enableWebLog(50,"pool.ntp.org","CST6CDT");
|
||||
homeSpan.begin(Category::Lighting,"HomeSpan Light Hub",HUB_NAME);
|
||||
|
||||
homeSpan.begin(Category::Bridges,"HomeSpan Light Hub","homespanhub");
|
||||
new SpanAccessory(1); // here we specified the AID=1 for clarity (it would default to 1 anyway if left blank)
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Model("HomeSpan Programmable Hub");
|
||||
|
||||
for(int i=0;i<NLIGHTS;i++) // create default names for each light
|
||||
sprintf(lightNames[i],"Light-%02d",i+1);
|
||||
|
||||
size_t len;
|
||||
nvs_open("LIGHTS",NVS_READWRITE,&lightNVS); // open LIGHTS NVS
|
||||
if(!nvs_get_blob(lightNVS,"LIGHTDATA",NULL,&len)) // if data found
|
||||
nvs_get_blob(lightNVS,"LIGHTDATA",&lightData,&len); // retrieve data
|
||||
|
||||
// Create Bridge Accessory
|
||||
|
||||
new SpanAccessory(1);
|
||||
new DEV_Identify("HomeSpan Hub","HomeSpan","LS-123","Light Server","1.0",3);
|
||||
new Service::HAPProtocolInformation();
|
||||
new Characteristic::Version("1.1.0");
|
||||
|
||||
// Dynamically create a new Accessory for each Light defined
|
||||
|
||||
for(int i=0;i<NLIGHTS;i++){
|
||||
if(lightData[i].pin>0){
|
||||
new SpanAccessory(i+2);
|
||||
new DEV_Identify(lightNames[i],"HomeSpan",lightNames[i],lightData[i].dimmable?"Dimmable":"Not Dimmable","1.0",0);
|
||||
new DEV_GenericLED(lightData[i].pin,lightData[i].dimmable);
|
||||
}
|
||||
for(int i=0;i<MAX_LIGHTS;i++){ // create Light Accessories based on saved data
|
||||
if(lightData[i].aid)
|
||||
addLight(i);
|
||||
}
|
||||
|
||||
new SpanUserCommand('a',"<name> - add non-dimmable light accessory using name=<name>",[](const char *c){addLight(c+1,false,NO_COLOR);});
|
||||
new SpanUserCommand('A',"<name> - add dimmable light accessory using name=<name>",[](const char *c){addLight(c+1,true,NO_COLOR);});
|
||||
new SpanUserCommand('t',"<name> - add non-dimmable light accessory with color-temperature control using name=<name>",[](const char *c){addLight(c+1,false,TEMPERATURE_ONLY);});
|
||||
new SpanUserCommand('T',"<name> - add dimmable light accessory with color-temperature control using name=<name>",[](const char *c){addLight(c+1,true,TEMPERATURE_ONLY);});
|
||||
new SpanUserCommand('r',"<name> - add non-dimmable light accessory with full RGB color control using name=<name>",[](const char *c){addLight(c+1,false,FULL_RGB);});
|
||||
new SpanUserCommand('R',"<name> - add dimmable light accessory with full RGB color control using name=<name>",[](const char *c){addLight(c+1,true,FULL_RGB);});
|
||||
|
||||
new SpanUserCommand('l'," - list all light accessories",listAccessories);
|
||||
new SpanUserCommand('d',"<index> - delete a light accessory with index=<index>",[](const char *buf){deleteAccessory(atoi(buf+1));});
|
||||
new SpanUserCommand('D'," - delete ALL light accessories",deleteAllAccessories);
|
||||
new SpanUserCommand('u',"- update accessories database",updateAccessories);
|
||||
|
||||
} // end of setup()
|
||||
|
||||
//////////////////////////////////////
|
||||
///////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
homeSpan.poll();
|
||||
webServer.handleClient(); // need to process webServer once each loop
|
||||
webServer.handleClient(); // handle incoming web server traffic
|
||||
}
|
||||
|
||||
} // end of loop()
|
||||
///////////////////////////
|
||||
|
||||
//////////////////////////////////////
|
||||
void addLight(int index){
|
||||
|
||||
Serial.printf("Adding Light Accessory: Name='%s' Dimmable=%s Color=%s\n",
|
||||
lightData[index].name,lightData[index].isDimmable?"YES":"NO",lightData[index].colorType==NO_COLOR?"NONE":(lightData[index].colorType==TEMPERATURE_ONLY?"TEMPERATURE_ONLY":"FULL_RGB"));
|
||||
|
||||
new SpanAccessory(lightData[index].aid);
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name(lightData[index].name);
|
||||
char sNum[32];
|
||||
sprintf(sNum,"Light-%02d",index);
|
||||
new Characteristic::SerialNumber(sNum);
|
||||
|
||||
new Service::LightBulb();
|
||||
new Characteristic::On(0,true);
|
||||
if(lightData[index].isDimmable)
|
||||
new Characteristic::Brightness(100,true);
|
||||
if(lightData[index].colorType==TEMPERATURE_ONLY)
|
||||
new Characteristic::ColorTemperature(200,true);
|
||||
if(lightData[index].colorType==FULL_RGB){
|
||||
new Characteristic::Hue(0,true);
|
||||
new Characteristic::Saturation(0,true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
|
||||
int addLight(const char *name, boolean isDimmable, colorType_t colorType){
|
||||
|
||||
int index=0;
|
||||
for(index=0;index<MAX_LIGHTS && lightData[index].aid;index++);
|
||||
|
||||
if(index==MAX_LIGHTS){
|
||||
Serial.printf("Can't add Light Accessory - maximum number of %d are already defined.\n",MAX_LIGHTS);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
int n=strncpy_trim(lightData[index].name,name,sizeof(lightData[index].name));
|
||||
|
||||
if(n==1){
|
||||
Serial.printf("Can't add Light Accessory without a name specified.\n");
|
||||
return(-1);
|
||||
}
|
||||
|
||||
if(n>sizeof(lightData[index].name))
|
||||
Serial.printf("Warning - name trimmed to max length of %d characters.\n",MAX_NAME_LENGTH);
|
||||
|
||||
lightData[index].isDimmable=isDimmable;
|
||||
lightData[index].colorType=colorType;
|
||||
lightData[index].aid=aidStore++;
|
||||
|
||||
nvs_set_blob(savedData,"LIGHTDATA",&lightData,sizeof(lightData)); // update data in the NVS
|
||||
nvs_set_u32(savedData,"AID",aidStore);
|
||||
nvs_commit(savedData);
|
||||
|
||||
addLight(index);
|
||||
return(index);
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
|
||||
size_t strncpy_trim(char *dest, const char *src, size_t dSize){
|
||||
|
||||
while(*src==' ') // skip over any leading spaces
|
||||
src++;
|
||||
|
||||
size_t sLen=strlen(src); // string length of src after skipping over leading spaces
|
||||
while(sLen>0 && src[sLen-1]==' ') // shorten length to remove trailing spaces
|
||||
sLen--;
|
||||
|
||||
size_t sSize=sLen+1; // add room for null terminator
|
||||
|
||||
if(dest!=NULL)
|
||||
*stpncpy(dest,src,(dSize<sSize?dSize:sSize)-1)='\0';
|
||||
|
||||
return(sSize); // return total size needed for entire trimmed string, including null terminator
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
|
||||
void deleteAccessory(int index){
|
||||
|
||||
if(index<0 || index>=MAX_LIGHTS){
|
||||
Serial.printf("Invalid Light Accessory index - must be between 0 and %d.\n",MAX_LIGHTS-1);
|
||||
return;
|
||||
}
|
||||
|
||||
if(homeSpan.deleteAccessory(lightData[index].aid)){ // if deleteAccessory() is true, a match has been found
|
||||
Serial.printf("Deleting Light Accessory: Name='%s'\n",lightData[index].name);
|
||||
|
||||
lightData[index].aid=0;
|
||||
nvs_set_blob(savedData,"LIGHTDATA",&lightData,sizeof(lightData)); // update data in the NVS
|
||||
nvs_commit(savedData);
|
||||
|
||||
} else {
|
||||
Serial.printf("Nothing to delete - there is no Light Accessory at index=%d.\n",index);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
|
||||
void deleteAllAccessories(const char *buf){
|
||||
|
||||
for(int i=0;i<MAX_LIGHTS;i++){
|
||||
homeSpan.deleteAccessory(lightData[i].aid);
|
||||
lightData[i].aid=0;
|
||||
}
|
||||
|
||||
nvs_set_blob(savedData,"LIGHTDATA",&lightData,sizeof(lightData)); // update data in the NVS
|
||||
nvs_commit(savedData);
|
||||
|
||||
Serial.printf("All Light Accessories deleted!\n");
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
|
||||
void updateAccessories(const char *buf){
|
||||
|
||||
if(homeSpan.updateDatabase())
|
||||
Serial.printf("Accessories Database updated. New configuration number broadcasted...\n");
|
||||
else
|
||||
Serial.printf("Nothing to update - no changes were made!\n");
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
|
||||
void listAccessories(const char *buf){
|
||||
|
||||
Serial.printf("\nIndex Dimmable Color Name\n");
|
||||
Serial.printf("----- -------- ----- ");
|
||||
|
||||
for(int i=0;i<MAX_NAME_LENGTH;i++)
|
||||
Serial.printf("-");
|
||||
Serial.printf("\n");
|
||||
for(int i=0;i<MAX_LIGHTS;i++){
|
||||
if(lightData[i].aid)
|
||||
Serial.printf("%5d %8s %5s %-s\n",i,lightData[i].isDimmable?"YES":"NO",lightData[i].colorType==NO_COLOR?"NONE":(lightData[i].colorType==TEMPERATURE_ONLY?"TEMP":"RGB"),lightData[i].name);
|
||||
}
|
||||
Serial.printf("\n");
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
|
||||
void setupWeb(){
|
||||
Serial.print("Starting Light Server Hub...\n\n");
|
||||
|
||||
Serial.printf("Starting Light Server Hub at %s.local\n\n",HUB_NAME);
|
||||
webServer.begin();
|
||||
|
||||
// Create web routines inline
|
||||
webServer.on("/", []() {
|
||||
|
||||
webServer.on("/addForm", []() {
|
||||
String content="<html><body><h3>Add New Light Accessory</h3>";
|
||||
String response = "<html><head><title>HomeSpan Programmable Light Hub</title>";
|
||||
response += "<style>table, th, td {border: 1px solid black; border-collapse: collapse;} th, td { padding: 5px; text-align: center; } </style></head>\n";
|
||||
response += "<body><h2>HomeSpan Lights</h2>";
|
||||
response += "<form action='/addLight' method='get'>";
|
||||
response += "<table><tr><th style='text-align:left;'>Accessory</th><th>Dim?</th><th>Color Control</th><th>Action</th></tr>";
|
||||
|
||||
content+="<form action='/addLight' method='get'>";
|
||||
content+="<label for='lightName'>Light Name:</label>";
|
||||
content+="<input type='text' id='lightName' name='lightName' maxlength='24' required>";
|
||||
int openSlots=MAX_LIGHTS;
|
||||
|
||||
content+="<p>Select Light Features:</p>";
|
||||
content+="<input type='radio' id='noColor' name='colorControl' value='0' checked>";
|
||||
content+="<label for='noColor'>No Color</label><br>";
|
||||
content+="<input type='radio' id='colorTemp' name='colorControl' value='1'>";
|
||||
content+="<label for='colorTemp'>Color Temperature Only</label><br>";
|
||||
content+="<input type='radio' id='fullColor' name='colorControl' value='2'>";
|
||||
content+="<label for='fullColor'>Full RGB Color</label><br><br>";
|
||||
for(int i=0;i<MAX_LIGHTS;i++){
|
||||
if(lightData[i].aid){
|
||||
response += "<tr><td style='text-align:left;'>" + String(lightData[i].name) + "</td>";
|
||||
response += "<td><input type='checkbox' disabled " + String(lightData[i].isDimmable?"checked>":">") + "</td>";
|
||||
response += "<td><input type='radio' disabled " + String(lightData[i].colorType==NO_COLOR?"checked>":">") + " NONE ";
|
||||
response += "<input type='radio' disabled " + String(lightData[i].colorType==TEMPERATURE_ONLY?"checked>":">") + " TEMP ONLY ";
|
||||
response += "<input type='radio' disabled " + String(lightData[i].colorType==FULL_RGB?"checked>":">") + " FULL COLOR </td>";
|
||||
response += "<td><button type='button' onclick=\"document.location='/deleteLight?index=" + String(i) + "'\">Delete Light</button></td>";
|
||||
response += "</tr>";
|
||||
openSlots--;
|
||||
}
|
||||
}
|
||||
|
||||
content+="<input type='checkbox' id='isDimmable' name='isDimmable' value='4'>";
|
||||
content+="<label for='isDimmable'>Dimmable</label><br><br>";
|
||||
response += "<tr><td style='text-align:left;'><input type='text' name='name' required placeholder='Type accessory name here...' size='"
|
||||
+ String(MAX_NAME_LENGTH) + "' maxlength='" + String(MAX_NAME_LENGTH) + "'></td>";
|
||||
response += "<td><input type='checkbox' name='isDimmable'></td>";
|
||||
response += "<td><input type='radio' checked name='colorType' for='no_color' value='" + String(NO_COLOR) + "'><label for='no_color'> NONE </label>";
|
||||
response += "<input type='radio' name='colorType' for='temp_only' value='" + String(TEMPERATURE_ONLY) + "'><label for='temp_only'> TEMP ONLY </label>";
|
||||
response += "<input type='radio' name='colorType' for='full_rgb' value='" + String(FULL_RGB) + "'><label for='full_rgb'> FULL COLOR </label></td>";
|
||||
response += "<td><input type='submit' value='Add Light'" + String(openSlots?"":" disabled") + "></td>";
|
||||
response += "</tr>";
|
||||
|
||||
content+="<input type='submit' value='Add'>";
|
||||
content+="<button type='button' onclick=\"document.location='/'\">Cancel</button>";
|
||||
response += "</table>";
|
||||
response += "</form>";
|
||||
|
||||
if(!openSlots)
|
||||
response += "<p>Can't add any more Light Accessories. Max="+ String(MAX_LIGHTS) + "</p>";
|
||||
|
||||
content+="</form>";
|
||||
content+="</body></html>";
|
||||
response += "<p>Press here to delete ALL Light Accessories: <button type='button' onclick=\"document.location='/deleteAll'\">Delete All Lights</button></p>";
|
||||
response += "<p>Press here to update the Home App when finished making changes: <button type='button' onclick=\"document.location='/update'\">Upddate HomeKit</button></p>";
|
||||
|
||||
webServer.send(200, "text/html", content);
|
||||
response += "</body></html>";
|
||||
webServer.send(200, "text/html", response);
|
||||
|
||||
});
|
||||
|
||||
webServer.on("/deleteLight", []() {
|
||||
|
||||
int index=atoi(webServer.arg(0).c_str());
|
||||
|
||||
String response = "<html><head><title>HomeSpan Programmable Light Hub</title><meta http-equiv='refresh' content = '3; url=/'/></head>";
|
||||
response += "<body>Deleting Light Accessory '" + String(lightData[index].name) + "'...</body></html>";
|
||||
|
||||
deleteAccessory(index);
|
||||
|
||||
webServer.send(200, "text/html", response);
|
||||
|
||||
});
|
||||
|
||||
webServer.on("/deleteAll", []() {
|
||||
|
||||
String response = "<html><head><title>HomeSpan Programmable Light Hub</title><meta http-equiv='refresh' content = '3; url=/'/></head>";
|
||||
response += "<body>Deleting All Light Accessories...</body></html>";
|
||||
|
||||
webServer.send(200, "text/html", response);
|
||||
deleteAllAccessories("");
|
||||
|
||||
});
|
||||
|
||||
webServer.on("/update", []() {
|
||||
|
||||
String response = "<html><head><title>HomeSpan Programmable Light Hub</title><meta http-equiv='refresh' content = '3; url=/'/></head><body>";
|
||||
|
||||
if(homeSpan.updateDatabase())
|
||||
response += "Accessories Database updated. New configuration number broadcasted...";
|
||||
else
|
||||
response += "Nothing to update - no changes were made...";
|
||||
|
||||
response += "...</body></html>";
|
||||
|
||||
webServer.send(200, "text/html", response);
|
||||
|
||||
});
|
||||
|
||||
webServer.on("/addLight", []() {
|
||||
|
||||
char lightName[32];
|
||||
uint8_t flags=0;
|
||||
|
||||
String sColorControl("colorControl");
|
||||
String sIsDimmable("isDimmable");
|
||||
String sLightName("lightName");
|
||||
colorType_t colorType=NO_COLOR;
|
||||
boolean isDimmable=false;
|
||||
int iName=-1;
|
||||
|
||||
for(int i=0;i<webServer.args();i++){
|
||||
if(!webServer.argName(i).compareTo(sColorControl) || !webServer.argName(i).compareTo(sIsDimmable))
|
||||
flags+=webServer.arg(i).toInt();
|
||||
else if(!webServer.argName(i).compareTo(sLightName)){
|
||||
webServer.arg(i).toCharArray(lightName,sizeof(lightName)-1);
|
||||
}
|
||||
if(!webServer.argName(i).compareTo(String("colorType")))
|
||||
colorType=(colorType_t)webServer.arg(i).toInt();
|
||||
else if(!webServer.argName(i).compareTo(String("isDimmable")))
|
||||
isDimmable=true;
|
||||
else if(!webServer.argName(i).compareTo(String("name")))
|
||||
iName=i;
|
||||
}
|
||||
|
||||
Serial.printf("'%s' %d\n",lightName,flags);
|
||||
String response = "<html><head><title>HomeSpan Programmable Light Hub</title><meta http-equiv='refresh' content = '3; url=/'/></head><body>";
|
||||
|
||||
String content="<html><body><h3>Light Added!</h3>";
|
||||
content+="</body></html>";
|
||||
content+="<button type='button' onclick=\"document.location='/addForm'\">Add Another</button>";
|
||||
content+="<button type='button' onclick=\"document.location='/'\">Done</button>";
|
||||
content+="</body></html>";
|
||||
if(iName!=-1){
|
||||
int index=addLight(webServer.arg(iName).c_str(),isDimmable,colorType);
|
||||
response += "Adding Light Accessory '" + String(lightData[index].name) + "'";
|
||||
} else
|
||||
response += "Error - bad URL request";
|
||||
|
||||
webServer.send(200, "text/html", content);
|
||||
response += "...</body></html>";
|
||||
|
||||
webServer.send(200, "text/html", response);
|
||||
|
||||
});
|
||||
|
||||
webServer.on("/", []() {
|
||||
|
||||
String content = "<html><body><form action='/configure' method='POST'><b>HomeSpan Light Server Hub Configuration</b><br><br>";
|
||||
content += "Select pins and check box if dimmable:<br><br>";
|
||||
}
|
||||
|
||||
for(int i=0;i<NLIGHTS;i++){
|
||||
content += "<span style=\"color:";
|
||||
if(lightData[i].pin==0)
|
||||
content += "grey";
|
||||
else if(lightData[i].dimmable)
|
||||
content += "red";
|
||||
else
|
||||
content += "blue";
|
||||
content += ";\">";
|
||||
|
||||
content += String(lightNames[i]) + ": ";
|
||||
|
||||
content += "<select name='p" + String(i) + "'>";
|
||||
for(int j=0;j<sizeof(pinList);j++){
|
||||
content += "<option value='" + String(pinList[j]) + "'";
|
||||
if(lightData[i].pin==pinList[j])
|
||||
content += " selected";
|
||||
content += ">";
|
||||
if(j>0)
|
||||
content += "Pin " + String(pinList[j]);
|
||||
else
|
||||
content += "None";
|
||||
content += "</option>";
|
||||
}
|
||||
content += "</select> ";
|
||||
content += "<input type='checkbox' value='1'";
|
||||
if(lightData[i].dimmable)
|
||||
content += " checked";
|
||||
content += " name='t" + String(i) + "'></span><br>";
|
||||
}
|
||||
|
||||
content += "<br><input type='reset'><input type='submit' value='Update'></form><br>";
|
||||
|
||||
webServer.send(200, "text/html", content);
|
||||
|
||||
});
|
||||
|
||||
webServer.on("/configure", []() {
|
||||
|
||||
for(int i=0;i<NLIGHTS;i++) // clear dimmable status since checkboxes only provide data if box is checked
|
||||
lightData[i].dimmable=0;
|
||||
|
||||
for(int i=0;i<webServer.args();i++){
|
||||
switch(webServer.argName(i).charAt(0)){
|
||||
case 'p':
|
||||
lightData[webServer.argName(i).substring(1).toInt()].pin=webServer.arg(i).toInt();
|
||||
break;
|
||||
case 't':
|
||||
lightData[webServer.argName(i).substring(1).toInt()].dimmable=webServer.arg(i).toInt();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
String content = "<html><body>Settings Saved!<br><br>";
|
||||
|
||||
for(int i=0;i<NLIGHTS;i++)
|
||||
if(lightData[i].pin)
|
||||
content += lightNames[i] + String(": Pin=") + String(lightData[i].pin) + String(lightData[i].dimmable?" Dimmable":"") + "<br>";
|
||||
else
|
||||
lightData[i].dimmable=0;
|
||||
|
||||
content += "<br><button onclick=\"document.location='/'\">Return</button> ";
|
||||
content += "<button onclick=\"document.location='/reboot'\">Reboot</button>";
|
||||
|
||||
nvs_set_blob(lightNVS,"LIGHTDATA",&lightData,sizeof(lightData)); // update data
|
||||
nvs_commit(lightNVS); // commit to NVS
|
||||
|
||||
webServer.send(200, "text/html", content);
|
||||
|
||||
});
|
||||
|
||||
webServer.on("/reboot", []() {
|
||||
|
||||
String content = "<html><body>Rebooting! Will return to configuration page in 10 seconds.<br><br>";
|
||||
content += "<meta http-equiv = \"refresh\" content = \"10; url = /\" />";
|
||||
webServer.send(200, "text/html", content);
|
||||
|
||||
for(int j=0;j<sizeof(pinList);j++) // this seems to be needed to ensure all pins are disconnected from led PWM on reboot
|
||||
gpio_reset_pin((gpio_num_t)pinList[j]); // otherwise ESP32 seems to be retaining some info about pins connectivity?
|
||||
|
||||
ESP.restart();
|
||||
});
|
||||
|
||||
} // setupWeb
|
||||
///////////////////////////
|
||||
|
|
|
|||
Loading…
Reference in New Issue