diff --git a/CHANGELOG.md b/CHANGELOG.md index 46bb0deea..220093420 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,19 @@ All notable changes to this project will be documented in this file. ## [Unreleased] - Development -## [13.4.0.3] +## [13.4.0.4] +### Added +- Command ``PowerLock`` to disable power control of selected outputs (#21081) + +### Breaking Changed + +### Changed + +### Fixed + +### Removed + +## [13.4.0.3] 20240402 ### Added - Zigbee support for attributes of type `uint48` used by energy monitoring (#20992) - Support for single channel EU863-870 LoRaWanBridge (#17790) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 66bd89613..d37e6936d 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -116,8 +116,9 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm [Complete list](BUILDS.md) of available feature and sensors. -## Changelog v13.4.0.3 +## Changelog v13.4.0.4 ### Added +- Command ``PowerLock`` to disable power control of selected outputs [#21081](https://github.com/arendst/Tasmota/issues/21081) - Support for calculated heat index if temperature and humidity is available with ``#define USE_HEAT_INDEX`` [#4771](https://github.com/arendst/Tasmota/issues/4771) - Support for LoRa and single channel EU863-870 LoRaWanBridge [#17790](https://github.com/arendst/Tasmota/issues/17790) - Support for AMS5915/AMS6915 temperature and pressure sensors [#20814](https://github.com/arendst/Tasmota/issues/20814) diff --git a/tasmota/include/i18n.h b/tasmota/include/i18n.h index a01d7e285..75588497f 100644 --- a/tasmota/include/i18n.h +++ b/tasmota/include/i18n.h @@ -286,6 +286,7 @@ #define D_CMND_STATE "State" #define D_CMND_POWER "Power" #define D_CMND_TIMEDPOWER "TimedPower" +#define D_CMND_POWERLOCK "PowerLock" #define D_CMND_FANSPEED "FanSpeed" #define D_CMND_POWERONSTATE "PowerOnState" #define D_CMND_PULSETIME "PulseTime" diff --git a/tasmota/include/tasmota_types.h b/tasmota/include/tasmota_types.h index a415c847a..680eda700 100755 --- a/tasmota/include/tasmota_types.h +++ b/tasmota/include/tasmota_types.h @@ -857,9 +857,7 @@ typedef struct { uint16_t flowratemeter_calibration[2];// F78 int32_t energy_kWhexport_ph[3]; // F7C uint32_t eth_ipv4_address[5]; // F88 - - uint32_t ex_energy_kWhtotal; // F9C - + uint32_t power_lock; // F9C SBitfield1 sbflag1; // FA0 TeleinfoCfg teleinfo; // FA4 uint64_t rf_protocol_mask; // FA8 diff --git a/tasmota/include/tasmota_version.h b/tasmota/include/tasmota_version.h index cf235db62..1014d8c9d 100644 --- a/tasmota/include/tasmota_version.h +++ b/tasmota/include/tasmota_version.h @@ -22,6 +22,6 @@ #define TASMOTA_SHA_SHORT // Filled by Github sed -const uint32_t TASMOTA_VERSION = 0x0D040003; // 13.4.0.3 +const uint32_t TASMOTA_VERSION = 0x0D040004; // 13.4.0.4 #endif // _TASMOTA_VERSION_H_ diff --git a/tasmota/tasmota_support/settings.ino b/tasmota/tasmota_support/settings.ino index 1ee96f41b..4f554a945 100644 --- a/tasmota/tasmota_support/settings.ino +++ b/tasmota/tasmota_support/settings.ino @@ -46,7 +46,7 @@ void RtcSettingsSave(void) { memset(&RtcSettings, 0, sizeof(RtcSettings)); RtcSettings.valid = RTC_MEM_VALID; // RtcSettings.ex_energy_kWhtoday = Settings->energy_power_calibration2; // = ex_energy_kWhtoday -// RtcSettings.ex_energy_kWhtotal = Settings->ex_energy_kWhtotal; +// RtcSettings.ex_energy_kWhtotal = Settings->power_lock; for (uint32_t i = 0; i < 3; i++) { RtcSettings.energy_kWhtoday_ph[i] = Settings->energy_kWhtoday_ph[i]; RtcSettings.energy_kWhtotal_ph[i] = Settings->energy_kWhtotal_ph[i]; @@ -1639,7 +1639,7 @@ void SettingsDelta(void) { memset(&Settings->sensors, 0xFF, 16); // Enable all possible sensors } if (Settings->version < 0x09050004) { - Settings->ex_energy_kWhtotal = Settings->ipv4_address[4]; + Settings->power_lock = Settings->ipv4_address[4]; ParseIPv4(&Settings->ipv4_address[4], PSTR(WIFI_DNS2)); } if (Settings->version < 0x09050005) { @@ -1664,7 +1664,7 @@ void SettingsDelta(void) { if (Settings->version < 0x09050009) { // 9.5.0.9 memset(&Settings->energy_kWhtoday_ph, 0, 36); memset(&RtcSettings.energy_kWhtoday_ph, 0, 24); - Settings->energy_kWhtotal_ph[0] = Settings->ex_energy_kWhtotal; + Settings->energy_kWhtotal_ph[0] = Settings->power_lock; Settings->energy_kWhtoday_ph[0] = Settings->energy_power_calibration2; // = ex_energy_kWhtoday Settings->energy_kWhyesterday_ph[0] = Settings->energy_voltage_calibration2; // = ex_energy_kWhyesterday RtcSettings.energy_kWhtoday_ph[0] = RtcSettings.ex_energy_kWhtoday; @@ -1773,6 +1773,9 @@ void SettingsDelta(void) { } #endif */ + if (Settings->version < 0x0D040004) { // 13.4.0.4 + Settings->power_lock = 0; + } Settings->version = TASMOTA_VERSION; SettingsSave(1); diff --git a/tasmota/tasmota_support/support_command.ino b/tasmota/tasmota_support/support_command.ino index 3a79e9e6c..ec483b167 100644 --- a/tasmota/tasmota_support/support_command.ino +++ b/tasmota/tasmota_support/support_command.ino @@ -23,7 +23,7 @@ const char kTasmotaCommands[] PROGMEM = "|" // No prefix // Other commands D_CMND_UPGRADE "|" D_CMND_UPLOAD "|" D_CMND_OTAURL "|" D_CMND_SERIALLOG "|" D_CMND_RESTART "|" #ifndef FIRMWARE_MINIMAL_ONLY - D_CMND_BACKLOG "|" D_CMND_DELAY "|" D_CMND_POWER "|" D_CMND_TIMEDPOWER "|" D_CMND_STATUS "|" D_CMND_STATE "|" D_CMND_SLEEP "|" + D_CMND_BACKLOG "|" D_CMND_DELAY "|" D_CMND_POWER "|" D_CMND_POWERLOCK "|" D_CMND_TIMEDPOWER "|" D_CMND_STATUS "|" D_CMND_STATE "|" D_CMND_SLEEP "|" D_CMND_POWERONSTATE "|" D_CMND_PULSETIME "|" D_CMND_BLINKTIME "|" D_CMND_BLINKCOUNT "|" D_CMND_SAVEDATA "|" D_CMND_SO "|" D_CMND_SETOPTION "|" D_CMND_TEMPERATURE_RESOLUTION "|" D_CMND_HUMIDITY_RESOLUTION "|" D_CMND_PRESSURE_RESOLUTION "|" D_CMND_POWER_RESOLUTION "|" D_CMND_VOLTAGE_RESOLUTION "|" D_CMND_FREQUENCY_RESOLUTION "|" D_CMND_CURRENT_RESOLUTION "|" D_CMND_ENERGY_RESOLUTION "|" D_CMND_WEIGHT_RESOLUTION "|" @@ -63,7 +63,7 @@ SO_SYNONYMS(kTasmotaSynonyms, void (* const TasmotaCommand[])(void) PROGMEM = { &CmndUpgrade, &CmndUpgrade, &CmndOtaUrl, &CmndSeriallog, &CmndRestart, #ifndef FIRMWARE_MINIMAL_ONLY - &CmndBacklog, &CmndDelay, &CmndPower, &CmndTimedPower, &CmndStatus, &CmndState, &CmndSleep, + &CmndBacklog, &CmndDelay, &CmndPower, &CmndPowerLock, &CmndTimedPower, &CmndStatus, &CmndState, &CmndSleep, &CmndPowerOnState, &CmndPulsetime, &CmndBlinktime, &CmndBlinkcount, &CmndSavedata, &CmndSetoption, &CmndSetoption, &CmndTemperatureResolution, &CmndHumidityResolution, &CmndPressureResolution, &CmndPowerResolution, &CmndVoltageResolution, &CmndFrequencyResolution, &CmndCurrentResolution, &CmndEnergyResolution, &CmndWeightResolution, @@ -658,6 +658,26 @@ void CmndPower(void) } } +void CmndPowerLock(void) { + // PowerLock - Show current state + // PowerLock0 0 - Reset all power locks + // PowerLock0 1 - Set all power locks + // PowerLock1 1 - Set Power1 lock + if (XdrvMailbox.index <= TasmotaGlobal.devices_present) { + if (XdrvMailbox.payload >= 0) { + XdrvMailbox.payload &= 1; + if (0 == XdrvMailbox.index) { // Control all bits + Settings->power_lock = (XdrvMailbox.payload) ? (1 << TasmotaGlobal.devices_present) -1 : 0; + } else { // Control individual bits + bitWrite(Settings->power_lock, XdrvMailbox.index -1, XdrvMailbox.payload & 1); + } + } + char stemp1[33]; + ext_snprintf_P(stemp1, sizeof(stemp1), PSTR("%*_b"), TasmotaGlobal.devices_present, Settings->power_lock); + ResponseCmndChar(stemp1); + } +} + /********************************************************************************************/ typedef struct { diff --git a/tasmota/tasmota_support/support_tasmota.ino b/tasmota/tasmota_support/support_tasmota.ino index 47d407268..f7e7f6498 100644 --- a/tasmota/tasmota_support/support_tasmota.ino +++ b/tasmota/tasmota_support/support_tasmota.ino @@ -693,9 +693,9 @@ void ExecuteCommandPower(uint32_t device, uint32_t state, uint32_t source) #ifdef USE_SONOFF_IFAN if (IsModuleIfan()) { - TasmotaGlobal.blink_mask &= 1; // No blinking on the fan relays - Settings->flag.interlock = 0; // No interlock mode as it is already done by the microcontroller - CMND_INTERLOCK - Enable/disable interlock - Settings->pulse_timer[1] = 0; // No pulsetimers on the fan relays + TasmotaGlobal.blink_mask &= 1; // No blinking on the fan relays + Settings->flag.interlock = 0; // No interlock mode as it is already done by the microcontroller - CMND_INTERLOCK - Enable/disable interlock + Settings->pulse_timer[1] = 0; // No pulsetimers on the fan relays Settings->pulse_timer[2] = 0; Settings->pulse_timer[3] = 0; } @@ -703,7 +703,7 @@ void ExecuteCommandPower(uint32_t device, uint32_t state, uint32_t source) bool publish_power = true; if ((state >= POWER_OFF_NO_STATE) && (state <= POWER_TOGGLE_NO_STATE)) { - state &= 3; // POWER_OFF, POWER_ON or POWER_TOGGLE + state &= 3; // POWER_OFF, POWER_ON or POWER_TOGGLE publish_power = false; } @@ -712,26 +712,31 @@ void ExecuteCommandPower(uint32_t device, uint32_t state, uint32_t source) } TasmotaGlobal.active_device = device; + if (bitRead(Settings->power_lock, device -1)) { + AddLog(LOG_LEVEL_INFO, PSTR("CMD: Power%d is LOCKED"), device); + state = POWER_SHOW_STATE; // Only show state. Make no change + } + if (state != POWER_SHOW_STATE) { SetPulseTimer((device -1) % MAX_PULSETIMERS, 0); } - static bool interlock_mutex = false; // Interlock power command pending - power_t mask = 1 << (device -1); // Device to control + static bool interlock_mutex = false; // Interlock power command pending + power_t mask = 1 << (device -1); // Device to control if (state <= POWER_TOGGLE) { if ((TasmotaGlobal.blink_mask & mask)) { TasmotaGlobal.blink_mask &= (POWER_MASK ^ mask); // Clear device mask MqttPublishPowerBlinkState(device); } - if (Settings->flag.interlock && // CMND_INTERLOCK - Enable/disable interlock + if (Settings->flag.interlock && // CMND_INTERLOCK - Enable/disable interlock !interlock_mutex && ((POWER_ON == state) || ((POWER_TOGGLE == state) && !(TasmotaGlobal.power & mask))) ) { - interlock_mutex = true; // Clear all but masked relay in interlock group if new set requested + interlock_mutex = true; // Clear all but masked relay in interlock group if new set requested bool perform_interlock_delay = false; for (uint32_t i = 0; i < MAX_INTERLOCKS; i++) { - if (Settings->interlock[i] & mask) { // Find interlock group + if (Settings->interlock[i] & mask) { // Find interlock group for (uint32_t j = 0; j < TasmotaGlobal.devices_present; j++) { power_t imask = 1 << j; if ((Settings->interlock[i] & imask) && (TasmotaGlobal.power & imask) && (mask != imask)) { @@ -739,11 +744,11 @@ void ExecuteCommandPower(uint32_t device, uint32_t state, uint32_t source) perform_interlock_delay = true; } } - break; // An interlocked relay is only present in one group so quit + break; // An interlocked relay is only present in one group so quit } } if (perform_interlock_delay) { - delay(50); // Add some delay to make sure never have more than one relay on + delay(50); // Add some delay to make sure never have more than one relay on } interlock_mutex = false; } @@ -791,7 +796,7 @@ void ExecuteCommandPower(uint32_t device, uint32_t state, uint32_t source) } TasmotaGlobal.blink_timer = millis() + 100; TasmotaGlobal.blink_counter = ((!Settings->blinkcount) ? 64000 : (Settings->blinkcount *2)) +1; - TasmotaGlobal.blink_mask |= mask; // Set device mask + TasmotaGlobal.blink_mask |= mask; // Set device mask MqttPublishPowerBlinkState(device); return; }