From a72acf37fe8545b089b2c6ba0872d8403e97d4f0 Mon Sep 17 00:00:00 2001 From: Paul C Diem Date: Fri, 13 Mar 2020 12:08:44 -0500 Subject: [PATCH 1/4] Use Light module to manage PWM, Rework multi-button sequences --- Device_Groups.md | 12 +- PWM_Dimmer.md | 105 +--- tasmota/i18n.h | 1 - tasmota/my_user_config.h | 4 +- tasmota/support_device_groups.ino | 7 +- tasmota/support_tasmota.ino | 10 +- tasmota/tasmota.h | 2 +- tasmota/xdrv_01_webserver.ino | 11 - tasmota/xdrv_04_light.ino | 79 +-- tasmota/xdrv_12_home_assistant.ino | 6 +- tasmota/xdrv_35_pwm_dimmer.ino | 903 ++++++++++------------------- 11 files changed, 392 insertions(+), 748 deletions(-) diff --git a/Device_Groups.md b/Device_Groups.md index 4f2e9530e..40bca68a7 100644 --- a/Device_Groups.md +++ b/Device_Groups.md @@ -6,7 +6,7 @@ UDP multicasts, followed by UDP unicasts if necessary, are used to send updates To include device groups support in the build, define USE_DEVICE_GROUPS in your user_config_override. This adds 3.5K to the code size. All devices in a group must be running firmware with device group support and have device groups enabled. -To enable device groups, set Option85 to 1. +To enable device groups, execute the command SetOption85 1. ## Device Groups Operation @@ -29,17 +29,15 @@ The items that are sent to the group and the items that are received from the gr DevGroupShare - ,<out> = set incoming and outgoing shared item mask (default = 0xffffffff,0xffffffff) -

-1 = Power, 2 = Light brightness, 4 = Light fade/speed, 8 = Light scheme, 16 = Light color, 32 = Minimum brightness + <in>,<out> = set incoming and outgoing shared item mask (default = 0xffffffff,0xffffffff)
+1 = Power, 2 = Light brightness, 4 = Light fade/speed, 8 = Light scheme, 16 = Light color, 32 = Dimmer settings (presets) GroupTopic<x> - 1 = reset device group <x> MQTT group topic to firmware default (MQTT_GRPTOPIC) and restart -

- = set device group <x> MQTT group topic (32 chars max) and restart + 1 = reset device group <x> MQTT group topic to firmware default (MQTT_GRPTOPIC) and restart
+<value> = set device group <x> MQTT group topic (32 chars max) and restart \ No newline at end of file diff --git a/PWM_Dimmer.md b/PWM_Dimmer.md index 5ba143fec..0ac600f53 100644 --- a/PWM_Dimmer.md +++ b/PWM_Dimmer.md @@ -11,100 +11,28 @@ To enable PWM dimmer operation, select the PWM Dimmer module. Pressing and releasing the power button toggles the power on/off. If the toggle turns the power on, the load is returned to the last brightness it was adjusted to. If Fade is enabled, the load is faded on/off at the rate defined by the Speed setting. -When the power is on, holding the down or up button decreases/increases the brightness (PWM value). The brightness is changed faster at higher brightnesses. The DimmerRange command sets the minimum and maximum PWM values. Brightness values (0 through 255) are scaled to dimmerMin through dimmerMax. Typically, dimmerMin would be set to the lowest value at which the lights show visible light and dimmerMax would be set 1023. If you want to increase the lowest brightness level and/or decrease the highest brightness level, set higher dimmerMin and/or lower dimmerMax values. +When the power is on, holding the down or up button decreases/increases the brightness. The brightness can also be changed using just the power button. When the power is on, holding the power button alternately increases or decreases the brightness. Initially, holding the power button increases the brightness. Releasing and then holding the power button again decreases the brightness. -The brightness can also be changed using just the power button. When the power is on, holding the power button alternately increases or decreases the brightness. Initially, holding the power button increases the brightness. Releasing and then holding the power button again decreases the brightness. +When the power is off, pressing and releasing the down or up button turns the power on at a temporary brightness of the low/high levels set by the DimmerPreset command. Turning the power on at the low preset can also be accomplished by holding the power button while the power is off. The dimmer presets are intended to enable quickly turning on a light to a dim or bright level without changing the normal desired brightness. Turning the power on to a preset does not change the brightness the load will be set to when the switch is turned on the next time. For example, if the light is on and you adjust the brightness to 80 and then turn the light off, when you turn it back on, the brightness will return to 80. If you turn the power off again and then press the down button, the light will be turned on with a brightness of the low preset. If you then turn the light off and on again, the brightness will return to 80. -When the power is off, pressing the down or up button turns the power on at a temporary brightness of the low/high levels set by the BriPreset command (default =10,255). Turning the power on at the low preset can also be accomplished by holding the power button while the power is off. The brightness presets are intended to enable quickly turning on a light to a dim or bright level without changing the normal desired brightness. Turning the power on to a preset does not change the brightness the load will be set to when the switch is turned on the next time. For example, if the light is on and you adjust the brightness to 80 and then turn the light off, when you turn it back on, the brightness will return to 80. If you turn the power off again and then press the down button, the light will be turned on with a brightness of the low preset. If you then turn the light off and on again, the brightness will return to 80. +When the power is off, holding the down or up button publishes an MQTT EVENT command. The topic follows the format of the Full Topic with a prefix of Event (ex. cmnd/LightSwitch1/EVENT). The MQTT payload is Trigger#, where # is 1 if the down button is held or 2 if the up button is held. These triggers can be used in rules on remote devices (ON Event#Trigger1) or by automation software to trigger automations such as scene changes. For example, the Event topic Trigger1 payload could trigger the automation software to turn on the previous scene in a list and the Trigger2 payload could trigger the automation software to turn on the next scene in a list. -If there are LED’s defined in the template, they are turned on to indicate the current brightness. More LEDs are turned on at higher brightnesses. The LedTimeout command enables/disables an LED timeout. If LED timeout is enabled, the LED’s turn off five seconds after the last change in brightness. Note that the lowest LED and the blue power LED are always on when the power is on. +If there are LED’s defined in the template, they are turned on to indicate the current brightness. More LEDs are turned on at higher brightnesses. The LedTimeout command enables/disables an LED timeout. If LED timeout is enabled, the LED’s turn off five seconds after the last change in brightness. Note that the lowest LED and the blue power LED are always on when the power is on. The LED timeout can also be enabled/disabled by holding the power button while tapping (pressing and releasing quickly) the down button. -The LEDLink LED can be used as a nightlight/powered-off indicator. The PoweredOffLed command enables/disables turning the LEDLink LED on when the power is off. +The LEDLink LED can be used as a nightlight/powered-off indicator. The PoweredOffLed command enables/disables turning the LEDLink LED on when the power is off. The powered-off indicator can also be enabled/disabled by holding the power button and tapping the up button. -When the power is on, tapping (pressing and releasing quickly) the down or up buttons a given number of times and then holding the down or up button decreases or increases settings according to the table below. For example, tapping the down button once and then holding the up button sets all RGB lights in the device group to the next fixed color. Tapping the up button three times and then holding the down button decreases the high brightness preset. +Holding the power button, tapping the down button and then tapping or holding the down or up button sends a device group message to set RGB lights in the device group to the previous/next fixed color. The command is sent/value is adjusted once every .5 seconds for as long as the button is held. The color sequence as defined by the Light module is red, green, blue, orange, light green, light blue, amber, cyan, purple, yellow, pink, white using RGB channels, white using CT channels. +Holding the power button, tapping the up button and then tapping or holding the down or up button publishes an MQTT Event command. The command is sent/value is adjusted once every .5 seconds for as long as the button is held. The MQTT topic is as described above. The MQTT payload is Trigger#, where # is 3 if the down button is held or 4 if the up button is held. - - - - - - - - - - - - - - - - - - - - - -
Taps - Down Button - Up Button -
1 - Set fixed color1 - Publish MQTT event2 -
2 - Adjust minimum brightness - Adjust fade speed -
3 - Adjust low brightness preset - Adjust high brightness preset -
+Holding the down or up button alone for over 10 seconds executes the WiFiConfig 2 command. +Pressing and releasing any button publishes an MQTT TOGGLE command for the button. Holding a button publishes an MQTT HOLD command followed by an MQTT OFF command when the button is released. -1. Setting the previous/next color only functions when remote device mode is enabled (see below) and only when the switch is in a device group with an RGB light. The color sequence as defined by the Light module is red, green, blue, orange, light green, light blue, amber, cyan, purple, yellow, pink, white using RGB channels, white using CT channels. - -2. The MQTT topic has the format %group-topic%/cmnd/Event, where %group-topic% is the group topic set by the GroupTopic command. The MQTT payload is SwitchTrigger#, where # is 1 if the down button is held or 2 if the up button is held. These triggers can be used in rules on remote devices (ON Event#SwitchTrigger1) or by automation software to trigger automations such as scene changes. For example, the Event topic SwitchTrigger1 payload could trigger the automation software to turn on the previous scene in a list and the SwitchTrigger2 payload could trigger the automation software to turn on the next scene in a list. - -Holding the power button, pressing the down/up buttons a given number of times and then releasing the power button toggles options according to the table below. Note that you must press a down or up button within 0.5 seconds to prevent the power button hold action from taking place. - - - - - - - - - - - - - - - - - - -
Presses - Down Button - Up Button -
1 - Toggle powered-off LED - Toggle brightness LED timeout -
2 - Toggle fading - -
- - -Holding any button for over 10 seconds executes the WiFiConfig 2 command. - -Pressing and releasing a button publishes an MQTT TOGGLE command. Holding a button publishes an MQTT HOLD command followed by an MQTT OFF command when the button is released. - -When Device Groups are enabled, the PWM Dimmer brightness presets, dimmer minimum and maximum values, fade and speed settings are kept in sync across all switches in the group. The powered-off LED and LED timeout settings are specific to each switch. Changing them does not replicate the change to the other switches in the group. - +When Device Groups are enabled, the PWM Dimmer brightness presets are kept in sync across all switches in the group. The powered-off LED and LED timeout settings are specific to each switch. Changing them does not replicate the change to the other switches in the group. ### Commands - - - - - @@ -162,12 +83,12 @@ Remote device mode allows PWM Dimmer switches to control remote devices. With re To include remote device mode support in the build, define USE_PWM_DIMMER_REMOTE in your user_config_override. Remote device mode support requires device group support so USE_DEVICE_GROUPS is automatically defined if USE_PWM_DIMMER_REMOTE is defined. Remote device mode support adds 0.7K to the code size in addition to the code size required for device groups support. -To enable remote device mode, set Option88 to 1. Each remote device must be running firmware with device group support and have remote device support enabled. The remote devices do not need to be built with PWM dimmer support nor do they need to be switches. +To enable remote device mode, execute the command SetOption88 1. Each remote device must be running firmware with device group support and have remote device support enabled. The remote devices do not need to be built with PWM dimmer support nor do they need to be switches. If a remote device is a PWM dimmer, the device acts like a 3-way dimmer switch would and may or may not have a load connected to it. It’s also possible to use a PWM dimmer switch without a load to act as a wall switch to control the power, brightness and color of one or more smart lights with Tasmota with device group support loaded on them. -With remote device mode enabled, button 1 is the power button for the local device while buttons 2 and 3 are the power buttons for remote devices. Group names for buttons 2 and 3 are set by the GroupTopic2 and GroupTopic3 commands respectively. Note that the button numbers are defined by the module template and can be in any physical order on the switch (button 1 can be defined as the top button, the middle button or the bottom button). +With remote device mode is enabled, button 1 is the power button for the local device while buttons 2 and 3 are the power buttons for remote devices. Group names for buttons 2 and 3 are set by the GroupTopic2 and GroupTopic3 commands respectively. Note that the button numbers are defined by the module template and can be in any physical order on the switch (button 1 can be defined as the top button, the middle button or the bottom button). Button combinations that publish MQTT Event commands use a topic in the format cmnd/%group-topic%/EVENT. -Pressing and releasing a power button toggles the power on all devices in the group assigned to the button. When the power is on, holding the button alternately increases or decreases the brightness. When the power is off, holding the button turns the power on at a temporary brightness of the low level set by the BriPreset command (default =10). +Pressing and releasing a power button toggles the power on all devices in the group assigned to the button. When the power is on, holding the button alternately increases or decreases the brightness. When the power is off, holding the button turns the power on at a temporary brightness of the low level set by the BriPreset command. While holding a power button, the other two buttons act like the down and up buttons for the remote device. All the functions performed by the down and up buttons in non-remote device mode are available in remote device mode. While holding button 1, button 2 performs the functions of the down button and button 3 performs the functions of the up button. While holding button 2, button 1 performs the functions of the down button and button 3 performs the functions of the up button. While holding button 3, button 1 performs the functions of the down button and button 2 performs the functions of the up button. diff --git a/tasmota/i18n.h b/tasmota/i18n.h index 8a0b210a8..9e48a7e50 100644 --- a/tasmota/i18n.h +++ b/tasmota/i18n.h @@ -549,7 +549,6 @@ // Commands xdrv_34_pwm_dimmer.ino #ifdef USE_PWM_DIMMER -#define D_CMND_BRI_MIN "BriMin" #define D_CMND_BRI_PRESET "BriPreset" #endif diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index a62c6d191..d7bbc8900 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -417,8 +417,8 @@ // #define EXS_MCU_CMNDS // Add command to send MCU commands (+0k8 code) //#define USE_HOTPLUG // Add support for sensor HotPlug #define USE_DEVICE_GROUPS // Add support for device groups (+5k6 code) -#define USE_PWM_DIMMER // Add support for MJ-SD01/acenx/NTONPOWER PWM dimmers (+4k5 code) - #define USE_PWM_DIMMER_REMOTE // Add support for remote switches to PWM Dimmer, also adds device groups support (+0k7 code, also includes device groups) +#define USE_PWM_DIMMER // Add support for MJ-SD01/acenx/NTONPOWER PWM dimmers (+2k5 code) + #define USE_PWM_DIMMER_REMOTE // Add support for remote switches to PWM Dimmer, also adds device groups support (+1k1 code plus device groups size) //#define USE_KEELOQ // Add support for Jarolift rollers by Keeloq algorithm (+4k5 code) #define USE_SONOFF_D1 // Add support for Sonoff D1 Dimmer (+0k7 code) diff --git a/tasmota/support_device_groups.ino b/tasmota/support_device_groups.ino index d0844de7a..fe1e94482 100644 --- a/tasmota/support_device_groups.ino +++ b/tasmota/support_device_groups.ino @@ -111,6 +111,7 @@ bool DeviceGroupItemShared(bool incoming, uint8_t item) uint8_t mask = 0; switch (item) { case DGR_ITEM_LIGHT_BRI: + case DGR_ITEM_BRI_POWER_ON: mask = DGR_SHARE_LIGHT_BRI; break; case DGR_ITEM_POWER: @@ -127,8 +128,9 @@ bool DeviceGroupItemShared(bool incoming, uint8_t item) case DGR_ITEM_LIGHT_SPEED: mask = DGR_SHARE_LIGHT_FADE; break; - case DGR_ITEM_DIMMER_RANGE: - mask = DGR_SHARE_DIMMER_RANGE; + case DGR_ITEM_BRI_PRESET_LOW: + case DGR_ITEM_BRI_PRESET_HIGH: + mask = DGR_SHARE_DIMMER_SETTINGS; break; } return (!mask || ((incoming ? Settings.device_group_share_in : Settings.device_group_share_out) & mask)); @@ -544,7 +546,6 @@ void ProcessDeviceGroupMessage(char * packet, int packet_length) case DGR_ITEM_BRI_POWER_ON: case DGR_ITEM_POWER: case DGR_ITEM_LIGHT_CHANNELS: - case DGR_ITEM_DIMMER_RANGE: break; default: AddLog_P2(LOG_LEVEL_ERROR, PSTR("DGR: ********** invalid item=%u received from device group %s member %s"), item, device_group->group_name, IPAddressToString(remote_ip)); diff --git a/tasmota/support_tasmota.ino b/tasmota/support_tasmota.ino index 19f592a18..2eca4eefc 100644 --- a/tasmota/support_tasmota.ino +++ b/tasmota/support_tasmota.ino @@ -641,12 +641,6 @@ void MqttShowState(void) break; } #endif // USE_SONOFF_IFAN -#ifdef USE_PWM_DIMMER - if (PWM_DIMMER == my_module_type) { - ResponseAppend_P(PSTR(",\"" D_CMND_DIMMER "\":%d,\"" D_CMND_FADE "\":\"%s\",\"" D_CMND_SPEED "\":%d"), - Settings.light_dimmer, GetStateText(Settings.light_fade), Settings.light_speed); - } -#endif // USE_PWM_DIMMER #ifdef USE_LIGHT } #endif @@ -1479,6 +1473,10 @@ void GpioInit(void) digitalWrite(pin[GPIO_LEDLNK], ledlnk_inverted); } +#ifdef USE_PWM_DIMMER + if (PWM_DIMMER == my_module_type && pin[GPIO_REL1] < 99) devices_present--; +#endif // USE_PWM_DIMMER + ButtonInit(); SwitchInit(); #ifdef ROTARY_V1 diff --git a/tasmota/tasmota.h b/tasmota/tasmota.h index 664446502..8585bc020 100644 --- a/tasmota/tasmota.h +++ b/tasmota/tasmota.h @@ -323,7 +323,7 @@ enum DeviceGroupItem { DGR_ITEM_EOL, DGR_ITEM_STATUS, DGR_ITEM_LIGHT_CHANNELS }; enum DeviceGroupShareItem { DGR_SHARE_POWER = 1, DGR_SHARE_LIGHT_BRI = 2, DGR_SHARE_LIGHT_FADE = 4, DGR_SHARE_LIGHT_SCHEME = 8, - DGR_SHARE_LIGHT_COLOR = 16, DGR_SHARE_DIMMER_RANGE = 32 }; + DGR_SHARE_LIGHT_COLOR = 16, DGR_SHARE_DIMMER_SETTINGS = 32 }; enum CommandSource { SRC_IGNORE, SRC_MQTT, SRC_RESTART, SRC_BUTTON, SRC_SWITCH, SRC_BACKLOG, SRC_SERIAL, SRC_WEBGUI, SRC_WEBCOMMAND, SRC_WEBCONSOLE, SRC_PULSETIMER, SRC_TIMER, SRC_RULE, SRC_MAXPOWER, SRC_MAXENERGY, SRC_OVERTEMP, SRC_LIGHT, SRC_KNX, SRC_DISPLAY, SRC_WEMO, SRC_HUE, SRC_RETRY, SRC_REMOTE, SRC_SHUTTER, diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino index aea0d4c78..8dcae9523 100644 --- a/tasmota/xdrv_01_webserver.ino +++ b/tasmota/xdrv_01_webserver.ino @@ -1125,17 +1125,6 @@ void HandleRoot(void) } // Settings.flag3.pwm_multi_channels } #endif // USE_LIGHT -#ifdef USE_PWM_DIMMER - if (PWM_DIMMER == my_module_type) { - WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, // Brightness - Black to White - "c", // c - Unique HTML id - "#000", "#fff", // Black to White - 4, // sl4 - Unique range HTML id - Used as source for Saturation begin color - Settings.flag3.slider_dimmer_stay_on, 100, // Range 0/1 to 100% - Settings.light_dimmer, - 'd', 0); // d0 - Value id is related to lc("d0", value) and WebGetArg("d0", tmp, sizeof(tmp)); - } -#endif // USE_PWM_DIMMER #ifdef USE_SHUTTER if (Settings.flag3.shutter_mode) { // SetOption80 - Enable shutter support for (uint32_t i = 0; i < shutters_present; i++) { diff --git a/tasmota/xdrv_04_light.ino b/tasmota/xdrv_04_light.ino index 9a3bf7abc..d7b7d3d24 100644 --- a/tasmota/xdrv_04_light.ino +++ b/tasmota/xdrv_04_light.ino @@ -545,6 +545,9 @@ class LightStateClass { #ifdef DEBUG_LIGHT AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setBri RGB raw (%d %d %d) HS (%d %d) bri (%d)", _r, _g, _b, _hue, _sat, _briRGB); #endif +#ifdef USE_PWM_DIMMER + if (PWM_DIMMER == my_module_type) PWMDimmerSetBrightnessLeds(0); +#endif // USE_PWM_DIMMER } // changes the RGB brightness alone @@ -1858,6 +1861,10 @@ void LightAnimate(void) LightSetOutputs(Light.fade_cur_10); } } +#ifdef USE_PWM_DIMMER + // If the power is off and the fade is done, turn the relay off. + if (PWM_DIMMER == my_module_type && !Light.power && !Light.fade_running) PWMDimmerSetPower(); +#endif // USE_PWM_DIMMER } } @@ -2084,20 +2091,35 @@ void calcGammaBulbs(uint16_t cur_col_10[5]) { #ifdef USE_DEVICE_GROUPS void LightSendDeviceGroupStatus() { - uint8_t channels[LST_MAX]; - light_state.getChannels(channels); - SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_LIGHT_SCHEME, Settings.light_scheme, DGR_ITEM_LIGHT_CHANNELS, channels, - DGR_ITEM_LIGHT_BRI, (power ? light_state.getBri() : 0)); + if (Light.subtype > LST_SINGLE) { + uint8_t channels[LST_MAX]; + light_state.getChannels(channels); + SendLocalDeviceGroupMessage(DGR_MSGTYP_PARTIAL_UPDATE, DGR_ITEM_LIGHT_CHANNELS, channels); + } + SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_LIGHT_SCHEME, Settings.light_scheme, + DGR_ITEM_LIGHT_BRI, light_state.getBri()); } void LightHandleDeviceGroupItem() { static bool send_state = false; + static bool restore_power = false; + bool more_to_come; uint32_t value = XdrvMailbox.payload; +#ifdef USE_PWM_DIMMER_REMOTE + if (XdrvMailbox.index & 0xff00) return; // Ignore updates from other device groups +#endif // USE_PWM_DIMMER_REMOTE switch (XdrvMailbox.command_code) { case DGR_ITEM_EOL: + more_to_come = (XdrvMailbox.index & DGR_FLAG_MORE_TO_COME); + if (restore_power && !more_to_come) { + restore_power = false; + Light.power = Light.old_power; + } + LightAnimate(); - if (send_state && !(XdrvMailbox.index & DGR_FLAG_MORE_TO_COME)) { + + if (send_state && !more_to_come) { light_controller.saveSettings(); if (Settings.flag3.hass_tele_on_power) { // SetOption59 - Send tele/%topic%/STATE in addition to stat/%topic%/RESULT MqttPublishTeleState(); @@ -2107,7 +2129,7 @@ void LightHandleDeviceGroupItem() break; case DGR_ITEM_LIGHT_BRI: if (light_state.getBri() != value) { - light_controller.changeBri(value); + light_state.setBri(value); send_state = true; } break; @@ -2122,30 +2144,25 @@ void LightHandleDeviceGroupItem() send_state = true; break; case DGR_ITEM_LIGHT_FIXED_COLOR: - { - struct XDRVMAILBOX save_XdrvMailbox; - power_t save_power = Light.power; - + if (Light.subtype >= LST_RGBW) { if (value) { bool save_decimal_text = Settings.flag.decimal_text; char str[16]; - XdrvMailbox.index = 2; - XdrvMailbox.data_len = sprintf_P(str, PSTR("%u"), value); - XdrvMailbox.data = str; - CmndSupportColor(); + LightColorEntry(str, sprintf_P(str, PSTR("%u"), value)); Settings.flag.decimal_text = save_decimal_text; + uint32_t old_bri = light_state.getBri(); + light_controller.changeChannels(Light.entry_color); + light_controller.changeBri(old_bri); + Settings.light_scheme = 0; } else { - Light.fixed_color_index = 0; - XdrvMailbox.index = 1; - XdrvMailbox.payload = light_state.BriToDimmer(light_state.getBri()); - CmndWhite(); + light_state.setColorMode(LCM_CT); } - if (Light.power != save_power) { - XdrvMailbox.index = save_power; - LightSetPower(); + if (!restore_power && !Light.power) { + Light.old_power = Light.power; + Light.power = 0xff; + restore_power = true; } - XdrvMailbox = save_XdrvMailbox; send_state = true; } break; @@ -2536,6 +2553,10 @@ void CmndDimmer(void) LightPreparePower(); } } +#ifdef USE_DEVICE_GROUPS + Settings.bri_power_on = light_state.getBri();; + SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_BRI_POWER_ON, Settings.bri_power_on); +#endif // USE_DEVICE_GROUPS Light.update = true; if (skip_light_fade) LightAnimate(); } else { @@ -2544,8 +2565,6 @@ void CmndDimmer(void) skip_light_fade = false; } -#endif // USE_LIGHT -#if defined(USE_LIGHT) || defined(USE_PWM_DIMMER) void CmndDimmerRange(void) { // DimmerRange - Show current dimmer range as used by Tuya and PS16DZ Dimmers @@ -2562,15 +2581,10 @@ void CmndDimmerRange(void) Settings.dimmer_hw_min = parm[1]; Settings.dimmer_hw_max = parm[0]; } -#ifdef USE_DEVICE_GROUPS - SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_DIMMER_RANGE, Settings.dimmer_hw_min | Settings.dimmer_hw_max << 16); -#endif // USE_DEVICE_GROUPS if (PWM_DIMMER != my_module_type) restart_flag = 2; } Response_P(PSTR("{\"" D_CMND_DIMMER_RANGE "\":{\"Min\":%d,\"Max\":%d}}"), Settings.dimmer_hw_min, Settings.dimmer_hw_max); } -#endif // #if defined(USE_LIGHT) || defined(USE_PWM_DIMMER) -#ifdef USE_LIGHT void CmndLedTable(void) { @@ -2613,8 +2627,6 @@ void CmndRgbwwTable(void) ResponseCmndChar(scolor); } -#endif // USE_LIGHT -#if defined(USE_LIGHT) || defined(USE_PWM_DIMMER) void CmndFade(void) { // Fade - Show current Fade state @@ -2661,8 +2673,6 @@ void CmndSpeed(void) } ResponseCmndNumber(Settings.light_speed); } -#endif // #if defined(USE_LIGHT) || defined(USE_PWM_DIMMER) -#ifdef USE_LIGHT void CmndWakeupDuration(void) { @@ -2695,9 +2705,6 @@ bool Xdrv04(uint8_t function) bool result = false; if (FUNC_MODULE_INIT == function) { -#ifdef USE_PWM_DIMMER - if (PWM_DIMMER != my_module_type) -#endif // USE_PWM_DIMMER return LightModuleInit(); } else if (light_type) { diff --git a/tasmota/xdrv_12_home_assistant.ino b/tasmota/xdrv_12_home_assistant.ino index 4e002a220..7fb64f66c 100644 --- a/tasmota/xdrv_12_home_assistant.ino +++ b/tasmota/xdrv_12_home_assistant.ino @@ -205,7 +205,7 @@ void HAssAnnounceRelayLight(void) TryResponseAppend_P(HASS_DISCOVER_RELAY, command_topic, value_template, SettingsText(SET_STATE_TXT1), SettingsText(SET_STATE_TXT2)); TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO_SHORT, unique_id, ESP.getChipId()); -#if defined(USE_LIGHT) || defined(USE_PWM_DIMMER) +#ifdef USE_LIGHT if (is_light || PWM_DIMMER == my_module_type) { char *brightness_command_topic = stemp1; @@ -214,7 +214,6 @@ void HAssAnnounceRelayLight(void) strncpy_P(stemp3, Settings.flag.not_power_linked ? PSTR("last") : PSTR("brightness"), sizeof(stemp3)); // SetOption20 - Control power in relation to Dimmer/Color/Ct changes TryResponseAppend_P(HASS_DISCOVER_LIGHT_DIMMER, brightness_command_topic, state_topic, stemp3); -#ifdef USE_LIGHT if (Light.subtype >= LST_RGB) { char *rgb_command_topic = stemp1; @@ -240,9 +239,8 @@ void HAssAnnounceRelayLight(void) GetTopic_P(color_temp_command_topic, CMND, mqtt_topic, D_CMND_COLORTEMPERATURE); TryResponseAppend_P(HASS_DISCOVER_LIGHT_CT, color_temp_command_topic, state_topic); } -#endif // USE_LIGHT } -#endif // defined(USE_LIGHT) || defined(USE_PWM_DIMMER) +#endif // USE_LIGHT TryResponseAppend_P(PSTR("}")); } MqttPublish(stopic, true); diff --git a/tasmota/xdrv_35_pwm_dimmer.ino b/tasmota/xdrv_35_pwm_dimmer.ino index 1b093e640..18cfa8fc6 100644 --- a/tasmota/xdrv_35_pwm_dimmer.ino +++ b/tasmota/xdrv_35_pwm_dimmer.ino @@ -32,52 +32,41 @@ #define XDRV_35 35 -#ifndef MAX_FIXED_COLOR -#define MAX_FIXED_COLOR 12 -#endif // MAX_FIXED_COLOR - -// Note: CmndFade and CmndSpeed are in xdrv_04_light. const char kPWMDimmerCommands[] PROGMEM = "|" // No prefix - D_CMND_BRI_PRESET "|" D_CMND_DIMMER "|" D_CMND_DIMMER_RANGE "|" D_CMND_FADE "|" D_CMND_SPEED; + D_CMND_BRI_PRESET; void (* const PWMDimmerCommand[])(void) PROGMEM = { - CmndBriPreset, &PWMDmmerCmndDimmer, &CmndDimmerRange, &CmndFade, &CmndSpeed }; + &CmndBriPreset }; #ifdef USE_PWM_DIMMER_REMOTE struct remote_pwm_dimmer { power_t power; - uint32_t dimmer_range; - uint8_t light_speed; uint8_t bri_power_on; uint8_t bri_preset_low; uint8_t bri_preset_high; uint8_t fixed_color_index; uint8_t bri; bool power_button_increases_bri; - bool light_fade; }; #endif // USE_PWM_DIMMER_REMOTE uint32_t led_timeout_time = 0; uint32_t turn_off_brightness_leds_time = 0; -uint32_t button_press_count_timeout = 0; +uint32_t button_press_time; uint32_t button_hold_time[3]; -uint8_t current_bri; -uint8_t target_bri; uint8_t restore_powered_off_led = 0; uint8_t power_button_index = 0; uint8_t down_button_index = 1; uint8_t up_button_index = 2; -uint8_t fixed_color_index; uint8_t buttons_pressed = 0; -uint8_t button_press_count[3] = { 0, 0, 0 }; -bool relay_is_on = false; +uint8_t tap_count = 0; bool ignore_power_button_hold; bool ignore_power_button_release; -bool ignore_up_down_button_release = false; +bool down_button_tapped = false; bool button_was_held = false; bool power_button_increases_bri = true; bool invert_power_button_bri_direction = false; +bool restore_brightness_leds = false; bool button_hold_sent[3]; bool button_pressed[3] = { false, false, false }; #ifdef USE_PWM_DIMMER_REMOTE @@ -91,55 +80,23 @@ void PWMModuleInit() Settings.seriallog_level = 0; Settings.flag.mqtt_serial = 0; // Disable serial logging Settings.ledstate = 0; // Disable LED usage - Settings.flag.pwm_control = 0; // Use basic PWM control instead of Light if (Settings.last_module != Settings.module) { - Settings.dimmer_hw_min = 102; - Settings.dimmer_hw_max = 1023; - Settings.bri_power_on = 50; - Settings.bri_preset_low = 25; + Settings.bri_power_on = 128; + Settings.bri_preset_low = 10; Settings.bri_preset_high = 255; Settings.last_module = Settings.module; } - if (Settings.light_speed < 1) Settings.light_speed = 1; - - target_bri = ((Settings.power & 1) ? changeUIntScale(Settings.light_dimmer, 0, 100, 0, 255) : 0); - current_bri = target_bri; - - if (pin[GPIO_PWM1] < 99) { - uint32_t pwm_value = (current_bri ? changeUIntScale(current_bri, 1, 255, Settings.dimmer_hw_min, Settings.dimmer_hw_max) : 0); - analogWrite(pin[GPIO_PWM1], bitRead(pwm_inverted, 0) ? Settings.dimmer_hw_max - pwm_value : pwm_value); - } - - relay_is_on = (current_bri > 0); - if (pin[GPIO_REL1] < 99) DigitalWrite(GPIO_REL1, bitRead(rel_inverted, 0) ? !relay_is_on : relay_is_on); - PWMDimmerSetPoweredOffLed(); - PWMDimmerSetBrightnessLeds(0); -} - -void PWMDimmerInit(void) -{ -#ifdef USE_PWM_DIMMER_REMOTE - if (Settings.flag4.remote_device_mode) { - if (device_group_count > 1) { - if ((remote_pwm_dimmers = (struct remote_pwm_dimmer *) calloc(device_group_count - 1, sizeof(struct remote_pwm_dimmer))) == nullptr) { - AddLog_P2(LOG_LEVEL_ERROR, PSTR("PWMDimmer: error allocating PWM dimmer array")); - Settings.flag4.remote_device_mode = false; - } - } - } - - active_device_is_local = true; -#endif // USE_PWM_DIMMER_REMOTE } // operation: 0 = normal, -1 = all off, 1 = all on void PWMDimmerSetBrightnessLeds(int32_t operation) { if (leds_present) { - uint32_t step = (!operation ? 256 / (leds_present + 1) : operation < 0 ? 255 : 0); + uint32_t step = (!operation ? 256 / (leds_present + 1) : operation < 0 ? 256 : 0); + uint32_t current_bri = (Light.power ? light_state.getBri() : 0); uint32_t level = step; SetLedPowerIdx(0, current_bri >= level); if (leds_present > 1) { @@ -170,68 +127,17 @@ void PWMDimmerSetPoweredOffLed(void) } } -void PWMDimmerAnimate(bool no_fade) +void PWMDimmerSetPower(void) { - // We're only here if something changed. If this is no longer the case, uncomment the following - // line. - //if (current_bri == target_bri) return; - - // Advance the current brightness towards the target. - if (!no_fade && Settings.light_fade) { - uint8_t offset = current_bri / Settings.light_speed / 5 + 1; - uint8_t max_offset = abs(target_bri - current_bri); - if (offset > max_offset) offset = max_offset; - if (current_bri < target_bri) - current_bri += offset; - else - current_bri -= offset; - } - else { - current_bri = target_bri; - } - - // Set the new PWM value. - if (pin[GPIO_PWM1] < 99) { - uint32_t pwm_value = (current_bri ? changeUIntScale(current_bri, 1, 255, Settings.dimmer_hw_min, Settings.dimmer_hw_max) : 0); -//AddLog_P2(LOG_LEVEL_INFO, PSTR("PWMDimmerAnimate: current_bri=%u, pwm_value =%u"), current_bri, pwm_value); - analogWrite(pin[GPIO_PWM1], bitRead(pwm_inverted, 0) ? Settings.dimmer_hw_max - pwm_value : pwm_value); - } - - // Handle a power state change. - if (relay_is_on != (current_bri > 0)) { - bool power_is_on = ((power & 1) != 0); - if (power_is_on == relay_is_on) { - ExecuteCommandPower(1, (relay_is_on ? POWER_OFF : POWER_ON), SRC_SWITCH); - } - - relay_is_on = !relay_is_on; - if (pin[GPIO_REL1] < 99) DigitalWrite(GPIO_REL1, bitRead(rel_inverted, 0) ? !relay_is_on : relay_is_on); - - // Set the powered-off LED. - PWMDimmerSetPoweredOffLed(); - } - - // Set the brightness LED's. + DigitalWrite(GPIO_REL1, bitRead(rel_inverted, 0) ? !power : power); PWMDimmerSetBrightnessLeds(0); -} - -void PWMDimmerSetBri(uint8_t bri) -{ - if (bri == target_bri) return; - target_bri = bri; - Settings.bri_power_on = target_bri; - Settings.light_dimmer = changeUIntScale(target_bri, 0, 255, 0, 100); - if (Settings.flag3.hass_tele_on_power) { // SetOption59 - Send tele/%topic%/STATE in addition to stat/%topic%/RESULT - MqttPublishTeleState(); - } - PWMDimmerAnimate(false); + PWMDimmerSetPoweredOffLed(); } #ifdef USE_DEVICE_GROUPS void PWMDimmerHandleDeviceGroupItem() { - static bool send_state = false; - uint32_t value = XdrvMailbox.payload; + uint8_t value = XdrvMailbox.payload; #ifdef USE_PWM_DIMMER_REMOTE uint8_t device_group_index = XdrvMailbox.index >> 16 & 0xff; bool device_is_local = device_groups[device_group_index].local; @@ -239,58 +145,21 @@ void PWMDimmerHandleDeviceGroupItem() #endif // USE_PWM_DIMMER_REMOTE switch (XdrvMailbox.command_code) { - case DGR_ITEM_EOL: - if (send_state && !(XdrvMailbox.index & DGR_FLAG_MORE_TO_COME)) { - if (Settings.flag3.hass_tele_on_power) { // SetOption59 - Send tele/%topic%/STATE in addition to stat/%topic%/RESULT - MqttPublishTeleState(); - } - send_state = false; - } - break; +#ifdef USE_PWM_DIMMER_REMOTE case DGR_ITEM_LIGHT_BRI: -#ifdef USE_PWM_DIMMER_REMOTE - if (!device_is_local) { - remote_pwm_dimmer->bri = value; - } - else -#endif // USE_PWM_DIMMER_REMOTE - if (target_bri != value) { - PWMDimmerSetBri(value); - send_state = true; - } - break; - case DGR_ITEM_LIGHT_SPEED: -#ifdef USE_PWM_DIMMER_REMOTE - if (!device_is_local) { - remote_pwm_dimmer->light_speed = value; - } - else -#endif // USE_PWM_DIMMER_REMOTE - if (Settings.light_speed != value && value > 0 && value <= 40) { - Settings.light_speed = value; - send_state = true; - } + if (!device_is_local) remote_pwm_dimmer->bri = value; break; case DGR_ITEM_LIGHT_FIXED_COLOR: -#ifdef USE_PWM_DIMMER_REMOTE - if (!device_is_local) { - remote_pwm_dimmer->fixed_color_index = value; - } - else -#endif // USE_PWM_DIMMER_REMOTE - fixed_color_index = value; + if (!device_is_local) remote_pwm_dimmer->fixed_color_index = value; break; - case DGR_ITEM_DIMMER_RANGE: +#endif // USE_PWM_DIMMER_REMOTE + case DGR_ITEM_BRI_POWER_ON: #ifdef USE_PWM_DIMMER_REMOTE if (!device_is_local) - remote_pwm_dimmer->dimmer_range = value; - else { -#endif // USE_PWM_DIMMER_REMOTE - Settings.dimmer_hw_min = value & 0xffff; - Settings.dimmer_hw_max = value >> 16; -#ifdef USE_PWM_DIMMER_REMOTE - } + remote_pwm_dimmer->bri_power_on = value; + else #endif // USE_PWM_DIMMER_REMOTE + Settings.bri_power_on = value; break; case DGR_ITEM_BRI_PRESET_LOW: #ifdef USE_PWM_DIMMER_REMOTE @@ -298,10 +167,7 @@ void PWMDimmerHandleDeviceGroupItem() remote_pwm_dimmer->bri_preset_low = value; else #endif // USE_PWM_DIMMER_REMOTE - if (Settings.bri_preset_low != value) { Settings.bri_preset_low = value; - send_state = true; - } break; case DGR_ITEM_BRI_PRESET_HIGH: #ifdef USE_PWM_DIMMER_REMOTE @@ -309,31 +175,14 @@ void PWMDimmerHandleDeviceGroupItem() remote_pwm_dimmer->bri_preset_high = value; else #endif // USE_PWM_DIMMER_REMOTE - if (Settings.bri_preset_high != value) { Settings.bri_preset_high = value; - send_state = true; - } - break; - case DGR_ITEM_BRI_POWER_ON: -#ifdef USE_PWM_DIMMER_REMOTE - if (!device_is_local) - remote_pwm_dimmer->bri_power_on = value; - else -#endif // USE_PWM_DIMMER_REMOTE - if (Settings.bri_power_on != value) { - Settings.bri_power_on = value; - send_state = true; - } break; case DGR_ITEM_STATUS: #ifdef USE_PWM_DIMMER_REMOTE if (device_is_local) #endif // USE_PWM_DIMMER_REMOTE - SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_LIGHT_FADE, Settings.light_fade, - DGR_ITEM_LIGHT_SPEED, Settings.light_speed, DGR_ITEM_LIGHT_BRI, target_bri, - DGR_ITEM_BRI_PRESET_LOW, Settings.bri_preset_low, DGR_ITEM_BRI_PRESET_HIGH, Settings.bri_preset_high, - DGR_ITEM_DIMMER_RANGE, Settings.dimmer_hw_min | Settings.dimmer_hw_max << 16, - DGR_ITEM_BRI_POWER_ON, Settings.bri_power_on); + SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_BRI_POWER_ON, Settings.bri_power_on, + DGR_ITEM_BRI_PRESET_LOW, Settings.bri_preset_low, DGR_ITEM_BRI_PRESET_HIGH, Settings.bri_preset_high); break; } } @@ -352,8 +201,8 @@ void PWMDimmerHandleButton() * Hold while Press & release down Any Any Toggle/change options * Hold while Press up On Yes Brighter * Hold while Press down On Yes Dimmer - * Released Press up On No Brigther - * Released Press down On No Dimmer + * Released Hold up On No Brigther + * Released Hold down On No Dimmer * Released Press & release up Off No Power on at bri preset low * Released Press & release down Off No Power on at bri preset high * @@ -369,11 +218,32 @@ void PWMDimmerHandleButton() * 2 1 3 * 3 1 2 */ + + // If the button is not pressed and was not just released (the most common case), ... + if (XdrvMailbox.payload && !button_pressed[XdrvMailbox.index]) { + + // If no buttons have been pressed for 250ms, reset the button press counts. + if (button_press_time && !buttons_pressed && millis() - button_press_time > 400) { + button_was_held = false; + tap_count = 0; + } + + return; + } + uint32_t button_index = XdrvMailbox.index; uint32_t now = millis(); - bool toggle_power = false; + uint8_t power_on_bri = 0; uint8_t dgr_item = 0; uint8_t dgr_value; + uint8_t dgr_more_to_come = false; + + // Set a bool indicating if the power is on. +#ifdef USE_PWM_DIMMER_REMOTE + bool power_is_on = (!active_device_is_local ? active_remote_pwm_dimmer->power : power); +#else // USE_PWM_DIMMER_REMOTE + bool power_is_on = power; +#endif // USE_PWM_DIMMER_REMOTE // If the button is pressed, ... if (!XdrvMailbox.payload) { @@ -382,6 +252,7 @@ void PWMDimmerHandleButton() // If the button was just pressed, flag the button as pressed, clear the hold sent flag and // increment the buttons pressed count. if (!button_pressed[button_index]) { + button_press_time = now; button_pressed[button_index] = true; button_hold_sent[button_index] = false; buttons_pressed++; @@ -409,7 +280,7 @@ void PWMDimmerHandleButton() // If this is not about the power button, load the new hold time. Note that the hold time for // the power button is longer than the hold time for the other buttons. - button_hold_time[button_index] = now + 500; + button_hold_time[button_index] = now + (tap_count ? 0 : 250); } // If the button is being held, send a button hold. @@ -418,14 +289,6 @@ void PWMDimmerHandleButton() button_hold_sent[button_index] = true; SendKey(KEY_BUTTON, button_index + 1, POWER_HOLD); } - - // If the button has been held for over 10 seconds, execute the WiFiConfig 2 command. - if (now - button_hold_time[button_index] > 10000) { - char scmnd[20]; - snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_WIFICONFIG " 2")); - ExecuteCommand(scmnd, SRC_BUTTON); - return; - } } // If this is about the power button, ... @@ -439,37 +302,35 @@ void PWMDimmerHandleButton() if (!ignore_power_button_hold && button_hold_time[button_index] < now) { ignore_power_button_release = true; - // If the device power is on, adjust the brightness. Set the direction based on the current + // If the power is on, adjust the brightness. Set the direction based on the current // direction for the device and then invert the direction when the power button is released. // The new brightness will be calculated below. + if (power_is_on) { #ifdef USE_PWM_DIMMER_REMOTE - if (!active_device_is_local ? active_remote_pwm_dimmer->power : power) { bri_direction = (!active_device_is_local ? (active_remote_pwm_dimmer->power_button_increases_bri ? 1 : -1) : (power_button_increases_bri ? 1 : -1)); #else // USE_PWM_DIMMER_REMOTE - if (power) { bri_direction = (power_button_increases_bri ? 1 : -1); #endif // USE_PWM_DIMMER_REMOTE invert_power_button_bri_direction = true; } - // If the power is not on, turn it on using an initial brightness of bri_preset_low and set - // the power button hold dimmer direction to true so holding the power switch increases the - // brightness. + // If the power is not on, turn it on using an initial brightness of bri_preset_low and + // set the power button hold dimmer direction to true so holding the power switch increases + // the brightness. else { #ifdef USE_PWM_DIMMER_REMOTE if (!active_device_is_local) { - active_remote_pwm_dimmer->bri = active_remote_pwm_dimmer->bri_preset_low; + power_on_bri = active_remote_pwm_dimmer->bri = active_remote_pwm_dimmer->bri_preset_low; active_remote_pwm_dimmer->power_button_increases_bri = true; } else { #endif // USE_PWM_DIMMER_REMOTE - target_bri = Settings.bri_preset_low; + power_on_bri = Settings.bri_preset_low; power_button_increases_bri = true; #ifdef USE_PWM_DIMMER_REMOTE } #endif // USE_PWM_DIMMER_REMOTE - toggle_power = true; - + // Reset the power button hold time to delay before we start increasing the brightness. button_hold_time[button_index] = now + 500; } @@ -478,6 +339,7 @@ void PWMDimmerHandleButton() // If this is about the down or up buttons, ... else { + bool is_down_button = (button_index == down_button_index); // If the power button is also pressed, set flags to ignore the power button being held and // the next power button release. @@ -485,219 +347,104 @@ void PWMDimmerHandleButton() ignore_power_button_release = ignore_power_button_hold = true; } - // If the active device is local or the power button if also pressed, ... -#ifdef USE_PWM_DIMMER_REMOTE - if (active_device_is_local == !button_pressed[power_button_index]) { -#endif // USE_PWM_DIMMER_REMOTE - bool is_down_button = (button_index == down_button_index); + // If the button is being held, ... + if (button_hold_time[button_index] < now) { + uint8_t mqtt_trigger = 0; - // If the power is not on, turn it on using a temporary brightness of bri_preset_low if the - // down button is pressed or bri_preset_low if the up button is pressed. + // If the up or down button was tapped while holding the power button before this, handle + // the operation. + if (tap_count) { + + // Send a device group update to select the previous/next fixed color. + if (down_button_tapped) { +#ifdef USE_DEVICE_GROUPS + uint8_t uint8_value; #ifdef USE_PWM_DIMMER_REMOTE - if ((!active_device_is_local ? !active_remote_pwm_dimmer->power : !power)) { -#else // USE_PWM_DIMMER_REMOTE - if (!power) { + if (!active_device_is_local) + uint8_value = active_remote_pwm_dimmer->fixed_color_index; + else #endif // USE_PWM_DIMMER_REMOTE + uint8_value = Light.fixed_color_index; + if (is_down_button) { + if (uint8_value) + uint8_value--; + else + uint8_value = MAX_FIXED_COLOR; + } + else { + if (uint8_value < MAX_FIXED_COLOR) + uint8_value++; + else + uint8_value = 0; + } #ifdef USE_PWM_DIMMER_REMOTE - if (!active_device_is_local) - active_remote_pwm_dimmer->bri = (is_down_button ? active_remote_pwm_dimmer->bri_preset_low : active_remote_pwm_dimmer->bri_preset_high); + if (!active_device_is_local) + active_remote_pwm_dimmer->fixed_color_index = uint8_value; + else +#endif // USE_PWM_DIMMER_REMOTE + Light.fixed_color_index = uint8_value; + dgr_item = DGR_ITEM_LIGHT_FIXED_COLOR; + dgr_value = uint8_value; + dgr_more_to_come = true; +#endif // USE_DEVICE_GROUPS + ; + } + + // Publish MQTT Event Trigger#. + else { + mqtt_trigger = (is_down_button ? 3 : 4); + } + } + + // If the power button is not also pressed and the button has been held for over 10 seconds, + // execute the WiFiConfig 2 command. + else if (!button_pressed[power_button_index] && now - button_hold_time[button_index] > 10000) { + button_hold_time[button_index] = now + 90000; + char scmnd[20]; + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_WIFICONFIG " 2")); + ExecuteCommand(scmnd, SRC_BUTTON); + return; + } + + // If the power is not on, publish MQTT Event Trigger#. + else if (!power_is_on) { + mqtt_trigger = (is_down_button ? 1 : 2); + } + + button_was_held = true; + button_hold_time[button_index] = now + 500; + + // If we need to publish an MQTT trigger, do it. + if (mqtt_trigger) { + char topic[TOPSZ]; +#ifdef USE_PWM_DIMMER_REMOTE + if (!active_device_is_local) { + snprintf_P(topic, sizeof(topic), PSTR("cmnd/%s/Event"), device_groups[power_button_index].group_name); + MqttPublish(topic); + } else #endif // USE_PWM_DIMMER_REMOTE - target_bri = (is_down_button ? Settings.bri_preset_low : Settings.bri_preset_high); - toggle_power = true; - button_hold_time[button_index] = now + 500; - ignore_up_down_button_release = true; - - // If the power button is also pressed, set the power button hold dimmer direction - // so holding the power switch adjusts the brightness away from the brightness we - // just set. -#ifdef USE_PWM_DIMMER_REMOTE - if (button_pressed[power_button_index]) active_remote_pwm_dimmer->power_button_increases_bri = is_down_button; -#endif // USE_PWM_DIMMER_REMOTE + GetTopic_P(topic, CMND, mqtt_topic, PSTR("Event")); + sprintf_P(mqtt_data, PSTR("Trigger%u"), mqtt_trigger); + MqttPublishPrefixTopic_P(CMND, PSTR("Event")); } - - // If the power is on and the down/up button has been held, handle the action based on the - // number of times the down or up button was pressed and released before holding it. - else if (button_hold_time[button_index] < now) { - uint8_t uint8_value; - bool down_button_was_tapped = (button_press_count[down_button_index] > 0); - uint8_t tap_count = (down_button_was_tapped ? button_press_count[down_button_index] : button_press_count[up_button_index]); - uint16_t uint16_value; - - switch (tap_count) { - case 0: - // Adjust the brightness. Set the direction based on which button is pressed. The new - // brightness will be calculated below. - bri_direction = (is_down_button ? -1 : 1); - break; - - case 1: - if (down_button_was_tapped) { - // Select the previous/next color. -#ifdef USE_DEVICE_GROUPS -#ifdef USE_PWM_DIMMER_REMOTE - if (!active_device_is_local) - uint8_value = active_remote_pwm_dimmer->fixed_color_index; - else -#endif // USE_PWM_DIMMER_REMOTE - uint8_value = fixed_color_index; - if (is_down_button) { - if (uint8_value) - uint8_value--; - else - uint8_value = MAX_FIXED_COLOR; - } - else { - if (uint8_value < MAX_FIXED_COLOR) - uint8_value++; - else - uint8_value = 0; - } -#ifdef USE_PWM_DIMMER_REMOTE - if (!active_device_is_local) - active_remote_pwm_dimmer->fixed_color_index = uint8_value; - else -#endif // USE_PWM_DIMMER_REMOTE - fixed_color_index = uint8_value; - dgr_item = DGR_ITEM_LIGHT_FIXED_COLOR; - dgr_value = uint8_value; -#endif // USE_DEVICE_GROUPS - ; - } - else { - // Publish MQTT Event SwitchTrigger#. - char topic[TOPSZ]; -#ifdef USE_PWM_DIMMER_REMOTE - snprintf_P(topic, sizeof(topic), PSTR("%s/cmnd/Event"), device_groups[power_button_index].group_name); -#else // USE_PWM_DIMMER_REMOTE - snprintf_P(topic, sizeof(topic), PSTR("%s/cmnd/Event"), SettingsText(SET_MQTT_GRP_TOPIC)); -#endif // USE_PWM_DIMMER_REMOTE - sprintf_P(mqtt_data, PSTR("SwitchTrigger%u"), (is_down_button ? 1 : 2)); - MqttPublish(topic); - } - break; - - case 2: - if (down_button_was_tapped) { - // Decrease/increase the minimum brightness. -#ifdef USE_PWM_DIMMER_REMOTE - if (!active_device_is_local) - uint16_value = active_remote_pwm_dimmer->dimmer_range & 0xffff; - else -#endif // USE_PWM_DIMMER_REMOTE - uint16_value = Settings.dimmer_hw_min; - if (is_down_button) { - if (uint16_value > 0) uint16_value--; - } - else if (uint16_value < 65535) { - uint16_value++; - } -#ifdef USE_PWM_DIMMER_REMOTE - dgr_item = DGR_ITEM_DIMMER_RANGE; - if (!active_device_is_local) { - active_remote_pwm_dimmer->dimmer_range &= 0xffff0000 | uint16_value; - dgr_value = active_remote_pwm_dimmer->dimmer_range; - } - else { -#endif // USE_PWM_DIMMER_REMOTE - Settings.dimmer_hw_min = uint16_value; - dgr_value = Settings.dimmer_hw_min | Settings.dimmer_hw_max << 16; -#ifdef USE_PWM_DIMMER_REMOTE - } -#endif // USE_PWM_DIMMER_REMOTE - } - else { - // Decrease/increase the fade speed. -#ifdef USE_PWM_DIMMER_REMOTE - if (!active_device_is_local) - uint8_value = active_remote_pwm_dimmer->light_speed; - else -#endif // USE_PWM_DIMMER_REMOTE - uint8_value = Settings.light_speed; - if (is_down_button) { - if (uint8_value > 1) uint8_value--; - } - else if (uint8_value < 40) { - uint8_value++; - } -#ifdef USE_PWM_DIMMER_REMOTE - if (!active_device_is_local) - active_remote_pwm_dimmer->light_speed = uint8_value; - else -#endif // USE_PWM_DIMMER_REMOTE - Settings.light_speed = uint8_value; - dgr_item = DGR_ITEM_LIGHT_SPEED; - dgr_value = uint8_value; - } - break; - - case 3: - if (down_button_was_tapped) { - // Decrease/increase the low brightness preset. -#ifdef USE_PWM_DIMMER_REMOTE - if (!active_device_is_local) - uint8_value = active_remote_pwm_dimmer->bri_preset_low; - else -#endif // USE_PWM_DIMMER_REMOTE - uint8_value = Settings.bri_preset_low; - if (is_down_button) { - if (uint8_value > 3) uint8_value--; - } - else if (uint8_value < 255) { - uint8_value++; - } -#ifdef USE_PWM_DIMMER_REMOTE - if (!active_device_is_local) - active_remote_pwm_dimmer->bri_preset_low = uint8_value; - else -#endif // USE_PWM_DIMMER_REMOTE - Settings.bri_preset_low = uint8_value; - dgr_item = DGR_ITEM_BRI_PRESET_LOW; - dgr_value = uint8_value; - } - else { - // Decrease/increase the high brightness preset. -#ifdef USE_PWM_DIMMER_REMOTE - if (!active_device_is_local) - uint8_value = active_remote_pwm_dimmer->bri_preset_high; - else -#endif // USE_PWM_DIMMER_REMOTE - uint8_value = Settings.bri_preset_high; - if (is_down_button) { - if (uint8_value > 3) uint8_value--; - } - else if (uint8_value < 255) { - uint8_value++; - } -#ifdef USE_PWM_DIMMER_REMOTE - if (!active_device_is_local) - active_remote_pwm_dimmer->bri_preset_high = uint8_value; - else -#endif // USE_PWM_DIMMER_REMOTE - Settings.bri_preset_high = uint8_value; - dgr_item = DGR_ITEM_BRI_PRESET_HIGH; - dgr_value = uint8_value; - } - break; - } - - button_was_held = true; - - // If the button was tapped before it was held, reset the button hold time to 1/2 second - // and turn all the brightness LEDs on for 250ms. - if (tap_count > 0) { - button_hold_time[button_index] = now + 500; - turn_off_brightness_leds_time = now + 250; - PWMDimmerSetBrightnessLeds(1); - } - } -#ifdef USE_PWM_DIMMER_REMOTE } -#endif // USE_PWM_DIMMER_REMOTE + + // If the power is on, the up or down button was not tapped while holding the power button + // before this and the active device is not local or the power button is not also pressed, + // adjust the brightness. Set the direction based on which button is pressed. The new + // brightness will be calculated below. + if (power_is_on && !tap_count) { +#ifdef USE_PWM_DIMMER_REMOTE + if (!active_device_is_local || !button_pressed[power_button_index]) +#else // USE_PWM_DIMMER_REMOTE + if (!button_pressed[power_button_index]) +#endif // USE_PWM_DIMMER_REMOTE + bri_direction = (is_down_button ? -1 : 1); + } } - // If we need to adjust the brightness, do it. The brightness is adjusted faster the longer the - // button is held. + // If we need to adjust the brightness, do it. if (bri_direction) { int32_t bri; #ifdef USE_PWM_DIMMER_REMOTE @@ -705,16 +452,16 @@ void PWMDimmerHandleButton() bri = active_remote_pwm_dimmer->bri; else #endif // USE_PWM_DIMMER_REMOTE - bri = target_bri; + bri = light_state.getBri(); int32_t new_bri; - int32_t offset = bri / 12 + 1; + int32_t offset = (Settings.light_correction ? 4 : bri / 16 + 1); if (bri_direction > 0) { new_bri = bri + offset; if (new_bri > 255) new_bri = 255; } else { new_bri = bri - offset; - if (new_bri < 3) new_bri = 3; + if (new_bri < 1) new_bri = 1; } if (new_bri != bri) { #ifdef USE_DEVICE_GROUPS @@ -725,207 +472,215 @@ void PWMDimmerHandleButton() active_remote_pwm_dimmer->bri_power_on = active_remote_pwm_dimmer->bri = new_bri; else { #endif // USE_PWM_DIMMER_REMOTE - Settings.bri_power_on = target_bri = new_bri; - Settings.light_dimmer = changeUIntScale(target_bri, 0, 255, 0, 100); - PWMDimmerAnimate(true); + skip_light_fade = true; + light_state.setBri(new_bri); + LightAnimate(); + skip_light_fade = false; + Settings.bri_power_on = new_bri; #ifdef USE_PWM_DIMMER_REMOTE } #endif // USE_PWM_DIMMER_REMOTE } + else { + PWMDimmerSetBrightnessLeds(0); + } } } // If the button was just released, ... - else if (button_pressed[button_index]) { + else { + if (now - button_press_time > Settings.button_debounce) { - // If the button was held, send a button off; otherwise, send a button toggle. - SendKey(KEY_BUTTON, button_index + 1, (button_hold_sent[button_index] ? POWER_OFF : POWER_TOGGLE)); + // If the button was held, send a button off; otherwise, send a button toggle. + SendKey(KEY_BUTTON, button_index + 1, (button_hold_sent[button_index] ? POWER_OFF : POWER_TOGGLE)); - // If this is about the power button, ... + // If this is about the power button, ... #ifdef USE_PWM_DIMMER_REMOTE - if (button_index == power_button_index) { + if (button_index == power_button_index) { #else // USE_PWM_DIMMER_REMOTE - if (!button_index) { + if (!button_index) { #endif // USE_PWM_DIMMER_REMOTE - // If we're ignoring the next power button released, ... - if (ignore_power_button_release) { - ignore_power_button_release = false; + // If we're ignoring the next power button released, ... + if (ignore_power_button_release) { + ignore_power_button_release = false; - // If the power button was held with no other buttons pressed, we changed the brightness so - // invert the bri direction for the next time and send a final update. - if (invert_power_button_bri_direction) { - invert_power_button_bri_direction = false; - dgr_item = DGR_ITEM_LIGHT_BRI; + // If the power button was held with no other buttons pressed, we changed the brightness + // so invert the bri direction for the next time and send a final update. + if (invert_power_button_bri_direction) { + invert_power_button_bri_direction = false; +#ifdef USE_PWM_DIMMER_REMOTE + if (!active_device_is_local) + active_remote_pwm_dimmer->power_button_increases_bri ^= 1; + else +#endif // USE_PWM_DIMMER_REMOTE + power_button_increases_bri ^= 1; +#ifdef USE_PWM_DIMMER_REMOTE + dgr_item = 255; +#endif // USE_PWM_DIMMER_REMOTE + } + + // If the up or down button was tapped while the power button was pressed, ... + else if (tap_count) { + + // If the button was tapped but not held, handle the operation based on which button was + // tapped. + if (!button_was_held) { + +#ifdef USE_PWM_DIMMER_REMOTE + if (active_device_is_local) { +#endif // USE_PWM_DIMMER_REMOTE + + // Toggle the powered-off LED option. + if (down_button_tapped) { + Settings.flag4.powered_off_led ^= 1; + PWMDimmerSetPoweredOffLed(); + } + + // Toggle the LED timeout. + else { + Settings.flag4.led_timeout ^= 1; + if (Light.power) PWMDimmerSetBrightnessLeds(Settings.flag4.led_timeout ? -1 : 0); + } +#ifdef USE_PWM_DIMMER_REMOTE + } +#endif // USE_PWM_DIMMER_REMOTE + } + + // If the down button was tapped and held, we changed the fixed color. Send a final + // update. + else if (down_button_tapped) { + dgr_item = 255; + } + } + } + + // If we're not ignoring the power button until it's released, toggle the power. + else { #ifdef USE_PWM_DIMMER_REMOTE if (!active_device_is_local) { - active_remote_pwm_dimmer->power_button_increases_bri ^= 1; - dgr_value = active_remote_pwm_dimmer->bri; + power_on_bri = active_remote_pwm_dimmer->bri_power_on; + if (active_remote_pwm_dimmer->bri > 251) + active_remote_pwm_dimmer->power_button_increases_bri = false; + else if (active_remote_pwm_dimmer->bri < 4) + active_remote_pwm_dimmer->power_button_increases_bri = true; } else { #endif // USE_PWM_DIMMER_REMOTE - power_button_increases_bri ^= 1; - dgr_value = target_bri; + power_on_bri = Settings.bri_power_on; + if (power_on_bri > 251) + power_button_increases_bri = false; + else if (power_on_bri < 4) + power_button_increases_bri = true; #ifdef USE_PWM_DIMMER_REMOTE } #endif // USE_PWM_DIMMER_REMOTE } - - // The up and/or down buttons were pressed while the power button was pressed. Handle the - // options based on the number of times the buttons were pressed. - else { - switch (button_press_count[down_button_index]) { - case 1: - // Toggle the powered-off LED option. -#ifdef USE_PWM_DIMMER_REMOTE - if (active_device_is_local) { -#endif // USE_PWM_DIMMER_REMOTE - Settings.flag4.powered_off_led ^= 1; - PWMDimmerSetPoweredOffLed(); -#ifdef USE_PWM_DIMMER_REMOTE - } -#endif // USE_PWM_DIMMER_REMOTE - break; - case 2: - // Toggle fading. - dgr_item = DGR_ITEM_LIGHT_FADE; -#ifdef USE_PWM_DIMMER_REMOTE - if (!active_device_is_local) { - active_remote_pwm_dimmer->light_fade ^= 1; - dgr_value = active_remote_pwm_dimmer->light_fade; - } - else { -#endif // USE_PWM_DIMMER_REMOTE - Settings.light_fade ^= 1; - dgr_value = Settings.light_fade; -#ifdef USE_PWM_DIMMER_REMOTE - } -#endif // USE_PWM_DIMMER_REMOTE - break; - } - button_press_count[down_button_index] = 0; - - switch (button_press_count[up_button_index]) { - case 1: - // Toggle the LED timeout. -#ifdef USE_PWM_DIMMER_REMOTE - if (active_device_is_local) { -#endif // USE_PWM_DIMMER_REMOTE - Settings.flag4.led_timeout ^= 1; - if (relay_is_on) PWMDimmerSetBrightnessLeds(Settings.flag4.led_timeout ? -1 : 0); -#ifdef USE_PWM_DIMMER_REMOTE - } -#endif // USE_PWM_DIMMER_REMOTE - break; - } - button_press_count[up_button_index] = 0; - } } - // If we're not ignoring the power button until it's released, toggle the power. + // If this is about the up or down buttons, ... else { -#ifdef USE_PWM_DIMMER_REMOTE - if (!active_device_is_local) - active_remote_pwm_dimmer->bri = active_remote_pwm_dimmer->bri_power_on; - else -#endif // USE_PWM_DIMMER_REMOTE - target_bri = Settings.bri_power_on; - toggle_power = true; - } - } + bool is_down_button = (button_index == down_button_index); - // If this is about the up or down buttons, ... - else { - - // If the button was held, ... - if (button_was_held) { - button_was_held = false; - - // If the button was not tapped before it was held, we changed the brightness and sent - // updates with the more-to-come message type. Send a final update. - if (button_press_count[down_button_index] == 0 && button_press_count[up_button_index == 0]) { - dgr_item = DGR_ITEM_LIGHT_BRI; -#ifdef USE_PWM_DIMMER_REMOTE - if (!active_device_is_local) - dgr_value = active_remote_pwm_dimmer->bri; - else -#endif // USE_PWM_DIMMER_REMOTE - dgr_value = target_bri; + if (restore_brightness_leds) { + restore_brightness_leds = false; + PWMDimmerSetBrightnessLeds(Settings.flag4.led_timeout ? -1 : 0); } - // Reset the the button press count. - button_press_count[button_index] = 0; - } + // If the power is on and the up or down button was not tapped while holding the power + // button before this, we changed the brightness and sent updates with the more-to-come + // message type. Send a final update. + if (power_is_on && !tap_count) { + dgr_item = 255; - // If the power was off and we turned it on with the down or up button, ignore it's release. - if (ignore_up_down_button_release) { - ignore_up_down_button_release = false; - } + // If the power button is also pressed, set the power button hold dimmer direction so + // holding the power switch adjusts the brightness away from the brightness we just set. +#ifdef USE_PWM_DIMMER_REMOTE + if (!active_device_is_local && button_pressed[power_button_index]) active_remote_pwm_dimmer->power_button_increases_bri = is_down_button; +#endif // USE_PWM_DIMMER_REMOTE + } - // If the button was tapped (pressed and released quickly) and the power is on, increment the - // count of how many times this button has been pressed. - else if (button_hold_time[button_index] >= now) { - button_press_count[button_index]++; + // If the button was not held, ... + if (!button_was_held) { + + // If the power button is also pressed, increment the count of how many times a button has + // been tapped. + if (button_pressed[power_button_index]) { + down_button_tapped = (button_index == down_button_index); + tap_count++; + } + + // If the power is off, turn it on using a temporary brightness of bri_preset_low if the + // down button is pressed or bri_preset_low if the up button is pressed. +#ifdef USE_PWM_DIMMER_REMOTE + else if ((!active_device_is_local ? !active_remote_pwm_dimmer->power : !power)) { +#else // USE_PWM_DIMMER_REMOTE + else if (!power) { +#endif // USE_PWM_DIMMER_REMOTE +#ifdef USE_PWM_DIMMER_REMOTE + if (!active_device_is_local) { + power_on_bri = active_remote_pwm_dimmer->bri = (is_down_button ? active_remote_pwm_dimmer->bri_preset_low : active_remote_pwm_dimmer->bri_preset_high); + active_remote_pwm_dimmer->power_button_increases_bri = is_down_button; + } + else { +#endif // USE_PWM_DIMMER_REMOTE + power_on_bri = (is_down_button ? Settings.bri_preset_low : Settings.bri_preset_high); + power_button_increases_bri = is_down_button; +#ifdef USE_PWM_DIMMER_REMOTE + } +#endif // USE_PWM_DIMMER_REMOTE + button_hold_time[button_index] = now + 500; + } + } } } // Flag the button as released. button_pressed[button_index] = false; buttons_pressed--; - button_press_count_timeout = now + 500; } - // If - else if (buttons_pressed == 0 && button_press_count_timeout && button_press_count_timeout < now) { - button_press_count[0] = button_press_count[1] = button_press_count[2] = 0; - } - - if (toggle_power) { + if (power_on_bri) { power_t new_power; - uint8_t new_bri; #ifdef USE_DEVICE_GROUPS #ifdef USE_PWM_DIMMER_REMOTE if (!active_device_is_local) { active_remote_pwm_dimmer->power ^= 1; new_power = active_remote_pwm_dimmer->power; - new_bri = active_remote_pwm_dimmer->bri; } else { #endif // USE_PWM_DIMMER_REMOTE new_power = power ^ 1; - new_bri = target_bri; #ifdef USE_PWM_DIMMER_REMOTE } #endif // USE_PWM_DIMMER_REMOTE if (new_power) - SendDeviceGroupMessage(power_button_index, DGR_MSGTYP_UPDATE, DGR_ITEM_LIGHT_BRI, new_bri, DGR_ITEM_POWER, new_power); + SendDeviceGroupMessage(power_button_index, DGR_MSGTYP_UPDATE, DGR_ITEM_LIGHT_BRI, power_on_bri, DGR_ITEM_POWER, new_power); else SendDeviceGroupMessage(power_button_index, DGR_MSGTYP_UPDATE, DGR_ITEM_POWER, new_power); -#endif //0 USE_DEVICE_GROUPS +#endif // USE_DEVICE_GROUPS #ifdef USE_PWM_DIMMER_REMOTE - // The target brightness has already been set. Execute the toggle power command with a source of - // SRC_RETRY. This will turn the power on using the current target brightness (see - // FUNC_SET_DEVICE_POWER below). - if (active_device_is_local) + if (active_device_is_local) { #endif // USE_PWM_DIMMER_REMOTE ExecuteCommandPower(1, POWER_TOGGLE, SRC_RETRY); + light_state.setBri(power_on_bri); +#ifdef USE_PWM_DIMMER_REMOTE + } +#endif // USE_PWM_DIMMER_REMOTE } // If we're not toggling the power and we made changes, send a group update. else if (dgr_item) { #ifdef USE_DEVICE_GROUPS - SendDeviceGroupMessage(power_button_index, DGR_MSGTYP_UPDATE_DIRECT, dgr_item, dgr_value); + if (dgr_item == 255) dgr_item = 0; + SendDeviceGroupMessage(power_button_index, (dgr_more_to_come ? DGR_MSGTYP_UPDATE_MORE_TO_COME : DGR_MSGTYP_UPDATE_DIRECT), dgr_item, dgr_value); #endif // USE_DEVICE_GROUPS + light_controller.saveSettings(); if (Settings.flag3.hass_tele_on_power) { // SetOption59 - Send tele/%topic%/STATE in addition to stat/%topic%/RESULT MqttPublishTeleState(); } } - - if (turn_off_brightness_leds_time && turn_off_brightness_leds_time < millis()) { - turn_off_brightness_leds_time = 0; - PWMDimmerSetBrightnessLeds(-1); - } } /*********************************************************************************************\ @@ -947,16 +702,15 @@ void CmndBriPreset(void) if (parm[i] < 255) parm[i]++; } else if (*ptr == '-') { - if (parm[i] > 0) parm[i]--; + if (parm[i] > 1) parm[i]--; } else { value = strtoul(ptr, &ptr, 0); - if (value < 1 || parm[i] > 255) { + if (value < 1 || value > 255) { valid = false; break; } parm[i] = value; - if (*ptr != ',') break; } ptr++; @@ -978,28 +732,6 @@ void CmndBriPreset(void) Response_P(PSTR("{\"" D_CMND_BRI_PRESET "\":{\"Low\":%d,\"High\":%d}}"), Settings.bri_preset_low, Settings.bri_preset_high); } -void PWMDmmerCmndDimmer(void) -{ - uint8_t dimmer = changeUIntScale(target_bri, 0, 255, 0, 100); - if (1 == XdrvMailbox.data_len) { - if ('+' == XdrvMailbox.data[0]) - XdrvMailbox.payload = (dimmer > 89) ? 100 : dimmer + 10; - else if ('-' == XdrvMailbox.data[0]) { - XdrvMailbox.payload = (dimmer < 11) ? 1 : dimmer - 10; - } - } - - if (XdrvMailbox.payload >= 0 && XdrvMailbox.payload <= 100) { - uint8_t bri = changeUIntScale(XdrvMailbox.payload, 0, 100, 0, 255); -#ifdef USE_DEVICE_GROUPS - SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_LIGHT_BRI, bri); -#endif // USE_DEVICE_GROUPS - PWMDimmerSetBri(bri); - } - - ResponseCmndNumber(Settings.light_dimmer); -} - /*********************************************************************************************\ * Interface \*********************************************************************************************/ @@ -1011,21 +743,17 @@ bool Xdrv35(uint8_t function) if (PWM_DIMMER != my_module_type) return result; switch (function) { - case FUNC_LOOP: - if (current_bri != target_bri) PWMDimmerAnimate(false); - break; - case FUNC_EVERY_SECOND: // Turn off the brightness LED's if it's time. if (led_timeout_time && led_timeout_time < millis()) { - for (uint32_t index = 0; index < leds_present; index++) SetLedPowerIdx(index, 0); led_timeout_time = 0; + PWMDimmerSetBrightnessLeds(-1); } - // The powered-off LED is also the LedLink LED. If we lose the WiFi or MQTT server - // connection, the LED will be set to a blinking state and will be turned off when the - // connection is restored. If the state is blinking now, set a flag so we know that we need - // to restore it when it stops blinking. + // The powered-off LED is also the LedLink LED. If we lose the WiFi or MQTT server connection, + // the LED will be set to a blinking state and will be turned off when the connection is + // restored. If the state is blinking now, set a flag so we know that we need to restore it + // when it stops blinking. if (global_state.data) restore_powered_off_led = 5; else if (restore_powered_off_led) { @@ -1035,7 +763,6 @@ bool Xdrv35(uint8_t function) break; case FUNC_BUTTON_PRESSED: - // Handle the button press/release instead of the button handler. PWMDimmerHandleButton(); result = true; break; @@ -1046,28 +773,26 @@ bool Xdrv35(uint8_t function) break; #endif // USE_DEVICE_GROUPS - case FUNC_SET_DEVICE_POWER: - // Handle turning the power on/off here so we can fade off. Set the target brightness and let - // Animate handle turning the power off/on. If the source is SRC_RETRY, we're turning the - // power on with a brightness preset and the target brightness has already been set. - if (!XdrvMailbox.index) - target_bri = 0; - else if (SRC_RETRY != XdrvMailbox.payload) { - target_bri = Settings.bri_power_on; - Settings.light_dimmer = changeUIntScale(target_bri, 0, 255, 0, 100); - } - result = true; - break; - case FUNC_COMMAND: result = DecodeCommand(kPWMDimmerCommands, PWMDimmerCommand); break; - case FUNC_INIT: - PWMDimmerInit(); + case FUNC_SET_DEVICE_POWER: + // If we're turning the power on, turn the relay and the brightness LEDs on and turn the + // powered-off LED off. + if (XdrvMailbox.index) + PWMDimmerSetPower(); + + // If we're turning the power off, return true so SetDevicePower doesn't turn the relay off. + // It will be turned off in LightApplyFade when the fade is done. + else + result = true; break; case FUNC_PRE_INIT: + // The relay initializes to on. If the power is supposed to be off, turn the relay off. + if (!power && pin[GPIO_REL1] < 99) digitalWrite(pin[GPIO_REL1], bitRead(rel_inverted, 0) ? 1 : 0); + #ifdef USE_PWM_DIMMER_REMOTE // If remote device mode is enabled, set the device group count to the number of buttons // present. @@ -1075,7 +800,15 @@ bool Xdrv35(uint8_t function) for (uint32_t button_index = 0; button_index < MAX_KEYS; button_index++) { if (pin[GPIO_KEY1 + button_index] < 99) device_group_count++; } + + if (device_group_count > 1) { + if ((remote_pwm_dimmers = (struct remote_pwm_dimmer *) calloc(device_group_count - 1, sizeof(struct remote_pwm_dimmer))) == nullptr) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR("PWMDimmer: error allocating PWM dimmer array")); + Settings.flag4.remote_device_mode = false; + } + } } + active_device_is_local = true; #endif // USE_PWM_DIMMER_REMOTE break; @@ -1087,4 +820,4 @@ bool Xdrv35(uint8_t function) return result; } -#endif // USE_PWM_DIMMER \ No newline at end of file +#endif // USE_PWM_DIMMER From 243f17585cf49589008c9f35f23c7a58cb0d8d8f Mon Sep 17 00:00:00 2001 From: Paul C Diem Date: Fri, 13 Mar 2020 15:15:38 -0500 Subject: [PATCH 2/4] Send DGR BRI_MIN only if USE_PWM_DIMMER defined --- tasmota/xdrv_04_light.ino | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tasmota/xdrv_04_light.ino b/tasmota/xdrv_04_light.ino index d7b7d3d24..0d466ad9d 100644 --- a/tasmota/xdrv_04_light.ino +++ b/tasmota/xdrv_04_light.ino @@ -2553,10 +2553,10 @@ void CmndDimmer(void) LightPreparePower(); } } -#ifdef USE_DEVICE_GROUPS +#if defined(USE_PWM_DIMMER) && defined(USE_DEVICE_GROUPS) Settings.bri_power_on = light_state.getBri();; SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_BRI_POWER_ON, Settings.bri_power_on); -#endif // USE_DEVICE_GROUPS +#endif // USE_PWM_DIMMER && USE_DEVICE_GROUPS Light.update = true; if (skip_light_fade) LightAnimate(); } else { From 322952a54f329caf5c99431b10cca41f6e474079 Mon Sep 17 00:00:00 2001 From: Paul C Diem Date: Sat, 14 Mar 2020 18:53:09 -0500 Subject: [PATCH 3/4] Move PWM Dimmer init logic to pre init, Use dimmer_hw_min for PWM, Remote mode fixes --- PWM_Dimmer.md | 2 +- tasmota/support_command.ino | 5 +++ tasmota/xdrv_04_light.ino | 6 ++- tasmota/xdrv_35_pwm_dimmer.ino | 78 ++++++++++++++++++---------------- 4 files changed, 52 insertions(+), 39 deletions(-) diff --git a/PWM_Dimmer.md b/PWM_Dimmer.md index 0ac600f53..7254a5da2 100644 --- a/PWM_Dimmer.md +++ b/PWM_Dimmer.md @@ -83,7 +83,7 @@ Remote device mode allows PWM Dimmer switches to control remote devices. With re To include remote device mode support in the build, define USE_PWM_DIMMER_REMOTE in your user_config_override. Remote device mode support requires device group support so USE_DEVICE_GROUPS is automatically defined if USE_PWM_DIMMER_REMOTE is defined. Remote device mode support adds 0.7K to the code size in addition to the code size required for device groups support. -To enable remote device mode, execute the command SetOption88 1. Each remote device must be running firmware with device group support and have remote device support enabled. The remote devices do not need to be built with PWM dimmer support nor do they need to be switches. +To enable remote device mode, execute the command SetOption88 1 (the device will restart). Each remote device must be running firmware with device group support and have remote device support enabled. The remote devices do not need to be built with PWM dimmer support nor do they need to be switches. If a remote device is a PWM dimmer, the device acts like a 3-way dimmer switch would and may or may not have a load connected to it. It’s also possible to use a PWM dimmer switch without a load to act as a wall switch to control the power, brightness and color of one or more smart lights with Tasmota with device group support loaded on them. diff --git a/tasmota/support_command.ino b/tasmota/support_command.ino index 2becd43e1..7915ea7a9 100644 --- a/tasmota/support_command.ino +++ b/tasmota/support_command.ino @@ -783,6 +783,11 @@ void CmndSetoption(void) } else if (4 == ptype) { // SetOption82 .. 113 bitWrite(Settings.flag4.data, pindex, XdrvMailbox.payload); + switch (pindex) { + case 6: // SetOption88 - PWM Dimmer Buttons control remote devices + restart_flag = 2; + break; + } } } else { ptype = 99; // Command Error diff --git a/tasmota/xdrv_04_light.ino b/tasmota/xdrv_04_light.ino index 0d466ad9d..6b72da9b2 100644 --- a/tasmota/xdrv_04_light.ino +++ b/tasmota/xdrv_04_light.ino @@ -1823,9 +1823,13 @@ void LightAnimate(void) } // final adjusments for PMW, post-gamma correction + uint16_t min = 1; +#ifdef USE_PWM_DIMMER + if (PWM_DIMMER == my_module_type) min = Settings.dimmer_hw_min; +#endif // USE_PWM_DIMMER for (uint32_t i = 0; i < LST_MAX; i++) { // scale from 0..1023 to 0..pwm_range, but keep any non-zero value to at least 1 - cur_col_10[i] = (cur_col_10[i] > 0) ? changeUIntScale(cur_col_10[i], 1, 1023, 1, Settings.pwm_range) : 0; + cur_col_10[i] = (cur_col_10[i] > 0) ? changeUIntScale(cur_col_10[i], 1, 1023, min, Settings.pwm_range) : 0; } // apply port remapping on both 8 bits and 10 bits versions diff --git a/tasmota/xdrv_35_pwm_dimmer.ino b/tasmota/xdrv_35_pwm_dimmer.ino index 18cfa8fc6..b053da7a8 100644 --- a/tasmota/xdrv_35_pwm_dimmer.ino +++ b/tasmota/xdrv_35_pwm_dimmer.ino @@ -75,20 +75,49 @@ struct remote_pwm_dimmer * active_remote_pwm_dimmer; bool active_device_is_local; #endif // USE_PWM_DIMMER_REMOTE -void PWMModuleInit() +void PWMModulePreInit() { Settings.seriallog_level = 0; Settings.flag.mqtt_serial = 0; // Disable serial logging Settings.ledstate = 0; // Disable LED usage - if (Settings.last_module != Settings.module) { - Settings.bri_power_on = 128; - Settings.bri_preset_low = 10; - Settings.bri_preset_high = 255; - Settings.last_module = Settings.module; - } + if (!Settings.bri_power_on) Settings.bri_power_on = 128; + if (!Settings.bri_preset_low) Settings.bri_preset_low = 10; + if (Settings.bri_preset_high < Settings.bri_preset_low) Settings.bri_preset_high = 255; PWMDimmerSetPoweredOffLed(); + + // The relay initializes to on. If the power is supposed to be off, turn the relay off. + if (!power && pin[GPIO_REL1] < 99) digitalWrite(pin[GPIO_REL1], bitRead(rel_inverted, 0) ? 1 : 0); + +#ifdef USE_PWM_DIMMER_REMOTE + // If remote device mode is enabled, set the device group count to the number of buttons + // present. + if (Settings.flag4.remote_device_mode) { + Settings.flag4.device_groups_enabled = true; + + for (uint32_t button_index = 0; button_index < MAX_KEYS; button_index++) { + if (pin[GPIO_KEY1 + button_index] < 99) device_group_count++; + } + + if (device_group_count > 1) { + uint8_t remote_pwm_dimmer_count = device_group_count - 1; + if ((remote_pwm_dimmers = (struct remote_pwm_dimmer *) calloc(remote_pwm_dimmer_count, sizeof(struct remote_pwm_dimmer))) == nullptr) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR("PWMDimmer: error allocating PWM dimmer array")); + Settings.flag4.remote_device_mode = false; + } + else { + for (uint8_t i = 0; i < remote_pwm_dimmer_count; i++) { + active_remote_pwm_dimmer = &remote_pwm_dimmers[i]; + active_remote_pwm_dimmer->bri_power_on = 128; + active_remote_pwm_dimmer->bri_preset_low = 10; + active_remote_pwm_dimmer->bri_preset_high = 255; + } + } + } + } + active_device_is_local = true; +#endif // USE_PWM_DIMMER_REMOTE } // operation: 0 = normal, -1 = all off, 1 = all on @@ -417,6 +446,7 @@ void PWMDimmerHandleButton() // If we need to publish an MQTT trigger, do it. if (mqtt_trigger) { char topic[TOPSZ]; + sprintf_P(mqtt_data, PSTR("Trigger%u"), mqtt_trigger); #ifdef USE_PWM_DIMMER_REMOTE if (!active_device_is_local) { snprintf_P(topic, sizeof(topic), PSTR("cmnd/%s/Event"), device_groups[power_button_index].group_name); @@ -424,9 +454,7 @@ void PWMDimmerHandleButton() } else #endif // USE_PWM_DIMMER_REMOTE - GetTopic_P(topic, CMND, mqtt_topic, PSTR("Event")); - sprintf_P(mqtt_data, PSTR("Trigger%u"), mqtt_trigger); - MqttPublishPrefixTopic_P(CMND, PSTR("Event")); + MqttPublishPrefixTopic_P(CMND, PSTR("Event")); } } @@ -489,7 +517,7 @@ void PWMDimmerHandleButton() // If the button was just released, ... else { - if (now - button_press_time > Settings.button_debounce) { +// if (now - button_press_time > Settings.button_debounce) { // If the button was held, send a button off; otherwise, send a button toggle. SendKey(KEY_BUTTON, button_index + 1, (button_hold_sent[button_index] ? POWER_OFF : POWER_TOGGLE)); @@ -633,7 +661,7 @@ void PWMDimmerHandleButton() } } } - } +// } // Flag the button as released. button_pressed[button_index] = false; @@ -790,31 +818,7 @@ bool Xdrv35(uint8_t function) break; case FUNC_PRE_INIT: - // The relay initializes to on. If the power is supposed to be off, turn the relay off. - if (!power && pin[GPIO_REL1] < 99) digitalWrite(pin[GPIO_REL1], bitRead(rel_inverted, 0) ? 1 : 0); - -#ifdef USE_PWM_DIMMER_REMOTE - // If remote device mode is enabled, set the device group count to the number of buttons - // present. - if (Settings.flag4.remote_device_mode) { - for (uint32_t button_index = 0; button_index < MAX_KEYS; button_index++) { - if (pin[GPIO_KEY1 + button_index] < 99) device_group_count++; - } - - if (device_group_count > 1) { - if ((remote_pwm_dimmers = (struct remote_pwm_dimmer *) calloc(device_group_count - 1, sizeof(struct remote_pwm_dimmer))) == nullptr) { - AddLog_P2(LOG_LEVEL_ERROR, PSTR("PWMDimmer: error allocating PWM dimmer array")); - Settings.flag4.remote_device_mode = false; - } - } - } - active_device_is_local = true; -#endif // USE_PWM_DIMMER_REMOTE - break; - - case FUNC_MODULE_INIT: - PWMModuleInit(); - result = true; + PWMModulePreInit(); break; } return result; From 32448bd75eade767102efc4deb0e76d6321d313d Mon Sep 17 00:00:00 2001 From: Paul C Diem Date: Sun, 15 Mar 2020 13:56:26 -0500 Subject: [PATCH 4/4] Reverse down/up tap order, Save settings only if local --- tasmota/xdrv_35_pwm_dimmer.ino | 53 ++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/tasmota/xdrv_35_pwm_dimmer.ino b/tasmota/xdrv_35_pwm_dimmer.ino index b053da7a8..54f270c4c 100644 --- a/tasmota/xdrv_35_pwm_dimmer.ino +++ b/tasmota/xdrv_35_pwm_dimmer.ino @@ -260,12 +260,13 @@ void PWMDimmerHandleButton() return; } - uint32_t button_index = XdrvMailbox.index; - uint32_t now = millis(); + bool state_updated = false; uint8_t power_on_bri = 0; uint8_t dgr_item = 0; uint8_t dgr_value; uint8_t dgr_more_to_come = false; + uint32_t button_index = XdrvMailbox.index; + uint32_t now = millis(); // Set a bool indicating if the power is on. #ifdef USE_PWM_DIMMER_REMOTE @@ -343,25 +344,25 @@ void PWMDimmerHandleButton() invert_power_button_bri_direction = true; } - // If the power is not on, turn it on using an initial brightness of bri_preset_low and - // set the power button hold dimmer direction to true so holding the power switch increases - // the brightness. + // If the power is not on, turn it on using an initial brightness of bri_preset_low, set the + // power button hold dimmer direction to true so holding the power switch increases the + // brightness and the power button hold time to delay before we start increasing the + // brightness. else { #ifdef USE_PWM_DIMMER_REMOTE if (!active_device_is_local) { power_on_bri = active_remote_pwm_dimmer->bri = active_remote_pwm_dimmer->bri_preset_low; active_remote_pwm_dimmer->power_button_increases_bri = true; + button_hold_time[button_index] = now + 1000; } else { #endif // USE_PWM_DIMMER_REMOTE power_on_bri = Settings.bri_preset_low; power_button_increases_bri = true; + button_hold_time[button_index] = now + 500; #ifdef USE_PWM_DIMMER_REMOTE } #endif // USE_PWM_DIMMER_REMOTE - - // Reset the power button hold time to delay before we start increasing the brightness. - button_hold_time[button_index] = now + 500; } } } @@ -458,17 +459,11 @@ void PWMDimmerHandleButton() } } - // If the power is on, the up or down button was not tapped while holding the power button - // before this and the active device is not local or the power button is not also pressed, - // adjust the brightness. Set the direction based on which button is pressed. The new - // brightness will be calculated below. + // If the power is on and the up or down button was not tapped while holding the power button + // before this, adjust the brightness. Set the direction based on which button is pressed. The + // new brightness will be calculated below. if (power_is_on && !tap_count) { -#ifdef USE_PWM_DIMMER_REMOTE - if (!active_device_is_local || !button_pressed[power_button_index]) -#else // USE_PWM_DIMMER_REMOTE - if (!button_pressed[power_button_index]) -#endif // USE_PWM_DIMMER_REMOTE - bri_direction = (is_down_button ? -1 : 1); + bri_direction = (is_down_button ? -1 : 1); } } @@ -545,6 +540,7 @@ void PWMDimmerHandleButton() power_button_increases_bri ^= 1; #ifdef USE_PWM_DIMMER_REMOTE dgr_item = 255; + state_updated = true; #endif // USE_PWM_DIMMER_REMOTE } @@ -561,14 +557,14 @@ void PWMDimmerHandleButton() // Toggle the powered-off LED option. if (down_button_tapped) { - Settings.flag4.powered_off_led ^= 1; - PWMDimmerSetPoweredOffLed(); + Settings.flag4.led_timeout ^= 1; + if (Light.power) PWMDimmerSetBrightnessLeds(Settings.flag4.led_timeout ? -1 : 0); } // Toggle the LED timeout. else { - Settings.flag4.led_timeout ^= 1; - if (Light.power) PWMDimmerSetBrightnessLeds(Settings.flag4.led_timeout ? -1 : 0); + Settings.flag4.powered_off_led ^= 1; + PWMDimmerSetPoweredOffLed(); } #ifdef USE_PWM_DIMMER_REMOTE } @@ -620,6 +616,7 @@ void PWMDimmerHandleButton() // message type. Send a final update. if (power_is_on && !tap_count) { dgr_item = 255; + state_updated = true; // If the power button is also pressed, set the power button hold dimmer direction so // holding the power switch adjusts the brightness away from the brightness we just set. @@ -704,10 +701,16 @@ void PWMDimmerHandleButton() if (dgr_item == 255) dgr_item = 0; SendDeviceGroupMessage(power_button_index, (dgr_more_to_come ? DGR_MSGTYP_UPDATE_MORE_TO_COME : DGR_MSGTYP_UPDATE_DIRECT), dgr_item, dgr_value); #endif // USE_DEVICE_GROUPS - light_controller.saveSettings(); - if (Settings.flag3.hass_tele_on_power) { // SetOption59 - Send tele/%topic%/STATE in addition to stat/%topic%/RESULT - MqttPublishTeleState(); +#ifdef USE_PWM_DIMMER_REMOTE + if (active_device_is_local) { +#endif // USE_PWM_DIMMER_REMOTE + light_controller.saveSettings(); + if (state_updated && Settings.flag3.hass_tele_on_power) { // SetOption59 - Send tele/%topic%/STATE in addition to stat/%topic%/RESULT + MqttPublishTeleState(); + } +#ifdef USE_PWM_DIMMER_REMOTE } +#endif // USE_PWM_DIMMER_REMOTE } }
Command @@ -121,13 +49,6 @@ When Device Groups are enabled, the PWM Dimmer brightness presets, dimmer minimu - = decrease brightness preset
DimmerRange - Change dimming range
-, = set minimum and maximum PWM values. Dimmer/brightness is scaled to this range.
-
SetOption86