Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shutter changes #18

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion include/RF/device.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ enum DeviceMode {
SHUTTER // Shutter style devices (MVR500)
};

enum DeviceStatus { OFF = 0, ON, UNDEFINED, PAUSE_SHUTTER };
enum DeviceStatus { OFF = 0, ON, UNDEFINED, SHUTTER_OPENING, SHUTTER_CLOSING, SHUTTER_STOPPED, SHUTTER_OPENED, SHUTTER_CLOSED};

enum DeviceAvailability { OFFLINE = 0, ONLINE };

Expand Down
4 changes: 3 additions & 1 deletion include/RF/e2bp.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ enum PayloadType {
PL_ON,
PL_OFF,
PL_DIM,
PL_SHUTTERPAUSE
PL_SHUTTERPAUSE,
PL_SHUTTEROPENING,
PL_SHUTTERCLOSING
};

// Very low level class to control a Yokis device just like a e2bp remote
Expand Down
2 changes: 1 addition & 1 deletion include/globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ extern "C" {
#define YOKIS_CMD_STATUS 0x00
#define YOKIS_CMD_ON 0xb9
#define YOKIS_CMD_OFF 0x1a
#define YOKIS_CMD_OFF_SHUTTER 0xfa
#define YOKIS_CMD_SHUTTER_OFF 0xfa
#define YOKIS_CMD_SHUTTER_PAUSE 0x1a

#define DEVICE_MAX_FAILED_POLLING_BEFORE_OFFLINE 3
Expand Down
2 changes: 1 addition & 1 deletion platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ default_envs = megaatmega2560, d1_mini, d1_mini_ota
[env]
framework = arduino
lib_deps =
RF24
nrf24/RF24@1.3.11

[env:megaatmega2560]
platform = atmelavr
Expand Down
16 changes: 12 additions & 4 deletions src/RF/device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,16 @@ const char* Device::getStatusAsString(DeviceStatus status) {
return "OFF";
case UNDEFINED:
return "UNDEFINED";
case PAUSE_SHUTTER:
return "PAUSE_SHUTTER";
case SHUTTER_STOPPED:
return "stopped";
case SHUTTER_OPENING:
return "opening";
case SHUTTER_CLOSING:
return "closing";
case SHUTTER_OPENED:
return "open";
case SHUTTER_CLOSED:
return "closed";
}

return NULL;
Expand Down Expand Up @@ -218,8 +226,8 @@ void Device::toggleStatus() {
case UNDEFINED:
setStatus(UNDEFINED);
break;
case PAUSE_SHUTTER:
setStatus(PAUSE_SHUTTER);
case SHUTTER_STOPPED:
setStatus(SHUTTER_STOPPED);
break;
case OFF:
setStatus(ON);
Expand Down
97 changes: 78 additions & 19 deletions src/RF/e2bp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,14 @@ bool E2bp::setDeviceStatus(DeviceStatus ds) {
reset();
setupRFModule();
// For all devices, use the on or off payload except for shutters
if(device->getMode() == SHUTTER && ds == PAUSE_SHUTTER) {
getPayload(buf, PL_SHUTTERPAUSE);
if(device->getMode() == SHUTTER ) {
if (ds == SHUTTER_STOPPED) {
getPayload(buf, PL_SHUTTERPAUSE);
} else if (ds == SHUTTER_OPENING) {
getPayload(buf, PL_SHUTTEROPENING);
} else if (ds == SHUTTER_CLOSING) {
getPayload(buf, PL_SHUTTERCLOSING);
}
} else {
getPayload(buf, ds == ON ? PL_ON : PL_OFF);
}
Expand All @@ -66,11 +72,23 @@ bool E2bp::setDeviceStatus(DeviceStatus ds) {
return ret;
}

bool E2bp::on() { return setDeviceStatus(ON); }
bool E2bp::on() {
if(device->getMode() == SHUTTER ) {
return setDeviceStatus(SHUTTER_OPENING);
} else {
return setDeviceStatus(ON);
}
}

bool E2bp::off() { return setDeviceStatus(OFF); }
bool E2bp::off() {
if(device->getMode() == SHUTTER ) {
return setDeviceStatus(SHUTTER_CLOSING);
} else {
return setDeviceStatus(OFF);
}
}

bool E2bp::pauseShutter() { return setDeviceStatus(PAUSE_SHUTTER); }
bool E2bp::pauseShutter() { return setDeviceStatus(SHUTTER_STOPPED); }

// Toggle a device
// This function emulates a normal operated e2bp.
Expand Down Expand Up @@ -183,6 +201,7 @@ DeviceStatus E2bp::pollForStatus() {
}

// Get device mode from previous received data from the device
// This way of detecting devices is not working. Shutter devices may return various values.
DeviceMode E2bp::getDeviceModeFromRecvData() {
switch (answerBuf[0]) {
case 0:
Expand Down Expand Up @@ -269,11 +288,9 @@ bool E2bp::pressAndHoldFor(unsigned long duration) {
// Fill a given buffer with the correct payload and return a pointer to it
uint8_t* E2bp::getPayload(uint8_t* buf, PayloadType type) {
buf[0] = 0x00;
buf[1] = device->getMode() == SHUTTER ? 0x16 : 0x04;
buf[1] = device->getMode() == SHUTTER ? 0x06 : 0x04; // 0x16 = Drive all shutters connected to the bus.
buf[2] = 0x00;
buf[3] = 0x20;
buf[4] = device->getHardwareAddress()[0];
buf[5] = device->getHardwareAddress()[1];
buf[3] = 0x00; // Initial project was using 0x20. Might be used to drive specific device over the bus, using another device as gateway?
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will test this on Yokis lights to see if this is working as expected.

Copy link
Author

@lecocotier lecocotier Jun 30, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Putting back my original comment:

My devices are connected together in a bus.
I figured out that if I send:
B9 06 00 00 00 00 ?? 00 00 (for example) one is moving
B9 16 00 00 00 00 ?? 00 00 (for example) all are moving
I always address the device I want to move because of the implementation of the commands.

Now from our discussion I'm thinking that maybe if I address another device (gateway), but put the address of the device I want to drive in the command, such as:
B9 06 00 00 (address of device) ?? 00 00 sent to the gateway
or maybe:
B9 06 00 20 (address of device) ?? 00 sent to the gateway
The other device may move? Gateway device propagate the command through the bus?
That could be interesting because it allows the mqtt gateway to be far away of some devices.

I didn't tested it. I saw this 0x20 on your firmware but I guess we have different setup.

I'm pretty sure it's possible to test it with other devices, using simple commands such as ON, and
B9 04 or B9 14 to address all.

If it's working as I expect it's a bigger change because the mqtt gateway could try to address any device it knows that is flagged as "part of bus" to reach transparently another one.

I don't think I can configure my Yokis remote to address a specific device through the bus. Maybe with another "intelligent" Yokis remote.

buf[4] = 0x00;
buf[5] = 0x00;
buf[6] = random(0, 0xff);
Expand All @@ -291,7 +308,7 @@ uint8_t* E2bp::getPayload(uint8_t* buf, PayloadType type) {
buf[0] = YOKIS_CMD_ON;
break;
case PL_OFF:
buf[0] = device->getMode() == SHUTTER ? YOKIS_CMD_OFF_SHUTTER : YOKIS_CMD_OFF;
buf[0] = device->getMode() == SHUTTER ? YOKIS_CMD_SHUTTER_OFF : YOKIS_CMD_OFF;
break;
case PL_STATUS:
buf[0] = 0;
Expand All @@ -304,6 +321,12 @@ uint8_t* E2bp::getPayload(uint8_t* buf, PayloadType type) {
case PL_SHUTTERPAUSE:
buf[0] = YOKIS_CMD_SHUTTER_PAUSE;
break;
case PL_SHUTTEROPENING:
buf[0] = YOKIS_CMD_ON;
break;
case PL_SHUTTERCLOSING:
buf[0] = YOKIS_CMD_SHUTTER_OFF;
break;
}

return buf;
Expand Down Expand Up @@ -393,26 +416,62 @@ bool E2bp::runMainLoop() {
if (available()) {
read(answerBuf, 2);

if (IS_DEBUG_ENABLED) {
// if (IS_DEBUG_ENABLED) {
LOG.print("Received: ");
LOG.print(device->getName());
LOG.print(" (");
LOG.print(device->getModeAsString(device->getMode()));
LOG.print(") ");
printBinaryRepresentation(answerBuf[0], true);
LOG.print(" ");
printBinaryRepresentation(answerBuf[1], true);
LOG.println();
}
LOG.print(" - ");
// }

// MVR500 window open and delay ok 00 10 11 11 - 00 00 00 10 10 = delay ok
// MVR500 window close and delay ok 00 01 11 10 - 00 00 00 10 10 = delay ok
// MVR500 window open and pause 00 00 11 11 - 00 00 00 10
// MVR500 send open or close the answerbuf take ******* - 00 00 00 11 busy
// MVR500 after pair send status when window open 00 00 11 10 - 00 00 00 10

if (firstPayloadStatus == UNDEFINED) {
firstPayloadStatus =
(answerBuf[1] == 1 || answerBuf[0] == 0x2f) ? ON : OFF;
// Seems there is various way of devices answering different ways. Trying to cover all.
// Devices answering 00 00 gives the same answer when closed or paused.
// In such case home assistant will block the close command
// Testing if a pause command was issued could be a possibility.
if ( device->getMode() == SHUTTER ) {
if ( ((answerBuf[0] & 0x21) == 0x21 && answerBuf[1] == 0x02) || (answerBuf[0] == 0x01 && answerBuf[1] == 0x00) ) {
firstPayloadStatus = SHUTTER_OPENED;
LOG.println("Opened");
} else if ( ((answerBuf[0] & 0x11) == 0x10 && answerBuf[1] == 0x02) || (answerBuf[0] == 0x00 && answerBuf[1] == 0x00) ) {
firstPayloadStatus = SHUTTER_CLOSED;
LOG.println("Closed");
} else if ( ((answerBuf[0] & 0x01) == 0x01 && answerBuf[1] == 0x03) || (answerBuf[0] == 0x01 && answerBuf[1] == 0x01) ) {
firstPayloadStatus = SHUTTER_OPENING;
LOG.println("Opening");
} else if (((answerBuf[0] & 0x01) == 0x00 && answerBuf[1] == 0x03) || (answerBuf[0] == 0x00 && answerBuf[1] == 0x01) ) {
firstPayloadStatus = SHUTTER_CLOSING;
LOG.println("Closing");
} else if (answerBuf[1] == 0x02) { // To be tested last
firstPayloadStatus = SHUTTER_STOPPED;
LOG.println("Stopped");
}
} else {
secondPayloadStatus =
(answerBuf[1] == 1 || answerBuf[0] == 0x2f) ? ON : OFF;
if (firstPayloadStatus == UNDEFINED) {
if (answerBuf[1] == 1 || answerBuf[0] == 0x2f) {
firstPayloadStatus = ON;
LOG.println("On");
} else {
firstPayloadStatus = OFF;
LOG.println("Off");
}
} else {
if (answerBuf[1] == 1 || answerBuf[0] == 0x2f) {
firstPayloadStatus = ON;
LOG.println("On");
} else {
firstPayloadStatus = OFF;
LOG.println("Off");
}
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ void pollForStatus(Device* d) {
IrqManager::irqType = E2BP;
g_bp->setDevice(d);
DeviceStatus ds = g_bp->pollForStatus();

if (ds != UNDEFINED) { // device reachable
if (d->getFailedPollings() > 0) {
LOG.print("Device ");
Expand Down
39 changes: 37 additions & 2 deletions src/net/mqttHass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,35 @@ char* MqttHass::newMessageJson(const Device* device, char* buf) {
"}",
device->getName(), device->getName(), device->getName(),
device->getName(), device->getName());
} else {
} else if (device->getMode() == SHUTTER) {
sprintf(buf,
"{"
"\"name\":\"Shutter %s\","
"\"optimistic\":\"false\"," // if false, cannot know the status
// of the device
"\"cmd_t\":\"~cmnd/POWER\","
"\"state_topic\":\"~tele/STATE\","
"\"val_tpl\":\"{{value_json.POWER}}\","
// "\"tilt_status_topic\": \"~tele/TILT\","
// "\"tilt_status_template\":\"{{value_json.TILT}}\","
"\"avty_t\":\"~tele/LWT\","
"\"pl_avail\":\"Online\","
"\"pl_not_avail\":\"Offline\","
"\"payload_close\":\"OFF\","
"\"payload_open\":\"ON\","
"\"payload_stop\":\"PAUSE\","
"\"uniq_id\":\"esp-%s\","
"\"device\":{"
"\"name\":\"%s\","
"\"identifiers\":[\"esp-%s\"],"
"\"model\":\"MVR500ERP \","
"\"mf\":\"Yokis\""
"},"
"\"~\":\"%s/\""
"}",
device->getName(), device->getName(), device->getName(),
device->getName(), device->getName());
} else {
sprintf(buf,
"{"
"\"name\":\"%s switch\","
Expand Down Expand Up @@ -88,7 +116,11 @@ char* MqttHass::newMessageJson(const Device* device, char* buf) {
}

char* MqttHass::newPublishTopic(const Device* device, char* buf) {
sprintf(buf, "%s/light/%s/config", HASS_PREFIX, device->getName());
if (device->getMode() == SHUTTER) {
sprintf(buf, "%s/cover/%s/config", HASS_PREFIX, device->getName());
} else {
sprintf(buf, "%s/light/%s/config", HASS_PREFIX, device->getName());
}
return buf;
}

Expand Down Expand Up @@ -169,6 +201,9 @@ void MqttHass::subscribeDevice(const Device* device) {
this->subscribe(buf);
sprintf(buf, "%s/cmnd/BRIGHTNESS", device->getName());
this->subscribe(buf);
} else if (device->getMode() == SHUTTER) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, this was missing.
Nevertheless, this could be grouped with the first if as the code is identical.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree so far, maybe in future I can implement the recall saved position here.

sprintf(buf, "%s/cmnd/POWER", device->getName());
this->subscribe(buf);
}
}
#endif