Fork of the espurna firmware for `mhsw` switches
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

198 lines
6.7 KiB

/*
KingArt Cover/Shutter/Blind/Curtain support for ESPURNA
Based on xdrv_19_ps16dz.dimmer.ino, PS_16_DZ dimmer support for Tasmota
Copyright (C) 2019 by Albert Weterings
*/
#include "curtain_kingart.h"
#if KINGART_CURTAIN_SUPPORT
#include "ntp.h"
#include "mqtt.h"
#ifndef KINGART_CURTAIN_PORT
#define KINGART_CURTAIN_PORT Serial // Hardware serial port by default
#endif
#ifndef KINGART_CURTAIN_BUFFER_SIZE
#define KINGART_CURTAIN_BUFFER_SIZE 100 // Local UART buffer size
#endif
#define KINGART_CURTAIN_TERMINATION '\e' // Termination character after each message
#define KINGART_CURTAIN_BAUDRATE 19200 // Serial speed is fixed for the device
char _KACurtainBuffer[KINGART_CURTAIN_BUFFER_SIZE];
bool _KACurtainNewData = false;
// -----------------------------------------------------------------------------
// Private
// -----------------------------------------------------------------------------
void _KACurtainSend(const char * tx_buffer) {
KINGART_CURTAIN_PORT.print(tx_buffer);
KINGART_CURTAIN_PORT.print(KINGART_CURTAIN_TERMINATION);
KINGART_CURTAIN_PORT.flush();
}
void _KACurtainReceiveUART() {
static unsigned char ndx = 0;
while (KINGART_CURTAIN_PORT.available() > 0 && !_KACurtainNewData) {
char rc = KINGART_CURTAIN_PORT.read();
if (rc != KINGART_CURTAIN_TERMINATION) {
_KACurtainBuffer[ndx] = rc;
if (ndx < KINGART_CURTAIN_BUFFER_SIZE - 1) ndx++;
} else {
_KACurtainBuffer[ndx] = '\0';
_KACurtainNewData = true;
ndx = 0;
}
}
}
/*
Buttons on the device will move Cover/Shutter/Blind/Curtain up/open or down/close On the end of
every movement the unit reports the last action and posiston over MQTT topic {hostname}/curtain
RAW paylod format looks like:
AT+UPDATE="switch":"on","setclose":13
AT+UPDATE="switch":"off","setclose":38
AT+UPDATE="switch":"pause","setclose":75
The device is listening to MQTT topic {hostname}/curtain/set, to which you can send:
- position value, in range from 0 to 100
- "pause", to stop the movement.
The device will determine the direction all by itself.
# Set the Cover / Shutter / Blind / Curtain run time
The factory default Open and Close run time for the switch is 50 seconds, and it must be set to
an accurate run time for smooth working. Some motors do not have the resistance stop function,
so when the Cover/Shutter/Blind/Curtain track open or close to the maximum length, but the motor keeps on running.
This might cause damage on the motor and the switch, it also wastes a lot of energy. In order
to protect the motor, this switch designed with a time setting function. After setting up the run time,
the switch will automaticly stop when the track reaches its limits. The run time setting is also helpful
for the accurate control when manually controlling the device via the touch panel.
After installing the switch and connecting the switch for the very first time:
- First, it will automatically close the Cover/Shutter/Blind/Curtain to the maximum.
- Press and hold the touch interface pause button for around 4 seconds until the red background
led lights up and starts blinking. Then, press the open touch button so start the opening process.
- When cover is fully open, press the Open or Close button to stop the timer and save the calculated run time.
To configure the device:
- Press up/down for 5 seconds to bring device into AP mode. After pressing up/down again, device will restart in normal mode.
*/
void _KACurtainResult() {
if (_KACurtainNewData) {
// Need to send confiramtion to the N76E003AT20 that message is received
_KACurtainSend("AT+SEND=ok");
// We don't handle "setclose" any other way, simply redirect payload value
const String buffer(_KACurtainBuffer);
#if MQTT_SUPPORT
int setclose_idx = buffer.indexOf("setclose");
if (setclose_idx > 0) {
auto position = buffer.substring(setclose_idx + strlen("setclose") + 2, buffer.length());
int leftovers = position.indexOf(',');
if (leftovers > 0) {
position = position.substring(0, leftovers);
}
mqttSend(MQTT_TOPIC_CURTAIN, position.c_str());
}
#endif // MQTT_SUPPORT
// Handle configuration button presses
if (buffer.indexOf("enterESPTOUCH") > 0) {
wifiStartAP();
} else if (buffer.indexOf("exitESPTOUCH") > 0) {
deferredReset(100, CUSTOM_RESET_HARDWARE);
}
_KACurtainNewData = false;
}
}
// %d = now() / time_t / NTP timestamp in seconds
// %03u = millis() / uint32_t / we need last 3 digits
// %s = char strings for various actions
// Tell N76E003AT20 to stop moving and report current position
void _KACurtainPause(const char * message) {
char tx_buffer[80] = {0};
snprintf_P(
tx_buffer, sizeof(tx_buffer),
PSTR("AT+UPDATE=\"sequence\":\"%d%03u\",\"switch\":\"%s\""),
now(), millis() % 1000,
message
);
_KACurtainSend(tx_buffer);
}
// Tell N76E003AT20 to go to position X (based on X N76E003AT20 decides to go up or down)
void _KACurtainSetclose(const char * message) {
char tx_buffer[80] = {0};
snprintf_P(
tx_buffer, sizeof(tx_buffer),
PSTR("AT+UPDATE=\"sequence\":\"%d%03u\",\"switch\":\"%s\",\"setclose\":%s"),
now(), millis() % 1000,
"off", message
);
_KACurtainSend(tx_buffer);
}
#if MQTT_SUPPORT
void _KACurtainActionSelect(const char * message) {
if (strcmp(message, "pause") == 0) {
_KACurtainPause(message);
} else {
_KACurtainSetclose(message);
}
}
void _KACurtainCallback(unsigned int type, const char * topic, char * payload) {
if (type == MQTT_CONNECT_EVENT) {
mqttSubscribe(MQTT_TOPIC_CURTAIN);
}
if (type == MQTT_MESSAGE_EVENT) {
// Match topic
const String t = mqttMagnitude(const_cast<char*>(topic));
if (t.equals(MQTT_TOPIC_CURTAIN)) {
_KACurtainActionSelect(payload);
}
}
}
#endif // MQTT_SUPPORT
// -----------------------------------------------------------------------------
// SETUP & LOOP
// -----------------------------------------------------------------------------
void _KACurtainLoop() {
_KACurtainReceiveUART();
_KACurtainResult();
}
void kingartCurtainSetup() {
// Init port to receive and send messages
KINGART_CURTAIN_PORT.begin(KINGART_CURTAIN_BAUDRATE);
// Register MQTT callback only when supported
#if MQTT_SUPPORT
mqttRegister(_KACurtainCallback);
#endif // MQTT_SUPPORT
// Register loop to poll the UART for new messages
espurnaRegisterLoop(_KACurtainLoop);
}
#endif // KINGART_CURTAIN_SUPPORT