Fix issue where nvs was not getting reset with rollingCodes from backup file.

This commit is contained in:
Robert Strouse 2023-07-20 20:10:43 -07:00
parent 7f4cc19914
commit d471745489
13 changed files with 280 additions and 109 deletions

View file

@ -352,7 +352,13 @@ bool ShadeConfigFile::loadFile(SomfyShadeController *s, const char *filename) {
} }
shade->lastRollingCode = this->readUInt16(0); shade->lastRollingCode = this->readUInt16(0);
if(this->header.version > 7) shade->flags = this->readUInt8(0); if(this->header.version > 7) shade->flags = this->readUInt8(0);
if(shade->getRemoteAddress() != 0) shade->lastRollingCode = max(pref.getUShort(shade->getRemotePrefId(), shade->lastRollingCode), shade->lastRollingCode); if(shade->getRemoteAddress() != 0) {
// If the last rolling code stored on the nvs is less than the rc we currently have
// then we need to set it.
uint16_t rc = pref.getUShort(shade->getRemotePrefId(), 0);
shade->lastRollingCode = max(rc, shade->lastRollingCode);
if(rc < shade->lastRollingCode) pref.putUShort(shade->getRemotePrefId(), shade->lastRollingCode);
}
if(this->header.version < 4) if(this->header.version < 4)
shade->myPos = static_cast<float>(this->readUInt8(255)); shade->myPos = static_cast<float>(this->readUInt8(255));
else { else {
@ -391,12 +397,18 @@ bool ShadeConfigFile::loadFile(SomfyShadeController *s, const char *filename) {
this->readString(group->name, sizeof(group->name)); this->readString(group->name, sizeof(group->name));
group->proto = static_cast<radio_proto>(this->readUInt8(0)); group->proto = static_cast<radio_proto>(this->readUInt8(0));
group->bitLength = this->readUInt8(56); group->bitLength = this->readUInt8(56);
if(group->getRemoteAddress() != 0) {
uint16_t rc = pref.getUShort(group->getRemotePrefId(), 0);
group->lastRollingCode = max(rc, group->lastRollingCode);
if(rc < group->lastRollingCode) pref.putUShort(group->getRemotePrefId(), group->lastRollingCode);
}
uint8_t lsd = 0; uint8_t lsd = 0;
for(uint8_t j = 0; j < SOMFY_MAX_GROUPED_SHADES; j++) { for(uint8_t j = 0; j < SOMFY_MAX_GROUPED_SHADES; j++) {
uint8_t shadeId = this->readUInt8(0); uint8_t shadeId = this->readUInt8(0);
// Do this to eliminate gaps. // Do this to eliminate gaps.
if(shadeId > 0) group->linkedShades[lsd++] = shadeId; if(shadeId > 0) group->linkedShades[lsd++] = shadeId;
} }
group->compressLinkedShadeIds();
} }
pref.end(); pref.end();
if(opened) { if(opened) {
@ -442,7 +454,7 @@ bool ShadeConfigFile::writeShadeRecord(SomfyShade *shade) {
} }
this->writeUInt16(shade->lastRollingCode); this->writeUInt16(shade->lastRollingCode);
if(shade->getShadeId() != 255) { if(shade->getShadeId() != 255) {
this->writeUInt8(shade->flags & static_cast<uint8_t>(somfy_flags_t::SunFlag)); this->writeUInt8(shade->flags & 0x0F);
this->writeFloat(shade->myPos, 5); this->writeFloat(shade->myPos, 5);
this->writeFloat(shade->myTiltPos, 5); this->writeFloat(shade->myTiltPos, 5);
this->writeFloat(shade->currentPos, 5); this->writeFloat(shade->currentPos, 5);

View file

@ -3,7 +3,7 @@
#ifndef configsettings_h #ifndef configsettings_h
#define configsettings_h #define configsettings_h
#define FW_VERSION "v2.0.3" #define FW_VERSION "v2.1.0"
enum DeviceStatus { enum DeviceStatus {
DS_OK = 0, DS_OK = 0,
DS_ERROR = 1, DS_ERROR = 1,

View file

@ -28,7 +28,7 @@ void MQTTClass::reset() {
bool MQTTClass::loop() { bool MQTTClass::loop() {
if(settings.MQTT.enabled && !mqttClient.connected()) if(settings.MQTT.enabled && !mqttClient.connected())
this->connect(); this->connect();
mqttClient.loop(); if(settings.MQTT.enabled) mqttClient.loop();
return true; return true;
} }
void MQTTClass::receive(const char *topic, byte*payload, uint32_t length) { void MQTTClass::receive(const char *topic, byte*payload, uint32_t length) {
@ -219,49 +219,49 @@ bool MQTTClass::subscribe(const char *topic) {
} }
return true; return true;
} }
bool MQTTClass::publish(const char *topic, const char *payload) { bool MQTTClass::publish(const char *topic, const char *payload, bool retain) {
if(mqttClient.connected()) { if(mqttClient.connected()) {
char top[128]; char top[128];
if(strlen(settings.MQTT.rootTopic) > 0) if(strlen(settings.MQTT.rootTopic) > 0)
snprintf(top, sizeof(top), "%s/%s", settings.MQTT.rootTopic, topic); snprintf(top, sizeof(top), "%s/%s", settings.MQTT.rootTopic, topic);
else else
strlcpy(top, topic, sizeof(top)); strlcpy(top, topic, sizeof(top));
mqttClient.publish(top, payload); mqttClient.publish(top, payload, retain);
return true; return true;
} }
return false; return false;
} }
bool MQTTClass::publish(const char *topic, uint32_t val) { bool MQTTClass::publish(const char *topic, uint32_t val, bool retain) {
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, retain);
} }
bool MQTTClass::publish(const char *topic, JsonDocument &doc) { bool MQTTClass::publish(const char *topic, JsonDocument &doc, bool retain) {
serializeJson(doc, g_content, sizeof(g_content)); serializeJson(doc, g_content, sizeof(g_content));
return this->publish(topic, g_content); return this->publish(topic, g_content, retain);
} }
bool MQTTClass::publish(const char *topic, JsonArray &arr) { bool MQTTClass::publish(const char *topic, JsonArray &arr, bool retain) {
serializeJson(arr, g_content, sizeof(g_content)); serializeJson(arr, g_content, sizeof(g_content));
return this->publish(topic, g_content); return this->publish(topic, g_content, retain);
} }
bool MQTTClass::publish(const char *topic, JsonObject &obj) { bool MQTTClass::publish(const char *topic, JsonObject &obj, bool retain) {
serializeJson(obj, g_content, sizeof(g_content)); serializeJson(obj, g_content, sizeof(g_content));
return this->publish(topic, g_content); return this->publish(topic, g_content, retain);
} }
bool MQTTClass::publish(const char *topic, int8_t val) { bool MQTTClass::publish(const char *topic, int8_t val, bool retain) {
snprintf(g_content, sizeof(g_content), "%d", val); snprintf(g_content, sizeof(g_content), "%d", val);
return this->publish(topic, g_content); return this->publish(topic, g_content, retain);
} }
bool MQTTClass::publish(const char *topic, uint8_t val) { bool MQTTClass::publish(const char *topic, uint8_t val, bool retain) {
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, retain);
} }
bool MQTTClass::publish(const char *topic, uint16_t val) { bool MQTTClass::publish(const char *topic, uint16_t val, bool retain) {
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, retain);
} }
bool MQTTClass::publish(const char *topic, bool val) { bool MQTTClass::publish(const char *topic, bool val, bool retain) {
snprintf(g_content, sizeof(g_content), "%s", val ? "true" : "false"); snprintf(g_content, sizeof(g_content), "%s", val ? "true" : "false");
return this->publish(topic, g_content); return this->publish(topic, g_content, retain);
} }
bool MQTTClass::connected() { bool MQTTClass::connected() {
if(settings.MQTT.enabled) return mqttClient.connected(); if(settings.MQTT.enabled) return mqttClient.connected();

18
MQTT.h
View file

@ -14,15 +14,15 @@ class MQTTClass {
bool disconnect(); bool disconnect();
bool connected(); bool connected();
void reset(); void reset();
bool publish(const char *topic, const char *payload); bool publish(const char *topic, const char *payload, bool retain = false);
bool publish(const char *topic, JsonDocument &doc); bool publish(const char *topic, JsonDocument &doc, bool retain = false);
bool publish(const char *topic, JsonArray &arr); bool publish(const char *topic, JsonArray &arr, bool retain = false);
bool publish(const char *topic, JsonObject &obj); bool publish(const char *topic, JsonObject &obj, bool retain = false);
bool publish(const char *topic, uint8_t val); bool publish(const char *topic, uint8_t val, bool retain = false);
bool publish(const char *topic, int8_t val); bool publish(const char *topic, int8_t val, bool retain = false);
bool publish(const char *topic, uint32_t val); bool publish(const char *topic, uint32_t val, bool retain = false);
bool publish(const char *topic, uint16_t val); bool publish(const char *topic, uint16_t val, bool retain = false);
bool publish(const char *topic, bool val); bool publish(const char *topic, bool val, bool retain = false);
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

@ -54,6 +54,10 @@ void Network::loop() {
this->emitSockets(); this->emitSockets();
if(!this->connected()) return; if(!this->connected()) return;
} }
if(this->connected() && millis() - this->lastMDNS > 60000) {
if(this->lastMDNS != 0) MDNS.setInstanceName(settings.hostname);
this->lastMDNS = millis();
}
sockEmit.loop(); sockEmit.loop();
if(settings.ssdpBroadcast) { if(settings.ssdpBroadcast) {
if(!SSDP.isStarted) SSDP.begin(); if(!SSDP.isStarted) SSDP.begin();

View file

@ -5,6 +5,7 @@
class Network { class Network {
protected: protected:
unsigned long lastEmit = 0; unsigned long lastEmit = 0;
unsigned long lastMDNS = 0;
int lastRSSI = 0; int lastRSSI = 0;
int lastChannel = 0; int lastChannel = 0;
int linkSpeed = 0; int linkSpeed = 0;

253
Somfy.cpp
View file

@ -447,7 +447,17 @@ SomfyGroup *SomfyShadeController::findGroupByRemoteAddress(uint32_t address) {
} }
return nullptr; return nullptr;
} }
void SomfyShadeController::updateGroupFlags() {
for(uint8_t i = 0; i < SOMFY_MAX_GROUPS; i++) {
SomfyGroup *group = &this->groups[i];
if(group && group->getGroupId() != 255) {
uint8_t flags = group->flags;
group->updateFlags();
if(flags != group->flags)
group->emitState();
}
}
}
bool SomfyShadeController::loadLegacy() { bool SomfyShadeController::loadLegacy() {
Serial.println("Loading Legacy shades using NVS"); Serial.println("Loading Legacy shades using NVS");
pref.begin("Shades", true); pref.begin("Shades", true);
@ -729,22 +739,64 @@ bool SomfyShade::unlinkRemote(uint32_t address) {
return false; return false;
} }
bool SomfyGroup::unlinkShade(uint8_t shadeId) { bool SomfyGroup::unlinkShade(uint8_t shadeId) {
bool removed = false;
for(uint8_t i = 0; i < SOMFY_MAX_GROUPED_SHADES; i++) { for(uint8_t i = 0; i < SOMFY_MAX_GROUPED_SHADES; i++) {
if(this->linkedShades[i] == shadeId) { if(this->linkedShades[i] == shadeId) {
this->linkedShades[i] = 0; this->linkedShades[i] = 0;
somfy.commit(); removed = true;
return true; }
}
// Compress the linked shade ids so we can stop looking on the first 0
if(removed) {
this->compressLinkedShadeIds();
somfy.commit();
}
return removed;
}
void SomfyGroup::compressLinkedShadeIds() {
// [1,0,4,3,0,0,0] i:0,j:0
// [1,0,4,3,0,0,0] i:1,j:1
// [1,4,0,3,0,0,0] i:2,j:1
// [1,4,3,0,0,0,0] i:3,j:2
// [1,4,3,0,0,0,0] i:4,j:2
// [1,2,0,0,3,0,0] i:0,j:0
// [1,2,0,0,3,0,0] i:1,j:1
// [1,2,0,0,3,0,0] i:2,j:2
// [1,2,0,0,3,0,0] i:3,j:2
// [1,2,3,0,0,0,0] i:4,j:2
// [1,2,3,0,0,0,0] i:5,j:3
for(uint8_t i = 0, j = 0; i < SOMFY_MAX_GROUPED_SHADES; i++) {
if(this->linkedShades[i] != 0) {
if(i != j) {
this->linkedShades[j] = this->linkedShades[i];
this->linkedShades[i] = 0;
}
j++;
} }
} }
return false;
} }
bool SomfyGroup::hasShadeId(uint8_t shadeId) { bool SomfyGroup::hasShadeId(uint8_t shadeId) {
for(uint8_t i = 0; i < SOMFY_MAX_GROUPED_SHADES; i++) { for(uint8_t i = 0; i < SOMFY_MAX_GROUPED_SHADES; i++) {
if(this->linkedShades[i] == 0) break;
if(this->linkedShades[i] == shadeId) return true; if(this->linkedShades[i] == shadeId) return true;
} }
return false; return false;
} }
bool SomfyShade::isAtTarget() { return this->currentPos == this->target && this->currentTiltPos == this->tiltTarget; } bool SomfyShade::isAtTarget() { return this->currentPos == this->target && this->currentTiltPos == this->tiltTarget; }
bool SomfyRemote::hasSunSensor() { return (this->flags & static_cast<uint8_t>(somfy_flags_t::SunSensor)) > 0;}
void SomfyRemote::setSunSensor(bool bHasSensor ) { bHasSensor ? this->flags |= static_cast<uint8_t>(somfy_flags_t::SunSensor) : this->flags &= ~(static_cast<uint8_t>(somfy_flags_t::SunSensor)); }
void SomfyGroup::updateFlags() {
this->flags = 0;
for(uint8_t i = 0; i < SOMFY_MAX_GROUPED_SHADES; i++) {
if(this->linkedShades[i] != 0) {
SomfyShade *shade = somfy.getShadeById(this->linkedShades[i]);
if(shade) this->flags |= shade->flags;
}
else break;
}
}
bool SomfyShade::isInGroup() { bool SomfyShade::isInGroup() {
if(this->getShadeId() == 255) return false; if(this->getShadeId() == 255) return false;
for(uint8_t i = 0; i < SOMFY_MAX_GROUPS; i++) { for(uint8_t i = 0; i < SOMFY_MAX_GROUPS; i++) {
@ -1138,34 +1190,64 @@ void SomfyShade::publish() {
snprintf(topic, sizeof(topic), "shades/%u/tiltTarget", this->shadeId); snprintf(topic, sizeof(topic), "shades/%u/tiltTarget", this->shadeId);
mqtt.publish(topic, this->transformPosition(this->tiltTarget)); mqtt.publish(topic, this->transformPosition(this->tiltTarget));
} }
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)); const uint8_t isSunny = !!(this->flags & static_cast<uint8_t>(somfy_flags_t::Sunny));
const uint8_t isSunny = !!(this->flags & static_cast<uint8_t>(somfy_flags_t::Sunny)); const uint8_t isWindy = !!(this->flags & static_cast<uint8_t>(somfy_flags_t::Windy));
const uint8_t isWindy = !!(this->flags & static_cast<uint8_t>(somfy_flags_t::Windy)); snprintf(topic, sizeof(topic), "shades/%u/sunSensor", this->shadeId);
mqtt.publish(topic, this->hasSunSensor());
snprintf(topic, sizeof(topic), "shades/%u/sunFlag", this->shadeId); snprintf(topic, sizeof(topic), "shades/%u/sunFlag", this->shadeId);
mqtt.publish(topic, sunFlag); mqtt.publish(topic, sunFlag);
snprintf(topic, sizeof(topic), "shades/%u/sunny", this->shadeId); snprintf(topic, sizeof(topic), "shades/%u/sunny", this->shadeId);
mqtt.publish(topic, isSunny); mqtt.publish(topic, isSunny);
snprintf(topic, sizeof(topic), "shades/%u/windy", this->shadeId); snprintf(topic, sizeof(topic), "shades/%u/windy", this->shadeId);
mqtt.publish(topic, isWindy); mqtt.publish(topic, isWindy);
}
} }
} }
void SomfyGroup::publish() {
if(mqtt.connected()) {
char topic[32];
snprintf(topic, sizeof(topic), "groups/%u/groupId", this->groupId);
mqtt.publish(topic, this->groupId);
snprintf(topic, sizeof(topic), "groups/%u/name", this->groupId);
mqtt.publish(topic, this->name);
snprintf(topic, sizeof(topic), "groups/%u/remoteAddress", this->groupId);
mqtt.publish(topic, this->getRemoteAddress());
snprintf(topic, sizeof(topic), "groups/%u/direction", this->groupId);
mqtt.publish(topic, this->direction);
snprintf(topic, sizeof(topic), "groups/%u/lastRollingCode", this->groupId);
mqtt.publish(topic, this->lastRollingCode);
snprintf(topic, sizeof(topic), "groups/%u/groupType", this->groupId);
mqtt.publish(topic, static_cast<uint8_t>(this->groupType));
snprintf(topic, sizeof(topic), "groups/%u/flags", this->groupId);
mqtt.publish(topic, this->flags);
const uint8_t sunFlag = !!(this->flags & static_cast<uint8_t>(somfy_flags_t::SunFlag));
const uint8_t isSunny = !!(this->flags & static_cast<uint8_t>(somfy_flags_t::Sunny));
const uint8_t isWindy = !!(this->flags & static_cast<uint8_t>(somfy_flags_t::Windy));
snprintf(topic, sizeof(topic), "groups/%u/sunSensor", this->groupId);
mqtt.publish(topic, this->hasSunSensor());
snprintf(topic, sizeof(topic), "groups/%u/sunFlag", this->groupId);
mqtt.publish(topic, sunFlag);
snprintf(topic, sizeof(topic), "groups/%u/sunny", this->groupId);
mqtt.publish(topic, isSunny);
snprintf(topic, sizeof(topic), "groups/%u/windy", this->groupId);
mqtt.publish(topic, isWindy);
}
}
void SomfyShade::emitState(const char *evt) { this->emitState(255, evt); } void SomfyShade::emitState(const char *evt) { this->emitState(255, evt); }
void SomfyShade::emitState(uint8_t num, const char *evt) { void SomfyShade::emitState(uint8_t num, const char *evt) {
char buf[420]; 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,\"flipCommands\":%s,\"flipPosition\":%s,\"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,\"sunSensor\":%s}",
this->shadeId, static_cast<uint8_t>(this->shadeType), this->getRemoteAddress(), this->name, this->direction, 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->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->transformPosition(this->tiltTarget), this->transformPosition(this->currentTiltPos),
this->flipCommands ? "true" : "false", this->flipPosition ? "true": "false", this->flags); this->flipCommands ? "true" : "false", this->flipPosition ? "true": "false", this->flags, this->hasSunSensor() ? "true" : "false");
else else
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}", 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,\"sunSensor\":%s}",
this->shadeId, static_cast<uint8_t>(this->shadeType), this->getRemoteAddress(), this->name, this->direction, 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->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); static_cast<uint8_t>(this->tiltType), this->flipCommands ? "true" : "false", this->flipPosition ? "true": "false", this->flags, this->hasSunSensor() ? "true" : "false");
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()) {
@ -1186,6 +1268,9 @@ void SomfyShade::emitState(uint8_t num, const char *evt) {
mqtt.publish(topic, this->transformPosition(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));
snprintf(topic, sizeof(topic), "shades/%u/sunSensor", this->shadeId);
mqtt.publish(topic, this->hasSunSensor());
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, this->transformPosition(this->myTiltPos)); mqtt.publish(topic, this->transformPosition(this->myTiltPos));
@ -1194,18 +1279,16 @@ void SomfyShade::emitState(uint8_t num, const char *evt) {
snprintf(topic, sizeof(topic), "shades/%u/tiltTarget", this->shadeId); snprintf(topic, sizeof(topic), "shades/%u/tiltTarget", this->shadeId);
mqtt.publish(topic, this->transformPosition(this->tiltTarget)); mqtt.publish(topic, this->transformPosition(this->tiltTarget));
} }
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)); const uint8_t isSunny = !!(this->flags & static_cast<uint8_t>(somfy_flags_t::Sunny));
const uint8_t isSunny = !!(this->flags & static_cast<uint8_t>(somfy_flags_t::Sunny)); const uint8_t isWindy = !!(this->flags & static_cast<uint8_t>(somfy_flags_t::Windy));
const uint8_t isWindy = !!(this->flags & static_cast<uint8_t>(somfy_flags_t::Windy));
snprintf(topic, sizeof(topic), "shades/%u/sunFlag", this->shadeId); snprintf(topic, sizeof(topic), "shades/%u/sunFlag", this->shadeId);
mqtt.publish(topic, sunFlag); mqtt.publish(topic, sunFlag);
snprintf(topic, sizeof(topic), "shades/%u/sunny", this->shadeId); snprintf(topic, sizeof(topic), "shades/%u/sunny", this->shadeId);
mqtt.publish(topic, isSunny); mqtt.publish(topic, isSunny);
snprintf(topic, sizeof(topic), "shades/%u/windy", this->shadeId); snprintf(topic, sizeof(topic), "shades/%u/windy", this->shadeId);
mqtt.publish(topic, isWindy); mqtt.publish(topic, isWindy);
}
} }
} }
void SomfyShade::emitCommand(somfy_commands cmd, const char *source, uint32_t sourceAddress, const char *evt) { this->emitCommand(255, cmd, source, sourceAddress, evt); } void SomfyShade::emitCommand(somfy_commands cmd, const char *source, uint32_t sourceAddress, const char *evt) { this->emitCommand(255, cmd, source, sourceAddress, evt); }
@ -1229,34 +1312,35 @@ void SomfyGroup::emitState(const char *evt) { this->emitState(255, evt); }
void SomfyGroup::emitState(uint8_t num, const char *evt) { void SomfyGroup::emitState(uint8_t num, const char *evt) {
ClientSocketEvent e(evt); ClientSocketEvent e(evt);
char buf[30]; char buf[30];
uint8_t flags = 0;
snprintf(buf, sizeof(buf), "{\"groupId\":%d,", this->groupId); snprintf(buf, sizeof(buf), "{\"groupId\":%d,", this->groupId);
e.appendMessage(buf); e.appendMessage(buf);
snprintf(buf, sizeof(buf), "\"remoteAddress\":%d,", this->getRemoteAddress()); snprintf(buf, sizeof(buf), "\"remoteAddress\":%d,", this->getRemoteAddress());
e.appendMessage(buf); e.appendMessage(buf);
snprintf(buf, sizeof(buf), "\"name\":\"%s\",", this->name); snprintf(buf, sizeof(buf), "\"name\":\"%s\",", this->name);
e.appendMessage(buf); e.appendMessage(buf);
snprintf(buf, sizeof(buf), "\"sunSensor\":%s,", this->hasSunSensor() ? "true" : "false");
e.appendMessage(buf);
snprintf(buf, sizeof(buf), "\"shades\":["); snprintf(buf, sizeof(buf), "\"shades\":[");
e.appendMessage(buf); e.appendMessage(buf);
for(uint8_t i = 0; i < SOMFY_MAX_GROUPED_SHADES; i++) { for(uint8_t i = 0; i < SOMFY_MAX_GROUPED_SHADES; i++) {
if(this->linkedShades[i] != 255) { if(this->linkedShades[i] != 255) {
snprintf(buf, sizeof(buf), "%s%d", i != 0 ? "," : "", this->linkedShades[i]); if(this->linkedShades[i] != 0) {
e.appendMessage(buf); SomfyShade *shade = somfy.getShadeById(this->linkedShades[i]);
if(shade) {
flags |= shade->flags;
snprintf(buf, sizeof(buf), "%s%d", i != 0 ? "," : "", this->linkedShades[i]);
e.appendMessage(buf);
}
}
} }
} }
e.appendMessage("]}"); snprintf(buf, sizeof(buf), "],\"flags\":%d}", flags);
e.appendMessage(buf);
if(num >= 255) sockEmit.sendToClients(&e); if(num >= 255) sockEmit.sendToClients(&e);
else sockEmit.sendToClient(num, &e); else sockEmit.sendToClient(num, &e);
if(mqtt.connected()) { this->publish();
char topic[32];
snprintf(topic, sizeof(topic), "groups/%u/type", this->groupId);
mqtt.publish(topic, static_cast<uint8_t>(this->groupType));
snprintf(topic, sizeof(topic), "groups/%u/remoteAddress", this->groupId);
mqtt.publish(topic, this->getRemoteAddress());
snprintf(topic, sizeof(topic), "groups/%u/lastRollingCode", this->groupId);
mqtt.publish(topic, this->lastRollingCode);
snprintf(topic, sizeof(topic), "groups/%u/direction", this->groupId);
mqtt.publish(topic, this->direction);
}
} }
int8_t SomfyShade::transformPosition(float fpos) { return static_cast<int8_t>(this->flipPosition && fpos >= 0.00f ? floor(100.0f - fpos) : floor(fpos)); } int8_t SomfyShade::transformPosition(float fpos) { return static_cast<int8_t>(this->flipPosition && fpos >= 0.00f ? floor(100.0f - fpos) : floor(fpos)); }
bool SomfyShade::isIdle() { return this->direction == 0 && this->tiltDirection == 0; } bool SomfyShade::isIdle() { return this->direction == 0 && this->tiltDirection == 0; }
@ -1390,12 +1474,10 @@ void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) {
const bool wasSunny = prevFlags & static_cast<uint8_t>(somfy_flags_t::Sunny); const bool wasSunny = prevFlags & static_cast<uint8_t>(somfy_flags_t::Sunny);
const bool wasWindy = prevFlags & static_cast<uint8_t>(somfy_flags_t::Windy); const bool wasWindy = prevFlags & static_cast<uint8_t>(somfy_flags_t::Windy);
const uint16_t status = frame.rollingCode << 4; const uint16_t status = frame.rollingCode << 4;
if (status & static_cast<uint8_t>(somfy_flags_t::Sunny)) if (status & static_cast<uint8_t>(somfy_flags_t::Sunny))
this->flags |= static_cast<uint8_t>(somfy_flags_t::Sunny); this->flags |= static_cast<uint8_t>(somfy_flags_t::Sunny);
else else
this->flags &= ~(static_cast<uint8_t>(somfy_flags_t::Sunny)); this->flags &= ~(static_cast<uint8_t>(somfy_flags_t::Sunny));
if (status & static_cast<uint8_t>(somfy_flags_t::Windy)) if (status & static_cast<uint8_t>(somfy_flags_t::Windy))
this->flags |= static_cast<uint8_t>(somfy_flags_t::Windy); this->flags |= static_cast<uint8_t>(somfy_flags_t::Windy);
else else
@ -1404,11 +1486,8 @@ void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) {
this->flags |= static_cast<uint8_t>(somfy_flags_t::DemoMode); this->flags |= static_cast<uint8_t>(somfy_flags_t::DemoMode);
else else
this->flags &= ~(static_cast<uint8_t>(somfy_flags_t::DemoMode)); this->flags &= ~(static_cast<uint8_t>(somfy_flags_t::DemoMode));
const bool isSunny = this->flags & static_cast<uint8_t>(somfy_flags_t::Sunny); const bool isSunny = this->flags & static_cast<uint8_t>(somfy_flags_t::Sunny);
const bool isWindy = this->flags & static_cast<uint8_t>(somfy_flags_t::Windy); const bool isWindy = this->flags & static_cast<uint8_t>(somfy_flags_t::Windy);
if (isSunny) if (isSunny)
{ {
this->noSunStart = 0; this->noSunStart = 0;
@ -1419,12 +1498,10 @@ void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) {
this->sunStart = 0; this->sunStart = 0;
this->sunDone = true; this->sunDone = true;
} }
if (isWindy) if (isWindy)
{ {
this->noWindStart = 0; this->noWindStart = 0;
this->noWindDone = true; this->noWindDone = true;
this->windLast = curTime; this->windLast = curTime;
} }
else else
@ -1432,38 +1509,32 @@ void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) {
this->windStart = 0; this->windStart = 0;
this->windDone = true; this->windDone = true;
} }
if (isSunny && !wasSunny) if (isSunny && !wasSunny)
{ {
this->sunStart = curTime; this->sunStart = curTime;
this->sunDone = false; this->sunDone = false;
Serial.printf("[%u] Sun -> start\r\n", this->shadeId); Serial.printf("[%u] Sun -> start\r\n", this->shadeId);
} }
else if (!isSunny && wasSunny) else if (!isSunny && wasSunny)
{ {
this->noSunStart = curTime; this->noSunStart = curTime;
this->noSunDone = false; this->noSunDone = false;
Serial.printf("[%u] No Sun -> start\r\n", this->shadeId); Serial.printf("[%u] No Sun -> start\r\n", this->shadeId);
} }
if (isWindy && !wasWindy) if (isWindy && !wasWindy)
{ {
this->windStart = curTime; this->windStart = curTime;
this->windDone = false; this->windDone = false;
Serial.printf("[%u] Wind -> start\r\n", this->shadeId); Serial.printf("[%u] Wind -> start\r\n", this->shadeId);
} }
else if (!isWindy && wasWindy) else if (!isWindy && wasWindy)
{ {
this->noWindStart = curTime; this->noWindStart = curTime;
this->noWindDone = false; this->noWindDone = false;
Serial.printf("[%u] No Wind -> start\r\n", this->shadeId); Serial.printf("[%u] No Wind -> start\r\n", this->shadeId);
} }
this->emitState(); this->emitState();
somfy.updateGroupFlags();
} }
break; break;
case somfy_commands::Prog: case somfy_commands::Prog:
@ -1479,6 +1550,7 @@ void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) {
somfy.isDirty = true; somfy.isDirty = true;
this->emitState(); this->emitState();
this->emitCommand(cmd, internal ? "internal" : "remote", frame.remoteAddress); this->emitCommand(cmd, internal ? "internal" : "remote", frame.remoteAddress);
somfy.updateGroupFlags();
break; break;
case somfy_commands::SunFlag: case somfy_commands::SunFlag:
{ {
@ -1495,6 +1567,7 @@ void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) {
somfy.isDirty = true; somfy.isDirty = true;
this->emitState(); this->emitState();
this->emitCommand(cmd, internal ? "internal" : "remote", frame.remoteAddress); this->emitCommand(cmd, internal ? "internal" : "remote", frame.remoteAddress);
somfy.updateGroupFlags();
} }
break; break;
case somfy_commands::Up: case somfy_commands::Up:
@ -1662,6 +1735,34 @@ void SomfyShade::processInternalCommand(somfy_commands cmd, uint8_t repeat) {
this->target = min(100.0f, this->currentPos + (100.0f/(static_cast<float>(this->downTime/static_cast<float>(this->stepSize))))); this->target = min(100.0f, this->currentPos + (100.0f/(static_cast<float>(this->downTime/static_cast<float>(this->stepSize)))));
} }
break; break;
case somfy_commands::Flag:
if(this->hasSunSensor()) {
this->flags &= ~(static_cast<uint8_t>(somfy_flags_t::SunFlag));
somfy.isDirty = true;
this->emitState();
}
else
Serial.printf("Shade does not have sensor %d\n", this->flags);
break;
case somfy_commands::SunFlag:
if(this->hasSunSensor()) {
const bool isWindy = this->flags & static_cast<uint8_t>(somfy_flags_t::Windy);
this->flags |= static_cast<uint8_t>(somfy_flags_t::SunFlag);
if (!isWindy)
{
const bool isSunny = this->flags & static_cast<uint8_t>(somfy_flags_t::Sunny);
if (isSunny && this->sunDone)
this->target = this->myPos >= 0 ? this->myPos : 100.0f;
else if (!isSunny && this->noSunDone)
this->target = 0.0f;
}
somfy.isDirty = true;
this->emitState();
}
else
Serial.printf("Shade does not have sensor %d\n", this->flags);
break;
default: default:
dir = 0; dir = 0;
break; break;
@ -1838,19 +1939,19 @@ void SomfyGroup::sendCommand(somfy_commands cmd, uint8_t repeat) {
this->direction = 1; this->direction = 1;
break; break;
} }
this->emitState();
for(uint8_t i = 0; i < SOMFY_MAX_GROUPED_SHADES; i++) { for(uint8_t i = 0; i < SOMFY_MAX_GROUPED_SHADES; i++) {
if(this->linkedShades[i] != 0) { if(this->linkedShades[i] != 0) {
SomfyShade * shade = somfy.getShadeById(this->linkedShades[i]); SomfyShade *shade = somfy.getShadeById(this->linkedShades[i]);
if(shade) { if(shade) {
shade->processInternalCommand(cmd, repeat); shade->processInternalCommand(cmd, repeat);
shade->emitCommand(cmd, "group", this->getRemoteAddress()); shade->emitCommand(cmd, "group", this->getRemoteAddress());
} }
} }
} }
this->updateFlags();
this->emitState();
} }
void SomfyShade::sendTiltCommand(somfy_commands cmd) { void SomfyShade::sendTiltCommand(somfy_commands cmd) {
if(cmd == somfy_commands::Up) { if(cmd == somfy_commands::Up) {
SomfyRemote::sendCommand(cmd, this->tiltType == tilt_types::tiltmotor ? TILT_REPEATS : 1); SomfyRemote::sendCommand(cmd, this->tiltType == tilt_types::tiltmotor ? TILT_REPEATS : 1);
@ -1963,6 +2064,7 @@ bool SomfyShade::fromJSON(JsonObject &obj) {
if(obj.containsKey("hasTilt")) this->tiltType = static_cast<bool>(obj["hasTilt"]) ? tilt_types::none : tilt_types::tiltmotor; if(obj.containsKey("hasTilt")) this->tiltType = static_cast<bool>(obj["hasTilt"]) ? tilt_types::none : tilt_types::tiltmotor;
if(obj.containsKey("bitLength")) this->bitLength = obj["bitLength"]; if(obj.containsKey("bitLength")) this->bitLength = obj["bitLength"];
if(obj.containsKey("proto")) this->proto = static_cast<radio_proto>(obj["proto"].as<uint8_t>()); if(obj.containsKey("proto")) this->proto = static_cast<radio_proto>(obj["proto"].as<uint8_t>());
if(obj.containsKey("sunSensor")) this->setSunSensor(obj["sunSensor"]);
if(obj.containsKey("shadeType")) { if(obj.containsKey("shadeType")) {
if(obj["shadeType"].is<const char *>()) { if(obj["shadeType"].is<const char *>()) {
if(strncmp(obj["shadeType"].as<const char *>(), "roller", 7) == 0) if(strncmp(obj["shadeType"].as<const char *>(), "roller", 7) == 0)
@ -1973,6 +2075,8 @@ bool SomfyShade::fromJSON(JsonObject &obj) {
this->shadeType = shade_types::blind; this->shadeType = shade_types::blind;
else if(strncmp(obj["shadeType"].as<const char *>(), "awning", 7) == 0) else if(strncmp(obj["shadeType"].as<const char *>(), "awning", 7) == 0)
this->shadeType = shade_types::awning; this->shadeType = shade_types::awning;
else if(strncmp(obj["shadeType"].as<const char *>(), "shutter", 8) == 0)
this->shadeType = shade_types::shutter;
} }
else { else {
this->shadeType = static_cast<shade_types>(obj["shadeType"].as<uint8_t>()); this->shadeType = static_cast<shade_types>(obj["shadeType"].as<uint8_t>());
@ -2019,6 +2123,7 @@ bool SomfyShade::toJSONRef(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["sunSensor"] = this->hasSunSensor();
SomfyRemote::toJSON(obj); SomfyRemote::toJSON(obj);
return true; return true;
} }
@ -2054,6 +2159,8 @@ bool SomfyShade::toJSON(JsonObject &obj) {
obj["flipCommands"] = this->flipCommands; obj["flipCommands"] = this->flipCommands;
obj["flipPosition"] = this->flipPosition; obj["flipPosition"] = this->flipPosition;
obj["inGroup"] = this->isInGroup(); obj["inGroup"] = this->isInGroup();
obj["sunSensor"] = this->hasSunSensor();
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++) {
@ -2070,6 +2177,7 @@ bool SomfyGroup::fromJSON(JsonObject &obj) {
if(obj.containsKey("remoteAddress")) this->setRemoteAddress(obj["remoteAddress"]); if(obj.containsKey("remoteAddress")) this->setRemoteAddress(obj["remoteAddress"]);
if(obj.containsKey("bitLength")) this->bitLength = obj["bitLength"]; if(obj.containsKey("bitLength")) this->bitLength = obj["bitLength"];
if(obj.containsKey("proto")) this->proto = static_cast<radio_proto>(obj["proto"].as<uint8_t>()); if(obj.containsKey("proto")) this->proto = static_cast<radio_proto>(obj["proto"].as<uint8_t>());
if(obj.containsKey("sunSensor")) obj["sunSensor"] = this->hasSunSensor();
if(obj.containsKey("linkedShades")) { if(obj.containsKey("linkedShades")) {
uint8_t linkedShades[SOMFY_MAX_GROUPED_SHADES]; uint8_t linkedShades[SOMFY_MAX_GROUPED_SHADES];
memset(linkedShades, 0x00, sizeof(linkedShades)); memset(linkedShades, 0x00, sizeof(linkedShades));
@ -2082,12 +2190,15 @@ bool SomfyGroup::fromJSON(JsonObject &obj) {
return true; return true;
} }
bool SomfyGroup::toJSON(JsonObject &obj) { bool SomfyGroup::toJSON(JsonObject &obj) {
this->updateFlags();
obj["groupId"] = this->getGroupId(); obj["groupId"] = this->getGroupId();
obj["name"] = this->name; obj["name"] = this->name;
obj["remoteAddress"] = this->m_remoteAddress; obj["remoteAddress"] = this->m_remoteAddress;
obj["lastRollingCode"] = this->lastRollingCode; obj["lastRollingCode"] = this->lastRollingCode;
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["sunSensor"] = this->hasSunSensor();
obj["flags"] = this->flags;
SomfyRemote::toJSON(obj); SomfyRemote::toJSON(obj);
JsonArray arr = obj.createNestedArray("linkedShades"); JsonArray arr = obj.createNestedArray("linkedShades");
for(uint8_t i = 0; i < SOMFY_MAX_GROUPED_SHADES; i++) { for(uint8_t i = 0; i < SOMFY_MAX_GROUPED_SHADES; i++) {
@ -2126,15 +2237,25 @@ void SomfyShadeController::emitState(uint8_t num) {
} }
} }
void SomfyShadeController::publish() { void SomfyShadeController::publish() {
StaticJsonDocument<128> doc; this->updateGroupFlags();
JsonArray arr = doc.to<JsonArray>(); StaticJsonDocument<128> docShades;
StaticJsonDocument<128> docGroups;
JsonArray arrShades = docShades.to<JsonArray>();
JsonArray arrGroups = docGroups.to<JsonArray>();
for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) { for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) {
SomfyShade *shade = &this->shades[i]; SomfyShade *shade = &this->shades[i];
if(shade->getShadeId() == 255) continue; if(shade->getShadeId() == 255) continue;
arr.add(shade->getShadeId()); arrShades.add(shade->getShadeId());
shade->publish(); shade->publish();
} }
mqtt.publish("shades", arr); mqtt.publish("shades", arrShades);
for(uint8_t i = 0; i < SOMFY_MAX_GROUPS; i++) {
SomfyGroup *group = &this->groups[i];
if(group->getGroupId() == 255) continue;
arrGroups.add(group->getGroupId());
group->publish();
}
mqtt.publish("groups", arrGroups);
} }
uint8_t SomfyShadeController::getNextShadeId() { uint8_t SomfyShadeController::getNextShadeId() {
// There is no shortcut for this since the deletion of // There is no shortcut for this since the deletion of

View file

@ -137,6 +137,7 @@ struct somfy_tx_queue_t {
enum class somfy_flags_t : byte { enum class somfy_flags_t : byte {
SunFlag = 0x01, SunFlag = 0x01,
SunSensor = 0x02,
DemoMode = 0x04, DemoMode = 0x04,
Windy = 0x10, Windy = 0x10,
Sunny = 0x20 Sunny = 0x20
@ -175,7 +176,6 @@ class SomfyRemote {
public: public:
radio_proto proto = radio_proto::RTS; radio_proto proto = radio_proto::RTS;
bool flipCommands = 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;}
@ -185,6 +185,8 @@ class SomfyRemote {
virtual uint16_t getNextRollingCode(); virtual uint16_t getNextRollingCode();
virtual uint16_t setRollingCode(uint16_t code); virtual uint16_t setRollingCode(uint16_t code);
uint16_t lastRollingCode = 0; uint16_t lastRollingCode = 0;
bool hasSunSensor();
void setSunSensor(bool bHasSensor);
virtual void sendCommand(somfy_commands cmd, uint8_t repeat = 1); virtual void sendCommand(somfy_commands cmd, uint8_t repeat = 1);
somfy_commands transformCommand(somfy_commands cmd); somfy_commands transformCommand(somfy_commands cmd);
}; };
@ -288,6 +290,9 @@ class SomfyGroup : public SomfyRemote {
bool linkShade(uint8_t shadeId); bool linkShade(uint8_t shadeId);
bool unlinkShade(uint8_t shadeId); bool unlinkShade(uint8_t shadeId);
bool hasShadeId(uint8_t shadeId); bool hasShadeId(uint8_t shadeId);
void compressLinkedShadeIds();
void publish();
void updateFlags();
void emitState(const char *evt = "groupState"); void emitState(const char *evt = "groupState");
void emitState(uint8_t num, const char *evt = "groupState"); void emitState(uint8_t num, const char *evt = "groupState");
void sendCommand(somfy_commands cmd, uint8_t repeat = 1); void sendCommand(somfy_commands cmd, uint8_t repeat = 1);
@ -420,6 +425,7 @@ class SomfyShadeController {
bool toJSONGroups(JsonArray &arr); bool toJSONGroups(JsonArray &arr);
uint8_t shadeCount(); uint8_t shadeCount();
uint8_t groupCount(); uint8_t groupCount();
void updateGroupFlags();
SomfyShade * getShadeById(uint8_t shadeId); SomfyShade * getShadeById(uint8_t shadeId);
SomfyGroup * getGroupById(uint8_t groupId); SomfyGroup * getGroupById(uint8_t groupId);
SomfyShade * findShadeByRemoteAddress(uint32_t address); SomfyShade * findShadeByRemoteAddress(uint32_t address);

Binary file not shown.

Binary file not shown.

View file

@ -1 +1 @@
2.0.3 2.1.0

View file

@ -3,11 +3,11 @@
<head> <head>
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="UTF-8"> <meta charset="UTF-8">
<link rel="stylesheet" href="main.css?v=2.0.3" type="text/css" /> <link rel="stylesheet" href="main.css?v=2.1.0" type="text/css" />
<link rel="stylesheet" href="widgets.css?v=2.0.3" type="text/css" /> <link rel="stylesheet" href="widgets.css?v=2.1.0" type="text/css" />
<link rel="stylesheet" href="icons.css?v=2.0.3" type="text/css" /> <link rel="stylesheet" href="icons.css?v=2.1.0" type="text/css" />
<link rel="icon" type="image/png" href="favicon.png" /> <link rel="icon" type="image/png" href="favicon.png" />
<script type="text/javascript" src="index.js?v=2.0.3"></script> <script type="text/javascript" src="index.js?v=2.1.0"></script>
</head> </head>
<body> <body>
<div id="divContainer" class="container main" data-auth="false"> <div id="divContainer" class="container main" data-auth="false">
@ -368,6 +368,12 @@
</label> </label>
</div> </div>
</div> </div>
<div style="margin-top:-10px;">
<div class="field-group">
<input id="cbHasSunsensor" name="hasSunSensor" data-bind="sunSensor" type="checkbox" style="" />
<label for="cbHasSunSensor" style="display:block;font-size:1em;margin-top:0px;margin-left:7px;display:inline-block;">Has Sun Sensor</label>
</div>
</div>
<div style="margin-top:-10px;"> <div style="margin-top:-10px;">
<div class="field-group"> <div class="field-group">
<input id="cbFlipCommands" name="flipCommands" data-bind="flipCommands" type="checkbox" style="" /> <input id="cbFlipCommands" name="flipCommands" data-bind="flipCommands" type="checkbox" style="" />

View file

@ -467,6 +467,9 @@ async function initSockets() {
case 'remoteFrame': case 'remoteFrame':
somfy.procRemoteFrame(msg); somfy.procRemoteFrame(msg);
break; break;
case 'groupState':
somfy.procGroupState(msg);
break;
case 'shadeState': case 'shadeState':
somfy.procShadeState(msg); somfy.procShadeState(msg);
break; break;
@ -1194,7 +1197,7 @@ var security = new Security();
class General { class General {
initialized = false; initialized = false;
appVersion = 'v2.0.3'; appVersion = 'v2.1.0';
reloadApp = false; reloadApp = false;
init() { init() {
if (this.initialized) return; if (this.initialized) return;
@ -1973,7 +1976,7 @@ class Somfy {
divCtl += '</div>'; divCtl += '</div>';
divCtl += `<div class="shadectl-buttons">`; divCtl += `<div class="shadectl-buttons">`;
divCtl += `<div class="button-sunflag cmd-button" data-cmd="sunflag" data-shadeid="${shade.shadeId}" data-on="${shade.flags & 0x01 ? 'true' : 'false'}" style="${shade.shadeType !== 3 ? 'display:none' : ''}"><i class="icss-sun-c"></i><i class="icss-sun-o"></i></div>`; divCtl += `<div class="button-sunflag cmd-button" data-cmd="sunflag" data-shadeid="${shade.shadeId}" data-on="${shade.flags & 0x01 ? 'true' : 'false'}" style="${!shade.sunSensor ? 'display:none' : ''}"><i class="icss-sun-c"></i><i class="icss-sun-o"></i></div>`;
divCtl += `<div class="button-outline cmd-button" data-cmd="up" data-shadeid="${shade.shadeId}"><i class="icss-somfy-up"></i></div>`; divCtl += `<div class="button-outline cmd-button" data-cmd="up" data-shadeid="${shade.shadeId}"><i class="icss-somfy-up"></i></div>`;
divCtl += `<div class="button-outline cmd-button my-button" data-cmd="my" data-shadeid="${shade.shadeId}" style="font-size:2em;padding:10px;"><span>my</span></div>`; divCtl += `<div class="button-outline cmd-button my-button" data-cmd="my" data-shadeid="${shade.shadeId}" style="font-size:2em;padding:10px;"><span>my</span></div>`;
divCtl += `<div class="button-outline cmd-button" data-cmd="down" data-shadeid="${shade.shadeId}"><i class="icss-somfy-down" style="margin-top:-4px;"></i></div>`; divCtl += `<div class="button-outline cmd-button" data-cmd="down" data-shadeid="${shade.shadeId}"><i class="icss-somfy-down" style="margin-top:-4px;"></i></div>`;
@ -1992,6 +1995,7 @@ class Somfy {
let cmd = event.currentTarget.getAttribute('data-cmd'); let cmd = event.currentTarget.getAttribute('data-cmd');
let shadeId = parseInt(event.currentTarget.getAttribute('data-shadeid'), 10); let shadeId = parseInt(event.currentTarget.getAttribute('data-shadeid'), 10);
if (this.btnTimer) { if (this.btnTimer) {
console.log({ timer: true, isOn: event.currentTarget.getAttribute('data-on'), cmd: cmd });
clearTimeout(this.btnTimer); clearTimeout(this.btnTimer);
this.btnTimer = null; this.btnTimer = null;
if (new Date().getTime() - this.btnDown > 2000) event.preventDefault(); if (new Date().getTime() - this.btnDown > 2000) event.preventDefault();
@ -2027,6 +2031,7 @@ class Somfy {
}, 2000); }, 2000);
} }
} }
else if (cmd === 'sunflag') return;
else if (makeBool(elShade.getAttribute('data-tilt'))) { else if (makeBool(elShade.getAttribute('data-tilt'))) {
this.btnTimer = setTimeout(() => { this.btnTimer = setTimeout(() => {
this.sendTiltCommand(shadeId, cmd); this.sendTiltCommand(shadeId, cmd);
@ -2090,6 +2095,7 @@ class Somfy {
} }
divCtl += '</div></div>'; divCtl += '</div></div>';
divCtl += `<div class="groupctl-buttons">`; divCtl += `<div class="groupctl-buttons">`;
divCtl += `<div class="button-sunflag cmd-button" data-cmd="sunflag" data-groupid="${group.groupId}" data-on="${group.flags & 0x01 ? 'true' : 'false'}" style="${!group.sunSensor ? 'display:none' : ''}"><i class="icss-sun-c"></i><i class="icss-sun-o"></i></div>`;
divCtl += `<div class="button-outline cmd-button" data-cmd="up" data-groupid="${group.groupId}"><i class="icss-somfy-up"></i></div>`; divCtl += `<div class="button-outline cmd-button" data-cmd="up" data-groupid="${group.groupId}"><i class="icss-somfy-up"></i></div>`;
divCtl += `<div class="button-outline cmd-button my-button" data-cmd="my" data-groupid="${group.groupId}" style="font-size:2em;padding:10px;"><span>my</span></div>`; divCtl += `<div class="button-outline cmd-button my-button" data-cmd="my" data-groupid="${group.groupId}" style="font-size:2em;padding:10px;"><span>my</span></div>`;
divCtl += `<div class="button-outline cmd-button" data-cmd="down" data-groupid="${group.groupId}"><i class="icss-somfy-down" style="margin-top:-4px;"></i></div>`; divCtl += `<div class="button-outline cmd-button" data-cmd="down" data-groupid="${group.groupId}"><i class="icss-somfy-down" style="margin-top:-4px;"></i></div>`;
@ -2104,9 +2110,16 @@ class Somfy {
btns[i].addEventListener('click', (event) => { btns[i].addEventListener('click', (event) => {
console.log(this); console.log(this);
console.log(event); console.log(event);
let cmd = event.currentTarget.getAttribute('data-cmd');
let groupId = parseInt(event.currentTarget.getAttribute('data-groupid'), 10); let groupId = parseInt(event.currentTarget.getAttribute('data-groupid'), 10);
this.sendGroupCommand(groupId, cmd); let cmd = event.currentTarget.getAttribute('data-cmd');
if (cmd === 'sunflag') {
if (makeBool(event.currentTarget.getAttribute('data-on')))
this.sendGroupCommand(groupId, 'flag');
else
this.sendGroupCommand(groupId, 'sunflag');
}
else
this.sendGroupCommand(groupId, cmd);
}, true); }, true);
} }
} }
@ -2246,6 +2259,14 @@ class Somfy {
sel.options[sel.options.length] = new Option(`GPIO-${i > 9 ? i.toString() : '0' + i.toString()}`, i, typeof opt !== 'undefined' && opt === i); sel.options[sel.options.length] = new Option(`GPIO-${i > 9 ? i.toString() : '0' + i.toString()}`, i, typeof opt !== 'undefined' && opt === i);
} }
} }
procGroupState(state) {
console.log(state);
let flags = document.querySelectorAll(`.button-sunflag[data-groupid="${state.groupId}"]`);
for (let i = 0; i < flags.length; i++) {
flags[i].style.display = state.sunSensor ? '' : 'none';
flags[i].setAttribute('data-on', state.flags & 0x01 === 0x01 ? 'true' : 'false');
}
}
procShadeState(state) { procShadeState(state) {
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}"]`);
@ -2260,7 +2281,7 @@ class Somfy {
} }
let flags = document.querySelectorAll(`.button-sunflag[data-shadeid="${state.shadeId}"]`); let flags = document.querySelectorAll(`.button-sunflag[data-shadeid="${state.shadeId}"]`);
for (let i = 0; i < flags.length; i++) { for (let i = 0; i < flags.length; i++) {
flags[i].style.display = state.type === 3 ? '' : 'none'; flags[i].style.display = state.sunSensor ? '' : 'none';
flags[i].setAttribute('data-on', state.flags & 0x01 === 0x01 ? 'true' : 'false'); flags[i].setAttribute('data-on', state.flags & 0x01 === 0x01 ? 'true' : 'false');
} }