esphome_optoma_rs232/optoma_rs232.cpp
2025-03-14 17:57:43 +01:00

180 lines
5.0 KiB
C++

#include "optoma_rs232.h"
#include "esphome/core/log.h"
#include "esphome/core/helpers.h"
namespace esphome {
namespace optoma_rs232 {
[[maybe_unused]] static const char *const TAG = "optoma_rs232";
static const char *QUERIES[] = {
"~00150 18\r", // Temp
"~00108 1\r", // Lamp time
"~00351 1\r", // Fan 1
"~00150 1\r", // Info
};
template<typename C, typename M> static void publish(C *c, M m) {
if (c)
c->publish_state(m);
}
void OptomaRS232Component::dump_config() {
ESP_LOGCONFIG(TAG, "Optoma RS232:");
check_uart_settings(9600);
}
void OptomaRS232Component::loop() {
if (!available())
return;
// pos = 0 and crlf: none
// pos > 0 and crlf: send
// pos < end and not crlf: add
// pos == end and not crlf: discard
while (available()) {
uint8_t c;
if (!read_byte(&c))
continue;
if (!c)
continue;
if (c == '\r' || c == '\n') {
if (cursor_ > 0) {
buffer_[cursor_] = 0;
process_line_(buffer_);
cursor_ = 0;
}
} else if (cursor_ < sizeof(buffer_) - 1) {
buffer_[cursor_++] = toupper(c);
}
}
}
void OptomaRS232Component::update() {
last_query_ = (last_query_ + 1) % sizeof(QUERIES) / sizeof(QUERIES[0]);
write_array(reinterpret_cast<const uint8_t *>(QUERIES[last_query_]), strlen(QUERIES[last_query_]));
}
void OptomaRS232Component::beamer_input_select_changed(const std::string &state, size_t) {
const char *data;
Inputs inp{UNKNOWN};
if (state == "Unknown")
return;
if (state == "HDMI 1") {
inp = HDMI_1;
data = "~0012 1\r";
} else if (state == "HDMI 2") {
inp = HDMI_2;
data = "~0012 15\r";
} else if (state == "VGA") {
inp = VGA;
data = "~0012 5\r"; // VGA 1
} else {
return;
}
if (inp == current_input_)
return;
current_input_ = inp;
write_array(reinterpret_cast<const uint8_t *>(data), strlen(data));
}
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");
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
// hence the toUpperCase call earlier
// 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);
return;
}
if ( // x == "OK0" || // status query returned power off
str == "INFO2" || // cooling down
str == "INFO0") { // going into standby
publish(beamer_power_binary_sensor_, false);
return;
}
if (str_startswith(str, "OK")) {
process_query_response_(str);
return;
}
ESP_LOGD("projector", "unhandled message: %s", str.c_str());
}
void OptomaRS232Component::process_query_response_(const std::string &str) {
if (str.length() >= 3) {
switch (last_query_) {
case 0:
publish(beamer_temp_sensor_, strtol(str.c_str() + 2, 0, 10));
break;
case 1:
publish(beamer_lamp_time_sensor_, strtol(str.c_str() + 2, 0, 10));
break;
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;
// 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(beamer_input_text_sensor_, "HDMI 1");
break;
case Inputs::HDMI_2:
current_input_ = Inputs::HDMI_2;
publish(beamer_input_text_sensor_, "HDMI 2");
break;
case Inputs::VGA:
current_input_ = Inputs::VGA;
publish(beamer_input_text_sensor_, "VGA");
break;
default:
case Inputs::UNKNOWN:
current_input_ = Inputs::UNKNOWN;
publish(beamer_input_text_sensor_, "Unknown");
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:;
}
}
}
#ifdef USE_SELECT
void InputSelect::control(const std::string &value) {
this->publish_state(value);
auto index = this->index_of(value);
if (index.has_value())
this->parent_->beamer_input_select_changed(value, *index);
}
#endif
} // namespace optoma_rs232
} // namespace esphome