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
-1 = Power, 2 = Light brightness, 4 = Light fade/speed, 8 = Light scheme, 16 = Light color, 32 = Minimum brightness +
-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
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 - | -
Presses - | -Down Button - | -Up Button - | -
1 - | -Toggle powered-off LED - | -Toggle brightness LED timeout - | -
2 - | -Toggle fading - | -- | -
Command @@ -121,13 +49,6 @@ When Device Groups are enabled, the PWM Dimmer brightness presets, dimmer minimu - = decrease brightness preset | |
DimmerRange - | -Change dimming range - - |
-
SetOption86 | @@ -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 (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. -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 abb9fccd8..7b6d50fe1 100644 --- a/tasmota/i18n.h +++ b/tasmota/i18n.h @@ -551,7 +551,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 d44cdea3c..dbc60bac3 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_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/support_device_groups.ino b/tasmota/support_device_groups.ino index f90e07c94..ed751fca1 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 c5e9c887f..eab6f136b 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 @@ -1478,6 +1472,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 6e9c8ac29..a1c7d1082 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 562f33697..08ac49574 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..6b72da9b2 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 @@ -1820,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 @@ -1858,6 +1865,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 +2095,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 +2133,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 +2148,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 +2557,10 @@ void CmndDimmer(void) LightPreparePower(); } } +#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_PWM_DIMMER && USE_DEVICE_GROUPS Light.update = true; if (skip_light_fade) LightAnimate(); } else { @@ -2544,8 +2569,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 +2585,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 +2631,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 +2677,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 +2709,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 323dd8674..8e7c229b9 100644 --- a/tasmota/xdrv_12_home_assistant.ino +++ b/tasmota/xdrv_12_home_assistant.ino @@ -211,7 +211,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; @@ -220,7 +220,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; @@ -246,9 +245,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..54f270c4c 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 @@ -86,51 +75,47 @@ 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 - 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_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); + 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(); - PWMDimmerSetBrightnessLeds(0); -} -void PWMDimmerInit(void) -{ + // 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) { - if ((remote_pwm_dimmers = (struct remote_pwm_dimmer *) calloc(device_group_count - 1, sizeof(struct remote_pwm_dimmer))) == nullptr) { + 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 } @@ -139,7 +124,8 @@ void PWMDimmerInit(void) 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 +156,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 +174,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 +196,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 +204,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 +230,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 +247,33 @@ void PWMDimmerHandleButton() * 2 1 3 * 3 1 2 */ - uint32_t button_index = XdrvMailbox.index; - uint32_t now = millis(); - bool toggle_power = false; + + // 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; + } + + 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 + 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 +282,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 +310,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 +319,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,45 +332,44 @@ 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 + // 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) { - 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; + button_hold_time[button_index] = now + 1000; } else { #endif // USE_PWM_DIMMER_REMOTE - target_bri = Settings.bri_preset_low; + 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 - toggle_power = true; - - // Reset the power button hold time to delay before we start increasing the brightness. - button_hold_time[button_index] = now + 500; } } } // 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 +377,97 @@ 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]; + 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); + 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 + 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 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) { + 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 +475,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,206 +495,222 @@ 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; + state_updated = true; +#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.led_timeout ^= 1; + if (Light.power) PWMDimmerSetBrightnessLeds(Settings.flag4.led_timeout ? -1 : 0); + } + + // Toggle the LED timeout. + else { + Settings.flag4.powered_off_led ^= 1; + PWMDimmerSetPoweredOffLed(); + } +#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; + state_updated = true; - // 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 - 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 } - } - - if (turn_off_brightness_leds_time && turn_off_brightness_leds_time < millis()) { - turn_off_brightness_leds_time = 0; - PWMDimmerSetBrightnessLeds(-1); +#endif // USE_PWM_DIMMER_REMOTE } } @@ -947,16 +733,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 +763,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 +774,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 +794,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,45 +804,27 @@ 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: -#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++; - } - } -#endif // USE_PWM_DIMMER_REMOTE - break; - - case FUNC_MODULE_INIT: - PWMModuleInit(); - result = true; + PWMModulePreInit(); break; } return result; } -#endif // USE_PWM_DIMMER \ No newline at end of file +#endif // USE_PWM_DIMMER