mirror of
https://github.com/rstrouse/ESPSomfy-RTS.git
synced 2025-12-12 18:42:10 +01:00
Added 80bit commands for Step, Stop, and Favorite #280
This commit is contained in:
parent
c4e1dbe44b
commit
e3e843387f
11 changed files with 280 additions and 188 deletions
37
Network.cpp
37
Network.cpp
|
|
@ -105,6 +105,7 @@ bool Network::changeAP(const uint8_t *bssid, const int32_t channel) {
|
|||
WiFi.begin(settings.WIFI.ssid, settings.WIFI.passphrase, channel, bssid);
|
||||
uint8_t retries = 0;
|
||||
while(retries < 100) {
|
||||
esp_task_wdt_reset(); // Make sure we do not reboot here.
|
||||
wl_status_t stat = WiFi.status();
|
||||
if(stat == WL_CONNECTED) {
|
||||
Serial.println("WiFi module connected");
|
||||
|
|
@ -436,6 +437,7 @@ void Network::updateHostname() {
|
|||
bool Network::connectWiFi() {
|
||||
if(settings.WIFI.ssid[0] != '\0') {
|
||||
if(WiFi.status() == WL_CONNECTED && WiFi.SSID().compareTo(settings.WIFI.ssid) == 0) {
|
||||
// If we are connected to the target SSID then just return.
|
||||
this->disconnected = 0;
|
||||
return true;
|
||||
}
|
||||
|
|
@ -536,41 +538,6 @@ bool Network::getStrongestAP(const char *ssid, uint8_t *bssid, int32_t *channel)
|
|||
WiFi.scanDelete();
|
||||
return chan > 0;
|
||||
}
|
||||
/*
|
||||
int Network::getStrengthBySSID(const char *ssid) {
|
||||
int32_t strength = -100;
|
||||
esp_task_wdt_delete(NULL);
|
||||
int n = WiFi.scanNetworks(false, false, false, 300, 0, ssid);
|
||||
esp_task_wdt_add(NULL);
|
||||
for(int i = 0; i < n; i++) {
|
||||
if(WiFi.SSID(i).compareTo(ssid) == 0) strength = max(WiFi.RSSI(i), strength);
|
||||
}
|
||||
if(strength == -100) {
|
||||
Serial.print("Could not find network [");
|
||||
Serial.print(ssid);
|
||||
Serial.print("] Scanned ");
|
||||
Serial.print(n);
|
||||
Serial.println(" Networks...");
|
||||
String network;
|
||||
for(int i = 0; i < n; i++) {
|
||||
//WiFi.getNetworkInfo(i, network, encType, RSSI, BSSID, channel, isHidden);
|
||||
if(network.compareTo(this->ssid) == 0) Serial.print("*");
|
||||
else Serial.print(" ");
|
||||
Serial.print(i);
|
||||
Serial.print(": ");
|
||||
Serial.print(WiFi.SSID(i).c_str());
|
||||
Serial.print(" (");
|
||||
Serial.print(WiFi.RSSI(i));
|
||||
Serial.print("dBm) CH:");
|
||||
Serial.print(WiFi.channel(i));
|
||||
Serial.print(" MAC:");
|
||||
Serial.print(WiFi.BSSIDstr(i).c_str());
|
||||
Serial.println();
|
||||
}
|
||||
}
|
||||
return strength;
|
||||
}
|
||||
*/
|
||||
bool Network::openSoftAP() {
|
||||
Serial.println();
|
||||
Serial.println("Turning the HotSpot On");
|
||||
|
|
|
|||
235
Somfy.cpp
235
Somfy.cpp
|
|
@ -58,8 +58,9 @@ somfy_commands translateSomfyCommand(const String& string) {
|
|||
else if (string.equalsIgnoreCase("Flag")) return somfy_commands::Flag;
|
||||
else if (string.equalsIgnoreCase("Sensor")) return somfy_commands::Sensor;
|
||||
else if (string.equalsIgnoreCase("Toggle")) return somfy_commands::Toggle;
|
||||
else if (string.equalsIgnoreCase("Favorite")) return somfy_commands::Fav;
|
||||
else if (string.startsWith("fav") || string.startsWith("FAV")) return somfy_commands::Fav;
|
||||
else if (string.equalsIgnoreCase("Favorite")) return somfy_commands::Favorite;
|
||||
else if (string.equalsIgnoreCase("Stop")) return somfy_commands::Stop;
|
||||
else if (string.startsWith("fav") || string.startsWith("FAV")) return somfy_commands::Favorite;
|
||||
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;
|
||||
|
|
@ -107,12 +108,23 @@ String translateSomfyCommand(const somfy_commands cmd) {
|
|||
return "Sensor";
|
||||
case somfy_commands::Toggle:
|
||||
return "Toggle";
|
||||
case somfy_commands::Fav:
|
||||
case somfy_commands::Favorite:
|
||||
return "Favorite";
|
||||
case somfy_commands::Stop:
|
||||
return "Stop";
|
||||
default:
|
||||
return "Unknown(" + String((uint8_t)cmd) + ")";
|
||||
}
|
||||
}
|
||||
byte somfy_frame_t::calc80Checksum(byte b0, byte b1, byte b2) {
|
||||
byte cs80 = 0;
|
||||
cs80 = (((b0 & 0xF0) >> 4) ^ ((b1 & 0xF0) >> 4));
|
||||
cs80 ^= ((b2 & 0xF0) >> 4);
|
||||
cs80 ^= (b0 & 0x0F);
|
||||
cs80 ^= (b1 & 0x0F);
|
||||
return cs80;
|
||||
}
|
||||
|
||||
void somfy_frame_t::decodeFrame(byte* frame) {
|
||||
byte decoded[10];
|
||||
decoded[0] = frame[0];
|
||||
|
|
@ -153,16 +165,22 @@ void somfy_frame_t::decodeFrame(byte* frame) {
|
|||
// We reuse this memory address so we must reset the processed
|
||||
// flag. This will ensure we can see frames on the first beat.
|
||||
this->processed = false;
|
||||
// Pull in the data for an 80-bit step command.
|
||||
if(this->cmd == somfy_commands::StepDown) 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 > 0 && this->remoteAddress < 16777215;
|
||||
if (this->cmd != somfy_commands::Sensor && this->valid) this->valid = (this->rollingCode > 0);
|
||||
|
||||
// Next lets process some of the RTS extensions for 80-bit frames
|
||||
if(this->valid && this->proto == radio_proto::RTS && this->bitLength == 80) {
|
||||
// Do a parity checksum on the 80 bit data.
|
||||
if((decoded[9] & 0x0F) != this->calc80Checksum(decoded[7], decoded[8], decoded[9])) this->valid = false;
|
||||
if(this->valid) {
|
||||
// Translate extensions for stop and favorite.
|
||||
if(this->cmd == somfy_commands::My) this->cmd = (somfy_commands)((decoded[1] >> 4) | ((decoded[8] & 0x0F) << 4));
|
||||
// Bit packing to get the step size prohibits translation on the byte.
|
||||
else if(this->cmd == somfy_commands::StepDown) this->cmd = (somfy_commands)((decoded[1] >> 4) | ((decoded[8] & 0x08) << 4));
|
||||
}
|
||||
}
|
||||
if (this->valid) {
|
||||
uint8_t csc80 = 0;
|
||||
uint8_t cs80 = 0;
|
||||
|
||||
// Check for valid command.
|
||||
switch (this->cmd) {
|
||||
|
|
@ -185,25 +203,12 @@ void somfy_frame_t::decodeFrame(byte* frame) {
|
|||
break;
|
||||
case somfy_commands::StepUp:
|
||||
case somfy_commands::StepDown:
|
||||
// Decode the step size.
|
||||
this->stepSize = ((decoded[8] & 0x07) << 4) | ((decoded[9] & 0xF0) >> 4);
|
||||
break;
|
||||
case somfy_commands::Toggle:
|
||||
case somfy_commands::Fav:
|
||||
// These must be 80 bit commands
|
||||
csc80 = (decoded[9] & 0x0F);
|
||||
cs80 = (((decoded[7] & 0xF0) >> 4) ^ ((decoded[8] & 0xF0) >> 4));
|
||||
cs80 ^= ((decoded[9] & 0xF0) >> 4);
|
||||
cs80 ^= (decoded[7] & 0x0F);
|
||||
cs80 ^= (decoded[8] & 0x0F);
|
||||
//cs80 = (((decoded[7] & 0xF0) >> 4) ^ ((decoded[8] & 0xF0) >> 4) ^ ((decoded[9] & 0xF0) >> 4) ^ (decoded[7] & 0x0F) ^ (decoded[8] & 0x0F));
|
||||
if(csc80 != cs80) this->valid = false;
|
||||
/*
|
||||
uint8_t ai = ((decoded[7] & 0xF0) >> 4);
|
||||
uint8_t aj = ((decoded[8] & 0xF0) >> 4);
|
||||
uint8_t ak = ((decoded[9] & 0xF0) >> 4);
|
||||
uint8_t al = (decoded[7] & 0x0F);
|
||||
uint8_t am = (decoded[8] & 0x0F);
|
||||
uint8_t an = (decoded[9] & 0x0F);
|
||||
cs80 = ai ^ aj ^ ak ^ al ^ am;
|
||||
*/
|
||||
case somfy_commands::Favorite:
|
||||
case somfy_commands::Stop:
|
||||
break;
|
||||
default:
|
||||
this->valid = false;
|
||||
|
|
@ -211,23 +216,7 @@ void somfy_frame_t::decodeFrame(byte* frame) {
|
|||
}
|
||||
}
|
||||
if(this->valid && this->encKey == 0) this->valid = false;
|
||||
if (this->valid) {
|
||||
/*
|
||||
Serial.print("KEY:");
|
||||
Serial.print(this->encKey);
|
||||
Serial.print(" ADDR:");
|
||||
Serial.print(this->remoteAddress);
|
||||
Serial.print(" CMD:");
|
||||
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);
|
||||
*/
|
||||
}
|
||||
else {
|
||||
if (!this->valid) {
|
||||
Serial.print("INVALID FRAME ");
|
||||
Serial.print("KEY:");
|
||||
Serial.print(this->encKey);
|
||||
|
|
@ -268,6 +257,60 @@ void somfy_frame_t::decodeFrame(somfy_rx_t *rx) {
|
|||
this->rssi = ELECHOUSE_cc1101.getRssi();
|
||||
this->decodeFrame(rx->payload);
|
||||
}
|
||||
void somfy_frame_t::encode80BitFrame(byte *frame, uint8_t repeat) {
|
||||
switch(this->cmd) {
|
||||
// Step up and down commands encode the step size into the last 3 bytes.
|
||||
case somfy_commands::StepUp:
|
||||
if(repeat == 0) frame[1] = (static_cast<byte>(somfy_commands::StepDown) << 4) | (frame[1] & 0x0F);
|
||||
if(this->stepSize == 0) this->stepSize = 1;
|
||||
frame[7] = 132; // For simplicity this appears to be constant.
|
||||
frame[8] = ((this->stepSize & 0x70) >> 4) | 0x38;
|
||||
frame[9] = ((this->stepSize & 0x0F) << 4);
|
||||
frame[9] |= this->calc80Checksum(frame[7], frame[8], frame[9]);
|
||||
break;
|
||||
case somfy_commands::StepDown:
|
||||
if(repeat == 0) frame[1] = (static_cast<byte>(somfy_commands::StepDown) << 4) | (frame[1] & 0x0F);
|
||||
if(this->stepSize == 0) this->stepSize = 1;
|
||||
frame[7] = 132; // For simplicity this appears to be constant.
|
||||
frame[8] = ((this->stepSize & 0x70) >> 4) | 0x30;
|
||||
frame[9] = ((this->stepSize & 0x0F) << 4);
|
||||
frame[9] |= this->calc80Checksum(frame[7], frame[8], frame[9]);
|
||||
break;
|
||||
case somfy_commands::Favorite:
|
||||
if(repeat == 0) frame[1] = (static_cast<byte>(somfy_commands::My) << 4) | (frame[1] & 0x0F);
|
||||
frame[7] = repeat > 0 ? 132 : 196;
|
||||
frame[8] = 44;
|
||||
frame[9] = 0x90;
|
||||
frame[9] |= this->calc80Checksum(frame[7], frame[8], frame[9]);
|
||||
break;
|
||||
case somfy_commands::Stop:
|
||||
if(repeat == 0) frame[1] = (static_cast<byte>(somfy_commands::My) << 4) | (frame[1] & 0x0F);
|
||||
frame[7] = repeat > 0 ? 132 : 196;
|
||||
frame[8] = 47;
|
||||
frame[9] = 0xF0;
|
||||
frame[9] |= this->calc80Checksum(frame[7], frame[8], frame[9]);
|
||||
break;
|
||||
case somfy_commands::Toggle:
|
||||
if(repeat == 0) {
|
||||
frame[0] = 164;
|
||||
frame[1] |= 0xF0;
|
||||
}
|
||||
frame[7] = 196;
|
||||
frame[8] = 0;
|
||||
frame[9] = 0x10;
|
||||
frame[9] |= this->calc80Checksum(frame[7], frame[8], frame[9]);
|
||||
break;
|
||||
case somfy_commands::Prog:
|
||||
if(repeat > 0) frame[7] = 196 + (repeat * 4);
|
||||
else frame[7] = 132;
|
||||
frame[8] = 0;
|
||||
frame[9] = ((repeat + 1) & 0x0F) << 4;
|
||||
frame[9] |= this->calc80Checksum(frame[7], frame[8], frame[9]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
void somfy_frame_t::encodeFrame(byte *frame) {
|
||||
const byte btn = static_cast<byte>(cmd);
|
||||
frame[0] = this->encKey; // Encryption key. Doesn't matter much
|
||||
|
|
@ -357,34 +400,7 @@ void somfy_frame_t::encodeFrame(byte *frame) {
|
|||
|
||||
}
|
||||
else {
|
||||
switch(this->cmd) {
|
||||
case somfy_commands::StepUp:
|
||||
frame[7] = 132;
|
||||
frame[8] = 56;
|
||||
frame[9] = 22;
|
||||
break;
|
||||
case somfy_commands::StepDown:
|
||||
frame[7] = 132;
|
||||
frame[8] = 48;
|
||||
frame[9] = 30;
|
||||
break;
|
||||
case somfy_commands::Toggle:
|
||||
frame[0] = 164;
|
||||
// This also needs to be a command val of 15.
|
||||
frame[1] |= 0xF0;
|
||||
frame[7] = 196;
|
||||
frame[8] = 0;
|
||||
frame[9] = 25;
|
||||
break;
|
||||
case somfy_commands::Prog:
|
||||
//frame[0] = 0xA6;
|
||||
frame[7] = 196;
|
||||
frame[8] = 0;
|
||||
frame[9] = 25;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if(this->bitLength == 80) this->encode80BitFrame(&frame[0], 0);
|
||||
}
|
||||
byte checksum = 0;
|
||||
|
||||
|
|
@ -584,6 +600,7 @@ bool SomfyShadeController::begin() {
|
|||
}
|
||||
void SomfyShadeController::commit() {
|
||||
if(git.lockFS) return;
|
||||
esp_task_wdt_reset(); // Make sure we don't reset inadvertently.
|
||||
ShadeConfigFile file;
|
||||
file.begin();
|
||||
file.save(this);
|
||||
|
|
@ -593,6 +610,7 @@ void SomfyShadeController::commit() {
|
|||
}
|
||||
void SomfyShadeController::writeBackup() {
|
||||
if(git.lockFS) return;
|
||||
esp_task_wdt_reset(); // Make sure we don't reset inadvertently.
|
||||
ShadeConfigFile file;
|
||||
file.begin("/controller.backup", false);
|
||||
file.backup(this);
|
||||
|
|
@ -2382,7 +2400,7 @@ void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) {
|
|||
else {
|
||||
Serial.println("Moving to My target");
|
||||
this->lastFrame.processed = true;
|
||||
if(this->myTiltPos >= 0.0f && this->myTiltPos >= 100.0f) this->p_tiltTarget(this->myTiltPos);
|
||||
if(this->myTiltPos >= 0.0f && this->myTiltPos <= 100.0f) this->p_tiltTarget(this->myTiltPos);
|
||||
if(this->myPos >= 0.0f && this->myPos <= 100.0f && this->tiltType != tilt_types::tiltonly) this->p_target(this->myPos);
|
||||
this->emitCommand(cmd, internal ? "internal" : "remote", frame.remoteAddress);
|
||||
}
|
||||
|
|
@ -2405,6 +2423,7 @@ void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) {
|
|||
// the motor must tilt in the direction first then move
|
||||
// so we have to calculate the target with this in mind.
|
||||
if(this->stepSize == 0) return; // Avoid divide by 0.
|
||||
if(this->lastFrame.stepSize == 0) this->lastFrame.stepSize = 1;
|
||||
if(this->tiltType == tilt_types::integrated) {
|
||||
// With integrated tilt this is more involved than ne would think because the step command can be moving not just the tilt
|
||||
// but the lift. So a determination needs to be made as to whether we are currently moving and it should stop.
|
||||
|
|
@ -2417,22 +2436,22 @@ void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) {
|
|||
// Set the tilt position. This should stop the lift movement.
|
||||
this->p_target(this->currentPos);
|
||||
if(this->tiltTime == 0) return; // Avoid divide by 0.
|
||||
this->p_tiltTarget(max(0.0f, this->currentTiltPos - (100.0f/(static_cast<float>(this->tiltTime/static_cast<float>(this->stepSize))))));
|
||||
this->p_tiltTarget(max(0.0f, this->currentTiltPos - (100.0f/(static_cast<float>(this->tiltTime/static_cast<float>(this->stepSize * this->lastFrame.stepSize))))));
|
||||
}
|
||||
else {
|
||||
// We only have the lift to move.
|
||||
if(this->upTime == 0) return; // Avoid divide by 0.
|
||||
this->p_tiltTarget(this->currentTiltPos);
|
||||
this->p_target(max(0.0f, this->currentPos - (100.0f/(static_cast<float>(this->upTime/static_cast<float>(this->stepSize))))));
|
||||
this->p_target(max(0.0f, this->currentPos - (100.0f/(static_cast<float>(this->upTime/static_cast<float>(this->stepSize * this->lastFrame.stepSize))))));
|
||||
}
|
||||
}
|
||||
else if(this->tiltType == tilt_types::tiltonly) {
|
||||
if(this->tiltTime == 0 || this->stepSize == 0) return;
|
||||
this->p_tiltTarget(max(0.0f, this->currentTiltPos - (100.0f/(static_cast<float>(this->tiltTime/static_cast<float>(this->stepSize))))));
|
||||
this->p_tiltTarget(max(0.0f, this->currentTiltPos - (100.0f/(static_cast<float>(this->tiltTime/static_cast<float>(this->stepSize * this->lastFrame.stepSize))))));
|
||||
}
|
||||
else if(this->currentPos > 0.0f) {
|
||||
if(this->downTime == 0 || this->stepSize == 0) return;
|
||||
this->p_target(max(0.0f, this->currentPos - (100.0f/(static_cast<float>(this->upTime/static_cast<float>(this->stepSize))))));
|
||||
this->p_target(max(0.0f, this->currentPos - (100.0f/(static_cast<float>(this->upTime/static_cast<float>(this->stepSize * this->lastFrame.stepSize))))));
|
||||
}
|
||||
this->emitCommand(cmd, internal ? "internal" : "remote", frame.remoteAddress);
|
||||
break;
|
||||
|
|
@ -2445,6 +2464,8 @@ void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) {
|
|||
// the motor must tilt in the direction first then move
|
||||
// so we have to calculate the target with this in mind.
|
||||
if(this->stepSize == 0) return; // Avoid divide by 0.
|
||||
if(this->lastFrame.stepSize == 0) this->lastFrame.stepSize = 1;
|
||||
|
||||
if(this->tiltType == tilt_types::integrated) {
|
||||
// With integrated tilt this is more involved than ne would think because the step command can be moving not just the tilt
|
||||
// but the lift. So a determination needs to be made as to whether we are currently moving and it should stop.
|
||||
|
|
@ -2457,22 +2478,22 @@ void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) {
|
|||
// Set the tilt position. This should stop the lift movement.
|
||||
this->p_target(this->currentPos);
|
||||
if(this->tiltTime == 0) return; // Avoid divide by 0.
|
||||
this->p_tiltTarget(min(100.0f, this->currentTiltPos + (100.0f/(static_cast<float>(this->tiltTime/static_cast<float>(this->stepSize))))));
|
||||
this->p_tiltTarget(min(100.0f, this->currentTiltPos + (100.0f/(static_cast<float>(this->tiltTime/static_cast<float>(this->stepSize * this->lastFrame.stepSize))))));
|
||||
}
|
||||
else {
|
||||
// We only have the lift to move.
|
||||
this->p_tiltTarget(this->currentTiltPos);
|
||||
if(this->downTime == 0) return; // Avoid divide by 0.
|
||||
this->p_target(min(100.0f, this->currentPos + (100.0f/(static_cast<float>(this->downTime/static_cast<float>(this->stepSize))))));
|
||||
this->p_target(min(100.0f, this->currentPos + (100.0f/(static_cast<float>(this->downTime/static_cast<float>(this->stepSize* this->lastFrame.stepSize))))));
|
||||
}
|
||||
}
|
||||
else if(this->tiltType == tilt_types::tiltonly) {
|
||||
if(this->tiltTime == 0 || this->stepSize == 0) return;
|
||||
this->p_target(min(100.0f, this->currentTiltPos + (100.0f/(static_cast<float>(this->tiltTime/static_cast<float>(this->stepSize))))));
|
||||
this->p_target(min(100.0f, this->currentTiltPos + (100.0f/(static_cast<float>(this->tiltTime/static_cast<float>(this->stepSize * this->lastFrame.stepSize))))));
|
||||
}
|
||||
else if(this->currentPos < 100.0f) {
|
||||
if(this->downTime == 0 || this->stepSize == 0) return;
|
||||
this->p_target(min(100.0f, this->currentPos + (100.0f/(static_cast<float>(this->downTime/static_cast<float>(this->stepSize))))));
|
||||
this->p_target(min(100.0f, this->currentPos + (100.0f/(static_cast<float>(this->downTime/static_cast<float>(this->stepSize * this->lastFrame.stepSize))))));
|
||||
}
|
||||
this->emitCommand(cmd, internal ? "internal" : "remote", frame.remoteAddress);
|
||||
break;
|
||||
|
|
@ -2485,6 +2506,23 @@ void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) {
|
|||
else this->p_target(this->lastMovement == -1 ? 100 : 0);
|
||||
this->emitCommand(cmd, internal ? "internal" : "remote", frame.remoteAddress);
|
||||
break;
|
||||
case somfy_commands::Stop:
|
||||
if(this->lastFrame.processed) return;
|
||||
this->lastFrame.processed = true;
|
||||
this->p_target(this->currentPos);
|
||||
this->p_tiltTarget(this->currentTiltPos);
|
||||
break;
|
||||
case somfy_commands::Favorite:
|
||||
this->lastFrame.processed = true;
|
||||
if(this->simMy()) {
|
||||
this->moveToMyPosition();
|
||||
}
|
||||
else {
|
||||
if(this->myTiltPos >= 0.0f && this->myTiltPos <= 100.0f) this->p_tiltTarget(this->myTiltPos);
|
||||
if(this->myPos >= 0.0f && this->myPos <= 100.0f && this->tiltType != tilt_types::tiltonly) this->p_target(this->myPos);
|
||||
this->emitCommand(cmd, internal ? "internal" : "remote", frame.remoteAddress);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
dir = 0;
|
||||
break;
|
||||
|
|
@ -2820,7 +2858,7 @@ void SomfyShade::moveToMyPosition() {
|
|||
SomfyRemote::sendCommand(somfy_commands::My, this->repeats);
|
||||
}
|
||||
void SomfyShade::sendCommand(somfy_commands cmd) { this->sendCommand(cmd, this->repeats); }
|
||||
void SomfyShade::sendCommand(somfy_commands cmd, uint8_t repeat) {
|
||||
void SomfyShade::sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSize) {
|
||||
// This sendCommand function will always be called externally. sendCommand at the remote level
|
||||
// is expected to be called internally when the motor needs commanded.
|
||||
if(this->bitLength == 0) this->bitLength = somfy.transceiver.config.type;
|
||||
|
|
@ -2875,14 +2913,14 @@ void SomfyShade::sendCommand(somfy_commands cmd, uint8_t repeat) {
|
|||
}
|
||||
}
|
||||
else if(cmd == somfy_commands::Toggle) {
|
||||
if(this->bitLength != 80) SomfyRemote::sendCommand(somfy_commands::My, repeat);
|
||||
if(this->bitLength != 80) SomfyRemote::sendCommand(somfy_commands::My, repeat, stepSize);
|
||||
else SomfyRemote::sendCommand(somfy_commands::Toggle, repeat);
|
||||
}
|
||||
else if(this->shadeType == shade_types::garage1 && cmd == somfy_commands::Prog) {
|
||||
SomfyRemote::sendCommand(somfy_commands::Toggle, repeat);
|
||||
SomfyRemote::sendCommand(somfy_commands::Toggle, repeat, stepSize);
|
||||
}
|
||||
else {
|
||||
SomfyRemote::sendCommand(cmd, repeat);
|
||||
SomfyRemote::sendCommand(cmd, repeat, stepSize);
|
||||
}
|
||||
}
|
||||
void SomfyGroup::sendCommand(somfy_commands cmd) { this->sendCommand(cmd, this->repeats); }
|
||||
|
|
@ -3848,12 +3886,13 @@ void SomfyRemote::sendSensorCommand(int8_t isWindy, int8_t isSunny, uint8_t repe
|
|||
somfy.processFrame(this->lastFrame, true);
|
||||
}
|
||||
void SomfyRemote::sendCommand(somfy_commands cmd) { this->sendCommand(cmd, this->repeats); }
|
||||
void SomfyRemote::sendCommand(somfy_commands cmd, uint8_t repeat) {
|
||||
void SomfyRemote::sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSize) {
|
||||
this->lastFrame.rollingCode = this->getNextRollingCode();
|
||||
this->lastFrame.remoteAddress = this->getRemoteAddress();
|
||||
this->lastFrame.cmd = this->transformCommand(cmd);
|
||||
this->lastFrame.repeats = repeat;
|
||||
this->lastFrame.bitLength = this->bitLength;
|
||||
this->lastFrame.stepSize = stepSize;
|
||||
// Match the encKey to the rolling code. These keys range from 160 to 175.
|
||||
this->lastFrame.encKey = 0xA0 | static_cast<uint8_t>(this->lastFrame.rollingCode & 0x000F);
|
||||
this->lastFrame.proto = this->proto;
|
||||
|
|
@ -3924,24 +3963,10 @@ void SomfyShadeController::sendFrame(somfy_frame_t &frame, uint8_t repeat) {
|
|||
byte frm[10];
|
||||
frame.encodeFrame(frm);
|
||||
this->transceiver.sendFrame(frm, frame.bitLength == 56 ? 2 : 12, frame.bitLength);
|
||||
// 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;
|
||||
case somfy_commands::Toggle:
|
||||
frm[7] = 136;
|
||||
frm[9] = 34;
|
||||
break;
|
||||
default:
|
||||
frm[9] = 46;
|
||||
break;
|
||||
}
|
||||
for(uint8_t i = 0; i < repeat; i++) {
|
||||
// For each 80-bit frame we need to adjust the byte encoding for the
|
||||
// silence.
|
||||
if(frame.bitLength == 80) frame.encode80BitFrame(&frm[0], i + 1);
|
||||
this->transceiver.sendFrame(frm, frame.bitLength == 56 ? 7 : 6, frame.bitLength);
|
||||
esp_task_wdt_reset();
|
||||
}
|
||||
|
|
@ -4330,7 +4355,7 @@ void RECEIVE_ATTR Transceiver::handleReceive() {
|
|||
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. 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
|
||||
// with give 4 or 14 bits of hardware sync. An 80 bit packet gives 12, 13 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;
|
||||
|
|
@ -4339,8 +4364,10 @@ void RECEIVE_ATTR Transceiver::handleReceive() {
|
|||
// 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 == 13) somfy_rx.bit_length = 80; // The RS485 device sends this sync.
|
||||
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;
|
||||
else somfy_rx.bit_length = 56;
|
||||
somfy_rx.status = receiving_data;
|
||||
}
|
||||
else {
|
||||
|
|
@ -4535,6 +4562,8 @@ void Transceiver::emitFrame(somfy_frame_t *frame, somfy_rx_t *rx) {
|
|||
json->addElem("proto", static_cast<uint8_t>(frame->proto));
|
||||
json->addElem("valid", frame->valid);
|
||||
json->addElem("sync", frame->hwsync);
|
||||
if(frame->cmd == somfy_commands::StepUp || frame->cmd == somfy_commands::StepDown)
|
||||
json->addElem("stepSize", frame->stepSize);
|
||||
json->beginArray("pulses");
|
||||
if(rx) {
|
||||
for(uint16_t i = 0; i < rx->pulseCount; i++) {
|
||||
|
|
|
|||
9
Somfy.h
9
Somfy.h
|
|
@ -47,7 +47,8 @@ enum class somfy_commands : byte {
|
|||
RTWProto = 0xF, // RTW Protocol
|
||||
// Command extensions for 80 bit frames
|
||||
StepUp = 0x8B,
|
||||
Fav = 0x90,
|
||||
Favorite = 0xC1,
|
||||
Stop = 0xF1
|
||||
};
|
||||
enum class group_types : byte {
|
||||
channel = 0x00
|
||||
|
|
@ -185,6 +186,8 @@ struct somfy_frame_t {
|
|||
uint16_t pulseCount = 0;
|
||||
uint8_t stepSize = 0;
|
||||
void print();
|
||||
void encode80BitFrame(byte *frame, uint8_t repeat);
|
||||
byte calc80Checksum(byte b0, byte b1, byte b2);
|
||||
void encodeFrame(byte *frame);
|
||||
void decodeFrame(byte* frame);
|
||||
void decodeFrame(somfy_rx_t *rx);
|
||||
|
|
@ -242,7 +245,7 @@ class SomfyRemote {
|
|||
void setLight(bool bHasLight);
|
||||
void setSimMy(bool bSimMy);
|
||||
virtual void sendCommand(somfy_commands cmd);
|
||||
virtual void sendCommand(somfy_commands cmd, uint8_t repeat);
|
||||
virtual void sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSize = 0);
|
||||
void sendSensorCommand(int8_t isWindy, int8_t isSunny, uint8_t repeat);
|
||||
void repeatFrame(uint8_t repeat);
|
||||
virtual uint16_t p_lastRollingCode(uint16_t code);
|
||||
|
|
@ -320,7 +323,7 @@ class SomfyShade : public SomfyRemote {
|
|||
void moveToTiltTarget(float target);
|
||||
void sendTiltCommand(somfy_commands cmd);
|
||||
void sendCommand(somfy_commands cmd);
|
||||
void sendCommand(somfy_commands cmd, uint8_t repeat);
|
||||
void sendCommand(somfy_commands cmd, uint8_t repeat, uint8_t stepSize = 0);
|
||||
bool linkRemote(uint32_t remoteAddress, uint16_t rollingCode = 0);
|
||||
bool unlinkRemote(uint32_t remoteAddress);
|
||||
void emitState(const char *evt = "shadeState");
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
15
Web.cpp
15
Web.cpp
|
|
@ -343,6 +343,7 @@ void Web::handleShadeCommand(WebServer& server) {
|
|||
HTTPMethod method = server.method();
|
||||
uint8_t shadeId = 255;
|
||||
uint8_t target = 255;
|
||||
uint8_t stepSize = 0;
|
||||
int8_t repeat = -1;
|
||||
somfy_commands command = somfy_commands::My;
|
||||
if (method == HTTP_GET || method == HTTP_PUT || method == HTTP_POST) {
|
||||
|
|
@ -351,10 +352,11 @@ void Web::handleShadeCommand(WebServer& server) {
|
|||
if (server.hasArg("command")) command = translateSomfyCommand(server.arg("command"));
|
||||
else if (server.hasArg("target")) target = atoi(server.arg("target").c_str());
|
||||
if (server.hasArg("repeat")) repeat = atoi(server.arg("repeat").c_str());
|
||||
if(server.hasArg("stepSize")) stepSize = atoi(server.arg("stepSize").c_str());
|
||||
}
|
||||
else if (server.hasArg("plain")) {
|
||||
Serial.println("Sending Shade Command");
|
||||
DynamicJsonDocument doc(256);
|
||||
DynamicJsonDocument doc(512);
|
||||
DeserializationError err = deserializeJson(doc, server.arg("plain"));
|
||||
if (err) {
|
||||
this->handleDeserializationError(server, err);
|
||||
|
|
@ -372,6 +374,7 @@ void Web::handleShadeCommand(WebServer& server) {
|
|||
target = obj["target"].as<uint8_t>();
|
||||
}
|
||||
if (obj.containsKey("repeat")) repeat = obj["repeat"].as<uint8_t>();
|
||||
if(obj.containsKey("stepSize")) stepSize = obj["stepSize"].as<uint8_t>();
|
||||
}
|
||||
}
|
||||
else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No shade object supplied.\"}"));
|
||||
|
|
@ -382,15 +385,13 @@ void Web::handleShadeCommand(WebServer& server) {
|
|||
// Send the command to the shade.
|
||||
if (target <= 100)
|
||||
shade->moveToTarget(shade->transformPosition(target));
|
||||
else if (repeat > 0)
|
||||
shade->sendCommand(command, repeat);
|
||||
else
|
||||
shade->sendCommand(command);
|
||||
shade->sendCommand(command, repeat > 0 ? repeat : shade->repeats, stepSize);
|
||||
JsonResponse resp;
|
||||
resp.beginResponse(&server, g_content, sizeof(g_content));
|
||||
resp.beginArray();
|
||||
resp.beginObject();
|
||||
shade->toJSONRef(resp);
|
||||
resp.endArray();
|
||||
resp.endObject();
|
||||
resp.endResponse();
|
||||
}
|
||||
else {
|
||||
|
|
@ -414,7 +415,7 @@ void Web::handleRepeatCommand(WebServer& server) {
|
|||
if(server.hasArg("command")) command = translateSomfyCommand(server.arg("command"));
|
||||
if(server.hasArg("repeat")) repeat = atoi(server.arg("repeat").c_str());
|
||||
if(shadeId == 255 && groupId == 255 && server.hasArg("plain")) {
|
||||
DynamicJsonDocument doc(256);
|
||||
DynamicJsonDocument doc(512);
|
||||
DeserializationError err = deserializeJson(doc, server.arg("plain"));
|
||||
if (err) {
|
||||
this->handleDeserializationError(server, err);
|
||||
|
|
|
|||
|
|
@ -1494,4 +1494,49 @@ i.icss-bars {
|
|||
i.icss-bars:after {
|
||||
top: 0.36em;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
i.icss-hand {
|
||||
width: .6em;
|
||||
height: .5em;
|
||||
border-radius: .35em .3em .5em .5em;
|
||||
margin: .5em .1em 0 .21em;
|
||||
}
|
||||
|
||||
i.icss-hand:before {
|
||||
width: .1em;
|
||||
height: .55em;
|
||||
background: currentColor;
|
||||
left: .5em;
|
||||
bottom: .3em;
|
||||
border-radius: 80% / 20%;
|
||||
box-shadow: -.13em -.1em 0, -.265em -.15em 0, -.4em -.11em 0;
|
||||
}
|
||||
|
||||
i.icss-hand:after {
|
||||
width: .12em;
|
||||
height: .43em;
|
||||
background: currentColor;
|
||||
bottom: .25em;
|
||||
left: -.06em;
|
||||
border-radius: .04em;
|
||||
transform: rotate(-16deg);
|
||||
border-radius: .04em 70% .04em .04em / .04em 70% .04em .04em;
|
||||
}
|
||||
i.icss-bookmark {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
background-color: transparent;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
i.icss-bookmark:before {
|
||||
width: .5em;
|
||||
height: .8em;
|
||||
transform: translate(-50%, -50%);
|
||||
border: .25em solid currentColor;
|
||||
border-color: currentColor currentColor transparent currentColor;
|
||||
border-radius: .03em;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
}
|
||||
|
|
@ -8,9 +8,9 @@
|
|||
<meta name="apple-mobile-web-app-title" content="ESPSomfy RTS App">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||
|
||||
<link rel="stylesheet" href="main.css?v=2.4.3b" type="text/css" />
|
||||
<link rel="stylesheet" href="widgets.css?v=2.4.3b" type="text/css" />
|
||||
<link rel="stylesheet" href="icons.css?v=2.4.3b" type="text/css" />
|
||||
<link rel="stylesheet" href="main.css?v=2.4.3c" type="text/css" />
|
||||
<link rel="stylesheet" href="widgets.css?v=2.4.3c" type="text/css" />
|
||||
<link rel="stylesheet" href="icons.css?v=2.4.3c" type="text/css" />
|
||||
<link rel="icon" type="image/png" href="favicon.png" />
|
||||
|
||||
<!-- iPad retina icon -->
|
||||
|
|
@ -114,7 +114,7 @@
|
|||
rel="apple-touch-startup-image">
|
||||
|
||||
|
||||
<script type="text/javascript" src="index.js?v=2.4.2r"></script>
|
||||
<script type="text/javascript" src="index.js?v=2.4.3c"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="divContainer" class="container main" data-auth="false">
|
||||
|
|
@ -775,11 +775,11 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="divVirtualRemote" style="display:none;" class="subtab-content">
|
||||
<div id="divVirtualRemote" style="display:none;" class="subtab-content" data-bitlength="56">
|
||||
<div class="field-group" style="margin-top:-18px;display:inline-block;width:100%;">
|
||||
<select id="selVRMotor" style="width:100%;">
|
||||
<select id="selVRMotor" style="width:100%;" onchange="document.getElementById('divVirtualRemote').setAttribute('data-bitlength', this.options[this.selectedIndex].getAttribute('data-bitlength'));">
|
||||
</select>
|
||||
<label for="selVRMotor">Select a Motor</label>
|
||||
<label for="selVRMotor">Select a Motor or Group</label>
|
||||
</div>
|
||||
<div class="vr-button vr-updownmy">
|
||||
<span>Remote Buttons</span>
|
||||
|
|
@ -789,12 +789,38 @@
|
|||
<div class="button-outline" data-cmd="down" onmousedown="somfy.sendVRCommand(this);" ontouchstart="somfy.sendVRCommand(this);"><i class="icss-somfy-down" style="margin-top:-4px;"></i></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="vr-button vr-stop vr-80bit">
|
||||
<span>Stop</span>
|
||||
<div>
|
||||
<div class="button-outline toggle-button" style="width:127px;text-align:center;border-radius:33%;font-size:2em;padding:10px;" data-cmd="stop" onmousedown="somfy.sendVRCommand(this);" ontouchstart="somfy.sendVRCommand(this);"><i class="icss-hand" style=""></i></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="vr-button vr-updownmy">
|
||||
<span>Toggle Button</span>
|
||||
<div>
|
||||
<div class="button-outline toggle-button" style="width:127px;text-align:center;border-radius:33%;font-size:2em;padding:10px;" data-cmd="toggle" onmousedown="somfy.sendVRCommand(this);" ontouchstart="somfy.sendVRCommand(this);"><i class="icss-somfy-toggle" style="margin-top:-4px;"></i></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="vr-button vr-updown vr-80bit">
|
||||
<span>Step</span>
|
||||
<div style="margin-right:7px;width:470px;font-size:.8em;">
|
||||
<input id="vrslidStepSize" name="stepSize" type="range" min="1" max="127" step="1" style="width:100%;" data-bind="stepSize" value="1" oninput="document.getElementById('vrspanStepSize').innerText = this.value;" />
|
||||
<div>
|
||||
<label style="color:#00bcd4;">Step Size:</label><span id="vrspanStepSize">1</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="button-outline" style="width:59px;text-align:center;border-radius:33%;font-size:2em;padding:10px;" data-cmd="StepUp" onmousedown="somfy.sendVRCommand(this);" ontouchstart="somfy.sendVRCommand(this);"><span></span><i class="icss-somfy-up" style="margin-top:4px;"></i></div>
|
||||
<div class="button-outline" style="width:59px;text-align:center;border-radius:33%;font-size:2em;padding:10px;" data-cmd="StepDown" onmousedown="somfy.sendVRCommand(this);" ontouchstart="somfy.sendVRCommand(this);"><span></span><i class="icss-somfy-down" style="margin-top:-4px;"></i></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="vr-button vr-favorite vr-80bit">
|
||||
<span>Favorite</span>
|
||||
<div>
|
||||
<div class="button-outline toggle-button" style="width:127px;text-align:center;border-radius:33%;font-size:2em;padding:10px;" data-cmd="favorite" onmousedown="somfy.sendVRCommand(this);" ontouchstart="somfy.sendVRCommand(this);"><i class="icss-bookmark" style=""></i></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="vr-button vr-updown">
|
||||
<span>Up + Down</span>
|
||||
<div>
|
||||
|
|
@ -819,18 +845,6 @@
|
|||
<div class="button-outline" style="width:127px;text-align:center;border-radius:33%;font-size:2em;padding:10px;" data-cmd="MyDown" onmousedown="somfy.sendVRCommand(this);" ontouchstart="somfy.sendVRCommand(this);"><span>my</span><span> + </span><i class="icss-somfy-down"></i></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="vr-button vr-updown">
|
||||
<span>Step Up</span>
|
||||
<div>
|
||||
<div class="button-outline" style="width:127px;text-align:center;border-radius:33%;font-size:2em;padding:10px;" data-cmd="StepUp" onmousedown="somfy.sendVRCommand(this);" ontouchstart="somfy.sendVRCommand(this);"><span>step </span><i class="icss-somfy-up"></i></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="vr-button vr-updown">
|
||||
<span>Step Down</span>
|
||||
<div>
|
||||
<div class="button-outline" style="width:127px;text-align:center;border-radius:33%;font-size:2em;padding:10px;" data-cmd="StepDown" onmousedown="somfy.sendVRCommand(this);" ontouchstart="somfy.sendVRCommand(this);"><span>step </span><i class="icss-somfy-down"></i></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="vr-button vr-updown">
|
||||
<span>Prog</span>
|
||||
<div>
|
||||
|
|
@ -838,15 +852,10 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="vr-button vr-updown">
|
||||
<span>Sun Flag On</span>
|
||||
<span>Sun Flag</span>
|
||||
<div>
|
||||
<div class="button-outline" style="width:127px;text-align:center;border-radius:33%;font-size:2em;padding:10px;" data-cmd="SunFlag" onmousedown="somfy.sendVRCommand(this);" ontouchstart="somfy.sendVRCommand(this);"><div class="button-sunflag" data-on="true" style="margin:0px;"><i class="icss-sun-c" style="background:white;"></i><i class="icss-sun-o" style="left:0px;color:white;"></i></div><span>on</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="vr-button vr-updown">
|
||||
<span>Sun Flag Off</span>
|
||||
<div>
|
||||
<div class="button-outline" style="width:127px;text-align:center;border-radius:33%;font-size:2em;padding:10px;" data-cmd="Flag" onmousedown="somfy.sendVRCommand(this);" ontouchstart="somfy.sendVRCommand(this);"><div class="button-sunflag" data-on="false" style="margin:0px;"><i class="icss-sun-c" style="background:transparent;"></i><i class="icss-sun-o" style="left:0px;color:white;"></i></div><span>off</span></div>
|
||||
<div class="button-outline" style="width: 59px; text-align: center; border-radius: 33%; font-size: 2em; padding: 10px; height: 52px;" data-cmd="SunFlag" onmousedown="somfy.sendVRCommand(this);" ontouchstart="somfy.sendVRCommand(this);"><div class="button-sunflag" data-on="true" style="margin:0px;margin-left:4px;"><i class="icss-sun-c" style="background:white;"></i><i class="icss-sun-o" style="left:0px;color:white;"></i></div></div>
|
||||
<div class="button-outline" style="width: 59px; text-align: center; border-radius: 33%; font-size: 2em; padding: 10px; height: 52px;" data-cmd="Flag" onmousedown="somfy.sendVRCommand(this);" ontouchstart="somfy.sendVRCommand(this);"><div class="button-sunflag" data-on="false" style="margin:0px;margin-left:4px;"><i class="icss-sun-c" style="background:transparent;"></i><i class="icss-sun-o" style="left:0px;color:white;"></i></div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="vr-button">
|
||||
|
|
|
|||
|
|
@ -2301,8 +2301,11 @@ class Somfy {
|
|||
opt.setAttribute('data-type', 'shade');
|
||||
opt.setAttribute('data-shadetype', shade.shadeType);
|
||||
opt.setAttribute('data-shadeid', shade.shadeId);
|
||||
opt.setAttribute('data-bitlength', shade.bitLength);
|
||||
optGroup.appendChild(opt);
|
||||
}
|
||||
let sopt = vrList.options[vrList.selectedIndex];
|
||||
document.getElementById('divVirtualRemote').setAttribute('data-bitlength', sopt ? sopt.getAttribute('data-bitlength') : 'none');
|
||||
document.getElementById('divShadeList').innerHTML = divCfg;
|
||||
let shadeControls = document.getElementById('divShadeControls');
|
||||
shadeControls.innerHTML = divCtl;
|
||||
|
|
@ -2611,9 +2614,13 @@ class Somfy {
|
|||
opt.setAttribute('data-address', group.remoteAddress);
|
||||
opt.setAttribute('data-type', 'group');
|
||||
opt.setAttribute('data-groupid', group.groupId);
|
||||
opt.setAttribute('data-bitlength', group.bitLength);
|
||||
optGroup.appendChild(opt);
|
||||
}
|
||||
}
|
||||
let sopt = vrList.options[vrList.selectedIndex];
|
||||
document.getElementById('divVirtualRemote').setAttribute('data-bitlength', sopt ? sopt.getAttribute('data-bitlength') : 'none');
|
||||
|
||||
document.getElementById('divGroupList').innerHTML = divCfg;
|
||||
let groupControls = document.getElementById('divGroupControls');
|
||||
groupControls.innerHTML = divCtl;
|
||||
|
|
@ -2891,7 +2898,7 @@ class Somfy {
|
|||
proto = '-V';
|
||||
break;
|
||||
}
|
||||
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}${proto}</span><span>${fnFmtTime(frame.time)}</span><div class="frame-pulses">`;
|
||||
let html = `<span>${frame.encKey}</span><span>${frame.address}</span><span>${frame.command}<sup>${frame.stepSize ? frame.stepSize : ''}</sup></span><span>${frame.rcode}</span><span>${frame.rssi}dBm</span><span>${frame.bits}${proto}</span><span>${fnFmtTime(frame.time)}</span><div class="frame-pulses">`;
|
||||
for (let i = 0; i < frame.pulses.length; i++) {
|
||||
if (i !== 0) html += ',';
|
||||
html += `${frame.pulses[i]}`;
|
||||
|
|
@ -3687,22 +3694,47 @@ class Somfy {
|
|||
return div;
|
||||
}
|
||||
sendCommand(shadeId, command, repeat, cb) {
|
||||
console.log(`Sending Shade command ${shadeId}-${command}`);
|
||||
let obj = { shadeId: shadeId };
|
||||
if (isNaN(parseInt(command, 10))) obj.command = command;
|
||||
else obj.target = parseInt(command, 10);
|
||||
if (typeof repeat === 'number') obj.repeat = parseInt(repeat);
|
||||
let obj = {};
|
||||
if (typeof shadeId.shadeId !== 'undefined') {
|
||||
obj = shadeId;
|
||||
cb = command;
|
||||
shadeId = obj.shadeId;
|
||||
repeat = obj.repeat;
|
||||
command = obj.command;
|
||||
}
|
||||
else {
|
||||
obj = { shadeId: shadeId };
|
||||
if (isNaN(parseInt(command, 10))) obj.command = command;
|
||||
else obj.target = parseInt(command, 10);
|
||||
if (typeof repeat === 'number') obj.repeat = parseInt(repeat);
|
||||
}
|
||||
putJSON('/shadeCommand', obj, (err, shade) => {
|
||||
if (typeof cb === 'function') cb(err, shade);
|
||||
});
|
||||
}
|
||||
sendCommandRepeat(shadeId, command, repeat, cb) {
|
||||
//console.log(`Sending Shade command ${shadeId}-${command}`);
|
||||
let obj = { shadeId: shadeId, command: command };
|
||||
if (typeof repeat === 'number') obj.repeat = parseInt(repeat);
|
||||
let obj = {};
|
||||
if (typeof shadeId.shadeId !== 'undefined') {
|
||||
obj = shadeId;
|
||||
cb = command;
|
||||
shadeId = obj.shadeId;
|
||||
repeat = obj.repeat;
|
||||
command = obj.command;
|
||||
}
|
||||
else {
|
||||
obj = { shadeId: shadeId, command: command };
|
||||
if (typeof repeat === 'number') obj.repeat = parseInt(repeat);
|
||||
}
|
||||
putJSON('/repeatCommand', obj, (err, shade) => {
|
||||
if (typeof cb === 'function') cb(err, shade);
|
||||
});
|
||||
|
||||
/*
|
||||
putJSON(`/repeatCommand?shadeId=${shadeId}&command=${command}`, null, (err, shade) => {
|
||||
if(typeof cb === 'function') cb(err, shade);
|
||||
});
|
||||
*/
|
||||
}
|
||||
sendGroupRepeat(groupId, command, repeat, cb) {
|
||||
let obj = { groupId: groupId, command: command };
|
||||
|
|
@ -3721,7 +3753,6 @@ class Somfy {
|
|||
cmd: el.getAttribute('data-cmd')
|
||||
};
|
||||
ui.fromElement(el.parentElement.parentElement, o);
|
||||
console.log(o);
|
||||
switch (o.type) {
|
||||
case 'shade':
|
||||
o.shadeId = parseInt(opt.getAttribute('data-shadeId'), 10);
|
||||
|
|
@ -3744,17 +3775,17 @@ class Somfy {
|
|||
else if (o.type === 'group')
|
||||
somfy.sendGroupRepeat(o.groupId, o.cmd, null, fnRepeatCommand);
|
||||
else
|
||||
somfy.sendCommandRepeat(o.shadeId, o.cmd, null, fnRepeatCommand);
|
||||
somfy.sendCommandRepeat(o, fnRepeatCommand);
|
||||
}
|
||||
}
|
||||
o.command = o.cmd;
|
||||
if (o.cmd === 'Sensor') {
|
||||
somfy.sendSetSensor(o);
|
||||
|
||||
}
|
||||
else if (o.type === 'group')
|
||||
somfy.sendGroupCommand(o.groupId, o.cmd, null, (err, group) => { fnRepeatCommand(err, group); });
|
||||
else
|
||||
somfy.sendCommand(o.shadeId, o.cmd, null, (err, shade) => { fnRepeatCommand(err, shade); });
|
||||
somfy.sendCommand(o, (err, shade) => { fnRepeatCommand(err, shade); });
|
||||
}
|
||||
sendSetSensor(obj, cb) {
|
||||
putJSON('/setSensor', obj, (err, device) => {
|
||||
|
|
|
|||
|
|
@ -702,6 +702,12 @@ div.wait-overlay > .lds-roller {
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
#divVirtualRemote[data-bitlength="56"] div.vr-button.vr-80bit {
|
||||
display:none;
|
||||
}
|
||||
#divVirtualRemote[data-bitlength="none"] div.vr-button {
|
||||
display:none;
|
||||
}
|
||||
.shade-positioner {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
|
|
@ -804,6 +810,7 @@ div.frame-header > span {
|
|||
}
|
||||
div.frame-row > span:nth-child(3),
|
||||
div.frame-header > span:nth-child(3) {
|
||||
white-space:nowrap;
|
||||
width: 80px;
|
||||
text-align:center;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue