From ff2718e8ca958568b51b98174703b2c9e6636865 Mon Sep 17 00:00:00 2001 From: Albert Weterings <36169962+AlbertWeterings@users.noreply.github.com> Date: Fri, 1 May 2020 11:45:41 +0200 Subject: [PATCH] KingArt WiFi Curtain Switch (#2063) This will add support for the KingArt Wifi Curtain Switch. There are no buttons in the web interface as I could not figure out how to create them (any help on that is welcome). For now the switch can be controlled over MQTT: "{hostname}/curtain/set" "{hostname}/curtain" --- code/espurna/config/arduino.h | 3 +- code/espurna/config/general.h | 1 + code/espurna/config/hardware.h | 18 ++- code/espurna/curtain_kingart.cpp | 198 +++++++++++++++++++++++++++++++ code/espurna/curtain_kingart.h | 12 ++ code/espurna/main.cpp | 4 + code/platformio.ini | 4 + code/test/build/nondefault.h | 15 +-- 8 files changed, 246 insertions(+), 9 deletions(-) create mode 100644 code/espurna/curtain_kingart.cpp create mode 100644 code/espurna/curtain_kingart.h diff --git a/code/espurna/config/arduino.h b/code/espurna/config/arduino.h index a496827d..ab219742 100644 --- a/code/espurna/config/arduino.h +++ b/code/espurna/config/arduino.h @@ -105,6 +105,7 @@ //#define JANGOE_WIFI_RELAY_NO //#define JINVOO_VALVE_SM_AW713 //#define JORGEGARCIA_WIFI_RELAYS +//#define KINGART_CURTAIN_SWITCH //#define KMC_70011 //#define KOGAN_SMARTER_HOME_PLUG_W_POW //#define LINGAN_SWA1 @@ -126,8 +127,8 @@ //#define MAGICHOME_ZJ_WFMN_C_11 //#define MANCAVEMADE_ESPLIVE //#define MAXCIO_WDE004 -//#define MAXCIO_WUS002S //#define MAXCIO_WUK007S +//#define MAXCIO_WUS002S //#define MUVIT_IO_MIOBULB001 //#define NEO_COOLCAM_NAS_WR01W //#define NEXETE_A19 diff --git a/code/espurna/config/general.h b/code/espurna/config/general.h index 038c91eb..306a0abd 100644 --- a/code/espurna/config/general.h +++ b/code/espurna/config/general.h @@ -1162,6 +1162,7 @@ #define MQTT_TOPIC_IROUT "irout" #define MQTT_TOPIC_OTA "ota" #define MQTT_TOPIC_TELNET_REVERSE "telnet_reverse" +#define MQTT_TOPIC_CURTAIN "curtain" // Light module #define MQTT_TOPIC_CHANNEL "channel" diff --git a/code/espurna/config/hardware.h b/code/espurna/config/hardware.h index 4e1b9f9b..343fbbf6 100644 --- a/code/espurna/config/hardware.h +++ b/code/espurna/config/hardware.h @@ -4550,6 +4550,23 @@ #define SENSOR_ENERGY_UNITS ENERGY_KWH #define SENSOR_POWER_UNITS POWER_WATTS +// ----------------------------------------------------------------------------- +// KINGART_CURTAIN_SWITCH +// ----------------------------------------------------------------------------- +#elif defined(KINGART_CURTAIN_SWITCH) + + // Info + #define MANUFACTURER "KINGART" + #define DEVICE "CURTAIN_SWITCH" + + // LEDs + #define LED1_PIN 13 + #define LED1_PIN_INVERSE 1 + + // KINGART module handles the UART, can't print any debug messages + #define KINGART_CURTAIN_SUPPORT 1 + #define DEBUG_SERIAL_SUPPORT 0 + // ----------------------------------------------------------------------------- // LSC Smart LED Light Strip (Smart CXonnect Series) available ACTION (Germany) // https://www.action.com/de-de/p/lsc-smart-connect-intelligenter-multicolor-led-strip-/ @@ -4641,4 +4658,3 @@ #error "UNSUPPORTED HARDWARE!!" #endif - diff --git a/code/espurna/curtain_kingart.cpp b/code/espurna/curtain_kingart.cpp new file mode 100644 index 00000000..d46398e1 --- /dev/null +++ b/code/espurna/curtain_kingart.cpp @@ -0,0 +1,198 @@ +/* + +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(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 diff --git a/code/espurna/curtain_kingart.h b/code/espurna/curtain_kingart.h new file mode 100644 index 00000000..afb5b6d6 --- /dev/null +++ b/code/espurna/curtain_kingart.h @@ -0,0 +1,12 @@ +/* + +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 "espurna.h" + +void kingartCurtainSetup(); diff --git a/code/espurna/main.cpp b/code/espurna/main.cpp index 20919f32..f8acf103 100644 --- a/code/espurna/main.cpp +++ b/code/espurna/main.cpp @@ -26,6 +26,7 @@ along with this program. If not, see . #include "broker.h" #include "button.h" #include "crash.h" +#include "curtain_kingart.h" #include "debug.h" #include "domoticz.h" #include "encoder.h" @@ -279,6 +280,9 @@ void setup() { #if TUYA_SUPPORT Tuya::tuyaSetup(); #endif + #if KINGART_CURTAIN_SUPPORT + kingartCurtainSetup(); + #endif // 3rd party code hook #if USE_EXTRA diff --git a/code/platformio.ini b/code/platformio.ini index 5554af3a..e19a5bea 100644 --- a/code/platformio.ini +++ b/code/platformio.ini @@ -999,6 +999,10 @@ src_build_flags = -DEHOMEDIY_WT02 extends = env:esp8266-1m-base src_build_flags = -DEHOMEDIY_WT03 +[env:kingart-curtain-switch] +extends = env:esp8266-1m-base +src_build_flags = -DKINGART_CURTAIN_SWITCH + [env:aoycocr-x5p] extends = env:esp8266-1m-base src_build_flags = -DAOYCOCR_X5P diff --git a/code/test/build/nondefault.h b/code/test/build/nondefault.h index bda85ecc..2cdaeb0d 100644 --- a/code/test/build/nondefault.h +++ b/code/test/build/nondefault.h @@ -1,12 +1,13 @@ +#define INFLUXDB_SUPPORT 1 +#define KINGART_CURTAIN_SUPPORT 1 #define LLMNR_SUPPORT 1 -#define NETBIOS_SUPPORT 1 -#define SSDP_SUPPORT 1 -#define RF_SUPPORT 1 -#define RFB_DIRECT 1 #define MDNS_CLIENT_SUPPORT 1 +#define NETBIOS_SUPPORT 1 #define NOFUSS_SUPPORT 1 -#define UART_MQTT_SUPPORT 1 -#define INFLUXDB_SUPPORT 1 #define OTA_MQTT_SUPPORT 1 -#define RPN_RULES_SUPPORT 1 +#define RFB_DIRECT 1 #define RFM69_SUPPORT 1 +#define RF_SUPPORT 1 +#define RPN_RULES_SUPPORT 1 +#define SSDP_SUPPORT 1 +#define UART_MQTT_SUPPORT 1