mirror of
https://github.com/rstrouse/ESPSomfy-RTS.git
synced 2025-12-13 02:52:11 +01:00
v1.4.0 upade
* Moved shade storage from NVS. NVS storage became limited because of the wired ethernet boards. This limited the number of potential shades to around 20. * Added the ability to backup the shade configuration * Added the ability to restore the shade configuration. * Increased up. down, and tilt timing value to allow for up to 54 days of transition. The previous 16bit value did not allow for very slow shades and was limited to just over a minute. * UI cleanup and additional messages. * Transceiver tuning now applies the rx bandwidth in the proper order so no reboot is required.
This commit is contained in:
parent
21d56993d0
commit
dce0ae0c04
15 changed files with 1023 additions and 241 deletions
370
ConfigFile.cpp
Normal file
370
ConfigFile.cpp
Normal file
|
|
@ -0,0 +1,370 @@
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <LittleFS.h>
|
||||||
|
#include <Preferences.h>
|
||||||
|
#include "ConfigFile.h"
|
||||||
|
#include "Utils.h"
|
||||||
|
|
||||||
|
extern Preferences pref;
|
||||||
|
|
||||||
|
#define SHADE_HDR_VER 1
|
||||||
|
#define SHADE_HDR_SIZE 16
|
||||||
|
#define SHADE_REC_SIZE 176
|
||||||
|
|
||||||
|
bool ConfigFile::begin(const char* filename, bool readOnly) {
|
||||||
|
this->file = LittleFS.open(filename, readOnly ? "r" : "w");
|
||||||
|
this->_opened = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
void ConfigFile::end() {
|
||||||
|
if(this->isOpen()) {
|
||||||
|
if(!this->readOnly) this->file.flush();
|
||||||
|
this->file.close();
|
||||||
|
}
|
||||||
|
this->_opened = false;
|
||||||
|
}
|
||||||
|
bool ConfigFile::isOpen() { return this->_opened; }
|
||||||
|
bool ConfigFile::seekChar(const char val) {
|
||||||
|
if(!this->isOpen()) return false;
|
||||||
|
char ch;
|
||||||
|
do {
|
||||||
|
ch = this->readChar('\0');
|
||||||
|
if(ch == '\0') return false;
|
||||||
|
} while(ch != val);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool ConfigFile::writeSeparator() {return this->writeChar(CFG_VALUE_SEP); }
|
||||||
|
bool ConfigFile::writeRecordEnd() { return this->writeChar(CFG_REC_END); }
|
||||||
|
bool ConfigFile::writeHeader() { return this->writeHeader(this->header); }
|
||||||
|
bool ConfigFile::writeHeader(const config_header_t &hdr) {
|
||||||
|
if(!this->isOpen()) return false;
|
||||||
|
this->writeUInt8(hdr.version);
|
||||||
|
this->writeUInt8(hdr.length);
|
||||||
|
this->writeUInt8(hdr.recordSize);
|
||||||
|
this->writeUInt8(hdr.records, CFG_REC_END);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool ConfigFile::readHeader() {
|
||||||
|
if(!this->isOpen()) return false;
|
||||||
|
//if(this->file.position() != 0) this->file.seek(0, SeekSet);
|
||||||
|
Serial.printf("Reading header at %u\n", this->file.position());
|
||||||
|
this->header.version = this->readUInt8(this->header.version);
|
||||||
|
this->header.length = this->readUInt8(0);
|
||||||
|
this->header.recordSize = this->readUInt8(this->header.recordSize);
|
||||||
|
this->header.records = this->readUInt8(this->header.records);
|
||||||
|
Serial.printf("version:%u len:%u size:%u recs:%u pos:%d\n", this->header.version, this->header.length, this->header.recordSize, this->header.records, this->file.position());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool ConfigFile::seekRecordByIndex(uint16_t ndx) {
|
||||||
|
if(!this->file) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(((this->header.recordSize * ndx) + this->header.length) > this->file.size()) return false;
|
||||||
|
}
|
||||||
|
bool ConfigFile::readString(char *buff, size_t len) {
|
||||||
|
if(!this->file) return false;
|
||||||
|
memset(buff, 0x00, len);
|
||||||
|
uint16_t i = 0;
|
||||||
|
while(i < len) {
|
||||||
|
uint8_t val;
|
||||||
|
if(this->file.read(&val, 1) == 1) {
|
||||||
|
switch(val) {
|
||||||
|
case CFG_REC_END:
|
||||||
|
case CFG_VALUE_SEP:
|
||||||
|
_rtrim(buff);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
buff[i++] = val;
|
||||||
|
if(i == len) {
|
||||||
|
_rtrim(buff);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_rtrim(buff);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool ConfigFile::writeString(const char *val, size_t len, const char tok) {
|
||||||
|
if(!this->isOpen()) return false;
|
||||||
|
int slen = strlen(val);
|
||||||
|
if(slen > 0)
|
||||||
|
if(this->file.write((uint8_t *)val, slen) != slen) return false;
|
||||||
|
// Now we need to pad the end of the string so that it is of a fixed length.
|
||||||
|
while(slen < len - 1) {
|
||||||
|
this->file.write(' ');
|
||||||
|
slen++;
|
||||||
|
}
|
||||||
|
// 255 = len = 4 slen = 3
|
||||||
|
if(tok != CFG_TOK_NONE)
|
||||||
|
return this->writeChar(tok);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool ConfigFile::writeChar(const char val) {
|
||||||
|
if(!this->isOpen()) return false;
|
||||||
|
if(this->file.write(static_cast<uint8_t>(val)) == 1) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool ConfigFile::writeUInt8(const uint8_t val, const char tok) {
|
||||||
|
char buff[4];
|
||||||
|
snprintf(buff, sizeof(buff), "%3u", val);
|
||||||
|
return this->writeString(buff, sizeof(buff), tok);
|
||||||
|
}
|
||||||
|
bool ConfigFile::writeUInt16(const uint16_t val, const char tok) {
|
||||||
|
char buff[6];
|
||||||
|
snprintf(buff, sizeof(buff), "%5u", val);
|
||||||
|
return this->writeString(buff, sizeof(buff), tok);
|
||||||
|
}
|
||||||
|
bool ConfigFile::writeUInt32(const uint32_t val, const char tok) {
|
||||||
|
char buff[11];
|
||||||
|
snprintf(buff, sizeof(buff), "%10u", val);
|
||||||
|
return this->writeString(buff, sizeof(buff), tok);
|
||||||
|
}
|
||||||
|
bool ConfigFile::writeFloat(const float val, const uint8_t prec, const char tok) {
|
||||||
|
char buff[20];
|
||||||
|
snprintf(buff, sizeof(buff), "%*.*f", 7 + prec, prec, val);
|
||||||
|
return this->writeString(buff, 8 + prec, tok);
|
||||||
|
}
|
||||||
|
bool ConfigFile::writeBool(const bool val, const char tok) {
|
||||||
|
return this->writeString(val ? "true" : "false", 6, tok);
|
||||||
|
}
|
||||||
|
|
||||||
|
char ConfigFile::readChar(const char defVal) {
|
||||||
|
uint8_t ch;
|
||||||
|
if(this->file.read(&ch, 1) == 1) return (char)ch;
|
||||||
|
return defVal;
|
||||||
|
}
|
||||||
|
uint8_t ConfigFile::readUInt8(const uint8_t defVal) {
|
||||||
|
char buff[4];
|
||||||
|
if(this->readString(buff, sizeof(buff)))
|
||||||
|
return static_cast<uint8_t>(atoi(buff));
|
||||||
|
return defVal;
|
||||||
|
}
|
||||||
|
uint16_t ConfigFile::readUInt16(const uint16_t defVal) {
|
||||||
|
char buff[6];
|
||||||
|
if(this->readString(buff, sizeof(buff)))
|
||||||
|
return static_cast<uint16_t>(atoi(buff));
|
||||||
|
return defVal;
|
||||||
|
}
|
||||||
|
uint32_t ConfigFile::readUInt32(const uint32_t defVal) {
|
||||||
|
char buff[11];
|
||||||
|
if(this->readString(buff, sizeof(buff)))
|
||||||
|
return static_cast<uint32_t>(atoi(buff));
|
||||||
|
return defVal;
|
||||||
|
}
|
||||||
|
float ConfigFile::readFloat(const float defVal) {
|
||||||
|
char buff[25];
|
||||||
|
if(this->readString(buff, sizeof(buff)))
|
||||||
|
return atof(buff);
|
||||||
|
return defVal;
|
||||||
|
}
|
||||||
|
bool ConfigFile::readBool(const bool defVal) {
|
||||||
|
char buff[6];
|
||||||
|
if(this->readString(buff, sizeof(buff))) {
|
||||||
|
switch(buff[0]) {
|
||||||
|
case 't':
|
||||||
|
case 'T':
|
||||||
|
case '1':
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return defVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShadeConfigFile::seekRecordById(uint8_t id) {
|
||||||
|
if(this->isOpen()) return false;
|
||||||
|
this->file.seek(this->header.length, SeekSet); // Start at the beginning of the file after the header.
|
||||||
|
uint8_t i = 0;
|
||||||
|
while(i < SOMFY_MAX_SHADES) {
|
||||||
|
uint32_t pos = this->file.position();
|
||||||
|
uint8_t len = this->readUInt8(this->header.recordSize);
|
||||||
|
uint8_t cid = this->readUInt8(255);
|
||||||
|
if(cid == id) {
|
||||||
|
this->file.seek(pos, SeekSet);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
pos += len;
|
||||||
|
this->file.seek(pos, SeekSet);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool ShadeConfigFile::begin(bool readOnly) { return this->begin("/shades.cfg", readOnly); }
|
||||||
|
bool ShadeConfigFile::begin(const char *filename, bool readOnly) { return ConfigFile::begin(filename, readOnly); }
|
||||||
|
void ShadeConfigFile::end() { ConfigFile::end(); }
|
||||||
|
bool ShadeConfigFile::save(SomfyShadeController *s) {
|
||||||
|
this->header.version = SHADE_HDR_VER;
|
||||||
|
this->header.recordSize = SHADE_REC_SIZE;
|
||||||
|
this->header.length = SHADE_HDR_SIZE;
|
||||||
|
this->header.records = SOMFY_MAX_SHADES;
|
||||||
|
this->writeHeader();
|
||||||
|
for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) {
|
||||||
|
SomfyShade *shade = &s->shades[i];
|
||||||
|
this->writeShadeRecord(shade);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool ShadeConfigFile::validate() {
|
||||||
|
this->readHeader();
|
||||||
|
if(this->header.version < 1) {
|
||||||
|
Serial.print("Invalid Header Version:");
|
||||||
|
Serial.println(this->header.version);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(this->header.recordSize < 100) {
|
||||||
|
Serial.print("Invalid Record Size:");
|
||||||
|
Serial.println(this->header.recordSize);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(this->header.records != 32) {
|
||||||
|
Serial.print("Invalid Record Count:");
|
||||||
|
Serial.println(this->header.records);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(this->file.position() != this->header.length) {
|
||||||
|
Serial.printf("File not positioned at %u end of header: %d\n", this->header.length, this->file.position());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// We should know the file size based upon the record information in the header
|
||||||
|
if(this->file.size() != this->header.length + (this->header.recordSize * this->header.records)) {
|
||||||
|
Serial.printf("File size is not correct should be %d and got %d", this->header.length + (this->header.recordSize * this->header.records), this->file.size());
|
||||||
|
}
|
||||||
|
// Next check to see if the records match the header length.
|
||||||
|
uint8_t recs = 0;
|
||||||
|
uint32_t startPos = this->file.position();
|
||||||
|
while(recs < this->header.records) {
|
||||||
|
uint32_t pos = this->file.position();
|
||||||
|
if(!this->seekChar(CFG_REC_END)) {
|
||||||
|
Serial.printf("Failed to find the record end %d\n", recs);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(this->file.position() - pos != this->header.recordSize) {
|
||||||
|
Serial.printf("Record length is %d and should be %d\n", this->file.position() - pos, this->header.recordSize);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
recs++;
|
||||||
|
}
|
||||||
|
this->file.seek(startPos, SeekSet);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool ShadeConfigFile::load(SomfyShadeController *s, const char *filename) {
|
||||||
|
ShadeConfigFile file;
|
||||||
|
if(file.begin(filename, true)) {
|
||||||
|
bool success = file.loadFile(s, filename);
|
||||||
|
file.end();
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool ShadeConfigFile::loadFile(SomfyShadeController *s, const char *filename) {
|
||||||
|
bool opened = false;
|
||||||
|
if(!this->isOpen()) {
|
||||||
|
Serial.println("Opening shade config file");
|
||||||
|
this->begin(filename, true);
|
||||||
|
opened = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//this->file.seek(0, SeekSet);
|
||||||
|
}
|
||||||
|
if(!this->validate()) {
|
||||||
|
Serial.println("Shade config file invalid!");
|
||||||
|
if(opened) this->end();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// We should be valid so start reading.
|
||||||
|
pref.begin("ShadeCodes");
|
||||||
|
for(uint8_t i = 0; i < this->header.records; i++) {
|
||||||
|
SomfyShade *shade = &s->shades[i];
|
||||||
|
shade->setShadeId(this->readUInt8(255));
|
||||||
|
shade->paired = this->readBool(false);
|
||||||
|
shade->shadeType = static_cast<shade_types>(this->readUInt8(0));
|
||||||
|
shade->setRemoteAddress(this->readUInt32(0));
|
||||||
|
this->readString(shade->name, sizeof(shade->name));
|
||||||
|
shade->hasTilt = this->readBool(false);
|
||||||
|
shade->upTime = this->readUInt32(shade->upTime);
|
||||||
|
shade->downTime = this->readUInt32(shade->downTime);
|
||||||
|
shade->tiltTime = this->readUInt32(shade->tiltTime);
|
||||||
|
for(uint8_t j = 0; j < SOMFY_MAX_LINKED_REMOTES; j++) {
|
||||||
|
SomfyLinkedRemote *rem = &shade->linkedRemotes[j];
|
||||||
|
rem->setRemoteAddress(this->readUInt32(0));
|
||||||
|
if(rem->getRemoteAddress() != 0) rem->lastRollingCode = pref.getUShort(rem->getRemotePrefId(), 0);
|
||||||
|
}
|
||||||
|
shade->lastRollingCode = this->readUInt16(0);
|
||||||
|
if(shade->getRemoteAddress() != 0) shade->lastRollingCode = max(pref.getUShort(shade->getRemotePrefId(), shade->lastRollingCode), shade->lastRollingCode);
|
||||||
|
shade->myPos = this->readUInt8(255);
|
||||||
|
shade->currentPos = this->readFloat(0);
|
||||||
|
shade->currentTiltPos = this->readFloat(0);
|
||||||
|
shade->tiltPosition = (uint8_t)floor(shade->currentTiltPos * 100);
|
||||||
|
shade->position = (uint8_t)floor(shade->currentPos * 100);
|
||||||
|
shade->target = shade->position;
|
||||||
|
shade->tiltTarget = shade->tiltPosition;
|
||||||
|
}
|
||||||
|
pref.end();
|
||||||
|
if(opened) {
|
||||||
|
Serial.println("Closing shade config file");
|
||||||
|
this->end();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool ShadeConfigFile::writeShadeRecord(SomfyShade *shade) {
|
||||||
|
this->writeUInt8(shade->getShadeId());
|
||||||
|
this->writeBool(shade->paired);
|
||||||
|
this->writeUInt8(static_cast<uint8_t>(shade->shadeType));
|
||||||
|
this->writeUInt32(shade->getRemoteAddress());
|
||||||
|
this->writeString(shade->name, sizeof(shade->name));
|
||||||
|
this->writeBool(shade->hasTilt);
|
||||||
|
this->writeUInt32(shade->upTime);
|
||||||
|
this->writeUInt32(shade->downTime);
|
||||||
|
this->writeUInt32(shade->tiltTime);
|
||||||
|
for(uint8_t j = 0; j < SOMFY_MAX_LINKED_REMOTES; j++) {
|
||||||
|
SomfyLinkedRemote *rem = &shade->linkedRemotes[j];
|
||||||
|
this->writeUInt32(rem->getRemoteAddress());
|
||||||
|
}
|
||||||
|
this->writeUInt16(shade->lastRollingCode);
|
||||||
|
this->writeUInt8(shade->myPos);
|
||||||
|
this->writeFloat(shade->currentPos, 5);
|
||||||
|
this->writeFloat(shade->currentTiltPos, 5, CFG_REC_END);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool ShadeConfigFile::exists() { return LittleFS.exists("/shades.cfg"); }
|
||||||
|
bool ShadeConfigFile::getAppVersion(appver_t &ver) {
|
||||||
|
char app[15];
|
||||||
|
if(!LittleFS.exists("/appversion")) return false;
|
||||||
|
File f = LittleFS.open("/appversion", "r");
|
||||||
|
size_t fsize = f.size();
|
||||||
|
memset(app, 0x00, sizeof(app));
|
||||||
|
f.read((uint8_t *)app, sizeof(app) - 1);
|
||||||
|
f.close();
|
||||||
|
// Now lets parse this pig.
|
||||||
|
memset(&ver, 0x00, sizeof(appver_t));
|
||||||
|
char num[3];
|
||||||
|
uint8_t i = 0;
|
||||||
|
for(uint8_t j = 0; j < 3 && i < strlen(app); j++) {
|
||||||
|
char ch = app[i++];
|
||||||
|
if(ch != '.')
|
||||||
|
num[j] = ch;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ver.major = static_cast<uint8_t>(atoi(num) & 0xFF);
|
||||||
|
memset(num, 0x00, sizeof(num));
|
||||||
|
for(uint8_t j = 0; j < 3 && i < strlen(app); j++) {
|
||||||
|
char ch = app[i++];
|
||||||
|
if(ch != '.')
|
||||||
|
num[j] = ch;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ver.minor = static_cast<uint8_t>(atoi(num) & 0xFF);
|
||||||
|
memset(num, 0x00, sizeof(num));
|
||||||
|
for(uint8_t j = 0; j < 3 && i < strlen(app); j++) {
|
||||||
|
char ch = app[i++];
|
||||||
|
if(ch != '.')
|
||||||
|
num[j] = ch;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ver.build = static_cast<uint8_t>(atoi(num) & 0xFF);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
66
ConfigFile.h
Normal file
66
ConfigFile.h
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
#include <LittleFS.h>
|
||||||
|
#include "Somfy.h"
|
||||||
|
#ifndef configfile_h
|
||||||
|
#define configfile_h
|
||||||
|
|
||||||
|
#define CFG_VALUE_SEP ','
|
||||||
|
#define CFG_REC_END '\n'
|
||||||
|
#define CFG_TOK_NONE 0x00
|
||||||
|
|
||||||
|
typedef struct config_header_t {
|
||||||
|
uint8_t version = 1;
|
||||||
|
uint16_t recordSize = 0;
|
||||||
|
uint16_t records = 0;
|
||||||
|
uint8_t length = 0;
|
||||||
|
};
|
||||||
|
class ConfigFile {
|
||||||
|
protected:
|
||||||
|
File file;
|
||||||
|
bool readOnly = false;
|
||||||
|
bool begin(const char *filename, bool readOnly = false);
|
||||||
|
uint32_t startRecPos = 0;
|
||||||
|
bool _opened = false;
|
||||||
|
public:
|
||||||
|
config_header_t header;
|
||||||
|
bool save();
|
||||||
|
void end();
|
||||||
|
bool isOpen();
|
||||||
|
bool seekRecordByIndex(uint16_t ndx);
|
||||||
|
bool readHeader();
|
||||||
|
bool seekChar(const char val);
|
||||||
|
bool writeHeader(const config_header_t &header);
|
||||||
|
bool writeHeader();
|
||||||
|
bool writeSeparator();
|
||||||
|
bool writeRecordEnd();
|
||||||
|
bool writeChar(const char val);
|
||||||
|
bool writeUInt8(const uint8_t val, const char tok = CFG_VALUE_SEP);
|
||||||
|
bool writeUInt16(const uint16_t val, const char tok = CFG_VALUE_SEP);
|
||||||
|
bool writeUInt32(const uint32_t val, const char tok = CFG_VALUE_SEP);
|
||||||
|
bool writeBool(const bool val, const char tok = CFG_VALUE_SEP);
|
||||||
|
bool writeFloat(const float val, const uint8_t prec, const char tok = CFG_VALUE_SEP);
|
||||||
|
bool readString(char *buff, size_t len);
|
||||||
|
bool writeString(const char *val, size_t len, const char tok = CFG_VALUE_SEP);
|
||||||
|
char readChar(const char defVal = '\0');
|
||||||
|
uint8_t readUInt8(const uint8_t defVal = 0);
|
||||||
|
uint16_t readUInt16(const uint16_t defVal = 0);
|
||||||
|
uint32_t readUInt32(const uint32_t defVal = 0);
|
||||||
|
bool readBool(const bool defVal = false);
|
||||||
|
float readFloat(const float defVal = 0.00);
|
||||||
|
};
|
||||||
|
class ShadeConfigFile : public ConfigFile {
|
||||||
|
protected:
|
||||||
|
bool writeShadeRecord(SomfyShade *shade);
|
||||||
|
public:
|
||||||
|
static bool getAppVersion(appver_t &ver);
|
||||||
|
static bool exists();
|
||||||
|
static bool load(SomfyShadeController *somfy, const char *filename = "/shades.cfg");
|
||||||
|
bool begin(const char *filename, bool readOnly = false);
|
||||||
|
bool begin(bool readOnly = false);
|
||||||
|
bool save(SomfyShadeController *sofmy);
|
||||||
|
bool loadFile(SomfyShadeController *somfy, const char *filename = "/shades.cfg");
|
||||||
|
void end();
|
||||||
|
bool seekRecordById(uint8_t id);
|
||||||
|
bool validate();
|
||||||
|
};
|
||||||
|
#endif;
|
||||||
|
|
@ -47,8 +47,6 @@ bool BaseSettings::parseIPAddress(JsonObject &obj, const char *prop, IPAddress *
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int BaseSettings::parseValueInt(JsonObject &obj, const char *prop, int defVal) {
|
int BaseSettings::parseValueInt(JsonObject &obj, const char *prop, int defVal) {
|
||||||
if(obj.containsKey(prop)) return obj[prop];
|
if(obj.containsKey(prop)) return obj[prop];
|
||||||
return defVal;
|
return defVal;
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
#ifndef configsettings_h
|
#ifndef configsettings_h
|
||||||
#define configsettings_h
|
#define configsettings_h
|
||||||
|
|
||||||
#define FW_VERSION "v1.3.2"
|
#define FW_VERSION "v1.4.0"
|
||||||
enum DeviceStatus {
|
enum DeviceStatus {
|
||||||
DS_OK = 0,
|
DS_OK = 0,
|
||||||
DS_ERROR = 1,
|
DS_ERROR = 1,
|
||||||
|
|
|
||||||
|
|
@ -123,10 +123,10 @@ void Network::setConnected(conn_types connType) {
|
||||||
Serial.print(" ");
|
Serial.print(" ");
|
||||||
Serial.print(ETH.linkSpeed());
|
Serial.print(ETH.linkSpeed());
|
||||||
Serial.println("Mbps");
|
Serial.println("Mbps");
|
||||||
|
char buf[128];
|
||||||
|
snprintf(buf, sizeof(buf), "{\"connected\":true,\"speed\":%d,\"fullduplex\":%s}", ETH.linkSpeed(), ETH.fullDuplex() ? "true" : "false");
|
||||||
|
sockEmit.sendToClients("ethernet", buf);
|
||||||
}
|
}
|
||||||
char buf[128];
|
|
||||||
snprintf(buf, sizeof(buf), "{\"connected\":true,\"speed\":%d,\"fullduplex\":%s}", ETH.linkSpeed(), ETH.fullDuplex() ? "true" : "false");
|
|
||||||
sockEmit.sendToClients("ethernet", buf);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Serial.println();
|
Serial.println();
|
||||||
|
|
|
||||||
510
Somfy.cpp
510
Somfy.cpp
|
|
@ -7,12 +7,14 @@
|
||||||
#include "Somfy.h"
|
#include "Somfy.h"
|
||||||
#include "Sockets.h"
|
#include "Sockets.h"
|
||||||
#include "MQTT.h"
|
#include "MQTT.h"
|
||||||
|
#include "ConfigFile.h"
|
||||||
|
|
||||||
extern Preferences pref;
|
extern Preferences pref;
|
||||||
extern SomfyShadeController somfy;
|
extern SomfyShadeController somfy;
|
||||||
extern SocketEmitter sockEmit;
|
extern SocketEmitter sockEmit;
|
||||||
extern MQTTClass mqtt;
|
extern MQTTClass mqtt;
|
||||||
|
|
||||||
|
|
||||||
uint8_t rxmode = 0; // Indicates whether the radio is in receive mode. Just to ensure there isn't more than one interrupt hooked.
|
uint8_t rxmode = 0; // Indicates whether the radio is in receive mode. Just to ensure there isn't more than one interrupt hooked.
|
||||||
#define SYMBOL 640
|
#define SYMBOL 640
|
||||||
#if defined(ESP8266)
|
#if defined(ESP8266)
|
||||||
|
|
@ -245,13 +247,16 @@ SomfyShade *SomfyShadeController::findShadeByRemoteAddress(uint32_t address) {
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
bool SomfyShadeController::begin() {
|
bool SomfyShadeController::loadLegacy() {
|
||||||
// Load up all the configuration data.
|
Serial.println("Loading Legacy shades using NVS");
|
||||||
//Serial.printf("sizeof(SomfyShade) = %d\n", sizeof(SomfyShade));
|
pref.begin("Shades", true);
|
||||||
pref.begin("Shades");
|
|
||||||
pref.getBytes("shadeIds", this->m_shadeIds, sizeof(this->m_shadeIds));
|
pref.getBytes("shadeIds", this->m_shadeIds, sizeof(this->m_shadeIds));
|
||||||
pref.end();
|
pref.end();
|
||||||
//this->transceiver.begin();
|
for(uint8_t i = 0; i < sizeof(this->m_shadeIds); i++) {
|
||||||
|
if(i != 0) DEBUG_SOMFY.print(",");
|
||||||
|
DEBUG_SOMFY.print(this->m_shadeIds[i]);
|
||||||
|
}
|
||||||
|
DEBUG_SOMFY.println();
|
||||||
sortArray<uint8_t>(this->m_shadeIds, sizeof(this->m_shadeIds));
|
sortArray<uint8_t>(this->m_shadeIds, sizeof(this->m_shadeIds));
|
||||||
#ifdef DEBUG_SOMFY
|
#ifdef DEBUG_SOMFY
|
||||||
for(uint8_t i = 0; i < sizeof(this->m_shadeIds); i++) {
|
for(uint8_t i = 0; i < sizeof(this->m_shadeIds); i++) {
|
||||||
|
|
@ -281,12 +286,63 @@ bool SomfyShadeController::begin() {
|
||||||
}
|
}
|
||||||
Serial.println();
|
Serial.println();
|
||||||
#endif
|
#endif
|
||||||
pref.begin("Shades");
|
if(!this->useNVS()) {
|
||||||
pref.putBytes("shadeIds", this->m_shadeIds, sizeof(this->m_shadeIds));
|
pref.begin("Shades");
|
||||||
pref.end();
|
pref.putBytes("shadeIds", this->m_shadeIds, sizeof(this->m_shadeIds));
|
||||||
|
pref.end();
|
||||||
|
}
|
||||||
|
this->commit();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool SomfyShadeController::begin() {
|
||||||
|
// Load up all the configuration data.
|
||||||
|
ShadeConfigFile::getAppVersion(this->appVersion);
|
||||||
|
Serial.printf("App Version:%u.%u.%u\n", this->appVersion.major, this->appVersion.minor, this->appVersion.build);
|
||||||
|
if(!this->useNVS()) { // At 1.4 we started using the configuration file. If the file doesn't exist then booh.
|
||||||
|
// We need to remove all the extraeneous data from NVS for the shades. From here on out we
|
||||||
|
// will rely on the shade configuration.
|
||||||
|
Serial.println("No longer using NVS");
|
||||||
|
if(ShadeConfigFile::exists()) {
|
||||||
|
ShadeConfigFile::load(this);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this->loadLegacy();
|
||||||
|
}
|
||||||
|
pref.begin("Shades");
|
||||||
|
if(pref.isKey("shadeIds")) {
|
||||||
|
pref.getBytes("shadeIds", this->m_shadeIds, sizeof(this->m_shadeIds));
|
||||||
|
pref.clear(); // Delete all the keys.
|
||||||
|
}
|
||||||
|
pref.end();
|
||||||
|
for(uint8_t i = 0; i < sizeof(this->m_shadeIds); i++) {
|
||||||
|
// Start deleting the keys for the shades.
|
||||||
|
if(this->m_shadeIds[i] == 255) continue;
|
||||||
|
char shadeKey[15];
|
||||||
|
sprintf(shadeKey, "SomfyShade%u", this->m_shadeIds[i]);
|
||||||
|
pref.begin(shadeKey);
|
||||||
|
pref.clear();
|
||||||
|
pref.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(ShadeConfigFile::exists()) {
|
||||||
|
Serial.println("shades.cfg exists so we are using that");
|
||||||
|
ShadeConfigFile::load(this);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Serial.println("Starting clean");
|
||||||
|
this->loadLegacy();
|
||||||
|
}
|
||||||
this->transceiver.begin();
|
this->transceiver.begin();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
void SomfyShadeController::commit() {
|
||||||
|
ShadeConfigFile file;
|
||||||
|
file.begin();
|
||||||
|
file.save(this);
|
||||||
|
file.end();
|
||||||
|
this->isDirty = false;
|
||||||
|
this->lastCommit = millis();
|
||||||
|
}
|
||||||
SomfyShade * SomfyShadeController::getShadeById(uint8_t shadeId) {
|
SomfyShade * SomfyShadeController::getShadeById(uint8_t shadeId) {
|
||||||
for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) {
|
for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) {
|
||||||
if(this->shades[i].getShadeId() == shadeId) return &this->shades[i];
|
if(this->shades[i].getShadeId() == shadeId) return &this->shades[i];
|
||||||
|
|
@ -304,41 +360,85 @@ bool SomfyShade::linkRemote(uint32_t address, uint16_t rollingCode) {
|
||||||
}
|
}
|
||||||
for(uint8_t i = 0; i < SOMFY_MAX_LINKED_REMOTES; i++) {
|
for(uint8_t i = 0; i < SOMFY_MAX_LINKED_REMOTES; i++) {
|
||||||
if(this->linkedRemotes[i].getRemoteAddress() == 0) {
|
if(this->linkedRemotes[i].getRemoteAddress() == 0) {
|
||||||
char shadeKey[15];
|
|
||||||
this->linkedRemotes[i].setRemoteAddress(address);
|
this->linkedRemotes[i].setRemoteAddress(address);
|
||||||
this->linkedRemotes[i].setRollingCode(rollingCode);
|
this->linkedRemotes[i].setRollingCode(rollingCode);
|
||||||
snprintf(shadeKey, sizeof(shadeKey), "SomfyShade%u", this->getShadeId());
|
if(somfy.useNVS()) {
|
||||||
pref.begin(shadeKey);
|
uint32_t linkedAddresses[SOMFY_MAX_LINKED_REMOTES];
|
||||||
uint32_t linkedAddresses[SOMFY_MAX_LINKED_REMOTES];
|
memset(linkedAddresses, 0x00, sizeof(linkedAddresses));
|
||||||
memset(linkedAddresses, 0x00, sizeof(linkedAddresses));
|
uint8_t j = 0;
|
||||||
uint8_t j = 0;
|
for(uint8_t i = 0; i < SOMFY_MAX_LINKED_REMOTES; i++) {
|
||||||
for(uint8_t i = 0; i < SOMFY_MAX_LINKED_REMOTES; i++) {
|
SomfyLinkedRemote lremote = this->linkedRemotes[i];
|
||||||
SomfyLinkedRemote lremote = this->linkedRemotes[i];
|
if(lremote.getRemoteAddress() != 0) linkedAddresses[j++] = lremote.getRemoteAddress();
|
||||||
if(lremote.getRemoteAddress() != 0) linkedAddresses[j++] = lremote.getRemoteAddress();
|
}
|
||||||
|
char shadeKey[15];
|
||||||
|
snprintf(shadeKey, sizeof(shadeKey), "SomfyShade%u", this->getShadeId());
|
||||||
|
pref.begin(shadeKey);
|
||||||
|
pref.putBytes("linkedAddr", linkedAddresses, sizeof(uint32_t) * SOMFY_MAX_LINKED_REMOTES);
|
||||||
|
pref.end();
|
||||||
}
|
}
|
||||||
pref.putBytes("linkedAddr", linkedAddresses, sizeof(uint32_t) * SOMFY_MAX_LINKED_REMOTES);
|
this->commit();
|
||||||
pref.end();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
void SomfyShade::commit() { somfy.commit(); }
|
||||||
|
void SomfyShade::commitShadePosition() {
|
||||||
|
somfy.isDirty = true;
|
||||||
|
char shadeKey[15];
|
||||||
|
if(somfy.useNVS()) {
|
||||||
|
snprintf(shadeKey, sizeof(shadeKey), "SomfyShade%u", this->shadeId);
|
||||||
|
Serial.print("Writing current shade position: ");
|
||||||
|
Serial.println(this->currentPos, 4);
|
||||||
|
pref.begin(shadeKey);
|
||||||
|
pref.putFloat("currentPos", this->currentPos);
|
||||||
|
pref.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void SomfyShade::commitMyPosition() {
|
||||||
|
somfy.isDirty = true;
|
||||||
|
if(somfy.useNVS()) {
|
||||||
|
char shadeKey[15];
|
||||||
|
snprintf(shadeKey, sizeof(shadeKey), "SomfyShade%u", this->shadeId);
|
||||||
|
Serial.print("Writing my shade position:");
|
||||||
|
Serial.print(this->myPos);
|
||||||
|
Serial.println("%");
|
||||||
|
pref.begin(shadeKey);
|
||||||
|
pref.putUShort("myPos", this->myPos);
|
||||||
|
pref.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void SomfyShade::commitTiltPosition() {
|
||||||
|
somfy.isDirty = true;
|
||||||
|
if(somfy.useNVS()) {
|
||||||
|
char shadeKey[15];
|
||||||
|
snprintf(shadeKey, sizeof(shadeKey), "SomfyShade%u", this->shadeId);
|
||||||
|
Serial.print("Writing current shade tilt position: ");
|
||||||
|
Serial.println(this->currentTiltPos, 4);
|
||||||
|
pref.begin(shadeKey);
|
||||||
|
pref.putFloat("currentTiltPos", this->currentTiltPos);
|
||||||
|
pref.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
bool SomfyShade::unlinkRemote(uint32_t address) {
|
bool SomfyShade::unlinkRemote(uint32_t address) {
|
||||||
for(uint8_t i = 0; i < SOMFY_MAX_LINKED_REMOTES; i++) {
|
for(uint8_t i = 0; i < SOMFY_MAX_LINKED_REMOTES; i++) {
|
||||||
if(this->linkedRemotes[i].getRemoteAddress() == address) {
|
if(this->linkedRemotes[i].getRemoteAddress() == address) {
|
||||||
char shadeKey[15];
|
|
||||||
this->linkedRemotes[i].setRemoteAddress(0);
|
this->linkedRemotes[i].setRemoteAddress(0);
|
||||||
snprintf(shadeKey, sizeof(shadeKey), "SomfyShade%u", this->getShadeId());
|
if(somfy.useNVS()) {
|
||||||
pref.begin(shadeKey);
|
char shadeKey[15];
|
||||||
uint32_t linkedAddresses[SOMFY_MAX_LINKED_REMOTES];
|
snprintf(shadeKey, sizeof(shadeKey), "SomfyShade%u", this->getShadeId());
|
||||||
memset(linkedAddresses, 0x00, sizeof(linkedAddresses));
|
uint32_t linkedAddresses[SOMFY_MAX_LINKED_REMOTES];
|
||||||
uint8_t j = 0;
|
memset(linkedAddresses, 0x00, sizeof(linkedAddresses));
|
||||||
for(uint8_t i = 0; i < SOMFY_MAX_LINKED_REMOTES; i++) {
|
uint8_t j = 0;
|
||||||
SomfyLinkedRemote lremote = this->linkedRemotes[i];
|
for(uint8_t i = 0; i < SOMFY_MAX_LINKED_REMOTES; i++) {
|
||||||
if(lremote.getRemoteAddress() != 0) linkedAddresses[j++] = lremote.getRemoteAddress();
|
SomfyLinkedRemote lremote = this->linkedRemotes[i];
|
||||||
|
if(lremote.getRemoteAddress() != 0) linkedAddresses[j++] = lremote.getRemoteAddress();
|
||||||
|
}
|
||||||
|
pref.begin(shadeKey);
|
||||||
|
pref.putBytes("linkedAddr", linkedAddresses, sizeof(uint32_t) * SOMFY_MAX_LINKED_REMOTES);
|
||||||
|
pref.end();
|
||||||
}
|
}
|
||||||
pref.putBytes("linkedAddr", linkedAddresses, sizeof(uint32_t) * SOMFY_MAX_LINKED_REMOTES);
|
this->commit();
|
||||||
pref.end();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -505,13 +605,7 @@ void SomfyShade::checkMovement() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(currDir != this->direction && this->direction == 0) {
|
if(currDir != this->direction && this->direction == 0) {
|
||||||
char shadeKey[15];
|
this->commitShadePosition();
|
||||||
snprintf(shadeKey, sizeof(shadeKey), "SomfyShade%u", this->shadeId);
|
|
||||||
Serial.print("Writing current shade position: ");
|
|
||||||
Serial.println(this->currentPos, 4);
|
|
||||||
pref.begin(shadeKey);
|
|
||||||
pref.putFloat("currentPos", this->currentPos);
|
|
||||||
pref.end();
|
|
||||||
if(this->settingMyPos) {
|
if(this->settingMyPos) {
|
||||||
delay(200);
|
delay(200);
|
||||||
// Set this position before sending the command. If you don't the processFrame function
|
// Set this position before sending the command. If you don't the processFrame function
|
||||||
|
|
@ -521,23 +615,11 @@ void SomfyShade::checkMovement() {
|
||||||
SomfyRemote::sendCommand(somfy_commands::My, SETMY_REPEATS);
|
SomfyRemote::sendCommand(somfy_commands::My, SETMY_REPEATS);
|
||||||
this->settingMyPos = false;
|
this->settingMyPos = false;
|
||||||
this->seekingMyPos = false;
|
this->seekingMyPos = false;
|
||||||
Serial.print("Committing My Position: ");
|
this->commitMyPosition();
|
||||||
Serial.print(this->myPos);
|
|
||||||
Serial.println("%");
|
|
||||||
|
|
||||||
pref.begin(shadeKey);
|
|
||||||
pref.putUShort("myPos", this->myPos);
|
|
||||||
pref.end();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(currTiltDir != this->tiltDirection && this->tiltDirection == 0) {
|
if(currTiltDir != this->tiltDirection && this->tiltDirection == 0) {
|
||||||
char shadeKey[15];
|
this->commitTiltPosition();
|
||||||
snprintf(shadeKey, sizeof(shadeKey), "SomfyShade%u", this->shadeId);
|
|
||||||
Serial.print("Writing current shade tilt position: ");
|
|
||||||
Serial.println(this->currentTiltPos, 4);
|
|
||||||
pref.begin(shadeKey);
|
|
||||||
pref.putFloat("currentTiltPos", this->currentTiltPos);
|
|
||||||
pref.end();
|
|
||||||
}
|
}
|
||||||
if(currDir != this->direction || currPos != this->position || currTiltDir != this->tiltDirection || currTiltPos != this->tiltPosition) {
|
if(currDir != this->direction || currPos != this->position || currTiltDir != this->tiltDirection || currTiltPos != this->tiltPosition) {
|
||||||
// We need to emit on the socket that our state has changed.
|
// We need to emit on the socket that our state has changed.
|
||||||
|
|
@ -555,19 +637,36 @@ void SomfyShade::load() {
|
||||||
// Now load up each of the shades into memory.
|
// Now load up each of the shades into memory.
|
||||||
//Serial.print("key:");
|
//Serial.print("key:");
|
||||||
//Serial.println(shadeKey);
|
//Serial.println(shadeKey);
|
||||||
pref.begin(shadeKey);
|
|
||||||
|
pref.begin(shadeKey, !somfy.useNVS());
|
||||||
pref.getString("name", this->name, sizeof(this->name));
|
pref.getString("name", this->name, sizeof(this->name));
|
||||||
this->paired = pref.getBool("paired", false);
|
this->paired = pref.getBool("paired", false);
|
||||||
this->upTime = pref.getUShort("upTime", 10000);
|
if(pref.isKey("upTime") && pref.getType("upTime") != PreferenceType::PT_U32) {
|
||||||
this->downTime = pref.getUShort("downTime", 10000);
|
// We need to convert these to 32 bits because earlier versions did not support this.
|
||||||
this->setRemoteAddress(pref.getULong("remoteAddress", 0));
|
this->upTime = static_cast<uint32_t>(pref.getUShort("upTime", 1000));
|
||||||
|
this->downTime = static_cast<uint32_t>(pref.getUShort("downTime", 1000));
|
||||||
|
this->tiltTime = static_cast<uint32_t>(pref.getUShort("tiltTime", 7000));
|
||||||
|
if(somfy.useNVS()) {
|
||||||
|
pref.remove("upTime");
|
||||||
|
pref.putUInt("upTime", this->upTime);
|
||||||
|
pref.remove("downTime");
|
||||||
|
pref.putUInt("downTime", this->downTime);
|
||||||
|
pref.remove("tiltTime");
|
||||||
|
pref.putUInt("tiltTime", this->tiltTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this->upTime = pref.getUInt("upTime", this->upTime);
|
||||||
|
this->downTime = pref.getUInt("downTime", this->downTime);
|
||||||
|
this->tiltTime = pref.getUInt("tiltTime", this->tiltTime);
|
||||||
|
}
|
||||||
|
this->setRemoteAddress(pref.getUInt("remoteAddress", 0));
|
||||||
this->currentPos = pref.getFloat("currentPos", 0);
|
this->currentPos = pref.getFloat("currentPos", 0);
|
||||||
this->position = (uint8_t)floor(this->currentPos * 100);
|
this->position = (uint8_t)floor(this->currentPos * 100);
|
||||||
this->target = this->position;
|
this->target = this->position;
|
||||||
this->myPos = pref.getUShort("myPos", this->myPos);
|
this->myPos = pref.getUShort("myPos", this->myPos);
|
||||||
this->hasTilt = pref.getBool("hasTilt", false);
|
this->hasTilt = pref.getBool("hasTilt", false);
|
||||||
this->shadeType = static_cast<shade_types>(pref.getChar("shadeType", static_cast<uint8_t>(this->shadeType)));
|
this->shadeType = static_cast<shade_types>(pref.getChar("shadeType", static_cast<uint8_t>(this->shadeType)));
|
||||||
this->tiltTime = pref.getUShort("tiltTime", 3000);
|
|
||||||
this->currentTiltPos = pref.getFloat("currentTiltPos", 0);
|
this->currentTiltPos = pref.getFloat("currentTiltPos", 0);
|
||||||
this->tiltPosition = (uint8_t)floor(this->currentTiltPos * 100);
|
this->tiltPosition = (uint8_t)floor(this->currentTiltPos * 100);
|
||||||
this->tiltTarget = this->tiltPosition;
|
this->tiltTarget = this->tiltPosition;
|
||||||
|
|
@ -591,7 +690,6 @@ void SomfyShade::load() {
|
||||||
lremote.lastRollingCode = pref.getUShort(lremote.getRemotePrefId(), 0);
|
lremote.lastRollingCode = pref.getUShort(lremote.getRemotePrefId(), 0);
|
||||||
}
|
}
|
||||||
pref.end();
|
pref.end();
|
||||||
|
|
||||||
}
|
}
|
||||||
void SomfyShade::publish() {
|
void SomfyShade::publish() {
|
||||||
if(mqtt.connected()) {
|
if(mqtt.connected()) {
|
||||||
|
|
@ -698,10 +796,7 @@ void SomfyShade::processWaitingFrame() {
|
||||||
this->myPos = 255;
|
this->myPos = 255;
|
||||||
else
|
else
|
||||||
this->myPos = this->position;
|
this->myPos = this->position;
|
||||||
Serial.print(this->name);
|
this->commitMyPosition();
|
||||||
Serial.print(" MY POSITION SET TO:");
|
|
||||||
Serial.print(this->myPos);
|
|
||||||
Serial.println("%");
|
|
||||||
this->lastFrame.processed = true;
|
this->lastFrame.processed = true;
|
||||||
this->emitState();
|
this->emitState();
|
||||||
}
|
}
|
||||||
|
|
@ -755,6 +850,7 @@ void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) {
|
||||||
// This is an internal tilt command.
|
// This is an internal tilt command.
|
||||||
Serial.println("Processing Tilt UP...");
|
Serial.println("Processing Tilt UP...");
|
||||||
this->setTiltMovement(-1);
|
this->setTiltMovement(-1);
|
||||||
|
this->lastFrame.processed = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
@ -777,6 +873,7 @@ void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) {
|
||||||
// This is an internal tilt command.
|
// This is an internal tilt command.
|
||||||
Serial.println("Processing Tilt DOWN...");
|
Serial.println("Processing Tilt DOWN...");
|
||||||
this->setTiltMovement(1);
|
this->setTiltMovement(1);
|
||||||
|
this->lastFrame.processed = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
@ -833,13 +930,7 @@ void SomfyShade::setTiltMovement(int8_t dir) {
|
||||||
this->tiltStart = 0;
|
this->tiltStart = 0;
|
||||||
this->tiltDirection = dir;
|
this->tiltDirection = dir;
|
||||||
if(currDir != dir) {
|
if(currDir != dir) {
|
||||||
char shadeKey[15];
|
this->commitTiltPosition();
|
||||||
snprintf(shadeKey, sizeof(shadeKey), "SomfyShade%u", this->shadeId);
|
|
||||||
Serial.print("Writing current shade position:");
|
|
||||||
Serial.println(this->currentTiltPos, 4);
|
|
||||||
pref.begin(shadeKey);
|
|
||||||
pref.putFloat("currentTiltPos", this->currentTiltPos);
|
|
||||||
pref.end();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(this->direction != dir) {
|
else if(this->direction != dir) {
|
||||||
|
|
@ -852,7 +943,6 @@ void SomfyShade::setTiltMovement(int8_t dir) {
|
||||||
this->emitState();
|
this->emitState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SomfyShade::setMovement(int8_t dir) {
|
void SomfyShade::setMovement(int8_t dir) {
|
||||||
int8_t currDir = this->direction;
|
int8_t currDir = this->direction;
|
||||||
if(dir == 0) {
|
if(dir == 0) {
|
||||||
|
|
@ -861,13 +951,7 @@ void SomfyShade::setMovement(int8_t dir) {
|
||||||
this->moveStart = 0;
|
this->moveStart = 0;
|
||||||
this->direction = dir;
|
this->direction = dir;
|
||||||
if(currDir != dir) {
|
if(currDir != dir) {
|
||||||
char shadeKey[15];
|
this->commitShadePosition();
|
||||||
snprintf(shadeKey, sizeof(shadeKey), "SomfyShade%u", this->shadeId);
|
|
||||||
Serial.print("Writing current shade position:");
|
|
||||||
Serial.println(this->currentPos, 4);
|
|
||||||
pref.begin(shadeKey);
|
|
||||||
pref.putFloat("currentPos", this->currentPos);
|
|
||||||
pref.end();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(this->direction != dir) {
|
else if(this->direction != dir) {
|
||||||
|
|
@ -895,18 +979,11 @@ void SomfyShade::setMyPosition(uint8_t target) {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this->sendCommand(somfy_commands::My, SETMY_REPEATS);
|
this->sendCommand(somfy_commands::My, SETMY_REPEATS);
|
||||||
char shadeKey[15];
|
|
||||||
snprintf(shadeKey, sizeof(shadeKey), "SomfyShade%u", this->shadeId);
|
|
||||||
if(target == this->myPos)
|
if(target == this->myPos)
|
||||||
this->myPos = 255;
|
this->myPos = 255;
|
||||||
else
|
else
|
||||||
this->myPos = target;
|
this->myPos = target;
|
||||||
Serial.print("Writing my shade position:");
|
this->commitMyPosition();
|
||||||
Serial.print(this->myPos);
|
|
||||||
Serial.println("%");
|
|
||||||
pref.begin(shadeKey);
|
|
||||||
pref.putUShort("myPos", this->myPos);
|
|
||||||
pref.end();
|
|
||||||
this->emitState();
|
this->emitState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1005,29 +1082,34 @@ void SomfyShade::moveToTarget(uint8_t target) {
|
||||||
SomfyRemote::sendCommand(cmd);
|
SomfyRemote::sendCommand(cmd);
|
||||||
}
|
}
|
||||||
bool SomfyShade::save() {
|
bool SomfyShade::save() {
|
||||||
char shadeKey[15];
|
if(somfy.useNVS()) {
|
||||||
snprintf(shadeKey, sizeof(shadeKey), "SomfyShade%u", this->getShadeId());
|
char shadeKey[15];
|
||||||
pref.begin(shadeKey);
|
snprintf(shadeKey, sizeof(shadeKey), "SomfyShade%u", this->getShadeId());
|
||||||
pref.putString("name", this->name);
|
pref.begin(shadeKey);
|
||||||
pref.putBool("hasTilt", this->hasTilt);
|
pref.clear();
|
||||||
pref.putBool("paired", this->paired);
|
pref.putChar("shadeType", static_cast<uint8_t>(this->shadeType));
|
||||||
pref.putUShort("upTime", this->upTime);
|
pref.putUInt("remoteAddress", this->getRemoteAddress());
|
||||||
pref.putUShort("downTime", this->downTime);
|
pref.putString("name", this->name);
|
||||||
pref.putUShort("tiltTime", this->tiltTime);
|
pref.putBool("hasTilt", this->hasTilt);
|
||||||
pref.putULong("remoteAddress", this->getRemoteAddress());
|
pref.putBool("paired", this->paired);
|
||||||
pref.putFloat("currentPos", this->currentPos);
|
pref.putUInt("upTime", this->upTime);
|
||||||
pref.putFloat("currentTiltPos", this->currentTiltPos);
|
pref.putUInt("downTime", this->downTime);
|
||||||
pref.putUShort("myPos", this->myPos);
|
pref.putUInt("tiltTime", this->tiltTime);
|
||||||
pref.putChar("shadeType", static_cast<uint8_t>(this->shadeType));
|
pref.putFloat("currentPos", this->currentPos);
|
||||||
uint32_t linkedAddresses[SOMFY_MAX_LINKED_REMOTES];
|
pref.putFloat("currentTiltPos", this->currentTiltPos);
|
||||||
memset(linkedAddresses, 0x00, sizeof(linkedAddresses));
|
pref.putUShort("myPos", this->myPos);
|
||||||
uint8_t j = 0;
|
uint32_t linkedAddresses[SOMFY_MAX_LINKED_REMOTES];
|
||||||
for(uint8_t i = 0; i < SOMFY_MAX_LINKED_REMOTES; i++) {
|
memset(linkedAddresses, 0x00, sizeof(linkedAddresses));
|
||||||
SomfyLinkedRemote lremote = this->linkedRemotes[i];
|
uint8_t j = 0;
|
||||||
if(lremote.getRemoteAddress() != 0) linkedAddresses[j++] = lremote.getRemoteAddress();
|
for(uint8_t i = 0; i < SOMFY_MAX_LINKED_REMOTES; i++) {
|
||||||
|
SomfyLinkedRemote lremote = this->linkedRemotes[i];
|
||||||
|
if(lremote.getRemoteAddress() != 0) linkedAddresses[j++] = lremote.getRemoteAddress();
|
||||||
|
}
|
||||||
|
pref.remove("linkedAddr");
|
||||||
|
pref.putBytes("linkedAddr", linkedAddresses, sizeof(uint32_t) * SOMFY_MAX_LINKED_REMOTES);
|
||||||
|
pref.end();
|
||||||
}
|
}
|
||||||
pref.putBytes("linkedAddr", linkedAddresses, sizeof(uint32_t) * SOMFY_MAX_LINKED_REMOTES);
|
this->commit();
|
||||||
pref.end();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool SomfyShade::fromJSON(JsonObject &obj) {
|
bool SomfyShade::fromJSON(JsonObject &obj) {
|
||||||
|
|
@ -1152,7 +1234,6 @@ uint8_t SomfyShadeController::getNextShadeId() {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 255;
|
return 255;
|
||||||
}
|
}
|
||||||
uint8_t SomfyShadeController::shadeCount() {
|
uint8_t SomfyShadeController::shadeCount() {
|
||||||
|
|
@ -1188,40 +1269,61 @@ SomfyShade *SomfyShadeController::addShade(JsonObject &obj) {
|
||||||
}
|
}
|
||||||
SomfyShade *SomfyShadeController::addShade() {
|
SomfyShade *SomfyShadeController::addShade() {
|
||||||
uint8_t shadeId = this->getNextShadeId();
|
uint8_t shadeId = this->getNextShadeId();
|
||||||
SomfyShade *shade = nullptr;
|
// So the next shade id will be the first one we run into with an id of 255 so
|
||||||
for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) {
|
// if it gets deleted in the middle then it will get the first slot that is empty.
|
||||||
if(this->shades[i].getShadeId() == 255) {
|
// There is no apparent way around this. In the future we might actually add an indexer
|
||||||
shade = &this->shades[i];
|
// to it for sorting later.
|
||||||
break;
|
if(shadeId == 255) return nullptr;
|
||||||
}
|
SomfyShade *shade = &this->shades[shadeId - 1];
|
||||||
}
|
|
||||||
if(shade) {
|
if(shade) {
|
||||||
shade->setShadeId(shadeId);
|
shade->setShadeId(shadeId);
|
||||||
for(uint8_t i = 0; i < sizeof(this->m_shadeIds); i++) {
|
this->isDirty = true;
|
||||||
this->m_shadeIds[i] = this->shades[i].getShadeId();
|
if(this->useNVS()) {
|
||||||
}
|
for(uint8_t i = 0; i < sizeof(this->m_shadeIds); i++) {
|
||||||
sortArray<uint8_t>(this->m_shadeIds, sizeof(this->m_shadeIds));
|
this->m_shadeIds[i] = this->shades[i].getShadeId();
|
||||||
uint8_t id = 0;
|
|
||||||
// This little diddy is about a bug I had previously that left duplicates in the
|
|
||||||
// sorted array. So we will walk the sorted array until we hit a duplicate where the previous
|
|
||||||
// value == the current value. Set it to 255 then sort the array again.
|
|
||||||
// 1,1,2,2,3,3,255...
|
|
||||||
bool hadDups = false;
|
|
||||||
for(uint8_t i = 0; i < sizeof(this->m_shadeIds); i++) {
|
|
||||||
if(this->m_shadeIds[i] == 255) break;
|
|
||||||
if(id == this->m_shadeIds[i]) {
|
|
||||||
id = this->m_shadeIds[i];
|
|
||||||
this->m_shadeIds[i] = 255;
|
|
||||||
hadDups = true;
|
|
||||||
}
|
}
|
||||||
else {
|
sortArray<uint8_t>(this->m_shadeIds, sizeof(this->m_shadeIds));
|
||||||
id = this->m_shadeIds[i];
|
uint8_t id = 0;
|
||||||
|
// This little diddy is about a bug I had previously that left duplicates in the
|
||||||
|
// sorted array. So we will walk the sorted array until we hit a duplicate where the previous
|
||||||
|
// value == the current value. Set it to 255 then sort the array again.
|
||||||
|
// 1,1,2,2,3,3,255...
|
||||||
|
bool hadDups = false;
|
||||||
|
for(uint8_t i = 0; i < sizeof(this->m_shadeIds); i++) {
|
||||||
|
if(this->m_shadeIds[i] == 255) break;
|
||||||
|
if(id == this->m_shadeIds[i]) {
|
||||||
|
id = this->m_shadeIds[i];
|
||||||
|
this->m_shadeIds[i] = 255;
|
||||||
|
hadDups = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
id = this->m_shadeIds[i];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if(hadDups) sortArray<uint8_t>(this->m_shadeIds, sizeof(this->m_shadeIds));
|
||||||
|
pref.begin("Shades");
|
||||||
|
pref.remove("shadeIds");
|
||||||
|
int x = pref.putBytes("shadeIds", this->m_shadeIds, sizeof(this->m_shadeIds));
|
||||||
|
Serial.printf("WROTE %d bytes to shadeIds\n", x);
|
||||||
|
pref.end();
|
||||||
|
for(uint8_t i = 0; i < sizeof(this->m_shadeIds); i++) {
|
||||||
|
if(i != 0) Serial.print(",");
|
||||||
|
else Serial.print("Shade Ids: ");
|
||||||
|
Serial.print(this->m_shadeIds[i]);
|
||||||
|
}
|
||||||
|
Serial.println();
|
||||||
|
pref.begin("Shades");
|
||||||
|
pref.getBytes("shadeIds", this->m_shadeIds, sizeof(this->m_shadeIds));
|
||||||
|
Serial.print("LENGTH:");
|
||||||
|
Serial.println(pref.getBytesLength("shadeIds"));
|
||||||
|
pref.end();
|
||||||
|
for(uint8_t i = 0; i < sizeof(this->m_shadeIds); i++) {
|
||||||
|
if(i != 0) Serial.print(",");
|
||||||
|
else Serial.print("Shade Ids: ");
|
||||||
|
Serial.print(this->m_shadeIds[i]);
|
||||||
|
}
|
||||||
|
Serial.println();
|
||||||
}
|
}
|
||||||
if(hadDups) sortArray<uint8_t>(this->m_shadeIds, sizeof(this->m_shadeIds));
|
|
||||||
pref.begin("Shades");
|
|
||||||
pref.putBytes("shadeIds", this->m_shadeIds, sizeof(this->m_shadeIds));
|
|
||||||
pref.end();
|
|
||||||
}
|
}
|
||||||
return shade;
|
return shade;
|
||||||
}
|
}
|
||||||
|
|
@ -1231,6 +1333,7 @@ void SomfyRemote::sendCommand(somfy_commands cmd, uint8_t repeat) {
|
||||||
frame.remoteAddress = this->getRemoteAddress();
|
frame.remoteAddress = this->getRemoteAddress();
|
||||||
frame.cmd = cmd;
|
frame.cmd = cmd;
|
||||||
frame.repeats = repeat;
|
frame.repeats = repeat;
|
||||||
|
this->lastRollingCode = frame.rollingCode;
|
||||||
somfy.sendFrame(frame, repeat);
|
somfy.sendFrame(frame, repeat);
|
||||||
somfy.processFrame(frame, true);
|
somfy.processFrame(frame, true);
|
||||||
}
|
}
|
||||||
|
|
@ -1261,18 +1364,24 @@ bool SomfyShadeController::deleteShade(uint8_t shadeId) {
|
||||||
this->shades[i].setShadeId(255);
|
this->shades[i].setShadeId(255);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for(uint8_t i = 0; i < sizeof(this->m_shadeIds) - 1; i++) {
|
if(this->useNVS()) {
|
||||||
if(this->m_shadeIds[i] == shadeId) {
|
for(uint8_t i = 0; i < sizeof(this->m_shadeIds) - 1; i++) {
|
||||||
this->m_shadeIds[i] = 255;
|
if(this->m_shadeIds[i] == shadeId) {
|
||||||
|
this->m_shadeIds[i] = 255;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//qsort(this->m_shadeIds, sizeof(this->m_shadeIds)/sizeof(this->m_shadeIds[0]), sizeof(this->m_shadeIds[0]), sort_asc);
|
||||||
|
sortArray<uint8_t>(this->m_shadeIds, sizeof(this->m_shadeIds));
|
||||||
|
|
||||||
|
pref.begin("Shades");
|
||||||
|
pref.putBytes("shadeIds", this->m_shadeIds, sizeof(this->m_shadeIds));
|
||||||
|
pref.end();
|
||||||
}
|
}
|
||||||
//qsort(this->m_shadeIds, sizeof(this->m_shadeIds)/sizeof(this->m_shadeIds[0]), sizeof(this->m_shadeIds[0]), sort_asc);
|
this->commit();
|
||||||
sortArray<uint8_t>(this->m_shadeIds, sizeof(this->m_shadeIds));
|
|
||||||
pref.begin("Shades");
|
|
||||||
pref.putBytes("shadeIds", this->m_shadeIds, sizeof(this->m_shadeIds));
|
|
||||||
pref.end();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
bool SomfyShadeController::loadShadesFile(const char *filename) { return ShadeConfigFile::load(this, filename); }
|
||||||
uint16_t SomfyRemote::getNextRollingCode() {
|
uint16_t SomfyRemote::getNextRollingCode() {
|
||||||
pref.begin("ShadeCodes");
|
pref.begin("ShadeCodes");
|
||||||
uint16_t code = pref.getUShort(this->m_remotePrefId, 0);
|
uint16_t code = pref.getUShort(this->m_remotePrefId, 0);
|
||||||
|
|
@ -1315,15 +1424,6 @@ bool SomfyShadeController::toJSON(JsonObject &obj) {
|
||||||
this->transceiver.toJSON(oradio);
|
this->transceiver.toJSON(oradio);
|
||||||
JsonArray arr = obj.createNestedArray("shades");
|
JsonArray arr = obj.createNestedArray("shades");
|
||||||
this->toJSON(arr);
|
this->toJSON(arr);
|
||||||
/*
|
|
||||||
for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) {
|
|
||||||
SomfyShade &shade = this->shades[i];
|
|
||||||
if(shade.getShadeId() != 255) {
|
|
||||||
JsonObject oshade = arr.createNestedObject();
|
|
||||||
shade.toJSON(oshade);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool SomfyShadeController::toJSON(JsonArray &arr) {
|
bool SomfyShadeController::toJSON(JsonArray &arr) {
|
||||||
|
|
@ -1341,6 +1441,10 @@ void SomfyShadeController::loop() {
|
||||||
for(uint8_t i; i < SOMFY_MAX_SHADES; i++) {
|
for(uint8_t i; i < SOMFY_MAX_SHADES; i++) {
|
||||||
if(this->shades[i].getShadeId() != 255) this->shades[i].checkMovement();
|
if(this->shades[i].getShadeId() != 255) this->shades[i].checkMovement();
|
||||||
}
|
}
|
||||||
|
// Only commit the file once per second.
|
||||||
|
if(this->isDirty && millis() - this->lastCommit > 1000) {
|
||||||
|
this->commit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
SomfyLinkedRemote::SomfyLinkedRemote() {}
|
SomfyLinkedRemote::SomfyLinkedRemote() {}
|
||||||
|
|
||||||
|
|
@ -1552,9 +1656,6 @@ bool Transceiver::fromJSON(JsonObject& obj) {
|
||||||
bool Transceiver::save() {
|
bool Transceiver::save() {
|
||||||
this->config.save();
|
this->config.save();
|
||||||
this->config.apply();
|
this->config.apply();
|
||||||
//ELECHOUSE_cc1101.setRxBW(this->config.rxBandwidth); // Set the Receive Bandwidth in kHz. Value from 58.03 to 812.50. Default is 812.50 kHz.
|
|
||||||
//ELECHOUSE_cc1101.setPA(this->config.txPower); // Set TxPower. The following settings are possible depending on the frequency band. (-30 -20 -15 -10 -6 0 5 7 10 11 12) Default is max!
|
|
||||||
//ELECHOUSE_cc1101.setDeviation(this->config.deviation);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool Transceiver::end() {
|
bool Transceiver::end() {
|
||||||
|
|
@ -1570,15 +1671,18 @@ void transceiver_config_t::fromJSON(JsonObject& obj) {
|
||||||
if(obj.containsKey("RXPin")) this->RXPin = obj["RXPin"];
|
if(obj.containsKey("RXPin")) this->RXPin = obj["RXPin"];
|
||||||
if(obj.containsKey("SCKPin")) this->SCKPin = obj["SCKPin"];
|
if(obj.containsKey("SCKPin")) this->SCKPin = obj["SCKPin"];
|
||||||
if(obj.containsKey("TXPin")) this->TXPin = obj["TXPin"];
|
if(obj.containsKey("TXPin")) this->TXPin = obj["TXPin"];
|
||||||
|
if(obj.containsKey("rxBandwidth")) this->rxBandwidth = obj["rxBandwidth"]; // float
|
||||||
|
if(obj.containsKey("frequency")) this->frequency = obj["frequency"]; // float
|
||||||
|
if(obj.containsKey("deviation")) this->deviation = obj["deviation"]; // float
|
||||||
|
if(obj.containsKey("enabled")) this->enabled = obj["enabled"];
|
||||||
|
if(obj.containsKey("txPower")) this->txPower = obj["txPower"];
|
||||||
|
|
||||||
|
/*
|
||||||
if (obj.containsKey("internalCCMode")) this->internalCCMode = obj["internalCCMode"];
|
if (obj.containsKey("internalCCMode")) this->internalCCMode = obj["internalCCMode"];
|
||||||
if (obj.containsKey("modulationMode")) this->modulationMode = obj["modulationMode"];
|
if (obj.containsKey("modulationMode")) this->modulationMode = obj["modulationMode"];
|
||||||
if (obj.containsKey("frequency")) this->frequency = obj["frequency"]; // float
|
|
||||||
if (obj.containsKey("deviation")) this->deviation = obj["deviation"]; // float
|
|
||||||
if (obj.containsKey("channel")) this->channel = obj["channel"];
|
if (obj.containsKey("channel")) this->channel = obj["channel"];
|
||||||
if (obj.containsKey("channelSpacing")) this->channelSpacing = obj["channelSpacing"]; // float
|
if (obj.containsKey("channelSpacing")) this->channelSpacing = obj["channelSpacing"]; // float
|
||||||
if (obj.containsKey("rxBandwidth")) this->rxBandwidth = obj["rxBandwidth"]; // float
|
|
||||||
if (obj.containsKey("dataRate")) this->dataRate = obj["dataRate"]; // float
|
if (obj.containsKey("dataRate")) this->dataRate = obj["dataRate"]; // float
|
||||||
if (obj.containsKey("txPower")) this->txPower = obj["txPower"];
|
|
||||||
if (obj.containsKey("syncMode")) this->syncMode = obj["syncMode"];
|
if (obj.containsKey("syncMode")) this->syncMode = obj["syncMode"];
|
||||||
if (obj.containsKey("syncWordHigh")) this->syncWordHigh = obj["syncWordHigh"];
|
if (obj.containsKey("syncWordHigh")) this->syncWordHigh = obj["syncWordHigh"];
|
||||||
if (obj.containsKey("syncWordLow")) this->syncWordLow = obj["syncWordLow"];
|
if (obj.containsKey("syncWordLow")) this->syncWordLow = obj["syncWordLow"];
|
||||||
|
|
@ -1597,7 +1701,7 @@ void transceiver_config_t::fromJSON(JsonObject& obj) {
|
||||||
if (obj.containsKey("pqtThreshold")) this->pqtThreshold = obj["pqtThreshold"];
|
if (obj.containsKey("pqtThreshold")) this->pqtThreshold = obj["pqtThreshold"];
|
||||||
if (obj.containsKey("appendStatus")) this->appendStatus = obj["appendStatus"];
|
if (obj.containsKey("appendStatus")) this->appendStatus = obj["appendStatus"];
|
||||||
if (obj.containsKey("printBuffer")) this->printBuffer = obj["printBuffer"];
|
if (obj.containsKey("printBuffer")) this->printBuffer = obj["printBuffer"];
|
||||||
if(obj.containsKey("enabled")) this->enabled = obj["enabled"];
|
*/
|
||||||
Serial.printf("SCK:%u MISO:%u MOSI:%u CSN:%u RX:%u TX:%u\n", this->SCKPin, this->MISOPin, this->MOSIPin, this->CSNPin, this->RXPin, this->TXPin);
|
Serial.printf("SCK:%u MISO:%u MOSI:%u CSN:%u RX:%u TX:%u\n", this->SCKPin, this->MISOPin, this->MOSIPin, this->CSNPin, this->RXPin, this->TXPin);
|
||||||
}
|
}
|
||||||
void transceiver_config_t::toJSON(JsonObject& obj) {
|
void transceiver_config_t::toJSON(JsonObject& obj) {
|
||||||
|
|
@ -1608,15 +1712,16 @@ void transceiver_config_t::toJSON(JsonObject& obj) {
|
||||||
obj["MOSIPin"] = this->MOSIPin;
|
obj["MOSIPin"] = this->MOSIPin;
|
||||||
obj["MISOPin"] = this->MISOPin;
|
obj["MISOPin"] = this->MISOPin;
|
||||||
obj["CSNPin"] = this->CSNPin;
|
obj["CSNPin"] = this->CSNPin;
|
||||||
obj["internalCCMode"] = this->internalCCMode;
|
obj["rxBandwidth"] = this->rxBandwidth; // float
|
||||||
obj["modulationMode"] = this->modulationMode;
|
|
||||||
obj["frequency"] = this->frequency; // float
|
obj["frequency"] = this->frequency; // float
|
||||||
obj["deviation"] = this->deviation; // float
|
obj["deviation"] = this->deviation; // float
|
||||||
|
obj["txPower"] = this->txPower;
|
||||||
|
/*
|
||||||
|
obj["internalCCMode"] = this->internalCCMode;
|
||||||
|
obj["modulationMode"] = this->modulationMode;
|
||||||
obj["channel"] = this->channel;
|
obj["channel"] = this->channel;
|
||||||
obj["channelSpacing"] = this->channelSpacing; // float
|
obj["channelSpacing"] = this->channelSpacing; // float
|
||||||
obj["rxBandwidth"] = this->rxBandwidth; // float
|
|
||||||
obj["dataRate"] = this->dataRate; // float
|
obj["dataRate"] = this->dataRate; // float
|
||||||
obj["txPower"] = this->txPower;
|
|
||||||
obj["syncMode"] = this->syncMode;
|
obj["syncMode"] = this->syncMode;
|
||||||
obj["syncWordHigh"] = this->syncWordHigh;
|
obj["syncWordHigh"] = this->syncWordHigh;
|
||||||
obj["syncWordLow"] = this->syncWordLow;
|
obj["syncWordLow"] = this->syncWordLow;
|
||||||
|
|
@ -1635,6 +1740,7 @@ void transceiver_config_t::toJSON(JsonObject& obj) {
|
||||||
obj["pqtThreshold"] = this->pqtThreshold;
|
obj["pqtThreshold"] = this->pqtThreshold;
|
||||||
obj["appendStatus"] = this->appendStatus;
|
obj["appendStatus"] = this->appendStatus;
|
||||||
obj["printBuffer"] = somfy.transceiver.printBuffer;
|
obj["printBuffer"] = somfy.transceiver.printBuffer;
|
||||||
|
*/
|
||||||
obj["enabled"] = this->enabled;
|
obj["enabled"] = this->enabled;
|
||||||
obj["radioInit"] = this->radioInit;
|
obj["radioInit"] = this->radioInit;
|
||||||
Serial.print("Serialize Radio JSON ");
|
Serial.print("Serialize Radio JSON ");
|
||||||
|
|
@ -1642,6 +1748,7 @@ void transceiver_config_t::toJSON(JsonObject& obj) {
|
||||||
}
|
}
|
||||||
void transceiver_config_t::save() {
|
void transceiver_config_t::save() {
|
||||||
pref.begin("CC1101");
|
pref.begin("CC1101");
|
||||||
|
pref.clear();
|
||||||
pref.putUChar("type", this->type);
|
pref.putUChar("type", this->type);
|
||||||
pref.putUChar("TXPin", this->TXPin);
|
pref.putUChar("TXPin", this->TXPin);
|
||||||
pref.putUChar("RXPin", this->RXPin);
|
pref.putUChar("RXPin", this->RXPin);
|
||||||
|
|
@ -1649,10 +1756,15 @@ void transceiver_config_t::save() {
|
||||||
pref.putUChar("MOSIPin", this->MOSIPin);
|
pref.putUChar("MOSIPin", this->MOSIPin);
|
||||||
pref.putUChar("MISOPin", this->MISOPin);
|
pref.putUChar("MISOPin", this->MISOPin);
|
||||||
pref.putUChar("CSNPin", this->CSNPin);
|
pref.putUChar("CSNPin", this->CSNPin);
|
||||||
pref.putBool("internalCCMode", this->internalCCMode);
|
|
||||||
pref.putUChar("modulationMode", this->modulationMode);
|
|
||||||
pref.putFloat("frequency", this->frequency); // float
|
pref.putFloat("frequency", this->frequency); // float
|
||||||
pref.putFloat("deviation", this->deviation); // float
|
pref.putFloat("deviation", this->deviation); // float
|
||||||
|
pref.putFloat("rxBandwidth", this->rxBandwidth); // float
|
||||||
|
pref.putBool("enabled", this->enabled);
|
||||||
|
pref.putBool("radioInit", true);
|
||||||
|
|
||||||
|
/*
|
||||||
|
pref.putBool("internalCCMode", this->internalCCMode);
|
||||||
|
pref.putUChar("modulationMode", this->modulationMode);
|
||||||
pref.putUChar("channel", this->channel);
|
pref.putUChar("channel", this->channel);
|
||||||
pref.putFloat("channelSpacing", this->channelSpacing); // float
|
pref.putFloat("channelSpacing", this->channelSpacing); // float
|
||||||
pref.putFloat("rxBandwidth", this->rxBandwidth); // float
|
pref.putFloat("rxBandwidth", this->rxBandwidth); // float
|
||||||
|
|
@ -1675,13 +1787,18 @@ void transceiver_config_t::save() {
|
||||||
pref.putUChar("minPreambleBytes", this->minPreambleBytes);
|
pref.putUChar("minPreambleBytes", this->minPreambleBytes);
|
||||||
pref.putUChar("pqtThreshold", this->pqtThreshold);
|
pref.putUChar("pqtThreshold", this->pqtThreshold);
|
||||||
pref.putBool("appendStatus", this->appendStatus);
|
pref.putBool("appendStatus", this->appendStatus);
|
||||||
pref.putBool("enabled", this->enabled);
|
*/
|
||||||
pref.putBool("radioInit", true);
|
|
||||||
pref.end();
|
pref.end();
|
||||||
|
|
||||||
Serial.print("Save Radio Settings ");
|
Serial.print("Save Radio Settings ");
|
||||||
Serial.printf("SCK:%u MISO:%u MOSI:%u CSN:%u RX:%u TX:%u\n", this->SCKPin, this->MISOPin, this->MOSIPin, this->CSNPin, this->RXPin, this->TXPin);
|
Serial.printf("SCK:%u MISO:%u MOSI:%u CSN:%u RX:%u TX:%u\n", this->SCKPin, this->MISOPin, this->MOSIPin, this->CSNPin, this->RXPin, this->TXPin);
|
||||||
}
|
}
|
||||||
|
void transceiver_config_t::removeNVSKey(const char *key) {
|
||||||
|
if(pref.isKey(key)) {
|
||||||
|
Serial.printf("Removing NVS Key: CC1101.%s\n", key);
|
||||||
|
pref.remove(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
void transceiver_config_t::load() {
|
void transceiver_config_t::load() {
|
||||||
pref.begin("CC1101");
|
pref.begin("CC1101");
|
||||||
this->type = pref.getUChar("type", 56);
|
this->type = pref.getUChar("type", 56);
|
||||||
|
|
@ -1691,35 +1808,37 @@ void transceiver_config_t::load() {
|
||||||
this->MOSIPin = pref.getUChar("MOSIPin", this->MOSIPin);
|
this->MOSIPin = pref.getUChar("MOSIPin", this->MOSIPin);
|
||||||
this->MISOPin = pref.getUChar("MISOPin", this->MISOPin);
|
this->MISOPin = pref.getUChar("MISOPin", this->MISOPin);
|
||||||
this->CSNPin = pref.getUChar("CSNPin", this->CSNPin);
|
this->CSNPin = pref.getUChar("CSNPin", this->CSNPin);
|
||||||
this->internalCCMode = pref.getBool("internalCCMode", this->internalCCMode);
|
|
||||||
this->modulationMode = pref.getUChar("modulationMode", this->modulationMode);
|
|
||||||
this->frequency = pref.getFloat("frequency", this->frequency); // float
|
this->frequency = pref.getFloat("frequency", this->frequency); // float
|
||||||
this->deviation = pref.getFloat("deviation", this->deviation); // float
|
this->deviation = pref.getFloat("deviation", this->deviation); // float
|
||||||
this->channel = pref.getUChar("channel", this->channel);
|
|
||||||
this->channelSpacing = pref.getFloat("channelSpacing", this->channelSpacing); // float
|
|
||||||
this->rxBandwidth = pref.getFloat("rxBandwidth", this->rxBandwidth); // float
|
|
||||||
this->dataRate = pref.getFloat("dataRate", this->dataRate); // float
|
|
||||||
this->txPower = pref.getChar("txPower", this->txPower);
|
|
||||||
this->syncMode = pref.getUChar("syncMode", this->syncMode);
|
|
||||||
this->syncWordHigh = pref.getUShort("syncWordHigh", this->syncWordHigh);
|
|
||||||
this->syncWordLow = pref.getUShort("syncWordLow", this->syncWordLow);
|
|
||||||
this->addrCheckMode = pref.getUChar("addrCheckMode", this->addrCheckMode);
|
|
||||||
this->checkAddr = pref.getUChar("checkAddr", this->checkAddr);
|
|
||||||
this->dataWhitening = pref.getBool("dataWhitening", this->dataWhitening);
|
|
||||||
this->pktFormat = pref.getUChar("pktFormat", this->pktFormat);
|
|
||||||
this->pktLengthMode = pref.getUChar("pktLengthMode", this->pktLengthMode);
|
|
||||||
this->pktLength = pref.getUChar("pktLength", this->pktLength);
|
|
||||||
this->useCRC = pref.getBool("useCRC", this->useCRC);
|
|
||||||
this->autoFlushCRC = pref.getBool("autoFlushCRC", this->autoFlushCRC);
|
|
||||||
this->disableDCFilter = pref.getBool("disableDCFilter", this->disableDCFilter);
|
|
||||||
this->enableManchester = pref.getBool("enableManchester", this->enableManchester);
|
|
||||||
this->enableFEC = pref.getBool("enableFEC", this->enableFEC);
|
|
||||||
this->minPreambleBytes = pref.getUChar("minPreambleBytes", this->minPreambleBytes);
|
|
||||||
this->pqtThreshold = pref.getUChar("pqtThreshold", this->pqtThreshold);
|
|
||||||
this->appendStatus = pref.getBool("appendStatus", this->appendStatus);
|
|
||||||
this->enabled = pref.getBool("enabled", this->enabled);
|
this->enabled = pref.getBool("enabled", this->enabled);
|
||||||
|
this->txPower = pref.getChar("txPower", this->txPower);
|
||||||
|
this->rxBandwidth = pref.getFloat("rxBandwidth", this->rxBandwidth);
|
||||||
|
|
||||||
|
|
||||||
|
this->removeNVSKey("internalCCMode");
|
||||||
|
this->removeNVSKey("modulationMode");
|
||||||
|
this->removeNVSKey("channel");
|
||||||
|
this->removeNVSKey("channelSpacing");
|
||||||
|
this->removeNVSKey("dataRate");
|
||||||
|
this->removeNVSKey("syncMode");
|
||||||
|
this->removeNVSKey("syncWordHigh");
|
||||||
|
this->removeNVSKey("syncWordLow");
|
||||||
|
this->removeNVSKey("addrCheckMode");
|
||||||
|
this->removeNVSKey("checkAddr");
|
||||||
|
this->removeNVSKey("dataWhitening");
|
||||||
|
this->removeNVSKey("pktFormat");
|
||||||
|
this->removeNVSKey("pktLengthMode");
|
||||||
|
this->removeNVSKey("pktLength");
|
||||||
|
this->removeNVSKey("useCRC");
|
||||||
|
this->removeNVSKey("autoFlushCRC");
|
||||||
|
this->removeNVSKey("disableDCFilter");
|
||||||
|
this->removeNVSKey("enableManchester");
|
||||||
|
this->removeNVSKey("enableFEC");
|
||||||
|
this->removeNVSKey("minPreambleBytes");
|
||||||
|
this->removeNVSKey("pqtThreshold");
|
||||||
|
this->removeNVSKey("appendStatus");
|
||||||
pref.end();
|
pref.end();
|
||||||
this->printBuffer = somfy.transceiver.printBuffer;
|
//this->printBuffer = somfy.transceiver.printBuffer;
|
||||||
}
|
}
|
||||||
void transceiver_config_t::apply() {
|
void transceiver_config_t::apply() {
|
||||||
somfy.transceiver.disableReceive();
|
somfy.transceiver.disableReceive();
|
||||||
|
|
@ -1733,7 +1852,7 @@ void transceiver_config_t::apply() {
|
||||||
this->radioInit = false;
|
this->radioInit = false;
|
||||||
pref.end();
|
pref.end();
|
||||||
if(!radioInit) return;
|
if(!radioInit) return;
|
||||||
Serial.print("Applying Initializing radio settings ");
|
Serial.print("Applying radio settings ");
|
||||||
Serial.printf("Setting Data Pins RX:%u TX:%u\n", this->RXPin, this->TXPin);
|
Serial.printf("Setting Data Pins RX:%u TX:%u\n", this->RXPin, this->TXPin);
|
||||||
ELECHOUSE_cc1101.setGDO(this->TXPin, this->RXPin);
|
ELECHOUSE_cc1101.setGDO(this->TXPin, this->RXPin);
|
||||||
Serial.printf("Setting SPI Pins SCK:%u MISO:%u MOSI:%u CSN:%u\n", this->SCKPin, this->MISOPin, this->MOSIPin, this->CSNPin);
|
Serial.printf("Setting SPI Pins SCK:%u MISO:%u MOSI:%u CSN:%u\n", this->SCKPin, this->MISOPin, this->MOSIPin, this->CSNPin);
|
||||||
|
|
@ -1742,6 +1861,7 @@ void transceiver_config_t::apply() {
|
||||||
ELECHOUSE_cc1101.Init();
|
ELECHOUSE_cc1101.Init();
|
||||||
ELECHOUSE_cc1101.setMHZ(this->frequency); // Here you can set your basic frequency. The lib calculates the frequency automatically (default = 433.92).The cc1101 can: 300-348 MHZ, 387-464MHZ and 779-928MHZ. Read More info from datasheet.
|
ELECHOUSE_cc1101.setMHZ(this->frequency); // Here you can set your basic frequency. The lib calculates the frequency automatically (default = 433.92).The cc1101 can: 300-348 MHZ, 387-464MHZ and 779-928MHZ. Read More info from datasheet.
|
||||||
ELECHOUSE_cc1101.setRxBW(this->rxBandwidth); // Set the Receive Bandwidth in kHz. Value from 58.03 to 812.50. Default is 812.50 kHz.
|
ELECHOUSE_cc1101.setRxBW(this->rxBandwidth); // Set the Receive Bandwidth in kHz. Value from 58.03 to 812.50. Default is 812.50 kHz.
|
||||||
|
ELECHOUSE_cc1101.setDeviation(this->deviation);
|
||||||
ELECHOUSE_cc1101.setPA(this->txPower); // Set TxPower. The following settings are possible depending on the frequency band. (-30 -20 -15 -10 -6 0 5 7 10 11 12) Default is max!
|
ELECHOUSE_cc1101.setPA(this->txPower); // Set TxPower. The following settings are possible depending on the frequency band. (-30 -20 -15 -10 -6 0 5 7 10 11 12) Default is max!
|
||||||
//ELECHOUSE_cc1101.setCCMode(this->internalCCMode); // set config for internal transmission mode.
|
//ELECHOUSE_cc1101.setCCMode(this->internalCCMode); // set config for internal transmission mode.
|
||||||
//ELECHOUSE_cc1101.setModulation(this->modulationMode); // set modulation mode. 0 = 2-FSK, 1 = GFSK, 2 = ASK/OOK, 3 = 4-FSK, 4 = MSK.
|
//ELECHOUSE_cc1101.setModulation(this->modulationMode); // set modulation mode. 0 = 2-FSK, 1 = GFSK, 2 = ASK/OOK, 3 = 4-FSK, 4 = MSK.
|
||||||
|
|
|
||||||
34
Somfy.h
34
Somfy.h
|
|
@ -4,6 +4,12 @@
|
||||||
#define SOMFY_MAX_SHADES 32
|
#define SOMFY_MAX_SHADES 32
|
||||||
#define SOMFY_MAX_LINKED_REMOTES 5
|
#define SOMFY_MAX_LINKED_REMOTES 5
|
||||||
|
|
||||||
|
typedef struct appver_t {
|
||||||
|
uint8_t major;
|
||||||
|
uint8_t minor;
|
||||||
|
uint8_t build;
|
||||||
|
};
|
||||||
|
|
||||||
enum class somfy_commands : byte {
|
enum class somfy_commands : byte {
|
||||||
My = 0x1,
|
My = 0x1,
|
||||||
Up = 0x2,
|
Up = 0x2,
|
||||||
|
|
@ -99,9 +105,9 @@ class SomfyShade : public SomfyRemote {
|
||||||
char name[21] = "";
|
char name[21] = "";
|
||||||
void setShadeId(uint8_t id) { shadeId = id; }
|
void setShadeId(uint8_t id) { shadeId = id; }
|
||||||
uint8_t getShadeId() { return shadeId; }
|
uint8_t getShadeId() { return shadeId; }
|
||||||
uint16_t upTime = 10000;
|
uint32_t upTime = 10000;
|
||||||
uint16_t downTime = 10000;
|
uint32_t downTime = 10000;
|
||||||
uint16_t tiltTime = 5000;
|
uint32_t tiltTime = 7000;
|
||||||
bool save();
|
bool save();
|
||||||
bool isIdle();
|
bool isIdle();
|
||||||
void checkMovement();
|
void checkMovement();
|
||||||
|
|
@ -121,6 +127,10 @@ class SomfyShade : public SomfyRemote {
|
||||||
void moveToMyPosition();
|
void moveToMyPosition();
|
||||||
void processWaitingFrame();
|
void processWaitingFrame();
|
||||||
void publish();
|
void publish();
|
||||||
|
void commit();
|
||||||
|
void commitShadePosition();
|
||||||
|
void commitTiltPosition();
|
||||||
|
void commitMyPosition();
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct transceiver_config_t {
|
typedef struct transceiver_config_t {
|
||||||
|
|
@ -134,15 +144,16 @@ typedef struct transceiver_config_t {
|
||||||
uint8_t MISOPin = 19;
|
uint8_t MISOPin = 19;
|
||||||
uint8_t CSNPin = 5;
|
uint8_t CSNPin = 5;
|
||||||
bool radioInit = false;
|
bool radioInit = false;
|
||||||
bool internalCCMode = false; // Use internal transmission mode FIFO buffers.
|
|
||||||
byte modulationMode = 2; // Modulation mode. 0 = 2-FSK, 1 = GFSK, 2 = ASK/OOK, 3 = 4-FSK, 4 = MSK.
|
|
||||||
float frequency = 433.42; // Basic frequency
|
float frequency = 433.42; // Basic frequency
|
||||||
float deviation = 47.60; // Set the Frequency deviation in kHz. Value from 1.58 to 380.85. Default is 47.60 kHz.
|
float deviation = 47.60; // Set the Frequency deviation in kHz. Value from 1.58 to 380.85. Default is 47.60 kHz.
|
||||||
|
float rxBandwidth = 812.5; // Receive bandwidth in kHz. Value from 58.03 to 812.50. Default is 99.97kHz.
|
||||||
|
int8_t txPower = 10; // Transmission power {-30, -20, -15, -10, -6, 0, 5, 7, 10, 11, 12}. Default is 12.
|
||||||
|
/*
|
||||||
|
bool internalCCMode = false; // Use internal transmission mode FIFO buffers.
|
||||||
|
byte modulationMode = 2; // Modulation mode. 0 = 2-FSK, 1 = GFSK, 2 = ASK/OOK, 3 = 4-FSK, 4 = MSK.
|
||||||
uint8_t channel = 0; // The channel number from 0 to 255
|
uint8_t channel = 0; // The channel number from 0 to 255
|
||||||
float channelSpacing = 199.95; // Channel spacing in multiplied by the channel number and added to the base frequency in kHz. 25.39 to 405.45. Default 199.95
|
float channelSpacing = 199.95; // Channel spacing in multiplied by the channel number and added to the base frequency in kHz. 25.39 to 405.45. Default 199.95
|
||||||
float rxBandwidth = 812.5; // Receive bandwidth in kHz. Value from 58.03 to 812.50. Default is 99.97kHz.
|
|
||||||
float dataRate = 99.97; // The data rate in kBaud. 0.02 to 1621.83 Default is 99.97.
|
float dataRate = 99.97; // The data rate in kBaud. 0.02 to 1621.83 Default is 99.97.
|
||||||
int8_t txPower = 10; // Transmission power {-30, -20, -15, -10, -6, 0, 5, 7, 10, 11, 12}. Default is 12.
|
|
||||||
uint8_t syncMode = 0; // 0=No preamble/sync,
|
uint8_t syncMode = 0; // 0=No preamble/sync,
|
||||||
// 1=16 sync word bits detected,
|
// 1=16 sync word bits detected,
|
||||||
// 2=16/16 sync words bits detected.
|
// 2=16/16 sync words bits detected.
|
||||||
|
|
@ -186,11 +197,13 @@ typedef struct transceiver_config_t {
|
||||||
// decreases the bounter by 8 each time a bit is received that is the same as the lats bit. A threshold of 4 PQT for this counter is used to gate sync word detection.
|
// decreases the bounter by 8 each time a bit is received that is the same as the lats bit. A threshold of 4 PQT for this counter is used to gate sync word detection.
|
||||||
// When PQT = 0 a sync word is always accepted.
|
// When PQT = 0 a sync word is always accepted.
|
||||||
bool appendStatus = false; // Appends the RSSI and LQI values to the TX packed as well as the CRC.
|
bool appendStatus = false; // Appends the RSSI and LQI values to the TX packed as well as the CRC.
|
||||||
|
*/
|
||||||
void fromJSON(JsonObject& obj);
|
void fromJSON(JsonObject& obj);
|
||||||
void toJSON(JsonObject& obj);
|
void toJSON(JsonObject& obj);
|
||||||
void save();
|
void save();
|
||||||
void load();
|
void load();
|
||||||
void apply();
|
void apply();
|
||||||
|
void removeNVSKey(const char *key);
|
||||||
};
|
};
|
||||||
class Transceiver {
|
class Transceiver {
|
||||||
private:
|
private:
|
||||||
|
|
@ -218,7 +231,11 @@ class Transceiver {
|
||||||
class SomfyShadeController {
|
class SomfyShadeController {
|
||||||
protected:
|
protected:
|
||||||
uint8_t m_shadeIds[SOMFY_MAX_SHADES];
|
uint8_t m_shadeIds[SOMFY_MAX_SHADES];
|
||||||
|
uint32_t lastCommit = 0;
|
||||||
public:
|
public:
|
||||||
|
appver_t appVersion;
|
||||||
|
bool useNVS() { return !(this->appVersion.major > 1 || this->appVersion.minor >= 4); }
|
||||||
|
bool isDirty = false;
|
||||||
uint32_t startingAddress;
|
uint32_t startingAddress;
|
||||||
uint8_t getNextShadeId();
|
uint8_t getNextShadeId();
|
||||||
uint32_t getNextRemoteAddress(uint8_t shadeId);
|
uint32_t getNextRemoteAddress(uint8_t shadeId);
|
||||||
|
|
@ -242,6 +259,9 @@ class SomfyShadeController {
|
||||||
void emitState(uint8_t num = 255);
|
void emitState(uint8_t num = 255);
|
||||||
void publish();
|
void publish();
|
||||||
void processWaitingFrame();
|
void processWaitingFrame();
|
||||||
|
void commit();
|
||||||
|
bool loadShadesFile(const char *filename);
|
||||||
|
bool loadLegacy();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
148
Web.cpp
148
Web.cpp
|
|
@ -271,6 +271,86 @@ void Web::begin() {
|
||||||
server.streamFile(file, _encoding_html);
|
server.streamFile(file, _encoding_html);
|
||||||
file.close();
|
file.close();
|
||||||
});
|
});
|
||||||
|
server.on("/shades.cfg", []() {
|
||||||
|
webServer.sendCORSHeaders();
|
||||||
|
// Load the index html page from the data directory.
|
||||||
|
Serial.println("Loading file shades.cfg");
|
||||||
|
File file = LittleFS.open("/shades.cfg", "r");
|
||||||
|
if (!file) {
|
||||||
|
Serial.println("Error opening shades.cfg");
|
||||||
|
server.send(500, _encoding_text, "shades.cfg");
|
||||||
|
}
|
||||||
|
server.streamFile(file, _encoding_text);
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
});
|
||||||
|
server.on("/shades.tmp", []() {
|
||||||
|
webServer.sendCORSHeaders();
|
||||||
|
// Load the index html page from the data directory.
|
||||||
|
Serial.println("Loading file shades.cfg");
|
||||||
|
File file = LittleFS.open("/shades.tmp", "r");
|
||||||
|
if (!file) {
|
||||||
|
Serial.println("Error opening shades.tmp");
|
||||||
|
server.send(500, _encoding_text, "shades.tmp");
|
||||||
|
}
|
||||||
|
server.streamFile(file, _encoding_text);
|
||||||
|
file.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
server.on("/backup", []() {
|
||||||
|
webServer.sendCORSHeaders();
|
||||||
|
char filename[120];
|
||||||
|
Timestamp ts;
|
||||||
|
char * iso = ts.getISOTime();
|
||||||
|
// Replace the invalid characters as quickly as we can.
|
||||||
|
for(uint8_t i = 0; i < strlen(iso); i++) {
|
||||||
|
switch(iso[i]) {
|
||||||
|
case '.':
|
||||||
|
// Just trim off the ms.
|
||||||
|
iso[i] = '\0';
|
||||||
|
break;
|
||||||
|
case ':':
|
||||||
|
iso[i] = '_';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
snprintf(filename, sizeof(filename), "attachment; filename=\"ESPSomfyRTS %s.backup\"", iso);
|
||||||
|
Serial.println(filename);
|
||||||
|
server.sendHeader(F("Content-Disposition"), filename);
|
||||||
|
Serial.println("Saving current shade information");
|
||||||
|
somfy.commit();
|
||||||
|
File file = LittleFS.open("/shades.cfg", "r");
|
||||||
|
if (!file) {
|
||||||
|
Serial.println("Error opening shades.cfg");
|
||||||
|
server.send(500, _encoding_text, "shades.cfg");
|
||||||
|
}
|
||||||
|
server.streamFile(file, _encoding_text);
|
||||||
|
file.close();
|
||||||
|
});
|
||||||
|
server.on("/restore", HTTP_POST, []() {
|
||||||
|
webServer.sendCORSHeaders();
|
||||||
|
server.sendHeader("Connection", "close");
|
||||||
|
server.send(200, _encoding_json, "{\"status\":\"Success\",\"desc\":\"Restoring Shade settings\"}");
|
||||||
|
}, []() {
|
||||||
|
HTTPUpload& upload = server.upload();
|
||||||
|
if (upload.status == UPLOAD_FILE_START) {
|
||||||
|
Serial.printf("Restore: %s\n", upload.filename.c_str());
|
||||||
|
// Begin by opening a new temporary file.
|
||||||
|
File fup = LittleFS.open("/shades.tmp", "w");
|
||||||
|
fup.close();
|
||||||
|
}
|
||||||
|
else if (upload.status == UPLOAD_FILE_WRITE) {
|
||||||
|
File fup = LittleFS.open("/shades.tmp", "a");
|
||||||
|
fup.write(upload.buf, upload.currentSize);
|
||||||
|
fup.close();
|
||||||
|
}
|
||||||
|
else if (upload.status == UPLOAD_FILE_END) {
|
||||||
|
// TODO: Do some validation of the file.
|
||||||
|
Serial.println("Validating restore");
|
||||||
|
// Go through the uploaded file to determine if it is valid.
|
||||||
|
somfy.loadShadesFile("/shades.tmp");
|
||||||
|
}
|
||||||
|
});
|
||||||
server.on("/index.js", []() {
|
server.on("/index.js", []() {
|
||||||
webServer.sendCacheHeaders(604800);
|
webServer.sendCacheHeaders(604800);
|
||||||
webServer.sendCORSHeaders();
|
webServer.sendCORSHeaders();
|
||||||
|
|
@ -324,6 +404,20 @@ void Web::begin() {
|
||||||
server.streamFile(file, "image/png");
|
server.streamFile(file, "image/png");
|
||||||
file.close();
|
file.close();
|
||||||
});
|
});
|
||||||
|
server.on("/icon.png", []() {
|
||||||
|
webServer.sendCacheHeaders(604800);
|
||||||
|
webServer.sendCORSHeaders();
|
||||||
|
|
||||||
|
// Load the index html page from the data directory.
|
||||||
|
Serial.println("Loading file favicon.png");
|
||||||
|
File file = LittleFS.open("/icon.png", "r");
|
||||||
|
if (!file) {
|
||||||
|
Serial.println("Error opening data/favicon.png");
|
||||||
|
server.send(500, _encoding_text, "Unable to open data/icons.css");
|
||||||
|
}
|
||||||
|
server.streamFile(file, "image/png");
|
||||||
|
file.close();
|
||||||
|
});
|
||||||
server.onNotFound([]() {
|
server.onNotFound([]() {
|
||||||
Serial.print("Request 404:");
|
Serial.print("Request 404:");
|
||||||
HTTPMethod method = server.method();
|
HTTPMethod method = server.method();
|
||||||
|
|
@ -385,7 +479,7 @@ void Web::begin() {
|
||||||
SomfyShade* shade;
|
SomfyShade* shade;
|
||||||
if (method == HTTP_POST || method == HTTP_PUT) {
|
if (method == HTTP_POST || method == HTTP_PUT) {
|
||||||
Serial.println("Adding a shade");
|
Serial.println("Adding a shade");
|
||||||
DynamicJsonDocument doc(256);
|
DynamicJsonDocument doc(512);
|
||||||
DeserializationError err = deserializeJson(doc, server.arg("plain"));
|
DeserializationError err = deserializeJson(doc, server.arg("plain"));
|
||||||
if (err) {
|
if (err) {
|
||||||
switch (err.code()) {
|
switch (err.code()) {
|
||||||
|
|
@ -410,7 +504,7 @@ void Web::begin() {
|
||||||
Serial.println("Adding shade");
|
Serial.println("Adding shade");
|
||||||
shade = somfy.addShade(obj);
|
shade = somfy.addShade(obj);
|
||||||
if (shade) {
|
if (shade) {
|
||||||
DynamicJsonDocument sdoc(256);
|
DynamicJsonDocument sdoc(512);
|
||||||
JsonObject sobj = sdoc.to<JsonObject>();
|
JsonObject sobj = sdoc.to<JsonObject>();
|
||||||
shade->toJSON(sobj);
|
shade->toJSON(sobj);
|
||||||
serializeJson(sdoc, g_content);
|
serializeJson(sdoc, g_content);
|
||||||
|
|
@ -592,7 +686,7 @@ void Web::begin() {
|
||||||
shade->moveToTiltTarget(target);
|
shade->moveToTiltTarget(target);
|
||||||
else
|
else
|
||||||
shade->sendTiltCommand(command);
|
shade->sendTiltCommand(command);
|
||||||
DynamicJsonDocument sdoc(256);
|
DynamicJsonDocument sdoc(512);
|
||||||
JsonObject sobj = sdoc.to<JsonObject>();
|
JsonObject sobj = sdoc.to<JsonObject>();
|
||||||
shade->toJSON(sobj);
|
shade->toJSON(sobj);
|
||||||
serializeJson(sdoc, g_content);
|
serializeJson(sdoc, g_content);
|
||||||
|
|
@ -656,7 +750,7 @@ void Web::begin() {
|
||||||
shade->moveToTarget(target);
|
shade->moveToTarget(target);
|
||||||
else
|
else
|
||||||
shade->sendCommand(command);
|
shade->sendCommand(command);
|
||||||
DynamicJsonDocument sdoc(256);
|
DynamicJsonDocument sdoc(512);
|
||||||
JsonObject sobj = sdoc.to<JsonObject>();
|
JsonObject sobj = sdoc.to<JsonObject>();
|
||||||
shade->toJSON(sobj);
|
shade->toJSON(sobj);
|
||||||
serializeJson(sdoc, g_content);
|
serializeJson(sdoc, g_content);
|
||||||
|
|
@ -711,7 +805,7 @@ void Web::begin() {
|
||||||
if(target == 255) target = shade->myPos;
|
if(target == 255) target = shade->myPos;
|
||||||
if(target >= 0 && target <= 100)
|
if(target >= 0 && target <= 100)
|
||||||
shade->setMyPosition(target);
|
shade->setMyPosition(target);
|
||||||
DynamicJsonDocument sdoc(256);
|
DynamicJsonDocument sdoc(512);
|
||||||
JsonObject sobj = sdoc.to<JsonObject>();
|
JsonObject sobj = sdoc.to<JsonObject>();
|
||||||
shade->toJSON(sobj);
|
shade->toJSON(sobj);
|
||||||
serializeJson(sdoc, g_content);
|
serializeJson(sdoc, g_content);
|
||||||
|
|
@ -761,7 +855,7 @@ void Web::begin() {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
shade->setRollingCode(rollingCode);
|
shade->setRollingCode(rollingCode);
|
||||||
StaticJsonDocument<256> doc;
|
DynamicJsonDocument doc(512);
|
||||||
JsonObject obj = doc.to<JsonObject>();
|
JsonObject obj = doc.to<JsonObject>();
|
||||||
shade->toJSON(obj);
|
shade->toJSON(obj);
|
||||||
serializeJson(doc, g_content);
|
serializeJson(doc, g_content);
|
||||||
|
|
@ -776,7 +870,7 @@ void Web::begin() {
|
||||||
uint8_t shadeId = 255;
|
uint8_t shadeId = 255;
|
||||||
if (server.hasArg("plain")) {
|
if (server.hasArg("plain")) {
|
||||||
// Its coming in the body.
|
// Its coming in the body.
|
||||||
StaticJsonDocument<129> doc;
|
DynamicJsonDocument doc(512);
|
||||||
DeserializationError err = deserializeJson(doc, server.arg("plain"));
|
DeserializationError err = deserializeJson(doc, server.arg("plain"));
|
||||||
if (err) {
|
if (err) {
|
||||||
switch (err.code()) {
|
switch (err.code()) {
|
||||||
|
|
@ -804,10 +898,10 @@ void Web::begin() {
|
||||||
server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade not found to pair\"}"));
|
server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade not found to pair\"}"));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
shade->sendCommand(somfy_commands::Prog, 4);
|
shade->sendCommand(somfy_commands::Prog, 7);
|
||||||
shade->paired = true;
|
shade->paired = true;
|
||||||
shade->save();
|
shade->save();
|
||||||
StaticJsonDocument<256> doc;
|
DynamicJsonDocument doc(512);
|
||||||
JsonObject obj = doc.to<JsonObject>();
|
JsonObject obj = doc.to<JsonObject>();
|
||||||
shade->toJSON(obj);
|
shade->toJSON(obj);
|
||||||
serializeJson(doc, g_content);
|
serializeJson(doc, g_content);
|
||||||
|
|
@ -822,7 +916,7 @@ void Web::begin() {
|
||||||
uint8_t shadeId = 255;
|
uint8_t shadeId = 255;
|
||||||
if (server.hasArg("plain")) {
|
if (server.hasArg("plain")) {
|
||||||
// Its coming in the body.
|
// Its coming in the body.
|
||||||
StaticJsonDocument<129> doc;
|
DynamicJsonDocument doc(512);
|
||||||
DeserializationError err = deserializeJson(doc, server.arg("plain"));
|
DeserializationError err = deserializeJson(doc, server.arg("plain"));
|
||||||
if (err) {
|
if (err) {
|
||||||
switch (err.code()) {
|
switch (err.code()) {
|
||||||
|
|
@ -850,10 +944,10 @@ void Web::begin() {
|
||||||
server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade not found to unpair\"}"));
|
server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade not found to unpair\"}"));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
shade->sendCommand(somfy_commands::Prog, 4);
|
shade->sendCommand(somfy_commands::Prog, 7);
|
||||||
shade->paired = false;
|
shade->paired = false;
|
||||||
shade->save();
|
shade->save();
|
||||||
StaticJsonDocument<256> doc;
|
DynamicJsonDocument doc(512);
|
||||||
JsonObject obj = doc.to<JsonObject>();
|
JsonObject obj = doc.to<JsonObject>();
|
||||||
shade->toJSON(obj);
|
shade->toJSON(obj);
|
||||||
serializeJson(doc, g_content);
|
serializeJson(doc, g_content);
|
||||||
|
|
@ -867,7 +961,7 @@ void Web::begin() {
|
||||||
if (method == HTTP_PUT || method == HTTP_POST) {
|
if (method == HTTP_PUT || method == HTTP_POST) {
|
||||||
// We are updating an existing shade by adding a linked remote.
|
// We are updating an existing shade by adding a linked remote.
|
||||||
if (server.hasArg("plain")) {
|
if (server.hasArg("plain")) {
|
||||||
DynamicJsonDocument doc(256);
|
DynamicJsonDocument doc(512);
|
||||||
DeserializationError err = deserializeJson(doc, server.arg("plain"));
|
DeserializationError err = deserializeJson(doc, server.arg("plain"));
|
||||||
if (err) {
|
if (err) {
|
||||||
switch (err.code()) {
|
switch (err.code()) {
|
||||||
|
|
@ -914,7 +1008,7 @@ void Web::begin() {
|
||||||
// We are updating an existing shade by adding a linked remote.
|
// We are updating an existing shade by adding a linked remote.
|
||||||
if (server.hasArg("plain")) {
|
if (server.hasArg("plain")) {
|
||||||
Serial.println("Linking a remote");
|
Serial.println("Linking a remote");
|
||||||
DynamicJsonDocument doc(256);
|
DynamicJsonDocument doc(512);
|
||||||
DeserializationError err = deserializeJson(doc, server.arg("plain"));
|
DeserializationError err = deserializeJson(doc, server.arg("plain"));
|
||||||
if (err) {
|
if (err) {
|
||||||
switch (err.code()) {
|
switch (err.code()) {
|
||||||
|
|
@ -1026,6 +1120,29 @@ void Web::begin() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
server.on("/updateShadeConfig", HTTP_POST, []() {
|
||||||
|
webServer.sendCORSHeaders();
|
||||||
|
server.sendHeader("Connection", "close");
|
||||||
|
server.send(200, _encoding_json, "{\"status\":\"ERROR\",\"desc\":\"Updating Shade Config: \"}");
|
||||||
|
}, []() {
|
||||||
|
HTTPUpload& upload = server.upload();
|
||||||
|
if (upload.status == UPLOAD_FILE_START) {
|
||||||
|
Serial.printf("Update: shades.cfg\n");
|
||||||
|
File fup = LittleFS.open("/shades.tmp", "w");
|
||||||
|
fup.close();
|
||||||
|
}
|
||||||
|
else if (upload.status == UPLOAD_FILE_WRITE) {
|
||||||
|
/* flashing littlefs to ESP*/
|
||||||
|
if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
|
||||||
|
File fup = LittleFS.open("/shades.tmp", "a");
|
||||||
|
fup.write(upload.buf, upload.currentSize);
|
||||||
|
fup.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (upload.status == UPLOAD_FILE_END) {
|
||||||
|
somfy.loadShadesFile("/shades.tmp");
|
||||||
|
}
|
||||||
|
});
|
||||||
server.on("/updateApplication", HTTP_POST, []() {
|
server.on("/updateApplication", HTTP_POST, []() {
|
||||||
webServer.sendCORSHeaders();
|
webServer.sendCORSHeaders();
|
||||||
server.sendHeader("Connection", "close");
|
server.sendHeader("Connection", "close");
|
||||||
|
|
@ -1041,7 +1158,7 @@ void Web::begin() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (upload.status == UPLOAD_FILE_WRITE) {
|
else if (upload.status == UPLOAD_FILE_WRITE) {
|
||||||
/* flashing firmware to ESP*/
|
/* flashing littlefs to ESP*/
|
||||||
if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
|
if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
|
||||||
Update.printError(Serial);
|
Update.printError(Serial);
|
||||||
}
|
}
|
||||||
|
|
@ -1049,6 +1166,7 @@ void Web::begin() {
|
||||||
else if (upload.status == UPLOAD_FILE_END) {
|
else if (upload.status == UPLOAD_FILE_END) {
|
||||||
if (Update.end(true)) { //true to set the size to the current progress
|
if (Update.end(true)) { //true to set the size to the current progress
|
||||||
Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
|
Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
|
||||||
|
somfy.commit();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Update.printError(Serial);
|
Update.printError(Serial);
|
||||||
|
|
|
||||||
1
data/appversion
Normal file
1
data/appversion
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
1.4.0
|
||||||
BIN
data/icon.png
Normal file
BIN
data/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
|
|
@ -3,15 +3,15 @@
|
||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<link rel="stylesheet" href="main.css?v=1.3.2" type="text/css" />
|
<link rel="stylesheet" href="main.css?v=1.4.1" type="text/css" />
|
||||||
<link rel="stylesheet" href="icons.css?v=1.3.2" type="text/css" />
|
<link rel="stylesheet" href="icons.css?v=1.4.0" type="text/css" />
|
||||||
<link rel="icon" type="image/png" href="favicon.png" />
|
<link rel="icon" type="image/png" href="favicon.png" />
|
||||||
<script type="text/javascript" src="index.js?v=1.3.2"></script>
|
<script type="text/javascript" src="index.js?v=1.4.0"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="divContainer" class="container" style="user-select:none;position:relative;">
|
<div id="divContainer" class="container" style="user-select:none;position:relative;border-radius:27px;">
|
||||||
<div id="divRadioError" style="text-align:center;background:gainsboro;color:gray;margin-bottom:7px;text-transform:uppercase;font-weight:bold;padding:4px;border-radius:5px;">Radio Not Initialized</div>
|
<div id="divRadioError" style="text-align:center;background:gainsboro;color:gray;margin-bottom:7px;text-transform:uppercase;font-weight:bold;padding:4px;border-radius:5px;">Radio Not Initialized</div>
|
||||||
<h1 style="text-align: center;"><span>ESPSomfy RTS</span><span class="button-outline" onclick="general.toggleConfig();" style="float:right;font-size:1.25rem;display:inline-block;vertical-align:middle;width:38px;height:38px;position:relative;padding-top:4px;"><span style="vertical-align:middle;clear:both;text-align:center;display:inline-block;"><i id="icoConfig" class="icss-gear" style=""></i></span></span></h1>
|
<h1 style="text-align: center;"><img src="icon.png" style="width:50px;float:left;margin-left:1px;margin-top:-10px;" /><span>ESPSomfy RTS</span><span class="button-outline" onclick="general.toggleConfig();" style="float:right;font-size:1.25rem;display:inline-block;vertical-align:middle;width:38px;height:38px;position:relative;padding-top:4px;"><span style="vertical-align:middle;clear:both;text-align:center;display:inline-block;"><i id="icoConfig" class="icss-gear" style=""></i></span></span></h1>
|
||||||
<div id="divConfigPnl" style="display:none;">
|
<div id="divConfigPnl" style="display:none;">
|
||||||
<div id="divWiFiStrength" style="margin-top:-10px;text-align:center;font-size:12px;">
|
<div id="divWiFiStrength" style="margin-top:-10px;text-align:center;font-size:12px;">
|
||||||
<div style="display:inline-block;vertical-align:middle;">
|
<div style="display:inline-block;vertical-align:middle;">
|
||||||
|
|
@ -234,9 +234,9 @@
|
||||||
</div>
|
</div>
|
||||||
<div id="divSomfyButtons" style="float:right;margin-top:10px;position:relative">
|
<div id="divSomfyButtons" style="float:right;margin-top:10px;position:relative">
|
||||||
<div style="display:inline-block;margin-right:7px;position:relative;font-size:48px;"><i id="icoShade" class="somfy-shade-icon icss-window-shade" data-shadeid="0" style="--shade-position:0%;vertical-align:middle;"></i><i class="icss-window-tilt" data-tiltposition="0" style="display:none;"></i></div>
|
<div style="display:inline-block;margin-right:7px;position:relative;font-size:48px;"><i id="icoShade" class="somfy-shade-icon icss-window-shade" data-shadeid="0" style="--shade-position:0%;vertical-align:middle;"></i><i class="icss-window-tilt" data-tiltposition="0" style="display:none;"></i></div>
|
||||||
<div class="button-outline" onclick="somfy.sendCommand(parseInt(document.getElementById('spanShadeId').innerText, 10), 'up');" style="display:inline-block;padding:7px;cursor:pointer;vertical-align:middle;margin:0px;"><i class="icss-somfy-up"></i></div>
|
<div class="button-outline" onclick="somfy.sendCommand(parseInt(document.getElementById('spanShadeId').innerText, 10), 'up');"><i class="icss-somfy-up"></i></div>
|
||||||
<div class="button-outline" onclick="somfy.sendCommand(parseInt(document.getElementById('spanShadeId').innerText, 10), 'my');" style="display: inline-block; font-size: 2em; padding: 10px; vertical-align: middle; cursor: pointer;margin:0px;"><span>my</span></div>
|
<div class="button-outline" onclick="somfy.sendCommand(parseInt(document.getElementById('spanShadeId').innerText, 10), 'my');" style="font-size: 2em; padding: 10px;"><span>my</span></div>
|
||||||
<div class="button-outline" onclick="somfy.sendCommand(parseInt(document.getElementById('spanShadeId').innerText, 10), 'down');" style="display: inline-block; padding: 7px; cursor: pointer; vertical-align: middle;margin:0px;"><i class="icss-somfy-down" style="margin-top:-4px;"></i></div>
|
<div class="button-outline" onclick="somfy.sendCommand(parseInt(document.getElementById('spanShadeId').innerText, 10), 'down');"><i class="icss-somfy-down" style="margin-top:-4px;"></i></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="field-group">
|
<div class="field-group">
|
||||||
|
|
@ -395,7 +395,14 @@
|
||||||
<button id="btnReloadApplication" type="button" onclick="general.reload();">
|
<button id="btnReloadApplication" type="button" onclick="general.reload();">
|
||||||
Refresh Cache
|
Refresh Cache
|
||||||
</button>
|
</button>
|
||||||
|
<div class="button-container" style="text-align:center;">
|
||||||
|
<button id="btnBackup" style="width:47%;display:inline-block;" type="button" onclick="firmware.backup();">
|
||||||
|
Backup
|
||||||
|
</button>
|
||||||
|
<button id="btnRestore" style="width:47%;display:inline-block;" type="button" onclick="firmware.restore();">
|
||||||
|
Restore
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
|
||||||
|
|
@ -193,6 +193,20 @@ function getJSON(url, cb) {
|
||||||
}
|
}
|
||||||
xhr.send();
|
xhr.send();
|
||||||
}
|
}
|
||||||
|
function getText(url, cb) {
|
||||||
|
let xhr = new XMLHttpRequest();
|
||||||
|
console.log({ get: url });
|
||||||
|
xhr.open('GET', url, true);
|
||||||
|
xhr.responseType = 'text';
|
||||||
|
xhr.onload = () => {
|
||||||
|
let status = xhr.status;
|
||||||
|
cb(status === 200 ? null : status, xhr.responseText);
|
||||||
|
}
|
||||||
|
xhr.onerror = (evt) => {
|
||||||
|
cb(xhr.status || 500, xhr.statusText);
|
||||||
|
}
|
||||||
|
xhr.send();
|
||||||
|
}
|
||||||
function putJSON(url, data, cb) {
|
function putJSON(url, data, cb) {
|
||||||
let xhr = new XMLHttpRequest();
|
let xhr = new XMLHttpRequest();
|
||||||
console.log({ put: url, data: data });
|
console.log({ put: url, data: data });
|
||||||
|
|
@ -355,7 +369,7 @@ async function reopenSocket() {
|
||||||
await initSockets();
|
await initSockets();
|
||||||
}
|
}
|
||||||
class General {
|
class General {
|
||||||
appVersion = 'v1.3.2';
|
appVersion = 'v1.4.0';
|
||||||
reloadApp = false;
|
reloadApp = false;
|
||||||
async init() {
|
async init() {
|
||||||
this.setAppVersion();
|
this.setAppVersion();
|
||||||
|
|
@ -1419,12 +1433,12 @@ class Somfy {
|
||||||
errorMessage(document.getElementById('fsSomfySettings'), 'You must provide a name for the shade between 1 and 20 characters.');
|
errorMessage(document.getElementById('fsSomfySettings'), 'You must provide a name for the shade between 1 and 20 characters.');
|
||||||
valid = false;
|
valid = false;
|
||||||
}
|
}
|
||||||
if (valid && (isNaN(obj.upTime) || obj.upTime < 1 || obj.upTime > 65355)) {
|
if (valid && (isNaN(obj.upTime) || obj.upTime < 1 || obj.upTime > 4294967295)) {
|
||||||
errorMessage(document.getElementById('fsSomfySettings'), 'Up Time must be a value between 0 and 65,355 milliseconds. This is the travel time to go from full closed to full open.');
|
errorMessage(document.getElementById('fsSomfySettings'), 'Up Time must be a value between 0 and 4,294,967,295 milliseconds. This is the travel time to go from full closed to full open.');
|
||||||
valid = false;
|
valid = false;
|
||||||
}
|
}
|
||||||
if (valid && (isNaN(obj.downTime) || obj.downTime < 1 || obj.downTime > 65355)) {
|
if (valid && (isNaN(obj.downTime) || obj.downTime < 1 || obj.downTime > 4294967295)) {
|
||||||
errorMessage(document.getElementById('fsSomfySettings'), 'Down Time must be a value between 0 and 65,355 milliseconds. This is the travel time to go from full open to full closed.');
|
errorMessage(document.getElementById('fsSomfySettings'), 'Down Time must be a value between 0 and 4,294,967,295 milliseconds. This is the travel time to go from full open to full closed.');
|
||||||
valid = false;
|
valid = false;
|
||||||
}
|
}
|
||||||
if (valid) {
|
if (valid) {
|
||||||
|
|
@ -1814,6 +1828,20 @@ class MQTT {
|
||||||
var mqtt = new MQTT();
|
var mqtt = new MQTT();
|
||||||
class Firmware {
|
class Firmware {
|
||||||
async init() { }
|
async init() { }
|
||||||
|
backup() {
|
||||||
|
var link = document.createElement('a');
|
||||||
|
link.href = '/backup';
|
||||||
|
link.setAttribute('download', 'backup');
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
link.remove();
|
||||||
|
};
|
||||||
|
restore() {
|
||||||
|
let div = this.createFileUploader('/restore');
|
||||||
|
let inst = div.querySelector('div[id=divInstText]');
|
||||||
|
inst.innerHTML = '<div style="font-size:14px;margin-bottom:20px;">Select a backup file that you would like to restore then press the Upload File button.</div>';
|
||||||
|
document.getElementById('fsUpdates').appendChild(div);
|
||||||
|
};
|
||||||
createFileUploader(service) {
|
createFileUploader(service) {
|
||||||
let div = document.createElement('div');
|
let div = document.createElement('div');
|
||||||
div.setAttribute('id', 'divUploadFile');
|
div.setAttribute('id', 'divUploadFile');
|
||||||
|
|
@ -1823,7 +1851,7 @@ class Firmware {
|
||||||
html += `<div id="divInstText"></div>`;
|
html += `<div id="divInstText"></div>`;
|
||||||
html += `<input id="fileName" type="file" name="updateFS" style="display:none;" onchange="document.getElementById('span-selected-file').innerText = this.files[0].name;"/>`;
|
html += `<input id="fileName" type="file" name="updateFS" style="display:none;" onchange="document.getElementById('span-selected-file').innerText = this.files[0].name;"/>`;
|
||||||
html += `<label for="fileName">`;
|
html += `<label for="fileName">`;
|
||||||
html += `<span id="span-selected-file" style="display:inline-block;min-width:257px;border-bottom:solid 2px white;font-size:17px"></span>`;
|
html += `<span id="span-selected-file" style="display:inline-block;min-width:257px;border-bottom:solid 2px white;font-size:14px;white-space:nowrap;overflow:hidden;max-width:320px;text-overflow:ellipsis;"></span>`;
|
||||||
html += `<div id="btn-select-file" class="button-outline" style="font-size:.8em;padding:10px;"><i class="icss-upload" style="margin:0px;"></i></div>`;
|
html += `<div id="btn-select-file" class="button-outline" style="font-size:.8em;padding:10px;"><i class="icss-upload" style="margin:0px;"></i></div>`;
|
||||||
html += `</label>`;
|
html += `</label>`;
|
||||||
html += `<div class="progress-bar" id="progFileUpload" style="--progress:0%;margin-top:10px;display:none;"></div>`
|
html += `<div class="progress-bar" id="progFileUpload" style="--progress:0%;margin-top:10px;display:none;"></div>`
|
||||||
|
|
@ -1837,14 +1865,15 @@ class Firmware {
|
||||||
updateFirmware() {
|
updateFirmware() {
|
||||||
let div = this.createFileUploader('/updateFirmware');
|
let div = this.createFileUploader('/updateFirmware');
|
||||||
let inst = div.querySelector('div[id=divInstText]');
|
let inst = div.querySelector('div[id=divInstText]');
|
||||||
inst.innerHTML = '<div style="font-size:14px;margin-bottom:20px;">Select a binary file containing the device firmware then press the Upload File button.</div>';
|
inst.innerHTML = '<div style="font-size:14px;margin-bottom:20px;">Select a binary file [SomfyController.ino.esp32.bin] containing the device firmware then press the Upload File button.</div>';
|
||||||
document.getElementById('fsUpdates').appendChild(div);
|
document.getElementById('fsUpdates').appendChild(div);
|
||||||
};
|
};
|
||||||
updateApplication() {
|
updateApplication() {
|
||||||
let div = this.createFileUploader('/updateApplication');
|
let div = this.createFileUploader('/updateApplication');
|
||||||
general.reloadApp = true;
|
general.reloadApp = true;
|
||||||
let inst = div.querySelector('div[id=divInstText]');
|
let inst = div.querySelector('div[id=divInstText]');
|
||||||
inst.innerHTML = '<div style="font-size:14px;margin-bottom:20px;">Select a binary file containing the littlefs data for the application then press the Upload File button.</div>';
|
inst.innerHTML = '<div style="font-size:14px;">Select a binary file [SomfyController.littlefs.bin] containing the littlefs data for the application then press the Upload File button.</div>';
|
||||||
|
inst.innerHTML += '<hr/><div style="font-size:14px;margin-bottom:10px;">A backup file for your configuration will be downloaded to your browser. If the application update process fails please restore this file using the restore button</div>';
|
||||||
document.getElementById('fsUpdates').appendChild(div);
|
document.getElementById('fsUpdates').appendChild(div);
|
||||||
};
|
};
|
||||||
async uploadFile(service, el) {
|
async uploadFile(service, el) {
|
||||||
|
|
@ -1857,6 +1886,28 @@ class Firmware {
|
||||||
errorMessage(el, 'This file is not a valid littleFS file system.');
|
errorMessage(el, 'This file is not a valid littleFS file system.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// The first thing we need to do is backup the configuration. So lets do this
|
||||||
|
// in a promise.
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
firmware.backup();
|
||||||
|
try {
|
||||||
|
// Next we need to download the current configuration data.
|
||||||
|
getText('/shades.cfg', (err, cfg) => {
|
||||||
|
if (err)
|
||||||
|
reject(err);
|
||||||
|
else {
|
||||||
|
resolve();
|
||||||
|
console.log(cfg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
reject(err);
|
||||||
|
serviceError(el, err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}).catch((err) => {
|
||||||
|
serviceError(el, err);
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
case '/updateFirmware':
|
case '/updateFirmware':
|
||||||
if (filename.indexOf('.ino.') === -1 || !filename.endsWith('.bin')) {
|
if (filename.indexOf('.ino.') === -1 || !filename.endsWith('.bin')) {
|
||||||
|
|
@ -1864,6 +1915,12 @@ class Firmware {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case '/restore':
|
||||||
|
if (!filename.endsWith('.backup') || filename.indexOf('ESPSomfyRTS') === -1) {
|
||||||
|
errorMessage(el, 'This file is not a valid backup file')
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
let formData = new FormData();
|
let formData = new FormData();
|
||||||
let btnUpload = el.querySelector('button[id="btnUploadFile"]');
|
let btnUpload = el.querySelector('button[id="btnUploadFile"]');
|
||||||
|
|
@ -1884,9 +1941,23 @@ class Firmware {
|
||||||
console.log(evt);
|
console.log(evt);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
xhr.onerror = function (err) {
|
||||||
|
console.log(err);
|
||||||
|
};
|
||||||
xhr.onload = function () {
|
xhr.onload = function () {
|
||||||
console.log('File upload load called');
|
console.log('File upload load called');
|
||||||
btnCancel.innerText = 'Close';
|
btnCancel.innerText = 'Close';
|
||||||
|
switch (service) {
|
||||||
|
case '/restore':
|
||||||
|
(async () => {
|
||||||
|
await somfy.init();
|
||||||
|
if (document.getElementById('divUploadFile')) document.getElementById('divUploadFile').remove();
|
||||||
|
})();
|
||||||
|
break;
|
||||||
|
case '/updateApplication':
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
xhr.send(formData);
|
xhr.send(formData);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -311,7 +311,7 @@ div.wifiSignal {
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
padding-right: 10px;
|
padding-right: 10px;
|
||||||
}
|
}
|
||||||
div.wifiSignal:hover {
|
#divAps div.wifiSignal:hover {
|
||||||
background: #00bcd4;
|
background: #00bcd4;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
@ -588,6 +588,7 @@ div.waitoverlay > .lds-roller {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
.somfyShadeCtl .shadectl-buttons {
|
.somfyShadeCtl .shadectl-buttons {
|
||||||
|
margin-top:3px;
|
||||||
float:right;
|
float:right;
|
||||||
white-space:nowrap;
|
white-space:nowrap;
|
||||||
}
|
}
|
||||||
|
|
@ -596,6 +597,16 @@ div.waitoverlay > .lds-roller {
|
||||||
padding: 7px;
|
padding: 7px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
#divSomfyButtons div.button-outline {
|
||||||
|
margin-top: -10px;
|
||||||
|
margin-left: 0px;
|
||||||
|
margin-right: 0px;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
display: inline-block;
|
||||||
|
padding: 7px;
|
||||||
|
cursor: pointer;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
.shade-positioner {
|
.shade-positioner {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue