mirror of
https://github.com/rstrouse/ESPSomfy-RTS.git
synced 2025-12-13 11:02:12 +01:00
Add the ability to set the number of repeats per motor when sending commands #113
This commit is contained in:
parent
cd24ca3510
commit
ff16bb6107
10 changed files with 134 additions and 71 deletions
|
|
@ -6,10 +6,10 @@
|
||||||
|
|
||||||
extern Preferences pref;
|
extern Preferences pref;
|
||||||
|
|
||||||
#define SHADE_HDR_VER 11
|
#define SHADE_HDR_VER 12
|
||||||
#define SHADE_HDR_SIZE 24
|
#define SHADE_HDR_SIZE 24
|
||||||
#define SHADE_REC_SIZE 248
|
#define SHADE_REC_SIZE 252
|
||||||
#define GROUP_REC_SIZE 176
|
#define GROUP_REC_SIZE 180
|
||||||
|
|
||||||
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");
|
||||||
|
|
@ -387,6 +387,7 @@ bool ShadeConfigFile::loadFile(SomfyShadeController *s, const char *filename) {
|
||||||
if(this->header.version >= 10) {
|
if(this->header.version >= 10) {
|
||||||
shade->flipPosition = this->readBool(false);
|
shade->flipPosition = this->readBool(false);
|
||||||
}
|
}
|
||||||
|
if(this->header.version >= 12) shade->repeats = this->readUInt8(1);
|
||||||
if(shade->getShadeId() == 255) shade->clear();
|
if(shade->getShadeId() == 255) shade->clear();
|
||||||
}
|
}
|
||||||
for(uint8_t i = 0; i < this->header.groupRecords; i++) {
|
for(uint8_t i = 0; i < this->header.groupRecords; i++) {
|
||||||
|
|
@ -408,7 +409,9 @@ bool ShadeConfigFile::loadFile(SomfyShadeController *s, const char *filename) {
|
||||||
// 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();
|
if(this->header.version >= 12) group->repeats = this->readUInt8(1);
|
||||||
|
if(group->getGroupId() == 255) group->clear();
|
||||||
|
else group->compressLinkedShadeIds();
|
||||||
}
|
}
|
||||||
pref.end();
|
pref.end();
|
||||||
if(opened) {
|
if(opened) {
|
||||||
|
|
@ -425,9 +428,9 @@ bool ShadeConfigFile::writeGroupRecord(SomfyGroup *group) {
|
||||||
this->writeUInt8(static_cast<uint8_t>(group->proto));
|
this->writeUInt8(static_cast<uint8_t>(group->proto));
|
||||||
this->writeUInt8(group->bitLength);
|
this->writeUInt8(group->bitLength);
|
||||||
for(uint8_t j = 0; j < SOMFY_MAX_GROUPED_SHADES; j++) {
|
for(uint8_t j = 0; j < SOMFY_MAX_GROUPED_SHADES; j++) {
|
||||||
if(j == SOMFY_MAX_GROUPED_SHADES - 1) this->writeUInt8(group->linkedShades[j], CFG_REC_END);
|
this->writeUInt8(group->linkedShades[j]);
|
||||||
else this->writeUInt8(group->linkedShades[j]);
|
|
||||||
}
|
}
|
||||||
|
this->writeUInt8(group->repeats, CFG_REC_END);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool ShadeConfigFile::writeShadeRecord(SomfyShade *shade) {
|
bool ShadeConfigFile::writeShadeRecord(SomfyShade *shade) {
|
||||||
|
|
@ -469,7 +472,8 @@ bool ShadeConfigFile::writeShadeRecord(SomfyShade *shade) {
|
||||||
this->writeFloat(0.0f, 5); // currentTiltPos
|
this->writeFloat(0.0f, 5); // currentTiltPos
|
||||||
}
|
}
|
||||||
this->writeBool(shade->flipCommands);
|
this->writeBool(shade->flipCommands);
|
||||||
this->writeBool(shade->flipPosition, CFG_REC_END);
|
this->writeBool(shade->flipPosition);
|
||||||
|
this->writeUInt8(shade->repeats, CFG_REC_END);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool ShadeConfigFile::exists() { return LittleFS.exists("/shades.cfg"); }
|
bool ShadeConfigFile::exists() { return LittleFS.exists("/shades.cfg"); }
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
#ifndef configsettings_h
|
#ifndef configsettings_h
|
||||||
#define configsettings_h
|
#define configsettings_h
|
||||||
|
|
||||||
#define FW_VERSION "v2.1.1"
|
#define FW_VERSION "v2.1.2"
|
||||||
enum DeviceStatus {
|
enum DeviceStatus {
|
||||||
DS_OK = 0,
|
DS_OK = 0,
|
||||||
DS_ERROR = 1,
|
DS_ERROR = 1,
|
||||||
|
|
|
||||||
47
Somfy.cpp
47
Somfy.cpp
|
|
@ -622,10 +622,13 @@ void SomfyShade::clear() {
|
||||||
this->downTime = 10000;
|
this->downTime = 10000;
|
||||||
this->tiltTime = 7000;
|
this->tiltTime = 7000;
|
||||||
this->stepSize = 100;
|
this->stepSize = 100;
|
||||||
|
this->repeats = 1;
|
||||||
}
|
}
|
||||||
void SomfyGroup::clear() {
|
void SomfyGroup::clear() {
|
||||||
this->setGroupId(255);
|
this->setGroupId(255);
|
||||||
this->setRemoteAddress(0);
|
this->setRemoteAddress(0);
|
||||||
|
this->repeats = 1;
|
||||||
|
memset(&this->linkedShades[0], 0x00, sizeof(this->linkedShades));
|
||||||
}
|
}
|
||||||
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
|
||||||
|
|
@ -914,13 +917,13 @@ void SomfyShade::checkMovement() {
|
||||||
// not moving otherwise the my function will kick in.
|
// not moving otherwise the my function will kick in.
|
||||||
if(this->settingPos) {
|
if(this->settingPos) {
|
||||||
if(!isAtTarget()) {
|
if(!isAtTarget()) {
|
||||||
if(this->target != 100.0) SomfyRemote::sendCommand(somfy_commands::My);
|
if(this->target != 100.0) SomfyRemote::sendCommand(somfy_commands::My, this->repeats);
|
||||||
delay(100);
|
delay(100);
|
||||||
// We now need to move the tilt to the position we requested.
|
// We now need to move the tilt to the position we requested.
|
||||||
this->moveToTiltTarget(this->tiltTarget);
|
this->moveToTiltTarget(this->tiltTarget);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
if(this->target != 100.0) SomfyRemote::sendCommand(somfy_commands::My);
|
if(this->target != 100.0) SomfyRemote::sendCommand(somfy_commands::My, this->repeats);
|
||||||
}
|
}
|
||||||
this->direction = 0;
|
this->direction = 0;
|
||||||
this->tiltStart = curTime;
|
this->tiltStart = curTime;
|
||||||
|
|
@ -959,13 +962,13 @@ void SomfyShade::checkMovement() {
|
||||||
// not moving otherwise the my function will kick in.
|
// not moving otherwise the my function will kick in.
|
||||||
if(this->settingPos) {
|
if(this->settingPos) {
|
||||||
if(!isAtTarget()) {
|
if(!isAtTarget()) {
|
||||||
if(this->target != 0.0) SomfyRemote::sendCommand(somfy_commands::My);
|
if(this->target != 0.0) SomfyRemote::sendCommand(somfy_commands::My, this->repeats);
|
||||||
delay(100);
|
delay(100);
|
||||||
// We now need to move the tilt to the position we requested.
|
// We now need to move the tilt to the position we requested.
|
||||||
this->moveToTiltTarget(this->tiltTarget);
|
this->moveToTiltTarget(this->tiltTarget);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
if(this->target != 0.0) SomfyRemote::sendCommand(somfy_commands::My);
|
if(this->target != 0.0) SomfyRemote::sendCommand(somfy_commands::My, this->repeats);
|
||||||
}
|
}
|
||||||
this->direction = 0;
|
this->direction = 0;
|
||||||
this->tiltStart = curTime;
|
this->tiltStart = curTime;
|
||||||
|
|
@ -1004,11 +1007,11 @@ void SomfyShade::checkMovement() {
|
||||||
if(this->settingTiltPos) {
|
if(this->settingTiltPos) {
|
||||||
if(this->tiltType == tilt_types::integrated) {
|
if(this->tiltType == tilt_types::integrated) {
|
||||||
// If this is an integrated tilt mechanism the we will simply let it finish. If it is not then we will stop it.
|
// If this is an integrated tilt mechanism the we will simply let it finish. If it is not then we will stop it.
|
||||||
if(this->tiltTarget != 100.0 || this->currentPos != 100.0) SomfyRemote::sendCommand(somfy_commands::My);
|
if(this->tiltTarget != 100.0 || this->currentPos != 100.0) SomfyRemote::sendCommand(somfy_commands::My, this->repeats);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// This is a tilt motor so let it complete if it is going to 0.
|
// This is a tilt motor so let it complete if it is going to 0.
|
||||||
if(this->tiltTarget != 100.0) SomfyRemote::sendCommand(somfy_commands::My);
|
if(this->tiltTarget != 100.0) SomfyRemote::sendCommand(somfy_commands::My, this->repeats);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this->tiltDirection = 0;
|
this->tiltDirection = 0;
|
||||||
|
|
@ -1052,11 +1055,11 @@ void SomfyShade::checkMovement() {
|
||||||
if(this->settingTiltPos) {
|
if(this->settingTiltPos) {
|
||||||
if(this->tiltType == tilt_types::integrated) {
|
if(this->tiltType == tilt_types::integrated) {
|
||||||
// If this is an integrated tilt mechanism the we will simply let it finish. If it is not then we will stop it.
|
// If this is an integrated tilt mechanism the we will simply let it finish. If it is not then we will stop it.
|
||||||
if(this->tiltTarget != 0.0 || this->currentPos != 0.0) SomfyRemote::sendCommand(somfy_commands::My);
|
if(this->tiltTarget != 0.0 || this->currentPos != 0.0) SomfyRemote::sendCommand(somfy_commands::My, this->repeats);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// This is a tilt motor so let it complete if it is going to 0.
|
// This is a tilt motor so let it complete if it is going to 0.
|
||||||
if(this->tiltTarget != 0.0) SomfyRemote::sendCommand(somfy_commands::My);
|
if(this->tiltTarget != 0.0) SomfyRemote::sendCommand(somfy_commands::My, this->repeats);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this->tiltDirection = 0;
|
this->tiltDirection = 0;
|
||||||
|
|
@ -1824,7 +1827,7 @@ void SomfyShade::setMyPosition(int8_t pos, int8_t tilt) {
|
||||||
this->moveToMyPosition();
|
this->moveToMyPosition();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
SomfyRemote::sendCommand(somfy_commands::My, 1);
|
SomfyRemote::sendCommand(somfy_commands::My, this->repeats);
|
||||||
this->settingPos = false;
|
this->settingPos = false;
|
||||||
this->settingMyPos = true;
|
this->settingMyPos = true;
|
||||||
}
|
}
|
||||||
|
|
@ -1854,7 +1857,7 @@ void SomfyShade::setMyPosition(int8_t pos, int8_t tilt) {
|
||||||
this->moveToMyPosition();
|
this->moveToMyPosition();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
SomfyRemote::sendCommand(somfy_commands::My, 1);
|
SomfyRemote::sendCommand(somfy_commands::My, this->repeats);
|
||||||
this->settingPos = false;
|
this->settingPos = false;
|
||||||
this->settingMyPos = true;
|
this->settingMyPos = true;
|
||||||
}
|
}
|
||||||
|
|
@ -1892,8 +1895,9 @@ void SomfyShade::moveToMyPosition() {
|
||||||
if(this->myPos >= 0.0f && this->myPos <= 100.0f) this->target = this->myPos;
|
if(this->myPos >= 0.0f && this->myPos <= 100.0f) this->target = this->myPos;
|
||||||
if(this->myTiltPos >= 0.0f && this->myTiltPos <= 100.0f) this->tiltTarget = this->myTiltPos;
|
if(this->myTiltPos >= 0.0f && this->myTiltPos <= 100.0f) this->tiltTarget = this->myTiltPos;
|
||||||
this->settingPos = false;
|
this->settingPos = false;
|
||||||
SomfyRemote::sendCommand(somfy_commands::My);
|
SomfyRemote::sendCommand(somfy_commands::My, this->repeats);
|
||||||
}
|
}
|
||||||
|
void SomfyShade::sendCommand(somfy_commands cmd) { this->sendCommand(cmd, this->repeats); }
|
||||||
void SomfyShade::sendCommand(somfy_commands cmd, uint8_t repeat) {
|
void SomfyShade::sendCommand(somfy_commands cmd, uint8_t repeat) {
|
||||||
// This sendCommand function will always be called externally. sendCommand at the remote level
|
// This sendCommand function will always be called externally. sendCommand at the remote level
|
||||||
// is expected to be called internally when the motor needs commanded.
|
// is expected to be called internally when the motor needs commanded.
|
||||||
|
|
@ -1923,6 +1927,7 @@ void SomfyShade::sendCommand(somfy_commands cmd, uint8_t repeat) {
|
||||||
SomfyRemote::sendCommand(cmd, repeat);
|
SomfyRemote::sendCommand(cmd, repeat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
void SomfyGroup::sendCommand(somfy_commands cmd) { this->sendCommand(cmd, this->repeats); }
|
||||||
void SomfyGroup::sendCommand(somfy_commands cmd, uint8_t repeat) {
|
void SomfyGroup::sendCommand(somfy_commands cmd, uint8_t repeat) {
|
||||||
// This sendCommand function will always be called externally. sendCommand at the remote level
|
// This sendCommand function will always be called externally. sendCommand at the remote level
|
||||||
// is expected to be called internally when the motor needs commanded.
|
// is expected to be called internally when the motor needs commanded.
|
||||||
|
|
@ -1954,15 +1959,15 @@ void SomfyGroup::sendCommand(somfy_commands cmd, uint8_t repeat) {
|
||||||
}
|
}
|
||||||
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 : this->repeats);
|
||||||
this->tiltTarget = 0.0f;
|
this->tiltTarget = 0.0f;
|
||||||
}
|
}
|
||||||
else if(cmd == somfy_commands::Down) {
|
else if(cmd == somfy_commands::Down) {
|
||||||
SomfyRemote::sendCommand(cmd, this->tiltType == tilt_types::tiltmotor ? TILT_REPEATS : 1);
|
SomfyRemote::sendCommand(cmd, this->tiltType == tilt_types::tiltmotor ? TILT_REPEATS : this->repeats);
|
||||||
this->tiltTarget = 100.0f;
|
this->tiltTarget = 100.0f;
|
||||||
}
|
}
|
||||||
else if(cmd == somfy_commands::My) {
|
else if(cmd == somfy_commands::My) {
|
||||||
SomfyRemote::sendCommand(cmd, this->tiltType == tilt_types::tiltmotor ? TILT_REPEATS : 1);
|
SomfyRemote::sendCommand(cmd, this->tiltType == tilt_types::tiltmotor ? TILT_REPEATS : this->repeats);
|
||||||
this->tiltTarget = this->currentTiltPos;
|
this->tiltTarget = this->currentTiltPos;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1981,10 +1986,10 @@ void SomfyShade::moveToTiltTarget(float target) {
|
||||||
Serial.print(this->currentTiltPos);
|
Serial.print(this->currentTiltPos);
|
||||||
Serial.print("% using ");
|
Serial.print("% using ");
|
||||||
Serial.println(translateSomfyCommand(cmd));
|
Serial.println(translateSomfyCommand(cmd));
|
||||||
SomfyRemote::sendCommand(cmd, this->tiltType == tilt_types::tiltmotor ? TILT_REPEATS : 1);
|
SomfyRemote::sendCommand(cmd, this->tiltType == tilt_types::tiltmotor ? TILT_REPEATS : this->repeats);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
SomfyRemote::sendCommand(cmd);
|
SomfyRemote::sendCommand(cmd, this->repeats);
|
||||||
}
|
}
|
||||||
this->tiltTarget = target;
|
this->tiltTarget = target;
|
||||||
}
|
}
|
||||||
|
|
@ -2013,7 +2018,7 @@ void SomfyShade::moveToTarget(float pos, float tilt) {
|
||||||
}
|
}
|
||||||
Serial.print("% using ");
|
Serial.print("% using ");
|
||||||
Serial.println(translateSomfyCommand(cmd));
|
Serial.println(translateSomfyCommand(cmd));
|
||||||
SomfyRemote::sendCommand(cmd);
|
SomfyRemote::sendCommand(cmd, this->repeats);
|
||||||
this->settingPos = true;
|
this->settingPos = true;
|
||||||
this->target = pos;
|
this->target = pos;
|
||||||
if(tilt >= 0) {
|
if(tilt >= 0) {
|
||||||
|
|
@ -2084,6 +2089,7 @@ bool SomfyShade::fromJSON(JsonObject &obj) {
|
||||||
}
|
}
|
||||||
if(obj.containsKey("flipCommands")) this->flipCommands = obj["flipCommands"].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("flipPosition")) this->flipPosition = obj["flipPosition"].as<bool>();
|
||||||
|
if(obj.containsKey("repeats")) this->repeats = obj["repeats"];
|
||||||
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)
|
||||||
|
|
@ -2124,6 +2130,7 @@ bool SomfyShade::toJSONRef(JsonObject &obj) {
|
||||||
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();
|
obj["sunSensor"] = this->hasSunSensor();
|
||||||
|
obj["repeats"] = this->repeats;
|
||||||
SomfyRemote::toJSON(obj);
|
SomfyRemote::toJSON(obj);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -2160,6 +2167,7 @@ bool SomfyShade::toJSON(JsonObject &obj) {
|
||||||
obj["flipPosition"] = this->flipPosition;
|
obj["flipPosition"] = this->flipPosition;
|
||||||
obj["inGroup"] = this->isInGroup();
|
obj["inGroup"] = this->isInGroup();
|
||||||
obj["sunSensor"] = this->hasSunSensor();
|
obj["sunSensor"] = this->hasSunSensor();
|
||||||
|
obj["repeats"] = this->repeats;
|
||||||
|
|
||||||
SomfyRemote::toJSON(obj);
|
SomfyRemote::toJSON(obj);
|
||||||
JsonArray arr = obj.createNestedArray("linkedRemotes");
|
JsonArray arr = obj.createNestedArray("linkedRemotes");
|
||||||
|
|
@ -2177,7 +2185,8 @@ 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("sunSensor")) this->hasSunSensor() = obj["sunSensor"]; This is calculated
|
||||||
|
if(obj.containsKey("repeats")) this->repeats = obj["repeats"];
|
||||||
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));
|
||||||
|
|
@ -2199,6 +2208,7 @@ bool SomfyGroup::toJSON(JsonObject &obj) {
|
||||||
obj["proto"] = static_cast<uint8_t>(this->proto);
|
obj["proto"] = static_cast<uint8_t>(this->proto);
|
||||||
obj["sunSensor"] = this->hasSunSensor();
|
obj["sunSensor"] = this->hasSunSensor();
|
||||||
obj["flags"] = this->flags;
|
obj["flags"] = this->flags;
|
||||||
|
obj["repeats"] = this->repeats;
|
||||||
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++) {
|
||||||
|
|
@ -2440,6 +2450,7 @@ somfy_commands SomfyRemote::transformCommand(somfy_commands cmd) {
|
||||||
}
|
}
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
void SomfyRemote::sendCommand(somfy_commands cmd) { this->sendCommand(cmd, this->repeats); }
|
||||||
void SomfyRemote::sendCommand(somfy_commands cmd, uint8_t repeat) {
|
void SomfyRemote::sendCommand(somfy_commands cmd, uint8_t repeat) {
|
||||||
somfy_frame_t frame;
|
somfy_frame_t frame;
|
||||||
frame.rollingCode = this->getNextRollingCode();
|
frame.rollingCode = this->getNextRollingCode();
|
||||||
|
|
|
||||||
10
Somfy.h
10
Somfy.h
|
|
@ -178,6 +178,7 @@ class SomfyRemote {
|
||||||
bool flipCommands = false;
|
bool flipCommands = false;
|
||||||
uint8_t flags = 0;
|
uint8_t flags = 0;
|
||||||
uint8_t bitLength = 0;
|
uint8_t bitLength = 0;
|
||||||
|
uint8_t repeats = 1;
|
||||||
char *getRemotePrefId() {return m_remotePrefId;}
|
char *getRemotePrefId() {return m_remotePrefId;}
|
||||||
virtual bool toJSON(JsonObject &obj);
|
virtual bool toJSON(JsonObject &obj);
|
||||||
virtual void setRemoteAddress(uint32_t address);
|
virtual void setRemoteAddress(uint32_t address);
|
||||||
|
|
@ -187,7 +188,8 @@ class SomfyRemote {
|
||||||
uint16_t lastRollingCode = 0;
|
uint16_t lastRollingCode = 0;
|
||||||
bool hasSunSensor();
|
bool hasSunSensor();
|
||||||
void setSunSensor(bool bHasSensor);
|
void setSunSensor(bool bHasSensor);
|
||||||
virtual void sendCommand(somfy_commands cmd, uint8_t repeat = 1);
|
virtual void sendCommand(somfy_commands cmd);
|
||||||
|
virtual void sendCommand(somfy_commands cmd, uint8_t repeat);
|
||||||
somfy_commands transformCommand(somfy_commands cmd);
|
somfy_commands transformCommand(somfy_commands cmd);
|
||||||
};
|
};
|
||||||
class SomfyLinkedRemote : public SomfyRemote {
|
class SomfyLinkedRemote : public SomfyRemote {
|
||||||
|
|
@ -255,7 +257,8 @@ class SomfyShade : public SomfyRemote {
|
||||||
void moveToTarget(float pos, float tilt = -1.0f);
|
void moveToTarget(float pos, float tilt = -1.0f);
|
||||||
void moveToTiltTarget(float target);
|
void moveToTiltTarget(float target);
|
||||||
void sendTiltCommand(somfy_commands cmd);
|
void sendTiltCommand(somfy_commands cmd);
|
||||||
void sendCommand(somfy_commands cmd, uint8_t repeat = 1);
|
void sendCommand(somfy_commands cmd);
|
||||||
|
void sendCommand(somfy_commands cmd, uint8_t repeat);
|
||||||
bool linkRemote(uint32_t remoteAddress, uint16_t rollingCode = 0);
|
bool linkRemote(uint32_t remoteAddress, uint16_t rollingCode = 0);
|
||||||
bool unlinkRemote(uint32_t remoteAddress);
|
bool unlinkRemote(uint32_t remoteAddress);
|
||||||
void emitState(const char *evt = "shadeState");
|
void emitState(const char *evt = "shadeState");
|
||||||
|
|
@ -295,7 +298,8 @@ class SomfyGroup : public SomfyRemote {
|
||||||
void updateFlags();
|
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);
|
||||||
|
void sendCommand(somfy_commands cmd, uint8_t repeat);
|
||||||
};
|
};
|
||||||
struct transceiver_config_t {
|
struct transceiver_config_t {
|
||||||
bool printBuffer = false;
|
bool printBuffer = false;
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
22
Web.cpp
22
Web.cpp
|
|
@ -323,7 +323,7 @@ void Web::begin() {
|
||||||
HTTPMethod method = apiServer.method();
|
HTTPMethod method = apiServer.method();
|
||||||
uint8_t shadeId = 255;
|
uint8_t shadeId = 255;
|
||||||
uint8_t target = 255;
|
uint8_t target = 255;
|
||||||
uint8_t repeat = 1;
|
int8_t repeat = -1;
|
||||||
somfy_commands command = somfy_commands::My;
|
somfy_commands command = somfy_commands::My;
|
||||||
if (method == HTTP_GET || method == HTTP_PUT || method == HTTP_POST) {
|
if (method == HTTP_GET || method == HTTP_PUT || method == HTTP_POST) {
|
||||||
if (apiServer.hasArg("shadeId")) {
|
if (apiServer.hasArg("shadeId")) {
|
||||||
|
|
@ -373,8 +373,10 @@ void Web::begin() {
|
||||||
// Send the command to the shade.
|
// Send the command to the shade.
|
||||||
if(target <= 100)
|
if(target <= 100)
|
||||||
shade->moveToTarget(shade->transformPosition(target));
|
shade->moveToTarget(shade->transformPosition(target));
|
||||||
else
|
else if(repeat > 0)
|
||||||
shade->sendCommand(command, repeat);
|
shade->sendCommand(command, repeat);
|
||||||
|
else
|
||||||
|
shade->sendCommand(command);
|
||||||
DynamicJsonDocument sdoc(512);
|
DynamicJsonDocument sdoc(512);
|
||||||
JsonObject sobj = sdoc.to<JsonObject>();
|
JsonObject sobj = sdoc.to<JsonObject>();
|
||||||
shade->toJSON(sobj);
|
shade->toJSON(sobj);
|
||||||
|
|
@ -390,7 +392,7 @@ void Web::begin() {
|
||||||
if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; }
|
if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; }
|
||||||
HTTPMethod method = apiServer.method();
|
HTTPMethod method = apiServer.method();
|
||||||
uint8_t groupId = 255;
|
uint8_t groupId = 255;
|
||||||
uint8_t repeat = 1;
|
int8_t repeat = -1;
|
||||||
somfy_commands command = somfy_commands::My;
|
somfy_commands command = somfy_commands::My;
|
||||||
if (method == HTTP_GET || method == HTTP_PUT || method == HTTP_POST) {
|
if (method == HTTP_GET || method == HTTP_PUT || method == HTTP_POST) {
|
||||||
if (apiServer.hasArg("groupId")) {
|
if (apiServer.hasArg("groupId")) {
|
||||||
|
|
@ -434,7 +436,8 @@ void Web::begin() {
|
||||||
Serial.print("Received:");
|
Serial.print("Received:");
|
||||||
Serial.println(apiServer.arg("plain"));
|
Serial.println(apiServer.arg("plain"));
|
||||||
// Send the command to the group.
|
// Send the command to the group.
|
||||||
group->sendCommand(command, repeat);
|
if(repeat != -1) group->sendCommand(command, repeat);
|
||||||
|
else group->sendCommand(command);
|
||||||
DynamicJsonDocument sdoc(512);
|
DynamicJsonDocument sdoc(512);
|
||||||
JsonObject sobj = sdoc.to<JsonObject>();
|
JsonObject sobj = sdoc.to<JsonObject>();
|
||||||
group->toJSON(sobj);
|
group->toJSON(sobj);
|
||||||
|
|
@ -1068,7 +1071,7 @@ void Web::begin() {
|
||||||
HTTPMethod method = server.method();
|
HTTPMethod method = server.method();
|
||||||
uint8_t shadeId = 255;
|
uint8_t shadeId = 255;
|
||||||
uint8_t target = 255;
|
uint8_t target = 255;
|
||||||
uint8_t repeat = 1;
|
int8_t repeat = -1;
|
||||||
somfy_commands command = somfy_commands::My;
|
somfy_commands command = somfy_commands::My;
|
||||||
if (method == HTTP_GET || method == HTTP_PUT || method == HTTP_POST) {
|
if (method == HTTP_GET || method == HTTP_PUT || method == HTTP_POST) {
|
||||||
if (server.hasArg("shadeId")) {
|
if (server.hasArg("shadeId")) {
|
||||||
|
|
@ -1118,8 +1121,10 @@ void Web::begin() {
|
||||||
// Send the command to the shade.
|
// Send the command to the shade.
|
||||||
if(target <= 100)
|
if(target <= 100)
|
||||||
shade->moveToTarget(shade->transformPosition(target));
|
shade->moveToTarget(shade->transformPosition(target));
|
||||||
else
|
else if(repeat > 0)
|
||||||
shade->sendCommand(command, repeat);
|
shade->sendCommand(command, repeat);
|
||||||
|
else
|
||||||
|
shade->sendCommand(command);
|
||||||
DynamicJsonDocument sdoc(512);
|
DynamicJsonDocument sdoc(512);
|
||||||
JsonObject sobj = sdoc.to<JsonObject>();
|
JsonObject sobj = sdoc.to<JsonObject>();
|
||||||
shade->toJSON(sobj);
|
shade->toJSON(sobj);
|
||||||
|
|
@ -1135,7 +1140,7 @@ void Web::begin() {
|
||||||
if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; }
|
if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; }
|
||||||
HTTPMethod method = server.method();
|
HTTPMethod method = server.method();
|
||||||
uint8_t groupId = 255;
|
uint8_t groupId = 255;
|
||||||
uint8_t repeat = 1;
|
int8_t repeat = -1;
|
||||||
somfy_commands command = somfy_commands::My;
|
somfy_commands command = somfy_commands::My;
|
||||||
if (method == HTTP_GET || method == HTTP_PUT || method == HTTP_POST) {
|
if (method == HTTP_GET || method == HTTP_PUT || method == HTTP_POST) {
|
||||||
if (server.hasArg("groupId")) {
|
if (server.hasArg("groupId")) {
|
||||||
|
|
@ -1179,7 +1184,8 @@ void Web::begin() {
|
||||||
Serial.print("Received:");
|
Serial.print("Received:");
|
||||||
Serial.println(server.arg("plain"));
|
Serial.println(server.arg("plain"));
|
||||||
// Send the command to the group.
|
// Send the command to the group.
|
||||||
group->sendCommand(command, repeat);
|
if(repeat > 0) group->sendCommand(command, repeat);
|
||||||
|
else group->sendCommand(command);
|
||||||
DynamicJsonDocument sdoc(512);
|
DynamicJsonDocument sdoc(512);
|
||||||
JsonObject sobj = sdoc.to<JsonObject>();
|
JsonObject sobj = sdoc.to<JsonObject>();
|
||||||
group->toJSON(sobj);
|
group->toJSON(sobj);
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
2.1.1
|
2.1.2
|
||||||
|
|
@ -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.1.1" type="text/css" />
|
<link rel="stylesheet" href="main.css?v=2.1.2" type="text/css" />
|
||||||
<link rel="stylesheet" href="widgets.css?v=2.1.1" type="text/css" />
|
<link rel="stylesheet" href="widgets.css?v=2.1.2" type="text/css" />
|
||||||
<link rel="stylesheet" href="icons.css?v=2.1.1" type="text/css" />
|
<link rel="stylesheet" href="icons.css?v=2.1.2" 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.1.1"></script>
|
<script type="text/javascript" src="index.js?v=2.1.2"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="divContainer" class="container main" data-auth="false">
|
<div id="divContainer" class="container main" data-auth="false">
|
||||||
|
|
@ -375,7 +375,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style="margin-top:-10px;">
|
<div style="margin-top:-10px;">
|
||||||
<div class="field-group">
|
<div class="field-group" style="display:inline-block">
|
||||||
<input id="cbFlipCommands" name="flipCommands" data-bind="flipCommands" type="checkbox" style="" />
|
<input id="cbFlipCommands" name="flipCommands" data-bind="flipCommands" type="checkbox" style="" />
|
||||||
<label for="cbFlipCommands" 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>
|
||||||
|
|
@ -386,6 +386,24 @@
|
||||||
<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>
|
<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 classs="field-group" style="display:inline-block;">
|
||||||
|
<label for="selRepeatCommnds" style="cursor:pointer;color:#00bcd4;margin-right:4px;">Repeat Commands</label>
|
||||||
|
<select id="selRepeatCommands" data-bind="repeats" data-datatype="int" style="width:127px;">
|
||||||
|
<option value="0">No Repeats</option>
|
||||||
|
<option value="1">1 Time</option>
|
||||||
|
<option value="2">2 Times</option>
|
||||||
|
<option value="3">3 Times</option>
|
||||||
|
<option value="4">4 Times</option>
|
||||||
|
<option value="5">5 Times</option>
|
||||||
|
<option value="6">6 Times</option>
|
||||||
|
<option value="7">7 Times</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="margin-top:-10px;">
|
||||||
|
<div class="field-group">
|
||||||
|
</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
|
||||||
|
|
@ -459,6 +477,20 @@
|
||||||
<input id="fldGroupName" name="groupName" data-bind="name" type="text" length=20 placeholder="Name">
|
<input id="fldGroupName" name="groupName" data-bind="name" type="text" length=20 placeholder="Name">
|
||||||
<label for="fldGroupName">Name</label>
|
<label for="fldGroupName">Name</label>
|
||||||
</div>
|
</div>
|
||||||
|
<div classs="field-group" style="display:inline-block;">
|
||||||
|
<label for="selRepeatCommnds" style="cursor:pointer;color:#00bcd4;margin-right:4px;">Repeat Commands</label>
|
||||||
|
<select id="selRepeatCommands" data-bind="repeats" data-datatype="int" style="width:127px;">
|
||||||
|
<option value="0">No Repeats</option>
|
||||||
|
<option value="1">1 Time</option>
|
||||||
|
<option value="2">2 Times</option>
|
||||||
|
<option value="3">3 Times</option>
|
||||||
|
<option value="4">4 Times</option>
|
||||||
|
<option value="5">5 Times</option>
|
||||||
|
<option value="6">6 Times</option>
|
||||||
|
<option value="7">7 Times</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="button-container" style="margin-top:-10px;padding-left:7px;padding-right:7px;">
|
<div class="button-container" style="margin-top:-10px;padding-left:7px;padding-right:7px;">
|
||||||
<button id="btnSaveGroup" type="button" onclick="somfy.saveGroup();">
|
<button id="btnSaveGroup" type="button" onclick="somfy.saveGroup();">
|
||||||
Save Group
|
Save Group
|
||||||
|
|
|
||||||
|
|
@ -1197,7 +1197,7 @@ var security = new Security();
|
||||||
|
|
||||||
class General {
|
class General {
|
||||||
initialized = false;
|
initialized = false;
|
||||||
appVersion = 'v2.1.1';
|
appVersion = 'v2.1.2';
|
||||||
reloadApp = false;
|
reloadApp = false;
|
||||||
init() {
|
init() {
|
||||||
if (this.initialized) return;
|
if (this.initialized) return;
|
||||||
|
|
@ -2073,33 +2073,37 @@ class Somfy {
|
||||||
setGroupsList(groups) {
|
setGroupsList(groups) {
|
||||||
let divCfg = '';
|
let divCfg = '';
|
||||||
let divCtl = '';
|
let divCtl = '';
|
||||||
for (let i = 0; i < groups.length; i++) {
|
if (typeof groups !== 'undefined') {
|
||||||
let group = groups[i];
|
for (let i = 0; i < groups.length; i++) {
|
||||||
divCfg += `<div class="somfyGroup" data-groupid="${group.groupId}" data-remoteaddress="${group.remoteAddress}">`;
|
let group = groups[i];
|
||||||
divCfg += `<div class="button-outline" onclick="somfy.openEditGroup(${group.groupId});"><i class="icss-edit"></i></div>`;
|
divCfg += `<div class="somfyGroup" data-groupid="${group.groupId}" data-remoteaddress="${group.remoteAddress}">`;
|
||||||
//divCfg += `<i class="Group-icon" data-position="${Group.position || 0}%"></i>`;
|
divCfg += `<div class="button-outline" onclick="somfy.openEditGroup(${group.groupId});"><i class="icss-edit"></i></div>`;
|
||||||
divCfg += `<span class="group-name">${group.name}</span>`;
|
//divCfg += `<i class="Group-icon" data-position="${Group.position || 0}%"></i>`;
|
||||||
divCfg += `<span class="group-address">${group.remoteAddress}</span>`;
|
divCfg += `<span class="group-name">${group.name}</span>`;
|
||||||
divCfg += `<div class="button-outline" onclick="somfy.deleteGroup(${group.groupId});"><i class="icss-trash"></i></div>`;
|
divCfg += `<span class="group-address">${group.remoteAddress}</span>`;
|
||||||
divCfg += '</div>';
|
divCfg += `<div class="button-outline" onclick="somfy.deleteGroup(${group.groupId});"><i class="icss-trash"></i></div>`;
|
||||||
|
divCfg += '</div>';
|
||||||
|
|
||||||
divCtl += `<div class="somfyGroupCtl" data-groupId="${group.groupId}" data-remoteaddress="${group.remoteAddress}">`;
|
divCtl += `<div class="somfyGroupCtl" data-groupId="${group.groupId}" data-remoteaddress="${group.remoteAddress}">`;
|
||||||
divCtl += `<div class="group-name">`;
|
divCtl += `<div class="group-name">`;
|
||||||
divCtl += `<span class="groupctl-name">${group.name}</span>`;
|
divCtl += `<span class="groupctl-name">${group.name}</span>`;
|
||||||
divCtl += `<div class="groupctl-shades">`;
|
divCtl += `<div class="groupctl-shades">`;
|
||||||
for (let j = 0; j < group.linkedShades.length; j++) {
|
if (typeof group.linkedShades !== 'undefined') {
|
||||||
divCtl += '<span>';
|
for (let j = 0; j < group.linkedShades.length; j++) {
|
||||||
if (j !== 0) divCtl += ', ';
|
divCtl += '<span>';
|
||||||
divCtl += group.linkedShades[j].name;
|
if (j !== 0) divCtl += ', ';
|
||||||
divCtl += '</span>';
|
divCtl += group.linkedShades[j].name;
|
||||||
|
divCtl += '</span>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
divCtl += '</div></div>';
|
||||||
|
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 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></div>';
|
||||||
}
|
}
|
||||||
divCtl += '</div></div>';
|
|
||||||
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 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></div>';
|
|
||||||
}
|
}
|
||||||
document.getElementById('divGroupList').innerHTML = divCfg;
|
document.getElementById('divGroupList').innerHTML = divCfg;
|
||||||
let groupControls = document.getElementById('divGroupControls');
|
let groupControls = document.getElementById('divGroupControls');
|
||||||
|
|
@ -3305,7 +3309,7 @@ class Firmware {
|
||||||
}
|
}
|
||||||
backup() {
|
backup() {
|
||||||
var link = document.createElement('a');
|
var link = document.createElement('a');
|
||||||
link.href = '/backup';
|
link.href = baseUrl.length > 0 ? `${baseUrl}/backup` : '/backup';
|
||||||
link.setAttribute('download', 'backup');
|
link.setAttribute('download', 'backup');
|
||||||
document.body.appendChild(link);
|
document.body.appendChild(link);
|
||||||
link.click();
|
link.click();
|
||||||
|
|
@ -3431,8 +3435,10 @@ class Firmware {
|
||||||
prog.style.display = '';
|
prog.style.display = '';
|
||||||
btnSelectFile.style.visibility = 'hidden';
|
btnSelectFile.style.visibility = 'hidden';
|
||||||
formData.append('file', field.files[0]);
|
formData.append('file', field.files[0]);
|
||||||
|
|
||||||
let xhr = new XMLHttpRequest();
|
let xhr = new XMLHttpRequest();
|
||||||
xhr.open('POST', service, true);
|
//xhr.open('POST', service, true);
|
||||||
|
xhr.open('POST', baseUrl.length > 0 ? `${baseUrl}${service}` : service, true);
|
||||||
xhr.upload.onprogress = function (evt) {
|
xhr.upload.onprogress = function (evt) {
|
||||||
let pct = evt.total ? Math.round((evt.loaded / evt.total) * 100) : 0;
|
let pct = evt.total ? Math.round((evt.loaded / evt.total) * 100) : 0;
|
||||||
prog.style.setProperty('--progress', `${pct}%`);
|
prog.style.setProperty('--progress', `${pct}%`);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue