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