mirror of
https://github.com/rstrouse/ESPSomfy-RTS.git
synced 2025-12-13 11:02:12 +01:00
Add repeater function #261
This commit is contained in:
parent
b458435ddf
commit
4e0d89e7db
15 changed files with 430 additions and 81 deletions
|
|
@ -7,12 +7,13 @@
|
|||
|
||||
extern Preferences pref;
|
||||
|
||||
#define SHADE_HDR_VER 20
|
||||
#define SHADE_HDR_SIZE 66
|
||||
#define SHADE_HDR_VER 21
|
||||
#define SHADE_HDR_SIZE 76
|
||||
#define SHADE_REC_SIZE 276
|
||||
#define GROUP_REC_SIZE 194
|
||||
#define TRANS_REC_SIZE 74
|
||||
#define ROOM_REC_SIZE 29
|
||||
#define REPEATER_REC_SIZE 77
|
||||
|
||||
extern ConfigSettings settings;
|
||||
|
||||
|
|
@ -51,7 +52,8 @@ bool ConfigFile::writeHeader(const config_header_t &hdr) {
|
|||
this->writeUInt8(hdr.shadeRecords);
|
||||
this->writeUInt16(hdr.groupRecordSize);
|
||||
this->writeUInt8(hdr.groupRecords);
|
||||
|
||||
this->writeUInt16(hdr.repeaterRecordSize);
|
||||
this->writeUInt8(hdr.repeaterRecords);
|
||||
this->writeUInt16(hdr.settingsRecordSize);
|
||||
this->writeUInt16(hdr.netRecordSize);
|
||||
this->writeUInt16(hdr.transRecordSize);
|
||||
|
|
@ -76,6 +78,10 @@ bool ConfigFile::readHeader() {
|
|||
else this->header.groupRecordSize = this->readUInt8(this->header.groupRecordSize);
|
||||
this->header.groupRecords = this->readUInt8(this->header.groupRecords);
|
||||
}
|
||||
if(this->header.version >= 21) {
|
||||
this->header.repeaterRecordSize = this->readUInt16(this->header.repeaterRecordSize);
|
||||
this->header.repeaterRecords = this->readUInt8(this->header.repeaterRecords);
|
||||
}
|
||||
if(this->header.version > 13) {
|
||||
this->header.settingsRecordSize = this->readUInt16(this->header.settingsRecordSize);
|
||||
this->header.netRecordSize = this->readUInt16(this->header.netRecordSize);
|
||||
|
|
@ -292,6 +298,8 @@ bool ShadeConfigFile::save(SomfyShadeController *s) {
|
|||
this->header.shadeRecords = s->shadeCount();
|
||||
this->header.groupRecordSize = GROUP_REC_SIZE;
|
||||
this->header.groupRecords = s->groupCount();
|
||||
this->header.repeaterRecords = 1;
|
||||
this->header.repeaterRecordSize = REPEATER_REC_SIZE;
|
||||
this->header.settingsRecordSize = 0;
|
||||
this->header.netRecordSize = 0;
|
||||
this->header.transRecordSize = 0;
|
||||
|
|
@ -311,6 +319,7 @@ bool ShadeConfigFile::save(SomfyShadeController *s) {
|
|||
if(group->getGroupId() != 255)
|
||||
this->writeGroupRecord(group);
|
||||
}
|
||||
this->writeRepeaterRecord(s);
|
||||
return true;
|
||||
}
|
||||
bool ShadeConfigFile::backup(SomfyShadeController *s) {
|
||||
|
|
@ -322,6 +331,8 @@ bool ShadeConfigFile::backup(SomfyShadeController *s) {
|
|||
this->header.shadeRecords = s->shadeCount();
|
||||
this->header.groupRecordSize = GROUP_REC_SIZE;
|
||||
this->header.groupRecords = s->groupCount();
|
||||
this->header.repeaterRecords = 1;
|
||||
this->header.repeaterRecordSize = REPEATER_REC_SIZE;
|
||||
this->header.settingsRecordSize = settings.calcSettingsRecSize();
|
||||
this->header.netRecordSize = settings.calcNetRecSize();
|
||||
this->header.transRecordSize = TRANS_REC_SIZE;
|
||||
|
|
@ -341,6 +352,7 @@ bool ShadeConfigFile::backup(SomfyShadeController *s) {
|
|||
if(group->getGroupId() != 255)
|
||||
this->writeGroupRecord(group);
|
||||
}
|
||||
this->writeRepeaterRecord(s);
|
||||
this->writeSettingsRecord();
|
||||
this->writeNetRecord();
|
||||
this->writeTransRecord(s->transceiver.config);
|
||||
|
|
@ -394,6 +406,9 @@ bool ShadeConfigFile::validate() {
|
|||
fsize += (this->header.netRecordSize);
|
||||
fsize += (this->header.transRecordSize);
|
||||
}
|
||||
if(this->header.version >= 21) {
|
||||
fsize += (this->header.repeaterRecordSize * this->header.repeaterRecords);
|
||||
}
|
||||
if(this->file.size() != fsize) {
|
||||
Serial.printf("File size is not correct should be %d and got %d\n", fsize, this->file.size());
|
||||
}
|
||||
|
|
@ -442,6 +457,17 @@ bool ShadeConfigFile::validate() {
|
|||
}
|
||||
}
|
||||
}
|
||||
if(this->header.version >= 21) {
|
||||
recs = 0;
|
||||
while(recs < this->header.repeaterRecords) {
|
||||
uint32_t pos = this->file.position();
|
||||
if(!this->seekChar(CFG_REC_END)) {
|
||||
Serial.printf("Failed to find the repeater record end %d\n", recs);
|
||||
}
|
||||
recs++;
|
||||
|
||||
}
|
||||
}
|
||||
this->file.seek(startPos, SeekSet);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -519,6 +545,15 @@ bool ShadeConfigFile::restoreFile(SomfyShadeController *s, const char *filename,
|
|||
+ (this->header.shadeRecords * this->header.shadeRecordSize)
|
||||
+ (this->header.groupRecords * this->header.groupRecordSize), SeekSet); // Start at the beginning of the file after the header.
|
||||
}
|
||||
if(opts.repeaters) {
|
||||
Serial.println("Restoring Repeaters...");
|
||||
if(this->header.repeaterRecords > 0) {
|
||||
memset(s->repeaters, 0x00, sizeof(uint32_t) * SOMFY_MAX_REPEATERS);
|
||||
for(uint8_t i = 0; i < this->header.repeaterRecords; i++) {
|
||||
this->readRepeaterRecord(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(opts.settings) {
|
||||
// First read out the data.
|
||||
this->readSettingsRecord();
|
||||
|
|
@ -654,6 +689,18 @@ bool ShadeConfigFile::readGroupRecord(SomfyGroup *group) {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
bool ShadeConfigFile::readRepeaterRecord(SomfyShadeController *s) {
|
||||
uint32_t startPos = this->file.position();
|
||||
|
||||
for(uint8_t i; i < SOMFY_MAX_REPEATERS; i++) {
|
||||
s->linkRepeater(this->readUInt32(0));
|
||||
}
|
||||
if(this->file.position() != startPos + this->header.repeaterRecordSize) {
|
||||
Serial.println("Reading to end of repeater record");
|
||||
this->seekChar(CFG_REC_END);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool ShadeConfigFile::readRoomRecord(SomfyRoom *room) {
|
||||
uint32_t startPos = this->file.position();
|
||||
room->roomId = this->readUInt8(0);
|
||||
|
|
@ -795,6 +842,11 @@ bool ShadeConfigFile::loadFile(SomfyShadeController *s, const char *filename) {
|
|||
((SomfyGroup *)&s->groups[ndx++])->clear();
|
||||
}
|
||||
}
|
||||
if(this->header.repeaterRecords > 0) {
|
||||
memset(s->repeaters, 0x00, sizeof(uint32_t) * SOMFY_MAX_REPEATERS);
|
||||
for(uint8_t i = 0; i < this->header.repeaterRecords; i++)
|
||||
this->readRepeaterRecord(s);
|
||||
}
|
||||
if(opened) {
|
||||
Serial.println("Closing shade config file");
|
||||
this->end();
|
||||
|
|
@ -817,13 +869,18 @@ bool ShadeConfigFile::writeGroupRecord(SomfyGroup *group) {
|
|||
this->writeUInt8(group->roomId, CFG_REC_END);
|
||||
return true;
|
||||
}
|
||||
bool ShadeConfigFile::writeRepeaterRecord(SomfyShadeController *s) {
|
||||
for(uint8_t i = 0; i < SOMFY_MAX_REPEATERS; i++) {
|
||||
this->writeUInt32(s->repeaters[i], i == SOMFY_MAX_REPEATERS - 1 ? CFG_REC_END : CFG_VALUE_SEP);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool ShadeConfigFile::writeRoomRecord(SomfyRoom *room) {
|
||||
this->writeUInt8(room->roomId);
|
||||
this->writeString(room->name, sizeof(room->name));
|
||||
this->writeUInt8(room->sortOrder, CFG_REC_END);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ShadeConfigFile::writeShadeRecord(SomfyShade *shade) {
|
||||
if(shade->tiltType == tilt_types::none || shade->shadeType != shade_types::blind) {
|
||||
shade->myTiltPos = -1;
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@
|
|||
|
||||
struct config_header_t {
|
||||
uint8_t version = 1;
|
||||
uint8_t repeaterRecords = 0;
|
||||
uint8_t repeaterRecordSize = 0;
|
||||
uint8_t roomRecords = 0;
|
||||
uint16_t roomRecordSize = 0;
|
||||
uint16_t shadeRecordSize = 0;
|
||||
|
|
@ -65,12 +67,14 @@ class ConfigFile {
|
|||
};
|
||||
class ShadeConfigFile : public ConfigFile {
|
||||
protected:
|
||||
bool writeRepeaterRecord(SomfyShadeController *s);
|
||||
bool writeRoomRecord(SomfyRoom *room);
|
||||
bool writeShadeRecord(SomfyShade *shade);
|
||||
bool writeGroupRecord(SomfyGroup *group);
|
||||
bool writeSettingsRecord();
|
||||
bool writeNetRecord();
|
||||
bool writeTransRecord(transceiver_config_t &cfg);
|
||||
bool readRepeaterRecord(SomfyShadeController *s);
|
||||
bool readRoomRecord(SomfyRoom *room);
|
||||
bool readShadeRecord(SomfyShade *shade);
|
||||
bool readGroupRecord(SomfyGroup *group);
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ void restore_options_t::fromJSON(JsonObject &obj) {
|
|||
if(obj.containsKey("settings")) this->settings = obj["settings"];
|
||||
if(obj.containsKey("network")) this->network = obj["network"];
|
||||
if(obj.containsKey("transceiver")) this->transceiver = obj["transceiver"];
|
||||
if(obj.containsKey("repeaters")) this->repeaters = obj["repeaters"];
|
||||
}
|
||||
int8_t appver_t::compare(appver_t &ver) {
|
||||
if(this->major == ver.major && this->minor == ver.minor && this->build == ver.build) return 0;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
#ifndef configsettings_h
|
||||
#define configsettings_h
|
||||
|
||||
#define FW_VERSION "v2.3.3"
|
||||
#define FW_VERSION "v2.4.0"
|
||||
enum DeviceStatus {
|
||||
DS_OK = 0,
|
||||
DS_ERROR = 1,
|
||||
|
|
@ -14,6 +14,7 @@ struct restore_options_t {
|
|||
bool shades = false;
|
||||
bool network = false;
|
||||
bool transceiver = false;
|
||||
bool repeaters = false;
|
||||
void fromJSON(JsonObject &obj);
|
||||
};
|
||||
struct appver_t {
|
||||
|
|
|
|||
167
Somfy.cpp
167
Somfy.cpp
|
|
@ -29,6 +29,7 @@ uint8_t rxmode = 0; // Indicates whether the radio is in receive mode. Just to
|
|||
|
||||
#define SETMY_REPEATS 35
|
||||
#define TILT_REPEATS 15
|
||||
#define TX_QUEUE_DELAY 100
|
||||
|
||||
int sort_asc(const void *cmp1, const void *cmp2) {
|
||||
int a = *((uint8_t *)cmp1);
|
||||
|
|
@ -613,7 +614,7 @@ void SomfyShade::clear() {
|
|||
this->lastRollingCode = 0;
|
||||
this->shadeType = shade_types::roller;
|
||||
this->tiltType = tilt_types::none;
|
||||
this->txQueue.clear();
|
||||
//this->txQueue.clear();
|
||||
this->currentPos = 0.0f;
|
||||
this->currentTiltPos = 0.0f;
|
||||
this->direction = 0;
|
||||
|
|
@ -804,6 +805,17 @@ void SomfyGroup::compressLinkedShadeIds() {
|
|||
}
|
||||
}
|
||||
}
|
||||
void SomfyShadeController::compressRepeaters() {
|
||||
for(uint8_t i = 0, j = 0; i < SOMFY_MAX_REPEATERS; i++) {
|
||||
if(this->repeaters[i] != 0) {
|
||||
if(i != j) {
|
||||
this->repeaters[j] = this->repeaters[i];
|
||||
this->repeaters[i] = 0;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
}
|
||||
}
|
||||
bool SomfyGroup::hasShadeId(uint8_t shadeId) {
|
||||
for(uint8_t i = 0; i < SOMFY_MAX_GROUPED_SHADES; i++) {
|
||||
if(this->linkedShades[i] == 0) break;
|
||||
|
|
@ -3164,8 +3176,9 @@ bool SomfyRemote::toJSON(JsonObject &obj) {
|
|||
void SomfyRemote::setRemoteAddress(uint32_t address) { this->m_remoteAddress = address; snprintf(this->m_remotePrefId, sizeof(this->m_remotePrefId), "_%lu", (unsigned long)this->m_remoteAddress); }
|
||||
uint32_t SomfyRemote::getRemoteAddress() { return this->m_remoteAddress; }
|
||||
void SomfyShadeController::processFrame(somfy_frame_t &frame, bool internal) {
|
||||
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() != 255) this->shades[i].processFrame(frame, internal);
|
||||
}
|
||||
}
|
||||
void SomfyShadeController::processWaitingFrame() {
|
||||
for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++)
|
||||
|
|
@ -3300,7 +3313,13 @@ int8_t SomfyShadeController::getMaxRoomOrder() {
|
|||
}
|
||||
return order;
|
||||
}
|
||||
|
||||
uint8_t SomfyShadeController::repeaterCount() {
|
||||
uint8_t count = 0;
|
||||
for(uint8_t i = 0; i < SOMFY_MAX_REPEATERS; i++) {
|
||||
if(this->repeaters[i] != 0) count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
uint8_t SomfyShadeController::roomCount() {
|
||||
uint8_t count = 0;
|
||||
for(uint8_t i = 0; i < SOMFY_MAX_ROOMS; i++) {
|
||||
|
|
@ -3411,6 +3430,30 @@ SomfyShade *SomfyShadeController::addShade() {
|
|||
}
|
||||
return shade;
|
||||
}
|
||||
bool SomfyShadeController::unlinkRepeater(uint32_t address) {
|
||||
for(uint8_t i = 0; i < SOMFY_MAX_REPEATERS; i++) {
|
||||
if(this->repeaters[i] == address) this->repeaters[i] = 0;
|
||||
}
|
||||
this->compressRepeaters();
|
||||
this->isDirty = true;
|
||||
return true;
|
||||
}
|
||||
bool SomfyShadeController::linkRepeater(uint32_t address) {
|
||||
bool bSet = false;
|
||||
for(uint8_t i = 0; i < SOMFY_MAX_REPEATERS; i++) {
|
||||
if(!bSet && this->repeaters[i] == address) bSet = true;
|
||||
else if(bSet && this->repeaters[i] == address) this->repeaters[i] = 0;
|
||||
}
|
||||
if(!bSet) {
|
||||
for(uint8_t i = 0; i < SOMFY_MAX_REPEATERS; i++) {
|
||||
if(this->repeaters[i] == 0) {
|
||||
this->repeaters[i] = address;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
SomfyRoom *SomfyShadeController::addRoom(JsonObject &obj) {
|
||||
SomfyRoom *room = this->addRoom();
|
||||
if(room) {
|
||||
|
|
@ -3721,6 +3764,13 @@ bool SomfyShadeController::toJSON(JsonObject &obj) {
|
|||
this->toJSONGroups(arrGroups);
|
||||
return true;
|
||||
}
|
||||
bool SomfyShadeController::toJSONRepeaters(JsonArray &arr) {
|
||||
for(uint8_t i = 0; i < SOMFY_MAX_REPEATERS; i++) {
|
||||
if(somfy.repeaters[i] != 0) arr.add(somfy.repeaters[i]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SomfyShadeController::toJSONRooms(JsonArray &arr) {
|
||||
for(uint8_t i = 0; i < SOMFY_MAX_ROOMS; i++) {
|
||||
SomfyRoom &room = this->rooms[i];
|
||||
|
|
@ -3791,6 +3841,7 @@ static const uint32_t tempo_if_gap = 30415; // Gap between frames
|
|||
static int16_t bitMin = SYMBOL * TOLERANCE_MIN;
|
||||
static somfy_rx_t somfy_rx;
|
||||
static somfy_rx_queue_t rx_queue;
|
||||
static somfy_tx_queue_t tx_queue;
|
||||
bool somfy_tx_queue_t::pop(somfy_tx_t *tx) {
|
||||
// Read the oldest index.
|
||||
for(int8_t i = MAX_TX_BUFFER - 1; i >= 0; i--) {
|
||||
|
|
@ -3798,39 +3849,44 @@ bool somfy_tx_queue_t::pop(somfy_tx_t *tx) {
|
|||
uint8_t ndx = this->index[i];
|
||||
memcpy(tx, &this->items[ndx], sizeof(somfy_tx_t));
|
||||
this->items[ndx].clear();
|
||||
this->length--;
|
||||
if(this->length > 0) this->length--;
|
||||
this->index[i] = 255;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool somfy_tx_queue_t::push(uint32_t await, somfy_commands cmd, uint8_t repeats) {
|
||||
void somfy_tx_queue_t::push(somfy_rx_t *rx) { this->push(rx->cpt_synchro_hw, rx->payload, rx->bit_length); }
|
||||
void somfy_tx_queue_t::push(uint8_t hwsync, uint8_t *payload, uint8_t bit_length) {
|
||||
if(this->length >= MAX_TX_BUFFER) {
|
||||
// We have overflowed the buffer simply empty the last item
|
||||
// in this instance we will simply throw it away.
|
||||
uint8_t ndx = this->index[MAX_TX_BUFFER - 1];
|
||||
if(ndx < MAX_TX_BUFFER) this->items[ndx].clear();
|
||||
this->index[MAX_TX_BUFFER - 1] = 255;
|
||||
this->length = MAX_TX_BUFFER - 1;
|
||||
if (ndx < MAX_TX_BUFFER)
|
||||
this->items[ndx].clear();
|
||||
this->length--;
|
||||
}
|
||||
// Place the command in the first empty slot. Empty slots are those
|
||||
// with a millis of 0. We will shift the indexes right so that this
|
||||
// is indexed int slot 0.
|
||||
uint8_t first = 0;
|
||||
// Place this record in the first empty slot. There will
|
||||
// be one since we cleared a space above should there
|
||||
// be an overflow.
|
||||
for(uint8_t i = 0; i < MAX_TX_BUFFER; i++) {
|
||||
if(this->items[i].await == 0) {
|
||||
this->items[i].await = await;
|
||||
this->items[i].cmd = cmd;
|
||||
this->items[i].repeats = repeats;
|
||||
if(this->items[i].bit_length == 0) {
|
||||
first = i;
|
||||
this->items[i].bit_length = bit_length;
|
||||
this->items[i].hwsync = hwsync;
|
||||
memcpy(&this->items[i].payload, payload, sizeof(this->items[i].payload));
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Move the index so that it is the at position 0. The oldest item will fall off.
|
||||
for(uint8_t j = MAX_TX_BUFFER - 1; j > 0; j--) {
|
||||
this->index[j] = this->index[j - 1];
|
||||
for(uint8_t i = MAX_TX_BUFFER - 1; i > 0; i--) {
|
||||
this->index[i] = this->index[i - 1];
|
||||
}
|
||||
this->length++;
|
||||
this->index[0] = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
// When popping from the queue we always pull from the end
|
||||
this->index[0] = first;
|
||||
this->delay_time = millis() + TX_QUEUE_DELAY; // We do not want to process this frame until a full frame beat has passed.
|
||||
}
|
||||
void somfy_rx_queue_t::init() {
|
||||
Serial.println("Initializing RX Queue");
|
||||
|
|
@ -3847,13 +3903,14 @@ bool somfy_rx_queue_t::pop(somfy_rx_t *rx) {
|
|||
uint8_t ndx = this->index[i];
|
||||
memcpy(rx, &this->items[this->index[i]], sizeof(somfy_rx_t));
|
||||
this->items[ndx].clear();
|
||||
this->length--;
|
||||
if(this->length > 0) this->length--;
|
||||
this->index[i] = 255;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Transceiver::sendFrame(byte *frame, uint8_t sync, uint8_t bitLength) {
|
||||
if(!this->config.enabled) return;
|
||||
uint32_t pin = 1 << this->config.TXPin;
|
||||
|
|
@ -3926,7 +3983,7 @@ void Transceiver::sendFrame(byte *frame, uint8_t sync, uint8_t bitLength) {
|
|||
delayMicroseconds(13717);
|
||||
delayMicroseconds(13717);
|
||||
}
|
||||
|
||||
Serial.println("Frame Sent...");
|
||||
/*
|
||||
if(bitLength == 80)
|
||||
delayMicroseconds(15055);
|
||||
|
|
@ -4026,7 +4083,7 @@ void RECEIVE_ATTR Transceiver::handleReceive() {
|
|||
if (somfy_rx.status == receiving_data && somfy_rx.cpt_bits >= somfy_rx.bit_length) {
|
||||
// Since we are operating within the interrupt all data really needs to be static
|
||||
// for the handoff to the frame decoder. For this reason we are buffering up to
|
||||
// 3 total frames. Althought it may not matter considering the lenght of a packet
|
||||
// 3 total frames. Althought it may not matter considering the length of a packet
|
||||
// will likely not push over the loop timing. For now lets assume that there
|
||||
// may be some pressure on the loop for features.
|
||||
if(rx_queue.length >= MAX_RX_BUFFER) {
|
||||
|
|
@ -4127,14 +4184,13 @@ void Transceiver::emitFrequencyScan(uint8_t num) {
|
|||
if(num >= 255) sockEmit.sendToClients("frequencyScan", buf);
|
||||
else sockEmit.sendToClient(num, "frequencyScan", buf);
|
||||
}
|
||||
bool Transceiver::receive() {
|
||||
bool Transceiver::receive(somfy_rx_t *rx) {
|
||||
// Check to see if there is anything in the buffer
|
||||
if(rx_queue.length > 0) {
|
||||
//Serial.printf("Processing receive %d\n", rx_queue.length);
|
||||
somfy_rx_t rx;
|
||||
rx_queue.pop(&rx);
|
||||
this->frame.decodeFrame(&rx);
|
||||
this->emitFrame(&this->frame, &rx);
|
||||
rx_queue.pop(rx);
|
||||
this->frame.decodeFrame(rx);
|
||||
this->emitFrame(&this->frame, rx);
|
||||
return this->frame.valid;
|
||||
}
|
||||
return false;
|
||||
|
|
@ -4555,21 +4611,54 @@ bool Transceiver::begin() {
|
|||
return true;
|
||||
}
|
||||
void Transceiver::loop() {
|
||||
if(rxmode == 3) {
|
||||
if(rx_queue.length > 0) {
|
||||
//Serial.printf("Processing receive %d\n", rx_queue.length);
|
||||
somfy_rx_t rx;
|
||||
rx_queue.pop(&rx);
|
||||
this->frame.decodeFrame(&rx);
|
||||
this->processFrequencyScan(this->frame.valid);
|
||||
}
|
||||
if(rxmode == 3) {
|
||||
if(this->receive(&rx))
|
||||
this->processFrequencyScan(true);
|
||||
else
|
||||
this->processFrequencyScan(false);
|
||||
}
|
||||
else if (this->receive())
|
||||
else if (this->receive(&rx)) {
|
||||
for(uint8_t i = 0; i < SOMFY_MAX_REPEATERS; i++) {
|
||||
if(somfy.repeaters[i] == frame.remoteAddress) {
|
||||
tx_queue.push(&rx);
|
||||
Serial.println("Queued repeater frame...");
|
||||
break;
|
||||
}
|
||||
}
|
||||
somfy.processFrame(this->frame, false);
|
||||
else
|
||||
}
|
||||
else {
|
||||
somfy.processWaitingFrame();
|
||||
// Check to see if there is anything in the buffer
|
||||
if(tx_queue.length > 0 && millis() > tx_queue.delay_time && somfy_rx.cpt_synchro_hw == 0) {
|
||||
this->beginTransmit();
|
||||
somfy_tx_t tx;
|
||||
|
||||
tx_queue.pop(&tx);
|
||||
Serial.printf("Sending frame %d - %d-BIT [", tx.hwsync, tx.bit_length);
|
||||
for(uint8_t j = 0; j < 10; j++) {
|
||||
Serial.print(tx.payload[j]);
|
||||
if(j < 9) Serial.print(", ");
|
||||
}
|
||||
Serial.println("]");
|
||||
this->sendFrame(tx.payload, tx.hwsync, tx.bit_length);
|
||||
tx_queue.delay_time = millis() + TX_QUEUE_DELAY;
|
||||
|
||||
/*
|
||||
while(tx_queue.length > 0 && tx_queue.pop(&tx)) {
|
||||
Serial.printf("Sending frame %d - %d-BIT [", tx.hwsync, tx.bit_length);
|
||||
for(uint8_t j = 0; j < 10; j++) {
|
||||
Serial.print(tx.payload[j]);
|
||||
if(j < 9) Serial.print(", ");
|
||||
}
|
||||
Serial.println("]");
|
||||
this->sendFrame(tx.payload, tx.hwsync, tx.bit_length);
|
||||
}
|
||||
*/
|
||||
this->endTransmit();
|
||||
}
|
||||
}
|
||||
}
|
||||
somfy_frame_t& Transceiver::lastFrame() { return this->frame; }
|
||||
void Transceiver::beginTransmit() {
|
||||
|
|
|
|||
43
Somfy.h
43
Somfy.h
|
|
@ -7,6 +7,7 @@
|
|||
#define SOMFY_MAX_LINKED_REMOTES 7
|
||||
#define SOMFY_MAX_GROUPED_SHADES 32
|
||||
#define SOMFY_MAX_ROOMS 16
|
||||
#define SOMFY_MAX_REPEATERS 7
|
||||
|
||||
#define SECS_TO_MILLIS(x) ((x) * 1000)
|
||||
#define MINS_TO_MILLIS(x) SECS_TO_MILLIS((x) * 60)
|
||||
|
|
@ -77,7 +78,7 @@ somfy_commands translateSomfyCommand(const String& string);
|
|||
|
||||
#define MAX_TIMINGS 300
|
||||
#define MAX_RX_BUFFER 3
|
||||
#define MAX_TX_BUFFER 3
|
||||
#define MAX_TX_BUFFER 5
|
||||
|
||||
typedef enum {
|
||||
waiting_synchro = 0,
|
||||
|
|
@ -115,34 +116,35 @@ struct somfy_rx_queue_t {
|
|||
uint8_t length = 0;
|
||||
uint8_t index[MAX_RX_BUFFER];
|
||||
somfy_rx_t items[MAX_RX_BUFFER];
|
||||
//void push(somfy_rx_t *rx);
|
||||
void push(somfy_rx_t *rx);
|
||||
bool pop(somfy_rx_t *rx);
|
||||
};
|
||||
struct somfy_tx_t {
|
||||
void clear() {
|
||||
this->await = 0;
|
||||
this->cmd = somfy_commands::Unknown0;
|
||||
this->repeats = 0;
|
||||
this->hwsync = 0;
|
||||
this->bit_length = 0;
|
||||
memset(this->payload, 0x00, sizeof(this->payload));
|
||||
}
|
||||
uint32_t await = 0;
|
||||
somfy_commands cmd;
|
||||
uint8_t repeats;
|
||||
uint8_t hwsync = 0;
|
||||
uint8_t bit_length = 0;
|
||||
uint8_t payload[10] = {};
|
||||
};
|
||||
struct somfy_tx_queue_t {
|
||||
somfy_tx_queue_t() {
|
||||
this->clear();
|
||||
}
|
||||
somfy_tx_queue_t() { this->clear(); }
|
||||
void clear() {
|
||||
for (uint8_t i = 0; i < MAX_TX_BUFFER; i++) {
|
||||
this->index[i] = 255;
|
||||
this->items[i].clear();
|
||||
}
|
||||
this->length = 0;
|
||||
}
|
||||
unsigned long delay_time = 0;
|
||||
uint8_t length = 0;
|
||||
uint8_t index[MAX_TX_BUFFER];
|
||||
uint8_t index[MAX_TX_BUFFER] = {255};
|
||||
somfy_tx_t items[MAX_TX_BUFFER];
|
||||
bool pop(somfy_tx_t *tx);
|
||||
bool push(uint32_t await, somfy_commands cmd, uint8_t repeats);
|
||||
void push(somfy_rx_t *rx); // Used for repeats
|
||||
void push(uint8_t hwsync, byte *payload, uint8_t bit_length);
|
||||
};
|
||||
|
||||
enum class somfy_flags_t : byte {
|
||||
|
|
@ -157,6 +159,11 @@ enum class somfy_flags_t : byte {
|
|||
enum class gpio_flags_t : byte {
|
||||
LowLevelTrigger = 0x01
|
||||
};
|
||||
struct somfy_relay_t {
|
||||
uint32_t remoteAddress = 0;
|
||||
uint8_t sync = 0;
|
||||
byte frame[10] = {0};
|
||||
};
|
||||
struct somfy_frame_t {
|
||||
bool valid = false;
|
||||
bool processed = false;
|
||||
|
|
@ -270,7 +277,7 @@ class SomfyShade : public SomfyRemote {
|
|||
#ifdef USE_NVS
|
||||
void load();
|
||||
#endif
|
||||
somfy_tx_queue_t txQueue;
|
||||
//somfy_tx_queue_t txQueue;
|
||||
float currentPos = 0.0f;
|
||||
float currentTiltPos = 0.0f;
|
||||
//uint16_t movement = 0;
|
||||
|
|
@ -479,7 +486,7 @@ class Transceiver {
|
|||
bool begin();
|
||||
void loop();
|
||||
bool end();
|
||||
bool receive();
|
||||
bool receive(somfy_rx_t *rx);
|
||||
void clearReceived();
|
||||
void enableReceive();
|
||||
void disableReceive();
|
||||
|
|
@ -523,14 +530,20 @@ class SomfyShadeController {
|
|||
bool begin();
|
||||
void loop();
|
||||
void end();
|
||||
void compressRepeaters();
|
||||
uint32_t repeaters[SOMFY_MAX_REPEATERS] = {0};
|
||||
SomfyRoom rooms[SOMFY_MAX_ROOMS];
|
||||
SomfyShade shades[SOMFY_MAX_SHADES];
|
||||
SomfyGroup groups[SOMFY_MAX_GROUPS];
|
||||
bool linkRepeater(uint32_t address);
|
||||
bool unlinkRepeater(uint32_t address);
|
||||
bool toJSON(DynamicJsonDocument &doc);
|
||||
bool toJSON(JsonObject &obj);
|
||||
bool toJSONRooms(JsonArray &arr);
|
||||
bool toJSONShades(JsonArray &arr);
|
||||
bool toJSONGroups(JsonArray &arr);
|
||||
bool toJSONRepeaters(JsonArray &arr);
|
||||
uint8_t repeaterCount();
|
||||
uint8_t roomCount();
|
||||
uint8_t shadeCount();
|
||||
uint8_t groupCount();
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
92
Web.cpp
92
Web.cpp
|
|
@ -312,6 +312,14 @@ void Web::handleController(WebServer &server) {
|
|||
this->chunkShadesResponse(server);
|
||||
server.sendContent(",\"groups\":");
|
||||
this->chunkGroupsResponse(server);
|
||||
server.sendContent(",\"repeaters\":");
|
||||
{
|
||||
DynamicJsonDocument doc(512);
|
||||
JsonArray r = doc.to<JsonArray>();
|
||||
somfy.toJSONRepeaters(r);
|
||||
serializeJson(doc, g_content);
|
||||
server.sendContent(g_content);
|
||||
}
|
||||
server.sendContent("}");
|
||||
server.sendContent("", 0);
|
||||
}
|
||||
|
|
@ -331,6 +339,19 @@ void Web::handleLoginContext(WebServer &server) {
|
|||
serializeJson(doc, g_content);
|
||||
server.send(200, _encoding_json, g_content);
|
||||
}
|
||||
void Web::handleGetRepeaters(WebServer &server) {
|
||||
webServer.sendCORSHeaders(server);
|
||||
if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; }
|
||||
HTTPMethod method = server.method();
|
||||
if (method == HTTP_POST || method == HTTP_GET) {
|
||||
DynamicJsonDocument doc(512);
|
||||
JsonArray r = doc.to<JsonArray>();
|
||||
somfy.toJSONRepeaters(r);
|
||||
serializeJson(doc, g_content);
|
||||
server.send(200, _encoding_json, g_content);
|
||||
}
|
||||
else server.send(404, _encoding_text, _response_404);
|
||||
}
|
||||
void Web::handleGetRooms(WebServer &server) {
|
||||
webServer.sendCORSHeaders(server);
|
||||
if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; }
|
||||
|
|
@ -1654,6 +1675,77 @@ void Web::begin() {
|
|||
}
|
||||
}
|
||||
});
|
||||
server.on("/linkRepeater", []() {
|
||||
webServer.sendCORSHeaders(server);
|
||||
if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; }
|
||||
HTTPMethod method = server.method();
|
||||
if (method == HTTP_PUT || method == HTTP_POST) {
|
||||
// We are adding a linked repeater.
|
||||
uint32_t address = 0;
|
||||
if (server.hasArg("plain")) {
|
||||
Serial.println("Linking a repeater");
|
||||
DynamicJsonDocument doc(512);
|
||||
DeserializationError err = deserializeJson(doc, server.arg("plain"));
|
||||
if (err) {
|
||||
webServer.handleDeserializationError(server, err);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
if (obj.containsKey("address")) address = obj["address"];
|
||||
else if(obj.containsKey("remoteAddress")) address = obj["remoteAddress"];
|
||||
}
|
||||
}
|
||||
else if(server.hasArg("address"))
|
||||
address = atoi(server.arg("address").c_str());
|
||||
if(address == 0)
|
||||
server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No repeater address was supplied.\"}"));
|
||||
else {
|
||||
somfy.linkRepeater(address);
|
||||
DynamicJsonDocument doc(512);
|
||||
JsonArray r = doc.to<JsonArray>();
|
||||
somfy.toJSONRepeaters(r);
|
||||
serializeJson(doc, g_content);
|
||||
server.send(200, _encoding_json, g_content);
|
||||
}
|
||||
}
|
||||
});
|
||||
server.on("/unlinkRepeater", []() {
|
||||
webServer.sendCORSHeaders(server);
|
||||
if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; }
|
||||
HTTPMethod method = server.method();
|
||||
if (method == HTTP_PUT || method == HTTP_POST) {
|
||||
// We are adding a linked repeater.
|
||||
uint32_t address = 0;
|
||||
if (server.hasArg("plain")) {
|
||||
Serial.println("Unlinking a repeater");
|
||||
DynamicJsonDocument doc(512);
|
||||
DeserializationError err = deserializeJson(doc, server.arg("plain"));
|
||||
if (err) {
|
||||
webServer.handleDeserializationError(server, err);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
if (obj.containsKey("address")) address = obj["address"];
|
||||
else if(obj.containsKey("remoteAddress")) address = obj["remoteAddress"];
|
||||
}
|
||||
}
|
||||
else if(server.hasArg("address"))
|
||||
address = atoi(server.arg("address").c_str());
|
||||
if(address == 0)
|
||||
server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No repeater address was supplied.\"}"));
|
||||
else {
|
||||
somfy.unlinkRepeater(address);
|
||||
DynamicJsonDocument doc(512);
|
||||
JsonArray r = doc.to<JsonArray>();
|
||||
somfy.toJSONRepeaters(r);
|
||||
serializeJson(doc, g_content);
|
||||
server.send(200, _encoding_json, g_content);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
server.on("/unlinkRemote", []() {
|
||||
webServer.sendCORSHeaders(server);
|
||||
if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; }
|
||||
|
|
|
|||
1
Web.h
1
Web.h
|
|
@ -12,6 +12,7 @@ class Web {
|
|||
void handleStreamFile(WebServer &server, const char *filename, const char *encoding);
|
||||
void handleController(WebServer &server);
|
||||
void handleLoginContext(WebServer &server);
|
||||
void handleGetRepeaters(WebServer &server);
|
||||
void handleGetRooms(WebServer &server);
|
||||
void handleGetShades(WebServer &server);
|
||||
void handleGetGroups(WebServer &server);
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
2.3.3
|
||||
2.4.0
|
||||
|
|
@ -3,11 +3,11 @@
|
|||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" href="main.css?v=2.3.3" type="text/css" />
|
||||
<link rel="stylesheet" href="widgets.css?v=2.3.3" type="text/css" />
|
||||
<link rel="stylesheet" href="icons.css?v=2.3.3" type="text/css" />
|
||||
<link rel="stylesheet" href="main.css?v=2.4.0" type="text/css" />
|
||||
<link rel="stylesheet" href="widgets.css?v=2.4.0" type="text/css" />
|
||||
<link rel="stylesheet" href="icons.css?v=2.4.0" type="text/css" />
|
||||
<link rel="icon" type="image/png" href="favicon.png" />
|
||||
<script type="text/javascript" src="index.js?v=2.3.3"></script>
|
||||
<script type="text/javascript" src="index.js?v=2.4.0"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="divContainer" class="container main" data-auth="false">
|
||||
|
|
@ -295,7 +295,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div id="divSomfySettings" style="display:none;">
|
||||
<div class="subtab-container"><span class="selected" data-grpid="divSomfyRooms">Rooms</span><span class="" data-grpid="divSomfyMotors">Shades</span><span data-grpid="divSomfyGroups">Groups</span><span data-grpid="divVirtualRemote">Virtual Remote</span></div>
|
||||
<div class="subtab-container"><span class="selected" data-grpid="divSomfyRooms">Rooms</span><span class="" data-grpid="divSomfyMotors">Shades</span><span data-grpid="divSomfyGroups">Groups</span><span data-grpid="divRepeater">Repeater</span><span data-grpid="divVirtualRemote">Virtual Remote</span></div>
|
||||
<div id="divSomfyRooms" class="subtab-content" style="padding-top:10px;">
|
||||
<div id="divRoomListContainer">
|
||||
<div style="font-size:.8em;">Drag each item to set the order in which they appear in the list.</div>
|
||||
|
|
@ -631,6 +631,17 @@
|
|||
</div>
|
||||
|
||||
</div>
|
||||
<div id="divRepeater" style="display:none;padding-top:10px;" class="subtab-content">
|
||||
<div id="divRepeaterListContainer">
|
||||
<div style="font-size:.8em;">All repeated remote addresses are listed below.</div>
|
||||
<div id="divRepeatList" class="edit-repeaterlist"></div>
|
||||
<div class="button-container">
|
||||
<button id="btnLinkRepeater" type="button" onclick="somfy.linkRepeatRemote();">
|
||||
Scan Remote
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="divVirtualRemote" style="display:none;" class="subtab-content">
|
||||
<div class="field-group" style="margin-top:-18px;display:inline-block;width:100%;">
|
||||
<select id="selVRMotor" style="width:100%;">
|
||||
|
|
@ -927,14 +938,14 @@
|
|||
});
|
||||
(async () => { await init(); })();
|
||||
/*
|
||||
security.init();
|
||||
//(async () => { await general.init(); })();
|
||||
general.init();
|
||||
wifi.init();
|
||||
somfy.init();
|
||||
mqtt.init();
|
||||
firmware.init();
|
||||
*/
|
||||
security.init();
|
||||
//(async () => { await general.init(); })();
|
||||
general.init();
|
||||
wifi.init();
|
||||
somfy.init();
|
||||
mqtt.init();
|
||||
firmware.init();
|
||||
*/
|
||||
/*
|
||||
let o = {
|
||||
general: { hostname: 'hostname', ntpServer: 'pool.ntp.org', posixZone: '<-10>10', ssdpBroadcast: true }
|
||||
|
|
|
|||
|
|
@ -1264,7 +1264,7 @@ var security = new Security();
|
|||
|
||||
class General {
|
||||
initialized = false;
|
||||
appVersion = 'v2.3.3';
|
||||
appVersion = 'v2.4.0';
|
||||
reloadApp = false;
|
||||
init() {
|
||||
if (this.initialized) return;
|
||||
|
|
@ -1957,6 +1957,7 @@ class Somfy {
|
|||
this.setRoomsList(somfy.rooms);
|
||||
this.setShadesList(somfy.shades);
|
||||
this.setGroupsList(somfy.groups);
|
||||
this.setRepeaterList(somfy.repeaters);
|
||||
if (typeof somfy.version !== 'undefined') firmware.procFwStatus(somfy.version);
|
||||
}
|
||||
});
|
||||
|
|
@ -2203,6 +2204,18 @@ class Somfy {
|
|||
});
|
||||
});
|
||||
}
|
||||
setRepeaterList(addresses) {
|
||||
let divCfg = '';
|
||||
if (typeof addresses !== 'undefined') {
|
||||
for (let i = 0; i < addresses.length; i++) {
|
||||
divCfg += `<div class="somfyRepeater" data-address="${addresses[i]}"><div class="repeater-name">${addresses[i]}</div>`;
|
||||
divCfg += `<div class="button-outline" onclick="somfy.unlinkRepeater(${addresses[i]});"><i class="icss-trash"></i></div>`;
|
||||
divCfg += '</div>';
|
||||
}
|
||||
}
|
||||
document.getElementById('divRepeatList').innerHTML = divCfg;
|
||||
|
||||
}
|
||||
setShadesList(shades) {
|
||||
let divCfg = '';
|
||||
let divCtl = '';
|
||||
|
|
@ -2832,6 +2845,16 @@ class Somfy {
|
|||
this.setLinkedRemotesList(shade);
|
||||
});
|
||||
}
|
||||
else {
|
||||
lnk = document.getElementById('divLinkRepeater');
|
||||
if (lnk) {
|
||||
putJSONSync(`/linkRepeater`, {address:frame.address}, (err, repeaters) => {
|
||||
lnk.remove();
|
||||
if (err) ui.serviceError(err);
|
||||
else this.setRepeaterList(repeaters);
|
||||
});
|
||||
}
|
||||
}
|
||||
let frames = document.getElementById('divFrames');
|
||||
let row = document.createElement('div');
|
||||
row.classList.add('frame-row');
|
||||
|
|
@ -3092,6 +3115,8 @@ class Somfy {
|
|||
showEditRoom(bShow) {
|
||||
let el = document.getElementById('divLinking');
|
||||
if (el) el.remove();
|
||||
el = document.getElementById('divLinkRepeater');
|
||||
if (el) el.remove();
|
||||
el = document.getElementById('divPairing');
|
||||
if (el) el.remove();
|
||||
el = document.getElementById('divRollingCode');
|
||||
|
|
@ -3108,6 +3133,8 @@ class Somfy {
|
|||
showEditShade(bShow) {
|
||||
let el = document.getElementById('divLinking');
|
||||
if (el) el.remove();
|
||||
el = document.getElementById('divLinkRepeater');
|
||||
if (el) el.remove();
|
||||
el = document.getElementById('divPairing');
|
||||
if (el) el.remove();
|
||||
el = document.getElementById('divRollingCode');
|
||||
|
|
@ -3124,6 +3151,8 @@ class Somfy {
|
|||
showEditGroup(bShow) {
|
||||
let el = document.getElementById('divLinking');
|
||||
if (el) el.remove();
|
||||
el = document.getElementById('divLinkRepeater');
|
||||
if (el) el.remove();
|
||||
el = document.getElementById('divPairing');
|
||||
if (el) el.remove();
|
||||
el = document.getElementById('divRollingCode');
|
||||
|
|
@ -3332,6 +3361,15 @@ class Somfy {
|
|||
}
|
||||
});
|
||||
}
|
||||
updateRepeatList() {
|
||||
getJSONSync('/repeaters', (err, repeaters) => {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
ui.serviceError(err);
|
||||
}
|
||||
else this.setRepeaterList(repeaters);
|
||||
});
|
||||
}
|
||||
deleteRoom(roomId) {
|
||||
let valid = true;
|
||||
if (isNaN(roomId) || roomId >= 255 || roomId <= 0) {
|
||||
|
|
@ -3740,6 +3778,20 @@ class Somfy {
|
|||
document.getElementById('somfyShade').appendChild(div);
|
||||
return div;
|
||||
}
|
||||
linkRepeatRemote() {
|
||||
let div = document.createElement('div');
|
||||
let html = `<div id="divLinkRepeater" class="instructions" data-type="link-repeatremote" style="border-radius:27px;">`;
|
||||
html += '<div>Press any button on the remote to repeat its signals.</div>';
|
||||
html += '<div class="sub-message">When assigned, ESPSomfy RTS will act as a repeater and repeat any frames for the identified remotes. Only repeat remotes when ESPSomfy RTS reliably hears the remote but the motor does not. Repeating unnecessary radio signals will degrade radio performance.</div>'
|
||||
html += '<div class="sub-message">Once a signal is detected from the remote this window will close and the remote signals will be repeated.</div>'
|
||||
html += '<hr></hr>';
|
||||
html += `<div><div class="button-container"><button id="btnStopLinking" type="button" style="padding-left:20px;padding-right:20px;" onclick="document.getElementById('divLinkRepeater').remove();">Cancel</button></div>`;
|
||||
html += '</div>';
|
||||
div.innerHTML = html;
|
||||
document.getElementById('divConfigPnl').appendChild(div);
|
||||
return div;
|
||||
}
|
||||
|
||||
linkGroupShade(groupId) {
|
||||
let div = document.createElement('div');
|
||||
let html = `<div id="divLinkGroup" class="inst-overlay wizard" data-type="link-shade" data-groupid="${groupId}" data-stepid="1">`;
|
||||
|
|
@ -3956,6 +4008,17 @@ class Somfy {
|
|||
});
|
||||
return div;
|
||||
}
|
||||
unlinkRepeater(address) {
|
||||
let prompt = ui.promptMessage('Are you sure you want to stop repeating frames from this address?', () => {
|
||||
putJSONSync('/unlinkRepeater', { address: address }, (err, repeaters) => {
|
||||
if (err) ui.serviceError(err);
|
||||
else this.setRepeaterList(repeaters);
|
||||
prompt.remove();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
unlinkRemote(shadeId, remoteAddress) {
|
||||
let prompt = ui.promptMessage('Are you sure you want to unlink this remote from the shade?', () => {
|
||||
let obj = {
|
||||
|
|
@ -4192,6 +4255,7 @@ class Firmware {
|
|||
html += `<div style="font-size:14px;">Restoring network settings from a different board than the original will ignore Ethernet chip settings. Security, MQTT and WiFi will also not be restored since backup files do not contain passwords.</div><hr/>`;
|
||||
html += '<div style="font-size:14px;margin-bottom:27px;text-align:left;margin-left:70px;">';
|
||||
html += `<div class="field-group" style="vertical-align:middle;width:auto;"><input id="cbRestoreShades" type="checkbox" data-bind="shades" style="display:inline-block;" checked="true" /><label for="cbRestoreShades" style="display:inline-block;cursor:pointer;color:white;">Restore Shades and Groups</label></div>`;
|
||||
html += `<div class="field-group" style="vertical-align:middle;width:auto;"><input id="cbRestoreRepeaters" type="checkbox" data-bind="repeaters" style="display:inline-block;" /><label for="cbRestoreRepeaters" style="display:inline-block;cursor:pointer;color:white;">Restore Repeaters</label></div>`;
|
||||
html += `<div class="field-group" style="vertical-align:middle;width:auto;"><input id="cbRestoreSystem" type="checkbox" data-bind="settings" style="display:inline-block;" /><label for="cbRestoreSystem" style="display:inline-block;cursor:pointer;color:white;">Restore System Settings</label></div>`;
|
||||
html += `<div class="field-group" style="vertical-align:middle;width:auto;"><input id="cbRestoreNetwork" type="checkbox" data-bind="network" style="display:inline-block;" /><label for="cbRestoreNetwork" style="display:inline-block;cursor:pointer;color:white;">Restore Network Settings</label></div>`
|
||||
html += `<div class="field-group" style="vertical-align:middle;width:auto;"><input id="cbRestoreTransceiver" type="checkbox" data-bind="transceiver" style="display:inline-block;" /><label for="cbRestoreTransceiver" style="display:inline-block;cursor:pointer;color:white;">Restore Radio Settings</label></div>`;
|
||||
|
|
@ -4559,11 +4623,15 @@ class Firmware {
|
|||
ui.errorMessage('You must select a valid backup file to proceed.');
|
||||
return;
|
||||
}
|
||||
else if (!filename.endsWith('.backup') || filename.indexOf('ESPSomfyRTS') === -1) {
|
||||
else if (field.files[0].size > 20480) {
|
||||
ui.errorMessage(el, `This file is ${field.files[0].size.fmt("#,##0")} bytes in length. This file is too large to be a valid backup file.`);
|
||||
return;
|
||||
}
|
||||
else if (!filename.endsWith('.backup')) {
|
||||
ui.errorMessage(el, 'This file is not a valid backup file');
|
||||
return;
|
||||
}
|
||||
if (!data.shades && !data.settings && !data.network && !data.transceiver) {
|
||||
if (!data.shades && !data.settings && !data.network && !data.transceiver && !data.repeaters) {
|
||||
ui.errorMessage(el, 'No restore options have been selected');
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,6 +46,10 @@
|
|||
left: 0px;
|
||||
color: white;
|
||||
}
|
||||
#divLinkRepeat {
|
||||
border-radius:24px;
|
||||
}
|
||||
.edit-repeaterlist,
|
||||
.edit-roomlist,
|
||||
.edit-grouplist,
|
||||
.edit-motorlist {
|
||||
|
|
@ -61,6 +65,7 @@
|
|||
max-height:calc(100% - 77px);
|
||||
overflow:auto;
|
||||
}
|
||||
.instructions .sub-message,
|
||||
.prompt-message .sub-message {
|
||||
font-size: 17px;
|
||||
padding-left: 10px;
|
||||
|
|
@ -82,6 +87,7 @@
|
|||
.wizard[data-stepid="6"] .wizard-step:not([data-stepid="6"]) { display: none; }
|
||||
.wizard[data-stepid="7"] .wizard-step:not([data-stepid="7"]) { display: none; }
|
||||
|
||||
.somfyRepeater,
|
||||
.somfyRoom,
|
||||
.somfyGroup,
|
||||
.somfyShade {
|
||||
|
|
@ -93,6 +99,8 @@
|
|||
}
|
||||
.linked-shade > div,
|
||||
.linked-shade > span,
|
||||
.somfyRepeater > div,
|
||||
.somfyRepeater > span,
|
||||
.somfyRoom > div,
|
||||
.somfyRoom > span,
|
||||
.somfyGroup > div,
|
||||
|
|
@ -103,12 +111,16 @@
|
|||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
}
|
||||
.repeater-name,
|
||||
.linked-shade {
|
||||
width: 100%;
|
||||
padding-bottom: 4px;
|
||||
display: inline-table;
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
}
|
||||
.repeater-name {
|
||||
text-align:left;
|
||||
}
|
||||
.linked-shade > .linkedshade-name { width: 100%; }
|
||||
.pin-digit {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue