mirror of
https://github.com/rstrouse/ESPSomfy-RTS.git
synced 2025-12-13 11:02:12 +01:00
Add the ability to set the shade and group display order #139 and suspend MQTT responses while updating the firmware.
This commit is contained in:
parent
92b9b679f9
commit
90b247e7d8
16 changed files with 498 additions and 482 deletions
|
|
@ -6,10 +6,10 @@
|
|||
|
||||
extern Preferences pref;
|
||||
|
||||
#define SHADE_HDR_VER 12
|
||||
#define SHADE_HDR_SIZE 24
|
||||
#define SHADE_REC_SIZE 252
|
||||
#define GROUP_REC_SIZE 180
|
||||
#define SHADE_HDR_VER 13
|
||||
#define SHADE_HDR_SIZE 28
|
||||
#define SHADE_REC_SIZE 256
|
||||
#define GROUP_REC_SIZE 184
|
||||
|
||||
bool ConfigFile::begin(const char* filename, bool readOnly) {
|
||||
this->file = LittleFS.open(filename, readOnly ? "r" : "w");
|
||||
|
|
@ -40,9 +40,9 @@ bool ConfigFile::writeHeader(const config_header_t &hdr) {
|
|||
if(!this->isOpen()) return false;
|
||||
this->writeUInt8(hdr.version);
|
||||
this->writeUInt8(hdr.length);
|
||||
this->writeUInt8(hdr.shadeRecordSize);
|
||||
this->writeUInt16(hdr.shadeRecordSize);
|
||||
this->writeUInt8(hdr.shadeRecords);
|
||||
this->writeUInt8(hdr.groupRecordSize);
|
||||
this->writeUInt16(hdr.groupRecordSize);
|
||||
this->writeUInt8(hdr.groupRecords, CFG_REC_END);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -52,10 +52,12 @@ bool ConfigFile::readHeader() {
|
|||
Serial.printf("Reading header at %u\n", this->file.position());
|
||||
this->header.version = this->readUInt8(this->header.version);
|
||||
this->header.length = this->readUInt8(0);
|
||||
this->header.shadeRecordSize = this->readUInt8(this->header.shadeRecordSize);
|
||||
if(this->header.version >= 13) this->header.shadeRecordSize = this->readUInt16(this->header.shadeRecordSize);
|
||||
else this->header.shadeRecordSize = this->readUInt8((uint8_t)this->header.shadeRecordSize);
|
||||
this->header.shadeRecords = this->readUInt8(this->header.shadeRecords);
|
||||
if(this->header.version > 10) {
|
||||
this->header.groupRecordSize = this->readUInt8(this->header.groupRecordSize);
|
||||
if(this->header.version >= 13) this->header.groupRecordSize = this->readUInt16(this->header.groupRecordSize);
|
||||
else this->header.groupRecordSize = this->readUInt8(this->header.groupRecordSize);
|
||||
this->header.groupRecords = this->readUInt8(this->header.groupRecords);
|
||||
}
|
||||
Serial.printf("version:%u len:%u shadeSize:%u shadeRecs:%u groupSize:%u groupRecs: %u pos:%d\n", this->header.version, this->header.length, this->header.shadeRecordSize, this->header.shadeRecords, this->header.groupRecordSize, this->header.groupRecords, this->file.position());
|
||||
|
|
@ -332,18 +334,12 @@ bool ShadeConfigFile::loadFile(SomfyShadeController *s, const char *filename) {
|
|||
shade->tiltType = this->readBool(false) ? tilt_types::none : tilt_types::tiltmotor;
|
||||
else
|
||||
shade->tiltType = static_cast<tilt_types>(this->readUInt8(0));
|
||||
if(this->header.version > 6) {
|
||||
shade->proto = static_cast<radio_proto>(this->readUInt8(0));
|
||||
}
|
||||
if(this->header.version > 1) {
|
||||
shade->bitLength = this->readUInt8(56);
|
||||
}
|
||||
if(this->header.version > 6) shade->proto = static_cast<radio_proto>(this->readUInt8(0));
|
||||
if(this->header.version > 1) shade->bitLength = this->readUInt8(56);
|
||||
shade->upTime = this->readUInt32(shade->upTime);
|
||||
shade->downTime = this->readUInt32(shade->downTime);
|
||||
shade->tiltTime = this->readUInt32(shade->tiltTime);
|
||||
if(this->header.version > 5) {
|
||||
shade->stepSize = this->readUInt16(100);
|
||||
}
|
||||
if(this->header.version > 5) shade->stepSize = this->readUInt16(100);
|
||||
for(uint8_t j = 0; j < SOMFY_MAX_LINKED_REMOTES; j++) {
|
||||
SomfyLinkedRemote *rem = &shade->linkedRemotes[j];
|
||||
rem->setRemoteAddress(this->readUInt32(0));
|
||||
|
|
@ -381,15 +377,14 @@ bool ShadeConfigFile::loadFile(SomfyShadeController *s, const char *filename) {
|
|||
}
|
||||
shade->target = floor(shade->currentPos);
|
||||
shade->tiltTarget = floor(shade->currentTiltPos);
|
||||
if(this->header.version >= 9) {
|
||||
shade->flipCommands = this->readBool(false);
|
||||
}
|
||||
if(this->header.version >= 10) {
|
||||
shade->flipPosition = this->readBool(false);
|
||||
}
|
||||
if(this->header.version >= 9) shade->flipCommands = this->readBool(false);
|
||||
if(this->header.version >= 10) shade->flipPosition = this->readBool(false);
|
||||
if(this->header.version >= 12) shade->repeats = this->readUInt8(1);
|
||||
if(this->header.version >= 13) shade->sortOrder = this->readUInt8(shade->getShadeId() - 1);
|
||||
else shade->sortOrder = shade->getShadeId() - 1;
|
||||
if(shade->getShadeId() == 255) shade->clear();
|
||||
}
|
||||
|
||||
for(uint8_t i = 0; i < this->header.groupRecords; i++) {
|
||||
SomfyGroup *group = &s->groups[i];
|
||||
group->setGroupId(this->readUInt8(255));
|
||||
|
|
@ -410,6 +405,9 @@ bool ShadeConfigFile::loadFile(SomfyShadeController *s, const char *filename) {
|
|||
if(shadeId > 0) group->linkedShades[lsd++] = shadeId;
|
||||
}
|
||||
if(this->header.version >= 12) group->repeats = this->readUInt8(1);
|
||||
if(this->header.version >= 13) group->sortOrder = this->readUInt8(group->getGroupId() - 1);
|
||||
else group->sortOrder = group->getGroupId() - 1;
|
||||
|
||||
if(group->getGroupId() == 255) group->clear();
|
||||
else group->compressLinkedShadeIds();
|
||||
}
|
||||
|
|
@ -430,7 +428,8 @@ bool ShadeConfigFile::writeGroupRecord(SomfyGroup *group) {
|
|||
for(uint8_t j = 0; j < SOMFY_MAX_GROUPED_SHADES; j++) {
|
||||
this->writeUInt8(group->linkedShades[j]);
|
||||
}
|
||||
this->writeUInt8(group->repeats, CFG_REC_END);
|
||||
this->writeUInt8(group->repeats);
|
||||
this->writeUInt8(group->sortOrder, CFG_REC_END);
|
||||
return true;
|
||||
}
|
||||
bool ShadeConfigFile::writeShadeRecord(SomfyShade *shade) {
|
||||
|
|
@ -473,7 +472,8 @@ bool ShadeConfigFile::writeShadeRecord(SomfyShade *shade) {
|
|||
}
|
||||
this->writeBool(shade->flipCommands);
|
||||
this->writeBool(shade->flipPosition);
|
||||
this->writeUInt8(shade->repeats, CFG_REC_END);
|
||||
this->writeUInt8(shade->repeats);
|
||||
this->writeUInt8(shade->sortOrder, CFG_REC_END);
|
||||
return true;
|
||||
}
|
||||
bool ShadeConfigFile::exists() { return LittleFS.exists("/shades.cfg"); }
|
||||
|
|
|
|||
|
|
@ -10,11 +10,11 @@
|
|||
|
||||
struct config_header_t {
|
||||
uint8_t version = 1;
|
||||
uint8_t shadeRecordSize = 0;
|
||||
uint16_t shadeRecordSize = 0;
|
||||
uint8_t shadeRecords = 0;
|
||||
uint8_t groupRecordSize = 0;
|
||||
uint16_t groupRecordSize = 0;
|
||||
uint8_t groupRecords = 0;
|
||||
uint8_t length = 0;
|
||||
int8_t length = 0;
|
||||
};
|
||||
class ConfigFile {
|
||||
protected:
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
#ifndef configsettings_h
|
||||
#define configsettings_h
|
||||
|
||||
#define FW_VERSION "v2.1.4"
|
||||
#define FW_VERSION "v2.1.5"
|
||||
enum DeviceStatus {
|
||||
DS_OK = 0,
|
||||
DS_ERROR = 1,
|
||||
|
|
|
|||
12
MQTT.cpp
12
MQTT.cpp
|
|
@ -14,19 +14,21 @@ static char g_content[MQTT_MAX_RESPONSE];
|
|||
extern ConfigSettings settings;
|
||||
extern SomfyShadeController somfy;
|
||||
bool MQTTClass::begin() {
|
||||
this->suspended = false;
|
||||
return true;
|
||||
}
|
||||
bool MQTTClass::end() {
|
||||
this->suspended = true;
|
||||
this->disconnect();
|
||||
this->lastConnect = 0;
|
||||
this->connect();
|
||||
return true;
|
||||
}
|
||||
void MQTTClass::reset() {
|
||||
this->disconnect();
|
||||
this->lastConnect = 0;
|
||||
this->connect();
|
||||
}
|
||||
bool MQTTClass::loop() {
|
||||
if(settings.MQTT.enabled && !mqttClient.connected())
|
||||
if(settings.MQTT.enabled && !this->suspended && !mqttClient.connected())
|
||||
this->connect();
|
||||
if(settings.MQTT.enabled) mqttClient.loop();
|
||||
return true;
|
||||
|
|
@ -149,12 +151,12 @@ void MQTTClass::receive(const char *topic, byte*payload, uint32_t length) {
|
|||
}
|
||||
bool MQTTClass::connect() {
|
||||
if(mqttClient.connected()) {
|
||||
if(!settings.MQTT.enabled)
|
||||
if(!settings.MQTT.enabled || this->suspended)
|
||||
return this->disconnect();
|
||||
else
|
||||
return true;
|
||||
}
|
||||
if(settings.MQTT.enabled) {
|
||||
if(settings.MQTT.enabled && !this->suspended) {
|
||||
if(this->lastConnect + 10000 > millis()) return false;
|
||||
uint64_t mac = ESP.getEfuseMac();
|
||||
snprintf(this->clientId, sizeof(this->clientId), "client-%08x%08x", (uint32_t)((mac >> 32) & 0xFFFFFFFF), (uint32_t)(mac & 0xFFFFFFFF));
|
||||
|
|
|
|||
3
MQTT.h
3
MQTT.h
|
|
@ -6,6 +6,7 @@
|
|||
class MQTTClass {
|
||||
public:
|
||||
uint64_t lastConnect = 0;
|
||||
bool suspended = false;
|
||||
char clientId[32] = {'\0'};
|
||||
bool begin();
|
||||
bool loop();
|
||||
|
|
@ -26,7 +27,5 @@ class MQTTClass {
|
|||
bool subscribe(const char *topic);
|
||||
bool unsubscribe(const char *topic);
|
||||
static void receive(const char *topic, byte *payload, uint32_t length);
|
||||
|
||||
|
||||
};
|
||||
#endif
|
||||
|
|
|
|||
36
Somfy.cpp
36
Somfy.cpp
|
|
@ -599,6 +599,7 @@ void SomfyShade::clear() {
|
|||
this->tiltTime = 7000;
|
||||
this->stepSize = 100;
|
||||
this->repeats = 1;
|
||||
this->sortOrder = 255;
|
||||
}
|
||||
void SomfyGroup::clear() {
|
||||
this->setGroupId(255);
|
||||
|
|
@ -1219,16 +1220,16 @@ void SomfyShade::emitState(const char *evt) { this->emitState(255, evt); }
|
|||
void SomfyShade::emitState(uint8_t num, const char *evt) {
|
||||
char buf[420];
|
||||
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,\"sunSensor\":%s,\"light\":%s}",
|
||||
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,\"light\":%s,\"sortOrder\":%d}",
|
||||
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, this->hasSunSensor() ? "true" : "false", this->hasLight() ? "true" : "false");
|
||||
this->flipCommands ? "true" : "false", this->flipPosition ? "true": "false", this->flags, this->hasSunSensor() ? "true" : "false", this->hasLight() ? "true" : "false", this->sortOrder);
|
||||
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,\"sunSensor\":%s,\"light\":%s}",
|
||||
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,\"light\":%s,\"sortOrder\":%d}",
|
||||
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, this->hasSunSensor() ? "true" : "false", this->hasLight() ? "true" : "false");
|
||||
static_cast<uint8_t>(this->tiltType), this->flipCommands ? "true" : "false", this->flipPosition ? "true": "false", this->flags, this->hasSunSensor() ? "true" : "false", this->hasLight() ? "true" : "false", this->sortOrder);
|
||||
if(num >= 255) sockEmit.sendToClients(evt, buf);
|
||||
else sockEmit.sendToClient(num, evt, buf);
|
||||
if(mqtt.connected()) {
|
||||
|
|
@ -2232,7 +2233,7 @@ bool SomfyShade::toJSON(JsonObject &obj) {
|
|||
obj["sunSensor"] = this->hasSunSensor();
|
||||
obj["light"] = this->hasLight();
|
||||
obj["repeats"] = this->repeats;
|
||||
|
||||
obj["sortOrder"] = this->sortOrder;
|
||||
SomfyRemote::toJSON(obj);
|
||||
JsonArray arr = obj.createNestedArray("linkedRemotes");
|
||||
for(uint8_t i = 0; i < SOMFY_MAX_LINKED_REMOTES; i++) {
|
||||
|
|
@ -2273,6 +2274,7 @@ bool SomfyGroup::toJSON(JsonObject &obj) {
|
|||
obj["sunSensor"] = this->hasSunSensor();
|
||||
obj["flags"] = this->flags;
|
||||
obj["repeats"] = this->repeats;
|
||||
obj["sortOrder"] = this->sortOrder;
|
||||
SomfyRemote::toJSON(obj);
|
||||
JsonArray arr = obj.createNestedArray("linkedShades");
|
||||
for(uint8_t i = 0; i < SOMFY_MAX_GROUPED_SHADES; i++) {
|
||||
|
|
@ -2351,6 +2353,25 @@ uint8_t SomfyShadeController::getNextShadeId() {
|
|||
}
|
||||
return 255;
|
||||
}
|
||||
int8_t SomfyShadeController::getMaxShadeOrder() {
|
||||
int8_t order = -1;
|
||||
for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) {
|
||||
SomfyShade *shade = &this->shades[i];
|
||||
if(shade->getShadeId() == 255) continue;
|
||||
if(order < shade->sortOrder) order = shade->sortOrder;
|
||||
}
|
||||
return order;
|
||||
}
|
||||
int8_t SomfyShadeController::getMaxGroupOrder() {
|
||||
int8_t order = -1;
|
||||
for(uint8_t i = 0; i < SOMFY_MAX_GROUPS; i++) {
|
||||
SomfyGroup *group = &this->groups[i];
|
||||
if(group->getGroupId() == 255) continue;
|
||||
if(order < group->sortOrder) order = group->sortOrder;
|
||||
}
|
||||
return order;
|
||||
}
|
||||
|
||||
uint8_t SomfyShadeController::getNextGroupId() {
|
||||
// There is no shortcut for this since the deletion of
|
||||
// a group in the middle makes all of this very difficult.
|
||||
|
|
@ -2415,11 +2436,13 @@ SomfyShade *SomfyShadeController::addShade() {
|
|||
// So the next shade id will be the first one we run into with an id of 255 so
|
||||
// if it gets deleted in the middle then it will get the first slot that is empty.
|
||||
// There is no apparent way around this. In the future we might actually add an indexer
|
||||
// to it for sorting later.
|
||||
// to it for sorting later. The time has come so the sort order is set below.
|
||||
if(shadeId == 255) return nullptr;
|
||||
SomfyShade *shade = &this->shades[shadeId - 1];
|
||||
if(shade) {
|
||||
shade->setShadeId(shadeId);
|
||||
shade->sortOrder = this->getMaxShadeOrder() + 1;
|
||||
Serial.printf("Sort order set to %d\n", shade->sortOrder);
|
||||
this->isDirty = true;
|
||||
if(this->useNVS()) {
|
||||
for(uint8_t i = 0; i < sizeof(this->m_shadeIds); i++) {
|
||||
|
|
@ -2489,6 +2512,7 @@ SomfyGroup *SomfyShadeController::addGroup() {
|
|||
SomfyGroup *group = &this->groups[groupId - 1];
|
||||
if(group) {
|
||||
group->setGroupId(groupId);
|
||||
group->sortOrder = this->getMaxGroupOrder() + 1;
|
||||
this->isDirty = true;
|
||||
}
|
||||
return group;
|
||||
|
|
|
|||
4
Somfy.h
4
Somfy.h
|
|
@ -227,6 +227,7 @@ class SomfyShade : public SomfyRemote {
|
|||
bool settingTiltPos = false;
|
||||
uint32_t awaitMy = 0;
|
||||
public:
|
||||
int8_t sortOrder = 0;
|
||||
bool flipPosition = false;
|
||||
shade_types shadeType = shade_types::roller;
|
||||
tilt_types tiltType = tilt_types::none;
|
||||
|
|
@ -290,6 +291,7 @@ class SomfyGroup : public SomfyRemote {
|
|||
protected:
|
||||
uint8_t groupId = 255;
|
||||
public:
|
||||
int8_t sortOrder = 0;
|
||||
group_types groupType = group_types::channel;
|
||||
int8_t direction = 0; // 0 = stopped, 1=down, -1=up.
|
||||
char name[21] = "";
|
||||
|
|
@ -419,6 +421,8 @@ class SomfyShadeController {
|
|||
uint32_t startingAddress;
|
||||
uint8_t getNextShadeId();
|
||||
uint8_t getNextGroupId();
|
||||
int8_t getMaxShadeOrder();
|
||||
int8_t getMaxGroupOrder();
|
||||
uint32_t getNextRemoteAddress(uint8_t shadeId);
|
||||
SomfyShadeController();
|
||||
Transceiver transceiver;
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
730
Web.cpp
730
Web.cpp
|
|
@ -310,27 +310,29 @@ void Web::handleShadeCommand(WebServer& server) {
|
|||
}
|
||||
}
|
||||
else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No shade object supplied.\"}"));
|
||||
SomfyShade* shade = somfy.getShadeById(shadeId);
|
||||
if (shade) {
|
||||
Serial.print("Received:");
|
||||
Serial.println(server.arg("plain"));
|
||||
// Send the command to the shade.
|
||||
if (target <= 100)
|
||||
shade->moveToTarget(shade->transformPosition(target));
|
||||
else if (repeat > 0)
|
||||
shade->sendCommand(command, repeat);
|
||||
else
|
||||
shade->sendCommand(command);
|
||||
DynamicJsonDocument sdoc(512);
|
||||
JsonObject sobj = sdoc.to<JsonObject>();
|
||||
shade->toJSON(sobj);
|
||||
serializeJson(sdoc, g_content);
|
||||
server.send(200, _encoding_json, g_content);
|
||||
}
|
||||
else {
|
||||
server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade with the specified id not found.\"}"));
|
||||
}
|
||||
}
|
||||
SomfyShade* shade = somfy.getShadeById(shadeId);
|
||||
if (shade) {
|
||||
Serial.print("Received:");
|
||||
Serial.println(server.arg("plain"));
|
||||
// Send the command to the shade.
|
||||
if (target <= 100)
|
||||
shade->moveToTarget(shade->transformPosition(target));
|
||||
else if (repeat > 0)
|
||||
shade->sendCommand(command, repeat);
|
||||
else
|
||||
shade->sendCommand(command);
|
||||
DynamicJsonDocument sdoc(512);
|
||||
JsonObject sobj = sdoc.to<JsonObject>();
|
||||
shade->toJSON(sobj);
|
||||
serializeJson(sdoc, g_content);
|
||||
server.send(200, _encoding_json, g_content);
|
||||
}
|
||||
else {
|
||||
server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade with the specified id not found.\"}"));
|
||||
}
|
||||
else
|
||||
server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Invalid Http method\"}"));
|
||||
}
|
||||
void Web::handleRepeatCommand(WebServer& server) {
|
||||
webServer.sendCORSHeaders(server);
|
||||
|
|
@ -415,40 +417,161 @@ void Web::handleRepeatCommand(WebServer& server) {
|
|||
server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Invalid Http method\"}"));
|
||||
}
|
||||
}
|
||||
void Web::begin() {
|
||||
Serial.println("Creating Web MicroServices...");
|
||||
server.enableCORS(true);
|
||||
const char *keys[1] = {"apikey"};
|
||||
server.collectHeaders(keys, 1);
|
||||
apiServer.collectHeaders(keys, 1);
|
||||
apiServer.enableCORS(true);
|
||||
apiServer.on("/discovery", []() {
|
||||
HTTPMethod method = apiServer.method();
|
||||
if (method == HTTP_POST || method == HTTP_GET) {
|
||||
Serial.println("Discovery Requested");
|
||||
DynamicJsonDocument doc(16384);
|
||||
JsonObject obj = doc.to<JsonObject>();
|
||||
obj["serverId"] = settings.serverId;
|
||||
obj["version"] = settings.fwVersion;
|
||||
obj["model"] = "ESPSomfyRTS";
|
||||
obj["hostname"] = settings.hostname;
|
||||
obj["authType"] = static_cast<uint8_t>(settings.Security.type);
|
||||
obj["permissions"] = settings.Security.permissions;
|
||||
JsonArray arrShades = obj.createNestedArray("shades");
|
||||
somfy.toJSONShades(arrShades);
|
||||
JsonArray arrGroups = obj.createNestedArray("groups");
|
||||
somfy.toJSONGroups(arrGroups);
|
||||
serializeJson(doc, g_content);
|
||||
apiServer.send(200, _encoding_json, g_content);
|
||||
void Web::handleGroupCommand(WebServer &server) {
|
||||
webServer.sendCORSHeaders(server);
|
||||
if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; }
|
||||
HTTPMethod method = server.method();
|
||||
uint8_t groupId = 255;
|
||||
int8_t repeat = -1;
|
||||
somfy_commands command = somfy_commands::My;
|
||||
if (method == HTTP_GET || method == HTTP_PUT || method == HTTP_POST) {
|
||||
if (server.hasArg("groupId")) {
|
||||
groupId = atoi(server.arg("groupId").c_str());
|
||||
if (server.hasArg("command")) command = translateSomfyCommand(server.arg("command"));
|
||||
if(server.hasArg("repeat")) repeat = atoi(server.arg("repeat").c_str());
|
||||
}
|
||||
apiServer.send(500, _encoding_text, "Invalid http method");
|
||||
});
|
||||
apiServer.on("/shades", []() { webServer.handleGetShades(apiServer); });
|
||||
apiServer.on("/groups", []() { webServer.handleGetGroups(apiServer); });
|
||||
apiServer.on("/login", []() { webServer.handleLogin(apiServer); });
|
||||
apiServer.onNotFound([]() {
|
||||
Serial.print("Request 404:");
|
||||
HTTPMethod method = apiServer.method();
|
||||
else if (server.hasArg("plain")) {
|
||||
Serial.println("Sending Group Command");
|
||||
DynamicJsonDocument doc(256);
|
||||
DeserializationError err = deserializeJson(doc, server.arg("plain"));
|
||||
if (err) {
|
||||
switch (err.code()) {
|
||||
case DeserializationError::InvalidInput:
|
||||
server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Invalid JSON payload\"}"));
|
||||
break;
|
||||
case DeserializationError::NoMemory:
|
||||
server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Out of memory parsing JSON\"}"));
|
||||
break;
|
||||
default:
|
||||
server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"General JSON Deserialization failed\"}"));
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
else {
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
if (obj.containsKey("groupId")) groupId = obj["groupId"];
|
||||
else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No group id was supplied.\"}"));
|
||||
if (obj.containsKey("command")) {
|
||||
String scmd = obj["command"];
|
||||
command = translateSomfyCommand(scmd);
|
||||
}
|
||||
if(obj.containsKey("repeat")) repeat = obj["repeat"].as<uint8_t>();
|
||||
}
|
||||
}
|
||||
else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No group object supplied.\"}"));
|
||||
SomfyGroup * group = somfy.getGroupById(groupId);
|
||||
if (group) {
|
||||
Serial.print("Received:");
|
||||
Serial.println(server.arg("plain"));
|
||||
// Send the command to the group.
|
||||
if(repeat > 0) group->sendCommand(command, repeat);
|
||||
else group->sendCommand(command);
|
||||
DynamicJsonDocument sdoc(512);
|
||||
JsonObject sobj = sdoc.to<JsonObject>();
|
||||
group->toJSON(sobj);
|
||||
serializeJson(sdoc, g_content);
|
||||
server.send(200, _encoding_json, g_content);
|
||||
}
|
||||
else {
|
||||
server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Group with the specified id not found.\"}"));
|
||||
}
|
||||
}
|
||||
else
|
||||
server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Invalid Http method\"}"));
|
||||
}
|
||||
void Web::handleTiltCommand(WebServer &server) {
|
||||
webServer.sendCORSHeaders(server);
|
||||
if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; }
|
||||
HTTPMethod method = server.method();
|
||||
uint8_t shadeId = 255;
|
||||
uint8_t target = 255;
|
||||
somfy_commands command = somfy_commands::My;
|
||||
if (method == HTTP_GET || method == HTTP_PUT || method == HTTP_POST) {
|
||||
if (server.hasArg("shadeId")) {
|
||||
shadeId = atoi(server.arg("shadeId").c_str());
|
||||
if (server.hasArg("command")) command = translateSomfyCommand(server.arg("command"));
|
||||
else if(server.hasArg("target")) target = atoi(server.arg("target").c_str());
|
||||
}
|
||||
else if (server.hasArg("plain")) {
|
||||
Serial.println("Sending Shade Tilt Command");
|
||||
DynamicJsonDocument doc(256);
|
||||
DeserializationError err = deserializeJson(doc, server.arg("plain"));
|
||||
if (err) {
|
||||
switch (err.code()) {
|
||||
case DeserializationError::InvalidInput:
|
||||
server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Invalid JSON payload\"}"));
|
||||
break;
|
||||
case DeserializationError::NoMemory:
|
||||
server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Out of memory parsing JSON\"}"));
|
||||
break;
|
||||
default:
|
||||
server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"General JSON Deserialization failed\"}"));
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
else {
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
if (obj.containsKey("shadeId")) shadeId = obj["shadeId"];
|
||||
else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No shade id was supplied.\"}"));
|
||||
if (obj.containsKey("command")) {
|
||||
String scmd = obj["command"];
|
||||
command = translateSomfyCommand(scmd);
|
||||
}
|
||||
else if(obj.containsKey("target")) {
|
||||
target = obj["target"].as<uint8_t>();
|
||||
}
|
||||
}
|
||||
}
|
||||
else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No shade object supplied.\"}"));
|
||||
SomfyShade* shade = somfy.getShadeById(shadeId);
|
||||
if (shade) {
|
||||
Serial.print("Received:");
|
||||
Serial.println(server.arg("plain"));
|
||||
// Send the command to the shade.
|
||||
if(target <= 100)
|
||||
shade->moveToTiltTarget(shade->transformPosition(target));
|
||||
else
|
||||
shade->sendTiltCommand(command);
|
||||
DynamicJsonDocument sdoc(512);
|
||||
JsonObject sobj = sdoc.to<JsonObject>();
|
||||
shade->toJSON(sobj);
|
||||
serializeJson(sdoc, g_content);
|
||||
server.send(200, _encoding_json, g_content);
|
||||
}
|
||||
else {
|
||||
server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade with the specified id not found.\"}"));
|
||||
}
|
||||
}
|
||||
else
|
||||
server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Invalid Http method\"}"));
|
||||
}
|
||||
void Web::handleDiscovery(WebServer &server) {
|
||||
HTTPMethod method = apiServer.method();
|
||||
if (method == HTTP_POST || method == HTTP_GET) {
|
||||
Serial.println("Discovery Requested");
|
||||
DynamicJsonDocument doc(16384);
|
||||
JsonObject obj = doc.to<JsonObject>();
|
||||
obj["serverId"] = settings.serverId;
|
||||
obj["version"] = settings.fwVersion;
|
||||
obj["model"] = "ESPSomfyRTS";
|
||||
obj["hostname"] = settings.hostname;
|
||||
obj["authType"] = static_cast<uint8_t>(settings.Security.type);
|
||||
obj["permissions"] = settings.Security.permissions;
|
||||
JsonArray arrShades = obj.createNestedArray("shades");
|
||||
somfy.toJSONShades(arrShades);
|
||||
JsonArray arrGroups = obj.createNestedArray("groups");
|
||||
somfy.toJSONGroups(arrGroups);
|
||||
serializeJson(doc, g_content);
|
||||
server.send(200, _encoding_json, g_content);
|
||||
}
|
||||
else
|
||||
server.send(500, _encoding_text, "Invalid http method");
|
||||
}
|
||||
void Web::handleNotFound(WebServer &server) {
|
||||
HTTPMethod method = server.method();
|
||||
Serial.printf("Request %s 404-%d ", server.uri().c_str(), method);
|
||||
switch (method) {
|
||||
case HTTP_POST:
|
||||
Serial.print("POST ");
|
||||
|
|
@ -460,8 +583,8 @@ void Web::begin() {
|
|||
Serial.print("PUT ");
|
||||
break;
|
||||
case HTTP_OPTIONS:
|
||||
Serial.print("OPTIONS ");
|
||||
apiServer.send(200, "OK");
|
||||
Serial.println("OPTIONS ");
|
||||
server.send(200, "OK");
|
||||
return;
|
||||
default:
|
||||
Serial.print("[");
|
||||
|
|
@ -470,210 +593,35 @@ void Web::begin() {
|
|||
break;
|
||||
|
||||
}
|
||||
snprintf(g_content, sizeof(g_content), "404 Service Not Found: %s", apiServer.uri().c_str());
|
||||
apiServer.send(404, _encoding_text, g_content);
|
||||
});
|
||||
snprintf(g_content, sizeof(g_content), "404 Service Not Found: %s", server.uri().c_str());
|
||||
server.send(404, _encoding_text, g_content);
|
||||
}
|
||||
void Web::begin() {
|
||||
Serial.println("Creating Web MicroServices...");
|
||||
server.enableCORS(true);
|
||||
const char *keys[1] = {"apikey"};
|
||||
server.collectHeaders(keys, 1);
|
||||
// API Server Handlers
|
||||
apiServer.collectHeaders(keys, 1);
|
||||
apiServer.enableCORS(true);
|
||||
apiServer.on("/discovery", []() { webServer.handleDiscovery(apiServer); });
|
||||
apiServer.on("/shades", []() { webServer.handleGetShades(apiServer); });
|
||||
apiServer.on("/groups", []() { webServer.handleGetGroups(apiServer); });
|
||||
apiServer.on("/login", []() { webServer.handleLogin(apiServer); });
|
||||
apiServer.onNotFound([]() { webServer.handleNotFound(apiServer); });
|
||||
apiServer.on("/controller", []() { webServer.handleController(apiServer); });
|
||||
apiServer.on("/shadeCommand", []() {
|
||||
webServer.sendCORSHeaders(apiServer);
|
||||
if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; }
|
||||
HTTPMethod method = apiServer.method();
|
||||
uint8_t shadeId = 255;
|
||||
uint8_t target = 255;
|
||||
int8_t repeat = -1;
|
||||
somfy_commands command = somfy_commands::My;
|
||||
if (method == HTTP_GET || method == HTTP_PUT || method == HTTP_POST) {
|
||||
if (apiServer.hasArg("shadeId")) {
|
||||
shadeId = atoi(apiServer.arg("shadeId").c_str());
|
||||
if (apiServer.hasArg("command")) command = translateSomfyCommand(apiServer.arg("command"));
|
||||
else if(apiServer.hasArg("target")) target = atoi(apiServer.arg("target").c_str());
|
||||
if(apiServer.hasArg("repeat")) repeat = atoi(apiServer.arg("repeat").c_str());
|
||||
}
|
||||
else if (apiServer.hasArg("plain")) {
|
||||
Serial.println("Sending Shade Command");
|
||||
DynamicJsonDocument doc(256);
|
||||
DeserializationError err = deserializeJson(doc, apiServer.arg("plain"));
|
||||
if (err) {
|
||||
switch (err.code()) {
|
||||
case DeserializationError::InvalidInput:
|
||||
apiServer.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Invalid JSON payload\"}"));
|
||||
break;
|
||||
case DeserializationError::NoMemory:
|
||||
apiServer.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Out of memory parsing JSON\"}"));
|
||||
break;
|
||||
default:
|
||||
apiServer.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"General JSON Deserialization failed\"}"));
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
else {
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
if (obj.containsKey("shadeId")) shadeId = obj["shadeId"];
|
||||
else apiServer.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No shade id was supplied.\"}"));
|
||||
if (obj.containsKey("command")) {
|
||||
String scmd = obj["command"];
|
||||
command = translateSomfyCommand(scmd);
|
||||
}
|
||||
else if(obj.containsKey("target")) {
|
||||
target = obj["target"].as<uint8_t>();
|
||||
}
|
||||
if(obj.containsKey("repeat")) repeat = obj["repeat"].as<uint8_t>();
|
||||
}
|
||||
}
|
||||
else apiServer.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No shade object supplied.\"}"));
|
||||
}
|
||||
SomfyShade* shade = somfy.getShadeById(shadeId);
|
||||
if (shade) {
|
||||
Serial.print("Received:");
|
||||
Serial.println(apiServer.arg("plain"));
|
||||
// Send the command to the shade.
|
||||
if(target <= 100)
|
||||
shade->moveToTarget(shade->transformPosition(target));
|
||||
else if(repeat > 0)
|
||||
shade->sendCommand(command, repeat);
|
||||
else
|
||||
shade->sendCommand(command);
|
||||
DynamicJsonDocument sdoc(512);
|
||||
JsonObject sobj = sdoc.to<JsonObject>();
|
||||
shade->toJSON(sobj);
|
||||
serializeJson(sdoc, g_content);
|
||||
apiServer.send(200, _encoding_json, g_content);
|
||||
}
|
||||
else {
|
||||
apiServer.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade with the specified id not found.\"}"));
|
||||
}
|
||||
});
|
||||
apiServer.on("/groupCommand", []() {
|
||||
webServer.sendCORSHeaders(apiServer);
|
||||
if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; }
|
||||
HTTPMethod method = apiServer.method();
|
||||
uint8_t groupId = 255;
|
||||
int8_t repeat = -1;
|
||||
somfy_commands command = somfy_commands::My;
|
||||
if (method == HTTP_GET || method == HTTP_PUT || method == HTTP_POST) {
|
||||
if (apiServer.hasArg("groupId")) {
|
||||
groupId = atoi(apiServer.arg("groupId").c_str());
|
||||
if (apiServer.hasArg("command")) command = translateSomfyCommand(apiServer.arg("command"));
|
||||
if(apiServer.hasArg("repeat")) repeat = atoi(apiServer.arg("repeat").c_str());
|
||||
}
|
||||
else if (apiServer.hasArg("plain")) {
|
||||
Serial.println("Sending Group Command");
|
||||
DynamicJsonDocument doc(256);
|
||||
DeserializationError err = deserializeJson(doc, apiServer.arg("plain"));
|
||||
if (err) {
|
||||
switch (err.code()) {
|
||||
case DeserializationError::InvalidInput:
|
||||
apiServer.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Invalid JSON payload\"}"));
|
||||
break;
|
||||
case DeserializationError::NoMemory:
|
||||
apiServer.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Out of memory parsing JSON\"}"));
|
||||
break;
|
||||
default:
|
||||
apiServer.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"General JSON Deserialization failed\"}"));
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
else {
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
if (obj.containsKey("groupId")) groupId = obj["groupId"];
|
||||
else apiServer.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No group id was supplied.\"}"));
|
||||
if (obj.containsKey("command")) {
|
||||
String scmd = obj["command"];
|
||||
command = translateSomfyCommand(scmd);
|
||||
}
|
||||
if(obj.containsKey("repeat")) repeat = obj["repeat"].as<uint8_t>();
|
||||
}
|
||||
}
|
||||
else apiServer.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No group object supplied.\"}"));
|
||||
}
|
||||
SomfyGroup * group = somfy.getGroupById(groupId);
|
||||
if (group) {
|
||||
Serial.print("Received:");
|
||||
Serial.println(apiServer.arg("plain"));
|
||||
// Send the command to the group.
|
||||
if(repeat != -1) group->sendCommand(command, repeat);
|
||||
else group->sendCommand(command);
|
||||
DynamicJsonDocument sdoc(512);
|
||||
JsonObject sobj = sdoc.to<JsonObject>();
|
||||
group->toJSON(sobj);
|
||||
serializeJson(sdoc, g_content);
|
||||
apiServer.send(200, _encoding_json, g_content);
|
||||
}
|
||||
else {
|
||||
apiServer.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Group with the specified id not found.\"}"));
|
||||
}
|
||||
});
|
||||
apiServer.on("/tiltCommand", []() {
|
||||
webServer.sendCORSHeaders(apiServer);
|
||||
if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; }
|
||||
HTTPMethod method = apiServer.method();
|
||||
uint8_t shadeId = 255;
|
||||
uint8_t target = 255;
|
||||
somfy_commands command = somfy_commands::My;
|
||||
if (method == HTTP_GET || method == HTTP_PUT || method == HTTP_POST) {
|
||||
if (apiServer.hasArg("shadeId")) {
|
||||
shadeId = atoi(apiServer.arg("shadeId").c_str());
|
||||
if (apiServer.hasArg("command")) command = translateSomfyCommand(apiServer.arg("command"));
|
||||
else if(apiServer.hasArg("target")) target = atoi(apiServer.arg("target").c_str());
|
||||
}
|
||||
else if (apiServer.hasArg("plain")) {
|
||||
Serial.println("Sending Shade Tilt Command");
|
||||
DynamicJsonDocument doc(256);
|
||||
DeserializationError err = deserializeJson(doc, apiServer.arg("plain"));
|
||||
if (err) {
|
||||
switch (err.code()) {
|
||||
case DeserializationError::InvalidInput:
|
||||
apiServer.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Invalid JSON payload\"}"));
|
||||
break;
|
||||
case DeserializationError::NoMemory:
|
||||
apiServer.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Out of memory parsing JSON\"}"));
|
||||
break;
|
||||
default:
|
||||
apiServer.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"General JSON Deserialization failed\"}"));
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
else {
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
if (obj.containsKey("shadeId")) shadeId = obj["shadeId"];
|
||||
else apiServer.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No shade id was supplied.\"}"));
|
||||
if (obj.containsKey("command")) {
|
||||
String scmd = obj["command"];
|
||||
command = translateSomfyCommand(scmd);
|
||||
}
|
||||
else if(obj.containsKey("target")) {
|
||||
target = obj["target"].as<uint8_t>();
|
||||
}
|
||||
}
|
||||
}
|
||||
else apiServer.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No shade object supplied.\"}"));
|
||||
}
|
||||
SomfyShade* shade = somfy.getShadeById(shadeId);
|
||||
if (shade) {
|
||||
Serial.print("Received:");
|
||||
Serial.println(apiServer.arg("plain"));
|
||||
// Send the command to the shade.
|
||||
if(target <= 100)
|
||||
shade->moveToTiltTarget(shade->transformPosition(target));
|
||||
else
|
||||
shade->sendTiltCommand(command);
|
||||
DynamicJsonDocument sdoc(512);
|
||||
JsonObject sobj = sdoc.to<JsonObject>();
|
||||
shade->toJSON(sobj);
|
||||
serializeJson(sdoc, g_content);
|
||||
apiServer.send(200, _encoding_json, g_content);
|
||||
}
|
||||
else {
|
||||
apiServer.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade with the specified id not found.\"}"));
|
||||
}
|
||||
});
|
||||
apiServer.on("/shadeCommand", []() { webServer.handleShadeCommand(apiServer); });
|
||||
apiServer.on("/groupCommand", []() { webServer.handleGroupCommand(apiServer); });
|
||||
apiServer.on("/tiltCommand", []() { webServer.handleTiltCommand(apiServer); });
|
||||
apiServer.on("/repeatCommand", []() { webServer.handleRepeatCommand(apiServer); });
|
||||
|
||||
server.on("/upnp.xml", []() {
|
||||
SSDP.schema(server.client());
|
||||
});
|
||||
// Web Interface
|
||||
server.on("/tiltCommand", []() { webServer.handleTiltCommand(server); });
|
||||
server.on("/repeatCommand", []() { webServer.handleRepeatCommand(server); });
|
||||
server.on("/shadeCommand", []() { webServer.handleShadeCommand(server); });
|
||||
server.on("/groupCommand", []() { webServer.handleGroupCommand(server); });
|
||||
|
||||
server.on("/upnp.xml", []() { SSDP.schema(server.client()); });
|
||||
server.on("/", []() { webServer.handleStreamFile(server, "/index.html", _encoding_html); });
|
||||
server.on("/login", []() { webServer.handleLogin(server); });
|
||||
server.on("/loginContext", []() { webServer.handleLoginContext(server); });
|
||||
|
|
@ -739,33 +687,7 @@ void Web::begin() {
|
|||
server.on("/icons.css", []() { webServer.sendCacheHeaders(604800); webServer.handleStreamFile(server, "/icons.css", "text/css"); });
|
||||
server.on("/favicon.png", []() { webServer.sendCacheHeaders(604800); webServer.handleStreamFile(server, "/favicon.png", "image/png"); });
|
||||
server.on("/icon.png", []() { webServer.sendCacheHeaders(604800); webServer.handleStreamFile(server, "/icon.png", "image/png"); });
|
||||
server.onNotFound([]() {
|
||||
HTTPMethod method = server.method();
|
||||
Serial.printf("Request %s 404-%d ", server.uri().c_str(), method);
|
||||
switch (method) {
|
||||
case HTTP_POST:
|
||||
Serial.print("POST ");
|
||||
break;
|
||||
case HTTP_GET:
|
||||
Serial.print("GET ");
|
||||
break;
|
||||
case HTTP_PUT:
|
||||
Serial.print("PUT ");
|
||||
break;
|
||||
case HTTP_OPTIONS:
|
||||
Serial.println("OPTIONS ");
|
||||
server.send(200, "OK");
|
||||
return;
|
||||
default:
|
||||
Serial.print("[");
|
||||
Serial.print(method);
|
||||
Serial.print("]");
|
||||
break;
|
||||
|
||||
}
|
||||
snprintf(g_content, sizeof(g_content), "404 Service Not Found: %s", server.uri().c_str());
|
||||
server.send(404, _encoding_text, g_content);
|
||||
});
|
||||
server.onNotFound([]() { webServer.handleNotFound(server); });
|
||||
server.on("/controller", []() { webServer.handleController(server); });
|
||||
server.on("/shades", []() { webServer.handleGetShades(server); });
|
||||
server.on("/groups", []() { webServer.handleGetGroups(server); });
|
||||
|
|
@ -845,7 +767,7 @@ void Web::begin() {
|
|||
JsonObject obj = doc.to<JsonObject>();
|
||||
shade->toJSON(obj);
|
||||
serializeJson(doc, g_content);
|
||||
Serial.println(g_content);
|
||||
//Serial.println(g_content);
|
||||
server.send(200, _encoding_json, g_content);
|
||||
}
|
||||
else {
|
||||
|
|
@ -1157,134 +1079,6 @@ void Web::begin() {
|
|||
else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No group object supplied.\"}"));
|
||||
}
|
||||
});
|
||||
server.on("/tiltCommand", []() {
|
||||
webServer.sendCORSHeaders(server);
|
||||
if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; }
|
||||
HTTPMethod method = server.method();
|
||||
uint8_t shadeId = 255;
|
||||
uint8_t target = 255;
|
||||
somfy_commands command = somfy_commands::My;
|
||||
if (method == HTTP_GET || method == HTTP_PUT || method == HTTP_POST) {
|
||||
if (server.hasArg("shadeId")) {
|
||||
shadeId = atoi(server.arg("shadeId").c_str());
|
||||
if (server.hasArg("command")) command = translateSomfyCommand(server.arg("command"));
|
||||
else if(server.hasArg("target")) target = atoi(server.arg("target").c_str());
|
||||
}
|
||||
else if (server.hasArg("plain")) {
|
||||
Serial.println("Sending Shade Tilt Command");
|
||||
DynamicJsonDocument doc(256);
|
||||
DeserializationError err = deserializeJson(doc, server.arg("plain"));
|
||||
if (err) {
|
||||
switch (err.code()) {
|
||||
case DeserializationError::InvalidInput:
|
||||
server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Invalid JSON payload\"}"));
|
||||
break;
|
||||
case DeserializationError::NoMemory:
|
||||
server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Out of memory parsing JSON\"}"));
|
||||
break;
|
||||
default:
|
||||
server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"General JSON Deserialization failed\"}"));
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
else {
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
if (obj.containsKey("shadeId")) shadeId = obj["shadeId"];
|
||||
else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No shade id was supplied.\"}"));
|
||||
if (obj.containsKey("command")) {
|
||||
String scmd = obj["command"];
|
||||
command = translateSomfyCommand(scmd);
|
||||
}
|
||||
else if(obj.containsKey("target")) {
|
||||
target = obj["target"].as<uint8_t>();
|
||||
}
|
||||
}
|
||||
}
|
||||
else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No shade object supplied.\"}"));
|
||||
}
|
||||
SomfyShade* shade = somfy.getShadeById(shadeId);
|
||||
if (shade) {
|
||||
Serial.print("Received:");
|
||||
Serial.println(server.arg("plain"));
|
||||
// Send the command to the shade.
|
||||
if(target <= 100)
|
||||
shade->moveToTiltTarget(shade->transformPosition(target));
|
||||
else
|
||||
shade->sendTiltCommand(command);
|
||||
DynamicJsonDocument sdoc(512);
|
||||
JsonObject sobj = sdoc.to<JsonObject>();
|
||||
shade->toJSON(sobj);
|
||||
serializeJson(sdoc, g_content);
|
||||
server.send(200, _encoding_json, g_content);
|
||||
}
|
||||
else {
|
||||
server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade with the specified id not found.\"}"));
|
||||
}
|
||||
});
|
||||
server.on("/repeatCommand", []() { webServer.handleRepeatCommand(server); });
|
||||
server.on("/shadeCommand", []() { webServer.handleShadeCommand(server); });
|
||||
server.on("/groupCommand", []() {
|
||||
webServer.sendCORSHeaders(server);
|
||||
if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; }
|
||||
HTTPMethod method = server.method();
|
||||
uint8_t groupId = 255;
|
||||
int8_t repeat = -1;
|
||||
somfy_commands command = somfy_commands::My;
|
||||
if (method == HTTP_GET || method == HTTP_PUT || method == HTTP_POST) {
|
||||
if (server.hasArg("groupId")) {
|
||||
groupId = atoi(server.arg("groupId").c_str());
|
||||
if (server.hasArg("command")) command = translateSomfyCommand(server.arg("command"));
|
||||
if(server.hasArg("repeat")) repeat = atoi(server.arg("repeat").c_str());
|
||||
}
|
||||
else if (server.hasArg("plain")) {
|
||||
Serial.println("Sending Group Command");
|
||||
DynamicJsonDocument doc(256);
|
||||
DeserializationError err = deserializeJson(doc, server.arg("plain"));
|
||||
if (err) {
|
||||
switch (err.code()) {
|
||||
case DeserializationError::InvalidInput:
|
||||
server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Invalid JSON payload\"}"));
|
||||
break;
|
||||
case DeserializationError::NoMemory:
|
||||
server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Out of memory parsing JSON\"}"));
|
||||
break;
|
||||
default:
|
||||
server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"General JSON Deserialization failed\"}"));
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
else {
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
if (obj.containsKey("groupId")) groupId = obj["groupId"];
|
||||
else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No group id was supplied.\"}"));
|
||||
if (obj.containsKey("command")) {
|
||||
String scmd = obj["command"];
|
||||
command = translateSomfyCommand(scmd);
|
||||
}
|
||||
if(obj.containsKey("repeat")) repeat = obj["repeat"].as<uint8_t>();
|
||||
}
|
||||
}
|
||||
else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No group object supplied.\"}"));
|
||||
}
|
||||
SomfyGroup * group = somfy.getGroupById(groupId);
|
||||
if (group) {
|
||||
Serial.print("Received:");
|
||||
Serial.println(server.arg("plain"));
|
||||
// Send the command to the group.
|
||||
if(repeat > 0) group->sendCommand(command, repeat);
|
||||
else group->sendCommand(command);
|
||||
DynamicJsonDocument sdoc(512);
|
||||
JsonObject sobj = sdoc.to<JsonObject>();
|
||||
group->toJSON(sobj);
|
||||
serializeJson(sdoc, g_content);
|
||||
server.send(200, _encoding_json, g_content);
|
||||
}
|
||||
else {
|
||||
server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Group with the specified id not found.\"}"));
|
||||
}
|
||||
});
|
||||
server.on("/setMyPosition", []() {
|
||||
webServer.sendCORSHeaders(server);
|
||||
if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; }
|
||||
|
|
@ -1324,23 +1118,25 @@ void Web::begin() {
|
|||
}
|
||||
}
|
||||
else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No shade object supplied.\"}"));
|
||||
SomfyShade* shade = somfy.getShadeById(shadeId);
|
||||
if (shade) {
|
||||
// Send the command to the shade.
|
||||
if(tilt < 0) tilt = shade->myPos;
|
||||
if(shade->tiltType == tilt_types::none) tilt = -1;
|
||||
if(pos >= 0 && pos <= 100)
|
||||
shade->setMyPosition(shade->transformPosition(pos), shade->transformPosition(tilt));
|
||||
DynamicJsonDocument sdoc(512);
|
||||
JsonObject sobj = sdoc.to<JsonObject>();
|
||||
shade->toJSON(sobj);
|
||||
serializeJson(sdoc, g_content);
|
||||
server.send(200, _encoding_json, g_content);
|
||||
}
|
||||
else {
|
||||
server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade with the specified id not found.\"}"));
|
||||
}
|
||||
}
|
||||
SomfyShade* shade = somfy.getShadeById(shadeId);
|
||||
if (shade) {
|
||||
// Send the command to the shade.
|
||||
if(tilt < 0) tilt = shade->myPos;
|
||||
if(shade->tiltType == tilt_types::none) tilt = -1;
|
||||
if(pos >= 0 && pos <= 100)
|
||||
shade->setMyPosition(shade->transformPosition(pos), shade->transformPosition(tilt));
|
||||
DynamicJsonDocument sdoc(512);
|
||||
JsonObject sobj = sdoc.to<JsonObject>();
|
||||
shade->toJSON(sobj);
|
||||
serializeJson(sdoc, g_content);
|
||||
server.send(200, _encoding_json, g_content);
|
||||
}
|
||||
else {
|
||||
server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade with the specified id not found.\"}"));
|
||||
}
|
||||
else
|
||||
server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Invalid Http method\"}"));
|
||||
});
|
||||
server.on("/setRollingCode", []() {
|
||||
webServer.sendCORSHeaders(server);
|
||||
|
|
@ -1780,7 +1576,6 @@ void Web::begin() {
|
|||
});
|
||||
server.on("/updateFirmware", HTTP_POST, []() {
|
||||
webServer.sendCORSHeaders(server);
|
||||
somfy.transceiver.end(); // Shut down the radio so we do not get any interrupts during this process.
|
||||
if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; }
|
||||
if (Update.hasError())
|
||||
server.send(500, _encoding_json, "{\"status\":\"ERROR\",\"desc\":\"Error updating firmware: \"}");
|
||||
|
|
@ -1795,6 +1590,10 @@ void Web::begin() {
|
|||
if (!Update.begin(UPDATE_SIZE_UNKNOWN)) { //start with max available size
|
||||
Update.printError(Serial);
|
||||
}
|
||||
else {
|
||||
somfy.transceiver.end(); // Shut down the radio so we do not get any interrupts during this process.
|
||||
mqtt.end();
|
||||
}
|
||||
}
|
||||
else if (upload.status == UPLOAD_FILE_WRITE) {
|
||||
/* flashing firmware to ESP*/
|
||||
|
|
@ -1837,7 +1636,6 @@ void Web::begin() {
|
|||
});
|
||||
server.on("/updateApplication", HTTP_POST, []() {
|
||||
webServer.sendCORSHeaders(server);
|
||||
somfy.transceiver.end(); // Shut down the radio so we do not get any interrupts during this process.
|
||||
if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; }
|
||||
server.sendHeader("Connection", "close");
|
||||
server.send(200, _encoding_json, "{\"status\":\"ERROR\",\"desc\":\"Updating Application: \"}");
|
||||
|
|
@ -1850,6 +1648,10 @@ void Web::begin() {
|
|||
if (!Update.begin(UPDATE_SIZE_UNKNOWN, U_SPIFFS)) { //start with max available size and tell it we are updating the file system.
|
||||
Update.printError(Serial);
|
||||
}
|
||||
else {
|
||||
somfy.transceiver.end(); // Shut down the radio so we do not get any interrupts during this process.
|
||||
mqtt.end();
|
||||
}
|
||||
}
|
||||
else if (upload.status == UPLOAD_FILE_WRITE) {
|
||||
/* flashing littlefs to ESP*/
|
||||
|
|
@ -2265,6 +2067,72 @@ void Web::begin() {
|
|||
serializeJson(doc, g_content);
|
||||
server.send(200, _encoding_json, g_content);
|
||||
});
|
||||
server.on("/shadeSortOrder", []() {
|
||||
if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; }
|
||||
DynamicJsonDocument doc(512);
|
||||
Serial.print("Plain: ");
|
||||
Serial.print(server.method());
|
||||
Serial.println(server.arg("plain"));
|
||||
DeserializationError err = deserializeJson(doc, server.arg("plain"));
|
||||
if (err) {
|
||||
Serial.print("Error parsing JSON ");
|
||||
Serial.println(err.c_str());
|
||||
String msg = err.c_str();
|
||||
server.send(400, _encoding_html, "Error parsing JSON body<br>" + msg);
|
||||
}
|
||||
else {
|
||||
JsonArray arr = doc.as<JsonArray>();
|
||||
HTTPMethod method = server.method();
|
||||
if (method == HTTP_POST || method == HTTP_PUT) {
|
||||
// Parse out all the inputs.
|
||||
uint8_t order = 0;
|
||||
for(JsonVariant v : arr) {
|
||||
uint8_t shadeId = v.as<uint8_t>();
|
||||
if (shadeId != 255) {
|
||||
SomfyShade *shade = somfy.getShadeById(shadeId);
|
||||
if(shade) shade->sortOrder = order++;
|
||||
}
|
||||
}
|
||||
server.send(200, "application/json", "{\"status\":\"OK\",\"desc\":\"Successfully set shade order\"}");
|
||||
}
|
||||
else {
|
||||
server.send(201, "application/json", "{\"status\":\"ERROR\",\"desc\":\"Invalid HTTP Method: \"}");
|
||||
}
|
||||
}
|
||||
});
|
||||
server.on("/groupSortOrder", []() {
|
||||
if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; }
|
||||
DynamicJsonDocument doc(512);
|
||||
Serial.print("Plain: ");
|
||||
Serial.print(server.method());
|
||||
Serial.println(server.arg("plain"));
|
||||
DeserializationError err = deserializeJson(doc, server.arg("plain"));
|
||||
if (err) {
|
||||
Serial.print("Error parsing JSON ");
|
||||
Serial.println(err.c_str());
|
||||
String msg = err.c_str();
|
||||
server.send(400, _encoding_html, "Error parsing JSON body<br>" + msg);
|
||||
}
|
||||
else {
|
||||
JsonArray arr = doc.as<JsonArray>();
|
||||
HTTPMethod method = server.method();
|
||||
if (method == HTTP_POST || method == HTTP_PUT) {
|
||||
// Parse out all the inputs.
|
||||
uint8_t order = 0;
|
||||
for(JsonVariant v : arr) {
|
||||
uint8_t groupId = v.as<uint8_t>();
|
||||
if (groupId != 255) {
|
||||
SomfyGroup *group = somfy.getGroupById(groupId);
|
||||
if(group) group->sortOrder = order++;
|
||||
}
|
||||
}
|
||||
server.send(200, "application/json", "{\"status\":\"OK\",\"desc\":\"Successfully set group order\"}");
|
||||
}
|
||||
else {
|
||||
server.send(201, "application/json", "{\"status\":\"ERROR\",\"desc\":\"Invalid HTTP Method: \"}");
|
||||
}
|
||||
}
|
||||
});
|
||||
server.begin();
|
||||
apiServer.begin();
|
||||
}
|
||||
|
|
|
|||
4
Web.h
4
Web.h
|
|
@ -15,6 +15,10 @@ class Web {
|
|||
void handleGetGroups(WebServer &server);
|
||||
void handleShadeCommand(WebServer &server);
|
||||
void handleRepeatCommand(WebServer &server);
|
||||
void handleGroupCommand(WebServer &server);
|
||||
void handleTiltCommand(WebServer &server);
|
||||
void handleDiscovery(WebServer &server);
|
||||
void handleNotFound(WebServer &server);
|
||||
void begin();
|
||||
void loop();
|
||||
void end();
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
2.1.4
|
||||
2.1.5
|
||||
|
|
@ -3,11 +3,11 @@
|
|||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" href="main.css?v=2.1.4" type="text/css" />
|
||||
<link rel="stylesheet" href="widgets.css?v=2.1.4" type="text/css" />
|
||||
<link rel="stylesheet" href="icons.css?v=2.1.4" type="text/css" />
|
||||
<link rel="stylesheet" href="main.css?v=2.1.5p" type="text/css" />
|
||||
<link rel="stylesheet" href="widgets.css?v=2.1.5p" type="text/css" />
|
||||
<link rel="stylesheet" href="icons.css?v=2.1.5p" type="text/css" />
|
||||
<link rel="icon" type="image/png" href="favicon.png" />
|
||||
<script type="text/javascript" src="index.js?v=2.1.4"></script>
|
||||
<script type="text/javascript" src="index.js?v=2.1.5p"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="divContainer" class="container main" data-auth="false">
|
||||
|
|
@ -276,6 +276,7 @@
|
|||
<div id="divSomfySettings" style="display:none;">
|
||||
<div class="subtab-container"><span class="selected" data-grpid="divSomfyMotors">Shades</span><span data-grpid="divSomfyGroups">Groups</span></div>
|
||||
<div id="divSomfyMotors" class="subtab-content" style="padding-top:10px;">
|
||||
<div style="font-size:.8em;">Drag each item to set the order in which they appear in the list.</div>
|
||||
<div id="divShadeListContainer">
|
||||
<div id="divShadeList" class="edit-motorlist"></div>
|
||||
<div class="button-container">
|
||||
|
|
@ -456,10 +457,11 @@
|
|||
</div>
|
||||
</div>
|
||||
<div id="divSomfyGroups" class="subtab-content" style="display:none;padding-top:10px;">
|
||||
<div style="font-size:.8em;">Drag each item to set the order in which they appear in the list.</div>
|
||||
<div id="divGroupListContainer">
|
||||
<div id="divGroupList" class="edit-grouplist"></div>
|
||||
<div class="button-container">
|
||||
<button id="btnAddShade" type="button" onclick="somfy.openEditGroup();">
|
||||
<button id="btnAddGroup" type="button" onclick="somfy.openEditGroup();">
|
||||
Add Group
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
|||
101
data/index.js
101
data/index.js
|
|
@ -1197,7 +1197,7 @@ var security = new Security();
|
|||
|
||||
class General {
|
||||
initialized = false;
|
||||
appVersion = 'v2.1.4';
|
||||
appVersion = 'v2.1.5';
|
||||
reloadApp = false;
|
||||
init() {
|
||||
if (this.initialized) return;
|
||||
|
|
@ -1931,12 +1931,16 @@ class Somfy {
|
|||
}
|
||||
btnDown = null;
|
||||
btnTimer = null;
|
||||
// Sort the array first to allow the user to drag and drop where they want the shade.
|
||||
|
||||
setShadesList(shades) {
|
||||
let divCfg = '';
|
||||
let divCtl = '';
|
||||
shades.sort((a, b) => { return a.sortOrder - b.sortOrder });
|
||||
console.log(shades);
|
||||
for (let i = 0; i < shades.length; i++) {
|
||||
let shade = shades[i];
|
||||
divCfg += `<div class="somfyShade" data-shadeid="${shade.shadeId}" data-remoteaddress="${shade.remoteAddress}" data-tilt="${shade.tiltType}" data-shadetype="${shade.shadeType}">`;
|
||||
divCfg += `<div class="somfyShade shade-draggable" draggable="true" data-shadeid="${shade.shadeId}" data-remoteaddress="${shade.remoteAddress}" data-tilt="${shade.tiltType}" data-shadetype="${shade.shadeType}">`;
|
||||
divCfg += `<div class="button-outline" onclick="somfy.openEditShade(${shade.shadeId});"><i class="icss-edit"></i></div>`;
|
||||
//divCfg += `<i class="shade-icon" data-position="${shade.position || 0}%"></i>`;
|
||||
divCfg += `<span class="shade-name">${shade.name}</span>`;
|
||||
|
|
@ -2083,14 +2087,87 @@ class Somfy {
|
|||
}
|
||||
}, true);
|
||||
}
|
||||
this.setListDraggable(document.getElementById('divShadeList'), '.shade-draggable', (list) => {
|
||||
// Get the shade order
|
||||
let items = list.querySelectorAll('.shade-draggable');
|
||||
let order = [];
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
order.push(parseInt(items[i].getAttribute('data-shadeid'), 10));
|
||||
// Reorder the shades on the main page.
|
||||
}
|
||||
putJSONSync('/shadeSortOrder', order, (err) => {
|
||||
for (let i = order.length - 1; i >= 0; i--) {
|
||||
let el = shadeControls.querySelector(`.somfyShadeCtl[data-shadeid="${order[i]}"`);
|
||||
if (el) {
|
||||
shadeControls.prepend(el);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
setListDraggable(list, itemclass, onChanged) {
|
||||
let items = list.querySelectorAll(itemclass);
|
||||
let changed = false;
|
||||
[].forEach.call(items, (item) => {
|
||||
item.addEventListener('dragstart', function(e) {
|
||||
console.log({ evt: 'dragStart', e: e, this: this });
|
||||
e.dataTransfer.effectAllowed = 'move';
|
||||
e.dataTransfer.setData('text/html', this.innerHTML);
|
||||
e.stopPropagation();
|
||||
this.style.opacity = '0.4';
|
||||
this.classList.add('dragging');
|
||||
});
|
||||
item.addEventListener('dragenter', function (e) {
|
||||
this.classList.add('over');
|
||||
});
|
||||
item.addEventListener('dragover', function(e) {
|
||||
e.preventDefault();
|
||||
e.dataTransfer.dropEffect = 'move';
|
||||
return false;
|
||||
});
|
||||
item.addEventListener('dragleave', function(e) {
|
||||
this.classList.remove('over');
|
||||
});
|
||||
item.addEventListener('drop', function(e) {
|
||||
// Shift around the items.
|
||||
console.log({ evt: 'drop', e: e, this: this });
|
||||
let elDrag = list.querySelector('.dragging');
|
||||
if (elDrag !== this) {
|
||||
let curr = 0, end = 0;
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
if (this === items[i]) end = i;
|
||||
if (elDrag === items[i]) curr = i;
|
||||
}
|
||||
console.log({ drag: elDrag, curr: curr, end: end, before: curr < end });
|
||||
if (curr !== end) {
|
||||
this.before(elDrag);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
item.addEventListener('dragend', function (e) {
|
||||
|
||||
[].forEach.call(items, (item) => { item.classList.remove('over') });
|
||||
this.style.opacity = '1';
|
||||
//overCounter = 0;
|
||||
this.classList.remove('dragging');
|
||||
if (changed && typeof onChanged === 'function') {
|
||||
onChanged(list);
|
||||
}
|
||||
//console.log(e);
|
||||
});
|
||||
});
|
||||
}
|
||||
setGroupsList(groups) {
|
||||
let divCfg = '';
|
||||
let divCtl = '';
|
||||
if (typeof groups !== 'undefined') {
|
||||
groups.sort((a, b) => { return a.sortOrder - b.sortOrder });
|
||||
for (let i = 0; i < groups.length; i++) {
|
||||
let group = groups[i];
|
||||
divCfg += `<div class="somfyGroup" data-groupid="${group.groupId}" data-remoteaddress="${group.remoteAddress}">`;
|
||||
divCfg += `<div class="somfyGroup group-draggable" draggable="true" data-groupid="${group.groupId}" data-remoteaddress="${group.remoteAddress}">`;
|
||||
divCfg += `<div class="button-outline" onclick="somfy.openEditGroup(${group.groupId});"><i class="icss-edit"></i></div>`;
|
||||
//divCfg += `<i class="Group-icon" data-position="${Group.position || 0}%"></i>`;
|
||||
divCfg += `<span class="group-name">${group.name}</span>`;
|
||||
|
|
@ -2140,6 +2217,24 @@ class Somfy {
|
|||
this.sendGroupCommand(groupId, cmd);
|
||||
}, true);
|
||||
}
|
||||
this.setListDraggable(document.getElementById('divGroupList'), '.group-draggable', (list) => {
|
||||
// Get the shade order
|
||||
let items = list.querySelectorAll('.group-draggable');
|
||||
let order = [];
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
order.push(parseInt(items[i].getAttribute('data-groupid'), 10));
|
||||
// Reorder the shades on the main page.
|
||||
}
|
||||
putJSONSync('/groupSortOrder', order, (err) => {
|
||||
for (let i = order.length - 1; i >= 0; i--) {
|
||||
let el = groupControls.querySelector(`.somfyGroupCtl[data-groupid="${order[i]}"`);
|
||||
if (el) {
|
||||
groupControls.prepend(el);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
closeShadePositioners() {
|
||||
let ctls = document.querySelectorAll('.shade-positioner');
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ input[type="range"] {
|
|||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
cursor:pointer;
|
||||
}
|
||||
.button-outline:active {
|
||||
-webkit-animation: flicker .5s infinite linear;
|
||||
|
|
|
|||
|
|
@ -128,6 +128,7 @@
|
|||
.shadectl-buttons[data-shadetype="5"] > .button-outline[data-cmd="down"] {
|
||||
display: none;
|
||||
}
|
||||
.shadectl-buttons:not([data-shadetype="5"]) > .button-outline[data-cmd="toggle"],
|
||||
.shadectl-buttons[data-shadetype="0"] > .button-outline[data-cmd="toggle"],
|
||||
.shadectl-buttons[data-shadetype="1"] > .button-outline[data-cmd="toggle"],
|
||||
.shadectl-buttons[data-shadetype="2"] > .button-outline[data-cmd="toggle"],
|
||||
|
|
@ -141,3 +142,19 @@
|
|||
#somfyShade[data-shadetype="6"] #divStepSettings {
|
||||
display:none;
|
||||
}
|
||||
.group-draggable,
|
||||
.shade-draggable {
|
||||
height:32px;
|
||||
border-top:solid 2px transparent;
|
||||
cursor:grab;
|
||||
}
|
||||
.group-draggable.dragging *,
|
||||
.group-draggable.over *,
|
||||
.shade-draggable.dragging *,
|
||||
.shade-draggable.over * {
|
||||
pointer-events: none;
|
||||
}
|
||||
.group-draggable.over,
|
||||
.shade-draggable.over {
|
||||
border-top: solid 2px var(--shade-color, '#00bcd4');
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue