Add tw7100 component with config generator
This commit is contained in:
parent
4bf101d938
commit
6eced6cc0d
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 uart
|
||||
from esphome.const import CONF_ID
|
||||
|
||||
|
||||
CODEOWNERS = ["@sqozz"]
|
||||
DEPENDENCIES = ["uart"]
|
||||
AUTO_LOAD = ["binary_sensor", "sensor", "text_sensor"]
|
||||
|
||||
tw7100_ns = cg.esphome_ns.namespace("tw7100")
|
||||
tw7100 = tw7100_ns.class_("tw7100Component", cg.PollingComponent, uart.UARTDevice)
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema({
|
||||
cv.GenerateID(): cv.declare_id(tw7100),
|
||||
}).extend(cv.polling_component_schema("60s")).extend(uart.UART_DEVICE_SCHEMA)
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema(
|
||||
"tw7100", baud_rate=9600, require_rx=True, require_tx=True
|
||||
)
|
||||
|
||||
|
||||
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)
|
38
binary_sensor.py
Normal file
38
binary_sensor.py
Normal file
|
@ -0,0 +1,38 @@
|
|||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import binary_sensor
|
||||
from esphome.const import (
|
||||
DEVICE_CLASS_POWER,
|
||||
DEVICE_CLASS_CONNECTIVITY,
|
||||
CONF_POWER,
|
||||
CONF_ID,
|
||||
)
|
||||
|
||||
from . import tw7100
|
||||
|
||||
DEPENDENCIES = ["tw7100"]
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(tw7100),
|
||||
cv.Optional("signal_detected"): binary_sensor.binary_sensor_schema(
|
||||
device_class=DEVICE_CLASS_CONNECTIVITY
|
||||
),
|
||||
cv.Optional(CONF_POWER): binary_sensor.binary_sensor_schema(
|
||||
device_class=DEVICE_CLASS_POWER
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
parent = await cg.get_variable(config[CONF_ID])
|
||||
|
||||
if "signal_detected" in config:
|
||||
sens = await binary_sensor.new_binary_sensor(config["signal_detected"])
|
||||
cg.add(parent.set_has_signal(sens))
|
||||
|
||||
if CONF_POWER in config:
|
||||
sens = await binary_sensor.new_binary_sensor(config[CONF_POWER])
|
||||
cg.add(parent.set_powered(sens))
|
33
example.yaml
Normal file
33
example.yaml
Normal file
|
@ -0,0 +1,33 @@
|
|||
external_components:
|
||||
- source:
|
||||
type: local
|
||||
path: external_components
|
||||
components:
|
||||
- tw7100
|
||||
|
||||
tw7100:
|
||||
uart_id: uart_bus
|
||||
|
||||
uart:
|
||||
id: uart_bus
|
||||
tx_pin: 15
|
||||
rx_pin: 13
|
||||
baud_rate: 9600
|
||||
|
||||
binary_sensor:
|
||||
- platform: tw7100
|
||||
signal_detected:
|
||||
name: "Signal"
|
||||
power:
|
||||
name: "Powered"
|
||||
|
||||
sensor:
|
||||
- platform: tw7100
|
||||
hours:
|
||||
name: "Projector lamp hours"
|
||||
power:
|
||||
name: "Projector power status"
|
||||
state:
|
||||
name: "Projector signal status"
|
||||
illuminance:
|
||||
name: "Projector luminance level"
|
58
sensor.py
Normal file
58
sensor.py
Normal file
|
@ -0,0 +1,58 @@
|
|||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor
|
||||
from esphome.const import (
|
||||
DEVICE_CLASS_POWER,
|
||||
DEVICE_CLASS_DURATION,
|
||||
DEVICE_CLASS_RUNNING,
|
||||
DEVICE_CLASS_ILLUMINANCE,
|
||||
CONF_ILLUMINANCE,
|
||||
CONF_HOURS,
|
||||
CONF_STATE,
|
||||
CONF_POWER,
|
||||
CONF_ID,
|
||||
)
|
||||
|
||||
from . import tw7100
|
||||
|
||||
DEPENDENCIES = ["tw7100"]
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(tw7100),
|
||||
cv.Optional(CONF_HOURS): sensor.sensor_schema(
|
||||
device_class=DEVICE_CLASS_DURATION # Lamp hours
|
||||
),
|
||||
cv.Optional(CONF_POWER): sensor.sensor_schema(
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_POWER # Power status
|
||||
),
|
||||
cv.Optional(CONF_STATE): sensor.sensor_schema(
|
||||
device_class=DEVICE_CLASS_POWER # Signal status
|
||||
),
|
||||
cv.Optional(CONF_ILLUMINANCE): sensor.sensor_schema(
|
||||
device_class=DEVICE_CLASS_ILLUMINANCE # Luminance level
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
parent = await cg.get_variable(config[CONF_ID])
|
||||
|
||||
if CONF_HOURS in config:
|
||||
sens = await sensor.new_sensor(config[CONF_HOURS])
|
||||
cg.add(parent.set_lamp_hours(sens))
|
||||
|
||||
if CONF_POWER in config:
|
||||
sens = await sensor.new_sensor(config[CONF_POWER])
|
||||
cg.add(parent.set_power_status(sens))
|
||||
|
||||
if CONF_STATE in config:
|
||||
sens = await sensor.new_sensor(config[CONF_STATE])
|
||||
cg.add(parent.set_signal_status(sens))
|
||||
|
||||
if CONF_ILLUMINANCE in config:
|
||||
sens = await sensor.new_sensor(config[CONF_ILLUMINANCE])
|
||||
cg.add(parent.set_luminance_level(sens))
|
228
tw7100.cpp
Normal file
228
tw7100.cpp
Normal file
|
@ -0,0 +1,228 @@
|
|||
#include "tw7100.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/components/uart/uart.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace tw7100 {
|
||||
|
||||
bool waiting_for_answer = false;
|
||||
std::vector<std::string> cmd_queue;
|
||||
|
||||
std::vector<std::string> pwr_off_query_cmds{ "PWR?", "LAMP?" };
|
||||
std::vector<std::string> pwr_on_query_cmds{
|
||||
// Projection screen adjustment settings
|
||||
/*
|
||||
"VKEYSTONE?",
|
||||
"HKEYSTONE?",
|
||||
"QC?",
|
||||
"CORRECTMET?",
|
||||
"ASPECT?",
|
||||
"LUMINANCE?",
|
||||
"OVSCAN?",
|
||||
*/
|
||||
//
|
||||
// Source/Input/Resolution settings
|
||||
"SOURCE?",
|
||||
// Image settings
|
||||
/*
|
||||
"BRIGHT?",
|
||||
"CONTRAST?",
|
||||
"DENSITY?",
|
||||
"TINT?",
|
||||
"CTEMP?",
|
||||
"FCOLOR?",
|
||||
"CMODE?",
|
||||
"NRS?",
|
||||
"MPEGNRS?",
|
||||
"OFFSETR?",
|
||||
"OFFSETG?",
|
||||
"OFFSETB?",
|
||||
"GAINR?",
|
||||
"GAING?",
|
||||
"GAINB?",
|
||||
"GAMMA?",
|
||||
"CSEL?",
|
||||
"4KENHANCE?",
|
||||
"IMGPRESET?",
|
||||
"SHRF?",
|
||||
"SHRS?",
|
||||
"DERANGE?",
|
||||
"MCFI?",
|
||||
"CLRSPACE?",
|
||||
"DYNRANGE?",
|
||||
"HDRPQ?",
|
||||
"HDRHLG?",
|
||||
"IMGPROC?",
|
||||
*/
|
||||
//
|
||||
// Sound settings
|
||||
/*
|
||||
"VOL?",
|
||||
"AUDIOOUT?",
|
||||
*/
|
||||
//
|
||||
"MUTE?",
|
||||
// Environment settings
|
||||
/*
|
||||
"HREVERSE?",
|
||||
"VREVERSE?",
|
||||
"MSEL?",
|
||||
"SPEED?",
|
||||
"ILLUM?",
|
||||
"STANDBYCONF?",
|
||||
"PRODUCT?",
|
||||
*/
|
||||
//
|
||||
// Home Screen settings
|
||||
"AUTOHOME?",
|
||||
// Network settings
|
||||
"WLPWR?",
|
||||
// Bluetooth
|
||||
"BTAUDIO?",
|
||||
// Information
|
||||
"SIGNAL?",
|
||||
"SOURCELIST?",
|
||||
// "SOURCELISTA?", // same as SOURCELIST
|
||||
"LOGTO?",
|
||||
"SNO?"
|
||||
};
|
||||
|
||||
void tw7100Component::setup() {
|
||||
static const char *const TAG = "setup()";
|
||||
ESP_LOGV(TAG, "SETUP");
|
||||
};
|
||||
|
||||
void tw7100Component::update() {
|
||||
static const char *const TAG = "update()";
|
||||
if (cmd_queue.size() == 0) {
|
||||
for (auto &cmd : pwr_off_query_cmds) {
|
||||
cmd_queue.push_back(cmd);
|
||||
}
|
||||
if ((powered_->state)) {;
|
||||
for (auto &cmd : pwr_on_query_cmds) {
|
||||
cmd_queue.push_back(cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void tw7100Component::loop() {
|
||||
static const char *const TAG = "loop()";
|
||||
unsigned long _startMillis; // used for timeout measurement
|
||||
|
||||
_startMillis = millis();
|
||||
do {
|
||||
std::string response = readStringUntil(':');
|
||||
|
||||
if (response == "") { // no response on bus, we can use our time to do something else
|
||||
if (cmd_queue.size() > 0 && !waiting_for_answer) {
|
||||
waiting_for_answer = true;
|
||||
std::string cmd = cmd_queue[0];
|
||||
cmd_queue.erase(cmd_queue.begin());
|
||||
ESP_LOGV(TAG, "sending out command: %s", cmd.c_str());
|
||||
write_str((cmd + "\r\n").c_str());
|
||||
flush();
|
||||
}
|
||||
} else { // response found, handle eventual errors
|
||||
ESP_LOGV(TAG, "buffer content: %s", response.c_str());
|
||||
std::pair<std::string, std::string> parsed_response = parse_response(response);
|
||||
if (parsed_response.first == "ERR" && parsed_response.second == "ERR") {
|
||||
if (powered_->state) {;
|
||||
cmd_queue.push_back("ERR?"); // TODO: produces a ERR? loop on bootup if projector is turned off
|
||||
} else {
|
||||
ESP_LOGE(TAG, "got ERR while projector is off, check if PWR+LAMP is too much?");
|
||||
}
|
||||
} else if (parsed_response.first == "ERR") {
|
||||
// TODO: Handle error
|
||||
} else {
|
||||
update_sensor(parsed_response);
|
||||
}
|
||||
waiting_for_answer = false;
|
||||
}
|
||||
//ESP_LOGV(TAG, "read processing finished");
|
||||
} while (millis() - _startMillis < _max_loop_time);
|
||||
//ESP_LOGV(TAG, "inner timed loop done");
|
||||
};
|
||||
|
||||
void tw7100Component::dump_config() {
|
||||
static const char *const TAG = "dump_config()";
|
||||
ESP_LOGCONFIG(TAG, "TW7100:");
|
||||
this->check_uart_settings(9600);
|
||||
};
|
||||
|
||||
void tw7100Component::update_sensor(std::pair<std::string, std::string> data) {
|
||||
static const char *const TAG = "update_sensor()";
|
||||
std::string cmd = data.first;
|
||||
std::string value_string = data.second;
|
||||
if (cmd == "PWR") {
|
||||
ESP_LOGV(TAG, "updating power sensors");
|
||||
int value = std::stoi(value_string);
|
||||
powered_->publish_state(value > 0);
|
||||
power_status_->publish_state(value);
|
||||
} else if (cmd == "LAMP") {
|
||||
ESP_LOGV(TAG, "updating lamp sensors");
|
||||
int value = std::stoi(value_string);
|
||||
lamp_hours_->publish_state(value);
|
||||
} else if (cmd == "SIGNAL") {
|
||||
ESP_LOGV(TAG, "updating signal sensors");
|
||||
int value = std::stoi(value_string);
|
||||
has_signal_->publish_state(value > 0);
|
||||
signal_status_->publish_state(value);
|
||||
} else if (cmd == "LUMINANCE") {
|
||||
ESP_LOGV(TAG, "updating luminance sensors");
|
||||
int value = std::stoi(value_string);
|
||||
luminance_level_->publish_state(value);
|
||||
} else if (cmd == "SOURCELIST") {
|
||||
for(char& c : value_string) {
|
||||
ESP_LOGV(TAG, "%c (%02x)", c, c);
|
||||
}
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Command %s unknown, skipping updating sensor with value", cmd.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<std::string, std::string> tw7100Component::parse_response(std::string data) {
|
||||
static const char *const TAG = "parse_response()";
|
||||
std::string key = "";
|
||||
std::string value = "";
|
||||
data.erase(std::remove(data.begin(), data.end(), '\r'), data.cend());
|
||||
data.erase(std::remove(data.begin(), data.end(), '\n'), data.cend());
|
||||
if (data != "") {
|
||||
key = data.substr(0, data.find("="));
|
||||
value = data.substr(data.find("=") + 1, data.length());
|
||||
}
|
||||
|
||||
ESP_LOGV(TAG, "parsed response into (key: %s, value: %s)", key.c_str(), value.c_str());
|
||||
return std::make_pair(key, value);
|
||||
}
|
||||
|
||||
int tw7100Component::timedRead() {
|
||||
unsigned long _startMillis; // used for timeout measurement
|
||||
int c;
|
||||
_startMillis = millis();
|
||||
do {
|
||||
if (this->available() > 0) {
|
||||
c = this->read();
|
||||
//ESP_LOGV("timedRead()", "timed read byte: %c (%x)", c, c);
|
||||
if (c >= 0) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
} while (millis() - _startMillis < _readStringUntil_timeout);
|
||||
return -1; // -1 indicates timeout
|
||||
}
|
||||
|
||||
std::string tw7100Component::readStringUntil(char terminator) {
|
||||
std::string line="";
|
||||
int c = timedRead();
|
||||
while (c >= 0 && (char)c != terminator) {
|
||||
line += (char)c;
|
||||
c = timedRead();
|
||||
}
|
||||
return line;
|
||||
};
|
||||
|
||||
} // namespace tw7100
|
||||
} // namespace esphome
|
49
tw7100.h
Normal file
49
tw7100.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
#include "esphome/components/text_sensor/text_sensor.h"
|
||||
#include "esphome/components/uart/uart.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace tw7100 {
|
||||
|
||||
class tw7100Component : public PollingComponent, public uart::UARTDevice {
|
||||
private:
|
||||
unsigned long _max_loop_time = 30;
|
||||
unsigned long _readStringUntil_timeout = _max_loop_time/5; // number of milliseconds to wait for the next char before aborting timed read
|
||||
|
||||
public:
|
||||
tw7100Component() = default;
|
||||
|
||||
std::string readStringUntil(char terminator);
|
||||
void update_sensor(std::pair<std::string, std::string> data);
|
||||
std::pair<std::string, std::string> parse_response(std::string data);
|
||||
int timedRead(void);
|
||||
void setup() override;
|
||||
void update() override;
|
||||
void loop() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::BUS; }
|
||||
void set_powered(binary_sensor::BinarySensor *powered) { this->powered_ = powered; }
|
||||
void set_has_signal(binary_sensor::BinarySensor *has_signal) { this->has_signal_ = has_signal; }
|
||||
void set_lamp_hours(sensor::Sensor *lamp_hours) { this->lamp_hours_ = lamp_hours; }
|
||||
void set_power_status(sensor::Sensor *power_status) { this->power_status_ = power_status; }
|
||||
void set_signal_status(sensor::Sensor *signal_status) { this->signal_status_ = signal_status; }
|
||||
void set_luminance_level(sensor::Sensor *luminance_level) { this->luminance_level_ = luminance_level; }
|
||||
void set_serial(text_sensor::TextSensor *serial) { this->serial_ = serial; }
|
||||
|
||||
protected:
|
||||
binary_sensor::BinarySensor *powered_{nullptr};
|
||||
binary_sensor::BinarySensor *has_signal_{nullptr};
|
||||
sensor::Sensor *lamp_hours_{nullptr};
|
||||
sensor::Sensor *power_status_{nullptr};
|
||||
sensor::Sensor *signal_status_{nullptr};
|
||||
sensor::Sensor *luminance_level_{nullptr};
|
||||
text_sensor::TextSensor *serial_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace tw7100
|
||||
} // namespace esphome
|
Loading…
Reference in a new issue