Skip to content

Commit

Permalink
Add long press to action button (#23)
Browse files Browse the repository at this point in the history
  • Loading branch information
TillFleisch authored Jan 25, 2024
1 parent d78af80 commit ba94c5d
Show file tree
Hide file tree
Showing 8 changed files with 172 additions and 28 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,27 @@ A example configuration can be found [here](example.yaml)
- **display_uart**(**Required**, string): ID of the UART-Component connected to the display unit
- **mainboard_uart**(**Required**, string): ID of the UART-Component connected to the mainboard
- **power_pin**(**Required**, [Pin](https://esphome.io/guides/configuration-types.html#config-pin)): Pin to which the MOSFET/Transistor is connected. This pin is used to temporarily turn of the display unit.
- **invert_power_pin**(**Optional**: boolean): If set to true the output of the power pin will be inverted. Defaults to `false`.
- **invert_power_pin**(**Optional**: boolean): If set to `true` the output of the power pin will be inverted. Defaults to `false`.
- **power_trip_delay**(**Optional**: Time): Determines the length of the power outage applied to the display unit, which is to trick it into turning on. Defaults to `500ms`.

## Philips Power switch

- **controller_id**(**Required**, string): The Philips Series 2200-Controller to which this entity belongs
- **clean**(**Optional**: boolean): If set to true the machine will perform a cleaning cycle during startup. Otherwise the machine will power on without cleaning. Defaults to `true`.
- **clean**(**Optional**: boolean): If set to `true` the machine will perform a cleaning cycle during startup. Otherwise the machine will power on without cleaning. Defaults to `true`.
- All other options from [Switch](https://esphome.io/components/switch/index.html#config-switch)

## Philips Action Button

- **controller_id**(**Required**, string): The Philips Series 2200-Controller to which this entity belongs
- **action**(**Required**, int): The action performed by this button. Select one of `MAKE_COFFEE`, `SELECT_COFFEE`, `SELECT_ESPRESSO`, `MAKE_ESPRESSO`, `SELECT_HOT_WATER`, `MAKE_HOT_WATER`, `SELECT_STEAM`, `MAKE_STEAM`, `BEAN`, `SIZE`, `AQUA_CLEAN`, `CALC_CLEAN`, `PLAY_PAUSE`.
- **long_press**(**Optional**, boolean): If set to `true` this button will perform a long press. This option is only available for actions which don't include `MAKE`.
- All other options from [Button](https://esphome.io/components/button/index.html#config-button)

## Philips Status Sensor

- **controller_id**(**Required**, string): The Philips Series 2200-Controller to which this entity belongs
- All other options from [Text Sensor](https://esphome.io/components/text_sensor/index.html#config-text-sensor)
- **use_cappuccino**(**Optional**, boolean): If set to true `Cappuccino selected` selected will be reported instead of `Steam selected`. This option is intended for machines like the EP2230 which can make cappuccino. Default to `false`.
- **use_cappuccino**(**Optional**, boolean): If set to `true`, `Cappuccino selected` will be reported instead of `Steam selected`. This option is intended for machines like the EP2230 which can make cappuccino. Default to `false`.

## Bean Settings

Expand Down
33 changes: 33 additions & 0 deletions components/philips_action_button/action_button.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,24 @@ namespace esphome
LOG_BUTTON("", "Philips Action Button", this);
}

void ActionButton::loop()
{
// Repeated message sending for long presses
if (millis() - press_start_ <= LONG_PRESS_DURATION)
{
if (millis() - last_message_sent_ > LONG_PRESS_REPETITION_DELAY)
{
last_message_sent_ = millis();
perform_action();
}
is_long_pressing_ = true;
}
else
{
is_long_pressing_ = false;
}
}

void ActionButton::write_array(const std::vector<uint8_t> &data)
{
for (unsigned int i = 0; i <= MESSAGE_REPETITIONS; i++)
Expand All @@ -23,6 +41,21 @@ namespace esphome
}

void ActionButton::press_action()
{
if (should_long_press_)
{
// Reset button press start time
press_start_ = millis();
last_message_sent_ = 0;
}
else
{
// Perform a single button press
perform_action();
}
}

void ActionButton::perform_action()
{
auto action = action_;
// Coffee
Expand Down
37 changes: 37 additions & 0 deletions components/philips_action_button/action_button.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

#define MESSAGE_REPETITIONS 5
#define BUTTON_SEQUENCE_DELAY 100
#define LONG_PRESS_REPETITION_DELAY 50
#define LONG_PRESS_DURATION 3500

namespace esphome
{
Expand Down Expand Up @@ -43,6 +45,7 @@ namespace esphome
{
public:
void dump_config() override;
void loop() override;

/**
* @brief Set the action used by this ActionButton.
Expand All @@ -58,6 +61,26 @@ namespace esphome
*/
void set_uart_device(uart::UARTDevice *uart) { mainboard_uart_ = uart; };

/**
* @brief Sets the long press parameter on this button component.
*
* @param long_press True if a long press should be executed, false otherwise
*/
void set_long_press(bool long_press)
{
should_long_press_ = long_press;
}

/**
* @brief Determines if the button is currently performing a long press
*
* @return True if the button is currently performing a long press
*/
bool is_long_pressing()
{
return is_long_pressing_;
}

private:
/**
* @brief Writes data MESSAGE_REPETITIONS times to the mainboard uart
Expand All @@ -72,10 +95,24 @@ namespace esphome
*/
void press_action() override;

/**
* @brief Writes the button to uart or initializes loop based message sending
*
*/
void perform_action();

/// @brief Action used by this Button
Action action_;
/// @brief reference to uart connected to mainboard
uart::UARTDevice *mainboard_uart_;
/// @brief time in ms for how long the button should be pressed.
bool should_long_press_ = false;
/// @brief true if the component is currently performing a long press
bool is_long_pressing_ = false;
/// @brief time at which the button press was started
long press_start_ = 0;
/// @brief time at which the last message was sent
long last_message_sent_ = 0;
};
} // namespace philips_action_button
} // namespace philips_series_2200
Expand Down
29 changes: 22 additions & 7 deletions components/philips_action_button/button.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
DEPENDENCIES = ["philips_series_2200"]

CONF_ACTION = "action"
CONF_LONG_PRESS = "long_press"

philips_action_button_ns = cg.esphome_ns.namespace("philips_series_2200").namespace(
"philips_action_button"
Expand All @@ -32,13 +33,26 @@
"PLAY_PAUSE": Action.PLAY_PAUSE,
}

CONFIG_SCHEMA = button.BUTTON_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(ActionButton),
cv.Required(CONTROLLER_ID): cv.use_id(PhilipsSeries2200),
cv.Required(CONF_ACTION): cv.enum(ACTIONS, upper=True, space="_"),
}
).extend(cv.COMPONENT_SCHEMA)

def validate_long_press(config):
"""Validate that long press only applies to select options."""
if config[CONF_LONG_PRESS] and "MAKE" in config[CONF_ACTION]:
raise cv.Invalid(f"Action {config[CONF_ACTION]} does not support long press.")

return config


CONFIG_SCHEMA = cv.All(
button.BUTTON_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(ActionButton),
cv.Required(CONTROLLER_ID): cv.use_id(PhilipsSeries2200),
cv.Required(CONF_ACTION): cv.enum(ACTIONS, upper=True, space="_"),
cv.Optional(CONF_LONG_PRESS, default=False): cv.boolean,
},
).extend(cv.COMPONENT_SCHEMA),
validate_long_press,
)


async def to_code(config):
Expand All @@ -48,4 +62,5 @@ async def to_code(config):
await button.register_button(var, config)

cg.add(var.set_action(config[CONF_ACTION]))
cg.add(var.set_long_press(config[CONF_LONG_PRESS]))
cg.add(parent.add_action_button(var))
15 changes: 14 additions & 1 deletion components/philips_series_2200/philips_series_2200.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,20 @@ namespace esphome
uint8_t size = std::min(display_uart_.available(), BUFFER_SIZE);
display_uart_.read_array(buffer, size);

mainboard_uart_.write_array(buffer, size);
// Check if a action button is currently performing a long press
bool long_pressing = false;
for (philips_action_button::ActionButton *button : action_buttons_)
{
if (button->is_long_pressing())
{
long_pressing = true;
break;
}
}

// Drop messages if button long-press is currently injecting messages
if (!long_pressing)
mainboard_uart_.write_array(buffer, size);
last_message_from_display_time_ = millis();
}

Expand Down
4 changes: 4 additions & 0 deletions components/philips_series_2200/philips_series_2200.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ namespace esphome
void add_action_button(philips_action_button::ActionButton *action_button)
{
action_button->set_uart_device(&mainboard_uart_);
action_buttons_.push_back(action_button);
}

/**
Expand Down Expand Up @@ -140,6 +141,9 @@ namespace esphome

/// @brief list of registered water sensors
std::vector<philips_size_settings::SizeSettings *> size_setting_;

/// @brief list of registered action buttons
std::vector<philips_action_button::ActionButton *> action_buttons_;
};

} // namespace philips_series_2200
Expand Down
49 changes: 45 additions & 4 deletions components/philips_status_sensor/status_sensor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,17 +79,32 @@ namespace esphome
if (data[3] == 0x00 && data[4] == 0x00 && (data[5] == 0x07 || data[5] == 0x38) && data[6] == 0x00)
{
if (millis() - play_pause_last_change_ < BLINK_THRESHOLD)
update_state((data[5] == 0x07) ? "Coffee selected" : "2x Coffee selected");
{
if (data[9] == 0x38)
{
update_state("Ground Coffee selected");
}
else if (data[11] == 0x00)
{
update_state("Coffee programming mode selected");
}
else
{
update_state((data[5] == 0x07) ? "Coffee selected" : "2x Coffee selected");
}
}
else
{
update_state("Busy");
}
return;
}

// Steam selected
if (data[3] == 0x00 && data[4] == 0x00 && data[5] == 0x00 && data[6] == 0x07)
{
if (millis() - play_pause_last_change_ < BLINK_THRESHOLD)
update_state(use_cappuccino_?"Cappuccino selected":"Steam selected");
update_state(use_cappuccino_ ? "Cappuccino selected" : "Steam selected");
else
update_state("Busy");
return;
Expand All @@ -99,19 +114,45 @@ namespace esphome
if (data[3] == 0x00 && data[4] == 0x07 && data[5] == 0x00 && data[6] == 0x00)
{
if (millis() - play_pause_last_change_ < BLINK_THRESHOLD)
update_state("Hot water selected");
{
if (data[11] == 0x07)
{
update_state("Hot water selected");
}
else
{
update_state("Hot water programming mode selected");
}
}
else
{
update_state("Busy");
}
return;
}

// Espresso selected
if ((data[3] == 0x07 || data[3] == 0x38) && data[4] == 0x00 && data[5] == 0x00 && data[6] == 0x00)
{
if (millis() - play_pause_last_change_ < BLINK_THRESHOLD)
update_state((data[3] == 0x07) ? "Espresso selected" : "2x Espresso selected");
{
if (data[9] == 0x38)
{
update_state("Ground Espresso selected");
}
else if (data[11] == 0x00)
{
update_state("Espresso programming mode selected");
}
else
{
update_state((data[3] == 0x07) ? "Espresso selected" : "2x Espresso selected");
}
}
else
{
update_state("Busy");
}
return;
}
}
Expand Down
26 changes: 13 additions & 13 deletions protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,19 +97,19 @@ The following table show the purpose of each byte and their known states
| 0 | START |
| 1 | START |
| 2 | INSTRUCTION |
| 3 | Espresso-LED | `03`/`07` - half/full brightness ; `38` - 2x espresso |
| 4 | Hot Water-LED | `03`/`07` - half/full brightness |
| 5 | Coffee-LED | `03`/`07` - half/full brightness; `38` - 2x coffee |
| 6 | Steam-LED | `03`/`07` - half/full brightness |
| 7 | | unknown |
| 8 | Bean-LED | `00` - 1 LED; `38` - 2 LEDs; `3F` - 3 LEDs |
| 9 | Bean-LED | `07` - show led group; `38` - powder selected |
| 10 | Size-LED | `00` - 1 LED; `38` - 2 LEDs; `3F` - 3 LEDs |
| 11 | Size-LED | `07` - show led group |
| 12 | | probably aqua_clean/calc_clean |
| 13 | | probably aqua_clean/calc_clean |
| 14 | Waste&Warning-LED | `07` - waste; `38` - warning sign |
| 15 | Play/Pause-LED | `07` - on |
| 3 | Espresso-LED | `03`/`07` - half/full brightness ; `38` - 2x espresso |
| 4 | Hot Water-LED | `03`/`07` - half/full brightness |
| 5 | Coffee-LED | `03`/`07` - half/full brightness; `38` - 2x coffee |
| 6 | Steam-LED | `03`/`07` - half/full brightness |
| 7 | | unknown |
| 8 | Bean-LED | `00` - 1 LED; `38` - 2 LEDs; `3F` - 3 LEDs |
| 9 | Bean-LED | `07` - show led group; `38` - powder selected |
| 10 | Size-LED | `00` - 1 LED; `38` - 2 LEDs; `3F` - 3 LEDs; `07` - Top LED |
| 11 | Size-LED | `07` - show led group |
| 12 | | probably aqua_clean/calc_clean |
| 13 | | probably aqua_clean/calc_clean |
| 14 | Waste&Warning-LED | `07` - waste; `38` - warning sign |
| 15 | Play/Pause-LED | `07` - on |
| 16 | checksum |
| 17 | checksum |

Expand Down

0 comments on commit ba94c5d

Please sign in to comment.