mirror of
https://github.com/rstrouse/ESPSomfy-RTS.git
synced 2026-02-17 12:12:11 +01:00
Added rooms to the configuration #221
This commit is contained in:
parent
67cd9a3200
commit
574b2ece04
14 changed files with 973 additions and 45 deletions
|
|
@ -7,11 +7,12 @@
|
||||||
|
|
||||||
extern Preferences pref;
|
extern Preferences pref;
|
||||||
|
|
||||||
#define SHADE_HDR_VER 18
|
#define SHADE_HDR_VER 19
|
||||||
#define SHADE_HDR_SIZE 56
|
#define SHADE_HDR_SIZE 66
|
||||||
#define SHADE_REC_SIZE 272
|
#define SHADE_REC_SIZE 276
|
||||||
#define GROUP_REC_SIZE 190
|
#define GROUP_REC_SIZE 194
|
||||||
#define TRANS_REC_SIZE 74
|
#define TRANS_REC_SIZE 74
|
||||||
|
#define ROOM_REC_SIZE 29
|
||||||
|
|
||||||
extern ConfigSettings settings;
|
extern ConfigSettings settings;
|
||||||
|
|
||||||
|
|
@ -44,6 +45,8 @@ bool ConfigFile::writeHeader(const config_header_t &hdr) {
|
||||||
if(!this->isOpen()) return false;
|
if(!this->isOpen()) return false;
|
||||||
this->writeUInt8(hdr.version);
|
this->writeUInt8(hdr.version);
|
||||||
this->writeUInt8(hdr.length);
|
this->writeUInt8(hdr.length);
|
||||||
|
this->writeUInt16(hdr.roomRecordSize);
|
||||||
|
this->writeUInt8(hdr.roomRecords);
|
||||||
this->writeUInt16(hdr.shadeRecordSize);
|
this->writeUInt16(hdr.shadeRecordSize);
|
||||||
this->writeUInt8(hdr.shadeRecords);
|
this->writeUInt8(hdr.shadeRecords);
|
||||||
this->writeUInt16(hdr.groupRecordSize);
|
this->writeUInt16(hdr.groupRecordSize);
|
||||||
|
|
@ -61,6 +64,10 @@ bool ConfigFile::readHeader() {
|
||||||
Serial.printf("Reading header at %u\n", this->file.position());
|
Serial.printf("Reading header at %u\n", this->file.position());
|
||||||
this->header.version = this->readUInt8(this->header.version);
|
this->header.version = this->readUInt8(this->header.version);
|
||||||
this->header.length = this->readUInt8(0);
|
this->header.length = this->readUInt8(0);
|
||||||
|
if(this->header.version >= 19) {
|
||||||
|
this->header.roomRecordSize = this->readUInt16(this->header.roomRecordSize);
|
||||||
|
this->header.roomRecords = this->readUInt8(this->header.roomRecords);
|
||||||
|
}
|
||||||
if(this->header.version >= 13) this->header.shadeRecordSize = this->readUInt16(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);
|
else this->header.shadeRecordSize = this->readUInt8((uint8_t)this->header.shadeRecordSize);
|
||||||
this->header.shadeRecords = this->readUInt8(this->header.shadeRecords);
|
this->header.shadeRecords = this->readUInt8(this->header.shadeRecords);
|
||||||
|
|
@ -75,8 +82,7 @@ bool ConfigFile::readHeader() {
|
||||||
this->header.transRecordSize = this->readUInt16(this->header.transRecordSize);
|
this->header.transRecordSize = this->readUInt16(this->header.transRecordSize);
|
||||||
this->readString(this->header.serverId, sizeof(this->header.serverId));
|
this->readString(this->header.serverId, sizeof(this->header.serverId));
|
||||||
}
|
}
|
||||||
|
Serial.printf("version:%u len:%u roomSize:%u roomRecs:%u shadeSize:%u shadeRecs:%u groupSize:%u groupRecs: %u pos:%d\n", this->header.version, this->header.length, this->header.roomRecordSize, this->header.roomRecords, this->header.shadeRecordSize, this->header.shadeRecords, this->header.groupRecordSize, this->header.groupRecords, this->file.position());
|
||||||
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());
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
|
|
@ -279,6 +285,8 @@ bool ShadeConfigFile::begin(const char *filename, bool readOnly) { return Config
|
||||||
void ShadeConfigFile::end() { ConfigFile::end(); }
|
void ShadeConfigFile::end() { ConfigFile::end(); }
|
||||||
bool ShadeConfigFile::save(SomfyShadeController *s) {
|
bool ShadeConfigFile::save(SomfyShadeController *s) {
|
||||||
this->header.version = SHADE_HDR_VER;
|
this->header.version = SHADE_HDR_VER;
|
||||||
|
this->header.roomRecordSize = ROOM_REC_SIZE;
|
||||||
|
this->header.roomRecords = s->roomCount();
|
||||||
this->header.shadeRecordSize = SHADE_REC_SIZE;
|
this->header.shadeRecordSize = SHADE_REC_SIZE;
|
||||||
this->header.length = SHADE_HDR_SIZE;
|
this->header.length = SHADE_HDR_SIZE;
|
||||||
this->header.shadeRecords = s->shadeCount();
|
this->header.shadeRecords = s->shadeCount();
|
||||||
|
|
@ -288,6 +296,11 @@ bool ShadeConfigFile::save(SomfyShadeController *s) {
|
||||||
this->header.netRecordSize = 0;
|
this->header.netRecordSize = 0;
|
||||||
this->header.transRecordSize = 0;
|
this->header.transRecordSize = 0;
|
||||||
this->writeHeader();
|
this->writeHeader();
|
||||||
|
for(uint8_t i = 0; i < SOMFY_MAX_ROOMS; i++) {
|
||||||
|
SomfyRoom *room = &s->rooms[i];
|
||||||
|
if(room->roomId != 0)
|
||||||
|
this->writeRoomRecord(room);
|
||||||
|
}
|
||||||
for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) {
|
for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) {
|
||||||
SomfyShade *shade = &s->shades[i];
|
SomfyShade *shade = &s->shades[i];
|
||||||
if(shade->getShadeId() != 255)
|
if(shade->getShadeId() != 255)
|
||||||
|
|
@ -302,6 +315,8 @@ bool ShadeConfigFile::save(SomfyShadeController *s) {
|
||||||
}
|
}
|
||||||
bool ShadeConfigFile::backup(SomfyShadeController *s) {
|
bool ShadeConfigFile::backup(SomfyShadeController *s) {
|
||||||
this->header.version = SHADE_HDR_VER;
|
this->header.version = SHADE_HDR_VER;
|
||||||
|
this->header.roomRecordSize = ROOM_REC_SIZE;
|
||||||
|
this->header.roomRecords = s->roomCount();
|
||||||
this->header.shadeRecordSize = SHADE_REC_SIZE;
|
this->header.shadeRecordSize = SHADE_REC_SIZE;
|
||||||
this->header.length = SHADE_HDR_SIZE;
|
this->header.length = SHADE_HDR_SIZE;
|
||||||
this->header.shadeRecords = s->shadeCount();
|
this->header.shadeRecords = s->shadeCount();
|
||||||
|
|
@ -311,6 +326,11 @@ bool ShadeConfigFile::backup(SomfyShadeController *s) {
|
||||||
this->header.netRecordSize = settings.calcNetRecSize();
|
this->header.netRecordSize = settings.calcNetRecSize();
|
||||||
this->header.transRecordSize = TRANS_REC_SIZE;
|
this->header.transRecordSize = TRANS_REC_SIZE;
|
||||||
this->writeHeader();
|
this->writeHeader();
|
||||||
|
for(uint8_t i = 0; i < SOMFY_MAX_ROOMS; i++) {
|
||||||
|
SomfyRoom *room = &s->rooms[i];
|
||||||
|
if(room->roomId != 0)
|
||||||
|
this->writeRoomRecord(room);
|
||||||
|
}
|
||||||
for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) {
|
for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) {
|
||||||
SomfyShade *shade = &s->shades[i];
|
SomfyShade *shade = &s->shades[i];
|
||||||
if(shade->getShadeId() != 255)
|
if(shade->getShadeId() != 255)
|
||||||
|
|
@ -368,6 +388,7 @@ bool ShadeConfigFile::validate() {
|
||||||
// We should know the file size based upon the record information in the header
|
// We should know the file size based upon the record information in the header
|
||||||
uint32_t fsize = this->header.length + (this->header.shadeRecordSize * this->header.shadeRecords);
|
uint32_t fsize = this->header.length + (this->header.shadeRecordSize * this->header.shadeRecords);
|
||||||
if(this->header.version > 10) fsize += (this->header.groupRecordSize * this->header.groupRecords);
|
if(this->header.version > 10) fsize += (this->header.groupRecordSize * this->header.groupRecords);
|
||||||
|
if(this->header.version >= 19) fsize += (this->header.roomRecordSize * this->header.roomRecords);
|
||||||
if(this->header.version > 13) {
|
if(this->header.version > 13) {
|
||||||
fsize += (this->header.settingsRecordSize);
|
fsize += (this->header.settingsRecordSize);
|
||||||
fsize += (this->header.netRecordSize);
|
fsize += (this->header.netRecordSize);
|
||||||
|
|
@ -379,6 +400,21 @@ bool ShadeConfigFile::validate() {
|
||||||
// Next check to see if the records match the header length.
|
// Next check to see if the records match the header length.
|
||||||
uint8_t recs = 0;
|
uint8_t recs = 0;
|
||||||
uint32_t startPos = this->file.position();
|
uint32_t startPos = this->file.position();
|
||||||
|
if(this->header.version >= 19) {
|
||||||
|
while(recs < this->header.roomRecords) {
|
||||||
|
uint32_t pos = this->file.position();
|
||||||
|
if(!this->seekChar(CFG_REC_END)) {
|
||||||
|
Serial.printf("Failed to find the room record end %d\n", recs);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(this->file.position() - pos != this->header.roomRecordSize) {
|
||||||
|
Serial.printf("Room record length is %d and should be %d\n", this->file.position() - pos, this->header.roomRecordSize);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
recs++;
|
||||||
|
}
|
||||||
|
recs = 0;
|
||||||
|
}
|
||||||
while(recs < this->header.shadeRecords) {
|
while(recs < this->header.shadeRecords) {
|
||||||
uint32_t pos = this->file.position();
|
uint32_t pos = this->file.position();
|
||||||
if(!this->seekChar(CFG_REC_END)) {
|
if(!this->seekChar(CFG_REC_END)) {
|
||||||
|
|
@ -440,6 +476,12 @@ bool ShadeConfigFile::restoreFile(SomfyShadeController *s, const char *filename,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if(opts.shades) {
|
if(opts.shades) {
|
||||||
|
Serial.println("Restoring Rooms...");
|
||||||
|
for(uint8_t i = 0; i < this->header.roomRecords; i++) {
|
||||||
|
this->readRoomRecord(&s->rooms[i]);
|
||||||
|
if(i > 0) Serial.print(",");
|
||||||
|
Serial.print(s->rooms[i].roomId);
|
||||||
|
}
|
||||||
Serial.println("Restoring Shades...");
|
Serial.println("Restoring Shades...");
|
||||||
// We should be valid so start reading.
|
// We should be valid so start reading.
|
||||||
for(uint8_t i = 0; i < this->header.shadeRecords; i++) {
|
for(uint8_t i = 0; i < this->header.shadeRecords; i++) {
|
||||||
|
|
@ -598,6 +640,7 @@ bool ShadeConfigFile::readGroupRecord(SomfyGroup *group) {
|
||||||
if(group->getGroupId() == 255) group->clear();
|
if(group->getGroupId() == 255) group->clear();
|
||||||
else group->compressLinkedShadeIds();
|
else group->compressLinkedShadeIds();
|
||||||
if(this->header.version >= 18) group->flipCommands = this->readBool(false);
|
if(this->header.version >= 18) group->flipCommands = this->readBool(false);
|
||||||
|
if(this->header.version >= 19) group->roomId = this->readUInt8(0);
|
||||||
pref.end();
|
pref.end();
|
||||||
if(this->file.position() != startPos + this->header.groupRecordSize) {
|
if(this->file.position() != startPos + this->header.groupRecordSize) {
|
||||||
Serial.println("Reading to end of group record");
|
Serial.println("Reading to end of group record");
|
||||||
|
|
@ -605,6 +648,18 @@ bool ShadeConfigFile::readGroupRecord(SomfyGroup *group) {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
bool ShadeConfigFile::readRoomRecord(SomfyRoom *room) {
|
||||||
|
uint32_t startPos = this->file.position();
|
||||||
|
room->roomId = this->readUInt8(0);
|
||||||
|
this->readString(room->name, sizeof(room->name));
|
||||||
|
room->sortOrder = this->readUInt8(room->roomId - 1);
|
||||||
|
if(this->file.position() != startPos + this->header.roomRecordSize) {
|
||||||
|
Serial.println("Reading to end of room record");
|
||||||
|
this->seekChar(CFG_REC_END);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool ShadeConfigFile::readShadeRecord(SomfyShade *shade) {
|
bool ShadeConfigFile::readShadeRecord(SomfyShade *shade) {
|
||||||
pref.begin("ShadeCodes");
|
pref.begin("ShadeCodes");
|
||||||
uint32_t startPos = this->file.position();
|
uint32_t startPos = this->file.position();
|
||||||
|
|
@ -683,6 +738,7 @@ bool ShadeConfigFile::readShadeRecord(SomfyShade *shade) {
|
||||||
}
|
}
|
||||||
if(shade->proto == radio_proto::GP_Remote)
|
if(shade->proto == radio_proto::GP_Remote)
|
||||||
pinMode(shade->gpioMy, OUTPUT);
|
pinMode(shade->gpioMy, OUTPUT);
|
||||||
|
if(this->header.version >= 19) shade->roomId = this->readUInt8(0);
|
||||||
if(this->file.position() != startPos + this->header.shadeRecordSize) {
|
if(this->file.position() != startPos + this->header.shadeRecordSize) {
|
||||||
Serial.println("Reading to end of shade record");
|
Serial.println("Reading to end of shade record");
|
||||||
this->seekChar(CFG_REC_END);
|
this->seekChar(CFG_REC_END);
|
||||||
|
|
@ -701,6 +757,17 @@ bool ShadeConfigFile::loadFile(SomfyShadeController *s, const char *filename) {
|
||||||
if(opened) this->end();
|
if(opened) this->end();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
for(uint8_t i = 0; i < this->header.roomRecords;i++) {
|
||||||
|
this->readRoomRecord(&s->rooms[i]);
|
||||||
|
}
|
||||||
|
if(this->header.roomRecords < SOMFY_MAX_ROOMS) {
|
||||||
|
uint8_t ndx = this->header.roomRecords;
|
||||||
|
// Clear out any positions that are not in the shade file.
|
||||||
|
while(ndx < SOMFY_MAX_ROOMS) {
|
||||||
|
((SomfyRoom *)&s->rooms[ndx++])->clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// We should be valid so start reading.
|
// We should be valid so start reading.
|
||||||
for(uint8_t i = 0; i < this->header.shadeRecords; i++) {
|
for(uint8_t i = 0; i < this->header.shadeRecords; i++) {
|
||||||
this->readShadeRecord(&s->shades[i]);
|
this->readShadeRecord(&s->shades[i]);
|
||||||
|
|
@ -740,9 +807,17 @@ bool ShadeConfigFile::writeGroupRecord(SomfyGroup *group) {
|
||||||
}
|
}
|
||||||
this->writeUInt8(group->repeats);
|
this->writeUInt8(group->repeats);
|
||||||
this->writeUInt8(group->sortOrder);
|
this->writeUInt8(group->sortOrder);
|
||||||
this->writeBool(group->flipCommands, CFG_REC_END);
|
this->writeBool(group->flipCommands);
|
||||||
|
this->writeUInt8(group->roomId, CFG_REC_END);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
bool ShadeConfigFile::writeRoomRecord(SomfyRoom *room) {
|
||||||
|
this->writeUInt8(room->roomId);
|
||||||
|
this->writeString(room->name, sizeof(room->name));
|
||||||
|
this->writeUInt8(room->sortOrder, CFG_REC_END);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool ShadeConfigFile::writeShadeRecord(SomfyShade *shade) {
|
bool ShadeConfigFile::writeShadeRecord(SomfyShade *shade) {
|
||||||
if(shade->tiltType == tilt_types::none || shade->shadeType != shade_types::blind) {
|
if(shade->tiltType == tilt_types::none || shade->shadeType != shade_types::blind) {
|
||||||
shade->myTiltPos = -1;
|
shade->myTiltPos = -1;
|
||||||
|
|
@ -788,7 +863,8 @@ bool ShadeConfigFile::writeShadeRecord(SomfyShade *shade) {
|
||||||
this->writeUInt8(shade->gpioUp);
|
this->writeUInt8(shade->gpioUp);
|
||||||
this->writeUInt8(shade->gpioDown);
|
this->writeUInt8(shade->gpioDown);
|
||||||
this->writeUInt8(shade->gpioMy);
|
this->writeUInt8(shade->gpioMy);
|
||||||
this->writeUInt8(shade->gpioFlags, CFG_REC_END);
|
this->writeUInt8(shade->gpioFlags);
|
||||||
|
this->writeUInt8(shade->roomId, CFG_REC_END);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool ShadeConfigFile::writeSettingsRecord() {
|
bool ShadeConfigFile::writeSettingsRecord() {
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@
|
||||||
|
|
||||||
struct config_header_t {
|
struct config_header_t {
|
||||||
uint8_t version = 1;
|
uint8_t version = 1;
|
||||||
|
uint8_t roomRecords = 0;
|
||||||
|
uint16_t roomRecordSize = 0;
|
||||||
uint16_t shadeRecordSize = 0;
|
uint16_t shadeRecordSize = 0;
|
||||||
uint8_t shadeRecords = 0;
|
uint8_t shadeRecords = 0;
|
||||||
uint16_t groupRecordSize = 0;
|
uint16_t groupRecordSize = 0;
|
||||||
|
|
@ -63,11 +65,13 @@ class ConfigFile {
|
||||||
};
|
};
|
||||||
class ShadeConfigFile : public ConfigFile {
|
class ShadeConfigFile : public ConfigFile {
|
||||||
protected:
|
protected:
|
||||||
|
bool writeRoomRecord(SomfyRoom *room);
|
||||||
bool writeShadeRecord(SomfyShade *shade);
|
bool writeShadeRecord(SomfyShade *shade);
|
||||||
bool writeGroupRecord(SomfyGroup *group);
|
bool writeGroupRecord(SomfyGroup *group);
|
||||||
bool writeSettingsRecord();
|
bool writeSettingsRecord();
|
||||||
bool writeNetRecord();
|
bool writeNetRecord();
|
||||||
bool writeTransRecord(transceiver_config_t &cfg);
|
bool writeTransRecord(transceiver_config_t &cfg);
|
||||||
|
bool readRoomRecord(SomfyRoom *room);
|
||||||
bool readShadeRecord(SomfyShade *shade);
|
bool readShadeRecord(SomfyShade *shade);
|
||||||
bool readGroupRecord(SomfyGroup *group);
|
bool readGroupRecord(SomfyGroup *group);
|
||||||
bool readSettingsRecord();
|
bool readSettingsRecord();
|
||||||
|
|
|
||||||
179
Somfy.cpp
179
Somfy.cpp
|
|
@ -562,6 +562,12 @@ void SomfyShadeController::writeBackup() {
|
||||||
file.backup(this);
|
file.backup(this);
|
||||||
file.end();
|
file.end();
|
||||||
}
|
}
|
||||||
|
SomfyRoom * SomfyShadeController::getRoomById(uint8_t roomId) {
|
||||||
|
for(uint8_t i = 0; i < SOMFY_MAX_ROOMS; i++) {
|
||||||
|
if(this->rooms[i].roomId == roomId) return &this->rooms[i];
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
SomfyShade * SomfyShadeController::getShadeById(uint8_t shadeId) {
|
SomfyShade * SomfyShadeController::getShadeById(uint8_t shadeId) {
|
||||||
for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) {
|
for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) {
|
||||||
if(this->shades[i].getShadeId() == shadeId) return &this->shades[i];
|
if(this->shades[i].getShadeId() == shadeId) return &this->shades[i];
|
||||||
|
|
@ -621,6 +627,10 @@ void SomfyShade::clear() {
|
||||||
this->repeats = 1;
|
this->repeats = 1;
|
||||||
this->sortOrder = 255;
|
this->sortOrder = 255;
|
||||||
}
|
}
|
||||||
|
void SomfyRoom::clear() {
|
||||||
|
this->roomId = 0;
|
||||||
|
strcpy(this->name, "");
|
||||||
|
}
|
||||||
void SomfyGroup::clear() {
|
void SomfyGroup::clear() {
|
||||||
this->setGroupId(255);
|
this->setGroupId(255);
|
||||||
this->setRemoteAddress(0);
|
this->setRemoteAddress(0);
|
||||||
|
|
@ -1307,6 +1317,28 @@ void SomfyShade::load() {
|
||||||
}
|
}
|
||||||
pref.end();
|
pref.end();
|
||||||
}
|
}
|
||||||
|
void SomfyRoom::publish() {
|
||||||
|
if(mqtt.connected()) {
|
||||||
|
char topic[64];
|
||||||
|
sprintf(topic, "rooms/%d/roomId", this->roomId);
|
||||||
|
mqtt.publish(topic, this->roomId, true);
|
||||||
|
sprintf(topic, "rooms/%d/name", this->roomId);
|
||||||
|
mqtt.publish(topic, this->name, true);
|
||||||
|
sprintf(topic, "rooms/%d/sortOrder", this->roomId);
|
||||||
|
mqtt.publish(topic, this->sortOrder, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void SomfyRoom::unpublish() {
|
||||||
|
if(mqtt.connected()) {
|
||||||
|
char topic[64];
|
||||||
|
sprintf(topic, "rooms/%d/roomId", this->roomId);
|
||||||
|
mqtt.unpublish(topic);
|
||||||
|
sprintf(topic, "rooms/%d/name", this->roomId);
|
||||||
|
mqtt.unpublish(topic);
|
||||||
|
sprintf(topic, "rooms/%d/sortOrder", this->roomId);
|
||||||
|
mqtt.unpublish(topic);
|
||||||
|
}
|
||||||
|
}
|
||||||
void SomfyShade::publishState() {
|
void SomfyShade::publishState() {
|
||||||
if(mqtt.connected()) {
|
if(mqtt.connected()) {
|
||||||
this->publish("position", this->transformPosition(this->currentPos), true);
|
this->publish("position", this->transformPosition(this->currentPos), true);
|
||||||
|
|
@ -1778,6 +1810,21 @@ void SomfyShade::emitCommand(uint8_t num, somfy_commands cmd, const char *source
|
||||||
this->publish("cmd", translateSomfyCommand(cmd).c_str());
|
this->publish("cmd", translateSomfyCommand(cmd).c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
void SomfyRoom::emitState(const char *evt) { this->emitState(255, evt); }
|
||||||
|
void SomfyRoom::emitState(uint8_t num, const char *evt) {
|
||||||
|
ClientSocketEvent e(evt);
|
||||||
|
char buf[55];
|
||||||
|
uint8_t flags = 0;
|
||||||
|
snprintf(buf, sizeof(buf), "{\"roomId\":%d,", this->roomId);
|
||||||
|
e.appendMessage(buf);
|
||||||
|
snprintf(buf, sizeof(buf), "\"name\":\"%s\",", this->name);
|
||||||
|
e.appendMessage(buf);
|
||||||
|
snprintf(buf, sizeof(buf), "\"sortOrder\":%d}", this->sortOrder);
|
||||||
|
e.appendMessage(buf);
|
||||||
|
if(num >= 255) sockEmit.sendToClients(&e);
|
||||||
|
else sockEmit.sendToClient(num, &e);
|
||||||
|
this->publish();
|
||||||
|
}
|
||||||
void SomfyGroup::emitState(const char *evt) { this->emitState(255, evt); }
|
void SomfyGroup::emitState(const char *evt) { this->emitState(255, evt); }
|
||||||
void SomfyGroup::emitState(uint8_t num, const char *evt) {
|
void SomfyGroup::emitState(uint8_t num, const char *evt) {
|
||||||
ClientSocketEvent e(evt);
|
ClientSocketEvent e(evt);
|
||||||
|
|
@ -2753,6 +2800,7 @@ bool SomfyShade::save() {
|
||||||
this->publish();
|
this->publish();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
bool SomfyRoom::save() { somfy.commit(); return true; }
|
||||||
bool SomfyGroup::save() { somfy.commit(); return true; }
|
bool SomfyGroup::save() { somfy.commit(); return true; }
|
||||||
bool SomfyShade::usesPin(uint8_t pin) {
|
bool SomfyShade::usesPin(uint8_t pin) {
|
||||||
if(this->proto != radio_proto::GP_Remote && this->proto != radio_proto::GP_Relay) return false;
|
if(this->proto != radio_proto::GP_Remote && this->proto != radio_proto::GP_Relay) return false;
|
||||||
|
|
@ -2846,6 +2894,7 @@ int8_t SomfyShade::fromJSON(JsonObject &obj) {
|
||||||
int8_t err = this->validateJSON(obj);
|
int8_t err = this->validateJSON(obj);
|
||||||
if(err == 0) {
|
if(err == 0) {
|
||||||
if(obj.containsKey("name")) strlcpy(this->name, obj["name"], sizeof(this->name));
|
if(obj.containsKey("name")) strlcpy(this->name, obj["name"], sizeof(this->name));
|
||||||
|
if(obj.containsKey("roomId")) this->roomId = obj["roomId"];
|
||||||
if(obj.containsKey("upTime")) this->upTime = obj["upTime"];
|
if(obj.containsKey("upTime")) this->upTime = obj["upTime"];
|
||||||
if(obj.containsKey("downTime")) this->downTime = obj["downTime"];
|
if(obj.containsKey("downTime")) this->downTime = obj["downTime"];
|
||||||
if(obj.containsKey("remoteAddress")) this->setRemoteAddress(obj["remoteAddress"]);
|
if(obj.containsKey("remoteAddress")) this->setRemoteAddress(obj["remoteAddress"]);
|
||||||
|
|
@ -2939,6 +2988,7 @@ int8_t SomfyShade::fromJSON(JsonObject &obj) {
|
||||||
}
|
}
|
||||||
bool SomfyShade::toJSONRef(JsonObject &obj) {
|
bool SomfyShade::toJSONRef(JsonObject &obj) {
|
||||||
obj["shadeId"] = this->getShadeId();
|
obj["shadeId"] = this->getShadeId();
|
||||||
|
obj["roomId"] = this->roomId;
|
||||||
obj["name"] = this->name;
|
obj["name"] = this->name;
|
||||||
obj["remoteAddress"] = this->m_remoteAddress;
|
obj["remoteAddress"] = this->m_remoteAddress;
|
||||||
obj["paired"] = this->paired;
|
obj["paired"] = this->paired;
|
||||||
|
|
@ -2958,6 +3008,7 @@ bool SomfyShade::toJSON(JsonObject &obj) {
|
||||||
//Serial.print(" ");
|
//Serial.print(" ");
|
||||||
//Serial.println(this->name);
|
//Serial.println(this->name);
|
||||||
obj["shadeId"] = this->getShadeId();
|
obj["shadeId"] = this->getShadeId();
|
||||||
|
obj["roomId"] = this->roomId;
|
||||||
obj["name"] = this->name;
|
obj["name"] = this->name;
|
||||||
obj["remoteAddress"] = this->m_remoteAddress;
|
obj["remoteAddress"] = this->m_remoteAddress;
|
||||||
obj["upTime"] = this->upTime;
|
obj["upTime"] = this->upTime;
|
||||||
|
|
@ -3003,8 +3054,21 @@ bool SomfyShade::toJSON(JsonObject &obj) {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
bool SomfyRoom::fromJSON(JsonObject &obj) {
|
||||||
|
if(obj.containsKey("name")) strlcpy(this->name, obj["name"], sizeof(this->name));
|
||||||
|
if(obj.containsKey("sortOrder")) this->sortOrder = obj["sortOrder"];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool SomfyRoom::toJSON(JsonObject &obj) {
|
||||||
|
obj["roomId"] = this->roomId;
|
||||||
|
obj["name"] = this->name;
|
||||||
|
obj["sortOrder"] = this->sortOrder;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool SomfyGroup::fromJSON(JsonObject &obj) {
|
bool SomfyGroup::fromJSON(JsonObject &obj) {
|
||||||
if(obj.containsKey("name")) strlcpy(this->name, obj["name"], sizeof(this->name));
|
if(obj.containsKey("name")) strlcpy(this->name, obj["name"], sizeof(this->name));
|
||||||
|
if(obj.containsKey("roomId")) this->roomId = obj["roomId"];
|
||||||
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>());
|
||||||
|
|
@ -3026,6 +3090,7 @@ bool SomfyGroup::fromJSON(JsonObject &obj) {
|
||||||
bool SomfyGroup::toJSON(JsonObject &obj) {
|
bool SomfyGroup::toJSON(JsonObject &obj) {
|
||||||
this->updateFlags();
|
this->updateFlags();
|
||||||
obj["groupId"] = this->getGroupId();
|
obj["groupId"] = this->getGroupId();
|
||||||
|
obj["roomId"] = this->roomId;
|
||||||
obj["name"] = this->name;
|
obj["name"] = this->name;
|
||||||
obj["remoteAddress"] = this->m_remoteAddress;
|
obj["remoteAddress"] = this->m_remoteAddress;
|
||||||
obj["lastRollingCode"] = this->lastRollingCode;
|
obj["lastRollingCode"] = this->lastRollingCode;
|
||||||
|
|
@ -3146,7 +3211,6 @@ int8_t SomfyShadeController::getMaxGroupOrder() {
|
||||||
}
|
}
|
||||||
return order;
|
return order;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t SomfyShadeController::getNextGroupId() {
|
uint8_t SomfyShadeController::getNextGroupId() {
|
||||||
// There is no shortcut for this since the deletion of
|
// There is no shortcut for this since the deletion of
|
||||||
// a group in the middle makes all of this very difficult.
|
// a group in the middle makes all of this very difficult.
|
||||||
|
|
@ -3167,6 +3231,43 @@ uint8_t SomfyShadeController::getNextGroupId() {
|
||||||
}
|
}
|
||||||
return 255;
|
return 255;
|
||||||
}
|
}
|
||||||
|
uint8_t SomfyShadeController::getNextRoomId() {
|
||||||
|
// There is no shortcut for this since the deletion of
|
||||||
|
// a room in the middle makes all of this very difficult.
|
||||||
|
for(uint8_t i = 1; i < SOMFY_MAX_ROOMS - 1; i++) {
|
||||||
|
bool id_exists = false;
|
||||||
|
for(uint8_t j = 0; j < SOMFY_MAX_ROOMS; j++) {
|
||||||
|
SomfyRoom *room = &this->rooms[j];
|
||||||
|
if(room->roomId == i) {
|
||||||
|
id_exists = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!id_exists) {
|
||||||
|
Serial.print("Got next room Id:");
|
||||||
|
Serial.print(i);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int8_t SomfyShadeController::getMaxRoomOrder() {
|
||||||
|
int8_t order = -1;
|
||||||
|
for(uint8_t i = 0; i < SOMFY_MAX_ROOMS; i++) {
|
||||||
|
SomfyRoom *room = &this->rooms[i];
|
||||||
|
if(room->roomId == 0) continue;
|
||||||
|
if(order < room->sortOrder) order = room->sortOrder;
|
||||||
|
}
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t SomfyShadeController::roomCount() {
|
||||||
|
uint8_t count = 0;
|
||||||
|
for(uint8_t i = 0; i < SOMFY_MAX_ROOMS; i++) {
|
||||||
|
if(this->rooms[i].roomId != 0) count++;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
uint8_t SomfyShadeController::shadeCount() {
|
uint8_t SomfyShadeController::shadeCount() {
|
||||||
uint8_t count = 0;
|
uint8_t count = 0;
|
||||||
for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) {
|
for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) {
|
||||||
|
|
@ -3268,6 +3369,28 @@ SomfyShade *SomfyShadeController::addShade() {
|
||||||
}
|
}
|
||||||
return shade;
|
return shade;
|
||||||
}
|
}
|
||||||
|
SomfyRoom *SomfyShadeController::addRoom(JsonObject &obj) {
|
||||||
|
SomfyRoom *room = this->addRoom();
|
||||||
|
if(room) {
|
||||||
|
room->fromJSON(obj);
|
||||||
|
room->save();
|
||||||
|
room->emitState("roomAdded");
|
||||||
|
}
|
||||||
|
return room;
|
||||||
|
}
|
||||||
|
SomfyRoom *SomfyShadeController::addRoom() {
|
||||||
|
uint8_t roomId = this->getNextRoomId();
|
||||||
|
// So the next room id will be the first one we run into with an id of 0 so
|
||||||
|
if(roomId == 0) return nullptr;
|
||||||
|
SomfyRoom *room = &this->rooms[roomId - 1];
|
||||||
|
if(room) {
|
||||||
|
room->roomId = roomId;
|
||||||
|
room->sortOrder = this->getMaxRoomOrder() + 1;
|
||||||
|
this->isDirty = true;
|
||||||
|
}
|
||||||
|
return room;
|
||||||
|
}
|
||||||
|
|
||||||
SomfyGroup *SomfyShadeController::addGroup(JsonObject &obj) {
|
SomfyGroup *SomfyShadeController::addGroup(JsonObject &obj) {
|
||||||
SomfyGroup *group = this->addGroup();
|
SomfyGroup *group = this->addGroup();
|
||||||
if(group) {
|
if(group) {
|
||||||
|
|
@ -3463,6 +3586,30 @@ bool SomfyShadeController::deleteShade(uint8_t shadeId) {
|
||||||
this->commit();
|
this->commit();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
bool SomfyShadeController::deleteRoom(uint8_t roomId) {
|
||||||
|
for(uint8_t i = 0; i < SOMFY_MAX_ROOMS; i++) {
|
||||||
|
if(this->rooms[i].roomId == roomId) {
|
||||||
|
rooms[i].unpublish();
|
||||||
|
for(uint8_t j = 0; j < SOMFY_MAX_SHADES; j++) {
|
||||||
|
if(shades[j].roomId == roomId) {
|
||||||
|
shades[j].roomId = 0;
|
||||||
|
shades[j].emitState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(uint8_t j = 0; j < SOMFY_MAX_GROUPS; j++) {
|
||||||
|
if(groups[j].roomId == roomId) {
|
||||||
|
groups[j].roomId = 0;
|
||||||
|
groups[j].emitState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rooms[i].emitState("roomRemoved");
|
||||||
|
this->rooms[i].clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this->commit();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool SomfyShadeController::deleteGroup(uint8_t groupId) {
|
bool SomfyShadeController::deleteGroup(uint8_t groupId) {
|
||||||
for(uint8_t i = 0; i < SOMFY_MAX_GROUPS; i++) {
|
for(uint8_t i = 0; i < SOMFY_MAX_GROUPS; i++) {
|
||||||
if(this->groups[i].getGroupId() == groupId) {
|
if(this->groups[i].getGroupId() == groupId) {
|
||||||
|
|
@ -3502,6 +3649,7 @@ uint16_t SomfyRemote::setRollingCode(uint16_t code) {
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
bool SomfyShadeController::toJSON(DynamicJsonDocument &doc) {
|
bool SomfyShadeController::toJSON(DynamicJsonDocument &doc) {
|
||||||
|
doc["maxRooms"] = SOMFY_MAX_ROOMS;
|
||||||
doc["maxShades"] = SOMFY_MAX_SHADES;
|
doc["maxShades"] = SOMFY_MAX_SHADES;
|
||||||
doc["maxGroups"] = SOMFY_MAX_GROUPS;
|
doc["maxGroups"] = SOMFY_MAX_GROUPS;
|
||||||
doc["maxGroupedShades"] = SOMFY_MAX_GROUPED_SHADES;
|
doc["maxGroupedShades"] = SOMFY_MAX_GROUPED_SHADES;
|
||||||
|
|
@ -3509,22 +3657,12 @@ bool SomfyShadeController::toJSON(DynamicJsonDocument &doc) {
|
||||||
doc["startingAddress"] = this->startingAddress;
|
doc["startingAddress"] = this->startingAddress;
|
||||||
JsonObject objRadio = doc.createNestedObject("transceiver");
|
JsonObject objRadio = doc.createNestedObject("transceiver");
|
||||||
this->transceiver.toJSON(objRadio);
|
this->transceiver.toJSON(objRadio);
|
||||||
|
JsonArray arrRooms = doc.createNestedArray("rooms");
|
||||||
|
this->toJSONRooms(arrRooms);
|
||||||
JsonArray arrShades = doc.createNestedArray("shades");
|
JsonArray arrShades = doc.createNestedArray("shades");
|
||||||
for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) {
|
this->toJSONShades(arrShades);
|
||||||
SomfyShade *shade = &this->shades[i];
|
|
||||||
if(shade->getShadeId() != 255) {
|
|
||||||
JsonObject oshade = arrShades.createNestedObject();
|
|
||||||
shade->toJSON(oshade);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
JsonArray arrGroups = doc.createNestedArray("groups");
|
JsonArray arrGroups = doc.createNestedArray("groups");
|
||||||
for(uint8_t i = 0; i < SOMFY_MAX_GROUPS; i++) {
|
this->toJSONGroups(arrGroups);
|
||||||
SomfyGroup *group = &this->groups[i];
|
|
||||||
if(group->getGroupId() != 255) {
|
|
||||||
JsonObject ogroup = arrGroups.createNestedObject();
|
|
||||||
group->toJSON(ogroup);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool SomfyShadeController::toJSON(JsonObject &obj) {
|
bool SomfyShadeController::toJSON(JsonObject &obj) {
|
||||||
|
|
@ -3539,6 +3677,17 @@ bool SomfyShadeController::toJSON(JsonObject &obj) {
|
||||||
this->toJSONGroups(arrGroups);
|
this->toJSONGroups(arrGroups);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
bool SomfyShadeController::toJSONRooms(JsonArray &arr) {
|
||||||
|
for(uint8_t i = 0; i < SOMFY_MAX_ROOMS; i++) {
|
||||||
|
SomfyRoom &room = this->rooms[i];
|
||||||
|
if(room.roomId != 0) {
|
||||||
|
JsonObject oroom = arr.createNestedObject();
|
||||||
|
room.toJSON(oroom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool SomfyShadeController::toJSONShades(JsonArray &arr) {
|
bool SomfyShadeController::toJSONShades(JsonArray &arr) {
|
||||||
for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) {
|
for(uint8_t i = 0; i < SOMFY_MAX_SHADES; i++) {
|
||||||
SomfyShade &shade = this->shades[i];
|
SomfyShade &shade = this->shades[i];
|
||||||
|
|
|
||||||
27
Somfy.h
27
Somfy.h
|
|
@ -6,6 +6,7 @@
|
||||||
#define SOMFY_MAX_GROUPS 16
|
#define SOMFY_MAX_GROUPS 16
|
||||||
#define SOMFY_MAX_LINKED_REMOTES 7
|
#define SOMFY_MAX_LINKED_REMOTES 7
|
||||||
#define SOMFY_MAX_GROUPED_SHADES 32
|
#define SOMFY_MAX_GROUPED_SHADES 32
|
||||||
|
#define SOMFY_MAX_ROOMS 16
|
||||||
|
|
||||||
#define SECS_TO_MILLIS(x) ((x) * 1000)
|
#define SECS_TO_MILLIS(x) ((x) * 1000)
|
||||||
#define MINS_TO_MILLIS(x) SECS_TO_MILLIS((x) * 60)
|
#define MINS_TO_MILLIS(x) SECS_TO_MILLIS((x) * 60)
|
||||||
|
|
@ -180,6 +181,21 @@ struct somfy_frame_t {
|
||||||
void copy(somfy_frame_t &f);
|
void copy(somfy_frame_t &f);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class SomfyRoom {
|
||||||
|
public:
|
||||||
|
uint8_t roomId = 0;
|
||||||
|
char name[21] = "";
|
||||||
|
int8_t sortOrder = 0;
|
||||||
|
void clear();
|
||||||
|
bool save();
|
||||||
|
bool fromJSON(JsonObject &obj);
|
||||||
|
bool toJSON(JsonObject &obj);
|
||||||
|
void emitState(const char *evt = "roomState");
|
||||||
|
void emitState(uint8_t num, const char *evt = "roomState");
|
||||||
|
void publish();
|
||||||
|
void unpublish();
|
||||||
|
};
|
||||||
|
|
||||||
class SomfyRemote {
|
class SomfyRemote {
|
||||||
// These sizes for the data have been
|
// These sizes for the data have been
|
||||||
// confirmed. The address is actually 24bits
|
// confirmed. The address is actually 24bits
|
||||||
|
|
@ -246,6 +262,7 @@ class SomfyShade : public SomfyRemote {
|
||||||
bool settingTiltPos = false;
|
bool settingTiltPos = false;
|
||||||
uint32_t awaitMy = 0;
|
uint32_t awaitMy = 0;
|
||||||
public:
|
public:
|
||||||
|
uint8_t roomId = 0;
|
||||||
int8_t sortOrder = 0;
|
int8_t sortOrder = 0;
|
||||||
bool flipPosition = false;
|
bool flipPosition = false;
|
||||||
shade_types shadeType = shade_types::roller;
|
shade_types shadeType = shade_types::roller;
|
||||||
|
|
@ -339,6 +356,7 @@ class SomfyGroup : public SomfyRemote {
|
||||||
protected:
|
protected:
|
||||||
uint8_t groupId = 255;
|
uint8_t groupId = 255;
|
||||||
public:
|
public:
|
||||||
|
uint8_t roomId = 0;
|
||||||
int8_t sortOrder = 0;
|
int8_t sortOrder = 0;
|
||||||
group_types groupType = group_types::channel;
|
group_types groupType = group_types::channel;
|
||||||
int8_t direction = 0; // 0 = stopped, 1=down, -1=up.
|
int8_t direction = 0; // 0 = stopped, 1=down, -1=up.
|
||||||
|
|
@ -481,32 +499,41 @@ class SomfyShadeController {
|
||||||
bool useNVS();
|
bool useNVS();
|
||||||
bool isDirty = false;
|
bool isDirty = false;
|
||||||
uint32_t startingAddress;
|
uint32_t startingAddress;
|
||||||
|
uint8_t getNextRoomId();
|
||||||
uint8_t getNextShadeId();
|
uint8_t getNextShadeId();
|
||||||
uint8_t getNextGroupId();
|
uint8_t getNextGroupId();
|
||||||
|
int8_t getMaxRoomOrder();
|
||||||
int8_t getMaxShadeOrder();
|
int8_t getMaxShadeOrder();
|
||||||
int8_t getMaxGroupOrder();
|
int8_t getMaxGroupOrder();
|
||||||
uint32_t getNextRemoteAddress(uint8_t shadeId);
|
uint32_t getNextRemoteAddress(uint8_t shadeId);
|
||||||
SomfyShadeController();
|
SomfyShadeController();
|
||||||
Transceiver transceiver;
|
Transceiver transceiver;
|
||||||
|
SomfyRoom *addRoom();
|
||||||
|
SomfyRoom *addRoom(JsonObject &obj);
|
||||||
SomfyShade *addShade();
|
SomfyShade *addShade();
|
||||||
SomfyShade *addShade(JsonObject &obj);
|
SomfyShade *addShade(JsonObject &obj);
|
||||||
SomfyGroup *addGroup();
|
SomfyGroup *addGroup();
|
||||||
SomfyGroup *addGroup(JsonObject &obj);
|
SomfyGroup *addGroup(JsonObject &obj);
|
||||||
|
bool deleteRoom(uint8_t roomId);
|
||||||
bool deleteShade(uint8_t shadeId);
|
bool deleteShade(uint8_t shadeId);
|
||||||
bool deleteGroup(uint8_t groupId);
|
bool deleteGroup(uint8_t groupId);
|
||||||
bool begin();
|
bool begin();
|
||||||
void loop();
|
void loop();
|
||||||
void end();
|
void end();
|
||||||
|
SomfyRoom rooms[SOMFY_MAX_ROOMS];
|
||||||
SomfyShade shades[SOMFY_MAX_SHADES];
|
SomfyShade shades[SOMFY_MAX_SHADES];
|
||||||
SomfyGroup groups[SOMFY_MAX_GROUPS];
|
SomfyGroup groups[SOMFY_MAX_GROUPS];
|
||||||
bool toJSON(DynamicJsonDocument &doc);
|
bool toJSON(DynamicJsonDocument &doc);
|
||||||
bool toJSON(JsonObject &obj);
|
bool toJSON(JsonObject &obj);
|
||||||
|
bool toJSONRooms(JsonArray &arr);
|
||||||
bool toJSONShades(JsonArray &arr);
|
bool toJSONShades(JsonArray &arr);
|
||||||
bool toJSONGroups(JsonArray &arr);
|
bool toJSONGroups(JsonArray &arr);
|
||||||
|
uint8_t roomCount();
|
||||||
uint8_t shadeCount();
|
uint8_t shadeCount();
|
||||||
uint8_t groupCount();
|
uint8_t groupCount();
|
||||||
void updateGroupFlags();
|
void updateGroupFlags();
|
||||||
SomfyShade * getShadeById(uint8_t shadeId);
|
SomfyShade * getShadeById(uint8_t shadeId);
|
||||||
|
SomfyRoom * getRoomById(uint8_t roomId);
|
||||||
SomfyGroup * getGroupById(uint8_t groupId);
|
SomfyGroup * getGroupById(uint8_t groupId);
|
||||||
SomfyShade * findShadeByRemoteAddress(uint32_t address);
|
SomfyShade * findShadeByRemoteAddress(uint32_t address);
|
||||||
SomfyGroup * findGroupByRemoteAddress(uint32_t address);
|
SomfyGroup * findGroupByRemoteAddress(uint32_t address);
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
273
Web.cpp
273
Web.cpp
|
|
@ -248,6 +248,19 @@ void Web::handleLoginContext(WebServer &server) {
|
||||||
serializeJson(doc, g_content);
|
serializeJson(doc, g_content);
|
||||||
server.send(200, _encoding_json, g_content);
|
server.send(200, _encoding_json, g_content);
|
||||||
}
|
}
|
||||||
|
void Web::handleGetRooms(WebServer &server) {
|
||||||
|
webServer.sendCORSHeaders(server);
|
||||||
|
if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; }
|
||||||
|
HTTPMethod method = server.method();
|
||||||
|
if (method == HTTP_POST || method == HTTP_GET) {
|
||||||
|
DynamicJsonDocument doc(16384);
|
||||||
|
JsonArray arr = doc.to<JsonArray>();
|
||||||
|
somfy.toJSONRooms(arr);
|
||||||
|
serializeJson(doc, g_content);
|
||||||
|
server.send(200, _encoding_json, g_content);
|
||||||
|
}
|
||||||
|
else server.send(404, _encoding_text, _response_404);
|
||||||
|
}
|
||||||
void Web::handleGetShades(WebServer &server) {
|
void Web::handleGetShades(WebServer &server) {
|
||||||
webServer.sendCORSHeaders(server);
|
webServer.sendCORSHeaders(server);
|
||||||
if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; }
|
if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; }
|
||||||
|
|
@ -558,6 +571,75 @@ void Web::handleTiltCommand(WebServer &server) {
|
||||||
else
|
else
|
||||||
server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Invalid Http method\"}"));
|
server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Invalid Http method\"}"));
|
||||||
}
|
}
|
||||||
|
void Web::handleRoom(WebServer &server) {
|
||||||
|
webServer.sendCORSHeaders(server);
|
||||||
|
if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; }
|
||||||
|
HTTPMethod method = server.method();
|
||||||
|
if (method == HTTP_GET) {
|
||||||
|
if (server.hasArg("roomId")) {
|
||||||
|
int roomId = atoi(server.arg("roomId").c_str());
|
||||||
|
SomfyRoom* room = somfy.getRoomById(roomId);
|
||||||
|
if (room) {
|
||||||
|
DynamicJsonDocument doc(512);
|
||||||
|
JsonObject obj = doc.to<JsonObject>();
|
||||||
|
room->toJSON(obj);
|
||||||
|
serializeJson(doc, g_content);
|
||||||
|
server.send(200, _encoding_json, g_content);
|
||||||
|
}
|
||||||
|
else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Room Id not found.\"}"));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"You must supply a valid room id.\"}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (method == HTTP_PUT || method == HTTP_POST) {
|
||||||
|
// We are updating an existing room.
|
||||||
|
if (server.hasArg("plain")) {
|
||||||
|
Serial.println("Updating a room");
|
||||||
|
DynamicJsonDocument doc(512);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
JsonObject obj = doc.as<JsonObject>();
|
||||||
|
if (obj.containsKey("roomId")) {
|
||||||
|
SomfyRoom* room = somfy.getRoomById(obj["roomId"]);
|
||||||
|
if (room) {
|
||||||
|
uint8_t err = room->fromJSON(obj);
|
||||||
|
if(err == 0) {
|
||||||
|
room->save();
|
||||||
|
DynamicJsonDocument sdoc(2048);
|
||||||
|
JsonObject sobj = sdoc.to<JsonObject>();
|
||||||
|
room->toJSON(sobj);
|
||||||
|
serializeJson(sdoc, g_content);
|
||||||
|
server.send(200, _encoding_json, g_content);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
snprintf(g_content, sizeof(g_content), "{\"status\":\"DATA\",\"desc\":\"Data Error.\", \"code\":%d}", err);
|
||||||
|
server.send(500, _encoding_json, g_content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Room Id not found.\"}"));
|
||||||
|
}
|
||||||
|
else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No room id was supplied.\"}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No room object supplied.\"}"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Invalid Http method\"}"));
|
||||||
|
}
|
||||||
void Web::handleShade(WebServer &server) {
|
void Web::handleShade(WebServer &server) {
|
||||||
webServer.sendCORSHeaders(server);
|
webServer.sendCORSHeaders(server);
|
||||||
if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; }
|
if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; }
|
||||||
|
|
@ -707,6 +789,8 @@ void Web::handleDiscovery(WebServer &server) {
|
||||||
if(net.connType == conn_types::ethernet) obj["connType"] = "Ethernet";
|
if(net.connType == conn_types::ethernet) obj["connType"] = "Ethernet";
|
||||||
else if(net.connType == conn_types::wifi) obj["connType"] = "Wifi";
|
else if(net.connType == conn_types::wifi) obj["connType"] = "Wifi";
|
||||||
else obj["connType"] = "Unknown";
|
else obj["connType"] = "Unknown";
|
||||||
|
JsonArray arrRooms = obj.createNestedArray("rooms");
|
||||||
|
somfy.toJSONRooms(arrRooms);
|
||||||
JsonArray arrShades = obj.createNestedArray("shades");
|
JsonArray arrShades = obj.createNestedArray("shades");
|
||||||
somfy.toJSONShades(arrShades);
|
somfy.toJSONShades(arrShades);
|
||||||
JsonArray arrGroups = obj.createNestedArray("groups");
|
JsonArray arrGroups = obj.createNestedArray("groups");
|
||||||
|
|
@ -951,6 +1035,7 @@ void Web::begin() {
|
||||||
apiServer.collectHeaders(keys, 1);
|
apiServer.collectHeaders(keys, 1);
|
||||||
apiServer.enableCORS(true);
|
apiServer.enableCORS(true);
|
||||||
apiServer.on("/discovery", []() { webServer.handleDiscovery(apiServer); });
|
apiServer.on("/discovery", []() { webServer.handleDiscovery(apiServer); });
|
||||||
|
apiServer.on("/rooms", []() {webServer.handleGetRooms(apiServer); });
|
||||||
apiServer.on("/shades", []() { webServer.handleGetShades(apiServer); });
|
apiServer.on("/shades", []() { webServer.handleGetShades(apiServer); });
|
||||||
apiServer.on("/groups", []() { webServer.handleGetGroups(apiServer); });
|
apiServer.on("/groups", []() { webServer.handleGetGroups(apiServer); });
|
||||||
apiServer.on("/login", []() { webServer.handleLogin(apiServer); });
|
apiServer.on("/login", []() { webServer.handleLogin(apiServer); });
|
||||||
|
|
@ -960,6 +1045,7 @@ void Web::begin() {
|
||||||
apiServer.on("/groupCommand", []() { webServer.handleGroupCommand(apiServer); });
|
apiServer.on("/groupCommand", []() { webServer.handleGroupCommand(apiServer); });
|
||||||
apiServer.on("/tiltCommand", []() { webServer.handleTiltCommand(apiServer); });
|
apiServer.on("/tiltCommand", []() { webServer.handleTiltCommand(apiServer); });
|
||||||
apiServer.on("/repeatCommand", []() { webServer.handleRepeatCommand(apiServer); });
|
apiServer.on("/repeatCommand", []() { webServer.handleRepeatCommand(apiServer); });
|
||||||
|
apiServer.on("/room", HTTP_GET, [] () { webServer.handleRoom(apiServer); });
|
||||||
apiServer.on("/shade", HTTP_GET, [] () { webServer.handleShade(apiServer); });
|
apiServer.on("/shade", HTTP_GET, [] () { webServer.handleShade(apiServer); });
|
||||||
apiServer.on("/group", HTTP_GET, [] () { webServer.handleGroup(apiServer); });
|
apiServer.on("/group", HTTP_GET, [] () { webServer.handleGroup(apiServer); });
|
||||||
apiServer.on("/setPositions", []() { webServer.handleSetPositions(apiServer); });
|
apiServer.on("/setPositions", []() { webServer.handleSetPositions(apiServer); });
|
||||||
|
|
@ -1070,10 +1156,22 @@ void Web::begin() {
|
||||||
server.on("/icon.png", []() { webServer.sendCacheHeaders(604800); webServer.handleStreamFile(server, "/icon.png", "image/png"); });
|
server.on("/icon.png", []() { webServer.sendCacheHeaders(604800); webServer.handleStreamFile(server, "/icon.png", "image/png"); });
|
||||||
server.onNotFound([]() { webServer.handleNotFound(server); });
|
server.onNotFound([]() { webServer.handleNotFound(server); });
|
||||||
server.on("/controller", []() { webServer.handleController(server); });
|
server.on("/controller", []() { webServer.handleController(server); });
|
||||||
|
server.on("/rooms", []() { webServer.handleGetRooms(server); });
|
||||||
server.on("/shades", []() { webServer.handleGetShades(server); });
|
server.on("/shades", []() { webServer.handleGetShades(server); });
|
||||||
server.on("/groups", []() { webServer.handleGetGroups(server); });
|
server.on("/groups", []() { webServer.handleGetGroups(server); });
|
||||||
|
server.on("/room", []() { webServer.handleRoom(server); });
|
||||||
server.on("/shade", []() { webServer.handleShade(server); });
|
server.on("/shade", []() { webServer.handleShade(server); });
|
||||||
server.on("/group", []() { webServer.handleGroup(server); });
|
server.on("/group", []() { webServer.handleGroup(server); });
|
||||||
|
server.on("/getNextRoom", []() {
|
||||||
|
webServer.sendCORSHeaders(server);
|
||||||
|
if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; }
|
||||||
|
StaticJsonDocument<256> doc;
|
||||||
|
uint8_t roomId = somfy.getNextRoomId();
|
||||||
|
JsonObject obj = doc.to<JsonObject>();
|
||||||
|
obj["roomId"] = roomId;
|
||||||
|
serializeJson(doc, g_content);
|
||||||
|
server.send(200, _encoding_json, g_content);
|
||||||
|
});
|
||||||
server.on("/getNextShade", []() {
|
server.on("/getNextShade", []() {
|
||||||
webServer.sendCORSHeaders(server);
|
webServer.sendCORSHeaders(server);
|
||||||
if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; }
|
if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; }
|
||||||
|
|
@ -1101,6 +1199,61 @@ void Web::begin() {
|
||||||
serializeJson(doc, g_content);
|
serializeJson(doc, g_content);
|
||||||
server.send(200, _encoding_json, g_content);
|
server.send(200, _encoding_json, g_content);
|
||||||
});
|
});
|
||||||
|
server.on("/addRoom", []() {
|
||||||
|
if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; }
|
||||||
|
HTTPMethod method = server.method();
|
||||||
|
SomfyRoom * room = nullptr;
|
||||||
|
if (method == HTTP_POST || method == HTTP_PUT) {
|
||||||
|
Serial.println("Adding a room");
|
||||||
|
DynamicJsonDocument doc(512);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
JsonObject obj = doc.as<JsonObject>();
|
||||||
|
Serial.println("Counting rooms");
|
||||||
|
if (somfy.roomCount() > SOMFY_MAX_ROOMS) {
|
||||||
|
server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Maximum number of rooms exceeded.\"}"));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Serial.println("Adding room");
|
||||||
|
room = somfy.addRoom(obj);
|
||||||
|
if (room) {
|
||||||
|
DynamicJsonDocument sdoc(512);
|
||||||
|
JsonObject sobj = sdoc.to<JsonObject>();
|
||||||
|
room->toJSON(sobj);
|
||||||
|
serializeJson(sdoc, g_content);
|
||||||
|
server.send(200, _encoding_json, g_content);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Error adding room.\"}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (room) {
|
||||||
|
DynamicJsonDocument doc(256);
|
||||||
|
JsonObject obj = doc.to<JsonObject>();
|
||||||
|
room->toJSON(obj);
|
||||||
|
serializeJson(doc, g_content);
|
||||||
|
Serial.println(g_content);
|
||||||
|
server.send(200, _encoding_json, g_content);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Error saving Somfy Room.\"}"));
|
||||||
|
}
|
||||||
|
});
|
||||||
server.on("/addShade", []() {
|
server.on("/addShade", []() {
|
||||||
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();
|
||||||
|
|
@ -1252,6 +1405,51 @@ void Web::begin() {
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
server.on("/saveRoom", []() {
|
||||||
|
webServer.sendCORSHeaders(server);
|
||||||
|
if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; }
|
||||||
|
HTTPMethod method = server.method();
|
||||||
|
if (method == HTTP_PUT || method == HTTP_POST) {
|
||||||
|
// We are updating an existing room.
|
||||||
|
if (server.hasArg("plain")) {
|
||||||
|
Serial.println("Updating a room");
|
||||||
|
DynamicJsonDocument doc(512);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
JsonObject obj = doc.as<JsonObject>();
|
||||||
|
if (obj.containsKey("roomId")) {
|
||||||
|
SomfyRoom* room = somfy.getRoomById(obj["roomId"]);
|
||||||
|
if (room) {
|
||||||
|
room->fromJSON(obj);
|
||||||
|
room->save();
|
||||||
|
DynamicJsonDocument sdoc(512);
|
||||||
|
JsonObject sobj = sdoc.to<JsonObject>();
|
||||||
|
room->toJSON(sobj);
|
||||||
|
serializeJson(sdoc, g_content);
|
||||||
|
server.send(200, _encoding_json, g_content);
|
||||||
|
}
|
||||||
|
else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Room Id not found.\"}"));
|
||||||
|
}
|
||||||
|
else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No room id was supplied.\"}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No room object supplied.\"}"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
server.on("/saveShade", []() {
|
server.on("/saveShade", []() {
|
||||||
webServer.sendCORSHeaders(server);
|
webServer.sendCORSHeaders(server);
|
||||||
if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; }
|
if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; }
|
||||||
|
|
@ -1756,6 +1954,47 @@ void Web::begin() {
|
||||||
else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No unlinking object supplied.\"}"));
|
else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No unlinking object supplied.\"}"));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
server.on("/deleteRoom", []() {
|
||||||
|
webServer.sendCORSHeaders(server);
|
||||||
|
if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; }
|
||||||
|
HTTPMethod method = server.method();
|
||||||
|
uint8_t roomId = 0;
|
||||||
|
if (method == HTTP_GET || method == HTTP_PUT || method == HTTP_POST) {
|
||||||
|
if (server.hasArg("roomId")) {
|
||||||
|
roomId = atoi(server.arg("roomId").c_str());
|
||||||
|
}
|
||||||
|
else if (server.hasArg("plain")) {
|
||||||
|
Serial.println("Deleting a Room");
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
JsonObject obj = doc.as<JsonObject>();
|
||||||
|
if (obj.containsKey("roomId")) roomId = obj["roomId"];
|
||||||
|
else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No room id was supplied.\"}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No room object supplied.\"}"));
|
||||||
|
}
|
||||||
|
SomfyRoom* room = somfy.getRoomById(roomId);
|
||||||
|
if (!room) server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Room with the specified id not found.\"}"));
|
||||||
|
else {
|
||||||
|
somfy.deleteRoom(roomId);
|
||||||
|
server.send(200, _encoding_json, F("{\"status\":\"SUCCESS\",\"desc\":\"Room deleted.\"}"));
|
||||||
|
}
|
||||||
|
});
|
||||||
server.on("/deleteShade", []() {
|
server.on("/deleteShade", []() {
|
||||||
webServer.sendCORSHeaders(server);
|
webServer.sendCORSHeaders(server);
|
||||||
if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; }
|
if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; }
|
||||||
|
|
@ -2362,6 +2601,40 @@ void Web::begin() {
|
||||||
serializeJson(doc, g_content);
|
serializeJson(doc, g_content);
|
||||||
server.send(200, _encoding_json, g_content);
|
server.send(200, _encoding_json, g_content);
|
||||||
});
|
});
|
||||||
|
server.on("/roomSortOrder", []() {
|
||||||
|
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 roomId = v.as<uint8_t>();
|
||||||
|
if (roomId != 0) {
|
||||||
|
SomfyRoom *room = somfy.getRoomById(roomId);
|
||||||
|
if(room) room->sortOrder = order++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
server.send(200, "application/json", "{\"status\":\"OK\",\"desc\":\"Successfully set room order\"}");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
server.send(201, "application/json", "{\"status\":\"ERROR\",\"desc\":\"Invalid HTTP Method: \"}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
server.on("/shadeSortOrder", []() {
|
server.on("/shadeSortOrder", []() {
|
||||||
if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; }
|
if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; }
|
||||||
DynamicJsonDocument doc(512);
|
DynamicJsonDocument doc(512);
|
||||||
|
|
|
||||||
2
Web.h
2
Web.h
|
|
@ -12,6 +12,7 @@ class Web {
|
||||||
void handleStreamFile(WebServer &server, const char *filename, const char *encoding);
|
void handleStreamFile(WebServer &server, const char *filename, const char *encoding);
|
||||||
void handleController(WebServer &server);
|
void handleController(WebServer &server);
|
||||||
void handleLoginContext(WebServer &server);
|
void handleLoginContext(WebServer &server);
|
||||||
|
void handleGetRooms(WebServer &server);
|
||||||
void handleGetShades(WebServer &server);
|
void handleGetShades(WebServer &server);
|
||||||
void handleGetGroups(WebServer &server);
|
void handleGetGroups(WebServer &server);
|
||||||
void handleShadeCommand(WebServer &server);
|
void handleShadeCommand(WebServer &server);
|
||||||
|
|
@ -20,6 +21,7 @@ class Web {
|
||||||
void handleTiltCommand(WebServer &server);
|
void handleTiltCommand(WebServer &server);
|
||||||
void handleDiscovery(WebServer &server);
|
void handleDiscovery(WebServer &server);
|
||||||
void handleNotFound(WebServer &server);
|
void handleNotFound(WebServer &server);
|
||||||
|
void handleRoom(WebServer &server);
|
||||||
void handleShade(WebServer &server);
|
void handleShade(WebServer &server);
|
||||||
void handleGroup(WebServer &server);
|
void handleGroup(WebServer &server);
|
||||||
void handleSetPositions(WebServer &server);
|
void handleSetPositions(WebServer &server);
|
||||||
|
|
|
||||||
|
|
@ -1472,3 +1472,26 @@ i.icss-cgate {
|
||||||
border-top-right-radius: .05em;
|
border-top-right-radius: .05em;
|
||||||
background-color: black;
|
background-color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i.icss-bars {
|
||||||
|
margin: .41em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
i.icss-bars,
|
||||||
|
i.icss-bars:before,
|
||||||
|
i.icss-bars:after {
|
||||||
|
width: 1em;
|
||||||
|
height: .18em;
|
||||||
|
border-radius: .06em;
|
||||||
|
background-color: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
i.icss-bars:before {
|
||||||
|
top: -0.36em;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
i.icss-bars:after {
|
||||||
|
top: 0.36em;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
@ -292,8 +292,35 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="divSomfySettings" style="display:none;">
|
<div id="divSomfySettings" style="display:none;">
|
||||||
<div class="subtab-container"><span class="selected" data-grpid="divSomfyMotors">Shades</span><span data-grpid="divSomfyGroups">Groups</span><span data-grpid="divVirtualRemote">Virtual Remote</span></div>
|
<div class="subtab-container"><span class="selected" data-grpid="divSomfyRooms">Rooms</span><span class="" data-grpid="divSomfyMotors">Shades</span><span data-grpid="divSomfyGroups">Groups</span><span data-grpid="divVirtualRemote">Virtual Remote</span></div>
|
||||||
<div id="divSomfyMotors" class="subtab-content" style="padding-top:10px;">
|
<div id="divSomfyRooms" class="subtab-content" style="padding-top:10px;">
|
||||||
|
<div id="divRoomListContainer">
|
||||||
|
<div style="font-size:.8em;">Drag each item to set the order in which they appear in the list.</div>
|
||||||
|
<div id="divRoomList" class="edit-roomlist"></div>
|
||||||
|
<div class="button-container">
|
||||||
|
<button id="btnAddRoom" type="button" onclick="somfy.openEditRoom();">
|
||||||
|
Add Room
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="somfyRoom" style="width:100%;display:none;">
|
||||||
|
<div style="display:inline-block;float:right;position:relative;"><span id="spanRoomId">*</span>/<span id="spanMaxRooms">16</span></div>
|
||||||
|
<div class="field-group" style="padding:0px;">
|
||||||
|
<input id="fldRoomName" name="roomName" data-bind="name" type="text" length=20 placeholder="Name">
|
||||||
|
<label for="fldRoomName">Name</label>
|
||||||
|
</div>
|
||||||
|
<div class="button-container" style="margin-top:-10px;padding-left:7px;padding-right:7px;">
|
||||||
|
<button id="btnSaveRoom" type="button" onclick="somfy.saveRoom();">
|
||||||
|
Save Room
|
||||||
|
</button>
|
||||||
|
<button id="btnRoomGoBack" type="button" onclick="somfy.showEditRoom(false);">
|
||||||
|
Done
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="divSomfyMotors" class="subtab-content" style="padding-top:10px;display:none;">
|
||||||
<div id="divShadeListContainer">
|
<div id="divShadeListContainer">
|
||||||
<div style="font-size:.8em;">Drag each item to set the order in which they appear in the list.</div>
|
<div style="font-size:.8em;">Drag each item to set the order in which they appear in the list.</div>
|
||||||
<div id="divShadeList" class="edit-motorlist"></div>
|
<div id="divShadeList" class="edit-motorlist"></div>
|
||||||
|
|
@ -378,6 +405,10 @@
|
||||||
<div class="button-outline toggle-button" style="width:127px;text-align:center;border-radius:33%;font-size:2em;padding:10px;" data-cmd="toggle" onclick="somfy.sendCommand(parseInt(document.getElementById('spanShadeId').innerText, 10), 'toggle');"><i class="icss-somfy-toggle" style="margin-top:-4px;"></i></div>
|
<div class="button-outline toggle-button" style="width:127px;text-align:center;border-radius:33%;font-size:2em;padding:10px;" data-cmd="toggle" onclick="somfy.sendCommand(parseInt(document.getElementById('spanShadeId').innerText, 10), 'toggle');"><i class="icss-somfy-toggle" style="margin-top:-4px;"></i></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="field-group" style="padding:0px;">
|
||||||
|
<select id="selShadeRoom" data-bind="roomId" data-datatype="int" style="width:100%;"></select>
|
||||||
|
<label for="selShadeRoom">Room</label>
|
||||||
|
</div>
|
||||||
<div class="field-group" style="padding:0px;">
|
<div class="field-group" style="padding:0px;">
|
||||||
<input id="fldShadeName" name="shadeName" data-bind="name" type="text" length=20 placeholder="Name">
|
<input id="fldShadeName" name="shadeName" data-bind="name" type="text" length=20 placeholder="Name">
|
||||||
<label for="fldShadeName">Name</label>
|
<label for="fldShadeName">Name</label>
|
||||||
|
|
@ -550,6 +581,10 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="field-group" style="padding:0px;">
|
||||||
|
<select id="selGroupRoom" data-bind="roomId" data-datatype="int" style="width:100%;"></select>
|
||||||
|
<label for="selGroupRoom">Room</label>
|
||||||
|
</div>
|
||||||
<div class="field-group" style="padding:0px;">
|
<div class="field-group" style="padding:0px;">
|
||||||
<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>
|
||||||
|
|
@ -795,6 +830,10 @@
|
||||||
</div>
|
</div>
|
||||||
<div id="divHomePnl" style="position:relative;">
|
<div id="divHomePnl" style="position:relative;">
|
||||||
<hr />
|
<hr />
|
||||||
|
<div id="divRoomSelector" class="room-selector" data-roomid="0" onclick="event.stopPropagation(); somfy.openSelectRoom();">
|
||||||
|
<i class="icss-bars"></i><span>Home</span>
|
||||||
|
<div id="divRoomSelector-list" class="room-selector-list"></div>
|
||||||
|
</div>
|
||||||
<div id="divGroupControls"></div>
|
<div id="divGroupControls"></div>
|
||||||
<div id="divShadeControls" style="min-height:130px;"></div>
|
<div id="divShadeControls" style="min-height:130px;"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
275
data/index.js
275
data/index.js
|
|
@ -1,5 +1,6 @@
|
||||||
//var hst = '192.168.1.208';
|
//var hst = '192.168.1.208';
|
||||||
var hst = '192.168.1.152';
|
var hst = '192.168.1.152';
|
||||||
|
var _rooms = [{ roomId: 0, name: 'Home' }];
|
||||||
|
|
||||||
var errors = [
|
var errors = [
|
||||||
{ code: -10, desc: "Pin setting in use for Transceiver. Output pins cannot be re-used." },
|
{ code: -10, desc: "Pin setting in use for Transceiver. Output pins cannot be re-used." },
|
||||||
|
|
@ -506,6 +507,12 @@ async function initSockets() {
|
||||||
case 'shadeCommand':
|
case 'shadeCommand':
|
||||||
console.log(msg);
|
console.log(msg);
|
||||||
break;
|
break;
|
||||||
|
case 'roomRemoved':
|
||||||
|
somfy.procRoomRemoved(msg);
|
||||||
|
break;
|
||||||
|
case 'roomAdded':
|
||||||
|
somfy.procRoomAdded(msg);
|
||||||
|
break;
|
||||||
case 'shadeRemoved':
|
case 'shadeRemoved':
|
||||||
break;
|
break;
|
||||||
case 'shadeAdded':
|
case 'shadeAdded':
|
||||||
|
|
@ -1935,6 +1942,7 @@ class Somfy {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
console.log(somfy);
|
console.log(somfy);
|
||||||
|
document.getElementById('spanMaxRooms').innerText = somfy.maxRooms || 0;
|
||||||
document.getElementById('spanMaxShades').innerText = somfy.maxShades;
|
document.getElementById('spanMaxShades').innerText = somfy.maxShades;
|
||||||
document.getElementById('spanMaxGroups').innerText = somfy.maxGroups;
|
document.getElementById('spanMaxGroups').innerText = somfy.maxGroups;
|
||||||
ui.toElement(document.getElementById('divTransceiverSettings'), somfy);
|
ui.toElement(document.getElementById('divTransceiverSettings'), somfy);
|
||||||
|
|
@ -1945,6 +1953,7 @@ class Somfy {
|
||||||
document.getElementById('divRadioError').style.display = '';
|
document.getElementById('divRadioError').style.display = '';
|
||||||
}
|
}
|
||||||
// Create the shades list.
|
// Create the shades list.
|
||||||
|
this.setRoomsList(somfy.rooms);
|
||||||
this.setShadesList(somfy.shades);
|
this.setShadesList(somfy.shades);
|
||||||
this.setGroupsList(somfy.groups);
|
this.setGroupsList(somfy.groups);
|
||||||
}
|
}
|
||||||
|
|
@ -2096,13 +2105,109 @@ class Somfy {
|
||||||
}
|
}
|
||||||
btnDown = null;
|
btnDown = null;
|
||||||
btnTimer = null;
|
btnTimer = null;
|
||||||
// Sort the array first to allow the user to drag and drop where they want the shade.
|
procRoomAdded(room) {
|
||||||
|
let r = _rooms.find(x => x.roomId === room.roomId);
|
||||||
|
if (typeof r === 'undefined' || !r) {
|
||||||
|
_rooms.push(room);
|
||||||
|
_rooms.sort((a, b) => { return a.sortOrder - b.sortOrder });
|
||||||
|
this.setRoomsList(_rooms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
procRoomRemoved(room) {
|
||||||
|
if (room.roomId === 0) return;
|
||||||
|
let r = _rooms.find(x => x.roomId === room.roomId);
|
||||||
|
if (typeof r !== 'undefined' && r.roomId === room.roomId) {
|
||||||
|
_rooms = _rooms.filter(x => x.roomId === room.roomId);
|
||||||
|
_rooms.sort((a, b) => { return a.sortOrder - b.sortOrder });
|
||||||
|
this.setRoomsList(_rooms);
|
||||||
|
let rs = document.getElementById('divRoomSelector');
|
||||||
|
let ss = document.getElementById('divShadeControls');
|
||||||
|
let gs = document.getElementById('divGroupControls');
|
||||||
|
let ctls = ss.querySelectorAll('.somfyShadeCtl');
|
||||||
|
for (let i = 0; i < ctls.length; i++) {
|
||||||
|
let x = ctls[i];
|
||||||
|
if (parseInt(x.getAttribute('data-roomid'), 10) === room.roomId)
|
||||||
|
x.setAttribute('data-roomid', '0');
|
||||||
|
}
|
||||||
|
ctls = gs.querySelectorAll('.somfyGroupCtl');
|
||||||
|
for (let i = 0; i < ctls.length; i++) {
|
||||||
|
let x = ctls[i];
|
||||||
|
if (parseInt(x.getAttribute('data-roomid'), 10) === room.roomId)
|
||||||
|
x.setAttribute('data-roomid', '0');
|
||||||
|
}
|
||||||
|
if (parseInt(rs.getAttribute('data-roomid'), 10) === room.roomId) this.selectRoom(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
selectRoom(roomId) {
|
||||||
|
let room = _rooms.find(x => x.roomId === roomId) || { roomId: 0, name: '' };
|
||||||
|
let rs = document.getElementById('divRoomSelector');
|
||||||
|
rs.setAttribute('data-roomid', roomId);
|
||||||
|
rs.querySelector('span').innerHTML = room.name;
|
||||||
|
document.getElementById('divRoomSelector-list').style.display = 'none';
|
||||||
|
let ss = document.getElementById('divShadeControls');
|
||||||
|
ss.setAttribute('data-roomid', roomId);
|
||||||
|
let ctls = ss.querySelectorAll('.somfyShadeCtl');
|
||||||
|
for (let i = 0; i < ctls.length; i++) {
|
||||||
|
let x = ctls[i];
|
||||||
|
if (roomId !== 0 && parseInt(x.getAttribute('data-roomid'), 10) !== roomId)
|
||||||
|
x.style.display = 'none';
|
||||||
|
else
|
||||||
|
x.style.display = '';
|
||||||
|
}
|
||||||
|
let gs = document.getElementById('divGroupControls');
|
||||||
|
ctls = gs.querySelectorAll('.somfyGroupCtl');
|
||||||
|
for (let i = 0; i < ctls.length; i++) {
|
||||||
|
let x = ctls[i];
|
||||||
|
if (roomId !== 0 && parseInt(x.getAttribute('data-roomid'), 10) !== roomId)
|
||||||
|
x.style.display = 'none';
|
||||||
|
else
|
||||||
|
x.style.display = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setRoomsList(rooms) {
|
||||||
|
let divCfg = '';
|
||||||
|
let divCtl = `<div class='room-row' data-roomid="${0}" onclick="somfy.selectRoom(0);event.stopPropagation();">Home</div>`;
|
||||||
|
let divOpts = '<option value="0">Home</option>';
|
||||||
|
_rooms = [{ roomId: 0, name: 'Home' }];
|
||||||
|
rooms.sort((a, b) => { return a.sortOrder - b.sortOrder });
|
||||||
|
for (let i = 0; i < rooms.length; i++) {
|
||||||
|
let room = rooms[i];
|
||||||
|
divCfg += `<div class="somfyRoom room-draggable" draggable="true" data-roomid="${room.roomId}">`;
|
||||||
|
divCfg += `<div class="button-outline" onclick="somfy.openEditRoom(${room.roomId});"><i class="icss-edit"></i></div>`;
|
||||||
|
divCfg += `<span class="room-name">${room.name}</span>`;
|
||||||
|
divCfg += `<div class="button-outline" onclick="somfy.deleteRoom(${room.roomId});"><i class="icss-trash"></i></div>`;
|
||||||
|
divCfg += '</div>';
|
||||||
|
divOpts += `<option value="${room.roomId}">${room.name}</option>`;
|
||||||
|
_rooms.push(room);
|
||||||
|
divCtl += `<div class='room-row' data-roomid="${room.roomId}" onclick="somfy.selectRoom(${room.roomId});event.stopPropagation();">${room.name}</div>`;
|
||||||
|
}
|
||||||
|
if (rooms.length === 0) document.getElementById('divRoomSelector').style.display = 'none';
|
||||||
|
else document.getElementById('divRoomSelector').style.display = '';
|
||||||
|
document.getElementById('divRoomSelector-list').innerHTML = divCtl;
|
||||||
|
document.getElementById('divRoomList').innerHTML = divCfg;
|
||||||
|
document.getElementById('selShadeRoom').innerHTML = divOpts;
|
||||||
|
document.getElementById('selGroupRoom').innerHTML = divOpts;
|
||||||
|
//roomControls.innerHTML = divCtl;
|
||||||
|
this.setListDraggable(document.getElementById('divRoomList'), '.room-draggable', (list) => {
|
||||||
|
// Get the shade order
|
||||||
|
let items = list.querySelectorAll('.room-draggable');
|
||||||
|
let order = [];
|
||||||
|
for (let i = 0; i < items.length; i++) {
|
||||||
|
order.push(parseInt(items[i].getAttribute('data-roomid'), 10));
|
||||||
|
// Reorder the shades on the main page.
|
||||||
|
}
|
||||||
|
putJSONSync('/roomSortOrder', order, (err) => {
|
||||||
|
if (err) ui.serviceError(err);
|
||||||
|
else this.updateRoomsList();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
setShadesList(shades) {
|
setShadesList(shades) {
|
||||||
let divCfg = '';
|
let divCfg = '';
|
||||||
let divCtl = '';
|
let divCtl = '';
|
||||||
shades.sort((a, b) => { return a.sortOrder - b.sortOrder });
|
shades.sort((a, b) => { return a.sortOrder - b.sortOrder });
|
||||||
console.log(shades);
|
console.log(shades);
|
||||||
|
let roomId = parseInt(document.getElementById('divRoomSelector').getAttribute('data-roomid'), 10)
|
||||||
|
|
||||||
let vrList = document.getElementById('selVRMotor');
|
let vrList = document.getElementById('selVRMotor');
|
||||||
// First get the optiongroup for the shades.
|
// First get the optiongroup for the shades.
|
||||||
|
|
@ -2123,17 +2228,23 @@ class Somfy {
|
||||||
}
|
}
|
||||||
for (let i = 0; i < shades.length; i++) {
|
for (let i = 0; i < shades.length; i++) {
|
||||||
let shade = shades[i];
|
let shade = shades[i];
|
||||||
|
let room = _rooms.find(x => x.roomId === shade.roomId) || { roomId: 0, name: '' };
|
||||||
let st = this.shadeTypes.find(x => x.type === shade.shadeType) || { type: shade.shadeType, ico: 'icss-window-shade' };
|
let st = this.shadeTypes.find(x => x.type === shade.shadeType) || { type: shade.shadeType, ico: 'icss-window-shade' };
|
||||||
|
|
||||||
divCfg += `<div class="somfyShade shade-draggable" draggable="true" data-mypos="${shade.myPos}" data-shadeid="${shade.shadeId}" data-remoteaddress="${shade.remoteAddress}" data-tilt="${shade.tiltType}" data-shadetype="${shade.shadeType} data-flipposition="${shade.flipPosition ? 'true' : 'false'}">`;
|
divCfg += `<div class="somfyShade shade-draggable" draggable="true" data-roomid="${shade.roomId}" data-mypos="${shade.myPos}" data-shadeid="${shade.shadeId}" data-remoteaddress="${shade.remoteAddress}" data-tilt="${shade.tiltType}" data-shadetype="${shade.shadeType} data-flipposition="${shade.flipPosition ? 'true' : 'false'}">`;
|
||||||
divCfg += `<div class="button-outline" onclick="somfy.openEditShade(${shade.shadeId});"><i class="icss-edit"></i></div>`;
|
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 += `<i class="shade-icon" data-position="${shade.position || 0}%"></i>`;
|
||||||
divCfg += `<span class="shade-name">${shade.name}</span>`;
|
//divCfg += `<span class="shade-name">${shade.name}</span>`;
|
||||||
|
divCfg += '<div class="shade-name">';
|
||||||
|
divCfg += `<div class="cfg-room">${room.name}</div>`;
|
||||||
|
divCfg += `<div class="">${shade.name}</div>`;
|
||||||
|
divCfg += '</div>'
|
||||||
|
|
||||||
divCfg += `<span class="shade-address">${shade.remoteAddress}</span>`;
|
divCfg += `<span class="shade-address">${shade.remoteAddress}</span>`;
|
||||||
divCfg += `<div class="button-outline" onclick="somfy.deleteShade(${shade.shadeId});"><i class="icss-trash"></i></div>`;
|
divCfg += `<div class="button-outline" onclick="somfy.deleteShade(${shade.shadeId});"><i class="icss-trash"></i></div>`;
|
||||||
divCfg += '</div>';
|
divCfg += '</div>';
|
||||||
|
|
||||||
divCtl += `<div class="somfyShadeCtl" data-shadeId="${shade.shadeId}" data-direction="${shade.direction}" data-remoteaddress="${shade.remoteAddress}" data-position="${shade.position}" data-target="${shade.target}" data-mypos="${shade.myPos}" data-mytiltpos="${shade.myTiltPos}" data-shadetype="${shade.shadeType}" data-tilt="${shade.tiltType}" data-flipposition="${shade.flipPosition ? 'true': 'false'}"`;
|
divCtl += `<div class="somfyShadeCtl" style="${roomId === 0 || roomId === room.roomId ? '' : 'display:none'}" data-shadeId="${shade.shadeId}" data-roomid="${shade.roomId}" data-direction="${shade.direction}" data-remoteaddress="${shade.remoteAddress}" data-position="${shade.position}" data-target="${shade.target}" data-mypos="${shade.myPos}" data-mytiltpos="${shade.myTiltPos}" data-shadetype="${shade.shadeType}" data-tilt="${shade.tiltType}" data-flipposition="${shade.flipPosition ? 'true' : 'false'}"`;
|
||||||
divCtl += ` data-windy="${(shade.flags & 0x10) === 0x10 ? 'true' : 'false'}" data-sunny=${(shade.flags & 0x20) === 0x20 ? 'true' : 'false'}`;
|
divCtl += ` data-windy="${(shade.flags & 0x10) === 0x10 ? 'true' : 'false'}" data-sunny=${(shade.flags & 0x20) === 0x20 ? 'true' : 'false'}`;
|
||||||
if (shade.tiltType !== 0) {
|
if (shade.tiltType !== 0) {
|
||||||
divCtl += ` data-tiltposition="${shade.tiltPosition}" data-tiltdirection="${shade.tiltDirection}" data-tilttarget="${shade.tiltTarget}"`;
|
divCtl += ` data-tiltposition="${shade.tiltPosition}" data-tiltdirection="${shade.tiltDirection}" data-tilttarget="${shade.tiltTarget}"`;
|
||||||
|
|
@ -2146,7 +2257,7 @@ class Somfy {
|
||||||
divCtl += shade.tiltType !== 0 ? `<i class="icss-window-tilt" data-shadeid="${shade.shadeId}" data-tiltposition="${shade.tiltPosition}"></i></div>` : '</div>';
|
divCtl += shade.tiltType !== 0 ? `<i class="icss-window-tilt" data-shadeid="${shade.shadeId}" data-tiltposition="${shade.tiltPosition}"></i></div>` : '</div>';
|
||||||
divCtl += `<div class="indicator indicator-wind"><i class="icss-warning"></i></div><div class="indicator indicator-sun"><i class="icss-sun"></i></div>`;
|
divCtl += `<div class="indicator indicator-wind"><i class="icss-warning"></i></div><div class="indicator indicator-sun"><i class="icss-sun"></i></div>`;
|
||||||
divCtl += `<div class="shade-name">`;
|
divCtl += `<div class="shade-name">`;
|
||||||
|
divCtl += `<span class="shadectl-room">${room.name}</span>`;
|
||||||
divCtl += `<span class="shadectl-name">${shade.name}</span>`;
|
divCtl += `<span class="shadectl-name">${shade.name}</span>`;
|
||||||
divCtl += `<span class="shadectl-mypos"><label class="my-pos"></label><span class="my-pos">${shade.myPos === -1 ? '---' : shade.myPos + '%'}</span><label class="my-pos-tilt"></label><span class="my-pos-tilt">${shade.myTiltPos === -1 ? '---' : shade.myTiltPos + '%'}</span >`;
|
divCtl += `<span class="shadectl-mypos"><label class="my-pos"></label><span class="my-pos">${shade.myPos === -1 ? '---' : shade.myPos + '%'}</span><label class="my-pos-tilt"></label><span class="my-pos-tilt">${shade.myTiltPos === -1 ? '---' : shade.myTiltPos + '%'}</span >`;
|
||||||
divCtl += '</div>';
|
divCtl += '</div>';
|
||||||
|
|
@ -2426,6 +2537,7 @@ class Somfy {
|
||||||
optGroup.innerHTML = '';
|
optGroup.innerHTML = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let roomId = parseInt(document.getElementById('divRoomSelector').getAttribute('data-roomid'), 10)
|
||||||
|
|
||||||
if (typeof groups !== 'undefined') {
|
if (typeof groups !== 'undefined') {
|
||||||
groups.sort((a, b) => { return a.sortOrder - b.sortOrder });
|
groups.sort((a, b) => { return a.sortOrder - b.sortOrder });
|
||||||
|
|
@ -2433,16 +2545,22 @@ class Somfy {
|
||||||
|
|
||||||
for (let i = 0; i < groups.length; i++) {
|
for (let i = 0; i < groups.length; i++) {
|
||||||
let group = groups[i];
|
let group = groups[i];
|
||||||
divCfg += `<div class="somfyGroup group-draggable" draggable="true" data-groupid="${group.groupId}" data-remoteaddress="${group.remoteAddress}">`;
|
let room = _rooms.find(x => x.roomId === group.roomId) || { roomId: 0, name: '' };
|
||||||
|
|
||||||
|
divCfg += `<div class="somfyGroup group-draggable" draggable="true" data-roomid="${group.roomId}" 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 += `<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 += `<i class="Group-icon" data-position="${Group.position || 0}%"></i>`;
|
||||||
divCfg += `<span class="group-name">${group.name}</span>`;
|
divCfg += '<div class="group-name">';
|
||||||
|
divCfg += `<div class="cfg-room">${room.name}</div>`;
|
||||||
|
divCfg += `<div class="">${group.name}</div>`;
|
||||||
|
divCfg += '</div>'
|
||||||
divCfg += `<span class="group-address">${group.remoteAddress}</span>`;
|
divCfg += `<span class="group-address">${group.remoteAddress}</span>`;
|
||||||
divCfg += `<div class="button-outline" onclick="somfy.deleteGroup(${group.groupId});"><i class="icss-trash"></i></div>`;
|
divCfg += `<div class="button-outline" onclick="somfy.deleteGroup(${group.groupId});"><i class="icss-trash"></i></div>`;
|
||||||
divCfg += '</div>';
|
divCfg += '</div>';
|
||||||
|
|
||||||
divCtl += `<div class="somfyGroupCtl" data-groupId="${group.groupId}" data-remoteaddress="${group.remoteAddress}">`;
|
divCtl += `<div class="somfyGroupCtl" style="${roomId === 0 || roomId === room.roomId ? '' : 'display:none'}" data-groupId="${group.groupId}" data-roomid="${group.roomId}" data-remoteaddress="${group.remoteAddress}">`;
|
||||||
divCtl += `<div class="group-name">`;
|
divCtl += `<div class="group-name">`;
|
||||||
|
divCtl += `<span class="groupctl-room">${room.name}</span>`;
|
||||||
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">`;
|
||||||
if (typeof group.linkedShades !== 'undefined') {
|
if (typeof group.linkedShades !== 'undefined') {
|
||||||
|
|
@ -2821,6 +2939,37 @@ class Somfy {
|
||||||
onShadeProtoChanged(el) {
|
onShadeProtoChanged(el) {
|
||||||
document.getElementById('somfyShade').setAttribute('data-proto', el.value);
|
document.getElementById('somfyShade').setAttribute('data-proto', el.value);
|
||||||
}
|
}
|
||||||
|
openEditRoom(roomId) {
|
||||||
|
|
||||||
|
if (typeof roomId === 'undefined') {
|
||||||
|
document.getElementById('btnSaveRoom').innerText = 'Add Room';
|
||||||
|
getJSONSync('/getNextRoom', (err, room) => {
|
||||||
|
document.getElementById('spanRoomId').innerText = '*';
|
||||||
|
if (err) ui.serviceError(err);
|
||||||
|
else {
|
||||||
|
console.log(room);
|
||||||
|
let elRoom = document.getElementById('somfyRoom');
|
||||||
|
room.name = '';
|
||||||
|
ui.toElement(elRoom, room);
|
||||||
|
this.showEditRoom(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
document.getElementById('btnSaveRoom').innerText = 'Save Room';
|
||||||
|
getJSONSync(`/room?roomId=${roomId}`, (err, room) => {
|
||||||
|
if (err) ui.serviceError(err);
|
||||||
|
else {
|
||||||
|
console.log(room);
|
||||||
|
document.getElementById('spanRoomId').innerText = roomId;
|
||||||
|
ui.toElement(document.getElementById('somfyRoom'), room);
|
||||||
|
this.showEditRoom(true);
|
||||||
|
document.getElementById('btnSaveRoom').style.display = 'inline-block';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
openEditShade(shadeId) {
|
openEditShade(shadeId) {
|
||||||
if (typeof shadeId === 'undefined') {
|
if (typeof shadeId === 'undefined') {
|
||||||
getJSONSync('/getNextShade', (err, shade) => {
|
getJSONSync('/getNextShade', (err, shade) => {
|
||||||
|
|
@ -2839,7 +2988,7 @@ class Somfy {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
console.log(shade);
|
console.log(shade);
|
||||||
let elShade = document.getElementById('somfyShade')
|
let elShade = document.getElementById('somfyShade');
|
||||||
shade.name = '';
|
shade.name = '';
|
||||||
shade.downTime = shade.upTime = 10000;
|
shade.downTime = shade.upTime = 10000;
|
||||||
shade.tiltTime = 7000;
|
shade.tiltTime = 7000;
|
||||||
|
|
@ -2939,6 +3088,22 @@ class Somfy {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
showEditRoom(bShow) {
|
||||||
|
let el = document.getElementById('divLinking');
|
||||||
|
if (el) el.remove();
|
||||||
|
el = document.getElementById('divPairing');
|
||||||
|
if (el) el.remove();
|
||||||
|
el = document.getElementById('divRollingCode');
|
||||||
|
if (el) el.remove();
|
||||||
|
el = document.getElementById('somfyRoom');
|
||||||
|
if (el) el.style.display = bShow ? '' : 'none';
|
||||||
|
el = document.getElementById('divRoomListContainer');
|
||||||
|
if (el) el.style.display = bShow ? 'none' : '';
|
||||||
|
if (bShow) {
|
||||||
|
this.showEditGroup(false);
|
||||||
|
this.showEditShade(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
showEditShade(bShow) {
|
showEditShade(bShow) {
|
||||||
let el = document.getElementById('divLinking');
|
let el = document.getElementById('divLinking');
|
||||||
if (el) el.remove();
|
if (el) el.remove();
|
||||||
|
|
@ -2950,6 +3115,10 @@ class Somfy {
|
||||||
if (el) el.style.display = bShow ? '' : 'none';
|
if (el) el.style.display = bShow ? '' : 'none';
|
||||||
el = document.getElementById('divShadeListContainer');
|
el = document.getElementById('divShadeListContainer');
|
||||||
if (el) el.style.display = bShow ? 'none' : '';
|
if (el) el.style.display = bShow ? 'none' : '';
|
||||||
|
if (bShow) {
|
||||||
|
this.showEditGroup(false);
|
||||||
|
this.showEditRoom(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
showEditGroup(bShow) {
|
showEditGroup(bShow) {
|
||||||
let el = document.getElementById('divLinking');
|
let el = document.getElementById('divLinking');
|
||||||
|
|
@ -2962,6 +3131,46 @@ class Somfy {
|
||||||
if (el) el.style.display = bShow ? '' : 'none';
|
if (el) el.style.display = bShow ? '' : 'none';
|
||||||
el = document.getElementById('divGroupListContainer');
|
el = document.getElementById('divGroupListContainer');
|
||||||
if (el) el.style.display = bShow ? 'none' : '';
|
if (el) el.style.display = bShow ? 'none' : '';
|
||||||
|
if (bShow) {
|
||||||
|
this.showEditRoom(false);
|
||||||
|
this.showEditShade(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
saveRoom() {
|
||||||
|
let roomId = parseInt(document.getElementById('spanRoomId').innerText, 10);
|
||||||
|
let obj = ui.fromElement(document.getElementById('somfyRoom'));
|
||||||
|
let valid = true;
|
||||||
|
if (valid && (typeof obj.name !== 'string' || obj.name === '' || obj.name.length > 20)) {
|
||||||
|
ui.errorMessage(document.getElementById('divSomfySettings'), 'You must provide a name for the room between 1 and 20 characters.');
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
if (valid) {
|
||||||
|
if (isNaN(roomId) || roomId === 0) {
|
||||||
|
// We are adding.
|
||||||
|
putJSONSync('/addRoom', obj, (err, room) => {
|
||||||
|
if (err) {
|
||||||
|
ui.serviceError(err);
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log(room);
|
||||||
|
document.getElementById('spanRoomId').innerText = room.roomId;
|
||||||
|
document.getElementById('btnSaveRoom').innerText = 'Save Room';
|
||||||
|
document.getElementById('btnSaveRoom').style.display = 'inline-block';
|
||||||
|
this.updateRoomsList();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
obj.roomId = roomId;
|
||||||
|
putJSONSync('/saveRoom', obj, (err, room) => {
|
||||||
|
if (err) ui.serviceError(err);
|
||||||
|
else this.updateRoomsList();
|
||||||
|
console.log(room);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
saveShade() {
|
saveShade() {
|
||||||
let shadeId = parseInt(document.getElementById('spanShadeId').innerText, 10);
|
let shadeId = parseInt(document.getElementById('spanShadeId').innerText, 10);
|
||||||
|
|
@ -3084,6 +3293,18 @@ class Somfy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
updateRoomsList() {
|
||||||
|
getJSONSync('/rooms', (err, shades) => {
|
||||||
|
if (err) {
|
||||||
|
console.log(err);
|
||||||
|
ui.serviceError(err);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.setRoomsList(shades);
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
updateShadeList() {
|
updateShadeList() {
|
||||||
getJSONSync('/shades', (err, shades) => {
|
getJSONSync('/shades', (err, shades) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
|
@ -3110,6 +3331,30 @@ class Somfy {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
deleteRoom(roomId) {
|
||||||
|
let valid = true;
|
||||||
|
if (isNaN(roomId) || roomId >= 255 || roomId <= 0) {
|
||||||
|
ui.errorMessage('A valid room id was not supplied.');
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
if (valid) {
|
||||||
|
getJSONSync(`/room?roomId=${roomId}`, (err, room) => {
|
||||||
|
if (err) ui.serviceError(err);
|
||||||
|
else {
|
||||||
|
let prompt = ui.promptMessage(`Are you sure you want to delete this room?`, () => {
|
||||||
|
ui.clearErrors();
|
||||||
|
putJSONSync('/deleteRoom', { roomId: roomId }, (err, room) => {
|
||||||
|
prompt.remove();
|
||||||
|
if (err) ui.serviceError(err);
|
||||||
|
else
|
||||||
|
this.updateRoomsList();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
prompt.querySelector('.sub-message').innerHTML = `<p>If this room was previously selected for motors or groups, they will be automatically assigned to the Home room.</p>`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
deleteShade(shadeId) {
|
deleteShade(shadeId) {
|
||||||
let valid = true;
|
let valid = true;
|
||||||
if (isNaN(shadeId) || shadeId >= 255 || shadeId <= 0) {
|
if (isNaN(shadeId) || shadeId >= 255 || shadeId <= 0) {
|
||||||
|
|
@ -3757,6 +4002,16 @@ class Somfy {
|
||||||
somfy.sendTiltCommand(shadeId, el.value);
|
somfy.sendTiltCommand(shadeId, el.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
openSelectRoom() {
|
||||||
|
this.closeShadePositioners();
|
||||||
|
console.log('Opening rooms');
|
||||||
|
let list = document.getElementById('divRoomSelector-list');
|
||||||
|
list.style.display = 'block';
|
||||||
|
document.body.addEventListener('click', () => {
|
||||||
|
list.style.display = '';
|
||||||
|
}, { once: true });
|
||||||
|
|
||||||
|
}
|
||||||
openSetPosition(shadeId) {
|
openSetPosition(shadeId) {
|
||||||
console.log('Opening Shade Positioner');
|
console.log('Opening Shade Positioner');
|
||||||
if (typeof shadeId === 'undefined') {
|
if (typeof shadeId === 'undefined') {
|
||||||
|
|
|
||||||
|
|
@ -556,6 +556,7 @@ div.wait-overlay > .lds-roller {
|
||||||
font-size:1.5em;
|
font-size:1.5em;
|
||||||
color:white;
|
color:white;
|
||||||
}
|
}
|
||||||
|
.room-name,
|
||||||
.group-name,
|
.group-name,
|
||||||
.shade-name {
|
.shade-name {
|
||||||
text-align:left;
|
text-align:left;
|
||||||
|
|
@ -567,6 +568,14 @@ div.wait-overlay > .lds-roller {
|
||||||
overflow:hidden;
|
overflow:hidden;
|
||||||
vertical-align:middle;
|
vertical-align:middle;
|
||||||
}
|
}
|
||||||
|
.cfg-room {
|
||||||
|
font-size:12px;
|
||||||
|
color:darkorange;
|
||||||
|
}
|
||||||
|
.somfyShade[data-roomid="0"] .cfg-room,
|
||||||
|
.somfyGroup[data-roomid="0"] .cfg-room {
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
.group-address,
|
.group-address,
|
||||||
.shade-address {
|
.shade-address {
|
||||||
width:77px;
|
width:77px;
|
||||||
|
|
@ -617,6 +626,8 @@ div.wait-overlay > .lds-roller {
|
||||||
position:relative;
|
position:relative;
|
||||||
display:flex;
|
display:flex;
|
||||||
vertical-align:middle;
|
vertical-align:middle;
|
||||||
|
align-items:center;
|
||||||
|
flex-direction:row;
|
||||||
}
|
}
|
||||||
.somfyShadeCtl .shade-icon {
|
.somfyShadeCtl .shade-icon {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|
@ -641,9 +652,12 @@ div.wait-overlay > .lds-roller {
|
||||||
vertical-align:middle;
|
vertical-align:middle;
|
||||||
width:100%;
|
width:100%;
|
||||||
}
|
}
|
||||||
|
.somfyGroupCtl .groupctl-shades {
|
||||||
|
font-size:12px;
|
||||||
|
}
|
||||||
.somfyGroupCtl .groupctl-name,
|
.somfyGroupCtl .groupctl-name,
|
||||||
.somfyShadeCtl .shadectl-name {
|
.somfyShadeCtl .shadectl-name {
|
||||||
font-size: 1.5em;
|
font-size: 1.37em;
|
||||||
color: silver;
|
color: silver;
|
||||||
display: block;
|
display: block;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
|
@ -654,10 +668,23 @@ div.wait-overlay > .lds-roller {
|
||||||
.somfyShadeCtl label {
|
.somfyShadeCtl label {
|
||||||
color:silver;
|
color:silver;
|
||||||
}
|
}
|
||||||
|
.somfyShadeCtl .shadectl-room,
|
||||||
|
.somfyGroupCtl .groupctl-room {
|
||||||
|
font-size:12px;
|
||||||
|
margin-bottom:-3px;
|
||||||
|
display:block;
|
||||||
|
color:darkorange;
|
||||||
|
}
|
||||||
|
.somfyGroupCtl[data-roomid="0"] .groupctl-room,
|
||||||
|
.somfyShadeCtl[data-roomid="0"] .shadectl-room {
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
.somfyGroupCtl .groupctl-mypos,
|
.somfyGroupCtl .groupctl-mypos,
|
||||||
.somfyShadeCtl .shadectl-mypos {
|
.somfyShadeCtl .shadectl-mypos {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
display:block;
|
||||||
|
margin-top:-3px;
|
||||||
}
|
}
|
||||||
.vr-button > div,
|
.vr-button > div,
|
||||||
.somfyGroupCtl .groupctl-buttons,
|
.somfyGroupCtl .groupctl-buttons,
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@
|
||||||
left: 0px;
|
left: 0px;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
.edit-roomlist,
|
||||||
.edit-grouplist,
|
.edit-grouplist,
|
||||||
.edit-motorlist {
|
.edit-motorlist {
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
|
@ -81,6 +82,7 @@
|
||||||
.wizard[data-stepid="6"] .wizard-step:not([data-stepid="6"]) { display: none; }
|
.wizard[data-stepid="6"] .wizard-step:not([data-stepid="6"]) { display: none; }
|
||||||
.wizard[data-stepid="7"] .wizard-step:not([data-stepid="7"]) { display: none; }
|
.wizard[data-stepid="7"] .wizard-step:not([data-stepid="7"]) { display: none; }
|
||||||
|
|
||||||
|
.somfyRoom,
|
||||||
.somfyGroup,
|
.somfyGroup,
|
||||||
.somfyShade {
|
.somfyShade {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
@ -91,13 +93,15 @@
|
||||||
}
|
}
|
||||||
.linked-shade > div,
|
.linked-shade > div,
|
||||||
.linked-shade > span,
|
.linked-shade > span,
|
||||||
|
.somfyRoom > div,
|
||||||
|
.somfyRoom > span,
|
||||||
.somfyGroup > div,
|
.somfyGroup > div,
|
||||||
.somfyGroup > span,
|
.somfyGroup > span,
|
||||||
.somfyShade > div,
|
.somfyShade > div,
|
||||||
.somfyShade > span {
|
.somfyShade > span {
|
||||||
display: table-cell;
|
display: table-cell;
|
||||||
padding-left:4px;
|
padding-left: 4px;
|
||||||
padding-right:4px;
|
padding-right: 4px;
|
||||||
}
|
}
|
||||||
.linked-shade {
|
.linked-shade {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
@ -185,6 +189,7 @@
|
||||||
.somfyShadeCtl .shadectl-mypos span.my-pos {
|
.somfyShadeCtl .shadectl-mypos span.my-pos {
|
||||||
margin-right:14px;
|
margin-right:14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#somfyShade #divGPIOControl {
|
#somfyShade #divGPIOControl {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
@ -233,20 +238,68 @@
|
||||||
#somfyShade[data-proto="8"][data-shadetype="9"] #divGPIOUp {
|
#somfyShade[data-proto="8"][data-shadetype="9"] #divGPIOUp {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
.room-draggable,
|
||||||
.group-draggable,
|
.group-draggable,
|
||||||
.shade-draggable {
|
.shade-draggable {
|
||||||
height: 32px;
|
height: 32px;
|
||||||
border-top: solid 2px transparent;
|
border-top: solid 2px transparent;
|
||||||
cursor: grab;
|
cursor: grab;
|
||||||
}
|
}
|
||||||
.group-draggable.dragging *,
|
.room-draggable.dragging *,
|
||||||
.group-draggable.over *,
|
.room-draggable.over *,
|
||||||
.shade-draggable.dragging *,
|
.group-draggable.dragging *,
|
||||||
.shade-draggable.over * {
|
.group-draggable.over *,
|
||||||
pointer-events: none;
|
.shade-draggable.dragging *,
|
||||||
}
|
.shade-draggable.over * {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.room-draggable.over,
|
||||||
.group-draggable.over,
|
.group-draggable.over,
|
||||||
.shade-draggable.over {
|
.shade-draggable.over {
|
||||||
border-top: solid 2px var(--shade-color, '#00bcd4');
|
border-top: solid 2px var(--shade-color, '#00bcd4');
|
||||||
}
|
}
|
||||||
|
.room-selector {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
font-size: 30px;
|
||||||
|
margin-top: -10px;
|
||||||
|
color: var(--shade-color, gray);
|
||||||
|
font-weight:bold;
|
||||||
|
text-shadow:1px 1px 1px silver;
|
||||||
|
justify-content:center;
|
||||||
|
flex-wrap:nowrap;
|
||||||
|
flex-direction:row;
|
||||||
|
align-items:center;
|
||||||
|
|
||||||
|
}
|
||||||
|
.room-selector > i {
|
||||||
|
font-size: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.room-selector > span {
|
||||||
|
margin-left: .15em;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.room-selector-list {
|
||||||
|
position: absolute;
|
||||||
|
min-width: 50%;
|
||||||
|
font-size: 1.37rem;
|
||||||
|
border: solid 1px gray;
|
||||||
|
box-shadow: 4px 4px 4px gray;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding-left: 1rem;
|
||||||
|
padding-right:1rem;
|
||||||
|
background:white;
|
||||||
|
z-index:1000;
|
||||||
|
display:none;
|
||||||
|
cursor:pointer;
|
||||||
|
top:3px;
|
||||||
|
}
|
||||||
|
.room-selector-list .room-row {
|
||||||
|
cursor: pointer;
|
||||||
|
color: gray;
|
||||||
|
text-shadow:none;
|
||||||
|
}
|
||||||
|
.room-selector-list .room-row:hover {
|
||||||
|
color: var(--shade-color, gray);
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue