init
This commit is contained in:
		
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | cmake*/ | ||||||
							
								
								
									
										10
									
								
								CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | cmake_minimum_required(VERSION 3.20) | ||||||
|  |  | ||||||
|  | project(optoma_rs232 LANGUAGES CXX) | ||||||
|  | set(CMAKE_CXX_STANDARD 17 CACHE STRING "") | ||||||
|  |  | ||||||
|  | include_directories(../../..) | ||||||
|  |  | ||||||
|  | add_library(optoma_rs232 SHARED | ||||||
|  | 		optoma_rs232.cpp | ||||||
|  | ) | ||||||
							
								
								
									
										26
									
								
								__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.components import i2c, uart | ||||||
|  | from esphome.const import CONF_ID | ||||||
|  |  | ||||||
|  | DEPENDENCIES = ["uart"] | ||||||
|  | MULTI_CONF = True | ||||||
|  |  | ||||||
|  | CONF_OPTOMA_RS232_ID = "optoma_rs232_id" | ||||||
|  |  | ||||||
|  | optoma_ns = cg.esphome_ns.namespace("optoma_rs232") | ||||||
|  | OptomaRS232Component = optoma_ns.class_("OptomaRS232Component", cg.PollingComponent, uart.UARTDevice) | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = ( | ||||||
|  |     cv.Schema({ | ||||||
|  |         cv.GenerateID(): cv.declare_id(OptomaRS232Component), | ||||||
|  |     }) | ||||||
|  |     .extend(cv.polling_component_schema("10s")) | ||||||
|  |     .extend(uart.UART_DEVICE_SCHEMA) | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | async def to_code(config): | ||||||
|  |     var = cg.new_Pvariable(config[CONF_ID]) | ||||||
|  |     await cg.register_component(var, config) | ||||||
|  |     await uart.register_uart_device(var, config) | ||||||
							
								
								
									
										21
									
								
								binary_sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								binary_sensor.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.components import binary_sensor | ||||||
|  | from esphome.const import DEVICE_CLASS_POWER, ICON_POWER | ||||||
|  |  | ||||||
|  | from . import CONF_OPTOMA_RS232_ID, OptomaRS232Component | ||||||
|  |  | ||||||
|  | DEPENDENCIES = ["optoma_rs232"] | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = binary_sensor.binary_sensor_schema( | ||||||
|  |     icon=ICON_POWER, | ||||||
|  |     device_class=DEVICE_CLASS_POWER, | ||||||
|  | ).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]) | ||||||
|  |     sens = await binary_sensor.new_binary_sensor(config) | ||||||
|  |     cg.add(hub.set_beamer_power_binary_sensor(sens)) | ||||||
							
								
								
									
										143
									
								
								optoma_rs232.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								optoma_rs232.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,143 @@ | |||||||
|  | #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::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 = -1;  // atol(buf + 8); BUGGY | ||||||
|  |         switch (input) { | ||||||
|  |           case Inputs::HDMI_1: | ||||||
|  |             publish(beamer_input_text_sensor_, "HDMI 1"); | ||||||
|  |             break; | ||||||
|  |           case Inputs::HDMI_2: | ||||||
|  |             publish(beamer_input_text_sensor_, "HDMI 2"); | ||||||
|  |             break; | ||||||
|  |           case Inputs::VGA: | ||||||
|  |             publish(beamer_input_text_sensor_, "VGA"); | ||||||
|  |             break; | ||||||
|  |           default: | ||||||
|  |           case 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:; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace optoma_rs232 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										75
									
								
								optoma_rs232.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								optoma_rs232.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,75 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/core/component.h" | ||||||
|  | #include "esphome/components/uart/uart.h" | ||||||
|  | #ifdef USE_SELECT | ||||||
|  | #include "esphome/components/sensor/sensor.h" | ||||||
|  | #endif | ||||||
|  | #ifdef USE_BINARY_SENSOR | ||||||
|  | #include "esphome/components/binary_sensor/binary_sensor.h" | ||||||
|  | #endif | ||||||
|  | #ifdef USE_TEXT_SENSOR | ||||||
|  | #include "esphome/components/text_sensor/text_sensor.h" | ||||||
|  | #endif | ||||||
|  | #ifdef USE_SELECT | ||||||
|  | #include "esphome/components/select/select.h" | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace optoma_rs232 { | ||||||
|  |  | ||||||
|  | // clang-format off | ||||||
|  | class DummySensor { public: template<typename M> static void publish_state(M) {} }; | ||||||
|  | class DummySelect {}; | ||||||
|  | // clang-format on | ||||||
|  |  | ||||||
|  | enum Inputs { | ||||||
|  |   UNKNOWN = 0, | ||||||
|  |   VGA = 2, | ||||||
|  |   HDMI_1 = 7, | ||||||
|  |   HDMI_2 = 8, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class OptomaRS232Component : public uart::UARTDevice, public PollingComponent { | ||||||
|  |  public: | ||||||
|  |   void dump_config() override; | ||||||
|  |   void loop() override; | ||||||
|  |   float get_setup_priority() const override { return setup_priority::DATA; } | ||||||
|  |   void update() override; | ||||||
|  |  | ||||||
|  |   // clang-format off | ||||||
|  | #ifndef USE_SENSOR | ||||||
|  | #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: | ||||||
|  | #endif | ||||||
|  | #ifndef USE_TEXT_SENSOR | ||||||
|  | #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: | ||||||
|  | #endif | ||||||
|  |   // clang-format on | ||||||
|  |  | ||||||
|  |   SUB_SENSOR(beamer_temp) | ||||||
|  |   SUB_SENSOR(beamer_lamp_time) | ||||||
|  |   SUB_SENSOR(beamer_fan1) | ||||||
|  |   SUB_SENSOR(beamer_color_mode) | ||||||
|  |   SUB_BINARY_SENSOR(beamer_power) | ||||||
|  |   SUB_TEXT_SENSOR(beamer_input) | ||||||
|  |   SUB_SELECT(beamer_input) | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   void process_line_(const std::string &str); | ||||||
|  |   void process_query_response_(const std::string &str); | ||||||
|  |   bool waiting_for_command_response_ = false; | ||||||
|  |   int last_query_ = -1; | ||||||
|  |  | ||||||
|  |  private: | ||||||
|  |   char buffer_[128]{}; | ||||||
|  |   size_t cursor_ = 0; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace optoma_rs232 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										26
									
								
								select.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								select.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.components import select | ||||||
|  |  | ||||||
|  | from . import CONF_OPTOMA_RS232_ID, OptomaRS232Component, optoma_ns | ||||||
|  |  | ||||||
|  | DEPENDENCIES = ["optoma_rs232"] | ||||||
|  |  | ||||||
|  | Inputs = optoma_ns.enum("Inputs") | ||||||
|  | INPUT_OPTIONS = { | ||||||
|  |     "Unknown": 0, | ||||||
|  |     "VGA": 2, | ||||||
|  |     "HDMI 1": 7, | ||||||
|  |     "HDMI 2": 8, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = select.select_schema( | ||||||
|  | ).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]) | ||||||
|  |     sens = await select.new_select(config, options=list(INPUT_OPTIONS)) | ||||||
|  |     cg.add(hub.set_beamer_input_select(sens)) | ||||||
							
								
								
									
										64
									
								
								sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								sensor.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,64 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.components import sensor, uart | ||||||
|  | from esphome.const import ( | ||||||
|  |     CONF_DURATION, | ||||||
|  |     CONF_TEMPERATURE, | ||||||
|  |     DEVICE_CLASS_DURATION, | ||||||
|  |     DEVICE_CLASS_SPEED, | ||||||
|  |     DEVICE_CLASS_TEMPERATURE, | ||||||
|  |     STATE_CLASS_MEASUREMENT, | ||||||
|  |     STATE_CLASS_TOTAL_INCREASING, | ||||||
|  |     UNIT_CELSIUS, | ||||||
|  |     UNIT_HOUR, | ||||||
|  |     UNIT_REVOLUTIONS_PER_MINUTE, | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | from . import CONF_OPTOMA_RS232_ID, optoma_ns, OptomaRS232Component | ||||||
|  |  | ||||||
|  | DEPENDENCIES = ["optoma_rs232"] | ||||||
|  |  | ||||||
|  | CONF_FAN_SPEED = "fan_speed" | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = ( | ||||||
|  |     cv.Schema({ | ||||||
|  |         cv.GenerateID(CONF_OPTOMA_RS232_ID): cv.use_id(OptomaRS232Component), | ||||||
|  |         cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( | ||||||
|  |             unit_of_measurement=UNIT_CELSIUS, | ||||||
|  |             accuracy_decimals=1, | ||||||
|  |             device_class=DEVICE_CLASS_TEMPERATURE, | ||||||
|  |             state_class=STATE_CLASS_MEASUREMENT, | ||||||
|  |         ), | ||||||
|  |         cv.Optional(CONF_DURATION): sensor.sensor_schema( | ||||||
|  |             unit_of_measurement=UNIT_HOUR, | ||||||
|  |             accuracy_decimals=0, | ||||||
|  |             device_class=DEVICE_CLASS_DURATION, | ||||||
|  |             state_class=STATE_CLASS_TOTAL_INCREASING, | ||||||
|  |         ), | ||||||
|  |         cv.Optional(CONF_FAN_SPEED): sensor.sensor_schema( | ||||||
|  |             unit_of_measurement=UNIT_REVOLUTIONS_PER_MINUTE, | ||||||
|  |             accuracy_decimals=0, | ||||||
|  |             device_class=DEVICE_CLASS_SPEED, | ||||||
|  |             state_class=STATE_CLASS_MEASUREMENT, | ||||||
|  |         ), | ||||||
|  |     }) | ||||||
|  |     .extend(cv.polling_component_schema("10s")) | ||||||
|  |     .extend(uart.UART_DEVICE_SCHEMA) | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | async def to_code(config): | ||||||
|  |     hub = await cg.get_variable(config[CONF_OPTOMA_RS232_ID]) | ||||||
|  |  | ||||||
|  |     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): | ||||||
|  |         sens = await sensor.new_sensor(cfg) | ||||||
|  |         cg.add(hub.set_beamer_lamp_time_sensor(sens)) | ||||||
|  |     if cfg := config.get(CONF_FAN_SPEED): | ||||||
|  |         sens = await sensor.new_sensor(cfg) | ||||||
|  |         cg.add(hub.set_beamer_fan1_sensor(sens)) | ||||||
|  |     # if cfg := config.get(): | ||||||
|  |     #     sens = await sensor.new_sensor(cfg) | ||||||
|  |     #     cg.add(hub.set_beamer_color_mode_sensor(sens)) | ||||||
							
								
								
									
										18
									
								
								text_sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								text_sensor.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.components import text_sensor | ||||||
|  |  | ||||||
|  | from . import CONF_OPTOMA_RS232_ID, OptomaRS232Component | ||||||
|  |  | ||||||
|  | DEPENDENCIES = ["optoma_rs232"] | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = text_sensor.text_sensor_schema( | ||||||
|  | ).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]) | ||||||
|  |     sens = await text_sensor.new_text_sensor(config) | ||||||
|  |     cg.add(hub.set_beamer_input_text_sensor(sens)) | ||||||
		Reference in New Issue
	
	Block a user
	 T
					T