mirror of
https://github.com/rstrouse/ESPSomfy-RTS.git
synced 2025-12-13 11:02:12 +01:00
Added sensor inputs from wind/sun sensors.
This commit is contained in:
parent
cd173525b1
commit
f850cc0a1e
6 changed files with 97 additions and 47 deletions
51
Somfy.cpp
51
Somfy.cpp
|
|
@ -50,12 +50,14 @@ somfy_commands translateSomfyCommand(const String& string) {
|
|||
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.equalsIgnoreCase("Sensor")) return somfy_commands::Sensor;
|
||||
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;
|
||||
else if (string.startsWith("su") || string.startsWith("SU")) return somfy_commands::StepUp;
|
||||
else if (string.startsWith("sd") || string.startsWith("SD")) return somfy_commands::StepDown;
|
||||
else if (string.startsWith("sen") || string.startsWith("SEN")) return somfy_commands::Sensor;
|
||||
else if (string.startsWith("p") || string.startsWith("P")) return somfy_commands::Prog;
|
||||
else if (string.startsWith("u") || string.startsWith("U")) return somfy_commands::Up;
|
||||
else if (string.startsWith("d") || string.startsWith("D")) return somfy_commands::Down;
|
||||
|
|
@ -91,6 +93,8 @@ String translateSomfyCommand(const somfy_commands cmd) {
|
|||
return "Step Up";
|
||||
case somfy_commands::StepDown:
|
||||
return "Step Down";
|
||||
case somfy_commands::Sensor:
|
||||
return "Sensor";
|
||||
default:
|
||||
return "Unknown(" + String((uint8_t)cmd) + ")";
|
||||
}
|
||||
|
|
@ -172,6 +176,7 @@ void somfy_frame_t::decodeFrame(byte* frame) {
|
|||
case somfy_commands::Prog:
|
||||
case somfy_commands::Flag:
|
||||
case somfy_commands::SunFlag:
|
||||
case somfy_commands::Sensor:
|
||||
break;
|
||||
case somfy_commands::UnknownC:
|
||||
case somfy_commands::UnknownD:
|
||||
|
|
@ -291,8 +296,6 @@ void somfy_frame_t::encodeFrame(byte *frame) {
|
|||
case somfy_commands::Flag:
|
||||
frame[0] = 142;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
@ -312,8 +315,6 @@ void somfy_frame_t::encodeFrame(byte *frame) {
|
|||
frame[8] = 0;
|
||||
frame[9] = 25;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
byte checksum = 0;
|
||||
|
|
@ -915,6 +916,9 @@ void SomfyShade::publish() {
|
|||
mqtt.publish(topic, static_cast<uint8_t>(this->shadeType));
|
||||
snprintf(topic, sizeof(topic), "shades/%u/tiltType", this->shadeId);
|
||||
mqtt.publish(topic, static_cast<uint8_t>(this->tiltType));
|
||||
snprintf(topic, sizeof(topic), "shades/%u/flags", this->shadeId);
|
||||
mqtt.publish(topic, this->flags);
|
||||
|
||||
if(this->tiltType != tilt_types::none) {
|
||||
snprintf(topic, sizeof(topic), "shades/%u/tiltDirection", this->shadeId);
|
||||
mqtt.publish(topic, this->tiltDirection);
|
||||
|
|
@ -1043,8 +1047,6 @@ void SomfyShade::processWaitingFrame() {
|
|||
Serial.println(" repeats");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1057,7 +1059,7 @@ void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) {
|
|||
if(!hasRemote) {
|
||||
for(uint8_t i = 0; i < SOMFY_MAX_LINKED_REMOTES; i++) {
|
||||
if(this->linkedRemotes[i].getRemoteAddress() == frame.remoteAddress) {
|
||||
this->linkedRemotes[i].setRollingCode(frame.rollingCode);
|
||||
if(frame.cmd != somfy_commands::Sensor) this->linkedRemotes[i].setRollingCode(frame.rollingCode);
|
||||
hasRemote = true;
|
||||
break;
|
||||
}
|
||||
|
|
@ -1066,6 +1068,7 @@ void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) {
|
|||
if(!hasRemote) return;
|
||||
this->lastFrame.copy(frame);
|
||||
int8_t dir = 0;
|
||||
int8_t tiltDir = 0;
|
||||
this->moveStart = this->tiltStart = millis();
|
||||
this->startPos = this->currentPos;
|
||||
this->startTiltPos = this->currentTiltPos;
|
||||
|
|
@ -1075,12 +1078,20 @@ void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) {
|
|||
// At this point we are not processing the combo buttons
|
||||
// will need to see what the shade does when you press both.
|
||||
switch(frame.cmd) {
|
||||
case somfy_commands::Sensor:
|
||||
if((frame.rollingCode << 4) & static_cast<uint8_t>(somfy_flags_t::Sunny)) this->flags |= static_cast<uint8_t>(somfy_flags_t::Sunny);
|
||||
else this->flags &= ~(static_cast<uint8_t>(somfy_flags_t::Sunny));
|
||||
if((frame.rollingCode << 4) & static_cast<uint8_t>(somfy_flags_t::Windy)) this->flags |= static_cast<uint8_t>(somfy_flags_t::Windy);
|
||||
else this->flags &= ~(static_cast<uint8_t>(somfy_flags_t::Windy));
|
||||
this->emitState();
|
||||
break;
|
||||
|
||||
case somfy_commands::Flag:
|
||||
this->flags &= ~(static_cast<uint8_t>(somfy_flags_t::Sun));
|
||||
this->flags &= ~(static_cast<uint8_t>(somfy_flags_t::SunFlag));
|
||||
this->emitState();
|
||||
break;
|
||||
case somfy_commands::SunFlag:
|
||||
this->flags |= static_cast<uint8_t>(somfy_flags_t::Sun);
|
||||
this->flags |= static_cast<uint8_t>(somfy_flags_t::SunFlag);
|
||||
this->emitState();
|
||||
break;
|
||||
case somfy_commands::Up:
|
||||
|
|
@ -1341,6 +1352,7 @@ void SomfyShade::sendTiltCommand(somfy_commands cmd) {
|
|||
}
|
||||
}
|
||||
void SomfyShade::moveToTiltTarget(float target) {
|
||||
int8_t newDir = 0;
|
||||
somfy_commands cmd = somfy_commands::My;
|
||||
if(target < this->currentTiltPos)
|
||||
cmd = somfy_commands::Up;
|
||||
|
|
@ -1365,6 +1377,7 @@ void SomfyShade::moveToTiltTarget(float target) {
|
|||
this->settingTiltPos = true;
|
||||
}
|
||||
void SomfyShade::moveToTarget(float pos, float tilt) {
|
||||
int8_t newDir = 0;
|
||||
somfy_commands cmd = somfy_commands::My;
|
||||
if(pos < this->currentPos)
|
||||
cmd = somfy_commands::Up;
|
||||
|
|
@ -1556,6 +1569,7 @@ void SomfyShadeController::publish() {
|
|||
mqtt.publish("shades", arr);
|
||||
}
|
||||
uint8_t SomfyShadeController::getNextShadeId() {
|
||||
uint8_t nextId = 0;
|
||||
// There is no shortcut for this since the deletion of
|
||||
// a shade in the middle makes all of this very difficult.
|
||||
for(uint8_t i = 1; i < SOMFY_MAX_SHADES - 1; i++) {
|
||||
|
|
@ -1795,7 +1809,7 @@ bool SomfyShadeController::toJSON(JsonArray &arr) {
|
|||
}
|
||||
void SomfyShadeController::loop() {
|
||||
this->transceiver.loop();
|
||||
for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) {
|
||||
for(uint8_t i; i < SOMFY_MAX_SHADES; i++) {
|
||||
if(this->shades[i].getShadeId() != 255) this->shades[i].checkMovement();
|
||||
}
|
||||
// Only commit the file once per second.
|
||||
|
|
@ -1827,16 +1841,17 @@ static const uint32_t tempo_if_gap = 30415; // Gap between frames
|
|||
|
||||
|
||||
static int16_t bitMin = SYMBOL * TOLERANCE_MIN;
|
||||
static uint16_t timing_index = 0;
|
||||
static somfy_rx_t somfy_rx;
|
||||
static somfy_rx_queue_t rx_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--) {
|
||||
for(uint8_t i = MAX_TX_BUFFER - 1; i >= 0; i--) {
|
||||
if(this->index[i] < MAX_TX_BUFFER) {
|
||||
uint8_t ndx = this->index[i];
|
||||
memcpy(tx, &this->items[ndx], sizeof(somfy_tx_t));
|
||||
this->items[ndx].clear();
|
||||
memset(&this->items[ndx], 0x00, sizeof(somfy_tx_t));
|
||||
this->length--;
|
||||
this->index[i] = 255;
|
||||
return true;
|
||||
|
|
@ -1849,8 +1864,7 @@ bool somfy_tx_queue_t::push(uint32_t await, somfy_commands cmd, uint8_t repeats)
|
|||
uint8_t ndx = this->index[MAX_TX_BUFFER - 1];
|
||||
this->index[MAX_TX_BUFFER - 1] = 255;
|
||||
this->length = MAX_TX_BUFFER - 1;
|
||||
if(ndx < MAX_TX_BUFFER)
|
||||
this->items[ndx].clear();
|
||||
if(ndx < MAX_TX_BUFFER) memset(&this->items[ndx], 0x00, sizeof(somfy_tx_t));
|
||||
}
|
||||
// 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
|
||||
|
|
@ -1873,8 +1887,7 @@ bool somfy_tx_queue_t::push(uint32_t await, somfy_commands cmd, uint8_t repeats)
|
|||
}
|
||||
void somfy_rx_queue_t::init() {
|
||||
Serial.println("Initializing RX Queue");
|
||||
for (uint8_t i = 0; i < MAX_RX_BUFFER; i++)
|
||||
this->items[i].clear();
|
||||
memset(&this->items[0], 0x00, sizeof(somfy_rx_t) * MAX_RX_BUFFER);
|
||||
memset(&this->index[0], 0xFF, MAX_RX_BUFFER);
|
||||
this->length = 0;
|
||||
}
|
||||
|
|
@ -1882,11 +1895,11 @@ void somfy_rx_queue_t::init() {
|
|||
bool somfy_rx_queue_t::pop(somfy_rx_t *rx) {
|
||||
// Read off the data from the oldest index.
|
||||
//Serial.println("Popping RX Queue");
|
||||
for(int8_t i = MAX_RX_BUFFER - 1; i >= 0; i--) {
|
||||
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));
|
||||
this->items[ndx].clear();
|
||||
memset(&this->items[ndx], 0x00, sizeof(somfy_rx_t));
|
||||
this->length--;
|
||||
this->index[i] = 255;
|
||||
return true;
|
||||
|
|
@ -2128,7 +2141,7 @@ void Transceiver::emitFrame(somfy_frame_t *frame, somfy_rx_t *rx) {
|
|||
evt.appendMessage(buf);
|
||||
snprintf(buf, sizeof(buf), "\"rcode\":%d,", frame->rollingCode);
|
||||
evt.appendMessage(buf);
|
||||
snprintf(buf, sizeof(buf), "\"command\":\"%s\",", translateSomfyCommand(frame->cmd).c_str());
|
||||
snprintf(buf, sizeof(buf), "\"command\":\"%s\",", translateSomfyCommand(frame->cmd));
|
||||
evt.appendMessage(buf);
|
||||
snprintf(buf, sizeof(buf), "\"rssi\":%d,", frame->rssi);
|
||||
evt.appendMessage(buf);
|
||||
|
|
|
|||
33
Somfy.h
33
Somfy.h
|
|
@ -28,7 +28,7 @@ enum class somfy_commands : byte {
|
|||
StepDown = 0xB,
|
||||
UnknownC = 0xC,
|
||||
UnknownD = 0xD,
|
||||
UnknownE = 0xE,
|
||||
Sensor = 0xE,
|
||||
RTWProto = 0xF, // RTW Protocol
|
||||
// Command extensions for 80 bit frames
|
||||
StepUp = 0x8B
|
||||
|
|
@ -59,17 +59,6 @@ typedef enum {
|
|||
} t_status;
|
||||
|
||||
struct somfy_rx_t {
|
||||
void clear() {
|
||||
this->status = t_status::waiting_synchro;
|
||||
this->bit_length = 56;
|
||||
this->cpt_synchro_hw = 0;
|
||||
this->cpt_bits = 0;
|
||||
this->previous_bit = 0;
|
||||
this->waiting_half_symbol = false;
|
||||
memset(this->payload, 0, sizeof(this->payload));
|
||||
memset(this->pulses, 0, sizeof(this->pulses));
|
||||
this->pulseCount = 0;
|
||||
}
|
||||
t_status status;
|
||||
uint8_t bit_length = 56;
|
||||
uint8_t cpt_synchro_hw = 0;
|
||||
|
|
@ -92,25 +81,13 @@ struct somfy_rx_queue_t {
|
|||
bool pop(somfy_rx_t *rx);
|
||||
};
|
||||
struct somfy_tx_t {
|
||||
void clear() {
|
||||
this->await = 0;
|
||||
this->cmd = somfy_commands::Unknown0;
|
||||
this->repeats = 0;
|
||||
}
|
||||
uint32_t await = 0;
|
||||
somfy_commands cmd;
|
||||
uint8_t repeats;
|
||||
};
|
||||
struct somfy_tx_queue_t {
|
||||
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();
|
||||
}
|
||||
}
|
||||
somfy_tx_queue_t() { memset(this->index, 255, MAX_TX_BUFFER); memset(&this->items[0], 0x00, sizeof(somfy_tx_queue_t) * MAX_TX_BUFFER); }
|
||||
void clear() { memset(&this->index[0], 255, MAX_TX_BUFFER); memset(&this->items[0], 0x00, sizeof(somfy_tx_queue_t) * MAX_TX_BUFFER); }
|
||||
uint8_t length = 0;
|
||||
uint8_t index[MAX_TX_BUFFER];
|
||||
somfy_tx_t items[MAX_TX_BUFFER];
|
||||
|
|
@ -119,7 +96,9 @@ struct somfy_tx_queue_t {
|
|||
};
|
||||
|
||||
enum class somfy_flags_t : byte {
|
||||
Sun = 1
|
||||
SunFlag = 0x01,
|
||||
Windy = 0x10,
|
||||
Sunny = 0x20
|
||||
};
|
||||
struct somfy_frame_t {
|
||||
bool valid = false;
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -863,3 +863,56 @@ div.button-sunflag {
|
|||
font-size: 32px;
|
||||
color: orange;
|
||||
}
|
||||
i.icss-warning {
|
||||
width: 1em;
|
||||
height: .1em;
|
||||
border-radius: 0.05em;
|
||||
margin: .82em 0 .06em 0;
|
||||
transform-origin: 50% -.33em;
|
||||
}
|
||||
div.indicator {
|
||||
position: absolute;
|
||||
top:34px;
|
||||
font-size:20px;
|
||||
}
|
||||
div.indicator-wind {
|
||||
color: orangered;
|
||||
clip-path: polygon(48% 18%, 0% 95%, 110% 92%);
|
||||
background:yellow;
|
||||
top:30%;
|
||||
left:13px;
|
||||
}
|
||||
div.indicator-sun {
|
||||
color: darkorange;
|
||||
left: -13px;
|
||||
top:3px;
|
||||
}
|
||||
.somfyShadeCtl[data-sunny="false"] .indicator-sun {
|
||||
display:none;
|
||||
}
|
||||
.somfyShadeCtl[data-windy="false"] .indicator-wind {
|
||||
display: none;
|
||||
}
|
||||
|
||||
i.icss-warning:before {
|
||||
width: 1.23em;
|
||||
height: 1.23em;
|
||||
border: 0.2em solid currentColor;
|
||||
border-color: currentColor currentColor transparent transparent;
|
||||
border-radius: .1em .13em;
|
||||
transform: translateX(-50%) scaleX(0.6) rotate(-45deg);
|
||||
top: -.55em;
|
||||
left: 50%;
|
||||
}
|
||||
|
||||
i.icss-warning:after {
|
||||
width: .125em;
|
||||
border: .0em solid transparent;
|
||||
border-top: .25em solid currentColor;
|
||||
border-bottom: .07em solid transparent;
|
||||
box-shadow: 0 .1em 0 -.012em;
|
||||
top: -.44em;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1056,8 +1056,9 @@ class Somfy {
|
|||
divCfg += '</div>';
|
||||
|
||||
divCtl += `<div class="somfyShadeCtl" data-shadeId="${shade.shadeId}" data-direction="${shade.direction}" data-remoteaddress="${shade.remoteAddress}" data-position="${shade.position}" data-target="${shade.target}" data-mypos="${shade.myPos}" data-mytiltpos="${shade.myTiltPos} data-shadetype="${shade.shadeType}" data-tilt="${shade.tiltType}"`;
|
||||
divCtl += ` data-windy="${(shade.flags & 0x10) === 0x10 ? 'true' : false}" data-sunny=${(shade.flags & 0x20) === 0x20 ? 'true' : 'false'}`;
|
||||
if (shade.tiltType !== 0) {
|
||||
divCtl += ` data-tiltposition="${shade.tiltPosition}" data-tiltdirection="${shade.tiltDirection}" data-tilttarget="${shade.tiltTarget}"`
|
||||
divCtl += ` data-tiltposition="${shade.tiltPosition}" data-tiltdirection="${shade.tiltDirection}" data-tilttarget="${shade.tiltTarget}"`;
|
||||
}
|
||||
divCtl += `><div class="shade-icon" data-shadeid="${shade.shadeId}" onclick="event.stopPropagation(); console.log(event); somfy.openSetPosition(${shade.shadeId});">`;
|
||||
divCtl += `<i class="somfy-shade-icon`;
|
||||
|
|
@ -1074,7 +1075,9 @@ class Somfy {
|
|||
}
|
||||
divCtl += `" data-shadeid="${shade.shadeId}" style="--shade-position:${shade.position}%;vertical-align: top;"></i>`;
|
||||
divCtl += shade.tiltType !== 0 ? `<i class="icss-window-tilt" data-shadeid="${shade.shadeId}" data-tiltposition="${shade.tiltPosition}"></i></div>` : '</div>';
|
||||
divCtl += `<div class="indicator indicator-wind"><i class="icss-warning"></i></div><div class="indicator indicator-sun"><i class="icss-sun"></i></div>`;
|
||||
divCtl += `<div class="shade-name">`;
|
||||
|
||||
divCtl += `<span class="shadectl-name">${shade.name}</span>`;
|
||||
divCtl += `<span class="shadectl-mypos"><label>My: </label><span id="spanMyPos">${shade.myPos > 0 ? shade.myPos + '%' : '---'}</span>`
|
||||
if (shade.myTiltPos > 0) divCtl += `<label> Tilt: </label><span id="spanMyTiltPos">${shade.myTiltPos > 0 ? shade.myTiltPos + '%' : '---'}</span>`
|
||||
|
|
@ -1321,6 +1324,8 @@ class Somfy {
|
|||
divs[i].setAttribute('data-position', state.position);
|
||||
divs[i].setAttribute('data-target', state.target);
|
||||
divs[i].setAttribute('data-mypos', state.mypos);
|
||||
divs[i].setAttribute('data-windy', (state.flags & 0x10) === 0x10 ? 'true' : 'false');
|
||||
divs[i].setAttribute('data-sunny', (state.flags & 0x20) === 0x20 ? 'true' : 'false');
|
||||
if (typeof state.myTiltPos !== 'undefined') divs[i].setAttribute('data-mytiltpos', state.myTiltPos);
|
||||
else divs[i].setAttribute('data-mytiltpos', -1);
|
||||
if (state.tiltType !== 0) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue