Tasmota/tasmota/xdrv_27_shutter.ino

1799 lines
88 KiB
Arduino
Raw Normal View History

/*
2020-09-09 13:04:57 +01:00
xdrv_27_Shutter[i].ino - Shutter/Blind support for Tasmota
2022-02-04 08:34:19 +00:00
Copyright (C) 2022 Stefan Bode
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef USE_SHUTTER
/*********************************************************************************************\
* Shutter or Blind support using two consecutive relays
\*********************************************************************************************/
#define XDRV_27 27
2020-09-05 19:39:24 +01:00
#ifndef SHUTTER_STEPPER
#define SHUTTER_STEPPER
#endif
#ifndef SHUTTER_RELAY_OPERATION_TIME
2022-02-04 08:13:02 +00:00
#define SHUTTER_RELAY_OPERATION_TIME 100 // wait for direction relay 0.1sec before power up main relay
#endif
#ifndef MOTOR_STOP_TIME
2022-02-04 08:13:02 +00:00
#define MOTOR_STOP_TIME 500 // wait 0.5 second after stop to do any other action. e.g. move in the opposite direction
#endif
2021-02-18 11:42:59 +00:00
//#define SHUTTER_UNITTEST
#define D_SHUTTER "SHUTTER"
const uint16_t RESOLUTION = 1000; // incresed to 1000 in 8.5 to ramp servos
2020-09-09 13:04:57 +01:00
const uint8_t STEPS_PER_SECOND = 20; // FUNC_EVERY_50_MSECOND
const uint16_t pwm_servo_max = 500;
const uint16_t pwm_servo_min = 90;
uint8_t calibrate_pos[6] = {0,30,50,70,90,100};
uint16_t messwerte[5] = {30,50,70,90,100};
int32_t velocity_max = 0;
int32_t velocity_change_per_step_max = 0;
int32_t min_runtime_ms = 0;
int32_t current_stop_way = 0;
int32_t next_possible_stop_position = 0;
int32_t current_real_position = 0;
int32_t current_pwm_velocity = 0;
2021-11-16 12:46:22 +00:00
int8_t savedata_original = 0;
2020-09-05 19:39:24 +01:00
const uint8_t MAX_MODES = 7;
enum Shutterposition_mode {SHT_UNDEF, SHT_TIME, SHT_TIME_UP_DOWN, SHT_TIME_GARAGE, SHT_COUNTER, SHT_PWM_VALUE, SHT_PWM_TIME,};
enum Shutterswitch_mode {SHT_SWITCH, SHT_PULSE,};
enum ShutterButtonStates { SHT_NOT_PRESSED, SHT_PRESSED_MULTI, SHT_PRESSED_HOLD, SHT_PRESSED_IMMEDIATE, SHT_PRESSED_EXT_HOLD, SHT_PRESSED_MULTI_SIMULTANEOUS, SHT_PRESSED_HOLD_SIMULTANEOUS, SHT_PRESSED_EXT_HOLD_SIMULTANEOUS,};
const char kShutterCommands[] PROGMEM = D_PRFX_SHUTTER "|"
D_CMND_SHUTTER_OPEN "|" D_CMND_SHUTTER_CLOSE "|" D_CMND_SHUTTER_TOGGLE "|" D_CMND_SHUTTER_TOGGLEDIR "|" D_CMND_SHUTTER_STOP "|" D_CMND_SHUTTER_POSITION "|"
D_CMND_SHUTTER_OPENTIME "|" D_CMND_SHUTTER_CLOSETIME "|" D_CMND_SHUTTER_RELAY "|" D_CMND_SHUTTER_MODE "|" D_CMND_SHUTTER_PWMRANGE "|"
2020-04-21 15:20:20 +01:00
D_CMND_SHUTTER_SETHALFWAY "|" D_CMND_SHUTTER_SETCLOSE "|" D_CMND_SHUTTER_SETOPEN "|" D_CMND_SHUTTER_INVERT "|" D_CMND_SHUTTER_CLIBRATION "|"
D_CMND_SHUTTER_MOTORDELAY "|" D_CMND_SHUTTER_FREQUENCY "|" D_CMND_SHUTTER_BUTTON "|" D_CMND_SHUTTER_LOCK "|" D_CMND_SHUTTER_ENABLEENDSTOPTIME "|" D_CMND_SHUTTER_INVERTWEBBUTTONS "|"
2021-02-18 11:42:59 +00:00
D_CMND_SHUTTER_STOPOPEN "|" D_CMND_SHUTTER_STOPCLOSE "|" D_CMND_SHUTTER_STOPTOGGLE "|" D_CMND_SHUTTER_STOPTOGGLEDIR "|" D_CMND_SHUTTER_STOPPOSITION "|" D_CMND_SHUTTER_INCDEC "|"
2021-11-16 12:02:36 +00:00
D_CMND_SHUTTER_UNITTEST "|" D_CMND_SHUTTER_TILTCONFIG "|" D_CMND_SHUTTER_SETTILT "|" D_CMND_SHUTTER_TILTINCDEC "|";
void (* const ShutterCommand[])(void) PROGMEM = {
&CmndShutterOpen, &CmndShutterClose, &CmndShutterToggle, &CmndShutterToggleDir, &CmndShutterStop, &CmndShutterPosition,
&CmndShutterOpenTime, &CmndShutterCloseTime, &CmndShutterRelay, &CmndShutterMode, &CmndShutterPwmRange,
2020-04-21 15:20:20 +01:00
&CmndShutterSetHalfway, &CmndShutterSetClose, &CmndShutterSetOpen, &CmndShutterInvert, &CmndShutterCalibration , &CmndShutterMotorDelay,
&CmndShutterFrequency, &CmndShutterButton, &CmndShutterLock, &CmndShutterEnableEndStopTime, &CmndShutterInvertWebButtons,
2021-02-18 11:42:59 +00:00
&CmndShutterStopOpen, &CmndShutterStopClose, &CmndShutterStopToggle, &CmndShutterStopToggleDir, &CmndShutterStopPosition, &CmndShutterIncDec,
2021-11-16 12:02:36 +00:00
&CmndShutterUnitTest,&CmndShutterTiltConfig,&CmndShutterSetTilt,&CmndShutterTiltIncDec};
2021-11-07 14:53:12 +00:00
const char JSON_SHUTTER_POS[] PROGMEM = "\"" D_PRFX_SHUTTER "%d\":{\"Position\":%d,\"Direction\":%d,\"Target\":%d,\"Tilt\":%d}";
2020-01-22 12:23:59 +00:00
const char JSON_SHUTTER_BUTTON[] PROGMEM = "\"" D_PRFX_SHUTTER "%d\":{\"Button%d\":%d}";
#include <Ticker.h>
Ticker TickerShutter;
struct SHUTTER {
2020-09-09 13:04:57 +01:00
uint32_t time; // operating time of the shutter in 0.05sec
int32_t open_max; // max value on maximum open calculated
int32_t target_position; // position to go to
int32_t start_position; // position before a movement is started. init at start
int32_t real_position; // value between 0 and Shutter[i].open_max
uint16_t open_time; // duration to open the Shutter[i]. 112 = 11.2sec
uint16_t close_time; // duration to close the Shutter[i]. 112 = 11.2sec
uint16_t close_velocity; // in relation to open velocity. higher value = faster
int8_t direction; // 1 == UP , 0 == stop; -1 == down
int8_t lastdirection; // last direction (1 == UP , -1 == down)
uint8_t switch_mode; // how to switch relays: SHT_SWITCH, SHT_PULSE
int8_t motordelay; // initial motorstarttime in 0.05sec. Also uses for ramp at steppers and servos, negative if motor stops late
2020-09-09 13:04:57 +01:00
int16_t pwm_velocity; // frequency of PWN for stepper motors or PWM duty cycle change for PWM servo
uint16_t pwm_value; // dutyload of PWM 0..1023 on ESP8266
uint16_t close_velocity_max; // maximum of PWM change during closeing. Defines velocity on opening. Steppers and Servos only
int32_t accelerator; // speed of ramp-up, ramp down of shutters with velocity control. Steppers and Servos only
2021-11-07 14:53:12 +00:00
int8_t tilt_config[5]; // tilt_min, tilt_max, duration, tilt_closed_value, tilt_opened_value
int8_t tilt_real_pos; // -90 to 90
int8_t tilt_target_pos; // target positon for movements of the tilt
int8_t tilt_start_pos; // saved start position before shutter moves
2021-11-07 14:53:12 +00:00
uint8_t tilt_velocity; // degree rotation per step 0.05sec
int8_t tiltmoving; // 0 operating move, 1 = operating tilt
uint16_t venetian_delay = 0; // Delay in steps before venetian shutter start physical moving. Based on tilt position
uint16_t min_realPositionChange = 0; // minimum change of the position before the shutter operates. different for PWM and time based operations
uint16_t min_TiltChange = 0; // minimum change of the tilt before the shutter operates. different for PWM and time based operations
2021-11-16 12:02:36 +00:00
uint16_t last_reported_time =0;
2020-09-09 13:04:57 +01:00
} Shutter[MAX_SHUTTERS];
struct SHUTTERGLOBAL {
power_t RelayShutterMask = 0; // bit mask with 11 at the position of relays that belong to at least ONE shutter
power_t RelayOldMask = 0; // bitmatrix that contain the last known state of all relays. Required to detemine the manual changed relay.
power_t RelayCurrentMask = 0; // bitmatrix that contain the current state of all relays
uint8_t position_mode = 0; // how to calculate actual position: SHT_TIME, SHT_COUNTER, SHT_PWM_VALUE, SHT_PWM_TIME
uint8_t skip_relay_change; // avoid overrun at endstops
uint8_t start_reported = 0; // indicates of the shutter start was reported through MQTT JSON
uint16_t open_velocity_max = RESOLUTION; // maximum of PWM change during opening. Defines velocity on opening. Steppers and Servos only
2020-09-09 13:04:57 +01:00
} ShutterGlobal;
2020-09-05 19:39:24 +01:00
#define SHT_DIV_ROUND(__A, __B) (((__A) + (__B)/2) / (__B))
2020-01-04 14:09:57 +00:00
void ShutterLogPos(uint32_t i)
{
char stemp2[10];
2020-09-09 13:04:57 +01:00
dtostrfd((float)Shutter[i].time / STEPS_PER_SECOND, 2, stemp2);
2021-11-07 14:53:12 +00:00
AddLog(LOG_LEVEL_DEBUG, PSTR("SHT: Shtr%d Real %d, Start %d, Stop %d, Dir %d, Delay %d, Rtc %s [s], Freq %d, PWM %d, Tilt %d"),
i+1, Shutter[i].real_position, Shutter[i].start_position, Shutter[i].target_position, Shutter[i].direction, Shutter[i].motordelay, stemp2,
Shutter[i].pwm_velocity, Shutter[i].pwm_value,Shutter[i].tilt_real_pos);
2020-09-05 19:39:24 +01:00
}
void ExecuteCommandPowerShutter(uint32_t device, uint32_t state, uint32_t source)
{
// first implementation for virtual relays. Avoid switching relay numbers that do not exist.
2020-10-30 11:29:48 +00:00
if (device <= TasmotaGlobal.devices_present) ExecuteCommandPower(device,state,source);
2020-09-05 19:39:24 +01:00
}
void ShutterUpdateVelocity(uint8_t i)
{
// No Logging allowed. Part of RTC Timer
// will be calles through RTC every 50ms.
// do not allow accellerator to stop movement
Shutter[i].pwm_velocity = tmax(velocity_change_per_step_max, Shutter[i].pwm_velocity+Shutter[i].accelerator);
Shutter[i].pwm_velocity = tmin(Shutter[i].direction==1 ? ShutterGlobal.open_velocity_max : Shutter[i].close_velocity_max,Shutter[i].pwm_velocity);
// respect hard coded SDK limit of PWM_MIN on PWM frequency.
2021-02-18 11:42:59 +00:00
if (ShutterGlobal.position_mode == SHT_COUNTER) {
Shutter[i].pwm_velocity = tmax(PWM_MIN,Shutter[i].pwm_velocity);
2021-02-18 11:42:59 +00:00
}
2020-01-04 14:09:57 +00:00
}
void ShutterRtc50mS(void)
{
// No Logging allowed. RTC Timer
2020-10-30 11:29:48 +00:00
for (uint8_t i = 0; i < TasmotaGlobal.shutters_present; i++) {
2020-09-09 13:04:57 +01:00
if (Shutter[i].direction) {
// update position data before increasing counter
2020-09-09 13:04:57 +01:00
Shutter[i].real_position = ShutterCalculatePosition(i);
Shutter[i].time++;
ShutterCalculateAccelerator(i);
2020-09-09 13:04:57 +01:00
switch (ShutterGlobal.position_mode) {
case SHT_PWM_VALUE:
2020-09-05 19:39:24 +01:00
ShutterUpdateVelocity(i);
2020-09-09 13:04:57 +01:00
Shutter[i].real_position += Shutter[i].direction > 0 ? Shutter[i].pwm_velocity : (Shutter[i].direction < 0 ? -Shutter[i].pwm_velocity : 0);
2021-06-11 17:14:12 +01:00
Shutter[i].pwm_value = SHT_DIV_ROUND((Settings->shutter_pwmrange[1][i]-Settings->shutter_pwmrange[0][i]) * Shutter[i].real_position , Shutter[i].open_max)+Settings->shutter_pwmrange[0][i];
2020-09-09 13:04:57 +01:00
analogWrite(Pin(GPIO_PWM1, i), Shutter[i].pwm_value);
break;
case SHT_COUNTER:
2020-09-09 13:04:57 +01:00
if (Shutter[i].accelerator) {
2021-01-23 15:26:23 +00:00
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Accelerator i=%d -> %d"),i, Shutter[i].accelerator);
ShutterUpdateVelocity(i);
2020-09-09 13:04:57 +01:00
analogWriteFreq(Shutter[i].pwm_velocity);
analogWrite(Pin(GPIO_PWM1, i), 50);
}
break;
}
2020-09-09 13:04:57 +01:00
} // if (Shutter[i].direction)
}
}
int32_t ShutterPercentToRealPosition(int16_t percent, uint32_t index)
{
2021-06-11 17:14:12 +01:00
if (Settings->shutter_set50percent[index] != 50) {
return (percent <= 5) ? Settings->shuttercoeff[2][index] * percent*10 : (Settings->shuttercoeff[1][index] * percent + (Settings->shuttercoeff[0][index]*10))*10;
} else {
2020-12-15 16:44:06 +00:00
int64_t realpos;
// check against DIV 0
2020-01-04 14:09:57 +00:00
for (uint32_t j = 0; j < 5; j++) {
2021-06-11 17:14:12 +01:00
if (0 == Settings->shuttercoeff[j][index]) {
2021-01-23 15:26:23 +00:00
AddLog(LOG_LEVEL_ERROR, PSTR("SHT: RESET/INIT CALIBRATION MATRIX DIV 0"));
2020-01-04 14:09:57 +00:00
for (uint32_t k = 0; k < 5; k++) {
2021-06-11 17:14:12 +01:00
Settings->shuttercoeff[k][index] = SHT_DIV_ROUND(calibrate_pos[k+1] * 1000, calibrate_pos[5]);
}
}
}
2020-09-09 13:04:57 +01:00
for (uint32_t k = 0; k < 5; k++) {
2021-06-11 17:14:12 +01:00
if ((percent * 10) >= Settings->shuttercoeff[k][index]) {
2020-09-09 13:04:57 +01:00
realpos = SHT_DIV_ROUND(Shutter[index].open_max * calibrate_pos[k+1], 100);
2021-06-11 17:14:12 +01:00
//AddLog(LOG_LEVEL_ERROR, PSTR("SHT: Realposition TEMP1: %d, %d %%, coeff %d"), realpos, percent, Settings->shuttercoeff[k][index]);
} else {
2021-02-18 11:42:59 +00:00
//AddLog(LOG_LEVEL_ERROR, PSTR("SHT: Shutter[%d].open_max: %d"),index, Shutter[index].open_max);
2020-09-09 13:04:57 +01:00
if (0 == k) {
2021-06-11 17:14:12 +01:00
realpos = SHT_DIV_ROUND((int64_t)percent * Shutter[index].open_max * calibrate_pos[k+1], Settings->shuttercoeff[k][index]*10 );
//AddLog(LOG_LEVEL_ERROR, PSTR("SHT: Realposition TEMP3: %d, %d %%, coeff %d"), realpos, percent, Settings->shuttercoeff[k][index]);
} else {
2021-06-11 17:14:12 +01:00
//uint32_t addon = ( percent*10 - Settings->shuttercoeff[k-1][index] ) * Shutter[index].open_max * (calibrate_pos[k+1] - calibrate_pos[k]) / (Settings->shuttercoeff[k][index] -Settings->shuttercoeff[k-1][index]) / 100;
//AddLog(LOG_LEVEL_ERROR, PSTR("SHT: Realposition TEMP2: %d, %d %%, coeff %d"), addon, (calibrate_pos[k+1] - calibrate_pos[k]), (Settings->shuttercoeff[k][index] -Settings->shuttercoeff[k-1][index]));
realpos += SHT_DIV_ROUND(((int64_t)percent*10 - Settings->shuttercoeff[k-1][index] ) * Shutter[index].open_max * (calibrate_pos[k+1] - calibrate_pos[k]), (Settings->shuttercoeff[k][index] - Settings->shuttercoeff[k-1][index])*100);
}
break;
}
}
return realpos < 0 ? 0 : realpos;
}
}
uint8_t ShutterRealToPercentPosition(int32_t realpos, uint32_t index)
{
2021-06-11 17:14:12 +01:00
if (Settings->shutter_set50percent[index] != 50) {
return (Settings->shuttercoeff[2][index] * 5 > realpos/10) ? SHT_DIV_ROUND(realpos/10, Settings->shuttercoeff[2][index]) : SHT_DIV_ROUND(realpos/10-Settings->shuttercoeff[0][index]*10, Settings->shuttercoeff[1][index]);
} else {
int64_t realpercent;
2020-09-09 13:04:57 +01:00
for (uint32_t j = 0; j < 5; j++) {
if (realpos >= Shutter[index].open_max * calibrate_pos[j+1] / 100) {
2021-06-11 17:14:12 +01:00
realpercent = SHT_DIV_ROUND(Settings->shuttercoeff[j][index], 10);
2021-02-18 11:42:59 +00:00
//AddLog(LOG_LEVEL_ERROR, PSTR("SHT: Realpercent TEMP1: %d %%, %d, coeff %d"), realpercent, realpos, Shutter[index].open_max * calibrate_pos[j+1] / 100);
} else {
2021-02-18 11:42:59 +00:00
//AddLog(LOG_LEVEL_ERROR, PSTR("SHT: Shutter[%d].open_max: %d"),index, Shutter[index].open_max);
2020-09-09 13:04:57 +01:00
if (0 == j) {
2021-06-11 17:14:12 +01:00
realpercent = SHT_DIV_ROUND(((int64_t)realpos - SHT_DIV_ROUND(Shutter[index].open_max * calibrate_pos[j], 100)) * Settings->shuttercoeff[j][index], calibrate_pos[j+1]/10*Shutter[index].open_max);
} else {
2021-06-11 17:14:12 +01:00
//uint16_t addon = ( realpos - (Shutter[index].open_max * calibrate_pos[j] / 100) ) * 10 * (Settings->shuttercoeff[j][index] - Settings->shuttercoeff[j-1][index]) / (calibrate_pos[j+1] - calibrate_pos[j])/Shutter[index].open_max;
//uint16_t addon = ( realpercent*10 - Settings->shuttercoeff[j-1][index] ) * Shutter[index].open_max * (calibrate_pos[j+1] - calibrate_pos[j]) / (Settings->shuttercoeff[j][index] -Settings->shuttercoeff[j-1][index]) / 100;
//AddLog(LOG_LEVEL_ERROR, PSTR("SHT: Realpercent TEMP2: %d %%, delta %d, %d, coeff %d"), addon,( realpos - (Shutter[index].open_max * calibrate_pos[j] / 100) ) , (calibrate_pos[j+1] - calibrate_pos[j])* Shutter[index].open_max/100, (Settings->shuttercoeff[j][index] -Settings->shuttercoeff[j-1][index]));
realpercent += SHT_DIV_ROUND(((int64_t)realpos - SHT_DIV_ROUND(Shutter[index].open_max * calibrate_pos[j], 100)) * (Settings->shuttercoeff[j][index] - Settings->shuttercoeff[j-1][index]), (calibrate_pos[j+1] - calibrate_pos[j])/10*Shutter[index].open_max) ;
}
break;
}
}
return realpercent < 0 ? 0 : realpercent;
}
}
void ShutterInit(void)
{
2020-10-30 11:29:48 +00:00
TasmotaGlobal.shutters_present = 0;
2020-09-09 13:04:57 +01:00
ShutterGlobal.RelayShutterMask = 0;
//Initialize to get relay that changed
2020-10-28 18:03:39 +00:00
ShutterGlobal.RelayOldMask = TasmotaGlobal.power;
// if shutter 4 is unused
2021-06-11 17:14:12 +01:00
if (Settings->shutter_startrelay[MAX_SHUTTERS -1] == 0) {
ShutterGlobal.open_velocity_max = Settings->shuttercoeff[4][3] > 0 ? Settings->shuttercoeff[4][3] : ShutterGlobal.open_velocity_max;
}
for (uint32_t i = 0; i < MAX_SHUTTERS; i++) {
// set startrelay to 1 on first init, but only to shutter 1. 90% usecase
2021-06-11 17:14:12 +01:00
Settings->shutter_startrelay[i] = (Settings->shutter_startrelay[i] == 0 && i == 0? 1 : Settings->shutter_startrelay[i]);
if (Settings->shutter_startrelay[i] && (Settings->shutter_startrelay[i] <= MAX_RELAYS )) {
2020-10-30 11:29:48 +00:00
TasmotaGlobal.shutters_present++;
// Add the two relays to the mask to knaw they belong to shutters
2021-06-11 17:14:12 +01:00
ShutterGlobal.RelayShutterMask |= 3 << (Settings->shutter_startrelay[i] -1) ;
// All shutters must have same mode. Switch OR Pulse. N
2021-06-11 17:14:12 +01:00
switch (Settings->pulse_timer[i]) {
2020-09-05 19:39:24 +01:00
case 0:
2020-09-09 13:04:57 +01:00
Shutter[i].switch_mode = SHT_SWITCH;
2020-09-05 19:39:24 +01:00
break;
default:
2020-09-09 13:04:57 +01:00
Shutter[i].switch_mode = SHT_PULSE;
2020-09-05 19:39:24 +01:00
break;
}
2021-06-11 17:14:12 +01:00
if (Settings->shutter_mode == SHT_UNDEF) {
bool relay_in_interlock = false;
2021-01-23 15:26:23 +00:00
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Mode undef.. calculate..."));
2021-06-11 17:14:12 +01:00
for (uint32_t j = 0; j < MAX_INTERLOCKS * Settings->flag.interlock; j++) { // CMND_INTERLOCK - Enable/disable interlock
//AddLog(LOG_LEVEL_DEBUG, PSTR("SHT: Interlock state i=%d %d, flag %d, Shuttermask %d, MaskedIL %d"),i, Settings->interlock[i], Settings->flag.interlock,ShutterGlobal.RelayShutterMask, Settings->interlock[i]&ShutterGlobal.RelayShutterMask);
if (Settings->interlock[j] && (Settings->interlock[j] & ShutterGlobal.RelayShutterMask)) {
//AddLog(LOG_LEVEL_DEBUG, PSTR("SHT: Relay in Interlock group"));
relay_in_interlock = true;
}
}
2020-09-09 13:04:57 +01:00
if (relay_in_interlock) {
ShutterGlobal.position_mode = SHT_TIME;
} else {
ShutterGlobal.position_mode = SHT_TIME_UP_DOWN;
if (PinUsed(GPIO_PWM1, i) && PinUsed(GPIO_CNTR1, i)) {
ShutterGlobal.position_mode = SHT_COUNTER;
}
}
2020-09-09 13:04:57 +01:00
} else {
2021-06-11 17:14:12 +01:00
ShutterGlobal.position_mode = Settings->shutter_mode;
}
// main function for stepper and servos to control velocity and acceleration.
TickerShutter.attach_ms(50, ShutterRtc50mS );
// default the 50 percent should not have any impact without changing it. set to 60
2021-06-11 17:14:12 +01:00
Settings->shutter_set50percent[i] = (Settings->shutter_set50percent[i] > 0) ? Settings->shutter_set50percent[i] : 50;
// use 10 sec. as default to allow everybody to play without deep initialize
2021-06-11 17:14:12 +01:00
Shutter[i].open_time = Settings->shutter_opentime[i] = (Settings->shutter_opentime[i] > 0) ? Settings->shutter_opentime[i] : 100;
Shutter[i].close_time = Settings->shutter_closetime[i] = (Settings->shutter_closetime[i] > 0) ? Settings->shutter_closetime[i] : 100;
// Update Calculation 20 because time interval is 0.05 sec ans time is in 0.1sec
2020-09-09 13:04:57 +01:00
Shutter[i].open_max = STEPS_PER_SECOND * RESOLUTION * Shutter[i].open_time / 10;
Shutter[i].close_velocity = Shutter[i].open_max / Shutter[i].close_time / 2 ;
2020-09-05 19:39:24 +01:00
// calculate a ramp slope at the first 5 percent to compensate that shutters move with down part later than the upper part
2021-06-11 17:14:12 +01:00
if (Settings->shutter_set50percent[i] != 50) {
Settings->shuttercoeff[1][i] = Shutter[i].open_max/10 * (100 - Settings->shutter_set50percent[i] ) / 5000 ;
Settings->shuttercoeff[0][i] = Shutter[i].open_max/100 - (Settings->shuttercoeff[1][i] * 10);
Settings->shuttercoeff[2][i] = (int32_t)(Settings->shuttercoeff[0][i]*10 + 5 * Settings->shuttercoeff[1][i]) / 5;
//AddLog(LOG_LEVEL_DEBUG, PSTR("SHT: Shtr%d Shutter[i].open_max %d, 50perc:%d, 0:%d, 1:%d 2:%d"), i, Shutter[i].open_max, Settings->shutter_set50percent[i], Settings->shuttercoeff[0][i],Settings->shuttercoeff[1][i],Settings->shuttercoeff[2][i]);
}
2021-06-11 17:14:12 +01:00
ShutterGlobal.RelayShutterMask |= 3 << (Settings->shutter_startrelay[i] -1);
2021-06-11 17:14:12 +01:00
Shutter[i].real_position = ShutterPercentToRealPosition(Settings->shutter_position[i], i);
2020-09-09 13:04:57 +01:00
Shutter[i].start_position = Shutter[i].target_position = Shutter[i].real_position;
2021-06-11 17:14:12 +01:00
Shutter[i].motordelay = Settings->shutter_motordelay[i];
Shutter[i].lastdirection = (50 < Settings->shutter_position[i]) ? 1 : -1;
2021-11-07 14:53:12 +00:00
// Venetian Blind
for (uint8_t k=0; k<5; k++) {
Shutter[i].tilt_config[k] = Settings->shutter_tilt_config[k][i];
}
2021-11-16 16:05:45 +00:00
Shutter[i].tilt_target_pos = Shutter[i].tilt_real_pos = Settings->shutter_tilt_pos[i];
2021-11-15 18:52:48 +00:00
Shutter[i].tilt_velocity = Shutter[i].tilt_config[2] > 0 ? ((Shutter[i].tilt_config[1]-Shutter[i].tilt_config[0])/Shutter[i].tilt_config[2])+1 : 1;
2021-11-07 14:53:12 +00:00
Shutter[i].close_velocity_max = ShutterGlobal.open_velocity_max*Shutter[i].open_time / Shutter[i].close_time;
Shutter[i].min_realPositionChange = 2 * tmax(ShutterGlobal.open_velocity_max, Shutter[i].close_velocity_max);
Shutter[i].min_TiltChange = 2 * Shutter[i].tilt_velocity;
2021-11-07 14:53:12 +00:00
2020-09-09 13:04:57 +01:00
switch (ShutterGlobal.position_mode) {
2020-09-05 19:39:24 +01:00
case SHT_PWM_VALUE:
2020-09-09 13:04:57 +01:00
ShutterGlobal.open_velocity_max = RESOLUTION;
// Initiate pwm range with defaults if not already set.
Settings->shutter_pwmrange[0][i] = Settings->shutter_pwmrange[0][i] > 0 ? Settings->shutter_pwmrange[0][i] : pwm_servo_min;
Settings->shutter_pwmrange[1][i] = Settings->shutter_pwmrange[1][i] > 0 ? Settings->shutter_pwmrange[1][i] : pwm_servo_max;
Shutter[i].min_realPositionChange = 0;
Shutter[i].min_TiltChange = 0;
2020-09-05 19:39:24 +01:00
break;
}
AddLog(LOG_LEVEL_DEBUG, PSTR("SHT: Shtr%d min realpos_chg: %d, min tilt_chg %d"),i+1,Shutter[i].min_realPositionChange,Shutter[i].min_TiltChange);
2021-11-15 16:31:15 +00:00
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shtr%d Openvel %d, Closevel: %d"),i, ShutterGlobal.open_velocity_max, Shutter[i].close_velocity_max);
AddLog(LOG_LEVEL_DEBUG, PSTR("SHT: Shtr%d Init. Pos %d, Inv %d, Locked %d, Endstop enab %d, webButt inv %d, Motordel: %d"),
2020-09-09 13:04:57 +01:00
i+1, Shutter[i].real_position,
(Settings->shutter_options[i]&1) ? 1 : 0, (Settings->shutter_options[i]&2) ? 1 : 0, (Settings->shutter_options[i]&4) ? 1 : 0, (Settings->shutter_options[i]&8) ? 1 : 0, Shutter[i].motordelay);
} else {
2020-09-09 13:04:57 +01:00
// terminate loop at first INVALID Shutter[i].
break;
}
2020-01-22 12:23:59 +00:00
ShutterLimitRealAndTargetPositions(i);
2021-06-11 17:14:12 +01:00
Settings->shutter_accuracy = 1;
}
}
void ShutterReportPosition(bool always, uint32_t index)
{
Response_P(PSTR("{"));
uint32_t i = 0;
2020-10-30 11:29:48 +00:00
uint32_t n = TasmotaGlobal.shutters_present;
2021-11-16 20:21:04 +00:00
uint8_t shutter_running = 0;
if( index != MAX_SHUTTERS) {
i = index;
n = index+1;
}
for (i; i < n; i++) {
2021-02-18 11:42:59 +00:00
//AddLog(LOG_LEVEL_DEBUG, PSTR("SHT: Shtr%d Real Pos %d"), i+1,Shutter[i].real_position);
2020-09-09 13:04:57 +01:00
uint32_t position = ShutterRealToPercentPosition(Shutter[i].real_position, i);
if (Shutter[i].direction != 0) {
2020-01-04 14:09:57 +00:00
ShutterLogPos(i);
2021-11-16 20:21:04 +00:00
shutter_running++;
}
if (i && index == MAX_SHUTTERS) { ResponseAppend_P(PSTR(",")); }
2020-09-09 13:04:57 +01:00
uint32_t target = ShutterRealToPercentPosition(Shutter[i].target_position, i);
2021-11-07 14:53:12 +00:00
ResponseAppend_P(JSON_SHUTTER_POS, i+1, (Settings->shutter_options[i] & 1) ? 100-position : position, Shutter[i].direction,(Settings->shutter_options[i] & 1) ? 100-target : target, Shutter[i].tilt_real_pos );
}
ResponseJsonEnd();
2021-11-16 20:21:04 +00:00
if (always || shutter_running) {
2020-07-20 16:24:51 +01:00
MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_STAT, PSTR(D_PRFX_SHUTTER)); // RulesProcess() now re-entry protected
}
2021-02-18 11:42:59 +00:00
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: rules_flag.shutter_moving: %d, moved %d"), TasmotaGlobal.rules_flag.shutter_moving, TasmotaGlobal.rules_flag.shutter_moved);
}
void ShutterLimitRealAndTargetPositions(uint32_t i)
{
2020-09-09 13:04:57 +01:00
if (Shutter[i].real_position<0) Shutter[i].real_position = 0;
if (Shutter[i].real_position>Shutter[i].open_max) Shutter[i].real_position = Shutter[i].open_max;
if (Shutter[i].target_position<0) Shutter[i].target_position = 0;
if (Shutter[i].target_position>Shutter[i].open_max) Shutter[i].target_position = Shutter[i].open_max;
2020-01-13 11:00:34 +00:00
}
2020-09-05 19:39:24 +01:00
void ShutterCalculateAccelerator(uint8_t i)
{
// No Logging allowed. Part of RTC Timer
2020-09-09 13:04:57 +01:00
if (Shutter[i].direction != 0) {
switch (ShutterGlobal.position_mode) {
case SHT_COUNTER:
case SHT_PWM_VALUE:
current_real_position = Shutter[i].real_position;
current_pwm_velocity = Shutter[i].pwm_velocity;
// calculate max velocity allowed in this direction
2020-09-09 13:04:57 +01:00
velocity_max = Shutter[i].direction == 1 ? ShutterGlobal.open_velocity_max : Shutter[i].close_velocity_max;
// calculate max change of velocyty based on the defined motordelay in steps
2020-09-09 13:04:57 +01:00
velocity_change_per_step_max = velocity_max / (Shutter[i].motordelay>0 ? Shutter[i].motordelay : 1);
// minimumtime required from current velocity to stop
min_runtime_ms = current_pwm_velocity * 1000 / STEPS_PER_SECOND / velocity_change_per_step_max;
// decellaration way from current velocity
current_stop_way = min_runtime_ms * STEPS_PER_SECOND * (current_pwm_velocity + velocity_change_per_step_max) * Shutter[i].direction / 2 / ShutterGlobal.open_velocity_max - (Shutter[i].accelerator<0?Shutter[i].direction*1000*current_pwm_velocity/ShutterGlobal.open_velocity_max:0);
next_possible_stop_position = current_real_position + current_stop_way ;
// ensure that the accelerotor kicks in at the first overrun of the target position
if ( Shutter[i].accelerator < 0 || next_possible_stop_position * Shutter[i].direction > Shutter[i].target_position * Shutter[i].direction ) {
// if startet to early because of 0.05sec maximum accuracy and final position is to far away (200) accelerate a bit less
if (next_possible_stop_position * Shutter[i].direction+200 < Shutter[i].target_position * Shutter[i].direction) {
Shutter[i].accelerator = -velocity_change_per_step_max*9/10;
} else {
// in any case increase accelleration if overrun is detected during decelleration
if (next_possible_stop_position * Shutter[i].direction > Shutter[i].target_position * Shutter[i].direction && Shutter[i].accelerator < 0) {
Shutter[i].accelerator = -velocity_change_per_step_max*11/10;
} else {
// as long as the calculated end position is ok stay with proposed decelleration
Shutter[i].accelerator = -velocity_change_per_step_max;
}
}
// detect during the acceleration phase the point final speed is reached
} else if ( Shutter[i].accelerator > 0 && current_pwm_velocity == velocity_max) {
2020-09-09 13:04:57 +01:00
Shutter[i].accelerator = 0;
}
break;
}
2020-09-05 19:39:24 +01:00
}
}
void ShutterDecellerateForStop(uint8_t i)
{
2020-09-09 13:04:57 +01:00
switch (ShutterGlobal.position_mode) {
2020-09-05 19:39:24 +01:00
case SHT_PWM_VALUE:
case SHT_COUNTER:
int16_t missing_steps;
Shutter[i].accelerator = -(ShutterGlobal.open_velocity_max / (Shutter[i].motordelay>4 ? (Shutter[i].motordelay*11)/10 : 4) );
while (Shutter[i].pwm_velocity > -2*Shutter[i].accelerator && Shutter[i].pwm_velocity != PWM_MIN) {
delay(50);
2021-01-23 15:26:23 +00:00
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Velocity %ld, Delta %d"), Shutter[i].pwm_velocity, Shutter[i].accelerator );
2020-09-05 19:39:24 +01:00
// Control will be done in RTC Ticker.
2020-09-05 19:39:24 +01:00
}
2020-09-09 13:04:57 +01:00
if (ShutterGlobal.position_mode == SHT_COUNTER){
missing_steps = ((Shutter[i].target_position-Shutter[i].start_position)*Shutter[i].direction*ShutterGlobal.open_velocity_max/RESOLUTION/STEPS_PER_SECOND) - RtcSettings.pulse_counter[i];
2020-09-05 19:39:24 +01:00
//prepare for stop PWM
2020-09-09 13:04:57 +01:00
Shutter[i].accelerator = 0;
Shutter[i].pwm_velocity = 0;
2021-11-07 14:53:12 +00:00
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Remain %d count %d -> target %d, dir %d"), missing_steps, RtcSettings.pulse_counter[i], (uint32_t)(Shutter[i].target_position-Shutter[i].start_position)*Shutter[i].direction*ShutterGlobal.open_velocity_max/RESOLUTION/STEPS_PER_SECOND, Shutter[i].direction);
while (RtcSettings.pulse_counter[i] < (uint32_t)(Shutter[i].target_position-Shutter[i].start_position)*Shutter[i].direction*ShutterGlobal.open_velocity_max/RESOLUTION/STEPS_PER_SECOND && missing_steps > 0) {
2020-09-05 19:39:24 +01:00
}
analogWrite(Pin(GPIO_PWM1, i), 0); // removed with 8.3 because of reset caused by watchog
2020-09-09 13:04:57 +01:00
Shutter[i].real_position = ShutterCalculatePosition(i);
2021-11-07 14:53:12 +00:00
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Remain steps %d"), missing_steps);
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Real %d, Pulsecount %d, tobe %d, Start %d"), Shutter[i].real_position,RtcSettings.pulse_counter[i], (uint32_t)(Shutter[i].target_position-Shutter[i].start_position)*Shutter[i].direction*ShutterGlobal.open_velocity_max/RESOLUTION/STEPS_PER_SECOND, Shutter[i].start_position);
2020-09-05 19:39:24 +01:00
}
2020-09-09 13:04:57 +01:00
Shutter[i].direction = 0;
Shutter[i].pwm_velocity = 0;
2020-09-05 19:39:24 +01:00
break;
}
}
void ShutterPowerOff(uint8_t i)
{
AddLog(LOG_LEVEL_DEBUG, PSTR("SHT: Stop %d Mode %d time %d"), i+1,Shutter[i].switch_mode, Shutter[i].time); // fix log to indicate correct shutter number
2020-09-05 19:39:24 +01:00
ShutterDecellerateForStop(i);
uint8_t cur_relay = Settings->shutter_startrelay[i] + (Shutter[i].direction == 1 ? 0 : (uint8_t)(ShutterGlobal.position_mode == SHT_TIME)) ;
2021-11-15 18:52:48 +00:00
if (Shutter[i].direction !=0) {
Shutter[i].direction = 0;
}
if (Shutter[i].real_position == Shutter[i].start_position) {
//AddLog(LOG_LEVEL_DEBUG, PSTR("SHT: Update target tilt shutter %d from %d to %d"), i+1, Shutter[i].tilt_target_pos , Shutter[i].tilt_real_pos);
Shutter[i].tilt_target_pos = Shutter[i].tilt_real_pos;
}
2021-11-16 20:11:34 +00:00
TasmotaGlobal.rules_flag.shutter_moved = 1;
2020-09-09 13:04:57 +01:00
switch (Shutter[i].switch_mode) {
2020-09-05 19:39:24 +01:00
case SHT_SWITCH:
for (int8_t k=0;k<2;k++) {
if ((1 << (Settings->shutter_startrelay[i]+k-1)) & TasmotaGlobal.power) {
ExecuteCommandPowerShutter(Settings->shutter_startrelay[i]+k, 0, SRC_SHUTTER);
}
2020-09-05 19:39:24 +01:00
}
break;
case SHT_PULSE:
// we have a momentary switch here. Needs additional pulse on same relay after the end
2020-10-30 11:29:48 +00:00
if ((SRC_PULSETIMER == TasmotaGlobal.last_source || SRC_SHUTTER == TasmotaGlobal.last_source || SRC_WEBGUI == TasmotaGlobal.last_source)) {
2020-09-05 19:39:24 +01:00
ExecuteCommandPowerShutter(cur_relay, 1, SRC_SHUTTER);
// switch off direction relay to make it power less
2021-06-11 17:14:12 +01:00
if (((1 << (Settings->shutter_startrelay[i])) & TasmotaGlobal.power) && Settings->shutter_startrelay[i]+1 != cur_relay) {
ExecuteCommandPowerShutter(Settings->shutter_startrelay[i]+1, 0, SRC_SHUTTER);
2020-09-05 19:39:24 +01:00
}
} else {
2020-10-30 11:29:48 +00:00
TasmotaGlobal.last_source = SRC_SHUTTER;
2020-09-05 19:39:24 +01:00
}
break;
}
// Store current PWM value to ensure proper position after reboot.
2020-09-09 13:04:57 +01:00
switch (ShutterGlobal.position_mode) {
case SHT_PWM_VALUE:
Shutter[i].pwm_value = SHT_DIV_ROUND((Settings->shutter_pwmrange[1][i]-Settings->shutter_pwmrange[0][i]) * Shutter[i].target_position , Shutter[i].open_max)+Settings->shutter_pwmrange[0][i];
analogWrite(Pin(GPIO_PWM1, i), Shutter[i].pwm_value);
AddLog(LOG_LEVEL_DEBUG, PSTR("SHT: PWM final %d"),Shutter[i].pwm_value);
char scmnd[20];
#ifdef SHUTTER_CLEAR_PWM_ONSTOP
// free the PWM servo lock on stop.
analogWrite(Pin(GPIO_PWM1, i), 0);
#endif
break;
}
2021-11-16 12:46:22 +00:00
Settings->save_data = savedata_original;
TasmotaGlobal.save_data_counter = Settings->save_data;
2021-11-15 18:52:48 +00:00
delay(MOTOR_STOP_TIME);
2020-09-05 19:39:24 +01:00
}
void ShutterUpdatePosition(void)
{
char scommand[CMDSZ];
char stopic[TOPSZ];
2020-10-30 11:29:48 +00:00
for (uint32_t i = 0; i < TasmotaGlobal.shutters_present; i++) {
2020-09-09 13:04:57 +01:00
if (Shutter[i].direction != 0) {
if (!ShutterGlobal.start_reported) {
ShutterReportPosition(true, i);
XdrvRulesProcess(0);
2020-09-09 13:04:57 +01:00
ShutterGlobal.start_reported = 1;
}
2021-11-16 12:02:36 +00:00
int32_t deltatime = Shutter[i].time-Shutter[i].last_reported_time;
2021-11-17 10:28:27 +00:00
Shutter[i].last_reported_time = Shutter[i].time+1;
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Time %d(%d), cStop %d, cVelo %d, mVelo %d, aVelo %d, mRun %d, aPos %d, aPos2 %d, nStop %d, Trgt %d, mVelo %d, Dir %d, Tilt %d, TrgtTilt: %d, Tiltmove: %d"),
Shutter[i].time, deltatime, current_stop_way, current_pwm_velocity, velocity_max, Shutter[i].accelerator, min_runtime_ms, current_real_position,Shutter[i].real_position,
next_possible_stop_position, Shutter[i].target_position, velocity_change_per_step_max, Shutter[i].direction,Shutter[i].tilt_real_pos, Shutter[i].tilt_target_pos,
Shutter[i].tiltmoving);
if ( ((Shutter[i].real_position * Shutter[i].direction >= Shutter[i].target_position * Shutter[i].direction && Shutter[i].tiltmoving==0) ||
((int16_t)Shutter[i].tilt_real_pos * Shutter[i].direction * Shutter[i].tilt_config[2] >= (int16_t)Shutter[i].tilt_target_pos * Shutter[i].direction * Shutter[i].tilt_config[2] && Shutter[i].tiltmoving==1))
|| (ShutterGlobal.position_mode == SHT_COUNTER && Shutter[i].accelerator <0 && Shutter[i].pwm_velocity+Shutter[i].accelerator<PWM_MIN)) {
if (Shutter[i].direction != 0) {
Shutter[i].lastdirection = Shutter[i].direction;
}
ShutterPowerOff(i);
ShutterLimitRealAndTargetPositions(i);
Settings->shutter_position[i] = ShutterRealToPercentPosition(Shutter[i].real_position, i);
Shutter[i].start_position = Shutter[i].real_position;
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Pre: Tilt not match %d -> %d, moving: %d"),Shutter[i].tilt_real_pos,Shutter[i].tilt_target_pos,Shutter[i].tiltmoving);
if (abs(Shutter[i].tilt_real_pos - Shutter[i].tilt_target_pos) > Shutter[i].min_TiltChange && Shutter[i].tiltmoving == 0
&& Settings->shutter_position[i] > 0 && Settings->shutter_position[i] < 100) {
2021-11-17 10:28:27 +00:00
AddLog(LOG_LEVEL_INFO, PSTR("SHT: Tilt not match %d -> %d"),Shutter[i].tilt_real_pos,Shutter[i].tilt_target_pos);
char databuf[1] = "";
XdrvMailbox.data = databuf;
2021-11-17 10:28:27 +00:00
XdrvMailbox.payload = -99;
XdrvMailbox.index = i+1;
Shutter[i].tiltmoving = 1;
CmndShutterPosition();
return;
} else {
Settings->shutter_tilt_pos[i] = Shutter[i].tilt_real_pos;
}
ShutterLogPos(i);
// sending MQTT result to broker
snprintf_P(scommand, sizeof(scommand),PSTR(D_SHUTTER "%d"), i+1);
GetTopic_P(stopic, STAT, TasmotaGlobal.mqtt_topic, scommand);
Response_P("%d", (Settings->shutter_options[i] & 1) ? 100 - Settings->shutter_position[i]: Settings->shutter_position[i]);
MqttPublish(stopic, Settings->flag.mqtt_power_retain); // CMND_POWERRETAIN
ShutterReportPosition(true, i);
TasmotaGlobal.rules_flag.shutter_moved = 1;
XdrvRulesProcess(0);
}
}
}
}
bool ShutterState(uint32_t device)
{
if (device > 4) { return false; }
device--;
device &= 3;
2021-06-11 17:14:12 +01:00
return (Settings->flag3.shutter_mode && // SetOption80 - Enable shutter support
(ShutterGlobal.RelayShutterMask & (1 << (Settings->shutter_startrelay[device]-1))) );
}
void ShutterAllowPreStartProcedure(uint8_t i)
{
#ifdef USE_RULES
uint32_t uptime_Local=0;
2021-11-15 16:31:15 +00:00
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Delay Start? var%d <99>=<%s>, max10s?"),i+1, rules_vars[i]);
XdrvRulesProcess(0);
2020-10-28 16:32:07 +00:00
uptime_Local = TasmotaGlobal.uptime;
while (uptime_Local+10 > TasmotaGlobal.uptime && (String)rules_vars[i] == "99") {
loop();
}
2021-11-15 16:31:15 +00:00
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Delay Start. Done"));
#endif // USE_RULES
}
2020-01-04 14:09:57 +00:00
void ShutterStartInit(uint32_t i, int32_t direction, int32_t target_pos)
{
2021-11-16 12:46:22 +00:00
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: dir %d, delta1 %d, delta2 %d"),direction, (Shutter[i].open_max - Shutter[i].real_position) / Shutter[i].close_velocity, Shutter[i].real_position / Shutter[i].close_velocity);
if ( ( ( (1 == direction) && ((Shutter[i].open_max - Shutter[i].real_position) <= Shutter[i].min_realPositionChange))
|| ( (-1 == direction) && (Shutter[i].real_position <= Shutter[i].min_realPositionChange)) )
&& abs(Shutter[i].tilt_real_pos-Shutter[i].tilt_target_pos) <= Shutter[i].min_TiltChange) {
2020-09-09 13:04:57 +01:00
ShutterGlobal.skip_relay_change = 1;
} else {
2020-09-09 13:04:57 +01:00
Shutter[i].pwm_velocity = 0;
switch (ShutterGlobal.position_mode) {
2020-09-05 19:39:24 +01:00
#ifdef SHUTTER_STEPPER
case SHT_COUNTER:
2020-09-09 13:04:57 +01:00
analogWriteFreq(Shutter[i].pwm_velocity);
2020-09-05 19:39:24 +01:00
analogWrite(Pin(GPIO_PWM1, i), 0);
RtcSettings.pulse_counter[i] = 0;
break;
#endif
}
2021-11-16 12:46:22 +00:00
2020-09-09 13:04:57 +01:00
Shutter[i].accelerator = ShutterGlobal.open_velocity_max / (Shutter[i].motordelay>0 ? Shutter[i].motordelay : 1);
Shutter[i].target_position = target_pos;
Shutter[i].start_position = Shutter[i].real_position;
2020-10-29 12:58:50 +00:00
TasmotaGlobal.rules_flag.shutter_moving = 1;
ShutterAllowPreStartProcedure(i);
2021-11-16 13:00:49 +00:00
Shutter[i].time = Shutter[i].last_reported_time = 0;
2021-11-16 12:46:22 +00:00
// avoid file system writes during move to minimize missing steps
savedata_original = Settings->save_data;
Settings->save_data = 0; // will be restored after movement
2021-11-16 20:11:34 +00:00
2021-11-16 12:46:22 +00:00
TasmotaGlobal.save_data_counter = Settings->save_data;
ShutterGlobal.skip_relay_change = 0;
2020-10-29 12:58:50 +00:00
TasmotaGlobal.rules_flag.shutter_moved = 0;
2020-09-09 13:04:57 +01:00
ShutterGlobal.start_reported = 0;
2021-11-07 14:53:12 +00:00
Shutter[i].tilt_real_pos = tmax(tmin(Shutter[i].tilt_real_pos,Shutter[i].tilt_config[1]),Shutter[i].tilt_config[0]);
Shutter[i].tilt_start_pos = Shutter[i].tilt_real_pos;
if (Shutter[i].tilt_config[1]-Shutter[i].tilt_config[0] != 0) {
2021-11-16 14:28:14 +00:00
Shutter[i].venetian_delay = (direction > 0 ? Shutter[i].tilt_config[1]-Shutter[i].tilt_real_pos : Shutter[i].tilt_real_pos-Shutter[i].tilt_config[0]) * Shutter[i].tilt_config[2] / (Shutter[i].tilt_config[1]-Shutter[i].tilt_config[0]);
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: real %d, start %d, counter %d,freq_max %d, dir %d, freq %d"),Shutter[i].real_position, Shutter[i].start_position ,RtcSettings.pulse_counter[i],ShutterGlobal.open_velocity_max , direction ,ShutterGlobal.open_velocity_max );
2021-11-16 12:46:22 +00:00
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: VenetianDelay: %d, Pos: %d, Dir: %d, Delta: %d, Dur: %d, StartP: %d, TgtP: %d"),
2021-11-16 14:28:14 +00:00
Shutter[i].venetian_delay, Shutter[i].tilt_real_pos,direction,(Shutter[i].tilt_config[1]-Shutter[i].tilt_config[0]), Shutter[i].tilt_config[2],Shutter[i].tilt_start_pos,Shutter[i].tilt_target_pos);
}
}
2021-11-16 12:46:22 +00:00
//AddLog(LOG_LEVEL_DEBUG, PSTR("SHT: Start shtr%d from %d to %d in dir: %d"), i, Shutter[i].start_position, Shutter[i].target_position, direction);
Shutter[i].direction = direction; // Last action. This causes RTC to start.
}
2020-09-05 19:39:24 +01:00
int32_t ShutterCalculatePosition(uint32_t i)
{
2020-11-14 13:02:15 +00:00
// No Logging allowed. Part of RTC Timer
2020-09-09 13:04:57 +01:00
if (Shutter[i].direction != 0) {
switch (ShutterGlobal.position_mode) {
case SHT_COUNTER:
2021-02-18 11:42:59 +00:00
return ((int64_t)RtcSettings.pulse_counter[i]*Shutter[i].direction*STEPS_PER_SECOND * RESOLUTION / ShutterGlobal.open_velocity_max)+Shutter[i].start_position;
break;
case SHT_TIME:
case SHT_TIME_UP_DOWN:
case SHT_TIME_GARAGE:
if (Shutter[i].tilt_config[2] > 0) {
if (Shutter[i].time <= Shutter[i].venetian_delay) {
Shutter[i].tilt_real_pos = (Shutter[i].tilt_start_pos + ((Shutter[i].direction * (int16_t)Shutter[i].time * (Shutter[i].tilt_config[1]-Shutter[i].tilt_config[0])) / Shutter[i].tilt_config[2]));
} else {
Shutter[i].tilt_real_pos = Shutter[i].direction == 1 ? Shutter[i].tilt_config[1] : Shutter[i].tilt_config[0];
}
2021-11-07 14:53:12 +00:00
}
return Shutter[i].start_position + ( (Shutter[i].time - tmin(Shutter[i].venetian_delay+Shutter[i].motordelay, Shutter[i].time)) * (Shutter[i].direction > 0 ? RESOLUTION : -Shutter[i].close_velocity));
break;
case SHT_PWM_TIME:
break;
case SHT_PWM_VALUE:
2020-09-09 13:04:57 +01:00
return Shutter[i].real_position;
2020-09-05 19:39:24 +01:00
break;
default:
break;
2020-09-05 19:39:24 +01:00
}
2020-11-14 13:02:15 +00:00
} else {
return Shutter[i].real_position;
}
return 0; // Never reaches here, Satisfy compiler
}
void ShutterRelayChanged(void)
{
2020-09-09 13:04:57 +01:00
// ShutterGlobal.RelayCurrentMask = binary relay that was recently changed and cause an Action
// powerstate_local = binary powermatrix and relays from shutter: 0..3
// relays_changed = bool if one of the relays that belong to the shutter changed not by shutter or pulsetimer
char stemp1[10];
2020-10-30 11:29:48 +00:00
for (uint32_t i = 0; i < TasmotaGlobal.shutters_present; i++) {
2021-06-11 17:14:12 +01:00
power_t powerstate_local = (TasmotaGlobal.power >> (Settings->shutter_startrelay[i] -1)) & 3;
2020-09-05 19:39:24 +01:00
// SRC_IGNORE added because INTERLOCK function bite causes this as last source for changing the relay.
2021-06-11 17:14:12 +01:00
//uint8 manual_relays_changed = ((ShutterGlobal.RelayCurrentMask >> (Settings->shutter_startrelay[i] -1)) & 3) && SRC_IGNORE != TasmotaGlobal.last_source && SRC_SHUTTER != TasmotaGlobal.last_source && SRC_PULSETIMER != TasmotaGlobal.last_source ;
uint8 manual_relays_changed = ((ShutterGlobal.RelayCurrentMask >> (Settings->shutter_startrelay[i] -1)) & 3) && SRC_SHUTTER != TasmotaGlobal.last_source && SRC_PULSETIMER != TasmotaGlobal.last_source ;
2021-11-15 18:52:48 +00:00
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shtr%d, Source %s, Powerstate %ld, RelayMask %d, ManualChange %d"),
2021-11-15 18:55:04 +00:00
// i+1, GetTextIndexed(stemp1, sizeof(stemp1), TasmotaGlobal.last_source, kCommandSource), powerstate_local,ShutterGlobal.RelayCurrentMask,manual_relays_changed);
if (manual_relays_changed) {
2020-09-09 13:04:57 +01:00
//ShutterGlobal.skip_relay_change = true;
2020-01-13 11:00:34 +00:00
ShutterLimitRealAndTargetPositions(i);
2020-09-09 13:04:57 +01:00
switch (Shutter[i].switch_mode ) {
2020-09-05 19:39:24 +01:00
case SHT_PULSE:
2020-09-09 13:04:57 +01:00
if (Shutter[i].direction != 0 && powerstate_local) {
Shutter[i].target_position = Shutter[i].real_position;
2020-09-05 19:39:24 +01:00
powerstate_local = 0;
2021-02-18 11:42:59 +00:00
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shtr%d, Switch OFF motor. Target %ld, Source %s, Powerstate %ld, RelayMask %d, ManualChange %d"),
2021-01-02 14:47:03 +00:00
i+1, Shutter[i].target_position, GetTextIndexed(stemp1, sizeof(stemp1), TasmotaGlobal.last_source, kCommandSource), powerstate_local,ShutterGlobal.RelayCurrentMask,manual_relays_changed);
2020-09-05 19:39:24 +01:00
}
break;
default:
2020-10-30 11:29:48 +00:00
TasmotaGlobal.last_source = SRC_SHUTTER; // avoid switch off in the next loop
if (Shutter[i].direction != 0 ) Shutter[i].target_position = Shutter[i].real_position;
2020-09-05 19:39:24 +01:00
}
2021-11-24 16:43:25 +00:00
if (powerstate_local > 0) {
Shutter[i].tiltmoving = 0;
}
2020-09-09 13:04:57 +01:00
switch (ShutterGlobal.position_mode) {
// enum Shutterposition_mode {SHT_TIME, SHT_TIME_UP_DOWN, SHT_TIME_GARAGE, SHT_COUNTER, SHT_PWM_VALUE, SHT_PWM_TIME,};
2020-09-05 19:39:24 +01:00
case SHT_TIME_UP_DOWN:
case SHT_COUNTER:
case SHT_PWM_VALUE:
case SHT_PWM_TIME:
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: power off manual change"));
2020-09-05 19:39:24 +01:00
ShutterPowerOff(i);
switch (powerstate_local) {
case 1:
2020-09-09 13:04:57 +01:00
ShutterStartInit(i, 1, Shutter[i].open_max);
2020-09-05 19:39:24 +01:00
break;
case 3:
ShutterStartInit(i, -1, 0);
break;
default:
2021-02-18 11:42:59 +00:00
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shtr%d Switch OFF motor."),i);
2020-09-09 13:04:57 +01:00
Shutter[i].target_position = Shutter[i].real_position;
2020-09-05 19:39:24 +01:00
}
break;
case SHT_TIME:
switch (powerstate_local) {
case 1:
2020-09-09 13:04:57 +01:00
ShutterStartInit(i, 1, Shutter[i].open_max);
2020-09-05 19:39:24 +01:00
break;
case 2:
ShutterStartInit(i, -1, 0);
break;
default:
2021-02-18 11:42:59 +00:00
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shtr%d Switch OFF motor."),i);
2020-09-09 13:04:57 +01:00
Shutter[i].target_position = Shutter[i].real_position;
2020-09-05 19:39:24 +01:00
}
break;
case SHT_TIME_GARAGE:
switch (powerstate_local) {
case 1:
2020-09-09 13:04:57 +01:00
ShutterStartInit(i, Shutter[i].lastdirection*-1 , Shutter[i].lastdirection == 1 ? 0 : Shutter[i].open_max);
2021-01-23 15:26:23 +00:00
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shtr%d Garage. NewTarget %d"), i, Shutter[i].target_position);
2020-09-05 19:39:24 +01:00
break;
default:
2020-09-09 13:04:57 +01:00
Shutter[i].target_position = Shutter[i].real_position;
2020-09-05 19:39:24 +01:00
}
2020-09-09 13:04:57 +01:00
} // switch (ShutterGlobal.position_mode)
2021-11-24 16:43:25 +00:00
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shtr%d, Target %ld, Power: %d, tiltmv: %d"), i+1, Shutter[i].target_position, powerstate_local,Shutter[i].tiltmoving);
2020-09-05 19:39:24 +01:00
} // if (manual_relays_changed)
2020-10-30 11:29:48 +00:00
} // for (uint32_t i = 0; i < TasmotaGlobal.shutters_present; i++)
}
bool ShutterButtonIsSimultaneousHold(uint32_t button_index, uint32_t shutter_index)
{
// check for simultaneous shutter button hold
uint32 min_shutterbutton_hold_timer = -1; // -1 == max(uint32)
for (uint32_t i = 0; i < MAX_SHUTTER_KEYS; i++) {
2021-06-11 17:14:12 +01:00
if ((button_index != i) && (Settings->shutter_button[i] & (1<<31)) && ((Settings->shutter_button[i] & 0x03) == shutter_index) && (Button.hold_timer[i] < min_shutterbutton_hold_timer))
min_shutterbutton_hold_timer = Button.hold_timer[i];
}
return ((-1 != min_shutterbutton_hold_timer) && (min_shutterbutton_hold_timer > (Button.hold_timer[button_index]>>1)));
}
Initial support for shutter button control New command "ShutterButton<x> <a> <b> <c> <d> <e> <f> <g> <h> <i> <j>" added that allows to assign a tasmota button <x> to control shutter <a>. Single press button shutter is set to position <b>. Double press button shutter is set to position <c>. Tripple press button shutter is set to position <d>. Hold button shutter is set to position <e>. Disabling any button action is given by <b> ... <e> equal to "-". Any press of the button while the shutter is moving will immediately stop that shutter. Global steering of all your shutters at home is supported by MQTT. By any button action an MQTT command can be initiated to the <grouptopic> of the device. For single press button this can be enabled by <f> equal to "1". Disabling is indicated by <f> equal to "0". Double to hold actions are given by <g> ... <i>, correspondingly. When <j> is equal to "0" only "cmnd/<grouptopic>/Shutterposition<y> ..." with <y>=<x> is fired. When <j> is equal to "1" <y>=1...4 is used to control any shutter number of a tasmota device having same <grouptopic>. Easy setup for an "up" button: ShutterButton<x> <a> up (same as ShutterButton<x> <a> 100 50 74 100 0 0 0 1 1) Single press will move shutter up to 100%, double press to 50% and tripple press to 74%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> up to 100%. Easy setup for an "down" button: ShutterButton<x> <a> down (same as ShutterButton<x> <a> 0 50 24 0 0 0 0 1 1) Single press will move shutter down to 0%, double press to 50% and tripple press to 24%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> down to 0%. Easy setup for an "updown" button: ShutterButton<x> <a> updown (same as ShutterButton<x> <a> 100 0 50 - 0 0 0 0 0) Single press will move shutter up to 100%, double press down to 0% and tripple press to 50%. No hold action and no other shutter control by MQTT.
2020-01-02 10:23:11 +00:00
void ShutterButtonHandler(void)
{
uint8_t buttonState = SHT_NOT_PRESSED;
uint8_t button = XdrvMailbox.payload;
uint8_t press_index;
uint32_t button_index = XdrvMailbox.index;
2021-06-11 17:14:12 +01:00
uint8_t shutter_index = Settings->shutter_button[button_index] & 0x03;
uint16_t loops_per_second = 1000 / Settings->button_debounce; // ButtonDebounce (50)
Initial support for shutter button control New command "ShutterButton<x> <a> <b> <c> <d> <e> <f> <g> <h> <i> <j>" added that allows to assign a tasmota button <x> to control shutter <a>. Single press button shutter is set to position <b>. Double press button shutter is set to position <c>. Tripple press button shutter is set to position <d>. Hold button shutter is set to position <e>. Disabling any button action is given by <b> ... <e> equal to "-". Any press of the button while the shutter is moving will immediately stop that shutter. Global steering of all your shutters at home is supported by MQTT. By any button action an MQTT command can be initiated to the <grouptopic> of the device. For single press button this can be enabled by <f> equal to "1". Disabling is indicated by <f> equal to "0". Double to hold actions are given by <g> ... <i>, correspondingly. When <j> is equal to "0" only "cmnd/<grouptopic>/Shutterposition<y> ..." with <y>=<x> is fired. When <j> is equal to "1" <y>=1...4 is used to control any shutter number of a tasmota device having same <grouptopic>. Easy setup for an "up" button: ShutterButton<x> <a> up (same as ShutterButton<x> <a> 100 50 74 100 0 0 0 1 1) Single press will move shutter up to 100%, double press to 50% and tripple press to 74%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> up to 100%. Easy setup for an "down" button: ShutterButton<x> <a> down (same as ShutterButton<x> <a> 0 50 24 0 0 0 0 1 1) Single press will move shutter down to 0%, double press to 50% and tripple press to 24%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> down to 0%. Easy setup for an "updown" button: ShutterButton<x> <a> updown (same as ShutterButton<x> <a> 100 0 50 - 0 0 0 0 0) Single press will move shutter up to 100%, double press down to 0% and tripple press to 50%. No hold action and no other shutter control by MQTT.
2020-01-02 10:23:11 +00:00
if ((PRESSED == button) && (NOT_PRESSED == Button.last_state[button_index])) {
2021-06-11 17:14:12 +01:00
if (Settings->flag.button_single) { // SetOption13 (0) - Allow only single button press for immediate action
Initial support for shutter button control New command "ShutterButton<x> <a> <b> <c> <d> <e> <f> <g> <h> <i> <j>" added that allows to assign a tasmota button <x> to control shutter <a>. Single press button shutter is set to position <b>. Double press button shutter is set to position <c>. Tripple press button shutter is set to position <d>. Hold button shutter is set to position <e>. Disabling any button action is given by <b> ... <e> equal to "-". Any press of the button while the shutter is moving will immediately stop that shutter. Global steering of all your shutters at home is supported by MQTT. By any button action an MQTT command can be initiated to the <grouptopic> of the device. For single press button this can be enabled by <f> equal to "1". Disabling is indicated by <f> equal to "0". Double to hold actions are given by <g> ... <i>, correspondingly. When <j> is equal to "0" only "cmnd/<grouptopic>/Shutterposition<y> ..." with <y>=<x> is fired. When <j> is equal to "1" <y>=1...4 is used to control any shutter number of a tasmota device having same <grouptopic>. Easy setup for an "up" button: ShutterButton<x> <a> up (same as ShutterButton<x> <a> 100 50 74 100 0 0 0 1 1) Single press will move shutter up to 100%, double press to 50% and tripple press to 74%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> up to 100%. Easy setup for an "down" button: ShutterButton<x> <a> down (same as ShutterButton<x> <a> 0 50 24 0 0 0 0 1 1) Single press will move shutter down to 0%, double press to 50% and tripple press to 24%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> down to 0%. Easy setup for an "updown" button: ShutterButton<x> <a> updown (same as ShutterButton<x> <a> 100 0 50 - 0 0 0 0 0) Single press will move shutter up to 100%, double press down to 0% and tripple press to 50%. No hold action and no other shutter control by MQTT.
2020-01-02 10:23:11 +00:00
buttonState = SHT_PRESSED_MULTI;
press_index = 1;
} else {
2020-09-09 13:04:57 +01:00
if ((Shutter[shutter_index].direction) && (Button.press_counter[button_index]==0)) {
Initial support for shutter button control New command "ShutterButton<x> <a> <b> <c> <d> <e> <f> <g> <h> <i> <j>" added that allows to assign a tasmota button <x> to control shutter <a>. Single press button shutter is set to position <b>. Double press button shutter is set to position <c>. Tripple press button shutter is set to position <d>. Hold button shutter is set to position <e>. Disabling any button action is given by <b> ... <e> equal to "-". Any press of the button while the shutter is moving will immediately stop that shutter. Global steering of all your shutters at home is supported by MQTT. By any button action an MQTT command can be initiated to the <grouptopic> of the device. For single press button this can be enabled by <f> equal to "1". Disabling is indicated by <f> equal to "0". Double to hold actions are given by <g> ... <i>, correspondingly. When <j> is equal to "0" only "cmnd/<grouptopic>/Shutterposition<y> ..." with <y>=<x> is fired. When <j> is equal to "1" <y>=1...4 is used to control any shutter number of a tasmota device having same <grouptopic>. Easy setup for an "up" button: ShutterButton<x> <a> up (same as ShutterButton<x> <a> 100 50 74 100 0 0 0 1 1) Single press will move shutter up to 100%, double press to 50% and tripple press to 74%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> up to 100%. Easy setup for an "down" button: ShutterButton<x> <a> down (same as ShutterButton<x> <a> 0 50 24 0 0 0 0 1 1) Single press will move shutter down to 0%, double press to 50% and tripple press to 24%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> down to 0%. Easy setup for an "updown" button: ShutterButton<x> <a> updown (same as ShutterButton<x> <a> 100 0 50 - 0 0 0 0 0) Single press will move shutter up to 100%, double press down to 0% and tripple press to 50%. No hold action and no other shutter control by MQTT.
2020-01-02 10:23:11 +00:00
buttonState = SHT_PRESSED_IMMEDIATE;
press_index = 1;
Button.press_counter[button_index] = 99; // Remember to discard further action for press & hold within button timings
} else {
Initial support for shutter button control New command "ShutterButton<x> <a> <b> <c> <d> <e> <f> <g> <h> <i> <j>" added that allows to assign a tasmota button <x> to control shutter <a>. Single press button shutter is set to position <b>. Double press button shutter is set to position <c>. Tripple press button shutter is set to position <d>. Hold button shutter is set to position <e>. Disabling any button action is given by <b> ... <e> equal to "-". Any press of the button while the shutter is moving will immediately stop that shutter. Global steering of all your shutters at home is supported by MQTT. By any button action an MQTT command can be initiated to the <grouptopic> of the device. For single press button this can be enabled by <f> equal to "1". Disabling is indicated by <f> equal to "0". Double to hold actions are given by <g> ... <i>, correspondingly. When <j> is equal to "0" only "cmnd/<grouptopic>/Shutterposition<y> ..." with <y>=<x> is fired. When <j> is equal to "1" <y>=1...4 is used to control any shutter number of a tasmota device having same <grouptopic>. Easy setup for an "up" button: ShutterButton<x> <a> up (same as ShutterButton<x> <a> 100 50 74 100 0 0 0 1 1) Single press will move shutter up to 100%, double press to 50% and tripple press to 74%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> up to 100%. Easy setup for an "down" button: ShutterButton<x> <a> down (same as ShutterButton<x> <a> 0 50 24 0 0 0 0 1 1) Single press will move shutter down to 0%, double press to 50% and tripple press to 24%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> down to 0%. Easy setup for an "updown" button: ShutterButton<x> <a> updown (same as ShutterButton<x> <a> 100 0 50 - 0 0 0 0 0) Single press will move shutter up to 100%, double press down to 0% and tripple press to 50%. No hold action and no other shutter control by MQTT.
2020-01-02 10:23:11 +00:00
Button.press_counter[button_index] = (Button.window_timer[button_index]) ? Button.press_counter[button_index] +1 : 1;
// Button.window_timer[button_index] = (Button.press_counter[button_index]==1) ? loops_per_second / 2 : loops_per_second; // 0.5 second multi press window after 1st press, 1s afterwards
2020-03-10 07:41:37 +00:00
Button.window_timer[button_index] = (loops_per_second >> 2) * 3; // 0.75 second multi press window
}
Initial support for shutter button control New command "ShutterButton<x> <a> <b> <c> <d> <e> <f> <g> <h> <i> <j>" added that allows to assign a tasmota button <x> to control shutter <a>. Single press button shutter is set to position <b>. Double press button shutter is set to position <c>. Tripple press button shutter is set to position <d>. Hold button shutter is set to position <e>. Disabling any button action is given by <b> ... <e> equal to "-". Any press of the button while the shutter is moving will immediately stop that shutter. Global steering of all your shutters at home is supported by MQTT. By any button action an MQTT command can be initiated to the <grouptopic> of the device. For single press button this can be enabled by <f> equal to "1". Disabling is indicated by <f> equal to "0". Double to hold actions are given by <g> ... <i>, correspondingly. When <j> is equal to "0" only "cmnd/<grouptopic>/Shutterposition<y> ..." with <y>=<x> is fired. When <j> is equal to "1" <y>=1...4 is used to control any shutter number of a tasmota device having same <grouptopic>. Easy setup for an "up" button: ShutterButton<x> <a> up (same as ShutterButton<x> <a> 100 50 74 100 0 0 0 1 1) Single press will move shutter up to 100%, double press to 50% and tripple press to 74%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> up to 100%. Easy setup for an "down" button: ShutterButton<x> <a> down (same as ShutterButton<x> <a> 0 50 24 0 0 0 0 1 1) Single press will move shutter down to 0%, double press to 50% and tripple press to 24%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> down to 0%. Easy setup for an "updown" button: ShutterButton<x> <a> updown (same as ShutterButton<x> <a> 100 0 50 - 0 0 0 0 0) Single press will move shutter up to 100%, double press down to 0% and tripple press to 50%. No hold action and no other shutter control by MQTT.
2020-01-02 10:23:11 +00:00
}
2020-10-29 11:39:44 +00:00
TasmotaGlobal.blinks = 201;
Initial support for shutter button control New command "ShutterButton<x> <a> <b> <c> <d> <e> <f> <g> <h> <i> <j>" added that allows to assign a tasmota button <x> to control shutter <a>. Single press button shutter is set to position <b>. Double press button shutter is set to position <c>. Tripple press button shutter is set to position <d>. Hold button shutter is set to position <e>. Disabling any button action is given by <b> ... <e> equal to "-". Any press of the button while the shutter is moving will immediately stop that shutter. Global steering of all your shutters at home is supported by MQTT. By any button action an MQTT command can be initiated to the <grouptopic> of the device. For single press button this can be enabled by <f> equal to "1". Disabling is indicated by <f> equal to "0". Double to hold actions are given by <g> ... <i>, correspondingly. When <j> is equal to "0" only "cmnd/<grouptopic>/Shutterposition<y> ..." with <y>=<x> is fired. When <j> is equal to "1" <y>=1...4 is used to control any shutter number of a tasmota device having same <grouptopic>. Easy setup for an "up" button: ShutterButton<x> <a> up (same as ShutterButton<x> <a> 100 50 74 100 0 0 0 1 1) Single press will move shutter up to 100%, double press to 50% and tripple press to 74%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> up to 100%. Easy setup for an "down" button: ShutterButton<x> <a> down (same as ShutterButton<x> <a> 0 50 24 0 0 0 0 1 1) Single press will move shutter down to 0%, double press to 50% and tripple press to 24%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> down to 0%. Easy setup for an "updown" button: ShutterButton<x> <a> updown (same as ShutterButton<x> <a> 100 0 50 - 0 0 0 0 0) Single press will move shutter up to 100%, double press down to 0% and tripple press to 50%. No hold action and no other shutter control by MQTT.
2020-01-02 10:23:11 +00:00
}
if (NOT_PRESSED == button) {
Button.hold_timer[button_index] = 0;
} else {
Button.hold_timer[button_index]++;
2021-06-11 17:14:12 +01:00
if (!Settings->flag.button_single) { // SetOption13 (0) - Allow only single button press for immediate action
if (Settings->param[P_HOLD_IGNORE] > 0) { // SetOption40 (0) - Do not ignore button hold
if (Button.hold_timer[button_index] > loops_per_second * Settings->param[P_HOLD_IGNORE] / 10) {
Initial support for shutter button control New command "ShutterButton<x> <a> <b> <c> <d> <e> <f> <g> <h> <i> <j>" added that allows to assign a tasmota button <x> to control shutter <a>. Single press button shutter is set to position <b>. Double press button shutter is set to position <c>. Tripple press button shutter is set to position <d>. Hold button shutter is set to position <e>. Disabling any button action is given by <b> ... <e> equal to "-". Any press of the button while the shutter is moving will immediately stop that shutter. Global steering of all your shutters at home is supported by MQTT. By any button action an MQTT command can be initiated to the <grouptopic> of the device. For single press button this can be enabled by <f> equal to "1". Disabling is indicated by <f> equal to "0". Double to hold actions are given by <g> ... <i>, correspondingly. When <j> is equal to "0" only "cmnd/<grouptopic>/Shutterposition<y> ..." with <y>=<x> is fired. When <j> is equal to "1" <y>=1...4 is used to control any shutter number of a tasmota device having same <grouptopic>. Easy setup for an "up" button: ShutterButton<x> <a> up (same as ShutterButton<x> <a> 100 50 74 100 0 0 0 1 1) Single press will move shutter up to 100%, double press to 50% and tripple press to 74%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> up to 100%. Easy setup for an "down" button: ShutterButton<x> <a> down (same as ShutterButton<x> <a> 0 50 24 0 0 0 0 1 1) Single press will move shutter down to 0%, double press to 50% and tripple press to 24%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> down to 0%. Easy setup for an "updown" button: ShutterButton<x> <a> updown (same as ShutterButton<x> <a> 100 0 50 - 0 0 0 0 0) Single press will move shutter up to 100%, double press down to 0% and tripple press to 50%. No hold action and no other shutter control by MQTT.
2020-01-02 10:23:11 +00:00
Button.hold_timer[button_index] = 0; // Reset button hold counter to stay below hold trigger
Button.press_counter[button_index] = 0; // Discard button press to disable functionality
}
}
2021-06-11 17:14:12 +01:00
if ((Button.press_counter[button_index]<99) && (Button.hold_timer[button_index] == loops_per_second * Settings->param[P_HOLD_TIME] / 10)) { // press still valid && SetOption32 (40) - Button hold
2020-01-22 12:23:59 +00:00
// check for simultaneous shutter button hold
if (ShutterButtonIsSimultaneousHold(button_index, shutter_index)) {
2020-01-22 12:23:59 +00:00
// simultaneous shutter button hold detected
for (uint32_t i = 0; i < MAX_SHUTTER_KEYS; i++)
2021-06-11 17:14:12 +01:00
if ((Settings->shutter_button[i] & (1<<31)) && ((Settings->shutter_button[i] & 0x03) == shutter_index))
2020-01-22 12:23:59 +00:00
Button.press_counter[i] = 99; // Remember to discard further action for press & hold within button timings
press_index = 0;
buttonState = SHT_PRESSED_HOLD_SIMULTANEOUS;
}
2020-01-22 12:23:59 +00:00
if (Button.press_counter[button_index]<99) {
press_index = 0;
Initial support for shutter button control New command "ShutterButton<x> <a> <b> <c> <d> <e> <f> <g> <h> <i> <j>" added that allows to assign a tasmota button <x> to control shutter <a>. Single press button shutter is set to position <b>. Double press button shutter is set to position <c>. Tripple press button shutter is set to position <d>. Hold button shutter is set to position <e>. Disabling any button action is given by <b> ... <e> equal to "-". Any press of the button while the shutter is moving will immediately stop that shutter. Global steering of all your shutters at home is supported by MQTT. By any button action an MQTT command can be initiated to the <grouptopic> of the device. For single press button this can be enabled by <f> equal to "1". Disabling is indicated by <f> equal to "0". Double to hold actions are given by <g> ... <i>, correspondingly. When <j> is equal to "0" only "cmnd/<grouptopic>/Shutterposition<y> ..." with <y>=<x> is fired. When <j> is equal to "1" <y>=1...4 is used to control any shutter number of a tasmota device having same <grouptopic>. Easy setup for an "up" button: ShutterButton<x> <a> up (same as ShutterButton<x> <a> 100 50 74 100 0 0 0 1 1) Single press will move shutter up to 100%, double press to 50% and tripple press to 74%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> up to 100%. Easy setup for an "down" button: ShutterButton<x> <a> down (same as ShutterButton<x> <a> 0 50 24 0 0 0 0 1 1) Single press will move shutter down to 0%, double press to 50% and tripple press to 24%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> down to 0%. Easy setup for an "updown" button: ShutterButton<x> <a> updown (same as ShutterButton<x> <a> 100 0 50 - 0 0 0 0 0) Single press will move shutter up to 100%, double press down to 0% and tripple press to 50%. No hold action and no other shutter control by MQTT.
2020-01-02 10:23:11 +00:00
buttonState = SHT_PRESSED_HOLD;
2020-01-22 12:23:59 +00:00
}
Initial support for shutter button control New command "ShutterButton<x> <a> <b> <c> <d> <e> <f> <g> <h> <i> <j>" added that allows to assign a tasmota button <x> to control shutter <a>. Single press button shutter is set to position <b>. Double press button shutter is set to position <c>. Tripple press button shutter is set to position <d>. Hold button shutter is set to position <e>. Disabling any button action is given by <b> ... <e> equal to "-". Any press of the button while the shutter is moving will immediately stop that shutter. Global steering of all your shutters at home is supported by MQTT. By any button action an MQTT command can be initiated to the <grouptopic> of the device. For single press button this can be enabled by <f> equal to "1". Disabling is indicated by <f> equal to "0". Double to hold actions are given by <g> ... <i>, correspondingly. When <j> is equal to "0" only "cmnd/<grouptopic>/Shutterposition<y> ..." with <y>=<x> is fired. When <j> is equal to "1" <y>=1...4 is used to control any shutter number of a tasmota device having same <grouptopic>. Easy setup for an "up" button: ShutterButton<x> <a> up (same as ShutterButton<x> <a> 100 50 74 100 0 0 0 1 1) Single press will move shutter up to 100%, double press to 50% and tripple press to 74%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> up to 100%. Easy setup for an "down" button: ShutterButton<x> <a> down (same as ShutterButton<x> <a> 0 50 24 0 0 0 0 1 1) Single press will move shutter down to 0%, double press to 50% and tripple press to 24%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> down to 0%. Easy setup for an "updown" button: ShutterButton<x> <a> updown (same as ShutterButton<x> <a> 100 0 50 - 0 0 0 0 0) Single press will move shutter up to 100%, double press down to 0% and tripple press to 50%. No hold action and no other shutter control by MQTT.
2020-01-02 10:23:11 +00:00
Button.press_counter[button_index] = 0;
}
2021-06-11 17:14:12 +01:00
if ((Button.press_counter[button_index]==0) && (Button.hold_timer[button_index] == loops_per_second * IMMINENT_RESET_FACTOR * Settings->param[P_HOLD_TIME] / 10)) { // SetOption32 (40) - Button held for factor times longer
press_index = -1;
// check for simultaneous shutter button extend hold
if (ShutterButtonIsSimultaneousHold(button_index, shutter_index)) {
// simultaneous shutter button extend hold detected
buttonState = SHT_PRESSED_EXT_HOLD_SIMULTANEOUS;
} else {
buttonState = SHT_PRESSED_EXT_HOLD;
}
}
Initial support for shutter button control New command "ShutterButton<x> <a> <b> <c> <d> <e> <f> <g> <h> <i> <j>" added that allows to assign a tasmota button <x> to control shutter <a>. Single press button shutter is set to position <b>. Double press button shutter is set to position <c>. Tripple press button shutter is set to position <d>. Hold button shutter is set to position <e>. Disabling any button action is given by <b> ... <e> equal to "-". Any press of the button while the shutter is moving will immediately stop that shutter. Global steering of all your shutters at home is supported by MQTT. By any button action an MQTT command can be initiated to the <grouptopic> of the device. For single press button this can be enabled by <f> equal to "1". Disabling is indicated by <f> equal to "0". Double to hold actions are given by <g> ... <i>, correspondingly. When <j> is equal to "0" only "cmnd/<grouptopic>/Shutterposition<y> ..." with <y>=<x> is fired. When <j> is equal to "1" <y>=1...4 is used to control any shutter number of a tasmota device having same <grouptopic>. Easy setup for an "up" button: ShutterButton<x> <a> up (same as ShutterButton<x> <a> 100 50 74 100 0 0 0 1 1) Single press will move shutter up to 100%, double press to 50% and tripple press to 74%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> up to 100%. Easy setup for an "down" button: ShutterButton<x> <a> down (same as ShutterButton<x> <a> 0 50 24 0 0 0 0 1 1) Single press will move shutter down to 0%, double press to 50% and tripple press to 24%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> down to 0%. Easy setup for an "updown" button: ShutterButton<x> <a> updown (same as ShutterButton<x> <a> 100 0 50 - 0 0 0 0 0) Single press will move shutter up to 100%, double press down to 0% and tripple press to 50%. No hold action and no other shutter control by MQTT.
2020-01-02 10:23:11 +00:00
}
}
2021-06-11 17:14:12 +01:00
if (!Settings->flag.button_single) { // SetOption13 (0) - Allow multi-press
Initial support for shutter button control New command "ShutterButton<x> <a> <b> <c> <d> <e> <f> <g> <h> <i> <j>" added that allows to assign a tasmota button <x> to control shutter <a>. Single press button shutter is set to position <b>. Double press button shutter is set to position <c>. Tripple press button shutter is set to position <d>. Hold button shutter is set to position <e>. Disabling any button action is given by <b> ... <e> equal to "-". Any press of the button while the shutter is moving will immediately stop that shutter. Global steering of all your shutters at home is supported by MQTT. By any button action an MQTT command can be initiated to the <grouptopic> of the device. For single press button this can be enabled by <f> equal to "1". Disabling is indicated by <f> equal to "0". Double to hold actions are given by <g> ... <i>, correspondingly. When <j> is equal to "0" only "cmnd/<grouptopic>/Shutterposition<y> ..." with <y>=<x> is fired. When <j> is equal to "1" <y>=1...4 is used to control any shutter number of a tasmota device having same <grouptopic>. Easy setup for an "up" button: ShutterButton<x> <a> up (same as ShutterButton<x> <a> 100 50 74 100 0 0 0 1 1) Single press will move shutter up to 100%, double press to 50% and tripple press to 74%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> up to 100%. Easy setup for an "down" button: ShutterButton<x> <a> down (same as ShutterButton<x> <a> 0 50 24 0 0 0 0 1 1) Single press will move shutter down to 0%, double press to 50% and tripple press to 24%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> down to 0%. Easy setup for an "updown" button: ShutterButton<x> <a> updown (same as ShutterButton<x> <a> 100 0 50 - 0 0 0 0 0) Single press will move shutter up to 100%, double press down to 0% and tripple press to 50%. No hold action and no other shutter control by MQTT.
2020-01-02 10:23:11 +00:00
if (Button.window_timer[button_index]) {
Button.window_timer[button_index]--;
} else {
2020-10-29 11:21:24 +00:00
if (!TasmotaGlobal.restart_flag && !Button.hold_timer[button_index] && (Button.press_counter[button_index] > 0)) {
Initial support for shutter button control New command "ShutterButton<x> <a> <b> <c> <d> <e> <f> <g> <h> <i> <j>" added that allows to assign a tasmota button <x> to control shutter <a>. Single press button shutter is set to position <b>. Double press button shutter is set to position <c>. Tripple press button shutter is set to position <d>. Hold button shutter is set to position <e>. Disabling any button action is given by <b> ... <e> equal to "-". Any press of the button while the shutter is moving will immediately stop that shutter. Global steering of all your shutters at home is supported by MQTT. By any button action an MQTT command can be initiated to the <grouptopic> of the device. For single press button this can be enabled by <f> equal to "1". Disabling is indicated by <f> equal to "0". Double to hold actions are given by <g> ... <i>, correspondingly. When <j> is equal to "0" only "cmnd/<grouptopic>/Shutterposition<y> ..." with <y>=<x> is fired. When <j> is equal to "1" <y>=1...4 is used to control any shutter number of a tasmota device having same <grouptopic>. Easy setup for an "up" button: ShutterButton<x> <a> up (same as ShutterButton<x> <a> 100 50 74 100 0 0 0 1 1) Single press will move shutter up to 100%, double press to 50% and tripple press to 74%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> up to 100%. Easy setup for an "down" button: ShutterButton<x> <a> down (same as ShutterButton<x> <a> 0 50 24 0 0 0 0 1 1) Single press will move shutter down to 0%, double press to 50% and tripple press to 24%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> down to 0%. Easy setup for an "updown" button: ShutterButton<x> <a> updown (same as ShutterButton<x> <a> 100 0 50 - 0 0 0 0 0) Single press will move shutter up to 100%, double press down to 0% and tripple press to 50%. No hold action and no other shutter control by MQTT.
2020-01-02 10:23:11 +00:00
if (Button.press_counter[button_index]<99) {
2020-01-22 12:23:59 +00:00
// check for simultaneous shutter button press
uint32 min_shutterbutton_press_counter = -1; // -1 == max(uint32)
for (uint32_t i = 0; i < MAX_SHUTTER_KEYS; i++) {
2021-02-18 11:42:59 +00:00
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: ShutterButton[i] %ld, ShutterIndex %d, ButtonPressCounter[i] %d, minShutterButtonPressCounter %d, i %d"),
2021-06-11 17:14:12 +01:00
Settings->shutter_button[i], shutter_index, Button.press_counter[i] , min_shutterbutton_press_counter, i);
if ((button_index != i) && (Settings->shutter_button[i] & (1<<31)) && ((Settings->shutter_button[i] & 0x03) == shutter_index) && (i != button_index) && (Button.press_counter[i] < min_shutterbutton_press_counter)) {
2020-01-22 12:23:59 +00:00
min_shutterbutton_press_counter = Button.press_counter[i];
2021-01-23 15:26:23 +00:00
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: minShutterButtonPressCounter %d"), min_shutterbutton_press_counter);
}
2020-01-22 12:23:59 +00:00
}
if (min_shutterbutton_press_counter == Button.press_counter[button_index]) {
// simultaneous shutter button press detected
2021-01-23 15:26:23 +00:00
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Simultanous press detected"));
2020-01-22 12:23:59 +00:00
press_index = Button.press_counter[button_index];
for (uint32_t i = 0; i < MAX_SHUTTER_KEYS; i++)
2021-06-11 17:14:12 +01:00
if ((Settings->shutter_button[i] & (1<<31)) && ((Settings->shutter_button[i] & 0x03) != shutter_index))
2020-01-22 12:23:59 +00:00
Button.press_counter[i] = 99; // Remember to discard further action for press & hold within button timings
buttonState = SHT_PRESSED_MULTI_SIMULTANEOUS;
}
2020-01-22 12:23:59 +00:00
if ((buttonState != SHT_PRESSED_MULTI_SIMULTANEOUS) && (Button.press_counter[button_index]<99)) {
// no simultaneous shutter button press >3 detected
2020-01-22 12:23:59 +00:00
press_index = Button.press_counter[button_index];
buttonState = SHT_PRESSED_MULTI;
}
Initial support for shutter button control New command "ShutterButton<x> <a> <b> <c> <d> <e> <f> <g> <h> <i> <j>" added that allows to assign a tasmota button <x> to control shutter <a>. Single press button shutter is set to position <b>. Double press button shutter is set to position <c>. Tripple press button shutter is set to position <d>. Hold button shutter is set to position <e>. Disabling any button action is given by <b> ... <e> equal to "-". Any press of the button while the shutter is moving will immediately stop that shutter. Global steering of all your shutters at home is supported by MQTT. By any button action an MQTT command can be initiated to the <grouptopic> of the device. For single press button this can be enabled by <f> equal to "1". Disabling is indicated by <f> equal to "0". Double to hold actions are given by <g> ... <i>, correspondingly. When <j> is equal to "0" only "cmnd/<grouptopic>/Shutterposition<y> ..." with <y>=<x> is fired. When <j> is equal to "1" <y>=1...4 is used to control any shutter number of a tasmota device having same <grouptopic>. Easy setup for an "up" button: ShutterButton<x> <a> up (same as ShutterButton<x> <a> 100 50 74 100 0 0 0 1 1) Single press will move shutter up to 100%, double press to 50% and tripple press to 74%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> up to 100%. Easy setup for an "down" button: ShutterButton<x> <a> down (same as ShutterButton<x> <a> 0 50 24 0 0 0 0 1 1) Single press will move shutter down to 0%, double press to 50% and tripple press to 24%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> down to 0%. Easy setup for an "updown" button: ShutterButton<x> <a> updown (same as ShutterButton<x> <a> 100 0 50 - 0 0 0 0 0) Single press will move shutter up to 100%, double press down to 0% and tripple press to 50%. No hold action and no other shutter control by MQTT.
2020-01-02 10:23:11 +00:00
}
Button.press_counter[button_index] = 0;
}
}
}
if (buttonState != SHT_NOT_PRESSED) {
2021-06-11 17:14:12 +01:00
if ((!Settings->flag.button_restrict) && (((press_index>=5) && (press_index<=7)) || (buttonState == SHT_PRESSED_EXT_HOLD) || (buttonState == SHT_PRESSED_EXT_HOLD_SIMULTANEOUS))){
// check number of buttons for this shutter
uint8_t shutter_index_num_buttons = 0;
for (uint32_t i = 0; i < MAX_SHUTTER_KEYS; i++) {
2021-06-11 17:14:12 +01:00
if ((Settings->shutter_button[i] & (1<<31)) && ((Settings->shutter_button[i] & 0x03) == shutter_index)) {
shutter_index_num_buttons++;
}
}
if ((buttonState == SHT_PRESSED_MULTI_SIMULTANEOUS) || ((shutter_index_num_buttons==1) && (buttonState == SHT_PRESSED_MULTI))){
// 5x..7x && no SetOption1 (0) checked above
// simultaneous or stand alone button press 5x, 6x, 7x detected
char scmnd[20];
2020-04-21 08:41:35 +01:00
snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_WIFICONFIG " 2"));
ExecuteCommand(scmnd, SRC_BUTTON);
return;
} else if ((buttonState == SHT_PRESSED_EXT_HOLD_SIMULTANEOUS) || ((shutter_index_num_buttons==1) && (buttonState == SHT_PRESSED_EXT_HOLD))){
// no SetOption1 (0) checked above
// simultaneous or stand alone button extended hold detected
char scmnd[20];
snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_RESET " 1"));
ExecuteCommand(scmnd, SRC_BUTTON);
return;
}
}
if (buttonState <= SHT_PRESSED_IMMEDIATE) {
2021-06-11 17:14:12 +01:00
if (Settings->shutter_startrelay[shutter_index] && Settings->shutter_startrelay[shutter_index] <9) {
2020-01-22 12:23:59 +00:00
uint8_t pos_press_index = (buttonState == SHT_PRESSED_HOLD) ? 3 : (press_index-1);
if (pos_press_index>3) pos_press_index=3;
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shtr%d, Button %d = %d (single=1, double=2, tripple=3, hold=4)"), shutter_index+1, button_index+1, pos_press_index+1);
2020-01-22 12:23:59 +00:00
XdrvMailbox.index = shutter_index +1;
2020-10-30 11:29:48 +00:00
TasmotaGlobal.last_source = SRC_BUTTON;
2020-01-22 12:23:59 +00:00
XdrvMailbox.data_len = 0;
char databuf[1] = "";
XdrvMailbox.data = databuf;
XdrvMailbox.command = NULL;
if (buttonState == SHT_PRESSED_IMMEDIATE) {
XdrvMailbox.payload = XdrvMailbox.index;
CmndShutterStop();
} else {
2021-06-11 17:14:12 +01:00
uint8_t position = (Settings->shutter_button[button_index]>>(6*pos_press_index + 2)) & 0x03f;
2020-01-22 12:23:59 +00:00
if (position) {
2020-09-09 13:04:57 +01:00
if (Shutter[shutter_index].direction) {
2020-01-22 12:23:59 +00:00
XdrvMailbox.payload = XdrvMailbox.index;
CmndShutterStop();
} else {
XdrvMailbox.payload = position = (position-1)<<1;
2021-02-18 11:42:59 +00:00
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shtr%d -> %d"), shutter_index+1, position);
if (102 == position) {
XdrvMailbox.payload = XdrvMailbox.index;
CmndShutterToggle();
} else {
CmndShutterPosition();
}
2021-06-11 17:14:12 +01:00
if (Settings->shutter_button[button_index] & ((0x01<<26)<<pos_press_index)) {
2020-01-22 12:23:59 +00:00
// MQTT broadcast to grouptopic
char scommand[CMDSZ];
char stopic[TOPSZ];
for (uint32_t i = 0; i < MAX_SHUTTERS; i++) {
2021-06-11 17:14:12 +01:00
if ((i==shutter_index) || (Settings->shutter_button[button_index] & (0x01<<30))) {
2020-01-22 12:23:59 +00:00
snprintf_P(scommand, sizeof(scommand),PSTR("ShutterPosition%d"), i+1);
GetGroupTopic_P(stopic, scommand, SET_MQTT_GRP_TOPIC);
2020-01-22 12:23:59 +00:00
Response_P("%d", position);
MqttPublish(stopic, false);
}
} // for (uint32_t)
2021-06-11 17:14:12 +01:00
} // if (Settings->shutter)
} // ende else
} // if (position)
} // end else
2021-06-11 17:14:12 +01:00
} // if if (Settings->shutter_startrelay[shutter_index]
Initial support for shutter button control New command "ShutterButton<x> <a> <b> <c> <d> <e> <f> <g> <h> <i> <j>" added that allows to assign a tasmota button <x> to control shutter <a>. Single press button shutter is set to position <b>. Double press button shutter is set to position <c>. Tripple press button shutter is set to position <d>. Hold button shutter is set to position <e>. Disabling any button action is given by <b> ... <e> equal to "-". Any press of the button while the shutter is moving will immediately stop that shutter. Global steering of all your shutters at home is supported by MQTT. By any button action an MQTT command can be initiated to the <grouptopic> of the device. For single press button this can be enabled by <f> equal to "1". Disabling is indicated by <f> equal to "0". Double to hold actions are given by <g> ... <i>, correspondingly. When <j> is equal to "0" only "cmnd/<grouptopic>/Shutterposition<y> ..." with <y>=<x> is fired. When <j> is equal to "1" <y>=1...4 is used to control any shutter number of a tasmota device having same <grouptopic>. Easy setup for an "up" button: ShutterButton<x> <a> up (same as ShutterButton<x> <a> 100 50 74 100 0 0 0 1 1) Single press will move shutter up to 100%, double press to 50% and tripple press to 74%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> up to 100%. Easy setup for an "down" button: ShutterButton<x> <a> down (same as ShutterButton<x> <a> 0 50 24 0 0 0 0 1 1) Single press will move shutter down to 0%, double press to 50% and tripple press to 24%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> down to 0%. Easy setup for an "updown" button: ShutterButton<x> <a> updown (same as ShutterButton<x> <a> 100 0 50 - 0 0 0 0 0) Single press will move shutter up to 100%, double press down to 0% and tripple press to 50%. No hold action and no other shutter control by MQTT.
2020-01-02 10:23:11 +00:00
}
2020-01-22 12:23:59 +00:00
Response_P(PSTR("{"));
ResponseAppend_P(JSON_SHUTTER_BUTTON, shutter_index+1, (buttonState <= SHT_PRESSED_EXT_HOLD) ? (button_index+1) : 0, press_index);
2020-01-22 12:23:59 +00:00
ResponseJsonEnd();
2020-07-20 16:24:51 +01:00
MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_STAT, PSTR(D_PRFX_SHUTTER));
Initial support for shutter button control New command "ShutterButton<x> <a> <b> <c> <d> <e> <f> <g> <h> <i> <j>" added that allows to assign a tasmota button <x> to control shutter <a>. Single press button shutter is set to position <b>. Double press button shutter is set to position <c>. Tripple press button shutter is set to position <d>. Hold button shutter is set to position <e>. Disabling any button action is given by <b> ... <e> equal to "-". Any press of the button while the shutter is moving will immediately stop that shutter. Global steering of all your shutters at home is supported by MQTT. By any button action an MQTT command can be initiated to the <grouptopic> of the device. For single press button this can be enabled by <f> equal to "1". Disabling is indicated by <f> equal to "0". Double to hold actions are given by <g> ... <i>, correspondingly. When <j> is equal to "0" only "cmnd/<grouptopic>/Shutterposition<y> ..." with <y>=<x> is fired. When <j> is equal to "1" <y>=1...4 is used to control any shutter number of a tasmota device having same <grouptopic>. Easy setup for an "up" button: ShutterButton<x> <a> up (same as ShutterButton<x> <a> 100 50 74 100 0 0 0 1 1) Single press will move shutter up to 100%, double press to 50% and tripple press to 74%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> up to 100%. Easy setup for an "down" button: ShutterButton<x> <a> down (same as ShutterButton<x> <a> 0 50 24 0 0 0 0 1 1) Single press will move shutter down to 0%, double press to 50% and tripple press to 24%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> down to 0%. Easy setup for an "updown" button: ShutterButton<x> <a> updown (same as ShutterButton<x> <a> 100 0 50 - 0 0 0 0 0) Single press will move shutter up to 100%, double press down to 0% and tripple press to 50%. No hold action and no other shutter control by MQTT.
2020-01-02 10:23:11 +00:00
}
}
2020-01-04 14:09:57 +00:00
void ShutterSetPosition(uint32_t device, uint32_t position)
{
char svalue[32]; // Command and number parameter
snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_POSITION "%d %d"), device, position);
2020-09-05 19:39:24 +01:00
ExecuteCommand(svalue, SRC_SHUTTER);
}
void ShutterToggle(bool dir)
{
2021-11-16 12:46:22 +00:00
AddLog(LOG_LEVEL_DEBUG, PSTR("SHT: Toggle: %d, i %d, dir %d, lastdir %d"), XdrvMailbox.payload, XdrvMailbox.index, dir, Shutter[XdrvMailbox.index-1].lastdirection);
if ((1 == XdrvMailbox.index) && (XdrvMailbox.payload != -99)) {
XdrvMailbox.index = XdrvMailbox.payload;
}
2020-10-30 11:29:48 +00:00
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= TasmotaGlobal.shutters_present)) {
uint32_t index = XdrvMailbox.index-1;
if (dir) {
2021-02-18 11:42:59 +00:00
XdrvMailbox.payload = (Shutter[index].direction==0 ? ((Shutter[index].lastdirection > 0) ? 0 : 100) : (Shutter[index].direction > 0) ? 0 : 100);
}
else {
2020-09-09 13:04:57 +01:00
XdrvMailbox.payload = (50 < ShutterRealToPercentPosition(Shutter[index].real_position, index)) ? 0 : 100;
}
XdrvMailbox.data_len = 0;
2020-10-30 11:29:48 +00:00
TasmotaGlobal.last_source = SRC_WEBGUI;
CmndShutterPosition();
}
}
/*********************************************************************************************\
* Commands
\*********************************************************************************************/
void CmndShutterOpen(void)
{
2021-01-23 15:26:23 +00:00
//AddLog(LOG_LEVEL_DEBUG, PSTR("SHT: Payload open: %d, i %d"), XdrvMailbox.payload, XdrvMailbox.index);
2020-01-04 14:09:57 +00:00
if ((1 == XdrvMailbox.index) && (XdrvMailbox.payload != -99)) {
XdrvMailbox.index = XdrvMailbox.payload;
}
XdrvMailbox.payload = 100;
2020-10-30 11:29:48 +00:00
TasmotaGlobal.last_source = SRC_WEBGUI;
CmndShutterPosition();
}
void CmndShutterStopOpen(void)
{
2020-10-30 11:29:48 +00:00
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= TasmotaGlobal.shutters_present)) {
uint32_t index = XdrvMailbox.index-1;
2020-09-09 13:04:57 +01:00
if (Shutter[index].direction) {
CmndShutterStop();
} else {
CmndShutterOpen();
}
}
}
void CmndShutterClose(void)
{
2021-01-23 15:26:23 +00:00
//AddLog(LOG_LEVEL_DEBUG, PSTR("SHT: Payload close: %d, i %d"), XdrvMailbox.payload, XdrvMailbox.index);
2020-01-04 14:09:57 +00:00
if ((1 == XdrvMailbox.index) && (XdrvMailbox.payload != -99)) {
XdrvMailbox.index = XdrvMailbox.payload;
}
XdrvMailbox.payload = 0;
XdrvMailbox.data_len = 0;
2020-10-30 11:29:48 +00:00
TasmotaGlobal.last_source = SRC_WEBGUI;
CmndShutterPosition();
}
void CmndShutterStopClose(void)
{
2020-10-30 11:29:48 +00:00
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= TasmotaGlobal.shutters_present)) {
uint32_t index = XdrvMailbox.index-1;
2020-09-09 13:04:57 +01:00
if (Shutter[index].direction) {
CmndShutterStop();
} else {
CmndShutterClose();
}
}
}
void CmndShutterToggle(void)
{
ShutterToggle(false);
}
void CmndShutterToggleDir(void)
{
ShutterToggle(true);
}
void CmndShutterStopToggle(void)
{
2020-10-30 11:29:48 +00:00
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= TasmotaGlobal.shutters_present)) {
uint32_t index = XdrvMailbox.index-1;
2020-09-09 13:04:57 +01:00
if (Shutter[index].direction) {
CmndShutterStop();
} else {
CmndShutterToggle();
}
}
}
void CmndShutterStopToggleDir(void)
{
2020-10-30 11:29:48 +00:00
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= TasmotaGlobal.shutters_present)) {
uint32_t index = XdrvMailbox.index-1;
2020-09-09 13:04:57 +01:00
if (Shutter[index].direction) {
CmndShutterStop();
} else {
CmndShutterToggleDir();
}
}
}
void CmndShutterStop(void)
{
2020-10-30 11:29:48 +00:00
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= TasmotaGlobal.shutters_present)) {
2021-06-11 17:14:12 +01:00
if (!(Settings->shutter_options[XdrvMailbox.index-1] & 2)) {
if ((1 == XdrvMailbox.index) && (XdrvMailbox.payload != -99)) {
XdrvMailbox.index = XdrvMailbox.payload;
}
uint32_t i = XdrvMailbox.index -1;
2020-09-09 13:04:57 +01:00
if (Shutter[i].direction != 0) {
2021-01-23 15:26:23 +00:00
AddLog(LOG_LEVEL_DEBUG, PSTR("SHT: Stop moving %d: dir: %d"), XdrvMailbox.index, Shutter[i].direction);
Shutter[i].target_position = Shutter[i].real_position;
2021-01-01 12:44:04 +00:00
}
if (XdrvMailbox.command)
ResponseCmndDone();
2021-02-18 11:42:59 +00:00
ShutterUpdatePosition();
} else {
if (XdrvMailbox.command)
ResponseCmndIdxChar("Locked");
}
}
}
void CmndShutterIncDec(void)
{
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Change in: payload %s (%d), payload %d, idx %d, src %d"), XdrvMailbox.data , XdrvMailbox.data_len, XdrvMailbox.payload , XdrvMailbox.index, TasmotaGlobal.last_source );
2020-10-30 11:29:48 +00:00
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= TasmotaGlobal.shutters_present)) {
if (XdrvMailbox.data_len > 0) {
XdrvMailbox.payload = ShutterRealToPercentPosition(Shutter[XdrvMailbox.index-1].target_position, XdrvMailbox.index-1)+XdrvMailbox.payload;
// limit position to boundaries
XdrvMailbox.payload = XdrvMailbox.payload < 0 ? 0 : (XdrvMailbox.payload > 100 ? 100 : XdrvMailbox.payload);
CmndShutterPosition();
}
}
}
void CmndShutterPosition(void)
{
2020-10-30 11:29:48 +00:00
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= TasmotaGlobal.shutters_present)) {
2021-06-11 17:14:12 +01:00
if (!(Settings->shutter_options[XdrvMailbox.index-1] & 2)) {
uint32_t index = XdrvMailbox.index-1;
//limit the payload
2021-01-23 15:26:23 +00:00
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Pos. in: payload %s (%d), payload %d, idx %d, src %d"), XdrvMailbox.data , XdrvMailbox.data_len, XdrvMailbox.payload , XdrvMailbox.index, TasmotaGlobal.last_source );
// value 0 with data_len > 0 can mean Open
// special handling fo UP,DOWN,TOGGLE,STOP command comming with payload -99
if ((XdrvMailbox.data_len > 1) && (XdrvMailbox.payload <= 0)) {
//UpperCase(XdrvMailbox.data, XdrvMailbox.data);
2020-09-09 13:04:57 +01:00
if (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_UP) || !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_OPEN) || ((Shutter[index].direction==0) && !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_STOPOPEN))) {
CmndShutterOpen();
return;
}
2020-09-09 13:04:57 +01:00
if (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_DOWN) || !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_CLOSE) || ((Shutter[index].direction==0) && !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_STOPCLOSE))) {
CmndShutterClose();
return;
}
if (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_TOGGLE)) {
CmndShutterToggle();
return;
}
if (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_TOGGLEDIR)) {
CmndShutterToggleDir();
return;
}
2020-09-09 13:04:57 +01:00
if (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_STOP) || ((Shutter[index].direction) && (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_STOPOPEN) || !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_STOPCLOSE)))) {
XdrvMailbox.payload = -99;
CmndShutterStop();
return;
}
}
2020-09-09 13:04:57 +01:00
int8_t target_pos_percent = (XdrvMailbox.payload < 0) ? (XdrvMailbox.payload == -99 ? ShutterRealToPercentPosition(Shutter[index].real_position, index) : 0) : ((XdrvMailbox.payload > 100) ? 100 : XdrvMailbox.payload);
// webgui still send also on inverted shutter the native position.
2021-06-11 17:14:12 +01:00
target_pos_percent = ((Settings->shutter_options[index] & 1) && (SRC_WEBGUI != TasmotaGlobal.last_source)) ? 100 - target_pos_percent : target_pos_percent;
if (XdrvMailbox.payload != -99) {
2021-06-11 17:14:12 +01:00
//target_pos_percent = (Settings->shutter_options[index] & 1) ? 100 - target_pos_percent : target_pos_percent;
2020-09-09 13:04:57 +01:00
Shutter[index].target_position = ShutterPercentToRealPosition(target_pos_percent, index);
//Shutter[i].accelerator[index] = ShutterGlobal.open_velocity_max / ((Shutter[i].motordelay[index] > 0) ? Shutter[i].motordelay[index] : 1);
2021-06-11 17:14:12 +01:00
//Shutter[i].target_position[index] = XdrvMailbox.payload < 5 ? Settings->shuttercoeff[2][index] * XdrvMailbox.payload : Settings->shuttercoeff[1][index] * XdrvMailbox.payload + Settings->shuttercoeff[0,index];
2021-11-07 14:53:12 +00:00
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: lastsource %d:, real %d, target %d, tiltreal: %d, tilttarget: %d, payload %d"), TasmotaGlobal.last_source, Shutter[index].real_position ,Shutter[index].target_position,Shutter[index].tilt_real_pos, Shutter[index].tilt_target_pos,target_pos_percent);
}
2021-11-07 14:53:12 +00:00
if ( (target_pos_percent >= 0) && (target_pos_percent <= 100) &&
(abs(Shutter[index].target_position - Shutter[index].real_position ) > Shutter[index].min_realPositionChange ||
abs(Shutter[index].tilt_target_pos - Shutter[index].tilt_real_pos ) > Shutter[index].min_TiltChange) ) {
2021-06-11 17:14:12 +01:00
if (Settings->shutter_options[index] & 4) {
2020-09-09 13:04:57 +01:00
if (0 == target_pos_percent) Shutter[index].target_position -= 1 * RESOLUTION * STEPS_PER_SECOND;
if (100 == target_pos_percent) Shutter[index].target_position += 1 * RESOLUTION * STEPS_PER_SECOND;
}
int8_t new_shutterdirection;
if (abs(Shutter[index].target_position - Shutter[index].real_position ) > Shutter[index].min_realPositionChange) {
new_shutterdirection = Shutter[index].real_position < Shutter[index].target_position ? 1 : -1;
2021-11-15 17:43:47 +00:00
Shutter[index].tiltmoving = 0;
} else {
2021-11-07 14:53:12 +00:00
new_shutterdirection = Shutter[index].tilt_real_pos < Shutter[index].tilt_target_pos ? 1 : -1;
2021-11-15 17:43:47 +00:00
Shutter[index].tiltmoving = 1;
2021-11-07 14:53:12 +00:00
}
2020-09-09 13:04:57 +01:00
if (Shutter[index].direction == -new_shutterdirection) {
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Stop shutter to reverse direction"));
2020-09-05 19:39:24 +01:00
ShutterPowerOff(index);
}
2020-09-09 13:04:57 +01:00
if (Shutter[index].direction != new_shutterdirection) {
ShutterStartInit(index, new_shutterdirection, Shutter[index].target_position);
uint8_t save_direction = Shutter[index].direction;
Shutter[index].direction = 0; // set temporary direction = 0 to avoid RTC timer sarting. Some delay may happen before shutter starts moving
2020-09-09 13:04:57 +01:00
switch (ShutterGlobal.position_mode) {
2020-09-05 19:39:24 +01:00
case SHT_COUNTER:
case SHT_PWM_TIME:
case SHT_PWM_VALUE:
case SHT_TIME_UP_DOWN:
2020-09-09 13:04:57 +01:00
if (!ShutterGlobal.skip_relay_change) {
2020-09-05 19:39:24 +01:00
// Code for shutters with circuit safe configuration, switch the direction Relay
2021-06-11 17:14:12 +01:00
ExecuteCommandPowerShutter(Settings->shutter_startrelay[index] +1, new_shutterdirection == 1 ? 0 : 1, SRC_SHUTTER);
delay(SHUTTER_RELAY_OPERATION_TIME);
2020-09-05 19:39:24 +01:00
// power on
2021-06-11 17:14:12 +01:00
ExecuteCommandPowerShutter(Settings->shutter_startrelay[index], 1, SRC_SHUTTER);
}
2021-06-11 17:14:12 +01:00
//if (ShutterGlobal.position_mode != SHT_TIME_UP_DOWN) ExecuteCommandPowerShutter(Settings->shutter_startrelay[index]+2, 1, SRC_SHUTTER);
2020-09-05 19:39:24 +01:00
break;
case SHT_TIME:
2020-09-09 13:04:57 +01:00
if (!ShutterGlobal.skip_relay_change) {
2021-06-11 17:14:12 +01:00
if ( (TasmotaGlobal.power >> (Settings->shutter_startrelay[index] -1)) & 3 > 0 ) {
ExecuteCommandPowerShutter(Settings->shutter_startrelay[index] + (new_shutterdirection == 1 ? 1 : 0), Shutter[index].switch_mode == SHT_SWITCH ? 0 : 1, SRC_SHUTTER);
2020-09-05 19:39:24 +01:00
}
2021-06-11 17:14:12 +01:00
ExecuteCommandPowerShutter(Settings->shutter_startrelay[index] + (new_shutterdirection == 1 ? 0 : 1), 1, SRC_SHUTTER);
2020-09-05 19:39:24 +01:00
}
break;
case SHT_TIME_GARAGE:
2020-09-09 13:04:57 +01:00
if (!ShutterGlobal.skip_relay_change) {
if (new_shutterdirection == Shutter[index].lastdirection) {
2021-01-23 15:26:23 +00:00
AddLog(LOG_LEVEL_INFO, PSTR("SHT: Garage not move in this direction: %d"), Shutter[index].switch_mode == SHT_PULSE);
2020-09-09 13:04:57 +01:00
for (uint8_t k=0 ; k <= (uint8_t)(Shutter[index].switch_mode == SHT_PULSE) ; k++) {
2021-06-11 17:14:12 +01:00
ExecuteCommandPowerShutter(Settings->shutter_startrelay[index], 1, SRC_SHUTTER);
delay(MOTOR_STOP_TIME);
2021-06-11 17:14:12 +01:00
ExecuteCommandPowerShutter(Settings->shutter_startrelay[index], 0, SRC_SHUTTER);
delay(MOTOR_STOP_TIME);
2020-09-05 19:39:24 +01:00
}
// reset shutter time to avoid 2 seconds above count as runtime
2020-09-09 13:04:57 +01:00
Shutter[index].time = 0;
} // if (new_shutterdirection == Shutter[i].lastdirection[index])
2021-06-11 17:14:12 +01:00
ExecuteCommandPowerShutter(Settings->shutter_startrelay[index], 1, SRC_SHUTTER);
2020-09-09 13:04:57 +01:00
} // if (!ShutterGlobal.skip_relay_change)
2020-09-05 19:39:24 +01:00
break;
2020-09-09 13:04:57 +01:00
} // switch (ShutterGlobal.position_mode)
Shutter[index].direction = save_direction;
2020-09-09 13:04:57 +01:00
ShutterGlobal.RelayCurrentMask = 0;
} // if (Shutter[i].direction[index] != new_shutterdirection)
} else {
2020-09-09 13:04:57 +01:00
target_pos_percent = ShutterRealToPercentPosition(Shutter[index].real_position, index);
ShutterReportPosition(true, index);
}
XdrvMailbox.index = index +1; // Fix random index for ShutterClose
if (XdrvMailbox.command)
2021-06-11 17:14:12 +01:00
ResponseCmndIdxNumber((Settings->shutter_options[index] & 1) ? 100 - target_pos_percent : target_pos_percent);
} else {
ShutterReportPosition(true, MAX_SHUTTERS);
if (XdrvMailbox.command)
ResponseCmndIdxChar("Locked");
}
}
}
void CmndShutterStopPosition(void)
{
2020-10-30 11:29:48 +00:00
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= TasmotaGlobal.shutters_present)) {
uint32_t index = XdrvMailbox.index-1;
2020-09-09 13:04:57 +01:00
if (Shutter[index].direction) {
XdrvMailbox.payload = -99;
CmndShutterStop();
} else {
CmndShutterPosition();
}
}
}
void CmndShutterOpenTime(void)
{
2020-10-30 11:29:48 +00:00
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= TasmotaGlobal.shutters_present)) {
if (XdrvMailbox.data_len > 0) {
2021-06-11 17:14:12 +01:00
Settings->shutter_opentime[XdrvMailbox.index -1] = (uint16_t)(10 * CharToFloat(XdrvMailbox.data));
ShutterInit();
}
char time_chr[10];
2021-06-11 17:14:12 +01:00
dtostrfd((float)(Settings->shutter_opentime[XdrvMailbox.index -1]) / 10, 1, time_chr);
ResponseCmndIdxChar(time_chr);
}
}
void CmndShutterCloseTime(void)
{
2020-10-30 11:29:48 +00:00
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= TasmotaGlobal.shutters_present)) {
if (XdrvMailbox.data_len > 0) {
2021-06-11 17:14:12 +01:00
Settings->shutter_closetime[XdrvMailbox.index -1] = (uint16_t)(10 * CharToFloat(XdrvMailbox.data));
ShutterInit();
}
char time_chr[10];
2021-06-11 17:14:12 +01:00
dtostrfd((float)(Settings->shutter_closetime[XdrvMailbox.index -1]) / 10, 1, time_chr);
ResponseCmndIdxChar(time_chr);
}
}
2019-10-30 08:28:46 +00:00
void CmndShutterMotorDelay(void)
{
2020-10-30 11:29:48 +00:00
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= TasmotaGlobal.shutters_present)) {
2019-10-30 08:28:46 +00:00
if (XdrvMailbox.data_len > 0) {
Settings->shutter_motordelay[XdrvMailbox.index -1] = (uint8_t)(STEPS_PER_SECOND * CharToFloat(XdrvMailbox.data));
2019-10-30 08:28:46 +00:00
ShutterInit();
2021-11-07 14:53:12 +00:00
//AddLog(LOG_LEVEL_DEBUG, PSTR("SHT: Shtr Init1. realdelay %d"),Shutter[XdrvMailbox.index -1].motordelay);
2019-10-30 08:28:46 +00:00
}
char time_chr[10];
dtostrfd((float)(Shutter[XdrvMailbox.index -1].motordelay) / STEPS_PER_SECOND, 2, time_chr);
2019-10-30 08:28:46 +00:00
ResponseCmndIdxChar(time_chr);
}
}
2020-09-05 19:39:24 +01:00
void CmndShutterMode(void)
{
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= MAX_MODES)) {
2020-09-09 13:04:57 +01:00
ShutterGlobal.position_mode = XdrvMailbox.payload;
2021-06-11 17:14:12 +01:00
Settings->shutter_mode = XdrvMailbox.payload;
2020-09-05 19:39:24 +01:00
ShutterInit();
}
2020-09-09 13:04:57 +01:00
ResponseCmndNumber(ShutterGlobal.position_mode);
2020-09-05 19:39:24 +01:00
}
void CmndShutterRelay(void)
{
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_SHUTTERS)) {
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 64)) {
2021-06-11 17:14:12 +01:00
Settings->shutter_startrelay[XdrvMailbox.index -1] = XdrvMailbox.payload;
if (XdrvMailbox.payload > 0) {
2020-09-09 13:04:57 +01:00
ShutterGlobal.RelayShutterMask |= 3 << (XdrvMailbox.payload - 1);
} else {
2021-06-11 17:14:12 +01:00
ShutterGlobal.RelayShutterMask ^= 3 << (Settings->shutter_startrelay[XdrvMailbox.index -1] - 1);
}
2021-06-11 17:14:12 +01:00
Settings->shutter_startrelay[XdrvMailbox.index -1] = XdrvMailbox.payload;
ShutterInit();
// if payload is 0 to disable the relay there must be a reboot. Otherwhise does not work
}
2021-06-11 17:14:12 +01:00
ResponseCmndIdxNumber(Settings->shutter_startrelay[XdrvMailbox.index -1]);
}
}
Initial support for shutter button control New command "ShutterButton<x> <a> <b> <c> <d> <e> <f> <g> <h> <i> <j>" added that allows to assign a tasmota button <x> to control shutter <a>. Single press button shutter is set to position <b>. Double press button shutter is set to position <c>. Tripple press button shutter is set to position <d>. Hold button shutter is set to position <e>. Disabling any button action is given by <b> ... <e> equal to "-". Any press of the button while the shutter is moving will immediately stop that shutter. Global steering of all your shutters at home is supported by MQTT. By any button action an MQTT command can be initiated to the <grouptopic> of the device. For single press button this can be enabled by <f> equal to "1". Disabling is indicated by <f> equal to "0". Double to hold actions are given by <g> ... <i>, correspondingly. When <j> is equal to "0" only "cmnd/<grouptopic>/Shutterposition<y> ..." with <y>=<x> is fired. When <j> is equal to "1" <y>=1...4 is used to control any shutter number of a tasmota device having same <grouptopic>. Easy setup for an "up" button: ShutterButton<x> <a> up (same as ShutterButton<x> <a> 100 50 74 100 0 0 0 1 1) Single press will move shutter up to 100%, double press to 50% and tripple press to 74%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> up to 100%. Easy setup for an "down" button: ShutterButton<x> <a> down (same as ShutterButton<x> <a> 0 50 24 0 0 0 0 1 1) Single press will move shutter down to 0%, double press to 50% and tripple press to 24%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> down to 0%. Easy setup for an "updown" button: ShutterButton<x> <a> updown (same as ShutterButton<x> <a> 100 0 50 - 0 0 0 0 0) Single press will move shutter up to 100%, double press down to 0% and tripple press to 50%. No hold action and no other shutter control by MQTT.
2020-01-02 10:23:11 +00:00
void CmndShutterButton(void)
{
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_SHUTTERS)) {
Initial support for shutter button control New command "ShutterButton<x> <a> <b> <c> <d> <e> <f> <g> <h> <i> <j>" added that allows to assign a tasmota button <x> to control shutter <a>. Single press button shutter is set to position <b>. Double press button shutter is set to position <c>. Tripple press button shutter is set to position <d>. Hold button shutter is set to position <e>. Disabling any button action is given by <b> ... <e> equal to "-". Any press of the button while the shutter is moving will immediately stop that shutter. Global steering of all your shutters at home is supported by MQTT. By any button action an MQTT command can be initiated to the <grouptopic> of the device. For single press button this can be enabled by <f> equal to "1". Disabling is indicated by <f> equal to "0". Double to hold actions are given by <g> ... <i>, correspondingly. When <j> is equal to "0" only "cmnd/<grouptopic>/Shutterposition<y> ..." with <y>=<x> is fired. When <j> is equal to "1" <y>=1...4 is used to control any shutter number of a tasmota device having same <grouptopic>. Easy setup for an "up" button: ShutterButton<x> <a> up (same as ShutterButton<x> <a> 100 50 74 100 0 0 0 1 1) Single press will move shutter up to 100%, double press to 50% and tripple press to 74%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> up to 100%. Easy setup for an "down" button: ShutterButton<x> <a> down (same as ShutterButton<x> <a> 0 50 24 0 0 0 0 1 1) Single press will move shutter down to 0%, double press to 50% and tripple press to 24%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> down to 0%. Easy setup for an "updown" button: ShutterButton<x> <a> updown (same as ShutterButton<x> <a> 100 0 50 - 0 0 0 0 0) Single press will move shutter up to 100%, double press down to 0% and tripple press to 50%. No hold action and no other shutter control by MQTT.
2020-01-02 10:23:11 +00:00
uint32_t setting = 0;
// (setting>>31)&(0x01) : enabled
// (setting>>30)&(0x01) : mqtt broadcast to all index
// (setting>>29)&(0x01) : mqtt broadcast hold
// (setting>>28)&(0x01) : mqtt broadcast tripple press
// (setting>>27)&(0x01) : mqtt broadcast double press
// (setting>>26)&(0x01) : mqtt broadcast single press
// (setting>>20)&(0x3f) : shutter_position hold; 0 disabled, 1..101 == 0..100%, 102 == toggle
// (setting>>14)&(0x3f) : shutter_position tripple press 0 disabled, 1..101 == 0..100%, 102 == toggle
// (setting>> 8)&(0x3f) : shutter_position double press 0 disabled, 1..101 == 0..100%, 102 == toggle
// (setting>> 2)&(0x3f) : shutter_position single press 0 disabled, 1..101 == 0..100%, 102 == toggle
Initial support for shutter button control New command "ShutterButton<x> <a> <b> <c> <d> <e> <f> <g> <h> <i> <j>" added that allows to assign a tasmota button <x> to control shutter <a>. Single press button shutter is set to position <b>. Double press button shutter is set to position <c>. Tripple press button shutter is set to position <d>. Hold button shutter is set to position <e>. Disabling any button action is given by <b> ... <e> equal to "-". Any press of the button while the shutter is moving will immediately stop that shutter. Global steering of all your shutters at home is supported by MQTT. By any button action an MQTT command can be initiated to the <grouptopic> of the device. For single press button this can be enabled by <f> equal to "1". Disabling is indicated by <f> equal to "0". Double to hold actions are given by <g> ... <i>, correspondingly. When <j> is equal to "0" only "cmnd/<grouptopic>/Shutterposition<y> ..." with <y>=<x> is fired. When <j> is equal to "1" <y>=1...4 is used to control any shutter number of a tasmota device having same <grouptopic>. Easy setup for an "up" button: ShutterButton<x> <a> up (same as ShutterButton<x> <a> 100 50 74 100 0 0 0 1 1) Single press will move shutter up to 100%, double press to 50% and tripple press to 74%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> up to 100%. Easy setup for an "down" button: ShutterButton<x> <a> down (same as ShutterButton<x> <a> 0 50 24 0 0 0 0 1 1) Single press will move shutter down to 0%, double press to 50% and tripple press to 24%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> down to 0%. Easy setup for an "updown" button: ShutterButton<x> <a> updown (same as ShutterButton<x> <a> 100 0 50 - 0 0 0 0 0) Single press will move shutter up to 100%, double press down to 0% and tripple press to 50%. No hold action and no other shutter control by MQTT.
2020-01-02 10:23:11 +00:00
// (setting>> 0)&(0x03) : shutter_index
if (XdrvMailbox.data_len > 0) {
2020-01-09 10:35:01 +00:00
uint32_t i = 0;
uint32_t button_index = 0;
bool done = false;
bool isShortCommand = false;
Initial support for shutter button control New command "ShutterButton<x> <a> <b> <c> <d> <e> <f> <g> <h> <i> <j>" added that allows to assign a tasmota button <x> to control shutter <a>. Single press button shutter is set to position <b>. Double press button shutter is set to position <c>. Tripple press button shutter is set to position <d>. Hold button shutter is set to position <e>. Disabling any button action is given by <b> ... <e> equal to "-". Any press of the button while the shutter is moving will immediately stop that shutter. Global steering of all your shutters at home is supported by MQTT. By any button action an MQTT command can be initiated to the <grouptopic> of the device. For single press button this can be enabled by <f> equal to "1". Disabling is indicated by <f> equal to "0". Double to hold actions are given by <g> ... <i>, correspondingly. When <j> is equal to "0" only "cmnd/<grouptopic>/Shutterposition<y> ..." with <y>=<x> is fired. When <j> is equal to "1" <y>=1...4 is used to control any shutter number of a tasmota device having same <grouptopic>. Easy setup for an "up" button: ShutterButton<x> <a> up (same as ShutterButton<x> <a> 100 50 74 100 0 0 0 1 1) Single press will move shutter up to 100%, double press to 50% and tripple press to 74%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> up to 100%. Easy setup for an "down" button: ShutterButton<x> <a> down (same as ShutterButton<x> <a> 0 50 24 0 0 0 0 1 1) Single press will move shutter down to 0%, double press to 50% and tripple press to 24%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> down to 0%. Easy setup for an "updown" button: ShutterButton<x> <a> updown (same as ShutterButton<x> <a> 100 0 50 - 0 0 0 0 0) Single press will move shutter up to 100%, double press down to 0% and tripple press to 50%. No hold action and no other shutter control by MQTT.
2020-01-02 10:23:11 +00:00
char *str_ptr;
2020-01-09 10:35:01 +00:00
char data_copy[strlen(XdrvMailbox.data) +1];
strncpy(data_copy, XdrvMailbox.data, sizeof(data_copy)); // Duplicate data as strtok_r will modify it.
// Loop through the data string, splitting on ' ' seperators.
for (char *str = strtok_r(data_copy, " ", &str_ptr); str && i < (1+4+4+1); str = strtok_r(nullptr, " ", &str_ptr), i++) {
Initial support for shutter button control New command "ShutterButton<x> <a> <b> <c> <d> <e> <f> <g> <h> <i> <j>" added that allows to assign a tasmota button <x> to control shutter <a>. Single press button shutter is set to position <b>. Double press button shutter is set to position <c>. Tripple press button shutter is set to position <d>. Hold button shutter is set to position <e>. Disabling any button action is given by <b> ... <e> equal to "-". Any press of the button while the shutter is moving will immediately stop that shutter. Global steering of all your shutters at home is supported by MQTT. By any button action an MQTT command can be initiated to the <grouptopic> of the device. For single press button this can be enabled by <f> equal to "1". Disabling is indicated by <f> equal to "0". Double to hold actions are given by <g> ... <i>, correspondingly. When <j> is equal to "0" only "cmnd/<grouptopic>/Shutterposition<y> ..." with <y>=<x> is fired. When <j> is equal to "1" <y>=1...4 is used to control any shutter number of a tasmota device having same <grouptopic>. Easy setup for an "up" button: ShutterButton<x> <a> up (same as ShutterButton<x> <a> 100 50 74 100 0 0 0 1 1) Single press will move shutter up to 100%, double press to 50% and tripple press to 74%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> up to 100%. Easy setup for an "down" button: ShutterButton<x> <a> down (same as ShutterButton<x> <a> 0 50 24 0 0 0 0 1 1) Single press will move shutter down to 0%, double press to 50% and tripple press to 24%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> down to 0%. Easy setup for an "updown" button: ShutterButton<x> <a> updown (same as ShutterButton<x> <a> 100 0 50 - 0 0 0 0 0) Single press will move shutter up to 100%, double press down to 0% and tripple press to 50%. No hold action and no other shutter control by MQTT.
2020-01-02 10:23:11 +00:00
int field;
switch (str[0]) {
case '-':
field = -1;
break;
case 't':
field = 102;
break;
default:
field = atoi(str);
break;
2020-01-09 10:35:01 +00:00
}
Initial support for shutter button control New command "ShutterButton<x> <a> <b> <c> <d> <e> <f> <g> <h> <i> <j>" added that allows to assign a tasmota button <x> to control shutter <a>. Single press button shutter is set to position <b>. Double press button shutter is set to position <c>. Tripple press button shutter is set to position <d>. Hold button shutter is set to position <e>. Disabling any button action is given by <b> ... <e> equal to "-". Any press of the button while the shutter is moving will immediately stop that shutter. Global steering of all your shutters at home is supported by MQTT. By any button action an MQTT command can be initiated to the <grouptopic> of the device. For single press button this can be enabled by <f> equal to "1". Disabling is indicated by <f> equal to "0". Double to hold actions are given by <g> ... <i>, correspondingly. When <j> is equal to "0" only "cmnd/<grouptopic>/Shutterposition<y> ..." with <y>=<x> is fired. When <j> is equal to "1" <y>=1...4 is used to control any shutter number of a tasmota device having same <grouptopic>. Easy setup for an "up" button: ShutterButton<x> <a> up (same as ShutterButton<x> <a> 100 50 74 100 0 0 0 1 1) Single press will move shutter up to 100%, double press to 50% and tripple press to 74%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> up to 100%. Easy setup for an "down" button: ShutterButton<x> <a> down (same as ShutterButton<x> <a> 0 50 24 0 0 0 0 1 1) Single press will move shutter down to 0%, double press to 50% and tripple press to 24%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> down to 0%. Easy setup for an "updown" button: ShutterButton<x> <a> updown (same as ShutterButton<x> <a> 100 0 50 - 0 0 0 0 0) Single press will move shutter up to 100%, double press down to 0% and tripple press to 50%. No hold action and no other shutter control by MQTT.
2020-01-02 10:23:11 +00:00
switch (i) {
case 0:
if ((field >= -1) && (field<=4)) {
button_index = (field<=0)?(-1):field;
done = (button_index==-1);
Initial support for shutter button control New command "ShutterButton<x> <a> <b> <c> <d> <e> <f> <g> <h> <i> <j>" added that allows to assign a tasmota button <x> to control shutter <a>. Single press button shutter is set to position <b>. Double press button shutter is set to position <c>. Tripple press button shutter is set to position <d>. Hold button shutter is set to position <e>. Disabling any button action is given by <b> ... <e> equal to "-". Any press of the button while the shutter is moving will immediately stop that shutter. Global steering of all your shutters at home is supported by MQTT. By any button action an MQTT command can be initiated to the <grouptopic> of the device. For single press button this can be enabled by <f> equal to "1". Disabling is indicated by <f> equal to "0". Double to hold actions are given by <g> ... <i>, correspondingly. When <j> is equal to "0" only "cmnd/<grouptopic>/Shutterposition<y> ..." with <y>=<x> is fired. When <j> is equal to "1" <y>=1...4 is used to control any shutter number of a tasmota device having same <grouptopic>. Easy setup for an "up" button: ShutterButton<x> <a> up (same as ShutterButton<x> <a> 100 50 74 100 0 0 0 1 1) Single press will move shutter up to 100%, double press to 50% and tripple press to 74%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> up to 100%. Easy setup for an "down" button: ShutterButton<x> <a> down (same as ShutterButton<x> <a> 0 50 24 0 0 0 0 1 1) Single press will move shutter down to 0%, double press to 50% and tripple press to 24%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> down to 0%. Easy setup for an "updown" button: ShutterButton<x> <a> updown (same as ShutterButton<x> <a> 100 0 50 - 0 0 0 0 0) Single press will move shutter up to 100%, double press down to 0% and tripple press to 50%. No hold action and no other shutter control by MQTT.
2020-01-02 10:23:11 +00:00
} else
done = true;
break;
case 1:
if (!strcmp_P(str, PSTR("up"))) {
setting |= (((100>>1)+1)<<2) | (((50>>1)+1)<<8) | (((75>>1)+1)<<14) | (((100>>1)+1)<<20);
isShortCommand = true;
Initial support for shutter button control New command "ShutterButton<x> <a> <b> <c> <d> <e> <f> <g> <h> <i> <j>" added that allows to assign a tasmota button <x> to control shutter <a>. Single press button shutter is set to position <b>. Double press button shutter is set to position <c>. Tripple press button shutter is set to position <d>. Hold button shutter is set to position <e>. Disabling any button action is given by <b> ... <e> equal to "-". Any press of the button while the shutter is moving will immediately stop that shutter. Global steering of all your shutters at home is supported by MQTT. By any button action an MQTT command can be initiated to the <grouptopic> of the device. For single press button this can be enabled by <f> equal to "1". Disabling is indicated by <f> equal to "0". Double to hold actions are given by <g> ... <i>, correspondingly. When <j> is equal to "0" only "cmnd/<grouptopic>/Shutterposition<y> ..." with <y>=<x> is fired. When <j> is equal to "1" <y>=1...4 is used to control any shutter number of a tasmota device having same <grouptopic>. Easy setup for an "up" button: ShutterButton<x> <a> up (same as ShutterButton<x> <a> 100 50 74 100 0 0 0 1 1) Single press will move shutter up to 100%, double press to 50% and tripple press to 74%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> up to 100%. Easy setup for an "down" button: ShutterButton<x> <a> down (same as ShutterButton<x> <a> 0 50 24 0 0 0 0 1 1) Single press will move shutter down to 0%, double press to 50% and tripple press to 24%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> down to 0%. Easy setup for an "updown" button: ShutterButton<x> <a> updown (same as ShutterButton<x> <a> 100 0 50 - 0 0 0 0 0) Single press will move shutter up to 100%, double press down to 0% and tripple press to 50%. No hold action and no other shutter control by MQTT.
2020-01-02 10:23:11 +00:00
break;
} else if (!strcmp_P(str, PSTR("down"))) {
setting |= (((0>>1)+1)<<2) | (((50>>1)+1)<<8) | (((25>>1)+1)<<14) | (((0>>1)+1)<<20);
isShortCommand = true;
Initial support for shutter button control New command "ShutterButton<x> <a> <b> <c> <d> <e> <f> <g> <h> <i> <j>" added that allows to assign a tasmota button <x> to control shutter <a>. Single press button shutter is set to position <b>. Double press button shutter is set to position <c>. Tripple press button shutter is set to position <d>. Hold button shutter is set to position <e>. Disabling any button action is given by <b> ... <e> equal to "-". Any press of the button while the shutter is moving will immediately stop that shutter. Global steering of all your shutters at home is supported by MQTT. By any button action an MQTT command can be initiated to the <grouptopic> of the device. For single press button this can be enabled by <f> equal to "1". Disabling is indicated by <f> equal to "0". Double to hold actions are given by <g> ... <i>, correspondingly. When <j> is equal to "0" only "cmnd/<grouptopic>/Shutterposition<y> ..." with <y>=<x> is fired. When <j> is equal to "1" <y>=1...4 is used to control any shutter number of a tasmota device having same <grouptopic>. Easy setup for an "up" button: ShutterButton<x> <a> up (same as ShutterButton<x> <a> 100 50 74 100 0 0 0 1 1) Single press will move shutter up to 100%, double press to 50% and tripple press to 74%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> up to 100%. Easy setup for an "down" button: ShutterButton<x> <a> down (same as ShutterButton<x> <a> 0 50 24 0 0 0 0 1 1) Single press will move shutter down to 0%, double press to 50% and tripple press to 24%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> down to 0%. Easy setup for an "updown" button: ShutterButton<x> <a> updown (same as ShutterButton<x> <a> 100 0 50 - 0 0 0 0 0) Single press will move shutter up to 100%, double press down to 0% and tripple press to 50%. No hold action and no other shutter control by MQTT.
2020-01-02 10:23:11 +00:00
break;
} else if (!strcmp_P(str, PSTR("updown"))) {
setting |= (((100>>1)+1)<<2) | (((0>>1)+1)<<8) | (((50>>1)+1)<<14);
isShortCommand = true;
Initial support for shutter button control New command "ShutterButton<x> <a> <b> <c> <d> <e> <f> <g> <h> <i> <j>" added that allows to assign a tasmota button <x> to control shutter <a>. Single press button shutter is set to position <b>. Double press button shutter is set to position <c>. Tripple press button shutter is set to position <d>. Hold button shutter is set to position <e>. Disabling any button action is given by <b> ... <e> equal to "-". Any press of the button while the shutter is moving will immediately stop that shutter. Global steering of all your shutters at home is supported by MQTT. By any button action an MQTT command can be initiated to the <grouptopic> of the device. For single press button this can be enabled by <f> equal to "1". Disabling is indicated by <f> equal to "0". Double to hold actions are given by <g> ... <i>, correspondingly. When <j> is equal to "0" only "cmnd/<grouptopic>/Shutterposition<y> ..." with <y>=<x> is fired. When <j> is equal to "1" <y>=1...4 is used to control any shutter number of a tasmota device having same <grouptopic>. Easy setup for an "up" button: ShutterButton<x> <a> up (same as ShutterButton<x> <a> 100 50 74 100 0 0 0 1 1) Single press will move shutter up to 100%, double press to 50% and tripple press to 74%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> up to 100%. Easy setup for an "down" button: ShutterButton<x> <a> down (same as ShutterButton<x> <a> 0 50 24 0 0 0 0 1 1) Single press will move shutter down to 0%, double press to 50% and tripple press to 24%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> down to 0%. Easy setup for an "updown" button: ShutterButton<x> <a> updown (same as ShutterButton<x> <a> 100 0 50 - 0 0 0 0 0) Single press will move shutter up to 100%, double press down to 0% and tripple press to 50%. No hold action and no other shutter control by MQTT.
2020-01-02 10:23:11 +00:00
break;
} else if (!strcmp_P(str, PSTR("toggle"))) {
setting |= (((102>>1)+1)<<2) | (((50>>1)+1)<<8);
isShortCommand = true;
break;
Initial support for shutter button control New command "ShutterButton<x> <a> <b> <c> <d> <e> <f> <g> <h> <i> <j>" added that allows to assign a tasmota button <x> to control shutter <a>. Single press button shutter is set to position <b>. Double press button shutter is set to position <c>. Tripple press button shutter is set to position <d>. Hold button shutter is set to position <e>. Disabling any button action is given by <b> ... <e> equal to "-". Any press of the button while the shutter is moving will immediately stop that shutter. Global steering of all your shutters at home is supported by MQTT. By any button action an MQTT command can be initiated to the <grouptopic> of the device. For single press button this can be enabled by <f> equal to "1". Disabling is indicated by <f> equal to "0". Double to hold actions are given by <g> ... <i>, correspondingly. When <j> is equal to "0" only "cmnd/<grouptopic>/Shutterposition<y> ..." with <y>=<x> is fired. When <j> is equal to "1" <y>=1...4 is used to control any shutter number of a tasmota device having same <grouptopic>. Easy setup for an "up" button: ShutterButton<x> <a> up (same as ShutterButton<x> <a> 100 50 74 100 0 0 0 1 1) Single press will move shutter up to 100%, double press to 50% and tripple press to 74%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> up to 100%. Easy setup for an "down" button: ShutterButton<x> <a> down (same as ShutterButton<x> <a> 0 50 24 0 0 0 0 1 1) Single press will move shutter down to 0%, double press to 50% and tripple press to 24%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> down to 0%. Easy setup for an "updown" button: ShutterButton<x> <a> updown (same as ShutterButton<x> <a> 100 0 50 - 0 0 0 0 0) Single press will move shutter up to 100%, double press down to 0% and tripple press to 50%. No hold action and no other shutter control by MQTT.
2020-01-02 10:23:11 +00:00
}
case 2:
if (isShortCommand) {
if ((field==1) && (setting & (0x3F<<(2+6*3))))
// if short command up or down (hold press position set) then also enable MQTT broadcast
setting |= (0x3<<29);
done = true;
break;
}
Initial support for shutter button control New command "ShutterButton<x> <a> <b> <c> <d> <e> <f> <g> <h> <i> <j>" added that allows to assign a tasmota button <x> to control shutter <a>. Single press button shutter is set to position <b>. Double press button shutter is set to position <c>. Tripple press button shutter is set to position <d>. Hold button shutter is set to position <e>. Disabling any button action is given by <b> ... <e> equal to "-". Any press of the button while the shutter is moving will immediately stop that shutter. Global steering of all your shutters at home is supported by MQTT. By any button action an MQTT command can be initiated to the <grouptopic> of the device. For single press button this can be enabled by <f> equal to "1". Disabling is indicated by <f> equal to "0". Double to hold actions are given by <g> ... <i>, correspondingly. When <j> is equal to "0" only "cmnd/<grouptopic>/Shutterposition<y> ..." with <y>=<x> is fired. When <j> is equal to "1" <y>=1...4 is used to control any shutter number of a tasmota device having same <grouptopic>. Easy setup for an "up" button: ShutterButton<x> <a> up (same as ShutterButton<x> <a> 100 50 74 100 0 0 0 1 1) Single press will move shutter up to 100%, double press to 50% and tripple press to 74%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> up to 100%. Easy setup for an "down" button: ShutterButton<x> <a> down (same as ShutterButton<x> <a> 0 50 24 0 0 0 0 1 1) Single press will move shutter down to 0%, double press to 50% and tripple press to 24%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> down to 0%. Easy setup for an "updown" button: ShutterButton<x> <a> updown (same as ShutterButton<x> <a> 100 0 50 - 0 0 0 0 0) Single press will move shutter up to 100%, double press down to 0% and tripple press to 50%. No hold action and no other shutter control by MQTT.
2020-01-02 10:23:11 +00:00
case 3:
case 4:
if ((field >= -1) && (field<=102))
Initial support for shutter button control New command "ShutterButton<x> <a> <b> <c> <d> <e> <f> <g> <h> <i> <j>" added that allows to assign a tasmota button <x> to control shutter <a>. Single press button shutter is set to position <b>. Double press button shutter is set to position <c>. Tripple press button shutter is set to position <d>. Hold button shutter is set to position <e>. Disabling any button action is given by <b> ... <e> equal to "-". Any press of the button while the shutter is moving will immediately stop that shutter. Global steering of all your shutters at home is supported by MQTT. By any button action an MQTT command can be initiated to the <grouptopic> of the device. For single press button this can be enabled by <f> equal to "1". Disabling is indicated by <f> equal to "0". Double to hold actions are given by <g> ... <i>, correspondingly. When <j> is equal to "0" only "cmnd/<grouptopic>/Shutterposition<y> ..." with <y>=<x> is fired. When <j> is equal to "1" <y>=1...4 is used to control any shutter number of a tasmota device having same <grouptopic>. Easy setup for an "up" button: ShutterButton<x> <a> up (same as ShutterButton<x> <a> 100 50 74 100 0 0 0 1 1) Single press will move shutter up to 100%, double press to 50% and tripple press to 74%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> up to 100%. Easy setup for an "down" button: ShutterButton<x> <a> down (same as ShutterButton<x> <a> 0 50 24 0 0 0 0 1 1) Single press will move shutter down to 0%, double press to 50% and tripple press to 24%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> down to 0%. Easy setup for an "updown" button: ShutterButton<x> <a> updown (same as ShutterButton<x> <a> 100 0 50 - 0 0 0 0 0) Single press will move shutter up to 100%, double press down to 0% and tripple press to 50%. No hold action and no other shutter control by MQTT.
2020-01-02 10:23:11 +00:00
setting |= (((field>>1)+1)<<(i*6 + (2-6)));
break;
case 5:
case 6:
case 7:
case 8:
case 9:
if (field==1)
setting |= (1<<(i + (26-5)));
break;
}
if (done) break;
}
if (button_index) {
if (button_index==-1) {
// remove all buttons for this shutter
for (uint32_t i=0 ; i < MAX_SHUTTER_KEYS ; i++)
2021-06-11 17:14:12 +01:00
if ((Settings->shutter_button[i]&0x3) == (XdrvMailbox.index-1))
Settings->shutter_button[i] = 0;
} else {
if (setting) {
// anything was set
setting |= (1<<31);
setting |= (XdrvMailbox.index-1) & 0x3;
}
2021-06-11 17:14:12 +01:00
Settings->shutter_button[button_index-1] = setting;
}
}
}
char setting_chr[30*MAX_SHUTTER_KEYS] = "-", *setting_chr_ptr = setting_chr;
for (uint32_t i=0 ; i < MAX_SHUTTER_KEYS ; i++) {
2021-06-11 17:14:12 +01:00
setting = Settings->shutter_button[i];
if ((setting&(1<<31)) && ((setting&0x3) == (XdrvMailbox.index-1))) {
if (*setting_chr_ptr == 0)
setting_chr_ptr += sprintf_P(setting_chr_ptr, PSTR("|"));
setting_chr_ptr += snprintf_P(setting_chr_ptr, 2, PSTR("%d"), i+1);
for (uint32_t j=0 ; j < 4 ; j++) {
int8_t pos = (((setting>> (2+6*j))&(0x3f))-1)<<1;
if (0 <= pos)
if (102 == pos) {
setting_chr_ptr += sprintf_P(setting_chr_ptr, PSTR(" t"));
} else {
setting_chr_ptr += snprintf_P(setting_chr_ptr, 5, PSTR(" %d"), pos);
}
else
setting_chr_ptr += sprintf_P(setting_chr_ptr, PSTR(" -"));
}
for (uint32_t j=0 ; j < 5 ; j++) {
bool mqtt = ((setting>>(26+j))&(0x01)!=0);
if (mqtt)
setting_chr_ptr += sprintf_P(setting_chr_ptr, PSTR(" 1"));
else
setting_chr_ptr += sprintf_P(setting_chr_ptr, PSTR(" -"));
Initial support for shutter button control New command "ShutterButton<x> <a> <b> <c> <d> <e> <f> <g> <h> <i> <j>" added that allows to assign a tasmota button <x> to control shutter <a>. Single press button shutter is set to position <b>. Double press button shutter is set to position <c>. Tripple press button shutter is set to position <d>. Hold button shutter is set to position <e>. Disabling any button action is given by <b> ... <e> equal to "-". Any press of the button while the shutter is moving will immediately stop that shutter. Global steering of all your shutters at home is supported by MQTT. By any button action an MQTT command can be initiated to the <grouptopic> of the device. For single press button this can be enabled by <f> equal to "1". Disabling is indicated by <f> equal to "0". Double to hold actions are given by <g> ... <i>, correspondingly. When <j> is equal to "0" only "cmnd/<grouptopic>/Shutterposition<y> ..." with <y>=<x> is fired. When <j> is equal to "1" <y>=1...4 is used to control any shutter number of a tasmota device having same <grouptopic>. Easy setup for an "up" button: ShutterButton<x> <a> up (same as ShutterButton<x> <a> 100 50 74 100 0 0 0 1 1) Single press will move shutter up to 100%, double press to 50% and tripple press to 74%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> up to 100%. Easy setup for an "down" button: ShutterButton<x> <a> down (same as ShutterButton<x> <a> 0 50 24 0 0 0 0 1 1) Single press will move shutter down to 0%, double press to 50% and tripple press to 24%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> down to 0%. Easy setup for an "updown" button: ShutterButton<x> <a> updown (same as ShutterButton<x> <a> 100 0 50 - 0 0 0 0 0) Single press will move shutter up to 100%, double press down to 0% and tripple press to 50%. No hold action and no other shutter control by MQTT.
2020-01-02 10:23:11 +00:00
}
}
}
ResponseCmndIdxChar(setting_chr);
Initial support for shutter button control New command "ShutterButton<x> <a> <b> <c> <d> <e> <f> <g> <h> <i> <j>" added that allows to assign a tasmota button <x> to control shutter <a>. Single press button shutter is set to position <b>. Double press button shutter is set to position <c>. Tripple press button shutter is set to position <d>. Hold button shutter is set to position <e>. Disabling any button action is given by <b> ... <e> equal to "-". Any press of the button while the shutter is moving will immediately stop that shutter. Global steering of all your shutters at home is supported by MQTT. By any button action an MQTT command can be initiated to the <grouptopic> of the device. For single press button this can be enabled by <f> equal to "1". Disabling is indicated by <f> equal to "0". Double to hold actions are given by <g> ... <i>, correspondingly. When <j> is equal to "0" only "cmnd/<grouptopic>/Shutterposition<y> ..." with <y>=<x> is fired. When <j> is equal to "1" <y>=1...4 is used to control any shutter number of a tasmota device having same <grouptopic>. Easy setup for an "up" button: ShutterButton<x> <a> up (same as ShutterButton<x> <a> 100 50 74 100 0 0 0 1 1) Single press will move shutter up to 100%, double press to 50% and tripple press to 74%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> up to 100%. Easy setup for an "down" button: ShutterButton<x> <a> down (same as ShutterButton<x> <a> 0 50 24 0 0 0 0 1 1) Single press will move shutter down to 0%, double press to 50% and tripple press to 24%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> down to 0%. Easy setup for an "updown" button: ShutterButton<x> <a> updown (same as ShutterButton<x> <a> 100 0 50 - 0 0 0 0 0) Single press will move shutter up to 100%, double press down to 0% and tripple press to 50%. No hold action and no other shutter control by MQTT.
2020-01-02 10:23:11 +00:00
}
}
void CmndShutterSetHalfway(void)
{
2020-10-30 11:29:48 +00:00
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= TasmotaGlobal.shutters_present)) {
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) {
2021-06-11 17:14:12 +01:00
Settings->shutter_set50percent[XdrvMailbox.index -1] = (Settings->shutter_options[XdrvMailbox.index -1] & 1) ? 100 - XdrvMailbox.payload : XdrvMailbox.payload;
Settings->shuttercoeff[0][XdrvMailbox.index -1] = 0;
ShutterInit();
}
2021-06-11 17:14:12 +01:00
ResponseCmndIdxNumber((Settings->shutter_options[XdrvMailbox.index -1] & 1) ? 100 - Settings->shutter_set50percent[XdrvMailbox.index -1] : Settings->shutter_set50percent[XdrvMailbox.index -1]);
}
}
void CmndShutterFrequency(void)
{
if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 20000)) {
2020-09-09 13:04:57 +01:00
ShutterGlobal.open_velocity_max = XdrvMailbox.payload;
2020-10-30 11:29:48 +00:00
if (TasmotaGlobal.shutters_present < 4) {
2021-06-11 17:14:12 +01:00
Settings->shuttercoeff[4][3] = ShutterGlobal.open_velocity_max;
}
ShutterInit();
}
2020-09-09 13:04:57 +01:00
ResponseCmndNumber(ShutterGlobal.open_velocity_max);
}
void CmndShutterSetClose(void)
{
2020-10-30 11:29:48 +00:00
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= TasmotaGlobal.shutters_present)) {
2020-09-09 13:04:57 +01:00
Shutter[XdrvMailbox.index -1].real_position = 0;
2021-11-07 14:53:12 +00:00
Shutter[XdrvMailbox.index -1].tilt_real_pos = Shutter[XdrvMailbox.index -1].tilt_config[0];
ShutterStartInit(XdrvMailbox.index -1, 0, 0);
2021-06-11 17:14:12 +01:00
Settings->shutter_position[XdrvMailbox.index -1] = 0;
ResponseCmndIdxChar(D_CONFIGURATION_RESET);
}
}
2020-04-21 15:20:20 +01:00
void CmndShutterSetOpen(void)
{
2020-10-30 11:29:48 +00:00
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= TasmotaGlobal.shutters_present)) {
2020-09-09 13:04:57 +01:00
Shutter[XdrvMailbox.index -1].real_position = Shutter[XdrvMailbox.index -1].open_max;
2021-11-07 14:53:12 +00:00
Shutter[XdrvMailbox.index -1].tilt_real_pos = Shutter[XdrvMailbox.index -1].tilt_config[1];
2020-09-09 13:04:57 +01:00
ShutterStartInit(XdrvMailbox.index -1, 0, Shutter[XdrvMailbox.index -1].open_max);
2021-06-11 17:14:12 +01:00
Settings->shutter_position[XdrvMailbox.index -1] = 100;
2020-04-21 15:20:20 +01:00
ResponseCmndIdxChar(D_CONFIGURATION_RESET);
}
}
void CmndShutterPwmRange(void)
{
2020-10-30 11:29:48 +00:00
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= TasmotaGlobal.shutters_present)) {
if (XdrvMailbox.data_len > 0) {
2020-09-09 15:23:50 +01:00
uint8_t i = 0;
char *str_ptr;
char data_copy[strlen(XdrvMailbox.data) +1];
strncpy(data_copy, XdrvMailbox.data, sizeof(data_copy)); // Duplicate data as strtok_r will modify it.
// Loop through the data string, splitting on ' ' seperators.
2020-09-09 15:23:50 +01:00
for (char *str = strtok_r(data_copy, " ", &str_ptr); str && i < 2; str = strtok_r(nullptr, " ", &str_ptr), i++) {
uint16_t field = atoi(str);
// The fields in a data string can only range from 1-30000.
// and following value must be higher than previous one
if ((field <= 0) || (field > 1023)) {
break;
}
2021-06-11 17:14:12 +01:00
Settings->shutter_pwmrange[i][XdrvMailbox.index -1] = field;
}
2021-06-11 17:14:12 +01:00
AddLog(LOG_LEVEL_DEBUG, PSTR("SHT: Shtr%d Init1. pwmmin %d, pwmmax %d"), XdrvMailbox.index , Settings->shutter_pwmrange[0][XdrvMailbox.index -1], Settings->shutter_pwmrange[1][XdrvMailbox.index -1]);
ShutterInit();
ResponseCmndIdxChar(XdrvMailbox.data);
} else {
char setting_chr[30] = "0";
2021-06-11 17:14:12 +01:00
snprintf_P(setting_chr, sizeof(setting_chr), PSTR("Shutter %d: min:%d max:%d"), XdrvMailbox.index, Settings->shutter_pwmrange[0][XdrvMailbox.index -1], Settings->shutter_pwmrange[1][XdrvMailbox.index -1]);
ResponseCmndIdxChar(setting_chr);
}
}
}
2020-01-09 10:35:01 +00:00
void CmndShutterCalibration(void)
{
2020-10-30 11:29:48 +00:00
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= TasmotaGlobal.shutters_present)) {
if (XdrvMailbox.data_len > 0) {
2020-09-09 15:23:50 +01:00
uint8_t i = 0;
2020-01-09 10:35:01 +00:00
char *str_ptr;
char data_copy[strlen(XdrvMailbox.data) +1];
strncpy(data_copy, XdrvMailbox.data, sizeof(data_copy)); // Duplicate data as strtok_r will modify it.
// Loop through the data string, splitting on ' ' seperators.
for (char *str = strtok_r(data_copy, " ", &str_ptr); str && i < 5; str = strtok_r(nullptr, " ", &str_ptr), i++) {
int field = atoi(str);
// The fields in a data string can only range from 1-30000.
// and following value must be higher than previous one
if ((field <= 0) || (field > 30000) || ( (i>0) && (field <= messwerte[i-1]) ) ) {
break;
}
2020-01-09 10:35:01 +00:00
messwerte[i] = field;
}
2021-06-11 17:14:12 +01:00
Settings->shutter_set50percent[XdrvMailbox.index -1] = 50;
2020-01-09 10:35:01 +00:00
for (i = 0; i < 5; i++) {
2021-06-11 17:14:12 +01:00
Settings->shuttercoeff[i][XdrvMailbox.index -1] = SHT_DIV_ROUND((uint32_t)messwerte[i] * 1000, messwerte[4]);
AddLog(LOG_LEVEL_DEBUG, PSTR("SHT: Shuttercoeff %d, i %d, Value %d, MeasuredValue %d"), i,XdrvMailbox.index -1,Settings->shuttercoeff[i][XdrvMailbox.index -1], messwerte[i]);
2020-01-09 10:35:01 +00:00
}
ShutterInit();
ResponseCmndIdxChar(XdrvMailbox.data);
} else {
char setting_chr[30] = "0";
2021-06-11 17:14:12 +01:00
snprintf_P(setting_chr, sizeof(setting_chr), PSTR("%d %d %d %d %d"), Settings->shuttercoeff[0][XdrvMailbox.index -1], Settings->shuttercoeff[1][XdrvMailbox.index -1], Settings->shuttercoeff[2][XdrvMailbox.index -1], Settings->shuttercoeff[3][XdrvMailbox.index -1], Settings->shuttercoeff[4][XdrvMailbox.index -1]);
ResponseCmndIdxChar(setting_chr);
}
}
}
2021-11-16 12:02:36 +00:00
void ShutterOptionsSetHelper(uint16_t option)
{
2020-10-30 11:29:48 +00:00
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= TasmotaGlobal.shutters_present)) {
if (XdrvMailbox.payload == 0) {
2021-06-11 17:14:12 +01:00
Settings->shutter_options[XdrvMailbox.index -1] &= ~(option);
} else if (XdrvMailbox.payload == 1) {
2021-06-11 17:14:12 +01:00
Settings->shutter_options[XdrvMailbox.index -1] |= (option);
}
2021-06-11 17:14:12 +01:00
ResponseCmndIdxNumber((Settings->shutter_options[XdrvMailbox.index -1] & option) ? 1 : 0);
}
}
2021-11-16 12:02:36 +00:00
void CmndShutterInvert(void)
{
2020-09-09 13:04:57 +01:00
ShutterOptionsSetHelper(1);
}
2021-11-16 12:02:36 +00:00
void CmndShutterLock(void)
{
2020-09-09 13:04:57 +01:00
ShutterOptionsSetHelper(2);
}
2021-11-16 12:02:36 +00:00
void CmndShutterEnableEndStopTime(void)
{
2020-09-09 13:04:57 +01:00
ShutterOptionsSetHelper(4);
2020-01-13 11:00:34 +00:00
}
2021-11-16 12:02:36 +00:00
void CmndShutterInvertWebButtons(void)
{
2020-09-09 13:04:57 +01:00
ShutterOptionsSetHelper(8);
2020-03-10 07:41:37 +00:00
}
2021-11-16 12:02:36 +00:00
void CmndShutterSetTilt(void)
{
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= TasmotaGlobal.shutters_present)) {
if (XdrvMailbox.payload != -99 ) {
Shutter[XdrvMailbox.index -1].tilt_target_pos = tmin(tmax(XdrvMailbox.payload, Shutter[XdrvMailbox.index -1].tilt_config[0]), Shutter[XdrvMailbox.index -1].tilt_config[1]);
XdrvMailbox.payload = -99;
}
if ((XdrvMailbox.data_len > 1) && (XdrvMailbox.payload <= 0)) {
if (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_OPEN) ) {
Shutter[XdrvMailbox.index -1].tilt_target_pos = Shutter[XdrvMailbox.index -1].tilt_config[3]; // open position
XdrvMailbox.payload = -99;
}
if (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_CLOSE) ) {
Shutter[XdrvMailbox.index -1].tilt_target_pos = Shutter[XdrvMailbox.index -1].tilt_config[4]; // close position
XdrvMailbox.payload = -99;
}
}
}
XdrvMailbox.data[0] = '\0';
AddLog(LOG_LEVEL_INFO, PSTR("SHT: TiltTarget %d, payload %d"), Shutter[XdrvMailbox.index -1].tilt_target_pos,XdrvMailbox.payload);
2021-11-16 12:02:36 +00:00
ResponseCmndNumber(Shutter[XdrvMailbox.index -1].tilt_target_pos);
Shutter[XdrvMailbox.index -1].tiltmoving = 1;
CmndShutterPosition();
}
void CmndShutterTiltConfig(void)
{
2021-11-07 14:53:12 +00:00
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= TasmotaGlobal.shutters_present)) {
if (XdrvMailbox.data_len > 0) {
uint8_t i = 0;
char *str_ptr;
char data_copy[strlen(XdrvMailbox.data) +1];
strncpy(data_copy, XdrvMailbox.data, sizeof(data_copy)); // Duplicate data as strtok_r will modify it.
// Loop through the data string, splitting on ' ' seperators.
for (char *str = strtok_r(data_copy, " ", &str_ptr); str && i < 6; str = strtok_r(nullptr, " ", &str_ptr), i++) {
Shutter[XdrvMailbox.index -1].tilt_config[i] = Settings->shutter_tilt_config[i][XdrvMailbox.index -1] = atoi(str);
}
ShutterInit();
ResponseCmndIdxChar(XdrvMailbox.data);
} else {
char setting_chr[30] = "0";
snprintf_P(setting_chr, sizeof(setting_chr), PSTR("SHT:%d %d %d %d %d %d"), XdrvMailbox.index -1,Shutter[XdrvMailbox.index -1].tilt_config[0], Shutter[XdrvMailbox.index -1].tilt_config[1],Shutter[XdrvMailbox.index -1].tilt_config[2],Shutter[XdrvMailbox.index -1].tilt_config[3],Shutter[XdrvMailbox.index -1].tilt_config[4]);
ResponseCmndIdxChar(setting_chr);
}
AddLog(LOG_LEVEL_INFO, PSTR("SHT: TiltConfig %d, min: %d, max %d, runtime %d, close_pos: %d, open_pos: %d"), XdrvMailbox.index ,Shutter[XdrvMailbox.index -1].tilt_config[0], Shutter[XdrvMailbox.index -1].tilt_config[1],Shutter[XdrvMailbox.index -1].tilt_config[2],Shutter[XdrvMailbox.index -1].tilt_config[3],Shutter[XdrvMailbox.index -1].tilt_config[4]);
}
}
2021-11-16 12:02:36 +00:00
void CmndShutterTiltIncDec(void)
{
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Change in: payload %s (%d), payload %d, idx %d, src %d"), XdrvMailbox.data , XdrvMailbox.data_len, XdrvMailbox.payload , XdrvMailbox.index, TasmotaGlobal.last_source );
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= TasmotaGlobal.shutters_present)) {
if (XdrvMailbox.data_len > 0) {
XdrvMailbox.payload = Shutter[XdrvMailbox.index -1].tilt_target_pos+XdrvMailbox.payload;
CmndShutterSetTilt();
}
}
ResponseCmndNumber(Shutter[XdrvMailbox.index -1].tilt_target_pos);
}
/*********************************************************************************************\
* Interface
\*********************************************************************************************/
bool Xdrv27(uint8_t function)
{
bool result = false;
2021-06-11 17:14:12 +01:00
if (Settings->flag3.shutter_mode) { // SetOption80 - Enable shutter support
switch (function) {
case FUNC_PRE_INIT:
ShutterInit();
break;
2021-11-16 20:11:34 +00:00
case FUNC_EVERY_50_MSECOND:
ShutterUpdatePosition();
break;
case FUNC_EVERY_SECOND:
//case FUNC_EVERY_250_MSECOND:
ShutterReportPosition(false, MAX_SHUTTERS);
break;
case FUNC_COMMAND:
result = DecodeCommand(kShutterCommands, ShutterCommand);
break;
case FUNC_JSON_APPEND:
2020-10-30 11:29:48 +00:00
for (uint8_t i = 0; i < TasmotaGlobal.shutters_present; i++) {
2021-06-11 17:14:12 +01:00
uint8_t position = (Settings->shutter_options[i] & 1) ? 100 - Settings->shutter_position[i] : Settings->shutter_position[i];
uint8_t target = (Settings->shutter_options[i] & 1) ? 100 - ShutterRealToPercentPosition(Shutter[i].target_position, i) : ShutterRealToPercentPosition(Shutter[i].target_position, i);
ResponseAppend_P(",");
2021-11-07 14:53:12 +00:00
ResponseAppend_P(JSON_SHUTTER_POS, i+1, position, Shutter[i].direction,target, Shutter[i].tilt_real_pos);
#ifdef USE_DOMOTICZ
2020-10-29 12:37:09 +00:00
if ((0 == TasmotaGlobal.tele_period) && (0 == i)) {
DomoticzSensor(DZ_SHUTTER, position);
}
#endif // USE_DOMOTICZ
}
break;
case FUNC_SET_POWER:
char stemp1[10];
// extract the number of the relay that was switched and save for later in Update Position.
2020-09-09 13:04:57 +01:00
ShutterGlobal.RelayCurrentMask = XdrvMailbox.index ^ ShutterGlobal.RelayOldMask;
2021-01-23 15:26:23 +00:00
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Switched relay %d by %s"), ShutterGlobal.RelayCurrentMask,GetTextIndexed(stemp1, sizeof(stemp1), TasmotaGlobal.last_source, kCommandSource));
ShutterRelayChanged();
2020-09-09 13:04:57 +01:00
ShutterGlobal.RelayOldMask = XdrvMailbox.index;
break;
case FUNC_SET_DEVICE_POWER:
2020-09-09 13:04:57 +01:00
if (ShutterGlobal.skip_relay_change ) {
uint8_t i;
2020-10-30 11:29:48 +00:00
for (i = 0; i < TasmotaGlobal.devices_present; i++) {
2020-09-09 13:04:57 +01:00
if (ShutterGlobal.RelayCurrentMask &1) {
break;
}
2020-09-09 13:04:57 +01:00
ShutterGlobal.RelayCurrentMask >>= 1;
}
2021-01-23 15:26:23 +00:00
//AddLog(LOG_LEVEL_ERROR, PSTR("SHT: Skip relay change %d"), i+1);
result = true;
2020-09-09 13:04:57 +01:00
ShutterGlobal.skip_relay_change = 0;
2021-01-23 15:26:23 +00:00
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Skipping switch off relay %d"), i);
2020-09-05 19:39:24 +01:00
ExecuteCommandPowerShutter(i+1, 0, SRC_SHUTTER);
}
break;
Initial support for shutter button control New command "ShutterButton<x> <a> <b> <c> <d> <e> <f> <g> <h> <i> <j>" added that allows to assign a tasmota button <x> to control shutter <a>. Single press button shutter is set to position <b>. Double press button shutter is set to position <c>. Tripple press button shutter is set to position <d>. Hold button shutter is set to position <e>. Disabling any button action is given by <b> ... <e> equal to "-". Any press of the button while the shutter is moving will immediately stop that shutter. Global steering of all your shutters at home is supported by MQTT. By any button action an MQTT command can be initiated to the <grouptopic> of the device. For single press button this can be enabled by <f> equal to "1". Disabling is indicated by <f> equal to "0". Double to hold actions are given by <g> ... <i>, correspondingly. When <j> is equal to "0" only "cmnd/<grouptopic>/Shutterposition<y> ..." with <y>=<x> is fired. When <j> is equal to "1" <y>=1...4 is used to control any shutter number of a tasmota device having same <grouptopic>. Easy setup for an "up" button: ShutterButton<x> <a> up (same as ShutterButton<x> <a> 100 50 74 100 0 0 0 1 1) Single press will move shutter up to 100%, double press to 50% and tripple press to 74%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> up to 100%. Easy setup for an "down" button: ShutterButton<x> <a> down (same as ShutterButton<x> <a> 0 50 24 0 0 0 0 1 1) Single press will move shutter down to 0%, double press to 50% and tripple press to 24%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> down to 0%. Easy setup for an "updown" button: ShutterButton<x> <a> updown (same as ShutterButton<x> <a> 100 0 50 - 0 0 0 0 0) Single press will move shutter up to 100%, double press down to 0% and tripple press to 50%. No hold action and no other shutter control by MQTT.
2020-01-02 10:23:11 +00:00
case FUNC_BUTTON_PRESSED:
2021-06-11 17:14:12 +01:00
if (Settings->shutter_button[XdrvMailbox.index] & (1<<31)) {
Initial support for shutter button control New command "ShutterButton<x> <a> <b> <c> <d> <e> <f> <g> <h> <i> <j>" added that allows to assign a tasmota button <x> to control shutter <a>. Single press button shutter is set to position <b>. Double press button shutter is set to position <c>. Tripple press button shutter is set to position <d>. Hold button shutter is set to position <e>. Disabling any button action is given by <b> ... <e> equal to "-". Any press of the button while the shutter is moving will immediately stop that shutter. Global steering of all your shutters at home is supported by MQTT. By any button action an MQTT command can be initiated to the <grouptopic> of the device. For single press button this can be enabled by <f> equal to "1". Disabling is indicated by <f> equal to "0". Double to hold actions are given by <g> ... <i>, correspondingly. When <j> is equal to "0" only "cmnd/<grouptopic>/Shutterposition<y> ..." with <y>=<x> is fired. When <j> is equal to "1" <y>=1...4 is used to control any shutter number of a tasmota device having same <grouptopic>. Easy setup for an "up" button: ShutterButton<x> <a> up (same as ShutterButton<x> <a> 100 50 74 100 0 0 0 1 1) Single press will move shutter up to 100%, double press to 50% and tripple press to 74%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> up to 100%. Easy setup for an "down" button: ShutterButton<x> <a> down (same as ShutterButton<x> <a> 0 50 24 0 0 0 0 1 1) Single press will move shutter down to 0%, double press to 50% and tripple press to 24%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> down to 0%. Easy setup for an "updown" button: ShutterButton<x> <a> updown (same as ShutterButton<x> <a> 100 0 50 - 0 0 0 0 0) Single press will move shutter up to 100%, double press down to 0% and tripple press to 50%. No hold action and no other shutter control by MQTT.
2020-01-02 10:23:11 +00:00
ShutterButtonHandler();
result = true;
}
break;
}
}
return result;
}
#endif //USE_SHUTTER
2021-02-18 11:42:59 +00:00
#ifdef SHUTTER_UNITTEST
void CmndShutterUnitTest(void) {
int16_t input_percent[10] = {-5,0,10,26,35,55,80,99,100,105};
int16_t output_percent[10] = {0,0,10,26,35,55,80,99,100,100};
uint32_t result_percent[2][2][10] = {{{0,0,24000,62400,84000,132000,192000,237600,240000,240000},
{0,0,360000,936000,1260000,1980000,2880000,3564000,3600000,3600000}},
{{0,0,76296,100000,113333,174299,205795,237983,240000,240000},
{0,0,1144444,1500000,1700000,2614488,3086929,3569748,3600000,3600000}}};
uint32_t result = 0;
char svalue[50]; // Command and number parameter
2021-06-11 17:14:12 +01:00
Settings->shuttercoeff[0][0] = 0;
2021-02-18 11:42:59 +00:00
for (uint8_t i=0; i<2 ; i++){
snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_OPENTIME "%d %d"), 1, 12);
ExecuteCommand(svalue, SRC_SHUTTER);
ShutterInit();
for (uint8_t j=0; j<2 ; j++){
for (uint8_t k=0; k<10 ; k++){
result += (result_percent[i][j][k] == ShutterPercentToRealPosition(input_percent[k] , 0) ? 0 : 1);
AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterPercentToRealPosition error %d: %d <-> %d"),result, ShutterPercentToRealPosition(input_percent[k] , 0), result_percent[i][j][k]);
}
snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_OPENTIME "%d %d"), 1, 180);
ExecuteCommand(svalue, SRC_SHUTTER);
}
snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_CLIBRATION "%d %s"), 1, "15 83 105 185 210");
ExecuteCommand(svalue, SRC_SHUTTER);
}
if (!result){
AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterPercentToRealPosition: PASS"));
} else {
AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterPercentToRealPosition: FAIL"));
}
2021-06-11 17:14:12 +01:00
Settings->shuttercoeff[0][0] = 0;
2021-02-18 11:42:59 +00:00
for (uint8_t i=0; i<2 ; i++){
snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_OPENTIME "%d %d"), 1, 12);
ExecuteCommand(svalue, SRC_SHUTTER);
ShutterInit();
for (uint8_t j=0; j<2 ; j++){
for (uint8_t k=0; k<10 ; k++){
result += (output_percent[k] == ShutterRealToPercentPosition(result_percent[i][j][k] , 0) ? 0 : 1);
AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterRealToPercentPosition error %d: %d <-> %d"),result, ShutterRealToPercentPosition(result_percent[i][j][k] , 0), output_percent[k]);
}
snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_OPENTIME "%d %d"), 1, 180);
ExecuteCommand(svalue, SRC_SHUTTER);
}
snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_CLIBRATION "%d %s"), 1, "15 83 105 185 210");
ExecuteCommand(svalue, SRC_SHUTTER);
}
if (!result){
AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterRealToPercentPosition: PASS"));
} else {
AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterRealToPercentPosition: FAIL"));
}
}
#else
void CmndShutterUnitTest(void) {}
#endif