diff --git a/README.md b/README.md index 07f52af..ce04fc6 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ A example configuration can be found [here](example.yaml) - **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`. - **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`. +- **model**(**Optional**: int): Different models or revisions may use different commands. This option can be used to specify the command set used by this component. Select one of `EP2220`. Defaults to `EP2220`. ## Philips Power switch diff --git a/components/philips_series_2200/__init__.py b/components/philips_series_2200/__init__.py index 3030d47..73e5de3 100644 --- a/components/philips_series_2200/__init__.py +++ b/components/philips_series_2200/__init__.py @@ -13,6 +13,9 @@ INVERT_POWER_PIN = "invert_power_pin" POWER_TRIP_DELAY = "power_trip_delay" +CONF_COMMAND_SET = "model" +COMMAND_SETS = {"EP_2220": "PHILIPS_EP2220"} + philips_series_2200_ns = cg.esphome_ns.namespace("philips_series_2200") PhilipsSeries2200 = philips_series_2200_ns.class_("PhilipsSeries2200", cg.Component) @@ -30,11 +33,17 @@ max_included=cv.TimePeriod(milliseconds=10000), ), ), + cv.Optional(CONF_COMMAND_SET, default="EP_2220"): cv.enum( + COMMAND_SETS, upper=True, space="_" + ), } ).extend(cv.COMPONENT_SCHEMA) def to_code(config): + # Use user-specified command set, default to EP_2200 + cg.add_define(config[CONF_COMMAND_SET]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) diff --git a/components/philips_series_2200/button/action_button.cpp b/components/philips_series_2200/button/action_button.cpp index 84a2758..6d5e19d 100644 --- a/components/philips_series_2200/button/action_button.cpp +++ b/components/philips_series_2200/button/action_button.cpp @@ -61,7 +61,7 @@ namespace esphome // Coffee if (action == SELECT_COFFEE || action == MAKE_COFFEE) { - write_array({0xD5, 0x55, 0x00, 0x01, 0x02, 0x00, 0x02, 0x08, 0x00, 0x00, 0x39, 0x1C}); + write_array(command_press_coffee); if (action == SELECT_COFFEE) return; @@ -72,7 +72,7 @@ namespace esphome // Espresso if (action == SELECT_ESPRESSO || action == MAKE_ESPRESSO) { - write_array({0xD5, 0x55, 0x00, 0x01, 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x09, 0x2D}); + write_array(command_press_espresso); if (action == SELECT_ESPRESSO) return; delay(BUTTON_SEQUENCE_DELAY); @@ -82,7 +82,7 @@ namespace esphome // Hot water if (action == SELECT_HOT_WATER || action == MAKE_HOT_WATER) { - write_array({0xD5, 0x55, 0x00, 0x01, 0x02, 0x00, 0x02, 0x04, 0x00, 0x00, 0x21, 0x01}); + write_array(command_press_hot_water); if (action == SELECT_HOT_WATER) return; delay(BUTTON_SEQUENCE_DELAY); @@ -92,7 +92,7 @@ namespace esphome // Steam if (action == SELECT_STEAM || action == MAKE_STEAM) { - write_array({0xD5, 0x55, 0x00, 0x01, 0x02, 0x00, 0x02, 0x10, 0x00, 0x00, 0x09, 0x26}); + write_array(command_press_steam); if (action == SELECT_STEAM) return; delay(BUTTON_SEQUENCE_DELAY); @@ -101,19 +101,19 @@ namespace esphome // press/play or subsequent press/play if (action == PLAY_PAUSE) - write_array({0xD5, 0x55, 0x00, 0x01, 0x02, 0x00, 0x02, 0x00, 0x00, 0x01, 0x19, 0x32}); + write_array(command_press_play_pause); else if (action == SELECT_BEAN) // bean button - write_array({0xD5, 0x55, 0x00, 0x01, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x09, 0x2F}); + write_array(command_press_bean); else if (action == SELECT_SIZE) // size button - write_array({0xD5, 0x55, 0x00, 0x01, 0x02, 0x00, 0x02, 0x00, 0x04, 0x00, 0x20, 0x05}); + write_array(command_press_size); else if (action == SELECT_AQUA_CLEAN) // aqua clean button - write_array({0xD5, 0x55, 0x00, 0x01, 0x02, 0x00, 0x02, 0x00, 0x10, 0x00, 0x0D, 0x36}); + write_array(command_press_aqua_clean); else if (action == SELECT_CALC_CLEAN) // calc clean button - write_array({0xD5, 0x55, 0x00, 0x01, 0x02, 0x00, 0x02, 0x00, 0x20, 0x00, 0x28, 0x37}); + write_array(command_press_calc_clean); else ESP_LOGE(TAG, "Invalid Action provided!"); } diff --git a/components/philips_series_2200/button/action_button.h b/components/philips_series_2200/button/action_button.h index 31b5df1..3e14af9 100644 --- a/components/philips_series_2200/button/action_button.h +++ b/components/philips_series_2200/button/action_button.h @@ -3,6 +3,7 @@ #include "esphome/core/component.h" #include "esphome/components/button/button.h" #include "esphome/components/uart/uart.h" +#include "../commands.h" #define MESSAGE_REPETITIONS 5 #define BUTTON_SEQUENCE_DELAY 100 diff --git a/components/philips_series_2200/commands.h b/components/philips_series_2200/commands.h new file mode 100644 index 0000000..5d84ef5 --- /dev/null +++ b/components/philips_series_2200/commands.h @@ -0,0 +1,51 @@ +#pragma once +#include +#include + +namespace esphome +{ + namespace philips_series_2200 + { +#if defined(PHILIPS_EP2220) +#define USE_DEFAULT_PHILIPS_COMMAND_SET +#else +#define USE_DEFAULT_PHILIPS_COMMAND_SET +#endif + +#ifdef USE_DEFAULT_PHILIPS_COMMAND_SET + const uint8_t message_header[2] = {0xD5, 0x55}; + const uint8_t led_off = 0x00; + const uint8_t led_half = 0x03; + const uint8_t led_on = 0x07; + const uint8_t led_second = 0x38; + const uint8_t led_third = 0x3F; + + const std::vector command_pre_power_on = + {0xD5, 0x55, 0x0A, 0x01, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0E, 0x12}; + const std::vector command_power_with_cleaning = + {0xD5, 0x55, 0x02, 0x01, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x38, 0x15}; + const std::vector command_power_without_cleaning = + {0xD5, 0x55, 0x01, 0x01, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x25, 0x27}; + const std::vector command_power_off = + {0xD5, 0x55, 0x00, 0x01, 0x02, 0x00, 0x02, 0x01, 0x00, 0x00, 0x1D, 0x3B}; + const std::vector command_press_play_pause = + {0xD5, 0x55, 0x00, 0x01, 0x02, 0x00, 0x02, 0x00, 0x00, 0x01, 0x19, 0x32}; + const std::vector command_press_coffee = + {0xD5, 0x55, 0x00, 0x01, 0x02, 0x00, 0x02, 0x08, 0x00, 0x00, 0x39, 0x1C}; + const std::vector command_press_espresso = + {0xD5, 0x55, 0x00, 0x01, 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x09, 0x2D}; + const std::vector command_press_hot_water = + {0xD5, 0x55, 0x00, 0x01, 0x02, 0x00, 0x02, 0x04, 0x00, 0x00, 0x21, 0x01}; + const std::vector command_press_steam = + {0xD5, 0x55, 0x00, 0x01, 0x02, 0x00, 0x02, 0x10, 0x00, 0x00, 0x09, 0x26}; + const std::vector command_press_bean = + {0xD5, 0x55, 0x00, 0x01, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x09, 0x2F}; + const std::vector command_press_size = + {0xD5, 0x55, 0x00, 0x01, 0x02, 0x00, 0x02, 0x00, 0x04, 0x00, 0x20, 0x05}; + const std::vector command_press_aqua_clean = + {0xD5, 0x55, 0x00, 0x01, 0x02, 0x00, 0x02, 0x00, 0x10, 0x00, 0x0D, 0x36}; + const std::vector command_press_calc_clean = + {0xD5, 0x55, 0x00, 0x01, 0x02, 0x00, 0x02, 0x00, 0x20, 0x00, 0x28, 0x37}; +#endif + } // namespace philips_series_2200 +} // namespace esphome \ No newline at end of file diff --git a/components/philips_series_2200/number/beverage_setting.cpp b/components/philips_series_2200/number/beverage_setting.cpp index 162494e..ef08473 100644 --- a/components/philips_series_2200/number/beverage_setting.cpp +++ b/components/philips_series_2200/number/beverage_setting.cpp @@ -26,7 +26,7 @@ namespace esphome void BeverageSetting::update_status(uint8_t *data, size_t len) { // reject invalid messages - if (len < 19 && data[0] != 0xD5 && data[1] != 0x55) + if (len < 19 && data[0] != message_header[0] && data[1] != message_header[1]) return; // only apply status if source is currently selected @@ -44,17 +44,17 @@ namespace esphome ((source_ == CAPPUCCINO || source_ == ANY) && status_sensor_->get_raw_state().compare("Cappuccino selected") == 0))) { - if (data[is_bean_ ? 9 : 11] == 0x07) + if (data[is_bean_ ? 9 : 11] == led_on) { switch (data[is_bean_ ? 8 : 10]) { - case 0x00: + case led_off: update_state(1); break; - case 0x38: + case led_second: update_state(2); break; - case 0x3F: + case led_third: update_state(3); break; default: @@ -66,9 +66,9 @@ namespace esphome { for (unsigned int i = 0; i <= MESSAGE_REPETITIONS; i++) if (is_bean_) - mainboard_uart_->write_array({0xD5, 0x55, 0x00, 0x01, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x09, 0x2F}); + mainboard_uart_->write_array(command_press_bean); else - mainboard_uart_->write_array({0xD5, 0x55, 0x00, 0x01, 0x02, 0x00, 0x02, 0x00, 0x04, 0x00, 0x20, 0x05}); + mainboard_uart_->write_array(command_press_size); mainboard_uart_->flush(); last_transmission_ = millis(); } diff --git a/components/philips_series_2200/number/beverage_setting.h b/components/philips_series_2200/number/beverage_setting.h index 3a1ce36..eab73d4 100644 --- a/components/philips_series_2200/number/beverage_setting.h +++ b/components/philips_series_2200/number/beverage_setting.h @@ -4,6 +4,7 @@ #include "esphome/components/number/number.h" #include "esphome/components/uart/uart.h" #include "../text_sensor/status_sensor.h" +#include "../commands.h" #define MESSAGE_REPETITIONS 5 #define SETTINGS_BUTTON_SEQUENCE_DELAY 500 diff --git a/components/philips_series_2200/philips_series_2200.cpp b/components/philips_series_2200/philips_series_2200.cpp index 223fe9e..a993583 100644 --- a/components/philips_series_2200/philips_series_2200.cpp +++ b/components/philips_series_2200/philips_series_2200.cpp @@ -50,7 +50,7 @@ namespace esphome while (mainboard_uart_.available()) { uint8_t buffer = mainboard_uart_.peek(); - if (buffer == 0xD5) + if (buffer == message_header[0]) break; display_uart_.write(mainboard_uart_.read()); } @@ -64,7 +64,7 @@ namespace esphome display_uart_.write_array(buffer, size); // Only process messages starting with start bytes - if (size > 1 && buffer[0] == 0xD5 && buffer[1] == 0x55) + if (size > 1 && buffer[0] == message_header[0] && buffer[1] == message_header[1]) { last_message_from_mainboard_time_ = millis(); diff --git a/components/philips_series_2200/philips_series_2200.h b/components/philips_series_2200/philips_series_2200.h index fe78e84..82a9a88 100644 --- a/components/philips_series_2200/philips_series_2200.h +++ b/components/philips_series_2200/philips_series_2200.h @@ -2,6 +2,7 @@ #include "esphome/core/component.h" #include "esphome/components/uart/uart.h" +#include "commands.h" #ifdef USE_SWITCH #include "switch/power.h" #endif diff --git a/components/philips_series_2200/switch/power.cpp b/components/philips_series_2200/switch/power.cpp index 6166c63..a1e6133 100644 --- a/components/philips_series_2200/switch/power.cpp +++ b/components/philips_series_2200/switch/power.cpp @@ -41,20 +41,20 @@ namespace esphome { // Send pre-power on message for (unsigned int i = 0; i <= MESSAGE_REPETITIONS; i++) - mainboard_uart_->write_array({0xD5, 0x55, 0x0A, 0x01, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0E, 0x12}); + mainboard_uart_->write_array(command_pre_power_on); // Send power on message if (cleaning_) { // Send power on command with cleaning for (unsigned int i = 0; i <= MESSAGE_REPETITIONS; i++) - mainboard_uart_->write_array({0xD5, 0x55, 0x02, 0x01, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x38, 0x15}); + mainboard_uart_->write_array(command_power_with_cleaning); } else { // Send power on command without cleaning for (unsigned int i = 0; i <= MESSAGE_REPETITIONS; i++) - mainboard_uart_->write_array({0xD5, 0x55, 0x01, 0x01, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x25, 0x27}); + mainboard_uart_->write_array(command_power_without_cleaning); } mainboard_uart_->flush(); @@ -67,7 +67,7 @@ namespace esphome { // Send power off message for (unsigned int i = 0; i <= MESSAGE_REPETITIONS; i++) - mainboard_uart_->write_array({0xD5, 0x55, 0x00, 0x01, 0x02, 0x00, 0x02, 0x01, 0x00, 0x00, 0x1D, 0x3B}); + mainboard_uart_->write_array(command_power_off); mainboard_uart_->flush(); } diff --git a/components/philips_series_2200/switch/power.h b/components/philips_series_2200/switch/power.h index 38ae3a1..78adf55 100644 --- a/components/philips_series_2200/switch/power.h +++ b/components/philips_series_2200/switch/power.h @@ -3,6 +3,7 @@ #include "esphome/core/component.h" #include "esphome/components/switch/switch.h" #include "esphome/components/uart/uart.h" +#include "../commands.h" #define MESSAGE_REPETITIONS 5 #define POWER_TRIP_RETRY_DELAY 100 diff --git a/components/philips_series_2200/text_sensor/status_sensor.cpp b/components/philips_series_2200/text_sensor/status_sensor.cpp index 6e51201..cb6d767 100644 --- a/components/philips_series_2200/text_sensor/status_sensor.cpp +++ b/components/philips_series_2200/text_sensor/status_sensor.cpp @@ -21,20 +21,20 @@ namespace esphome void StatusSensor::update_status(uint8_t *data, size_t len) { // reject invalid messages - if (len < 19 && data[0] != 0xD5 && data[1] != 0x55) + if (len < 19 && data[0] != message_header[0] && data[1] != message_header[1]) return; // TODO: figure out how the checksum is calculated and only parse valid messages // Check if the play/pause button is on/off/blinking - if ((data[16] == 0x07) != play_pause_led_) + if ((data[16] == led_on) != play_pause_led_) { play_pause_last_change_ = millis(); } - play_pause_led_ = data[16] == 0x07; + play_pause_led_ = data[16] == led_on; // Check for idle state (selection led on) - if (data[3] == 0x07 && data[4] == 0x07 && data[5] == 0x07 && data[6] == 0x07) + if (data[3] == led_on && data[4] == led_on && data[5] == led_on && data[6] == led_on) { // selecting a beverage can result in a short "busy" period since the play/pause button has not been blinking // This can be circumvented: if the user is on the selection screen/idle we can reset the timer @@ -45,7 +45,7 @@ namespace esphome } // Check for rotating icons - pre heating - if (data[3] == 0x03 || data[4] == 0x03 || data[5] == 0x03 || data[6] == 0x03) + if (data[3] == led_half || data[4] == led_half || data[5] == led_half || data[6] == led_half) { if (play_pause_led_) update_state("Cleaning"); @@ -55,42 +55,42 @@ namespace esphome } // Water empty led - if (data[14] == 0x38) + if (data[14] == led_second) { update_state("Water empty"); return; } // Waste container led - if (data[15] == 0x07) + if (data[15] == led_on) { update_state("Waste container warning"); return; } // Warning/Error led - if (data[15] == 0x38) + if (data[15] == led_second) { update_state("Error"); return; } // Coffee selected - if (data[3] == 0x00 && data[4] == 0x00 && (data[5] == 0x07 || data[5] == 0x38) && data[6] == 0x00) + if (data[3] == led_off && data[4] == led_off && (data[5] == led_on || data[5] == led_second) && data[6] == led_off) { if (millis() - play_pause_last_change_ < BLINK_THRESHOLD) { - if (data[9] == 0x38) + if (data[9] == led_second) { update_state("Ground Coffee selected"); } - else if (data[11] == 0x00) + else if (data[11] == led_off) { update_state("Coffee programming mode selected"); } else { - update_state((data[5] == 0x07) ? "Coffee selected" : "2x Coffee selected"); + update_state((data[5] == led_on) ? "Coffee selected" : "2x Coffee selected"); } } else @@ -101,7 +101,7 @@ namespace esphome } // Steam selected - if (data[3] == 0x00 && data[4] == 0x00 && data[5] == 0x00 && data[6] == 0x07) + if (data[3] == led_off && data[4] == led_off && data[5] == led_off && data[6] == led_on) { if (millis() - play_pause_last_change_ < BLINK_THRESHOLD) update_state(use_cappuccino_ ? "Cappuccino selected" : "Steam selected"); @@ -111,11 +111,11 @@ namespace esphome } // Hot water selected - if (data[3] == 0x00 && data[4] == 0x07 && data[5] == 0x00 && data[6] == 0x00) + if (data[3] == led_off && data[4] == led_on && data[5] == led_off && data[6] == led_off) { if (millis() - play_pause_last_change_ < BLINK_THRESHOLD) { - if (data[11] == 0x07) + if (data[11] == led_on) { update_state("Hot water selected"); } @@ -132,21 +132,21 @@ namespace esphome } // Espresso selected - if ((data[3] == 0x07 || data[3] == 0x38) && data[4] == 0x00 && data[5] == 0x00 && data[6] == 0x00) + if ((data[3] == led_on || data[3] == led_second) && data[4] == led_off && data[5] == led_off && data[6] == led_off) { if (millis() - play_pause_last_change_ < BLINK_THRESHOLD) { - if (data[9] == 0x38) + if (data[9] == led_second) { update_state("Ground Espresso selected"); } - else if (data[11] == 0x00) + else if (data[11] == led_off) { update_state("Espresso programming mode selected"); } else { - update_state((data[3] == 0x07) ? "Espresso selected" : "2x Espresso selected"); + update_state((data[3] == led_on) ? "Espresso selected" : "2x Espresso selected"); } } else diff --git a/components/philips_series_2200/text_sensor/status_sensor.h b/components/philips_series_2200/text_sensor/status_sensor.h index 6061244..083d40d 100644 --- a/components/philips_series_2200/text_sensor/status_sensor.h +++ b/components/philips_series_2200/text_sensor/status_sensor.h @@ -3,6 +3,7 @@ #include "esphome/core/component.h" #include "esphome/components/text_sensor/text_sensor.h" #include "esphome/components/uart/uart.h" +#include "../commands.h" // Feel free to lower this, you might get some invalid intermittent state though #define REPEAT_REQUIREMENT 30