mirror of
https://github.com/rstrouse/ESPSomfy-RTS.git
synced 2025-12-13 11:02:12 +01:00
80-bit and 56-bit protocols on same transceiver
Added the ability to process 80-bit and 56-bit motors on the same ESPSomfy RTS device.
This commit is contained in:
parent
3f662b7bd0
commit
370b8f1a23
10 changed files with 314 additions and 107 deletions
|
|
@ -6,9 +6,9 @@
|
|||
|
||||
extern Preferences pref;
|
||||
|
||||
#define SHADE_HDR_VER 1
|
||||
#define SHADE_HDR_VER 2
|
||||
#define SHADE_HDR_SIZE 16
|
||||
#define SHADE_REC_SIZE 176
|
||||
#define SHADE_REC_SIZE 180
|
||||
|
||||
bool ConfigFile::begin(const char* filename, bool readOnly) {
|
||||
this->file = LittleFS.open(filename, readOnly ? "r" : "w");
|
||||
|
|
@ -228,7 +228,7 @@ bool ShadeConfigFile::validate() {
|
|||
}
|
||||
// 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());
|
||||
Serial.printf("File size is not correct should be %d and got %d\n", 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;
|
||||
|
|
@ -282,6 +282,9 @@ bool ShadeConfigFile::loadFile(SomfyShadeController *s, const char *filename) {
|
|||
shade->setRemoteAddress(this->readUInt32(0));
|
||||
this->readString(shade->name, sizeof(shade->name));
|
||||
shade->hasTilt = this->readBool(false);
|
||||
if(this->header.version > 1) {
|
||||
shade->bitLength = this->readUInt8(56);
|
||||
}
|
||||
shade->upTime = this->readUInt32(shade->upTime);
|
||||
shade->downTime = this->readUInt32(shade->downTime);
|
||||
shade->tiltTime = this->readUInt32(shade->tiltTime);
|
||||
|
|
@ -314,6 +317,7 @@ bool ShadeConfigFile::writeShadeRecord(SomfyShade *shade) {
|
|||
this->writeUInt32(shade->getRemoteAddress());
|
||||
this->writeString(shade->name, sizeof(shade->name));
|
||||
this->writeBool(shade->hasTilt);
|
||||
this->writeUInt8(shade->bitLength);
|
||||
this->writeUInt32(shade->upTime);
|
||||
this->writeUInt32(shade->downTime);
|
||||
this->writeUInt32(shade->tiltTime);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
#ifndef configsettings_h
|
||||
#define configsettings_h
|
||||
|
||||
#define FW_VERSION "v1.4.1"
|
||||
#define FW_VERSION "v1.4.2"
|
||||
enum DeviceStatus {
|
||||
DS_OK = 0,
|
||||
DS_ERROR = 1,
|
||||
|
|
|
|||
306
Somfy.cpp
306
Somfy.cpp
|
|
@ -1,4 +1,3 @@
|
|||
#include <Arduino.h>
|
||||
#include <Preferences.h>
|
||||
#include <ELECHOUSE_CC1101_SRC_DRV.h>
|
||||
#include <SPI.h>
|
||||
|
|
@ -45,11 +44,12 @@ somfy_commands translateSomfyCommand(const String& string) {
|
|||
else if (string.equalsIgnoreCase("Down")) return somfy_commands::Down;
|
||||
else if (string.equalsIgnoreCase("MyDown")) return somfy_commands::MyDown;
|
||||
else if (string.equalsIgnoreCase("UpDown")) return somfy_commands::UpDown;
|
||||
else if (string.equalsIgnoreCase("MyUpDown")) return somfy_commands::MyUpDown;
|
||||
else if (string.equalsIgnoreCase("Prog")) return somfy_commands::Prog;
|
||||
else if (string.equalsIgnoreCase("SunFlag")) return somfy_commands::SunFlag;
|
||||
else if (string.equalsIgnoreCase("StepUp")) return somfy_commands::StepUp;
|
||||
else if (string.equalsIgnoreCase("StepDown")) return somfy_commands::StepDown;
|
||||
else if (string.equalsIgnoreCase("Flag")) return somfy_commands::Flag;
|
||||
else if (string.startsWith("mud") || string.startsWith("MUD")) return somfy_commands::MyUpDown;
|
||||
else if (string.startsWith("md") || string.startsWith("MD")) return somfy_commands::MyDown;
|
||||
else if (string.startsWith("ud") || string.startsWith("UD")) return somfy_commands::UpDown;
|
||||
else if (string.startsWith("mu") || string.startsWith("MU")) return somfy_commands::MyUp;
|
||||
|
|
@ -78,6 +78,8 @@ String translateSomfyCommand(const somfy_commands cmd) {
|
|||
return "Up+Down";
|
||||
case somfy_commands::MyDown:
|
||||
return "My+Down";
|
||||
case somfy_commands::MyUpDown:
|
||||
return "My+Up+Down";
|
||||
case somfy_commands::Prog:
|
||||
return "Prog";
|
||||
case somfy_commands::SunFlag:
|
||||
|
|
@ -95,12 +97,16 @@ String translateSomfyCommand(const somfy_commands cmd) {
|
|||
void somfy_frame_t::decodeFrame(byte* frame) {
|
||||
byte decoded[10];
|
||||
decoded[0] = frame[0];
|
||||
for (byte i = 1; i < 10; i++) {
|
||||
// The last 3 bytes are not encoded even on 80-bits. Go figure.
|
||||
decoded[7] = frame[7];
|
||||
decoded[8] = frame[8];
|
||||
decoded[9] = frame[9];
|
||||
for (byte i = 1; i < 7; i++) {
|
||||
decoded[i] = frame[i] ^ frame[i - 1];
|
||||
}
|
||||
byte checksum = 0;
|
||||
// We only want the upper nibble for the command byte.
|
||||
for (byte i = 0; i < 10; i++) {
|
||||
for (byte i = 0; i < 7; i++) {
|
||||
if (i == 1) checksum = checksum ^ (decoded[i] >> 4);
|
||||
else checksum = checksum ^ decoded[i] ^ (decoded[i] >> 4);
|
||||
}
|
||||
|
|
@ -108,7 +114,8 @@ void somfy_frame_t::decodeFrame(byte* frame) {
|
|||
|
||||
this->checksum = decoded[1] & 0b1111;
|
||||
this->encKey = decoded[0];
|
||||
this->cmd = (somfy_commands)(decoded[1] >> 4);
|
||||
// Pull in the 80-bit commands. The upper nibble will be 0 even on 80 bit packets.
|
||||
this->cmd = (somfy_commands)((decoded[1] >> 4) | ((decoded[8] & 0x08) << 4));
|
||||
this->rollingCode = decoded[3] + (decoded[2] << 8);
|
||||
this->remoteAddress = (decoded[6] + (decoded[5] << 8) + (decoded[4] << 16));
|
||||
this->valid = this->checksum == checksum && this->remoteAddress < 16777215;
|
||||
|
|
@ -122,15 +129,16 @@ void somfy_frame_t::decodeFrame(byte* frame) {
|
|||
case somfy_commands::Down:
|
||||
case somfy_commands::MyDown:
|
||||
case somfy_commands::UpDown:
|
||||
case somfy_commands::MyUpDown:
|
||||
case somfy_commands::Prog:
|
||||
case somfy_commands::SunFlag:
|
||||
case somfy_commands::Flag:
|
||||
case somfy_commands::StepUp:
|
||||
case somfy_commands::StepDown:
|
||||
case somfy_commands::Unknown7:
|
||||
case somfy_commands::UnknownC:
|
||||
case somfy_commands::UnknownD:
|
||||
case somfy_commands::UnknownE:
|
||||
case somfy_commands::UnknownF:
|
||||
case somfy_commands::StepDown:
|
||||
break;
|
||||
default:
|
||||
this->valid = false;
|
||||
|
|
@ -147,6 +155,8 @@ void somfy_frame_t::decodeFrame(byte* frame) {
|
|||
Serial.print(translateSomfyCommand(this->cmd));
|
||||
Serial.print(" RCODE:");
|
||||
Serial.print(this->rollingCode);
|
||||
Serial.print(" BITS:");
|
||||
Serial.print(this->bitLength);
|
||||
Serial.print(" HWSYNC:");
|
||||
Serial.println(this->hwsync);
|
||||
}
|
||||
|
|
@ -184,16 +194,44 @@ void somfy_frame_t::decodeFrame(byte* frame) {
|
|||
Serial.println();
|
||||
}
|
||||
}
|
||||
void somfy_frame_t::decodeFrame(somfy_rx_t *rx) {
|
||||
this->hwsync = rx->cpt_synchro_hw;
|
||||
this->pulseCount = rx->pulseCount;
|
||||
this->bitLength = rx->bit_length;
|
||||
this->rssi = ELECHOUSE_cc1101.getRssi();
|
||||
this->decodeFrame(rx->payload);
|
||||
}
|
||||
void somfy_frame_t::encodeFrame(byte *frame) {
|
||||
const byte btn = static_cast<byte>(cmd);
|
||||
frame[0] = this->encKey; // Encryption key. Doesn't matter much
|
||||
frame[1] = btn << 4; // Which button did you press? The 4 LSB will be the checksum
|
||||
frame[1] = (btn & 0x0F) << 4; // Which button did you press? The 4 LSB will be the checksum
|
||||
frame[2] = this->rollingCode >> 8; // Rolling code (big endian)
|
||||
frame[3] = this->rollingCode; // Rolling code
|
||||
frame[4] = this->remoteAddress >> 16; // Remote address
|
||||
frame[5] = this->remoteAddress >> 8; // Remote address
|
||||
frame[6] = this->remoteAddress; // Remote address
|
||||
frame[7] = 132;
|
||||
frame[8] = 0;
|
||||
frame[9] = 29;
|
||||
switch(this->cmd) {
|
||||
case somfy_commands::StepUp:
|
||||
frame[7] = 136;
|
||||
frame[8] = 52;
|
||||
frame[9] = 22;
|
||||
break;
|
||||
case somfy_commands::StepDown:
|
||||
frame[7] = 132;
|
||||
frame[8] = 48;
|
||||
frame[9] = 30;
|
||||
break;
|
||||
case somfy_commands::Prog:
|
||||
frame[7] = 196;
|
||||
frame[8] = 0;
|
||||
frame[9] = 25;
|
||||
break;
|
||||
}
|
||||
byte checksum = 0;
|
||||
|
||||
for (byte i = 0; i < 7; i++) {
|
||||
checksum = checksum ^ frame[i] ^ (frame[i] >> 4);
|
||||
}
|
||||
|
|
@ -347,6 +385,18 @@ bool SomfyShadeController::begin() {
|
|||
this->loadLegacy();
|
||||
}
|
||||
this->transceiver.begin();
|
||||
|
||||
// Set the radio type for shades that have yet to be specified.
|
||||
bool saveFlag = false;
|
||||
for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) {
|
||||
SomfyShade *shade = &this->shades[i];
|
||||
if(shade->getShadeId() != 255 && shade->bitLength == 0) {
|
||||
Serial.printf("Setting bit length to %d\n", this->transceiver.config.type);
|
||||
shade->bitLength = this->transceiver.config.type;
|
||||
saveFlag = true;
|
||||
}
|
||||
}
|
||||
if(saveFlag) somfy.commit();
|
||||
return true;
|
||||
}
|
||||
void SomfyShadeController::commit() {
|
||||
|
|
@ -508,10 +558,10 @@ void SomfyShade::checkMovement() {
|
|||
Serial.print("% target ");
|
||||
Serial.print(this->target);
|
||||
Serial.println("%");
|
||||
if(!this->seekingMyPos) this->sendCommand(somfy_commands::My);
|
||||
if(!this->seekingFixedPos) this->sendCommand(somfy_commands::My);
|
||||
else this->direction = 0;
|
||||
this->seekingPos = false;
|
||||
this->seekingMyPos = false;
|
||||
this->seekingFixedPos = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -549,9 +599,9 @@ void SomfyShade::checkMovement() {
|
|||
Serial.print("% target ");
|
||||
Serial.print(this->target);
|
||||
Serial.println("%");
|
||||
if(!this->seekingMyPos) this->sendCommand(somfy_commands::My);
|
||||
if(!this->seekingFixedPos) this->sendCommand(somfy_commands::My);
|
||||
else this->direction = 0;
|
||||
this->seekingMyPos = false;
|
||||
this->seekingFixedPos = false;
|
||||
this->seekingPos = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -628,7 +678,7 @@ void SomfyShade::checkMovement() {
|
|||
else this->myPos = this->position;
|
||||
SomfyRemote::sendCommand(somfy_commands::My, SETMY_REPEATS);
|
||||
this->settingMyPos = false;
|
||||
this->seekingMyPos = false;
|
||||
this->seekingFixedPos = false;
|
||||
this->commitMyPosition();
|
||||
}
|
||||
}
|
||||
|
|
@ -778,6 +828,26 @@ void SomfyShade::processWaitingFrame() {
|
|||
if(this->lastFrame.processed) return;
|
||||
if(this->lastFrame.await > 0 && (millis() > this->lastFrame.await)) {
|
||||
switch(this->lastFrame.cmd) {
|
||||
case somfy_commands::StepUp:
|
||||
this->lastFrame.processed = true;
|
||||
// Simply move the shade up by 1%.
|
||||
if(this->position > 0) {
|
||||
this->seekingFixedPos = true;
|
||||
this->seekingPos = true;
|
||||
this->target = this->position - 1;
|
||||
this->setMovement(-1);
|
||||
}
|
||||
break;
|
||||
case somfy_commands::StepDown:
|
||||
this->lastFrame.processed = true;
|
||||
// Simply move the shade down by 1%.
|
||||
if(this->position < 100) {
|
||||
this->seekingFixedPos = true;
|
||||
this->seekingPos = true;
|
||||
this->target = this->position + 1;
|
||||
this->setMovement(1);
|
||||
}
|
||||
break;
|
||||
case somfy_commands::Down:
|
||||
case somfy_commands::Up:
|
||||
if(this->hasTilt) { // Theoretically this should get here unless it does have a tilt.
|
||||
|
|
@ -818,7 +888,7 @@ void SomfyShade::processWaitingFrame() {
|
|||
int8_t dir = 0;
|
||||
if(myPos < this->position) dir = -1;
|
||||
else if(myPos > this->position) dir = 1;
|
||||
if(dir != 0) this->seekingMyPos = true;
|
||||
if(dir != 0) this->seekingFixedPos = true;
|
||||
this->seekingPos = true;
|
||||
this->target = this->myPos;
|
||||
this->setMovement(dir);
|
||||
|
|
@ -862,7 +932,6 @@ void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) {
|
|||
if(!internal) this->lastFrame.await = millis() + 500;
|
||||
else if(this->lastFrame.repeats >= TILT_REPEATS) {
|
||||
// This is an internal tilt command.
|
||||
Serial.println("Processing Tilt UP...");
|
||||
this->setTiltMovement(-1);
|
||||
this->lastFrame.processed = true;
|
||||
return;
|
||||
|
|
@ -885,7 +954,6 @@ void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) {
|
|||
if(!internal) this->lastFrame.await = millis() + 500;
|
||||
else if(this->lastFrame.repeats >= TILT_REPEATS) {
|
||||
// This is an internal tilt command.
|
||||
Serial.println("Processing Tilt DOWN...");
|
||||
this->setTiltMovement(1);
|
||||
this->lastFrame.processed = true;
|
||||
return;
|
||||
|
|
@ -918,7 +986,7 @@ void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) {
|
|||
else if(this->myPos > this->position) dir = 1;
|
||||
if(dir != 0) {
|
||||
Serial.println("Start moving to My Position");
|
||||
this->seekingMyPos = true;
|
||||
this->seekingFixedPos = true;
|
||||
}
|
||||
this->seekingPos = true;
|
||||
this->target = this->myPos;
|
||||
|
|
@ -929,6 +997,36 @@ void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) {
|
|||
// is no my position set.
|
||||
this->lastFrame.processed = true;
|
||||
break;
|
||||
case somfy_commands::StepUp:
|
||||
if(!internal) {
|
||||
this->lastFrame.await = millis() + 200;
|
||||
}
|
||||
else {
|
||||
this->lastFrame.processed = true;
|
||||
// Simply move the shade up by 1%.
|
||||
if(this->position > 0) {
|
||||
this->seekingFixedPos = true;
|
||||
this->seekingPos = true;
|
||||
this->target = this->position - 1;
|
||||
dir = -1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case somfy_commands::StepDown:
|
||||
if(!internal) {
|
||||
this->lastFrame.await = millis() + 200;
|
||||
}
|
||||
else {
|
||||
this->lastFrame.processed = true;
|
||||
// Simply move the shade down by 1%.
|
||||
if(this->position < 100) {
|
||||
this->seekingFixedPos = true;
|
||||
this->seekingPos = true;
|
||||
this->target = this->position + 1;
|
||||
dir = 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
dir = 0;
|
||||
break;
|
||||
|
|
@ -1007,7 +1105,7 @@ void SomfyShade::moveToMyPosition() {
|
|||
Serial.print("Seeking my Position:");
|
||||
Serial.print(this->myPos);
|
||||
Serial.println("%");
|
||||
this->seekingMyPos = true;
|
||||
this->seekingFixedPos = true;
|
||||
this->target = this->myPos;
|
||||
Serial.print("Moving to ");
|
||||
Serial.print(this->target);
|
||||
|
|
@ -1019,6 +1117,7 @@ void SomfyShade::moveToMyPosition() {
|
|||
SomfyRemote::sendCommand(somfy_commands::My);
|
||||
}
|
||||
void SomfyShade::sendCommand(somfy_commands cmd, uint8_t repeat) {
|
||||
if(this->bitLength == 0) this->bitLength = somfy.transceiver.config.type;
|
||||
if(cmd == somfy_commands::Up) {
|
||||
this->target = 0;
|
||||
this->seekingPos = false;
|
||||
|
|
@ -1133,6 +1232,7 @@ bool SomfyShade::fromJSON(JsonObject &obj) {
|
|||
if(obj.containsKey("remoteAddress")) this->setRemoteAddress(obj["remoteAddress"]);
|
||||
if(obj.containsKey("tiltTime")) this->tiltTime = obj["tiltTime"];
|
||||
if(obj.containsKey("hasTilt")) this->hasTilt = obj["hasTilt"];
|
||||
if(obj.containsKey("bitLength")) this->bitLength = obj["bitLength"];
|
||||
if(obj.containsKey("shadeType")) {
|
||||
if(obj["shadeType"].is<const char *>()) {
|
||||
if(strncmp(obj["shadeType"].as<const char *>(), "roller", 7) == 0)
|
||||
|
|
@ -1184,6 +1284,7 @@ bool SomfyShade::toJSON(JsonObject &obj) {
|
|||
obj["hasTilt"] = this->hasTilt;
|
||||
obj["tiltTime"] = this->tiltTime;
|
||||
obj["shadeType"] = static_cast<uint8_t>(this->shadeType);
|
||||
obj["bitLength"] = this->bitLength;
|
||||
SomfyRemote::toJSON(obj);
|
||||
JsonArray arr = obj.createNestedArray("linkedRemotes");
|
||||
for(uint8_t i = 0; i < SOMFY_MAX_LINKED_REMOTES; i++) {
|
||||
|
|
@ -1347,6 +1448,8 @@ void SomfyRemote::sendCommand(somfy_commands cmd, uint8_t repeat) {
|
|||
frame.remoteAddress = this->getRemoteAddress();
|
||||
frame.cmd = cmd;
|
||||
frame.repeats = repeat;
|
||||
frame.bitLength = this->bitLength;
|
||||
if(frame.bitLength == 0) frame.bitLength = bit_length;
|
||||
this->lastRollingCode = frame.rollingCode;
|
||||
somfy.sendFrame(frame, repeat);
|
||||
somfy.processFrame(frame, true);
|
||||
|
|
@ -1365,9 +1468,22 @@ void SomfyShadeController::sendFrame(somfy_frame_t &frame, uint8_t repeat) {
|
|||
|
||||
byte frm[10];
|
||||
frame.encodeFrame(frm);
|
||||
this->transceiver.sendFrame(frm, 2);
|
||||
this->transceiver.sendFrame(frm, frame.bitLength == 56 ? 2 : 12);
|
||||
// Transform the repeat bytes
|
||||
switch(frame.cmd) {
|
||||
case somfy_commands::StepUp:
|
||||
case somfy_commands::StepDown:
|
||||
break;
|
||||
case somfy_commands::Prog:
|
||||
frm[7] = 132;
|
||||
frm[9] = 63;
|
||||
break;
|
||||
default:
|
||||
frm[9] = 46;
|
||||
break;
|
||||
}
|
||||
for(uint8_t i = 0; i < repeat; i++) {
|
||||
this->transceiver.sendFrame(frm, 7);
|
||||
this->transceiver.sendFrame(frm, frame.bitLength == 56 ? 7 : 6);
|
||||
}
|
||||
this->transceiver.endTransmit();
|
||||
}
|
||||
|
|
@ -1479,32 +1595,37 @@ static const uint32_t tempo_symbol_max = SYMBOL * 2 * TOLERANCE_MAX;
|
|||
static const uint32_t tempo_inter_frame_gap = 30415;
|
||||
|
||||
static int16_t bitMin = SYMBOL * TOLERANCE_MIN;
|
||||
typedef enum {
|
||||
waiting_synchro = 0,
|
||||
receiving_data = 1,
|
||||
complete = 2
|
||||
} t_status;
|
||||
#define MAX_TIMINGS 500
|
||||
static uint16_t timing_index = 0;
|
||||
static uint32_t timings[MAX_TIMINGS];
|
||||
static struct somfy_rx_t
|
||||
{
|
||||
t_status status;
|
||||
uint8_t cpt_synchro_hw;
|
||||
uint8_t cpt_bits;
|
||||
uint8_t previous_bit;
|
||||
bool waiting_half_symbol;
|
||||
uint8_t payload[10];
|
||||
unsigned int pulses[MAX_TIMINGS];
|
||||
uint16_t pulseCount;
|
||||
} somfy_rx;
|
||||
uint8_t receive_buffer[10]; // 80 bits
|
||||
bool packet_received = false;
|
||||
uint8_t m_hwsync = 0;
|
||||
//static uint32_t timings[MAX_TIMINGS];
|
||||
static somfy_rx_t somfy_rx;
|
||||
static somfy_rx_queue_t rx_queue;
|
||||
|
||||
|
||||
void somfy_rx_queue_t::init() {
|
||||
Serial.println("Initializing RX Queue");
|
||||
memset(&this->items[0], 0x00, sizeof(somfy_rx_t) * MAX_RX_BUFFER);
|
||||
memset(&this->index[0], 0xFF, MAX_RX_BUFFER);
|
||||
this->length = 0;
|
||||
}
|
||||
bool somfy_rx_queue_t::pop(somfy_rx_t *rx) {
|
||||
// Read off the data from the oldest index.
|
||||
//Serial.println("Popping RX Queue");
|
||||
for(uint8_t i = MAX_RX_BUFFER - 1; i >= 0; i--) {
|
||||
if(this->index[i] < MAX_RX_BUFFER) {
|
||||
uint8_t ndx = this->index[i];
|
||||
memcpy(rx, &this->items[this->index[i]], sizeof(somfy_rx_t));
|
||||
memset(&this->items[ndx], 0x00, sizeof(somfy_rx_t));
|
||||
this->length--;
|
||||
this->index[i] = 255;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void Transceiver::sendFrame(byte *frame, uint8_t sync) {
|
||||
if(!this->config.enabled) return;
|
||||
uint32_t pin = 1 << this->config.TXPin;
|
||||
if (sync == 2) { // Only with the first frame.
|
||||
if (sync == 2 || sync == 12) { // Only with the first frame.
|
||||
// Wake-up pulse & Silence
|
||||
REG_WRITE(GPIO_OUT_W1TS_REG, pin);
|
||||
delayMicroseconds(9415);
|
||||
|
|
@ -1532,15 +1653,11 @@ void Transceiver::sendFrame(byte *frame, uint8_t sync) {
|
|||
delayMicroseconds(SYMBOL);
|
||||
REG_WRITE(GPIO_OUT_W1TS_REG, pin);
|
||||
delayMicroseconds(SYMBOL);
|
||||
//this->sendLow(SYMBOL);
|
||||
//this->sendHigh(SYMBOL);
|
||||
} else {
|
||||
REG_WRITE(GPIO_OUT_W1TS_REG, pin);
|
||||
delayMicroseconds(SYMBOL);
|
||||
REG_WRITE(GPIO_OUT_W1TC_REG, pin);
|
||||
delayMicroseconds(SYMBOL);
|
||||
//this->sendHigh(SYMBOL);
|
||||
//this->sendLow(SYMBOL);
|
||||
}
|
||||
}
|
||||
// Inter-frame silence
|
||||
|
|
@ -1560,19 +1677,25 @@ void RECEIVE_ATTR Transceiver::handleReceive() {
|
|||
last_time = time;
|
||||
switch (somfy_rx.status) {
|
||||
case waiting_synchro:
|
||||
somfy_rx.pulses[somfy_rx.pulseCount++] = duration;
|
||||
if(somfy_rx.pulseCount < MAX_TIMINGS) somfy_rx.pulses[somfy_rx.pulseCount++] = duration;
|
||||
if (duration > tempo_synchro_hw_min && duration < tempo_synchro_hw_max) {
|
||||
// We have found a hardware sync bit. There should be at least 4 of these.
|
||||
++somfy_rx.cpt_synchro_hw;
|
||||
}
|
||||
else if (duration > tempo_synchro_sw_min && duration < tempo_synchro_sw_max && somfy_rx.cpt_synchro_hw >= 4) {
|
||||
// If we have a full hardware sync then we should look for the software sync. If we have a software sync
|
||||
// bit and enough hardware sync bits then we should start receiving data.
|
||||
//memset(&somfy_rx, 0x00, sizeof(somfy_rx));
|
||||
// bit and enough hardware sync bits then we should start receiving data. It turns out that a 56 bit packet
|
||||
// with give 4 or 14 bits of hardware sync. An 80 bit packet give 12 or 24 bits of hw sync. Early on
|
||||
// I had some shorter and longer hw syncs but I can no longer repeat this.
|
||||
memset(somfy_rx.payload, 0x00, sizeof(somfy_rx.payload));
|
||||
somfy_rx.previous_bit = 0x00;
|
||||
somfy_rx.waiting_half_symbol = false;
|
||||
somfy_rx.cpt_bits = 0;
|
||||
// Keep an eye on this as it is possible that we might get fewer or more synchro bits.
|
||||
if (somfy_rx.cpt_synchro_hw <= 7) somfy_rx.bit_length = 56;
|
||||
else if (somfy_rx.cpt_synchro_hw == 14) somfy_rx.bit_length = 56;
|
||||
else if (somfy_rx.cpt_synchro_hw == 12) somfy_rx.bit_length = 80;
|
||||
else if (somfy_rx.cpt_synchro_hw > 17) somfy_rx.bit_length = 80;
|
||||
somfy_rx.status = receiving_data;
|
||||
}
|
||||
else {
|
||||
|
|
@ -1582,7 +1705,7 @@ void RECEIVE_ATTR Transceiver::handleReceive() {
|
|||
}
|
||||
break;
|
||||
case receiving_data:
|
||||
somfy_rx.pulses[somfy_rx.pulseCount++] = duration;
|
||||
if(somfy_rx.pulseCount < MAX_TIMINGS) somfy_rx.pulses[somfy_rx.pulseCount++] = duration;
|
||||
// We should be receiving data at this point.
|
||||
if (duration > tempo_symbol_min && duration < tempo_symbol_max && !somfy_rx.waiting_half_symbol) {
|
||||
somfy_rx.previous_bit = 1 - somfy_rx.previous_bit;
|
||||
|
|
@ -1608,40 +1731,68 @@ void RECEIVE_ATTR Transceiver::handleReceive() {
|
|||
somfy_rx.previous_bit = 0x00;
|
||||
somfy_rx.waiting_half_symbol = false;
|
||||
somfy_rx.cpt_bits = 0;
|
||||
somfy_rx.bit_length = 56;
|
||||
somfy_rx.status = waiting_synchro;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (somfy_rx.status == receiving_data && somfy_rx.cpt_bits == bit_length) {
|
||||
timing_index = somfy_rx.pulseCount;
|
||||
memcpy(timings, somfy_rx.pulses, somfy_rx.pulseCount * sizeof(uint32_t));
|
||||
memcpy(receive_buffer, somfy_rx.payload, sizeof(receive_buffer));
|
||||
packet_received = true;
|
||||
m_hwsync = somfy_rx.cpt_synchro_hw;
|
||||
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
|
||||
// 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) {
|
||||
// We have overflowed the buffer simply empty the last item
|
||||
// in this instance we will simply throw it away.
|
||||
uint8_t ndx = rx_queue.index[MAX_RX_BUFFER - 1];
|
||||
if(ndx < MAX_RX_BUFFER) rx_queue.items[ndx].pulseCount = 0;
|
||||
//memset(&this->items[ndx], 0x00, sizeof(somfy_rx_t));
|
||||
rx_queue.index[MAX_RX_BUFFER - 1] = 255;
|
||||
rx_queue.length--;
|
||||
}
|
||||
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_RX_BUFFER; i++) {
|
||||
if(rx_queue.items[i].pulseCount == 0) {
|
||||
first = i;
|
||||
memcpy(&rx_queue.items[i], &somfy_rx, sizeof(somfy_rx_t));
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Move the index so that it is the at position 0. The oldest item will fall off.
|
||||
for(uint8_t i = MAX_RX_BUFFER - 1; i > 0; i--) {
|
||||
rx_queue.index[i] = rx_queue.index[i - 1];
|
||||
}
|
||||
rx_queue.length++;
|
||||
rx_queue.index[0] = first;
|
||||
memset(&somfy_rx.payload, 0x00, sizeof(somfy_rx.payload));
|
||||
somfy_rx.cpt_synchro_hw = 0;
|
||||
somfy_rx.previous_bit = 0x00;
|
||||
somfy_rx.waiting_half_symbol = false;
|
||||
somfy_rx.cpt_bits = 0;
|
||||
somfy_rx.pulseCount = 0;
|
||||
somfy_rx.status = waiting_synchro;
|
||||
}
|
||||
}
|
||||
bool Transceiver::receive() {
|
||||
if (packet_received) {
|
||||
packet_received = false;
|
||||
this->frame.hwsync = m_hwsync;
|
||||
this->frame.rssi = ELECHOUSE_cc1101.getRssi();
|
||||
this->frame.decodeFrame(receive_buffer);
|
||||
//this->frame.lqi = ELECHOUSE_cc1101.getLqi();
|
||||
if (!this->frame.valid) this->clearReceived();
|
||||
this->emitFrame(&this->frame);
|
||||
return this->frame.valid;
|
||||
// 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);
|
||||
return this->frame.valid;
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void Transceiver::emitFrame(somfy_frame_t *frame) {
|
||||
void Transceiver::emitFrame(somfy_frame_t *frame, somfy_rx_t *rx) {
|
||||
if(sockEmit.activeClients(ROOM_EMIT_FRAME) > 0) {
|
||||
ClientSocketEvent evt("remoteFrame");
|
||||
char buf[30];
|
||||
|
|
@ -1655,19 +1806,23 @@ void Transceiver::emitFrame(somfy_frame_t *frame) {
|
|||
evt.appendMessage(buf);
|
||||
snprintf(buf, sizeof(buf), "\"rssi\":%d,", frame->rssi);
|
||||
evt.appendMessage(buf);
|
||||
snprintf(buf, sizeof(buf), "\"bits\":%d,", rx->bit_length);
|
||||
evt.appendMessage(buf);
|
||||
snprintf(buf, sizeof(buf), "\"sync\":%d,\"pulses\":[", frame->hwsync);
|
||||
evt.appendMessage(buf);
|
||||
for(uint16_t i = 0; i < timing_index; i++) {
|
||||
snprintf(buf, sizeof(buf), "%s%d", i != 0 ? "," : "", timings[i]);
|
||||
evt.appendMessage(buf);
|
||||
if(rx) {
|
||||
for(uint16_t i = 0; i < rx->pulseCount; i++) {
|
||||
snprintf(buf, sizeof(buf), "%s%d", i != 0 ? "," : "", rx->pulses[i]);
|
||||
evt.appendMessage(buf);
|
||||
}
|
||||
}
|
||||
evt.appendMessage("]}");
|
||||
sockEmit.sendToRoom(ROOM_EMIT_FRAME, &evt);
|
||||
}
|
||||
}
|
||||
void Transceiver::clearReceived(void) {
|
||||
packet_received = false;
|
||||
memset(receive_buffer, 0x00, sizeof(receive_buffer));
|
||||
//packet_received = false;
|
||||
//memset(receive_buffer, 0x00, sizeof(receive_buffer));
|
||||
if(this->config.enabled)
|
||||
attachInterrupt(interruptPin, handleReceive, CHANGE);
|
||||
}
|
||||
|
|
@ -1863,7 +2018,6 @@ void transceiver_config_t::load() {
|
|||
this->txPower = pref.getChar("txPower", this->txPower);
|
||||
this->rxBandwidth = pref.getFloat("rxBandwidth", this->rxBandwidth);
|
||||
|
||||
|
||||
this->removeNVSKey("internalCCMode");
|
||||
this->removeNVSKey("modulationMode");
|
||||
this->removeNVSKey("channel");
|
||||
|
|
@ -1962,17 +2116,13 @@ void transceiver_config_t::apply() {
|
|||
bool Transceiver::begin() {
|
||||
this->config.load();
|
||||
this->config.apply();
|
||||
rx_queue.init();
|
||||
return true;
|
||||
}
|
||||
void Transceiver::loop() {
|
||||
if (this->receive()) {
|
||||
this->clearReceived();
|
||||
//this->clearReceived();
|
||||
somfy.processFrame(this->frame, false);
|
||||
/*
|
||||
char buf[177];
|
||||
snprintf(buf, sizeof(buf), "{\"encKey\":%d,\"address\":%d,\"rcode\":%d,\"command\":\"%s\",\"rssi\":%d}", this->frame.encKey, this->frame.remoteAddress, this->frame.rollingCode, translateSomfyCommand(this->frame.cmd), this->frame.rssi);
|
||||
sockEmit.sendToClients("remoteFrame", buf);
|
||||
*/
|
||||
}
|
||||
else {
|
||||
somfy.processWaitingFrame();
|
||||
|
|
|
|||
46
Somfy.h
46
Somfy.h
|
|
@ -18,15 +18,17 @@ enum class somfy_commands : byte {
|
|||
Down = 0x4,
|
||||
MyDown = 0x5,
|
||||
UpDown = 0x6,
|
||||
Unknown7 = 0x7,
|
||||
MyUpDown = 0x7,
|
||||
Prog = 0x8,
|
||||
SunFlag = 0x9,
|
||||
Flag = 0xA,
|
||||
StepUp = 0xB,
|
||||
StepDown = 0xC,
|
||||
UnknownC = 0xC,
|
||||
UnknownD = 0xD,
|
||||
UnknownE = 0xE,
|
||||
UnknownF = 0xF,
|
||||
// Command extensions for 80 bit frames
|
||||
StepDown = 0x8B
|
||||
};
|
||||
enum class shade_types : byte {
|
||||
roller = 0x00,
|
||||
|
|
@ -37,6 +39,36 @@ enum class shade_types : byte {
|
|||
String translateSomfyCommand(const somfy_commands cmd);
|
||||
somfy_commands translateSomfyCommand(const String& string);
|
||||
|
||||
#define MAX_TIMINGS 300
|
||||
#define MAX_RX_BUFFER 3
|
||||
|
||||
typedef enum {
|
||||
waiting_synchro = 0,
|
||||
receiving_data = 1,
|
||||
complete = 2
|
||||
} t_status;
|
||||
typedef struct somfy_rx_t {
|
||||
t_status status;
|
||||
uint8_t bit_length = 56;
|
||||
uint8_t cpt_synchro_hw = 0;
|
||||
uint8_t cpt_bits = 0;
|
||||
uint8_t previous_bit = 0;
|
||||
bool waiting_half_symbol;
|
||||
uint8_t payload[10];
|
||||
unsigned int pulses[MAX_TIMINGS];
|
||||
uint16_t pulseCount = 0;
|
||||
};
|
||||
// A simple FIFO queue to hold rx buffers. We are using
|
||||
// a byte index to make it so we don't have to reorganize
|
||||
// the storage each time we push or pop.
|
||||
typedef struct somfy_rx_queue_t {
|
||||
void init();
|
||||
uint8_t length = 0;
|
||||
uint8_t index[MAX_RX_BUFFER];
|
||||
somfy_rx_t items[MAX_RX_BUFFER];
|
||||
//void push(somfy_rx_t *rx);
|
||||
bool pop(somfy_rx_t *rx);
|
||||
};
|
||||
typedef struct somfy_frame_t {
|
||||
bool valid = false;
|
||||
bool processed = false;
|
||||
|
|
@ -50,9 +82,12 @@ typedef struct somfy_frame_t {
|
|||
uint8_t hwsync = 0;
|
||||
uint8_t repeats = 0;
|
||||
uint32_t await = 0;
|
||||
uint8_t bitLength = 56;
|
||||
uint16_t pulseCount = 0;
|
||||
void print();
|
||||
void encodeFrame(byte *frame);
|
||||
void decodeFrame(byte* frame);
|
||||
void decodeFrame(somfy_rx_t *rx);
|
||||
bool isRepeat(somfy_frame_t &f);
|
||||
void copy(somfy_frame_t &f);
|
||||
};
|
||||
|
|
@ -65,6 +100,7 @@ class SomfyRemote {
|
|||
char m_remotePrefId[10] = "";
|
||||
uint32_t m_remoteAddress = 0;
|
||||
public:
|
||||
uint8_t bitLength = 0;
|
||||
char *getRemotePrefId() {return m_remotePrefId;}
|
||||
virtual bool toJSON(JsonObject &obj);
|
||||
virtual void setRemoteAddress(uint32_t address);
|
||||
|
|
@ -87,7 +123,7 @@ class SomfyShade : public SomfyRemote {
|
|||
float startTiltPos = 0.00;
|
||||
bool seekingPos = false;
|
||||
bool seekingTiltPos = false;
|
||||
bool seekingMyPos = false;
|
||||
bool seekingFixedPos = false;
|
||||
bool settingMyPos = false;
|
||||
uint32_t awaitMy = 0;
|
||||
public:
|
||||
|
|
@ -153,7 +189,7 @@ typedef struct transceiver_config_t {
|
|||
bool radioInit = false;
|
||||
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 rxBandwidth = 812.5; // Receive bandwidth in kHz. Value from 58.03 to 812.50. Default is 99.97kHz.
|
||||
float rxBandwidth = 99.97; // 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.
|
||||
|
|
@ -234,7 +270,7 @@ class Transceiver {
|
|||
void sendFrame(byte *frame, uint8_t sync);
|
||||
void beginTransmit();
|
||||
void endTransmit();
|
||||
void emitFrame(somfy_frame_t *frame);
|
||||
void emitFrame(somfy_frame_t *frame, somfy_rx_t *rx = nullptr);
|
||||
};
|
||||
class SomfyShadeController {
|
||||
protected:
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
3
Web.cpp
3
Web.cpp
|
|
@ -348,7 +348,7 @@ void Web::begin() {
|
|||
// 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");
|
||||
if(somfy.loadShadesFile("/shades.tmp")) somfy.commit();
|
||||
}
|
||||
});
|
||||
server.on("/index.js", []() {
|
||||
|
|
@ -471,6 +471,7 @@ void Web::begin() {
|
|||
JsonObject obj = doc.to<JsonObject>();
|
||||
obj["shadeId"] = shadeId;
|
||||
obj["remoteAddress"] = somfy.getNextRemoteAddress(shadeId);
|
||||
obj["bitLength"] = somfy.transceiver.config.type;
|
||||
serializeJson(doc, g_content);
|
||||
server.send(200, _encoding_json, g_content);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,10 +3,10 @@
|
|||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" href="main.css?v=1.4.1" type="text/css" />
|
||||
<link rel="stylesheet" href="icons.css?v=1.4.1" type="text/css" />
|
||||
<link rel="stylesheet" href="main.css?v=1.4.2" type="text/css" />
|
||||
<link rel="stylesheet" href="icons.css?v=1.4.2" type="text/css" />
|
||||
<link rel="icon" type="image/png" href="favicon.png" />
|
||||
<script type="text/javascript" src="index.js?v=1.4.1"></script>
|
||||
<script type="text/javascript" src="index.js?v=1.4.2"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="divContainer" class="container" style="user-select:none;position:relative;border-radius:27px;">
|
||||
|
|
@ -219,20 +219,28 @@
|
|||
<div>
|
||||
<div class="field-group" style="width:127px;display:inline-block;margin-top:-20px;float:left;">
|
||||
<div class="field-group">
|
||||
<select id="selShadeType" name="shadeType" style="width:100%;" onchange="somfy.onShadeTypeChanged(this);">
|
||||
<option value="0">Roller Shade</option>
|
||||
<option value="1">Blind</option>
|
||||
<option value="2">Drapery</option>
|
||||
</select>
|
||||
<label for="selShadeType">Type</label>
|
||||
<div class="field-group">
|
||||
<select id="selShadeBitLength" name="bitLength" style="width:100%;">
|
||||
<option value="56">56-BIT</option>
|
||||
<option value="80">80-BIT</option>
|
||||
</select>
|
||||
<label for="selShadeType">Bit Length</label>
|
||||
</div>
|
||||
<div class="field-group">
|
||||
<select id="selShadeType" name="shadeType" style="width:100%;" onchange="somfy.onShadeTypeChanged(this);">
|
||||
<option value="0">Roller Shade</option>
|
||||
<option value="1">Blind</option>
|
||||
<option value="2">Drapery</option>
|
||||
</select>
|
||||
<label for="selShadeType">Type</label>
|
||||
</div>
|
||||
<div class="field-group">
|
||||
<input id="fldShadeAddress" name="shadeAddress" type="number" length=5 placeholder="Address" style="width:100%;text-align:right;">
|
||||
<label for="fldShadeAddress">Remote Address</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field-group">
|
||||
<input id="fldShadeAddress" name="shadeAddress" type="number" length=5 placeholder="Address" style="width:100%;text-align:right;">
|
||||
<label for="fldShadeAddress">Remote Address</label>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div id="divSomfyButtons" style="float:right;margin-top:10px;position:relative">
|
||||
<div id="divSomfyButtons" style="float:right;margin-top:27px;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 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="font-size: 2em; padding: 10px;"><span>my</span></div>
|
||||
|
|
@ -418,7 +426,7 @@
|
|||
<div id="divFrameLog" class="frame-log" style="display:none;">
|
||||
<h1 style="text-align: center;padding:10px;"><span>Frame Logs</span><span class="button-outline" onclick="document.getElementById('divFrameLog').style.display = 'none';" 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-x" style=""></i></span></span></h1>
|
||||
<hr style="margin:0px;" />
|
||||
<div class="frame-header"><span>Key</span><span>Address</span><span>Command</span><span>Code</span><span>RSSI</span></div>
|
||||
<div class="frame-header"><span>Key</span><span>Address</span><span>Command</span><span>Code</span><span>RSSI</span><span>Bits</span></div>
|
||||
<div id="divFrames" class="frame-list"></div>
|
||||
<div class="button-container" style="text-align:center">
|
||||
<button type="button" class="btnCopyFrame" style="display:inline-block;width:44%;" onclick="somfy.framesToClipboard();">Copy</button>
|
||||
|
|
|
|||
|
|
@ -372,7 +372,7 @@ async function reopenSocket() {
|
|||
await initSockets();
|
||||
}
|
||||
class General {
|
||||
appVersion = 'v1.4.1';
|
||||
appVersion = 'v1.4.2';
|
||||
reloadApp = false;
|
||||
async init() {
|
||||
this.setAppVersion();
|
||||
|
|
@ -1300,7 +1300,7 @@ class Somfy {
|
|||
let frames = document.getElementById('divFrames');
|
||||
let row = document.createElement('div');
|
||||
row.classList.add('frame-row');
|
||||
let html = `<span>${frame.encKey}</span><span>${frame.address}</span><span>${frame.command}</span><span>${frame.rcode}</span><span>${frame.rssi}dBm</span><div class="frame-pulses">`;
|
||||
let html = `<span>${frame.encKey}</span><span>${frame.address}</span><span>${frame.command}</span><span>${frame.rcode}</span><span>${frame.rssi}dBm</span><span>${frame.bits}</span><div class="frame-pulses">`;
|
||||
for (let i = 0; i < frame.pulses.length; i++) {
|
||||
if (i !== 0) html += ',';
|
||||
html += `${frame.pulses[i]}`;
|
||||
|
|
@ -1398,6 +1398,7 @@ class Somfy {
|
|||
document.getElementsByName('shadeAddress')[0].value = shade.remoteAddress;
|
||||
document.getElementById('divLinkedRemoteList').innerHTML = '';
|
||||
document.getElementById('btnSetRollingCode').style.display = 'none';
|
||||
document.getElementById('selShadeBitLength').value = shade.bitLength || 56;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -1475,7 +1476,8 @@ class Somfy {
|
|||
upTime: parseInt(document.getElementsByName('shadeUpTime')[0].value, 10),
|
||||
downTime: parseInt(document.getElementsByName('shadeDownTime')[0].value, 10),
|
||||
shadeType: parseInt(document.getElementById('selShadeType').value, 10),
|
||||
tiltTime: parseInt(document.getElementById('fldTiltTime').value, 10)
|
||||
tiltTime: parseInt(document.getElementById('fldTiltTime').value, 10),
|
||||
bitLength: parseInt(document.getElementById('selShadeBitLength').value, 10) || 56
|
||||
};
|
||||
if (obj.shadeType == 1) {
|
||||
obj.hasTilt = document.getElementById('cbHasTilt').checked;
|
||||
|
|
|
|||
|
|
@ -726,6 +726,12 @@ div.frame-header > span {
|
|||
div.frame-header > span:nth-child(5) {
|
||||
width: 57px;
|
||||
}
|
||||
div.frame-row > span:nth-child(6),
|
||||
div.frame-header > span:nth-child(6) {
|
||||
width: 47px;
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
div.frame-list > div:nth-child(2n+1) {
|
||||
background: beige;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue