From 76f27cdfeb23b879c4665635aa1f3a370f9fb729 Mon Sep 17 00:00:00 2001 From: T Date: Thu, 20 Mar 2025 16:49:53 +0100 Subject: [PATCH] Bug fixes and switch --- optoma_rs232.cpp | 114 ++++++++++++++++++++++++++--------------------- optoma_rs232.h | 35 ++++++++++++--- select.py | 8 ++-- sensor.py | 2 +- switch.py | 23 ++++++++++ 5 files changed, 120 insertions(+), 62 deletions(-) create mode 100644 switch.py diff --git a/optoma_rs232.cpp b/optoma_rs232.cpp index 84bd935..8aa2f81 100644 --- a/optoma_rs232.cpp +++ b/optoma_rs232.cpp @@ -14,20 +14,19 @@ static const char *QUERIES[] = { "~00150 1\r", // Info }; -template static void publish(C *c, M m) { +template static void publish(C *c, const M &m) { if (c) c->publish_state(m); } void OptomaRS232Component::publish_input_(const std::string &state) const { -#ifdef USE_SELECT - if (beamer_input_select_) - beamer_input_select_->publish_state(state); -#endif -#ifdef USE_TEXT_SENSOR - if (beamer_input_text_sensor_) - beamer_input_text_sensor_->publish_state(state); -#endif + publish(beamer_input_select_, state); + publish(beamer_input_text_sensor_, state); +} + +void OptomaRS232Component::publish_power_(const bool state) const { + publish(beamer_power_binary_sensor_, state); + publish(beamer_power_switch_, state); } void OptomaRS232Component::dump_config() { @@ -63,7 +62,7 @@ void OptomaRS232Component::loop() { } void OptomaRS232Component::update() { - last_query_ = (last_query_ + 1) % sizeof(QUERIES) / sizeof(QUERIES[0]); + last_query_ = (last_query_ + 1) % std::size(QUERIES); write_array(reinterpret_cast(QUERIES[last_query_]), strlen(QUERIES[last_query_])); } @@ -90,13 +89,16 @@ void OptomaRS232Component::beamer_input_select_changed(const std::string &state, write_array(reinterpret_cast(data), strlen(data)); } +void OptomaRS232Component::beamer_power_switch_changed(const bool state) { + const char *cmd = state ? "~0000 1\r" : "~0000 2\r"; + write_array(reinterpret_cast(cmd), strlen(cmd)); +} + void OptomaRS232Component::process_line_(const std::string &str) { // if we are waiting for the projector to respond to a command. // it will respond P (pass) or F (fail) before giving us the actual response to the command. - if (str == "P" || str == "F") { - ESP_LOGD(TAG, "command response received"); + if (str == "P" || str == "F") return; - } // assuming any commands have been dealt with above, we listen for messages from the projector // the OK-something messages are in response to status queries, sometimes these are in caps, sometimes not @@ -104,23 +106,24 @@ void OptomaRS232Component::process_line_(const std::string &str) { // the INFO messages come in automatically when the projector changes state if ( // x == "OK1" || // status query returned power on str == "INFO1") { // warming up - publish(beamer_power_binary_sensor_, true); + publish_power_(true); return; } if ( // x == "OK0" || // status query returned power off str == "INFO2" || // cooling down str == "INFO0") { // going into standby - publish(beamer_power_binary_sensor_, false); + publish_power_(false); return; } if (str_startswith(str, "OK")) { + // ESP_LOGD(TAG, "OK-message: %s", str.c_str()); process_query_response_(str); return; } - ESP_LOGD("projector", "unhandled message: %s", str.c_str()); + // ESP_LOGD("projector", "unhandled message: %s", str.c_str()); } void OptomaRS232Component::process_query_response_(const std::string &str) { @@ -135,43 +138,44 @@ void OptomaRS232Component::process_query_response_(const std::string &str) { case 2: publish(beamer_fan1_sensor_, strtol(str.c_str() + 2, 0, 10)); break; - case 3: { - char buf[17]{}; - strncpy(buf, str.c_str(), sizeof(buf)); - publish(beamer_color_mode_sensor_, strtol(buf + 14, 0, 10)); - buf[14] = 0; + case 3: + if (str.length() >= 13) { + char buf[17]{}; + strncpy(buf, str.c_str(), sizeof(buf)); + publish(beamer_color_mode_sensor_, strtol(buf + 14, 0, 10)); + buf[14] = 0; - // publish(beamer_firmware_,strtol(buf + 10, 0, 10)); - buf[10] = 0; + // publish(beamer_firmware_,strtol(buf + 10, 0, 10)); + buf[10] = 0; - int input = strtol(buf + 8, 0, 10); // BUGGY - switch (input) { - case Inputs::HDMI_1: - current_input_ = Inputs::HDMI_1; - publish_input_("HDMI 1"); - break; - case Inputs::HDMI_2: - current_input_ = Inputs::HDMI_2; - publish_input_("HDMI 2"); - break; - case Inputs::VGA: - current_input_ = Inputs::VGA; - publish_input_("VGA"); - break; - default: - case Inputs::UNKNOWN: - current_input_ = Inputs::UNKNOWN; - publish_input_("Unknown"); - break; + int input = strtol(buf + 8, 0, 10); // OK10041700C00900 + buf[8] = 0; + switch (input) { + case Inputs::HDMI_1: + current_input_ = Inputs::HDMI_1; + publish_input_("HDMI 1"); + break; + case Inputs::HDMI_2: + current_input_ = Inputs::HDMI_2; + publish_input_("HDMI 2"); + break; + case Inputs::VGA: + current_input_ = Inputs::VGA; + publish_input_("VGA"); + break; + default: + case Inputs::UNKNOWN: + current_input_ = Inputs::UNKNOWN; + publish_input_("Unknown"); + break; + } + + publish(beamer_lamp_time_sensor_, strtol(buf + 3, 0, 10)); + buf[3] = 0; + + publish_power_(strtol(buf + 2, 0, 10)); + break; } - buf[8] = 0; - - publish(beamer_lamp_time_sensor_, strtol(buf + 3, 0, 10)); - buf[3] = 0; - - publish(beamer_power_binary_sensor_, strtol(buf + 2, 0, 10)); - break; - } default:; } } @@ -181,10 +185,18 @@ void OptomaRS232Component::process_query_response_(const std::string &str) { void InputSelect::control(const std::string &value) { this->publish_state(value); auto index = this->index_of(value); - if (index.has_value()) + if (index.has_value() && this->parent_) this->parent_->beamer_input_select_changed(value, *index); } #endif +#ifdef USE_SWITCH +void PowerSwitch::write_state(const bool state) { + this->publish_state(state); + if (this->parent_) + this->parent_->beamer_power_switch_changed(state); +} +#endif + } // namespace optoma_rs232 } // namespace esphome diff --git a/optoma_rs232.h b/optoma_rs232.h index 36aa75a..57a2cdc 100644 --- a/optoma_rs232.h +++ b/optoma_rs232.h @@ -2,7 +2,14 @@ #include "esphome/core/component.h" #include "esphome/components/uart/uart.h" -#ifdef USE_SELECT + +#undef USE_SENSOR +#undef USE_BINARY_SENSOR +#undef USE_TEXT_SENSOR +#undef USE_SELECT +#undef USE_SWITCH + +#ifdef USE_SENSOR #include "esphome/components/sensor/sensor.h" #endif #ifdef USE_BINARY_SENSOR @@ -14,13 +21,17 @@ #ifdef USE_SELECT #include "esphome/components/select/select.h" #endif +#ifdef USE_SWITCH +#include "esphome/components/switch/switch.h" +#endif namespace esphome { namespace optoma_rs232 { // clang-format off class DummySensor { public: template static void publish_state(M) {} }; -class DummySelect {}; +class DummySelect : public DummySensor {}; +class DummySwitch : public DummySensor {}; // clang-format on enum Inputs { @@ -37,19 +48,23 @@ class OptomaRS232Component : public uart::UARTDevice, public PollingComponent { float get_setup_priority() const override { return setup_priority::DATA; } void update() override; void beamer_input_select_changed(const std::string &, size_t); + void beamer_power_switch_changed(bool); // clang-format off #ifndef USE_SENSOR -#define SUB_SENSOR(name) protected: DummySensor *name##_sensor_{nullptr}; public: +#define SUB_SENSOR(name) protected: DummySensor *name## _sensor_{nullptr}; public: #endif #ifndef USE_BINARY_SENSOR -#define SUB_BINARY_SENSOR(name) protected: DummySensor *name##_binary_sensor_{nullptr}; public: +#define SUB_BINARY_SENSOR(name) protected: DummySensor *name## _binary_sensor_{nullptr}; public: #endif #ifndef USE_TEXT_SENSOR -#define SUB_TEXT_SENSOR(name) protected: DummySensor *name##_text_sensor_{nullptr}; public: +#define SUB_TEXT_SENSOR(name) protected: DummySensor *name## _text_sensor_{nullptr}; public: #endif #ifndef USE_SELECT -#define SUB_SELECT(name) protected: DummySelect *name##_select_{nullptr}; public: +#define SUB_SELECT(name) protected: DummySelect *name## _select_{nullptr}; public: +#endif +#ifndef USE_SWITCH +#define SUB_SWITCH(name) protected: DummySwitch *name## _switch_{nullptr}; public: #endif // clang-format on @@ -60,6 +75,7 @@ class OptomaRS232Component : public uart::UARTDevice, public PollingComponent { SUB_BINARY_SENSOR(beamer_power) SUB_TEXT_SENSOR(beamer_input) SUB_SELECT(beamer_input) + SUB_SWITCH(beamer_power) protected: Inputs current_input_{UNKNOWN}; @@ -74,6 +90,7 @@ class OptomaRS232Component : public uart::UARTDevice, public PollingComponent { size_t cursor_ = 0; void publish_input_(const std::string &state) const; + void publish_power_(bool state) const; }; #ifdef USE_SELECT @@ -82,6 +99,12 @@ class InputSelect : public select::Select, public Parented void control(const std::string &value) override; }; #endif +#ifdef USE_SWITCH +class PowerSwitch : public switch_::Switch, public Parented { + protected: + void write_state(bool state) override; +}; +#endif } // namespace optoma_rs232 } // namespace esphome diff --git a/select.py b/select.py index 0e66f5f..8619ca4 100644 --- a/select.py +++ b/select.py @@ -6,8 +6,6 @@ from . import CONF_OPTOMA_RS232_ID, OptomaRS232Component, optoma_ns DEPENDENCIES = ["optoma_rs232"] -InputSelect = optoma_ns.class_("InputSelect", select.Select) - Inputs = optoma_ns.enum("Inputs") INPUT_OPTIONS = { "Unknown": 0, @@ -16,6 +14,7 @@ INPUT_OPTIONS = { "HDMI 2": 8, } +InputSelect = optoma_ns.class_("InputSelect", select.Select) CONFIG_SCHEMA = select.select_schema(InputSelect).extend({ cv.GenerateID(CONF_OPTOMA_RS232_ID): cv.use_id(OptomaRS232Component), }) @@ -23,5 +22,6 @@ CONFIG_SCHEMA = select.select_schema(InputSelect).extend({ async def to_code(config): hub = await cg.get_variable(config[CONF_OPTOMA_RS232_ID]) - sens = await select.new_select(config, options=list(INPUT_OPTIONS)) - cg.add(hub.set_beamer_input_select(sens)) + cont = await select.new_select(config, options=list(INPUT_OPTIONS)) + await cg.register_parented(cont, config[CONF_OPTOMA_RS232_ID]) + cg.add(hub.set_beamer_input_select(cont)) diff --git a/sensor.py b/sensor.py index 85d41c6..5b51da8 100644 --- a/sensor.py +++ b/sensor.py @@ -56,7 +56,7 @@ async def to_code(config): if cfg := config.get(CONF_TEMPERATURE): sens = await sensor.new_sensor(cfg) cg.add(hub.set_beamer_temp_sensor(sens)) - if cfg := config.get(CONF_DURATION): + if cfg := config.get(CONF_LAMP_TIME): sens = await sensor.new_sensor(cfg) cg.add(hub.set_beamer_lamp_time_sensor(sens)) if cfg := config.get(CONF_FAN_SPEED): diff --git a/switch.py b/switch.py new file mode 100644 index 0000000..5cd1f4a --- /dev/null +++ b/switch.py @@ -0,0 +1,23 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import switch + +from . import CONF_OPTOMA_RS232_ID, OptomaRS232Component, optoma_ns +from ...const import DEVICE_CLASS_SWITCH + +DEPENDENCIES = ["optoma_rs232"] + +PowerSwitch = optoma_ns.class_("PowerSwitch", switch.Switch) +CONFIG_SCHEMA = switch.switch_schema( + PowerSwitch, + device_class=DEVICE_CLASS_SWITCH, +).extend({ + cv.GenerateID(CONF_OPTOMA_RS232_ID): cv.use_id(OptomaRS232Component), +}) + + +async def to_code(config): + hub = await cg.get_variable(config[CONF_OPTOMA_RS232_ID]) + cont = await switch.new_switch(config) + await cg.register_parented(cont, config[CONF_OPTOMA_RS232_ID]) + cg.add(hub.set_beamer_power_switch(cont))