Add invert options for commands and position #69

This commit is contained in:
Robert Strouse 2023-06-16 17:42:56 -07:00
parent 536bf839a1
commit ec499cbec4
11 changed files with 138 additions and 67 deletions

View file

@ -6,9 +6,9 @@
extern Preferences pref; extern Preferences pref;
#define SHADE_HDR_VER 9 #define SHADE_HDR_VER 10
#define SHADE_HDR_SIZE 16 #define SHADE_HDR_SIZE 16
#define SHADE_REC_SIZE 242 #define SHADE_REC_SIZE 248
bool ConfigFile::begin(const char* filename, bool readOnly) { bool ConfigFile::begin(const char* filename, bool readOnly) {
this->file = LittleFS.open(filename, readOnly ? "r" : "w"); this->file = LittleFS.open(filename, readOnly ? "r" : "w");
@ -330,8 +330,12 @@ bool ShadeConfigFile::loadFile(SomfyShadeController *s, const char *filename) {
shade->target = floor(shade->currentPos); shade->target = floor(shade->currentPos);
shade->tiltTarget = floor(shade->currentTiltPos); shade->tiltTarget = floor(shade->currentTiltPos);
if(this->header.version >= 9) { if(this->header.version >= 9) {
shade->inverted = this->readBool(false); shade->flipCommands = this->readBool(false);
} }
if(this->header.version >= 10) {
shade->flipPosition = this->readBool(false);
}
if(shade->getShadeId() == 255) shade->clear();
} }
pref.end(); pref.end();
if(opened) { if(opened) {
@ -378,7 +382,8 @@ bool ShadeConfigFile::writeShadeRecord(SomfyShade *shade) {
this->writeFloat(0.0f, 5); // currentPos this->writeFloat(0.0f, 5); // currentPos
this->writeFloat(0.0f, 5); // currentTiltPos this->writeFloat(0.0f, 5); // currentTiltPos
} }
this->writeBool(shade->inverted, CFG_REC_END); this->writeBool(shade->flipCommands);
this->writeBool(shade->flipPosition, CFG_REC_END);
return true; return true;
} }
bool ShadeConfigFile::exists() { return LittleFS.exists("/shades.cfg"); } bool ShadeConfigFile::exists() { return LittleFS.exists("/shades.cfg"); }

View file

@ -226,6 +226,10 @@ bool MQTTClass::publish(const char *topic, uint16_t val) {
snprintf(g_content, sizeof(g_content), "%u", val); snprintf(g_content, sizeof(g_content), "%u", val);
return this->publish(topic, g_content); return this->publish(topic, g_content);
} }
bool MQTTClass::publish(const char *topic, bool val) {
snprintf(g_content, sizeof(g_content), "%s", val ? "true" : "false");
return this->publish(topic, g_content);
}
bool MQTTClass::connected() { bool MQTTClass::connected() {
if(settings.MQTT.enabled) return mqttClient.connected(); if(settings.MQTT.enabled) return mqttClient.connected();
return false; return false;

1
MQTT.h
View file

@ -22,6 +22,7 @@ class MQTTClass {
bool publish(const char *topic, int8_t val); bool publish(const char *topic, int8_t val);
bool publish(const char *topic, uint32_t val); bool publish(const char *topic, uint32_t val);
bool publish(const char *topic, uint16_t val); bool publish(const char *topic, uint16_t val);
bool publish(const char *topic, bool val);
bool subscribe(const char *topic); bool subscribe(const char *topic);
bool unsubscribe(const char *topic); bool unsubscribe(const char *topic);
static void receive(const char *topic, byte *payload, uint32_t length); static void receive(const char *topic, byte *payload, uint32_t length);

View file

@ -172,9 +172,9 @@ void Network::setConnected(conn_types connType) {
SSDP.setURL(0, "/"); SSDP.setURL(0, "/");
if(MDNS.begin(settings.hostname)) { if(MDNS.begin(settings.hostname)) {
Serial.printf("MDNS Responder Started: serverId=%s\n", settings.serverId); Serial.printf("MDNS Responder Started: serverId=%s\n", settings.serverId);
MDNS.addService("http", "tcp", 80); //MDNS.addService("http", "tcp", 80);
MDNS.addServiceTxt("http", "tcp", "board", "ESP32"); //MDNS.addServiceTxt("http", "tcp", "board", "ESP32");
MDNS.addServiceTxt("http", "tcp", "model", "ESPSomfyRTS"); //MDNS.addServiceTxt("http", "tcp", "model", "ESPSomfyRTS");
MDNS.addService("espsomfy_rts", "tcp", 8080); MDNS.addService("espsomfy_rts", "tcp", 8080);
MDNS.addServiceTxt("espsomfy_rts", "tcp", "serverId", String(settings.serverId)); MDNS.addServiceTxt("espsomfy_rts", "tcp", "serverId", String(settings.serverId));

119
Somfy.cpp
View file

@ -506,6 +506,51 @@ SomfyShade * SomfyShadeController::getShadeById(uint8_t shadeId) {
} }
return nullptr; return nullptr;
} }
void SomfyShade::clear() {
this->setShadeId(255);
this->setRemoteAddress(0);
this->moveStart = 0;
this->tiltStart = 0;
this->noSunStart = 0;
this->sunStart = 0;
this->windStart = 0;
this->windLast = 0;
this->noWindStart = 0;
this->noSunDone = true;
this->sunDone = true;
this->windDone = true;
this->noWindDone = true;
this->startPos = 0.0f;
this->startTiltPos = 0.0f;
this->settingMyPos = false;
this->settingPos = false;
this->settingTiltPos = false;
this->awaitMy = 0;
this->flipPosition = false;
this->flipCommands = false;
this->lastRollingCode = 0;
this->shadeType = shade_types::roller;
this->tiltType = tilt_types::none;
this->txQueue.clear();
this->currentPos = 0.0f;
this->currentTiltPos = 0.0f;
this->direction = 0;
this->tiltDirection = 0;
this->target = 0.0f;
this->tiltTarget = 0.0f;
this->myPos = -1.0f;
this->myTiltPos = -1.0f;
this->bitLength = somfy.transceiver.config.type;
this->proto = somfy.transceiver.config.proto;
for(uint8_t i = 0; i < SOMFY_MAX_LINKED_REMOTES; i++)
this->linkedRemotes[i].setRemoteAddress(0);
this->paired = false;
this->name[0] = 0x00;
this->upTime = 10000;
this->downTime = 10000;
this->tiltTime = 7000;
this->stepSize = 100;
}
bool SomfyShade::linkRemote(uint32_t address, uint16_t rollingCode) { bool SomfyShade::linkRemote(uint32_t address, uint16_t rollingCode) {
// Check to see if the remote is already linked. If it is // Check to see if the remote is already linked. If it is
// just return true after setting the rolling code // just return true after setting the rolling code
@ -963,31 +1008,34 @@ void SomfyShade::publish() {
snprintf(topic, sizeof(topic), "shades/%u/remoteAddress", this->shadeId); snprintf(topic, sizeof(topic), "shades/%u/remoteAddress", this->shadeId);
mqtt.publish(topic, this->getRemoteAddress()); mqtt.publish(topic, this->getRemoteAddress());
snprintf(topic, sizeof(topic), "shades/%u/position", this->shadeId); snprintf(topic, sizeof(topic), "shades/%u/position", this->shadeId);
mqtt.publish(topic, static_cast<uint8_t>(floor(this->currentPos))); mqtt.publish(topic, this->transformPosition(this->currentPos));
snprintf(topic, sizeof(topic), "shades/%u/direction", this->shadeId); snprintf(topic, sizeof(topic), "shades/%u/direction", this->shadeId);
mqtt.publish(topic, this->direction); mqtt.publish(topic, this->direction);
snprintf(topic, sizeof(topic), "shades/%u/target", this->shadeId); snprintf(topic, sizeof(topic), "shades/%u/target", this->shadeId);
mqtt.publish(topic, static_cast<uint8_t>(floor(this->target))); mqtt.publish(topic, this->transformPosition(this->target));
snprintf(topic, sizeof(topic), "shades/%u/lastRollingCode", this->shadeId); snprintf(topic, sizeof(topic), "shades/%u/lastRollingCode", this->shadeId);
mqtt.publish(topic, this->lastRollingCode); mqtt.publish(topic, this->lastRollingCode);
snprintf(topic, sizeof(topic), "shades/%u/mypos", this->shadeId); snprintf(topic, sizeof(topic), "shades/%u/mypos", this->shadeId);
mqtt.publish(topic, static_cast<int8_t>(floor(this->myPos))); mqtt.publish(topic, this->transformPosition(this->myPos));
snprintf(topic, sizeof(topic), "shades/%u/myTiltPos", this->shadeId); snprintf(topic, sizeof(topic), "shades/%u/myTiltPos", this->shadeId);
mqtt.publish(topic, static_cast<int8_t>(floor(this->myTiltPos))); mqtt.publish(topic, this->transformPosition(this->myTiltPos));
snprintf(topic, sizeof(topic), "shades/%u/shadeType", this->shadeId); snprintf(topic, sizeof(topic), "shades/%u/shadeType", this->shadeId);
mqtt.publish(topic, static_cast<uint8_t>(this->shadeType)); mqtt.publish(topic, static_cast<uint8_t>(this->shadeType));
snprintf(topic, sizeof(topic), "shades/%u/tiltType", this->shadeId); snprintf(topic, sizeof(topic), "shades/%u/tiltType", this->shadeId);
mqtt.publish(topic, static_cast<uint8_t>(this->tiltType)); mqtt.publish(topic, static_cast<uint8_t>(this->tiltType));
snprintf(topic, sizeof(topic), "shades/%u/flags", this->shadeId); snprintf(topic, sizeof(topic), "shades/%u/flags", this->shadeId);
mqtt.publish(topic, this->flags); mqtt.publish(topic, this->flags);
snprintf(topic, sizeof(topic), "shades/%u/flipCommands", this->shadeId);
mqtt.publish(topic, this->flipCommands);
snprintf(topic, sizeof(topic), "shades/%u/flipPosition", this->shadeId);
mqtt.publish(topic, this->flipPosition);
if(this->tiltType != tilt_types::none) { if(this->tiltType != tilt_types::none) {
snprintf(topic, sizeof(topic), "shades/%u/tiltDirection", this->shadeId); snprintf(topic, sizeof(topic), "shades/%u/tiltDirection", this->shadeId);
mqtt.publish(topic, this->tiltDirection); mqtt.publish(topic, this->tiltDirection);
snprintf(topic, sizeof(topic), "shades/%u/tiltPosition", this->shadeId); snprintf(topic, sizeof(topic), "shades/%u/tiltPosition", this->shadeId);
mqtt.publish(topic, static_cast<uint8_t>(floor(this->currentTiltPos))); mqtt.publish(topic, this->transformPosition(this->currentTiltPos));
snprintf(topic, sizeof(topic), "shades/%u/tiltTarget", this->shadeId); snprintf(topic, sizeof(topic), "shades/%u/tiltTarget", this->shadeId);
mqtt.publish(topic, static_cast<uint8_t>(floor(this->tiltTarget))); mqtt.publish(topic, this->transformPosition(this->tiltTarget));
} }
else if (this->shadeType == shade_types::awning) { else if (this->shadeType == shade_types::awning) {
const uint8_t sunFlag = !!(this->flags & static_cast<uint8_t>(somfy_flags_t::SunFlag)); const uint8_t sunFlag = !!(this->flags & static_cast<uint8_t>(somfy_flags_t::SunFlag));
@ -1004,37 +1052,43 @@ void SomfyShade::publish() {
} }
} }
void SomfyShade::emitState(const char *evt) { this->emitState(255, evt); } void SomfyShade::emitState(const char *evt) { this->emitState(255, evt); }
int8_t SomfyShade::transformPosition(float fpos) { return static_cast<int8_t>(this->flipPosition && fpos >= 0.00f ? floor(100.0f - fpos) : floor(fpos)); }
void SomfyShade::emitState(uint8_t num, const char *evt) { void SomfyShade::emitState(uint8_t num, const char *evt) {
char buf[320]; char buf[420];
if(this->tiltType != tilt_types::none) if(this->tiltType != tilt_types::none)
snprintf(buf, sizeof(buf), "{\"shadeId\":%d,\"type\":%u,\"remoteAddress\":%d,\"name\":\"%s\",\"direction\":%d,\"position\":%d,\"target\":%d,\"mypos\":%d,\"myTiltPos\":%d,\"tiltType\":%u,\"tiltDirection\":%d,\"tiltTarget\":%d,\"tiltPosition\":%d,\"flags\":%d}", snprintf(buf, sizeof(buf), "{\"shadeId\":%d,\"type\":%u,\"remoteAddress\":%d,\"name\":\"%s\",\"direction\":%d,\"position\":%d,\"target\":%d,\"mypos\":%d,\"myTiltPos\":%d,\"tiltType\":%u,\"tiltDirection\":%d,\"tiltTarget\":%d,\"tiltPosition\":%d,\"flipCommands\":%s,\"flipPosition\":%s,\"flags\":%d}",
this->shadeId, static_cast<uint8_t>(this->shadeType), this->getRemoteAddress(), this->name, this->direction, static_cast<uint8_t>(floor(this->currentPos)), static_cast<uint8_t>(floor(this->target)), static_cast<int8_t>(floor(this->myPos)), static_cast<int8_t>(this->myTiltPos), static_cast<uint8_t>(this->tiltType), this->tiltDirection, static_cast<uint8_t>(floor(this->tiltTarget)), static_cast<uint8_t>(floor(this->currentTiltPos)),this->flags); this->shadeId, static_cast<uint8_t>(this->shadeType), this->getRemoteAddress(), this->name, this->direction,
this->transformPosition(this->currentPos), this->transformPosition(this->target), this->transformPosition(this->myPos), this->transformPosition(this->myTiltPos), static_cast<uint8_t>(this->tiltType), this->tiltDirection,
this->transformPosition(this->tiltTarget), this->transformPosition(this->currentTiltPos),
this->flipCommands ? "true" : "false", this->flipPosition ? "true": "false", this->flags);
else else
snprintf(buf, sizeof(buf), "{\"shadeId\":%d,\"type\":%u,\"remoteAddress\":%d,\"name\":\"%s\",\"direction\":%d,\"position\":%d,\"target\":%d,\"mypos\":%d,\"tiltType\":%u,\"flags\":%d}", snprintf(buf, sizeof(buf), "{\"shadeId\":%d,\"type\":%u,\"remoteAddress\":%d,\"name\":\"%s\",\"direction\":%d,\"position\":%d,\"target\":%d,\"mypos\":%d,\"tiltType\":%u,\"flipCommands\":%s,\"flipPosition\":%s,\"flags\":%d}",
this->shadeId, static_cast<uint8_t>(this->shadeType), this->getRemoteAddress(), this->name, this->direction, static_cast<uint8_t>(floor(this->currentPos)), static_cast<uint8_t>(floor(this->target)), static_cast<int8_t>(floor(this->myPos)), static_cast<uint8_t>(this->tiltType), this->flags); this->shadeId, static_cast<uint8_t>(this->shadeType), this->getRemoteAddress(), this->name, this->direction,
this->transformPosition(this->currentPos), this->transformPosition(this->target), this->transformPosition(this->myPos),
static_cast<uint8_t>(this->tiltType), this->flipCommands ? "true" : "false", this->flipPosition ? "true": "false", this->flags);
if(num >= 255) sockEmit.sendToClients(evt, buf); if(num >= 255) sockEmit.sendToClients(evt, buf);
else sockEmit.sendToClient(num, evt, buf); else sockEmit.sendToClient(num, evt, buf);
if(mqtt.connected()) { if(mqtt.connected()) {
char topic[32]; char topic[32];
snprintf(topic, sizeof(topic), "shades/%u/position", this->shadeId); snprintf(topic, sizeof(topic), "shades/%u/position", this->shadeId);
mqtt.publish(topic, static_cast<uint8_t>(floor(this->currentPos))); mqtt.publish(topic, this->transformPosition(this->currentPos));
snprintf(topic, sizeof(topic), "shades/%u/direction", this->shadeId); snprintf(topic, sizeof(topic), "shades/%u/direction", this->shadeId);
mqtt.publish(topic, this->direction); mqtt.publish(topic, this->direction);
snprintf(topic, sizeof(topic), "shades/%u/target", this->shadeId); snprintf(topic, sizeof(topic), "shades/%u/target", this->shadeId);
mqtt.publish(topic, static_cast<uint8_t>(floor(this->target))); mqtt.publish(topic, this->transformPosition(this->target));
snprintf(topic, sizeof(topic), "shades/%u/lastRollingCode", this->shadeId); snprintf(topic, sizeof(topic), "shades/%u/lastRollingCode", this->shadeId);
mqtt.publish(topic, this->lastRollingCode); mqtt.publish(topic, this->lastRollingCode);
snprintf(topic, sizeof(topic), "shades/%u/mypos", this->shadeId); snprintf(topic, sizeof(topic), "shades/%u/mypos", this->shadeId);
mqtt.publish(topic, static_cast<int8_t>(floor(this->myPos))); mqtt.publish(topic, this->transformPosition(this->myPos));
snprintf(topic, sizeof(topic), "shades/%u/tiltType", this->shadeId); snprintf(topic, sizeof(topic), "shades/%u/tiltType", this->shadeId);
mqtt.publish(topic, static_cast<uint8_t>(this->tiltType)); mqtt.publish(topic, static_cast<uint8_t>(this->tiltType));
if(this->tiltType != tilt_types::none) { if(this->tiltType != tilt_types::none) {
snprintf(topic, sizeof(topic), "shades/%u/myTiltPos", this->shadeId); snprintf(topic, sizeof(topic), "shades/%u/myTiltPos", this->shadeId);
mqtt.publish(topic, static_cast<int8_t>(floor(this->myTiltPos))); mqtt.publish(topic, this->transformPosition(this->myTiltPos));
snprintf(topic, sizeof(topic), "shades/%u/tiltPosition", this->shadeId); snprintf(topic, sizeof(topic), "shades/%u/tiltPosition", this->shadeId);
mqtt.publish(topic, static_cast<uint8_t>(floor(this->currentTiltPos))); mqtt.publish(topic, this->transformPosition(this->currentTiltPos));
snprintf(topic, sizeof(topic), "shades/%u/tiltTarget", this->shadeId); snprintf(topic, sizeof(topic), "shades/%u/tiltTarget", this->shadeId);
mqtt.publish(topic, static_cast<uint8_t>(floor(this->tiltTarget))); mqtt.publish(topic, this->transformPosition(this->tiltTarget));
} }
else if (this->shadeType == shade_types::awning) { else if (this->shadeType == shade_types::awning) {
const uint8_t sunFlag = !!(this->flags & static_cast<uint8_t>(somfy_flags_t::SunFlag)); const uint8_t sunFlag = !!(this->flags & static_cast<uint8_t>(somfy_flags_t::SunFlag));
@ -1645,7 +1699,8 @@ bool SomfyShade::fromJSON(JsonObject &obj) {
this->shadeType = static_cast<shade_types>(obj["shadeType"].as<uint8_t>()); this->shadeType = static_cast<shade_types>(obj["shadeType"].as<uint8_t>());
} }
} }
if(obj.containsKey("inverted")) this->inverted = obj["inverted"].as<bool>(); if(obj.containsKey("flipCommands")) this->flipCommands = obj["flipCommands"].as<bool>();
if(obj.containsKey("flipPosition")) this->flipPosition = obj["flipPosition"].as<bool>();
if(obj.containsKey("tiltType")) { if(obj.containsKey("tiltType")) {
if(obj["tiltType"].is<const char *>()) { if(obj["tiltType"].is<const char *>()) {
if(strncmp(obj["tiltType"].as<const char *>(), "none", 4) == 0) if(strncmp(obj["tiltType"].as<const char *>(), "none", 4) == 0)
@ -1689,15 +1744,15 @@ bool SomfyShade::toJSON(JsonObject &obj) {
obj["paired"] = this->paired; obj["paired"] = this->paired;
//obj["remotePrefId"] = this->getRemotePrefId(); //obj["remotePrefId"] = this->getRemotePrefId();
obj["lastRollingCode"] = this->lastRollingCode; obj["lastRollingCode"] = this->lastRollingCode;
obj["position"] = static_cast<uint8_t>(floor(this->currentPos)); obj["position"] = this->transformPosition(this->currentPos);
obj["tiltPosition"] = static_cast<uint8_t>(floor(this->currentTiltPos)); obj["tiltPosition"] = this->transformPosition(this->currentTiltPos);
obj["tiltDirection"] = this->tiltDirection; obj["tiltDirection"] = this->tiltDirection;
obj["tiltTime"] = this->tiltTime; obj["tiltTime"] = this->tiltTime;
obj["stepSize"] = this->stepSize; obj["stepSize"] = this->stepSize;
obj["tiltTarget"] = static_cast<uint8_t>(floor(this->tiltTarget)); obj["tiltTarget"] = this->transformPosition(this->tiltTarget);
obj["target"] = this->target; obj["target"] = this->transformPosition(this->target);
obj["myPos"] = static_cast<int8_t>(floor(this->myPos)); obj["myPos"] = this->transformPosition(this->myPos);
obj["myTiltPos"] = static_cast<int8_t>(floor(this->myTiltPos)); obj["myTiltPos"] = this->transformPosition(this->myTiltPos);
obj["direction"] = this->direction; obj["direction"] = this->direction;
obj["tiltType"] = static_cast<uint8_t>(this->tiltType); obj["tiltType"] = static_cast<uint8_t>(this->tiltType);
obj["tiltTime"] = this->tiltTime; obj["tiltTime"] = this->tiltTime;
@ -1705,7 +1760,8 @@ bool SomfyShade::toJSON(JsonObject &obj) {
obj["bitLength"] = this->bitLength; obj["bitLength"] = this->bitLength;
obj["proto"] = static_cast<uint8_t>(this->proto); obj["proto"] = static_cast<uint8_t>(this->proto);
obj["flags"] = this->flags; obj["flags"] = this->flags;
obj["inverted"] = this->inverted; obj["flipCommands"] = this->flipCommands;
obj["flipPosition"] = this->flipPosition;
SomfyRemote::toJSON(obj); SomfyRemote::toJSON(obj);
JsonArray arr = obj.createNestedArray("linkedRemotes"); JsonArray arr = obj.createNestedArray("linkedRemotes");
for(uint8_t i = 0; i < SOMFY_MAX_LINKED_REMOTES; i++) { for(uint8_t i = 0; i < SOMFY_MAX_LINKED_REMOTES; i++) {
@ -1863,7 +1919,7 @@ SomfyShade *SomfyShadeController::addShade() {
return shade; return shade;
} }
somfy_commands SomfyRemote::transformCommand(somfy_commands cmd) { somfy_commands SomfyRemote::transformCommand(somfy_commands cmd) {
if(this->inverted) { if(this->flipCommands) {
switch(cmd) { switch(cmd) {
case somfy_commands::Up: case somfy_commands::Up:
return somfy_commands::Down; return somfy_commands::Down;
@ -1935,14 +1991,7 @@ bool SomfyShadeController::deleteShade(uint8_t shadeId) {
for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) { for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) {
if(this->shades[i].getShadeId() == shadeId) { if(this->shades[i].getShadeId() == shadeId) {
shades[i].emitState("shadeRemoved"); shades[i].emitState("shadeRemoved");
this->shades[i].setShadeId(255); this->shades[i].clear();
this->shades[i].currentPos = 0;
this->shades[i].currentTiltPos = 0;
this->shades[i].myPos = -1.0f;
this->shades[i].myTiltPos = -1.0f;
this->shades[i].shadeType = shade_types::roller;
this->shades[i].tiltType = tilt_types::none;
this->shades[i].flags = 0;
} }
} }
if(this->useNVS()) { if(this->useNVS()) {

14
Somfy.h
View file

@ -167,7 +167,8 @@ class SomfyRemote {
uint32_t m_remoteAddress = 0; uint32_t m_remoteAddress = 0;
public: public:
radio_proto proto = radio_proto::RTS; radio_proto proto = radio_proto::RTS;
bool inverted = false; bool flipCommands = false;
uint8_t flags = 0; uint8_t flags = 0;
uint8_t bitLength = 0; uint8_t bitLength = 0;
char *getRemotePrefId() {return m_remotePrefId;} char *getRemotePrefId() {return m_remotePrefId;}
@ -205,6 +206,7 @@ class SomfyShade : public SomfyRemote {
bool settingTiltPos = false; bool settingTiltPos = false;
uint32_t awaitMy = 0; uint32_t awaitMy = 0;
public: public:
bool flipPosition = false;
shade_types shadeType = shade_types::roller; shade_types shadeType = shade_types::roller;
tilt_types tiltType = tilt_types::none; tilt_types tiltType = tilt_types::none;
void load(); void load();
@ -215,10 +217,10 @@ class SomfyShade : public SomfyRemote {
//uint16_t movement = 0; //uint16_t movement = 0;
int8_t direction = 0; // 0 = stopped, 1=down, -1=up. int8_t direction = 0; // 0 = stopped, 1=down, -1=up.
int8_t tiltDirection = 0; // 0=stopped, 1=clockwise, -1=counter clockwise int8_t tiltDirection = 0; // 0=stopped, 1=clockwise, -1=counter clockwise
float target = 0.0; float target = 0.0f;
float tiltTarget = 0.0; float tiltTarget = 0.0f;
float myPos = -1.0; float myPos = -1.0f;
float myTiltPos = -1.0; float myTiltPos = -1.0f;
SomfyLinkedRemote linkedRemotes[SOMFY_MAX_LINKED_REMOTES]; SomfyLinkedRemote linkedRemotes[SOMFY_MAX_LINKED_REMOTES];
bool paired = false; bool paired = false;
bool fromJSON(JsonObject &obj); bool fromJSON(JsonObject &obj);
@ -254,6 +256,8 @@ class SomfyShade : public SomfyRemote {
void commitShadePosition(); void commitShadePosition();
void commitTiltPosition(); void commitTiltPosition();
void commitMyPosition(); void commitMyPosition();
void clear();
int8_t transformPosition(float fpos);
}; };
struct transceiver_config_t { struct transceiver_config_t {

Binary file not shown.

Binary file not shown.

10
Web.cpp
View file

@ -167,7 +167,7 @@ void Web::begin() {
Serial.println(apiServer.arg("plain")); Serial.println(apiServer.arg("plain"));
// Send the command to the shade. // Send the command to the shade.
if(target <= 100) if(target <= 100)
shade->moveToTarget(target); shade->moveToTarget(shade->transformPosition(target));
else else
shade->sendCommand(command, repeat); shade->sendCommand(command, repeat);
DynamicJsonDocument sdoc(512); DynamicJsonDocument sdoc(512);
@ -231,7 +231,7 @@ void Web::begin() {
Serial.println(apiServer.arg("plain")); Serial.println(apiServer.arg("plain"));
// Send the command to the shade. // Send the command to the shade.
if(target <= 100) if(target <= 100)
shade->moveToTiltTarget(target); shade->moveToTiltTarget(shade->transformPosition(target));
else else
shade->sendTiltCommand(command); shade->sendTiltCommand(command);
DynamicJsonDocument sdoc(512); DynamicJsonDocument sdoc(512);
@ -688,7 +688,7 @@ void Web::begin() {
Serial.println(server.arg("plain")); Serial.println(server.arg("plain"));
// Send the command to the shade. // Send the command to the shade.
if(target <= 100) if(target <= 100)
shade->moveToTiltTarget(target); shade->moveToTiltTarget(shade->transformPosition(target));
else else
shade->sendTiltCommand(command); shade->sendTiltCommand(command);
DynamicJsonDocument sdoc(512); DynamicJsonDocument sdoc(512);
@ -755,7 +755,7 @@ void Web::begin() {
Serial.println(server.arg("plain")); Serial.println(server.arg("plain"));
// Send the command to the shade. // Send the command to the shade.
if(target <= 100) if(target <= 100)
shade->moveToTarget(target); shade->moveToTarget(shade->transformPosition(target));
else else
shade->sendCommand(command, repeat); shade->sendCommand(command, repeat);
DynamicJsonDocument sdoc(512); DynamicJsonDocument sdoc(512);
@ -813,7 +813,7 @@ void Web::begin() {
if(tilt < 0) tilt = shade->myPos; if(tilt < 0) tilt = shade->myPos;
if(shade->tiltType == tilt_types::none) tilt = -1; if(shade->tiltType == tilt_types::none) tilt = -1;
if(pos >= 0 && pos <= 100) if(pos >= 0 && pos <= 100)
shade->setMyPosition(pos, tilt); shade->setMyPosition(shade->transformPosition(pos), shade->transformPosition(tilt));
DynamicJsonDocument sdoc(512); DynamicJsonDocument sdoc(512);
JsonObject sobj = sdoc.to<JsonObject>(); JsonObject sobj = sdoc.to<JsonObject>();
shade->toJSON(sobj); shade->toJSON(sobj);

View file

@ -296,13 +296,18 @@
</label> </label>
</div> </div>
</div> </div>
<div id="divInverted" style="margin-top:-10px;"> <div style="margin-top:-10px;">
<div class="field-group"> <div class="field-group">
<input id="cbInverted" name="inverted" type="checkbox" style="" /> <input id="cbFlipCommands" name="flipCommands" type="checkbox" style="" />
<label for="cbInverted" style="display:block;font-size:1em;margin-top:0px;margin-left:7px;display:inline-block;">Invert Commands</label> <label for="cbFlipCommands" style="display:block;font-size:1em;margin-top:0px;margin-left:7px;display:inline-block;">Invert Commands</label>
</div>
</div>
<div style="margin-top:-10px;">
<div class="field-group">
<input id="cbFlipPosition" name="flipPos" type="checkbox" style="" />
<label for="cbFlipPosition" style="display:block;font-size:1em;margin-top:0px;margin-left:7px;display:inline-block;">Invert Position (expressed in % of open)</label>
</div> </div>
</div> </div>
<div class="button-container" style="text-align:center;"> <div class="button-container" style="text-align:center;">
<button id="btnPairShade" type="button" onclick="somfy.pairShade(parseInt(document.getElementById('spanShadeId').innerText, 10));" style="display:inline-block;width:47%;"> <button id="btnPairShade" type="button" onclick="somfy.pairShade(parseInt(document.getElementById('spanShadeId').innerText, 10));" style="display:inline-block;width:47%;">
Pair Shade Pair Shade

View file

@ -1073,7 +1073,7 @@ class Somfy {
divCtl += ' icss-window-shade'; divCtl += ' icss-window-shade';
break; break;
} }
divCtl += `" data-shadeid="${shade.shadeId}" style="--shade-position:${shade.position}%;vertical-align: top;"></i>`; divCtl += `" data-shadeid="${shade.shadeId}" style="--shade-position:${shade.flipPosition ? 100 - 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 += 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="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 += `<div class="shade-name">`;
@ -1304,7 +1304,7 @@ class Somfy {
console.log(state); console.log(state);
let icons = document.querySelectorAll(`.somfy-shade-icon[data-shadeid="${state.shadeId}"]`); let icons = document.querySelectorAll(`.somfy-shade-icon[data-shadeid="${state.shadeId}"]`);
for (let i = 0; i < icons.length; i++) { for (let i = 0; i < icons.length; i++) {
icons[i].style.setProperty('--shade-position', `${state.position}%`); icons[i].style.setProperty('--shade-position', `${state.flipPosition ? 100 - state.position : state.position}%`);
} }
if (state.tiltType !== 0) { if (state.tiltType !== 0) {
let tilts = document.querySelectorAll(`.icss-window-tilt[data-shadeid="${state.shadeId}"]`); let tilts = document.querySelectorAll(`.icss-window-tilt[data-shadeid="${state.shadeId}"]`);
@ -1323,7 +1323,7 @@ class Somfy {
divs[i].setAttribute('data-direction', state.direction); divs[i].setAttribute('data-direction', state.direction);
divs[i].setAttribute('data-position', state.position); divs[i].setAttribute('data-position', state.position);
divs[i].setAttribute('data-target', state.target); divs[i].setAttribute('data-target', state.target);
divs[i].setAttribute('data-mypos', state.mypos); divs[i].setAttribute('data-mypos', state.myPos);
divs[i].setAttribute('data-windy', (state.flags & 0x10) === 0x10 ? 'true' : 'false'); divs[i].setAttribute('data-windy', (state.flags & 0x10) === 0x10 ? 'true' : 'false');
divs[i].setAttribute('data-sunny', (state.flags & 0x20) === 0x20 ? '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); if (typeof state.myTiltPos !== 'undefined') divs[i].setAttribute('data-mytiltpos', state.myTiltPos);
@ -1486,7 +1486,8 @@ class Somfy {
document.getElementById('selShadeProto').value = shade.proto || 0; document.getElementById('selShadeProto').value = shade.proto || 0;
document.getElementById('slidStepSize').value = shade.stepSize || 100; document.getElementById('slidStepSize').value = shade.stepSize || 100;
document.getElementById('spanStepSize').innerHTML = shade.stepSize.fmt('#,##0'); document.getElementById('spanStepSize').innerHTML = shade.stepSize.fmt('#,##0');
document.getElementById('cbInverted').value = shade.inverted || false; document.getElementById('cbFlipCommands').value = shade.flipCommands || false;
document.getElementById('cbFlipPosition').value = shade.flipPosition || false;
} }
}); });
} }
@ -1520,7 +1521,8 @@ class Somfy {
document.getElementById('spanStepSize').innerHTML = shade.stepSize.fmt('#,##0'); document.getElementById('spanStepSize').innerHTML = shade.stepSize.fmt('#,##0');
document.getElementById('fldTiltTime').value = shade.tiltTime; document.getElementById('fldTiltTime').value = shade.tiltTime;
document.getElementById('selTiltType').value = shade.tiltType; document.getElementById('selTiltType').value = shade.tiltType;
document.getElementById('cbInverted').checked = shade.inverted; document.getElementById('cbFlipCommands').checked = shade.flipCommands;
document.getElementById('cbFlipPosition').checked = shade.flipPosition;
this.onShadeTypeChanged(document.getElementById('selShadeType')); this.onShadeTypeChanged(document.getElementById('selShadeType'));
let ico = document.getElementById('icoShade'); let ico = document.getElementById('icoShade');
switch (shade.shadeType) { switch (shade.shadeType) {
@ -1537,8 +1539,8 @@ class Somfy {
tilt.style.display = shade.tiltType !== 0 ? '' : 'none'; tilt.style.display = shade.tiltType !== 0 ? '' : 'none';
tilt.setAttribute('data-tiltposition', shade.tiltPosition); tilt.setAttribute('data-tiltposition', shade.tiltPosition);
tilt.setAttribute('data-shadeid', shade.shadeId); tilt.setAttribute('data-shadeid', shade.shadeId);
ico.style.setProperty('--shade-position', `${shade.position}%`); ico.style.setProperty('--shade-position', `${shade.flipPosition ? 100 - shade.position : shade.position}%`);
ico.style.setProperty('--tilt-position', `${shade.tiltPosition}%`); ico.style.setProperty('--tilt-position', `${shade.flipPosition ? 100 - shade.tiltPosition : shade.tiltPosition}%`);
ico.setAttribute('data-shadeid', shade.shadeId); ico.setAttribute('data-shadeid', shade.shadeId);
somfy.onShadeBitLengthChanged(document.getElementById('selShadeBitLength')); somfy.onShadeBitLengthChanged(document.getElementById('selShadeBitLength'));
document.getElementById('btnSetRollingCode').style.display = 'inline-block'; document.getElementById('btnSetRollingCode').style.display = 'inline-block';
@ -1579,7 +1581,8 @@ class Somfy {
bitLength: parseInt(document.getElementById('selShadeBitLength').value, 10) || 56, bitLength: parseInt(document.getElementById('selShadeBitLength').value, 10) || 56,
proto: parseInt(document.getElementById('selShadeProto').value, 10) || 0, proto: parseInt(document.getElementById('selShadeProto').value, 10) || 0,
stepSize: parseInt(document.getElementById('slidStepSize').value, 10) || 100, stepSize: parseInt(document.getElementById('slidStepSize').value, 10) || 100,
inverted: document.getElementById('cbInverted').checked flipCommands: document.getElementById('cbFlipCommands').checked,
flipPosition: document.getElementById('cbFlipPosition').checked
}; };
if (obj.shadeType === 1) { if (obj.shadeType === 1) {
obj.tiltType = parseInt(document.getElementById('selTiltType').value, 10); obj.tiltType = parseInt(document.getElementById('selTiltType').value, 10);
@ -1687,7 +1690,7 @@ class Somfy {
document.getElementsByName('shadeUpTime')[0].value = shade.upTime; document.getElementsByName('shadeUpTime')[0].value = shade.upTime;
document.getElementsByName('shadeDownTime')[0].value = shade.downTime; document.getElementsByName('shadeDownTime')[0].value = shade.downTime;
let ico = document.getElementById('icoShade'); let ico = document.getElementById('icoShade');
ico.style.setProperty('--shade-position', `${shade.position}%`); ico.style.setProperty('--shade-position', `${shade.flipPosition ? 100 - shade.position : shade.position}%`);
ico.setAttribute('data-shadeid', shade.shadeId); ico.setAttribute('data-shadeid', shade.shadeId);
if (shade.paired) { if (shade.paired) {
document.getElementById('btnUnpairShade').style.display = 'inline-block'; document.getElementById('btnUnpairShade').style.display = 'inline-block';
@ -1719,7 +1722,7 @@ class Somfy {
document.getElementsByName('shadeUpTime')[0].value = shade.upTime; document.getElementsByName('shadeUpTime')[0].value = shade.upTime;
document.getElementsByName('shadeDownTime')[0].value = shade.downTime; document.getElementsByName('shadeDownTime')[0].value = shade.downTime;
let ico = document.getElementById('icoShade'); let ico = document.getElementById('icoShade');
ico.style.setProperty('--shade-position', `${shade.position}%`); ico.style.setProperty('--shade-position', `${shade.flipPosition ? 100 - shade.position : shade.position}%`);
ico.setAttribute('data-shadeid', shade.shadeId); ico.setAttribute('data-shadeid', shade.shadeId);
if (shade.paired) { if (shade.paired) {
document.getElementById('btnUnpairShade').style.display = 'inline-block'; document.getElementById('btnUnpairShade').style.display = 'inline-block';
@ -1797,7 +1800,7 @@ class Somfy {
document.getElementsByName('shadeUpTime')[0].value = shade.upTime; document.getElementsByName('shadeUpTime')[0].value = shade.upTime;
document.getElementsByName('shadeDownTime')[0].value = shade.downTime; document.getElementsByName('shadeDownTime')[0].value = shade.downTime;
let ico = document.getElementById('icoShade'); let ico = document.getElementById('icoShade');
ico.style.setProperty('--shade-position', `${shade.position}%`); ico.style.setProperty('--shade-position', `${shade.flipPosition ? 100 - shade.position : shade.position}%`);
ico.setAttribute('data-shadeid', shade.shadeId); ico.setAttribute('data-shadeid', shade.shadeId);
if (shade.paired) { if (shade.paired) {
document.getElementById('btnUnpairShade').style.display = 'inline-block'; document.getElementById('btnUnpairShade').style.display = 'inline-block';