214 lines
6.0 KiB
C++
214 lines
6.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";
|
|
|
|
enum QUERIES {
|
|
INFO = 0,
|
|
TEMP,
|
|
LAMP_TIME,
|
|
FAN_1,
|
|
};
|
|
|
|
constexpr const char *QUERY_DATA[] = {
|
|
"~00150 1\r", // Info
|
|
"~00150 18\r", // Temp
|
|
"~00108 1\r", // Lamp time
|
|
"~00351 1\r", // Fan 1
|
|
};
|
|
|
|
template<typename C, typename M> static void publish(C *c, const M &m) {
|
|
if (c)
|
|
c->publish_state(m);
|
|
}
|
|
|
|
void OptomaRS232Component::publish_input_(const std::string &state) const {
|
|
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() {
|
|
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) % std::size(QUERY_DATA);
|
|
write_array(reinterpret_cast<const uint8_t *>(QUERY_DATA[last_query_]), strlen(QUERY_DATA[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::beamer_power_switch_changed(const bool state) {
|
|
const char *cmd = state ? "~0000 1\r" : "~0000 2\r";
|
|
write_array(reinterpret_cast<const uint8_t *>(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")
|
|
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_power_(true);
|
|
return;
|
|
}
|
|
|
|
if ( // x == "OK0" || // status query returned power off
|
|
str == "INFO2" || // cooling down
|
|
str == "INFO0") { // going into standby
|
|
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());
|
|
}
|
|
|
|
void OptomaRS232Component::process_query_response_(const std::string &str) {
|
|
if (str.length() >= 3) {
|
|
switch (last_query_) {
|
|
case QUERIES::TEMP:
|
|
publish(beamer_temp_sensor_, strtol(str.c_str() + 2, 0, 10));
|
|
break;
|
|
case QUERIES::LAMP_TIME:
|
|
if (str.length() == 7)
|
|
publish(beamer_lamp_time_sensor_, strtol(str.c_str() + 2, 0, 10));
|
|
break;
|
|
case QUERIES::FAN_1:
|
|
publish(beamer_fan1_sensor_, strtol(str.c_str() + 2, 0, 10));
|
|
break;
|
|
case QUERIES::INFO:
|
|
if (str.length() >= 13) {
|
|
char buf[17]{};
|
|
strncpy(buf, str.c_str(), sizeof(buf));
|
|
const auto color_mode = strtol(buf + 14, 0, 10);
|
|
buf[14] = 0;
|
|
const auto firmware = strtol(buf + 10, 0, 10);
|
|
buf[10] = 0;
|
|
const auto input = strtol(buf + 8, 0, 10);
|
|
buf[8] = 0;
|
|
const auto lamp_time = strtol(buf + 3, 0, 10);
|
|
buf[3] = 0;
|
|
const auto power = strtol(buf + 2, 0, 10);
|
|
|
|
publish_power_(power);
|
|
// publish(beamer_firmware_, firmware);
|
|
if (power)
|
|
publish(beamer_lamp_time_sensor_, lamp_time);
|
|
publish(beamer_color_mode_sensor_, color_mode);
|
|
|
|
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;
|
|
}
|
|
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_)
|
|
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
|