init
This commit is contained in:
commit
419143d70a
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))
|
Loading…
x
Reference in New Issue
Block a user