mirror of
https://github.com/esphome/esphome.git
synced 2026-03-19 01:36:45 +01:00
Nextion upload and sensors (#1464)
Co-authored-by: Senex Crenshaw <senexcrenshaw@gmail.com>
This commit is contained in:
parent
0992609bf4
commit
0651716b96
@ -73,6 +73,11 @@ esphome/components/midea_ac/* @dudanov
|
||||
esphome/components/midea_dongle/* @dudanov
|
||||
esphome/components/mitsubishi/* @RubyBailey
|
||||
esphome/components/network/* @esphome/core
|
||||
esphome/components/nextion/* @senexcrenshaw
|
||||
esphome/components/nextion/binary_sensor/* @senexcrenshaw
|
||||
esphome/components/nextion/sensor/* @senexcrenshaw
|
||||
esphome/components/nextion/switch/* @senexcrenshaw
|
||||
esphome/components/nextion/text_sensor/* @senexcrenshaw
|
||||
esphome/components/nfc/* @jesserockz
|
||||
esphome/components/number/* @esphome/core
|
||||
esphome/components/ota/* @esphome/core
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import uart
|
||||
|
||||
nextion_ns = cg.esphome_ns.namespace("nextion")
|
||||
Nextion = nextion_ns.class_("Nextion", cg.PollingComponent, uart.UARTDevice)
|
||||
nextion_ref = Nextion.operator("ref")
|
||||
|
||||
CONF_NEXTION_ID = "nextion_id"
|
||||
|
||||
30
esphome/components/nextion/automation.h
Normal file
30
esphome/components/nextion/automation.h
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
#include "esphome/core/automation.h"
|
||||
#include "nextion.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace nextion {
|
||||
|
||||
class SetupTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit SetupTrigger(Nextion *nextion) {
|
||||
nextion->add_setup_state_callback([this]() { this->trigger(); });
|
||||
}
|
||||
};
|
||||
|
||||
class SleepTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit SleepTrigger(Nextion *nextion) {
|
||||
nextion->add_sleep_state_callback([this]() { this->trigger(); });
|
||||
}
|
||||
};
|
||||
|
||||
class WakeTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit WakeTrigger(Nextion *nextion) {
|
||||
nextion->add_wake_state_callback([this]() { this->trigger(); });
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace nextion
|
||||
} // namespace esphome
|
||||
126
esphome/components/nextion/base_component.py
Normal file
126
esphome/components/nextion/base_component.py
Normal file
@ -0,0 +1,126 @@
|
||||
from string import ascii_letters, digits
|
||||
import esphome.config_validation as cv
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import color
|
||||
|
||||
from . import CONF_NEXTION_ID
|
||||
from . import Nextion
|
||||
|
||||
CONF_VARIABLE_NAME = "variable_name"
|
||||
CONF_COMPONENT_NAME = "component_name"
|
||||
CONF_WAVE_CHANNEL_ID = "wave_channel_id"
|
||||
CONF_WAVE_MAX_VALUE = "wave_max_value"
|
||||
CONF_PRECISION = "precision"
|
||||
CONF_WAVEFORM_SEND_LAST_VALUE = "waveform_send_last_value"
|
||||
CONF_TFT_URL = "tft_url"
|
||||
CONF_ON_SLEEP = "on_sleep"
|
||||
CONF_ON_WAKE = "on_wake"
|
||||
CONF_ON_SETUP = "on_setup"
|
||||
CONF_TOUCH_SLEEP_TIMEOUT = "touch_sleep_timeout"
|
||||
CONF_WAKE_UP_PAGE = "wake_up_page"
|
||||
CONF_AUTO_WAKE_ON_TOUCH = "auto_wake_on_touch"
|
||||
CONF_WAVE_MAX_LENGTH = "wave_max_length"
|
||||
CONF_BACKGROUND_COLOR = "background_color"
|
||||
CONF_BACKGROUND_PRESSED_COLOR = "background_pressed_color"
|
||||
CONF_FOREGROUND_COLOR = "foreground_color"
|
||||
CONF_FOREGROUND_PRESSED_COLOR = "foreground_pressed_color"
|
||||
CONF_FONT_ID = "font_id"
|
||||
CONF_VISIBLE = "visible"
|
||||
|
||||
|
||||
def NextionName(value):
|
||||
valid_chars = ascii_letters + digits + "."
|
||||
if not isinstance(value, str) or len(value) > 29:
|
||||
raise cv.Invalid("Must be a string less than 29 characters")
|
||||
|
||||
for char in value:
|
||||
if char not in valid_chars:
|
||||
raise cv.Invalid(
|
||||
"Must only consist of upper/lowercase characters, numbers and the period '.'. The character '{}' cannot be used.".format(
|
||||
char
|
||||
)
|
||||
)
|
||||
|
||||
return value
|
||||
|
||||
|
||||
CONFIG_BASE_COMPONENT_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_NEXTION_ID): cv.use_id(Nextion),
|
||||
cv.Optional(CONF_BACKGROUND_COLOR): cv.use_id(color),
|
||||
cv.Optional(CONF_FOREGROUND_COLOR): cv.use_id(color),
|
||||
cv.Optional(CONF_VISIBLE, default=True): cv.boolean,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
CONFIG_TEXT_COMPONENT_SCHEMA = CONFIG_BASE_COMPONENT_SCHEMA.extend(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_COMPONENT_NAME): NextionName,
|
||||
cv.Optional(CONF_FONT_ID): cv.int_range(min=0, max=255),
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
CONFIG_BINARY_SENSOR_SCHEMA = CONFIG_BASE_COMPONENT_SCHEMA.extend(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.Optional(CONF_COMPONENT_NAME): NextionName,
|
||||
cv.Optional(CONF_VARIABLE_NAME): NextionName,
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
CONFIG_SENSOR_COMPONENT_SCHEMA = CONFIG_BINARY_SENSOR_SCHEMA.extend(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.Optional(CONF_FONT_ID): cv.int_range(min=0, max=255),
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
CONFIG_SWITCH_COMPONENT_SCHEMA = CONFIG_SENSOR_COMPONENT_SCHEMA.extend(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.Optional(CONF_FOREGROUND_PRESSED_COLOR): cv.use_id(color),
|
||||
cv.Optional(CONF_BACKGROUND_PRESSED_COLOR): cv.use_id(color),
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
async def setup_component_core_(var, config, arg):
|
||||
|
||||
if CONF_VARIABLE_NAME in config:
|
||||
cg.add(var.set_variable_name(config[CONF_VARIABLE_NAME]))
|
||||
elif CONF_COMPONENT_NAME in config:
|
||||
cg.add(
|
||||
var.set_variable_name(
|
||||
config[CONF_COMPONENT_NAME],
|
||||
config[CONF_COMPONENT_NAME] + arg,
|
||||
)
|
||||
)
|
||||
|
||||
if CONF_BACKGROUND_COLOR in config:
|
||||
color_component = await cg.get_variable(config[CONF_BACKGROUND_COLOR])
|
||||
cg.add(var.set_background_color(color_component))
|
||||
|
||||
if CONF_BACKGROUND_PRESSED_COLOR in config:
|
||||
color_component = await cg.get_variable(config[CONF_BACKGROUND_PRESSED_COLOR])
|
||||
cg.add(var.set_background_pressed_color(color_component))
|
||||
|
||||
if CONF_FOREGROUND_COLOR in config:
|
||||
color_component = await cg.get_variable(config[CONF_FOREGROUND_COLOR])
|
||||
cg.add(var.set_foreground_color(color_component))
|
||||
|
||||
if CONF_FOREGROUND_PRESSED_COLOR in config:
|
||||
color_component = await cg.get_variable(config[CONF_FOREGROUND_PRESSED_COLOR])
|
||||
cg.add(var.set_foreground_pressed_color(color_component))
|
||||
|
||||
if CONF_FONT_ID in config:
|
||||
cg.add(var.set_font_id(config[CONF_FONT_ID]))
|
||||
|
||||
if CONF_VISIBLE in config:
|
||||
cg.add(var.set_visible(config[CONF_VISIBLE]))
|
||||
@ -1,34 +0,0 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import binary_sensor
|
||||
from esphome.const import CONF_COMPONENT_ID, CONF_PAGE_ID, CONF_ID
|
||||
from . import nextion_ns
|
||||
from .display import Nextion
|
||||
|
||||
DEPENDENCIES = ["display"]
|
||||
|
||||
CONF_NEXTION_ID = "nextion_id"
|
||||
|
||||
NextionTouchComponent = nextion_ns.class_(
|
||||
"NextionTouchComponent", binary_sensor.BinarySensor
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(NextionTouchComponent),
|
||||
cv.GenerateID(CONF_NEXTION_ID): cv.use_id(Nextion),
|
||||
cv.Required(CONF_PAGE_ID): cv.uint8_t,
|
||||
cv.Required(CONF_COMPONENT_ID): cv.uint8_t,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await binary_sensor.register_binary_sensor(var, config)
|
||||
|
||||
hub = await cg.get_variable(config[CONF_NEXTION_ID])
|
||||
cg.add(hub.register_touch_component(var))
|
||||
|
||||
cg.add(var.set_component_id(config[CONF_COMPONENT_ID]))
|
||||
cg.add(var.set_page_id(config[CONF_PAGE_ID]))
|
||||
54
esphome/components/nextion/binary_sensor/__init__.py
Normal file
54
esphome/components/nextion/binary_sensor/__init__.py
Normal file
@ -0,0 +1,54 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import binary_sensor
|
||||
|
||||
from esphome.const import CONF_COMPONENT_ID, CONF_PAGE_ID, CONF_ID
|
||||
from .. import nextion_ns, CONF_NEXTION_ID
|
||||
|
||||
|
||||
from ..base_component import (
|
||||
setup_component_core_,
|
||||
CONFIG_BINARY_SENSOR_SCHEMA,
|
||||
CONF_VARIABLE_NAME,
|
||||
CONF_COMPONENT_NAME,
|
||||
)
|
||||
|
||||
CODEOWNERS = ["@senexcrenshaw"]
|
||||
|
||||
NextionBinarySensor = nextion_ns.class_(
|
||||
"NextionBinarySensor", binary_sensor.BinarySensor, cg.PollingComponent
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
binary_sensor.BINARY_SENSOR_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(NextionBinarySensor),
|
||||
cv.Optional(CONF_PAGE_ID): cv.uint8_t,
|
||||
cv.Optional(CONF_COMPONENT_ID): cv.uint8_t,
|
||||
}
|
||||
)
|
||||
.extend(CONFIG_BINARY_SENSOR_SCHEMA)
|
||||
.extend(cv.polling_component_schema("never")),
|
||||
cv.has_at_least_one_key(
|
||||
CONF_PAGE_ID,
|
||||
CONF_COMPONENT_ID,
|
||||
CONF_COMPONENT_NAME,
|
||||
CONF_VARIABLE_NAME,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
hub = await cg.get_variable(config[CONF_NEXTION_ID])
|
||||
var = cg.new_Pvariable(config[CONF_ID], hub)
|
||||
await binary_sensor.register_binary_sensor(var, config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
if config.keys() >= {CONF_PAGE_ID, CONF_COMPONENT_ID}:
|
||||
cg.add(hub.register_touch_component(var))
|
||||
cg.add(var.set_component_id(config[CONF_COMPONENT_ID]))
|
||||
cg.add(var.set_page_id(config[CONF_PAGE_ID]))
|
||||
|
||||
if CONF_COMPONENT_NAME in config or CONF_VARIABLE_NAME in config:
|
||||
await setup_component_core_(var, config, ".val")
|
||||
cg.add(hub.register_binarysensor_component(var))
|
||||
@ -0,0 +1,69 @@
|
||||
#include "nextion_binarysensor.h"
|
||||
#include "esphome/core/util.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace nextion {
|
||||
|
||||
static const char *const TAG = "nextion_binarysensor";
|
||||
|
||||
void NextionBinarySensor::process_bool(const std::string &variable_name, bool state) {
|
||||
if (!this->nextion_->is_setup())
|
||||
return;
|
||||
|
||||
if (this->variable_name_.empty()) // This is a touch component
|
||||
return;
|
||||
|
||||
if (this->variable_name_ == variable_name) {
|
||||
this->publish_state(state);
|
||||
ESP_LOGD(TAG, "Processed binarysensor \"%s\" state %s", variable_name.c_str(), state ? "ON" : "OFF");
|
||||
}
|
||||
}
|
||||
|
||||
void NextionBinarySensor::process_touch(uint8_t page_id, uint8_t component_id, bool state) {
|
||||
if (this->page_id_ == page_id && this->component_id_ == component_id) {
|
||||
this->publish_state(state);
|
||||
}
|
||||
}
|
||||
|
||||
void NextionBinarySensor::update() {
|
||||
if (!this->nextion_->is_setup())
|
||||
return;
|
||||
|
||||
if (this->variable_name_.empty()) // This is a touch component
|
||||
return;
|
||||
|
||||
this->nextion_->add_to_get_queue(this);
|
||||
}
|
||||
|
||||
void NextionBinarySensor::set_state(bool state, bool publish, bool send_to_nextion) {
|
||||
if (!this->nextion_->is_setup())
|
||||
return;
|
||||
|
||||
if (this->component_id_ == 0) // This is a legacy touch component
|
||||
return;
|
||||
|
||||
if (send_to_nextion) {
|
||||
if (this->nextion_->is_sleeping() || !this->visible_) {
|
||||
this->needs_to_send_update_ = true;
|
||||
} else {
|
||||
this->needs_to_send_update_ = false;
|
||||
this->nextion_->add_no_result_to_queue_with_set(this, (int) state);
|
||||
}
|
||||
}
|
||||
|
||||
if (publish) {
|
||||
this->publish_state(state);
|
||||
} else {
|
||||
this->state = state;
|
||||
this->has_state_ = true;
|
||||
}
|
||||
|
||||
this->update_component_settings();
|
||||
|
||||
ESP_LOGN(TAG, "Wrote state for sensor \"%s\" state %s", this->variable_name_.c_str(),
|
||||
ONOFF(this->variable_name_.c_str()));
|
||||
}
|
||||
|
||||
} // namespace nextion
|
||||
} // namespace esphome
|
||||
@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
#include "../nextion_component.h"
|
||||
#include "../nextion_base.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace nextion {
|
||||
class NextionBinarySensor;
|
||||
|
||||
class NextionBinarySensor : public NextionComponent,
|
||||
public binary_sensor::BinarySensorInitiallyOff,
|
||||
public PollingComponent {
|
||||
public:
|
||||
NextionBinarySensor(NextionBase *nextion) { this->nextion_ = nextion; }
|
||||
|
||||
void update_component() override { this->update(); }
|
||||
void update() override;
|
||||
void send_state_to_nextion() override { this->set_state(this->state, false); };
|
||||
void process_bool(const std::string &variable_name, bool state) override;
|
||||
void process_touch(uint8_t page_id, uint8_t component_id, bool state) override;
|
||||
|
||||
// Set the components page id for Nextion Touch Component
|
||||
void set_page_id(uint8_t page_id) { page_id_ = page_id; }
|
||||
// Set the components component id for Nextion Touch Component
|
||||
void set_component_id(uint8_t component_id) { component_id_ = component_id; }
|
||||
|
||||
void set_state(bool state) override { this->set_state(state, true, true); }
|
||||
void set_state(bool state, bool publish) override { this->set_state(state, publish, true); }
|
||||
void set_state(bool state, bool publish, bool send_to_nextion) override;
|
||||
|
||||
NextionQueueType get_queue_type() override { return NextionQueueType::BINARY_SENSOR; }
|
||||
void set_state_from_string(const std::string &state_value, bool publish, bool send_to_nextion) override {}
|
||||
void set_state_from_int(int state_value, bool publish, bool send_to_nextion) override {
|
||||
this->set_state(state_value != 0, publish, send_to_nextion);
|
||||
}
|
||||
|
||||
protected:
|
||||
uint8_t page_id_;
|
||||
};
|
||||
} // namespace nextion
|
||||
} // namespace esphome
|
||||
@ -1,20 +1,58 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome import automation
|
||||
from esphome.components import display, uart
|
||||
from esphome.const import CONF_ID, CONF_LAMBDA, CONF_BRIGHTNESS
|
||||
from . import nextion_ns
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_LAMBDA,
|
||||
CONF_BRIGHTNESS,
|
||||
CONF_TRIGGER_ID,
|
||||
)
|
||||
|
||||
from . import Nextion, nextion_ns, nextion_ref
|
||||
from .base_component import (
|
||||
CONF_ON_SLEEP,
|
||||
CONF_ON_WAKE,
|
||||
CONF_ON_SETUP,
|
||||
CONF_TFT_URL,
|
||||
CONF_TOUCH_SLEEP_TIMEOUT,
|
||||
CONF_WAKE_UP_PAGE,
|
||||
CONF_AUTO_WAKE_ON_TOUCH,
|
||||
)
|
||||
|
||||
CODEOWNERS = ["@senexcrenshaw"]
|
||||
|
||||
DEPENDENCIES = ["uart"]
|
||||
AUTO_LOAD = ["binary_sensor"]
|
||||
AUTO_LOAD = ["binary_sensor", "switch", "sensor", "text_sensor"]
|
||||
|
||||
Nextion = nextion_ns.class_("Nextion", cg.PollingComponent, uart.UARTDevice)
|
||||
NextionRef = Nextion.operator("ref")
|
||||
SetupTrigger = nextion_ns.class_("SetupTrigger", automation.Trigger.template())
|
||||
SleepTrigger = nextion_ns.class_("SleepTrigger", automation.Trigger.template())
|
||||
WakeTrigger = nextion_ns.class_("WakeTrigger", automation.Trigger.template())
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
display.BASIC_DISPLAY_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(Nextion),
|
||||
cv.Optional(CONF_TFT_URL): cv.string,
|
||||
cv.Optional(CONF_BRIGHTNESS, default=1.0): cv.percentage,
|
||||
cv.Optional(CONF_ON_SETUP): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SetupTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_SLEEP): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SleepTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_WAKE): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(WakeTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_TOUCH_SLEEP_TIMEOUT): cv.int_range(min=3, max=65535),
|
||||
cv.Optional(CONF_WAKE_UP_PAGE): cv.positive_int,
|
||||
cv.Optional(CONF_AUTO_WAKE_ON_TOUCH, default=True): cv.boolean,
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("5s"))
|
||||
@ -31,8 +69,33 @@ async def to_code(config):
|
||||
cg.add(var.set_brightness(config[CONF_BRIGHTNESS]))
|
||||
if CONF_LAMBDA in config:
|
||||
lambda_ = await cg.process_lambda(
|
||||
config[CONF_LAMBDA], [(NextionRef, "it")], return_type=cg.void
|
||||
config[CONF_LAMBDA], [(nextion_ref, "it")], return_type=cg.void
|
||||
)
|
||||
cg.add(var.set_writer(lambda_))
|
||||
|
||||
if CONF_TFT_URL in config:
|
||||
cg.add_define("USE_TFT_UPLOAD")
|
||||
cg.add(var.set_tft_url(config[CONF_TFT_URL]))
|
||||
|
||||
if CONF_TOUCH_SLEEP_TIMEOUT in config:
|
||||
cg.add(var.set_touch_sleep_timeout_internal(config[CONF_TOUCH_SLEEP_TIMEOUT]))
|
||||
|
||||
if CONF_WAKE_UP_PAGE in config:
|
||||
cg.add(var.set_wake_up_page_internal(config[CONF_WAKE_UP_PAGE]))
|
||||
|
||||
if CONF_AUTO_WAKE_ON_TOUCH in config:
|
||||
cg.add(var.set_auto_wake_on_touch_internal(config[CONF_AUTO_WAKE_ON_TOUCH]))
|
||||
|
||||
await display.register_display(var, config)
|
||||
|
||||
for conf in config.get(CONF_ON_SETUP, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
|
||||
for conf in config.get(CONF_ON_SLEEP, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
|
||||
for conf in config.get(CONF_ON_WAKE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
#include "nextion.h"
|
||||
#include "esphome/core/util.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/application.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace nextion {
|
||||
@ -7,69 +9,171 @@ namespace nextion {
|
||||
static const char *const TAG = "nextion";
|
||||
|
||||
void Nextion::setup() {
|
||||
this->send_command_no_ack("");
|
||||
this->send_command_printf("bkcmd=3");
|
||||
this->set_backlight_brightness(static_cast<uint8_t>(brightness_ * 100));
|
||||
this->goto_page("0");
|
||||
this->is_setup_ = false;
|
||||
this->ignore_is_setup_ = true;
|
||||
|
||||
// Wake up the nextion
|
||||
this->send_command_("bkcmd=0");
|
||||
this->send_command_("sleep=0");
|
||||
|
||||
this->send_command_("bkcmd=0");
|
||||
this->send_command_("sleep=0");
|
||||
|
||||
// Reboot it
|
||||
this->send_command_("rest");
|
||||
|
||||
this->ignore_is_setup_ = false;
|
||||
}
|
||||
float Nextion::get_setup_priority() const { return setup_priority::PROCESSOR; }
|
||||
|
||||
bool Nextion::send_command_(const std::string &command) {
|
||||
if (!this->ignore_is_setup_ && !this->is_setup()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ESP_LOGN(TAG, "send_command %s", command.c_str());
|
||||
|
||||
this->write_str(command.c_str());
|
||||
const uint8_t to_send[3] = {0xFF, 0xFF, 0xFF};
|
||||
this->write_array(to_send, sizeof(to_send));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Nextion::check_connect_() {
|
||||
if (this->get_is_connected_())
|
||||
return true;
|
||||
|
||||
if (this->comok_sent_ == 0) {
|
||||
this->reset_(false);
|
||||
|
||||
this->ignore_is_setup_ = true;
|
||||
this->send_command_("boguscommand=0"); // bogus command. needed sometimes after updating
|
||||
this->send_command_("connect");
|
||||
|
||||
this->comok_sent_ = millis();
|
||||
this->ignore_is_setup_ = false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (millis() - this->comok_sent_ <= 500) // Wait 500 ms
|
||||
return false;
|
||||
|
||||
std::string response;
|
||||
|
||||
this->recv_ret_string_(response, 0, false);
|
||||
if (response.empty() || response.find("comok") == std::string::npos) {
|
||||
#ifdef NEXTION_PROTOCOL_LOG
|
||||
ESP_LOGN(TAG, "Bad connect request %s", response.c_str());
|
||||
for (int i = 0; i < response.length(); i++) {
|
||||
ESP_LOGN(TAG, "response %s %d %d %c", response.c_str(), i, response[i], response[i]);
|
||||
}
|
||||
#endif
|
||||
|
||||
ESP_LOGW(TAG, "Nextion is not connected! ");
|
||||
comok_sent_ = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
this->ignore_is_setup_ = true;
|
||||
ESP_LOGI(TAG, "Nextion is connected");
|
||||
this->is_connected_ = true;
|
||||
|
||||
ESP_LOGN(TAG, "connect request %s", response.c_str());
|
||||
|
||||
size_t start;
|
||||
size_t end = 0;
|
||||
std::vector<std::string> connect_info;
|
||||
while ((start = response.find_first_not_of(',', end)) != std::string::npos) {
|
||||
end = response.find(',', start);
|
||||
connect_info.push_back(response.substr(start, end - start));
|
||||
}
|
||||
|
||||
if (connect_info.size() == 7) {
|
||||
ESP_LOGN(TAG, "Received connect_info %zu", connect_info.size());
|
||||
|
||||
this->device_model_ = connect_info[2];
|
||||
this->firmware_version_ = connect_info[3];
|
||||
this->serial_number_ = connect_info[5];
|
||||
this->flash_size_ = connect_info[6];
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Nextion returned bad connect value \"%s\"", response.c_str());
|
||||
}
|
||||
|
||||
this->ignore_is_setup_ = false;
|
||||
this->dump_config();
|
||||
return true;
|
||||
}
|
||||
|
||||
void Nextion::reset_(bool reset_nextion) {
|
||||
uint8_t d;
|
||||
|
||||
while (this->available()) { // Clear receive buffer
|
||||
this->read_byte(&d);
|
||||
};
|
||||
this->nextion_queue_.clear();
|
||||
}
|
||||
|
||||
void Nextion::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "Nextion:");
|
||||
ESP_LOGCONFIG(TAG, " Device Model: %s", this->device_model_.c_str());
|
||||
ESP_LOGCONFIG(TAG, " Firmware Version: %s", this->firmware_version_.c_str());
|
||||
ESP_LOGCONFIG(TAG, " Serial Number: %s", this->serial_number_.c_str());
|
||||
ESP_LOGCONFIG(TAG, " Flash Size: %s", this->flash_size_.c_str());
|
||||
ESP_LOGCONFIG(TAG, " Wake On Touch: %s", this->auto_wake_on_touch_ ? "True" : "False");
|
||||
|
||||
if (this->touch_sleep_timeout_ != 0) {
|
||||
ESP_LOGCONFIG(TAG, " Touch Timeout: %d", this->touch_sleep_timeout_);
|
||||
}
|
||||
|
||||
if (this->wake_up_page_ != -1) {
|
||||
ESP_LOGCONFIG(TAG, " Wake Up Page : %d", this->wake_up_page_);
|
||||
}
|
||||
}
|
||||
|
||||
float Nextion::get_setup_priority() const { return setup_priority::DATA; }
|
||||
void Nextion::update() {
|
||||
if (!this->is_setup()) {
|
||||
return;
|
||||
}
|
||||
if (this->writer_.has_value()) {
|
||||
(*this->writer_)(*this);
|
||||
}
|
||||
}
|
||||
void Nextion::send_command_no_ack(const char *command) {
|
||||
// Flush RX...
|
||||
this->loop();
|
||||
|
||||
this->write_str(command);
|
||||
const uint8_t data[3] = {0xFF, 0xFF, 0xFF};
|
||||
this->write_array(data, sizeof(data));
|
||||
void Nextion::add_sleep_state_callback(std::function<void()> &&callback) {
|
||||
this->sleep_callback_.add(std::move(callback));
|
||||
}
|
||||
|
||||
bool Nextion::ack_() {
|
||||
if (!this->wait_for_ack_)
|
||||
return true;
|
||||
void Nextion::add_wake_state_callback(std::function<void()> &&callback) {
|
||||
this->wake_callback_.add(std::move(callback));
|
||||
}
|
||||
|
||||
uint32_t start = millis();
|
||||
while (!this->read_until_ack_()) {
|
||||
if (millis() - start > 100) {
|
||||
ESP_LOGW(TAG, "Waiting for ACK timed out!");
|
||||
return false;
|
||||
void Nextion::add_setup_state_callback(std::function<void()> &&callback) {
|
||||
this->setup_callback_.add(std::move(callback));
|
||||
}
|
||||
|
||||
void Nextion::update_all_components() {
|
||||
if ((!this->is_setup() && !this->ignore_is_setup_) || this->is_sleeping())
|
||||
return;
|
||||
|
||||
for (auto *binarysensortype : this->binarysensortype_) {
|
||||
binarysensortype->update_component();
|
||||
}
|
||||
for (auto *sensortype : this->sensortype_) {
|
||||
sensortype->update_component();
|
||||
}
|
||||
for (auto *switchtype : this->switchtype_) {
|
||||
switchtype->update_component();
|
||||
}
|
||||
for (auto *textsensortype : this->textsensortype_) {
|
||||
textsensortype->update_component();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void Nextion::set_component_text(const char *component, const char *text) {
|
||||
this->send_command_printf("%s.txt=\"%s\"", component, text);
|
||||
}
|
||||
void Nextion::set_component_value(const char *component, int value) {
|
||||
this->send_command_printf("%s.val=%d", component, value);
|
||||
}
|
||||
void Nextion::display_picture(int picture_id, int x_start, int y_start) {
|
||||
this->send_command_printf("pic %d %d %d", x_start, y_start, picture_id);
|
||||
}
|
||||
void Nextion::set_component_background_color(const char *component, const char *color) {
|
||||
this->send_command_printf("%s.bco=\"%s\"", component, color);
|
||||
}
|
||||
void Nextion::set_component_pressed_background_color(const char *component, const char *color) {
|
||||
this->send_command_printf("%s.bco2=\"%s\"", component, color);
|
||||
}
|
||||
void Nextion::set_component_font_color(const char *component, const char *color) {
|
||||
this->send_command_printf("%s.pco=\"%s\"", component, color);
|
||||
}
|
||||
void Nextion::set_component_pressed_font_color(const char *component, const char *color) {
|
||||
this->send_command_printf("%s.pco2=\"%s\"", component, color);
|
||||
}
|
||||
void Nextion::set_component_coordinates(const char *component, int x, int y) {
|
||||
this->send_command_printf("%s.xcen=%d", component, x);
|
||||
this->send_command_printf("%s.ycen=%d", component, y);
|
||||
}
|
||||
void Nextion::set_component_font(const char *component, uint8_t font_id) {
|
||||
this->send_command_printf("%s.font=%d", component, font_id);
|
||||
}
|
||||
void Nextion::goto_page(const char *page) { this->send_command_printf("page %s", page); }
|
||||
|
||||
bool Nextion::send_command_printf(const char *format, ...) {
|
||||
if ((!this->is_setup() && !this->ignore_is_setup_) || this->is_sleeping())
|
||||
return false;
|
||||
|
||||
char buffer[256];
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
@ -79,208 +183,911 @@ bool Nextion::send_command_printf(const char *format, ...) {
|
||||
ESP_LOGW(TAG, "Building command for format '%s' failed!", format);
|
||||
return false;
|
||||
}
|
||||
this->send_command_no_ack(buffer);
|
||||
if (!this->ack_()) {
|
||||
ESP_LOGW(TAG, "Sending command '%s' failed because no ACK was received", buffer);
|
||||
|
||||
if (this->send_command_(buffer)) {
|
||||
this->add_no_result_to_queue_("send_command_printf");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef NEXTION_PROTOCOL_LOG
|
||||
void Nextion::print_queue_members_() {
|
||||
ESP_LOGN(TAG, "print_queue_members_ (top 10) size %zu", this->nextion_queue_.size());
|
||||
ESP_LOGN(TAG, "*******************************************");
|
||||
int count = 0;
|
||||
for (auto *i : this->nextion_queue_) {
|
||||
if (count++ == 10)
|
||||
break;
|
||||
|
||||
if (i == nullptr) {
|
||||
ESP_LOGN(TAG, "Nextion queue is null");
|
||||
} else {
|
||||
ESP_LOGN(TAG, "Nextion queue type: %d:%s , name: %s", i->component->get_queue_type(),
|
||||
i->component->get_queue_type_string().c_str(), i->component->get_variable_name().c_str());
|
||||
}
|
||||
}
|
||||
ESP_LOGN(TAG, "*******************************************");
|
||||
}
|
||||
#endif
|
||||
|
||||
void Nextion::loop() {
|
||||
if (!this->check_connect_() || this->is_updating_)
|
||||
return;
|
||||
|
||||
if (this->nextion_reports_is_setup_ && !this->sent_setup_commands_) {
|
||||
this->ignore_is_setup_ = true;
|
||||
this->sent_setup_commands_ = true;
|
||||
this->send_command_("bkcmd=3"); // Always, returns 0x00 to 0x23 result of serial command.
|
||||
|
||||
this->set_backlight_brightness(this->brightness_);
|
||||
this->goto_page("0");
|
||||
|
||||
this->set_auto_wake_on_touch(this->auto_wake_on_touch_);
|
||||
|
||||
if (this->touch_sleep_timeout_ != 0) {
|
||||
this->set_touch_sleep_timeout(this->touch_sleep_timeout_);
|
||||
}
|
||||
|
||||
if (this->wake_up_page_ != -1) {
|
||||
this->set_wake_up_page(this->wake_up_page_);
|
||||
}
|
||||
|
||||
this->ignore_is_setup_ = false;
|
||||
}
|
||||
|
||||
this->process_serial_(); // Receive serial data
|
||||
this->process_nextion_commands_(); // Process nextion return commands
|
||||
|
||||
if (!this->nextion_reports_is_setup_) {
|
||||
if (this->started_ms_ == 0)
|
||||
this->started_ms_ = millis();
|
||||
|
||||
if (this->started_ms_ + this->startup_override_ms_ < millis()) {
|
||||
ESP_LOGD(TAG, "Manually set nextion report ready");
|
||||
this->nextion_reports_is_setup_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Nextion::remove_from_q_(bool report_empty) {
|
||||
if (this->nextion_queue_.empty()) {
|
||||
if (report_empty)
|
||||
ESP_LOGE(TAG, "Nextion queue is empty!");
|
||||
return false;
|
||||
}
|
||||
|
||||
NextionQueue *nb = this->nextion_queue_.front();
|
||||
NextionComponentBase *component = nb->component;
|
||||
|
||||
ESP_LOGN(TAG, "Removing %s from the queue", component->get_variable_name().c_str());
|
||||
|
||||
if (component->get_queue_type() == NextionQueueType::NO_RESULT) {
|
||||
if (component->get_variable_name() == "sleep_wake") {
|
||||
this->is_sleeping_ = false;
|
||||
}
|
||||
delete component;
|
||||
}
|
||||
delete nb;
|
||||
this->nextion_queue_.pop_front();
|
||||
return true;
|
||||
}
|
||||
void Nextion::hide_component(const char *component) { this->send_command_printf("vis %s,0", component); }
|
||||
void Nextion::show_component(const char *component) { this->send_command_printf("vis %s,1", component); }
|
||||
void Nextion::enable_component_touch(const char *component) { this->send_command_printf("tsw %s,1", component); }
|
||||
void Nextion::disable_component_touch(const char *component) { this->send_command_printf("tsw %s,0", component); }
|
||||
void Nextion::add_waveform_data(int component_id, uint8_t channel_number, uint8_t value) {
|
||||
this->send_command_printf("add %d,%u,%u", component_id, channel_number, value);
|
||||
}
|
||||
void Nextion::fill_area(int x1, int y1, int width, int height, const char *color) {
|
||||
this->send_command_printf("fill %d,%d,%d,%d,%s", x1, y1, width, height, color);
|
||||
}
|
||||
void Nextion::line(int x1, int y1, int x2, int y2, const char *color) {
|
||||
this->send_command_printf("line %d,%d,%d,%d,%s", x1, y1, x2, y2, color);
|
||||
}
|
||||
void Nextion::rectangle(int x1, int y1, int width, int height, const char *color) {
|
||||
this->send_command_printf("draw %d,%d,%d,%d,%s", x1, y1, x1 + width, y1 + height, color);
|
||||
}
|
||||
void Nextion::circle(int center_x, int center_y, int radius, const char *color) {
|
||||
this->send_command_printf("cir %d,%d,%d,%s", center_x, center_y, radius, color);
|
||||
}
|
||||
void Nextion::filled_circle(int center_x, int center_y, int radius, const char *color) {
|
||||
this->send_command_printf("cirs %d,%d,%d,%s", center_x, center_y, radius, color);
|
||||
}
|
||||
bool Nextion::read_until_ack_() {
|
||||
while (this->available() >= 4) {
|
||||
// flush preceding filler bytes
|
||||
uint8_t temp;
|
||||
while (this->available() && this->peek_byte(&temp) && temp == 0xFF)
|
||||
this->read_byte(&temp);
|
||||
|
||||
if (!this->available())
|
||||
break;
|
||||
void Nextion::process_serial_() {
|
||||
uint8_t d;
|
||||
|
||||
uint8_t event;
|
||||
// event type
|
||||
this->read_byte(&event);
|
||||
|
||||
uint8_t data[255];
|
||||
// total length of data (including end bytes)
|
||||
uint8_t data_length = 0;
|
||||
// message is terminated by three consecutive 0xFF
|
||||
// this variable keeps track of ohow many of those have
|
||||
// been received
|
||||
uint8_t end_length = 0;
|
||||
while (this->available() && end_length < 3 && data_length < sizeof(data)) {
|
||||
uint8_t byte;
|
||||
this->read_byte(&byte);
|
||||
if (byte == 0xFF) {
|
||||
end_length++;
|
||||
} else {
|
||||
end_length = 0;
|
||||
while (this->available()) {
|
||||
read_byte(&d);
|
||||
this->command_data_ += d;
|
||||
}
|
||||
data[data_length++] = byte;
|
||||
}
|
||||
// nextion.tech/instruction-set/
|
||||
void Nextion::process_nextion_commands_() {
|
||||
if (this->command_data_.length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (end_length != 3) {
|
||||
ESP_LOGW(TAG, "Received unknown filler end bytes from Nextion!");
|
||||
continue;
|
||||
size_t to_process_length = 0;
|
||||
std::string to_process;
|
||||
|
||||
ESP_LOGN(TAG, "this->command_data_ %s length %d", this->command_data_.c_str(), this->command_data_.length());
|
||||
#ifdef NEXTION_PROTOCOL_LOG
|
||||
this->print_queue_members_();
|
||||
#endif
|
||||
while ((to_process_length = this->command_data_.find(COMMAND_DELIMITER)) != std::string::npos) {
|
||||
ESP_LOGN(TAG, "print_queue_members_ size %zu", this->nextion_queue_.size());
|
||||
while (to_process_length + COMMAND_DELIMITER.length() < this->command_data_.length() &&
|
||||
static_cast<uint8_t>(this->command_data_[to_process_length + COMMAND_DELIMITER.length()]) == 0xFF) {
|
||||
++to_process_length;
|
||||
ESP_LOGN(TAG, "Add extra 0xFF to process");
|
||||
}
|
||||
|
||||
data_length -= 3; // remove filler bytes
|
||||
this->nextion_event_ = this->command_data_[0];
|
||||
|
||||
bool invalid_data_length = false;
|
||||
switch (event) {
|
||||
case 0x01: // successful execution of instruction (ACK)
|
||||
return true;
|
||||
case 0x00: // invalid instruction
|
||||
to_process_length -= 1;
|
||||
to_process = this->command_data_.substr(1, to_process_length);
|
||||
|
||||
switch (this->nextion_event_) {
|
||||
case 0x00: // instruction sent by user has failed
|
||||
ESP_LOGW(TAG, "Nextion reported invalid instruction!");
|
||||
this->remove_from_q_();
|
||||
|
||||
break;
|
||||
case 0x02: // component ID invalid
|
||||
ESP_LOGW(TAG, "Nextion reported component ID invalid!");
|
||||
case 0x01: // instruction sent by user was successful
|
||||
|
||||
ESP_LOGVV(TAG, "instruction sent by user was successful");
|
||||
ESP_LOGN(TAG, "this->nextion_queue_.empty() %s", this->nextion_queue_.empty() ? "True" : "False");
|
||||
|
||||
this->remove_from_q_();
|
||||
if (!this->is_setup_) {
|
||||
if (this->nextion_queue_.empty()) {
|
||||
ESP_LOGD(TAG, "Nextion is setup");
|
||||
this->is_setup_ = true;
|
||||
this->setup_callback_.call();
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case 0x03: // page ID invalid
|
||||
case 0x02: // invalid Component ID or name was used
|
||||
this->remove_from_q_();
|
||||
break;
|
||||
case 0x03: // invalid Page ID or name was used
|
||||
ESP_LOGW(TAG, "Nextion reported page ID invalid!");
|
||||
this->remove_from_q_();
|
||||
break;
|
||||
case 0x04: // picture ID invalid
|
||||
case 0x04: // invalid Picture ID was used
|
||||
ESP_LOGW(TAG, "Nextion reported picture ID invalid!");
|
||||
this->remove_from_q_();
|
||||
break;
|
||||
case 0x05: // font ID invalid
|
||||
case 0x05: // invalid Font ID was used
|
||||
ESP_LOGW(TAG, "Nextion reported font ID invalid!");
|
||||
this->remove_from_q_();
|
||||
break;
|
||||
case 0x11: // baud rate setting invalid
|
||||
case 0x06: // File operation fails
|
||||
ESP_LOGW(TAG, "Nextion File operation fail!");
|
||||
break;
|
||||
case 0x09: // Instructions with CRC validation fails their CRC check
|
||||
ESP_LOGW(TAG, "Nextion Instructions with CRC validation fails their CRC check!");
|
||||
break;
|
||||
case 0x11: // invalid Baud rate was used
|
||||
ESP_LOGW(TAG, "Nextion reported baud rate invalid!");
|
||||
break;
|
||||
case 0x12: // curve control ID number or channel number is invalid
|
||||
ESP_LOGW(TAG, "Nextion reported control/channel ID invalid!");
|
||||
case 0x12: // invalid Waveform ID or Channel # was used
|
||||
|
||||
if (!this->nextion_queue_.empty()) {
|
||||
int index = 0;
|
||||
int found = -1;
|
||||
for (auto &nb : this->nextion_queue_) {
|
||||
NextionComponentBase *component = nb->component;
|
||||
|
||||
if (component->get_queue_type() == NextionQueueType::WAVEFORM_SENSOR) {
|
||||
ESP_LOGW(TAG, "Nextion reported invalid Waveform ID %d or Channel # %d was used!",
|
||||
component->get_component_id(), component->get_wave_channel_id());
|
||||
|
||||
ESP_LOGN(TAG, "Removing waveform from queue with component id %d and waveform id %d",
|
||||
component->get_component_id(), component->get_wave_channel_id());
|
||||
|
||||
found = index;
|
||||
|
||||
delete component;
|
||||
delete nb;
|
||||
|
||||
break;
|
||||
}
|
||||
++index;
|
||||
}
|
||||
|
||||
if (found != -1) {
|
||||
this->nextion_queue_.erase(this->nextion_queue_.begin() + found);
|
||||
} else {
|
||||
ESP_LOGW(
|
||||
TAG,
|
||||
"Nextion reported invalid Waveform ID or Channel # was used but no waveform sensor in queue found!");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x1A: // variable name invalid
|
||||
ESP_LOGW(TAG, "Nextion reported variable name invalid!");
|
||||
this->remove_from_q_();
|
||||
|
||||
break;
|
||||
case 0x1B: // variable operation invalid
|
||||
ESP_LOGW(TAG, "Nextion reported variable operation invalid!");
|
||||
this->remove_from_q_();
|
||||
break;
|
||||
case 0x1C: // failed to assign
|
||||
ESP_LOGW(TAG, "Nextion reported failed to assign variable!");
|
||||
this->remove_from_q_();
|
||||
break;
|
||||
case 0x1D: // operate EEPROM failed
|
||||
ESP_LOGW(TAG, "Nextion reported operating EEPROM failed!");
|
||||
break;
|
||||
case 0x1E: // parameter quantity invalid
|
||||
ESP_LOGW(TAG, "Nextion reported parameter quantity invalid!");
|
||||
this->remove_from_q_();
|
||||
break;
|
||||
case 0x1F: // IO operation failed
|
||||
ESP_LOGW(TAG, "Nextion reported component I/O operation invalid!");
|
||||
break;
|
||||
case 0x20: // undefined escape characters
|
||||
ESP_LOGW(TAG, "Nextion reported undefined escape characters!");
|
||||
this->remove_from_q_();
|
||||
break;
|
||||
case 0x23: // too long variable name
|
||||
ESP_LOGW(TAG, "Nextion reported too long variable name!");
|
||||
this->remove_from_q_();
|
||||
|
||||
break;
|
||||
case 0x24: // Serial Buffer overflow occurs
|
||||
ESP_LOGW(TAG, "Nextion reported Serial Buffer overflow!");
|
||||
break;
|
||||
case 0x65: { // touch event return data
|
||||
if (data_length != 3) {
|
||||
invalid_data_length = true;
|
||||
if (to_process_length != 3) {
|
||||
ESP_LOGW(TAG, "Touch event data is expecting 3, received %zu", to_process_length);
|
||||
|
||||
break;
|
||||
}
|
||||
uint8_t page_id = data[0];
|
||||
uint8_t component_id = data[1];
|
||||
uint8_t touch_event = data[2]; // 0 -> release, 1 -> press
|
||||
uint8_t page_id = to_process[0];
|
||||
uint8_t component_id = to_process[1];
|
||||
uint8_t touch_event = to_process[2]; // 0 -> release, 1 -> press
|
||||
ESP_LOGD(TAG, "Got touch page=%u component=%u type=%s", page_id, component_id,
|
||||
touch_event ? "PRESS" : "RELEASE");
|
||||
for (auto *touch : this->touch_) {
|
||||
touch->process(page_id, component_id, touch_event);
|
||||
touch->process_touch(page_id, component_id, touch_event != 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x67:
|
||||
case 0x68: { // touch coordinate data
|
||||
if (data_length != 5) {
|
||||
invalid_data_length = true;
|
||||
case 0x67: { // Touch Coordinate (awake)
|
||||
break;
|
||||
}
|
||||
uint16_t x = (uint16_t(data[0]) << 8) | data[1];
|
||||
uint16_t y = (uint16_t(data[2]) << 8) | data[3];
|
||||
uint8_t touch_event = data[4]; // 0 -> release, 1 -> press
|
||||
case 0x68: { // touch coordinate data (sleep)
|
||||
|
||||
if (to_process_length != 5) {
|
||||
ESP_LOGW(TAG, "Touch coordinate data is expecting 5, received %zu", to_process_length);
|
||||
ESP_LOGW(TAG, "%s", to_process.c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
uint16_t x = (uint16_t(to_process[0]) << 8) | to_process[1];
|
||||
uint16_t y = (uint16_t(to_process[2]) << 8) | to_process[3];
|
||||
uint8_t touch_event = to_process[4]; // 0 -> release, 1 -> press
|
||||
ESP_LOGD(TAG, "Got touch at x=%u y=%u type=%s", x, y, touch_event ? "PRESS" : "RELEASE");
|
||||
break;
|
||||
}
|
||||
case 0x66: // sendme page id
|
||||
case 0x66: {
|
||||
break;
|
||||
} // sendme page id
|
||||
|
||||
// 0x70 0x61 0x62 0x31 0x32 0x33 0xFF 0xFF 0xFF
|
||||
// Returned when using get command for a string.
|
||||
// Each byte is converted to char.
|
||||
// data: ab123
|
||||
case 0x70: // string variable data return
|
||||
{
|
||||
if (this->nextion_queue_.empty()) {
|
||||
ESP_LOGW(TAG, "ERROR: Received string return but the queue is empty");
|
||||
break;
|
||||
}
|
||||
|
||||
NextionQueue *nb = this->nextion_queue_.front();
|
||||
NextionComponentBase *component = nb->component;
|
||||
|
||||
if (component->get_queue_type() != NextionQueueType::TEXT_SENSOR) {
|
||||
ESP_LOGE(TAG, "ERROR: Received string return but next in queue \"%s\" is not a text sensor",
|
||||
component->get_variable_name().c_str());
|
||||
} else {
|
||||
ESP_LOGN(TAG, "Received get_string response: \"%s\" for component id: %s, type: %s", to_process.c_str(),
|
||||
component->get_variable_name().c_str(), component->get_queue_type_string().c_str());
|
||||
component->set_state_from_string(to_process, true, false);
|
||||
}
|
||||
|
||||
delete nb;
|
||||
this->nextion_queue_.pop_front();
|
||||
|
||||
break;
|
||||
}
|
||||
// 0x71 0x01 0x02 0x03 0x04 0xFF 0xFF 0xFF
|
||||
// Returned when get command to return a number
|
||||
// 4 byte 32-bit value in little endian order.
|
||||
// (0x01+0x02*256+0x03*65536+0x04*16777216)
|
||||
// data: 67305985
|
||||
case 0x71: // numeric variable data return
|
||||
case 0x86: // device automatically enters into sleep mode
|
||||
{
|
||||
if (this->nextion_queue_.empty()) {
|
||||
ESP_LOGE(TAG, "ERROR: Received numeric return but the queue is empty");
|
||||
break;
|
||||
}
|
||||
|
||||
if (to_process_length == 0) {
|
||||
ESP_LOGE(TAG, "ERROR: Received numeric return but no data!");
|
||||
break;
|
||||
}
|
||||
|
||||
int dataindex = 0;
|
||||
|
||||
int value = 0;
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
value += to_process[i] << (8 * i);
|
||||
++dataindex;
|
||||
}
|
||||
|
||||
NextionQueue *nb = this->nextion_queue_.front();
|
||||
NextionComponentBase *component = nb->component;
|
||||
|
||||
if (component->get_queue_type() != NextionQueueType::SENSOR &&
|
||||
component->get_queue_type() != NextionQueueType::BINARY_SENSOR &&
|
||||
component->get_queue_type() != NextionQueueType::SWITCH) {
|
||||
ESP_LOGE(TAG, "ERROR: Received numeric return but next in queue \"%s\" is not a valid sensor type %d",
|
||||
component->get_variable_name().c_str(), component->get_queue_type());
|
||||
} else {
|
||||
ESP_LOGN(TAG, "Received numeric return for variable %s, queue type %d:%s, value %d",
|
||||
component->get_variable_name().c_str(), component->get_queue_type(),
|
||||
component->get_queue_type_string().c_str(), value);
|
||||
component->set_state_from_int(value, true, false);
|
||||
}
|
||||
|
||||
delete nb;
|
||||
this->nextion_queue_.pop_front();
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x86: { // device automatically enters into sleep mode
|
||||
ESP_LOGVV(TAG, "Received Nextion entering sleep automatically");
|
||||
this->is_sleeping_ = true;
|
||||
this->sleep_callback_.call();
|
||||
break;
|
||||
}
|
||||
case 0x87: // device automatically wakes up
|
||||
{
|
||||
ESP_LOGVV(TAG, "Received Nextion leaves sleep automatically");
|
||||
this->is_sleeping_ = false;
|
||||
this->wake_callback_.call();
|
||||
break;
|
||||
}
|
||||
case 0x88: // system successful start up
|
||||
case 0x89: // start SD card upgrade
|
||||
case 0xFD: // data transparent transmit finished
|
||||
case 0xFE: // data transparent transmit ready
|
||||
{
|
||||
ESP_LOGD(TAG, "system successful start up %zu", to_process_length);
|
||||
this->nextion_reports_is_setup_ = true;
|
||||
break;
|
||||
}
|
||||
case 0x89: { // start SD card upgrade
|
||||
break;
|
||||
}
|
||||
// Data from nextion is
|
||||
// 0x90 - Start
|
||||
// variable length of 0x70 return formatted data (bytes) that contain the variable name: prints "temp1",0
|
||||
// 00 - NULL
|
||||
// 00/01 - Single byte for on/off
|
||||
// FF FF FF - End
|
||||
case 0x90: { // Switched component
|
||||
std::string variable_name;
|
||||
uint8_t index = 0;
|
||||
|
||||
// Get variable name
|
||||
index = to_process.find('\0');
|
||||
if (static_cast<char>(index) == std::string::npos || (to_process_length - index - 1) < 1) {
|
||||
ESP_LOGE(TAG, "Bad switch component data received for 0x90 event!");
|
||||
ESP_LOGN(TAG, "to_process %s %zu %d", to_process.c_str(), to_process_length, index);
|
||||
break;
|
||||
}
|
||||
|
||||
variable_name = to_process.substr(0, index);
|
||||
++index;
|
||||
|
||||
ESP_LOGN(TAG, "Got Switch variable_name=%s value=%d", variable_name.c_str(), to_process[0] != 0);
|
||||
|
||||
for (auto *switchtype : this->switchtype_) {
|
||||
switchtype->process_bool(variable_name, to_process[index] != 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Data from nextion is
|
||||
// 0x91 - Start
|
||||
// variable length of 0x70 return formatted data (bytes) that contain the variable name: prints "temp1",0
|
||||
// 00 - NULL
|
||||
// variable length of 0x71 return data: prints temp1.val,0
|
||||
// FF FF FF - End
|
||||
case 0x91: { // Sensor component
|
||||
std::string variable_name;
|
||||
uint8_t index = 0;
|
||||
|
||||
index = to_process.find('\0');
|
||||
if (static_cast<char>(index) == std::string::npos || (to_process_length - index - 1) != 4) {
|
||||
ESP_LOGE(TAG, "Bad sensor component data received for 0x91 event!");
|
||||
ESP_LOGN(TAG, "to_process %s %zu %d", to_process.c_str(), to_process_length, index);
|
||||
break;
|
||||
}
|
||||
|
||||
index = to_process.find('\0');
|
||||
variable_name = to_process.substr(0, index);
|
||||
// // Get variable name
|
||||
int value = 0;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
value += to_process[i + index + 1] << (8 * i);
|
||||
}
|
||||
|
||||
ESP_LOGN(TAG, "Got sensor variable_name=%s value=%d", variable_name.c_str(), value);
|
||||
|
||||
for (auto *sensor : this->sensortype_) {
|
||||
sensor->process_sensor(variable_name, value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Data from nextion is
|
||||
// 0x92 - Start
|
||||
// variable length of 0x70 return formatted data (bytes) that contain the variable name: prints "temp1",0
|
||||
// 00 - NULL
|
||||
// variable length of 0x70 return formatted data (bytes) that contain the text prints temp1.txt,0
|
||||
// 00 - NULL
|
||||
// FF FF FF - End
|
||||
case 0x92: { // Text Sensor Component
|
||||
std::string variable_name;
|
||||
std::string text_value;
|
||||
uint8_t index = 0;
|
||||
|
||||
// Get variable name
|
||||
index = to_process.find('\0');
|
||||
if (static_cast<char>(index) == std::string::npos || (to_process_length - index - 1) < 1) {
|
||||
ESP_LOGE(TAG, "Bad text sensor component data received for 0x92 event!");
|
||||
ESP_LOGN(TAG, "to_process %s %zu %d", to_process.c_str(), to_process_length, index);
|
||||
break;
|
||||
}
|
||||
|
||||
variable_name = to_process.substr(0, index);
|
||||
++index;
|
||||
|
||||
text_value = to_process.substr(index);
|
||||
|
||||
ESP_LOGN(TAG, "Got Text Sensor variable_name=%s value=%s", variable_name.c_str(), text_value.c_str());
|
||||
|
||||
// NextionTextSensorResponseQueue *nq = new NextionTextSensorResponseQueue;
|
||||
// nq->variable_name = variable_name;
|
||||
// nq->state = text_value;
|
||||
// this->textsensorq_.push_back(nq);
|
||||
for (auto *textsensortype : this->textsensortype_) {
|
||||
textsensortype->process_text(variable_name, text_value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Data from nextion is
|
||||
// 0x93 - Start
|
||||
// variable length of 0x70 return formatted data (bytes) that contain the variable name: prints "temp1",0
|
||||
// 00 - NULL
|
||||
// 00/01 - Single byte for on/off
|
||||
// FF FF FF - End
|
||||
case 0x93: { // Binary Sensor component
|
||||
std::string variable_name;
|
||||
uint8_t index = 0;
|
||||
|
||||
// Get variable name
|
||||
index = to_process.find('\0');
|
||||
if (static_cast<char>(index) == std::string::npos || (to_process_length - index - 1) < 1) {
|
||||
ESP_LOGE(TAG, "Bad binary sensor component data received for 0x92 event!");
|
||||
ESP_LOGN(TAG, "to_process %s %zu %d", to_process.c_str(), to_process_length, index);
|
||||
break;
|
||||
}
|
||||
|
||||
variable_name = to_process.substr(0, index);
|
||||
++index;
|
||||
|
||||
ESP_LOGN(TAG, "Got Binary Sensor variable_name=%s value=%d", variable_name.c_str(), to_process[index] != 0);
|
||||
|
||||
for (auto *binarysensortype : this->binarysensortype_) {
|
||||
binarysensortype->process_bool(&variable_name[0], to_process[index] != 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0xFD: { // data transparent transmit finished
|
||||
ESP_LOGVV(TAG, "Nextion reported data transmit finished!");
|
||||
break;
|
||||
}
|
||||
case 0xFE: { // data transparent transmit ready
|
||||
ESP_LOGVV(TAG, "Nextion reported ready for transmit!");
|
||||
|
||||
int index = 0;
|
||||
int found = -1;
|
||||
for (auto &nb : this->nextion_queue_) {
|
||||
auto component = nb->component;
|
||||
if (component->get_queue_type() == NextionQueueType::WAVEFORM_SENSOR) {
|
||||
size_t buffer_to_send = component->get_wave_buffer().size() < 255 ? component->get_wave_buffer().size()
|
||||
: 255; // ADDT command can only send 255
|
||||
|
||||
this->write_array(component->get_wave_buffer().data(), static_cast<int>(buffer_to_send));
|
||||
|
||||
ESP_LOGN(TAG, "Nextion sending waveform data for component id %d and waveform id %d, size %zu",
|
||||
component->get_component_id(), component->get_wave_channel_id(), buffer_to_send);
|
||||
|
||||
if (component->get_wave_buffer().size() <= 255) {
|
||||
component->get_wave_buffer().clear();
|
||||
} else {
|
||||
component->get_wave_buffer().erase(component->get_wave_buffer().begin(),
|
||||
component->get_wave_buffer().begin() + buffer_to_send);
|
||||
}
|
||||
found = index;
|
||||
delete component;
|
||||
delete nb;
|
||||
break;
|
||||
}
|
||||
++index;
|
||||
}
|
||||
|
||||
if (found == -1) {
|
||||
ESP_LOGE(TAG, "No waveforms in queue to send data!");
|
||||
break;
|
||||
} else {
|
||||
this->nextion_queue_.erase(this->nextion_queue_.begin() + found);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ESP_LOGW(TAG, "Received unknown event from nextion: 0x%02X", event);
|
||||
ESP_LOGW(TAG, "Received unknown event from nextion: 0x%02X", this->nextion_event_);
|
||||
break;
|
||||
}
|
||||
if (invalid_data_length) {
|
||||
ESP_LOGW(TAG, "Invalid data length from nextion!");
|
||||
|
||||
// ESP_LOGN(TAG, "nextion_event_ deleting from 0 to %d", to_process_length + COMMAND_DELIMITER.length() + 1);
|
||||
this->command_data_.erase(0, to_process_length + COMMAND_DELIMITER.length() + 1);
|
||||
// App.feed_wdt(); Remove before master merge
|
||||
this->process_serial_();
|
||||
}
|
||||
|
||||
uint32_t ms = millis();
|
||||
|
||||
if (!this->nextion_queue_.empty() && this->nextion_queue_.front()->queue_time + this->max_q_age_ms_ < ms) {
|
||||
for (int i = 0; i < this->nextion_queue_.size(); i++) {
|
||||
NextionComponentBase *component = this->nextion_queue_[i]->component;
|
||||
if (this->nextion_queue_[i]->queue_time + this->max_q_age_ms_ < ms) {
|
||||
if (this->nextion_queue_[i]->queue_time == 0)
|
||||
ESP_LOGD(TAG, "Removing old queue type \"%s\" name \"%s\" queue_time 0",
|
||||
component->get_queue_type_string().c_str(), component->get_variable_name().c_str());
|
||||
|
||||
if (component->get_variable_name() == "sleep_wake") {
|
||||
this->is_sleeping_ = false;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Removing old queue type \"%s\" name \"%s\"", component->get_queue_type_string().c_str(),
|
||||
component->get_variable_name().c_str());
|
||||
|
||||
if (component->get_queue_type() == NextionQueueType::NO_RESULT) {
|
||||
if (component->get_variable_name() == "sleep_wake") {
|
||||
this->is_sleeping_ = false;
|
||||
}
|
||||
delete component;
|
||||
}
|
||||
|
||||
delete this->nextion_queue_[i];
|
||||
|
||||
this->nextion_queue_.erase(this->nextion_queue_.begin() + i);
|
||||
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ESP_LOGN(TAG, "Loop End");
|
||||
// App.feed_wdt(); Remove before master merge
|
||||
this->process_serial_();
|
||||
} // namespace nextion
|
||||
|
||||
void Nextion::set_nextion_sensor_state(int queue_type, const std::string &name, float state) {
|
||||
this->set_nextion_sensor_state(static_cast<NextionQueueType>(queue_type), name, state);
|
||||
}
|
||||
|
||||
void Nextion::set_nextion_sensor_state(NextionQueueType queue_type, const std::string &name, float state) {
|
||||
ESP_LOGN(TAG, "Received state for variable %s, state %lf for queue type %d", name.c_str(), state, queue_type);
|
||||
|
||||
switch (queue_type) {
|
||||
case NextionQueueType::SENSOR: {
|
||||
for (auto *sensor : this->sensortype_) {
|
||||
if (name == sensor->get_variable_name()) {
|
||||
sensor->set_state(state, true, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NextionQueueType::BINARY_SENSOR: {
|
||||
for (auto *sensor : this->binarysensortype_) {
|
||||
if (name == sensor->get_variable_name()) {
|
||||
sensor->set_state(state != 0, true, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NextionQueueType::SWITCH: {
|
||||
for (auto *sensor : this->switchtype_) {
|
||||
if (name == sensor->get_variable_name()) {
|
||||
sensor->set_state(state != 0, true, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
ESP_LOGW(TAG, "set_nextion_sensor_state does not support a queue type %d", queue_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Nextion::set_nextion_text_state(const std::string &name, const std::string &state) {
|
||||
ESP_LOGD(TAG, "Received state for variable %s, state %s", name.c_str(), state.c_str());
|
||||
|
||||
for (auto *sensor : this->textsensortype_) {
|
||||
if (name == sensor->get_variable_name()) {
|
||||
sensor->set_state(state, true, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Nextion::all_components_send_state_(bool force_update) {
|
||||
ESP_LOGD(TAG, "all_components_send_state_ ");
|
||||
for (auto *binarysensortype : this->binarysensortype_) {
|
||||
if (force_update || binarysensortype->get_needs_to_send_update())
|
||||
binarysensortype->send_state_to_nextion();
|
||||
}
|
||||
for (auto *sensortype : this->sensortype_) {
|
||||
if ((force_update || sensortype->get_needs_to_send_update()) && sensortype->get_wave_chan_id() == 0)
|
||||
sensortype->send_state_to_nextion();
|
||||
}
|
||||
for (auto *switchtype : this->switchtype_) {
|
||||
if (force_update || switchtype->get_needs_to_send_update())
|
||||
switchtype->send_state_to_nextion();
|
||||
}
|
||||
for (auto *textsensortype : this->textsensortype_) {
|
||||
if (force_update || textsensortype->get_needs_to_send_update())
|
||||
textsensortype->send_state_to_nextion();
|
||||
}
|
||||
}
|
||||
|
||||
void Nextion::update_components_by_prefix(const std::string &prefix) {
|
||||
for (auto *binarysensortype : this->binarysensortype_) {
|
||||
if (binarysensortype->get_variable_name().find(prefix, 0) != std::string::npos)
|
||||
binarysensortype->update_component_settings(true);
|
||||
}
|
||||
for (auto *sensortype : this->sensortype_) {
|
||||
if (sensortype->get_variable_name().find(prefix, 0) != std::string::npos)
|
||||
sensortype->update_component_settings(true);
|
||||
}
|
||||
for (auto *switchtype : this->switchtype_) {
|
||||
if (switchtype->get_variable_name().find(prefix, 0) != std::string::npos)
|
||||
switchtype->update_component_settings(true);
|
||||
}
|
||||
for (auto *textsensortype : this->textsensortype_) {
|
||||
if (textsensortype->get_variable_name().find(prefix, 0) != std::string::npos)
|
||||
textsensortype->update_component_settings(true);
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t Nextion::recv_ret_string_(std::string &response, uint32_t timeout, bool recv_flag) {
|
||||
uint16_t ret = 0;
|
||||
uint8_t c = 0;
|
||||
uint8_t nr_of_ff_bytes = 0;
|
||||
uint64_t start;
|
||||
bool exit_flag = false;
|
||||
bool ff_flag = false;
|
||||
|
||||
start = millis();
|
||||
|
||||
while ((timeout == 0 && this->available()) || millis() - start <= timeout) {
|
||||
this->read_byte(&c);
|
||||
if (c == 0xFF)
|
||||
nr_of_ff_bytes++;
|
||||
else {
|
||||
nr_of_ff_bytes = 0;
|
||||
ff_flag = false;
|
||||
}
|
||||
|
||||
if (nr_of_ff_bytes >= 3)
|
||||
ff_flag = true;
|
||||
|
||||
response += (char) c;
|
||||
if (recv_flag) {
|
||||
if (response.find(0x05) != std::string::npos) {
|
||||
exit_flag = true;
|
||||
}
|
||||
}
|
||||
App.feed_wdt();
|
||||
delay(1);
|
||||
|
||||
if (exit_flag || ff_flag) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ff_flag)
|
||||
response = response.substr(0, response.length() - 3); // Remove last 3 0xFF
|
||||
|
||||
ret = response.length();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
*
|
||||
* @param variable_name Name for the queue
|
||||
*/
|
||||
void Nextion::add_no_result_to_queue_(const std::string &variable_name) {
|
||||
nextion::NextionQueue *nextion_queue = new nextion::NextionQueue;
|
||||
|
||||
nextion_queue->component = new nextion::NextionComponentBase;
|
||||
nextion_queue->component->set_variable_name(variable_name);
|
||||
|
||||
nextion_queue->queue_time = millis();
|
||||
|
||||
this->nextion_queue_.push_back(nextion_queue);
|
||||
|
||||
ESP_LOGN(TAG, "Add to queue type: NORESULT component %s", nextion_queue->component->get_variable_name().c_str());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
*
|
||||
* @param variable_name Variable name for the queue
|
||||
* @param command
|
||||
*/
|
||||
void Nextion::add_no_result_to_queue_with_command_(const std::string &variable_name, const std::string &command) {
|
||||
if ((!this->is_setup() && !this->ignore_is_setup_) || command.empty())
|
||||
return;
|
||||
|
||||
if (this->send_command_(command)) {
|
||||
this->add_no_result_to_queue_(variable_name);
|
||||
}
|
||||
}
|
||||
|
||||
bool Nextion::add_no_result_to_queue_with_ignore_sleep_printf_(const std::string &variable_name, const char *format,
|
||||
...) {
|
||||
if ((!this->is_setup() && !this->ignore_is_setup_))
|
||||
return false;
|
||||
}
|
||||
void Nextion::loop() {
|
||||
while (this->available() >= 4) {
|
||||
this->read_until_ack_();
|
||||
}
|
||||
}
|
||||
#ifdef USE_TIME
|
||||
void Nextion::set_nextion_rtc_time(time::ESPTime time) {
|
||||
this->send_command_printf("rtc0=%u", time.year);
|
||||
this->send_command_printf("rtc1=%u", time.month);
|
||||
this->send_command_printf("rtc2=%u", time.day_of_month);
|
||||
this->send_command_printf("rtc3=%u", time.hour);
|
||||
this->send_command_printf("rtc4=%u", time.minute);
|
||||
this->send_command_printf("rtc5=%u", time.second);
|
||||
}
|
||||
#endif
|
||||
|
||||
void Nextion::set_backlight_brightness(uint8_t brightness) { this->send_command_printf("dim=%u", brightness); }
|
||||
void Nextion::set_touch_sleep_timeout(uint16_t timeout) { this->send_command_printf("thsp=%u", timeout); }
|
||||
|
||||
void Nextion::set_writer(const nextion_writer_t &writer) { this->writer_ = writer; }
|
||||
void Nextion::set_component_text_printf(const char *component, const char *format, ...) {
|
||||
char buffer[256];
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
char buffer[256];
|
||||
int ret = vsnprintf(buffer, sizeof(buffer), format, arg);
|
||||
va_end(arg);
|
||||
if (ret > 0)
|
||||
this->set_component_text(component, buffer);
|
||||
}
|
||||
void Nextion::set_wait_for_ack(bool wait_for_ack) { this->wait_for_ack_ = wait_for_ack; }
|
||||
if (ret <= 0) {
|
||||
ESP_LOGW(TAG, "Building command for format '%s' failed!", format);
|
||||
return false;
|
||||
}
|
||||
|
||||
void NextionTouchComponent::process(uint8_t page_id, uint8_t component_id, bool on) {
|
||||
if (this->page_id_ == page_id && this->component_id_ == component_id) {
|
||||
this->publish_state(on);
|
||||
this->add_no_result_to_queue_with_command_(variable_name, buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sends a formatted command to the nextion
|
||||
*
|
||||
* @param variable_name Variable name for the queue
|
||||
* @param format The printf-style command format, like "vis %s,0"
|
||||
* @param ... The format arguments
|
||||
*/
|
||||
bool Nextion::add_no_result_to_queue_with_printf_(const std::string &variable_name, const char *format, ...) {
|
||||
if ((!this->is_setup() && !this->ignore_is_setup_) || this->is_sleeping())
|
||||
return false;
|
||||
|
||||
char buffer[256];
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
int ret = vsnprintf(buffer, sizeof(buffer), format, arg);
|
||||
va_end(arg);
|
||||
if (ret <= 0) {
|
||||
ESP_LOGW(TAG, "Building command for format '%s' failed!", format);
|
||||
return false;
|
||||
}
|
||||
|
||||
this->add_no_result_to_queue_with_command_(variable_name, buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
*
|
||||
* @param variable_name Variable name for the queue
|
||||
* @param variable_name_to_send Variable name for the left of the command
|
||||
* @param state_value Value to set
|
||||
* @param is_sleep_safe The command is safe to send when the Nextion is sleeping
|
||||
*/
|
||||
|
||||
void Nextion::add_no_result_to_queue_with_set(NextionComponentBase *component, int state_value) {
|
||||
this->add_no_result_to_queue_with_set(component->get_variable_name(), component->get_variable_name_to_send(),
|
||||
state_value);
|
||||
}
|
||||
|
||||
void Nextion::add_no_result_to_queue_with_set(const std::string &variable_name,
|
||||
const std::string &variable_name_to_send, int state_value) {
|
||||
this->add_no_result_to_queue_with_set_internal_(variable_name, variable_name_to_send, state_value);
|
||||
}
|
||||
|
||||
void Nextion::add_no_result_to_queue_with_set_internal_(const std::string &variable_name,
|
||||
const std::string &variable_name_to_send, int state_value,
|
||||
bool is_sleep_safe) {
|
||||
if ((!this->is_setup() && !this->ignore_is_setup_) || (!is_sleep_safe && this->is_sleeping()))
|
||||
return;
|
||||
|
||||
this->add_no_result_to_queue_with_ignore_sleep_printf_(variable_name, "%s=%d", variable_name_to_send.c_str(),
|
||||
state_value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
*
|
||||
* @param variable_name Variable name for the queue
|
||||
* @param variable_name_to_send Variable name for the left of the command
|
||||
* @param state_value Sting value to set
|
||||
* @param is_sleep_safe The command is safe to send when the Nextion is sleeping
|
||||
*/
|
||||
void Nextion::add_no_result_to_queue_with_set(NextionComponentBase *component, const std::string &state_value) {
|
||||
this->add_no_result_to_queue_with_set(component->get_variable_name(), component->get_variable_name_to_send(),
|
||||
state_value);
|
||||
}
|
||||
void Nextion::add_no_result_to_queue_with_set(const std::string &variable_name,
|
||||
const std::string &variable_name_to_send,
|
||||
const std::string &state_value) {
|
||||
this->add_no_result_to_queue_with_set_internal_(variable_name, variable_name_to_send, state_value);
|
||||
}
|
||||
|
||||
void Nextion::add_no_result_to_queue_with_set_internal_(const std::string &variable_name,
|
||||
const std::string &variable_name_to_send,
|
||||
const std::string &state_value, bool is_sleep_safe) {
|
||||
if ((!this->is_setup() && !this->ignore_is_setup_) || (!is_sleep_safe && this->is_sleeping()))
|
||||
return;
|
||||
|
||||
this->add_no_result_to_queue_with_printf_(variable_name, "%s=\"%s\"", variable_name_to_send.c_str(),
|
||||
state_value.c_str());
|
||||
}
|
||||
|
||||
void Nextion::add_to_get_queue(NextionComponentBase *component) {
|
||||
if ((!this->is_setup() && !this->ignore_is_setup_))
|
||||
return;
|
||||
|
||||
nextion::NextionQueue *nextion_queue = new nextion::NextionQueue;
|
||||
|
||||
nextion_queue->component = component;
|
||||
nextion_queue->queue_time = millis();
|
||||
|
||||
ESP_LOGN(TAG, "Add to queue type: %s component %s", component->get_queue_type_string().c_str(),
|
||||
component->get_variable_name().c_str());
|
||||
|
||||
std::string command = "get " + component->get_variable_name_to_send();
|
||||
|
||||
if (this->send_command_(command)) {
|
||||
this->nextion_queue_.push_back(nextion_queue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add addt command to the queue
|
||||
*
|
||||
* @param component_id The waveform component id
|
||||
* @param wave_chan_id The waveform channel to send it to
|
||||
* @param buffer_to_send The buffer size
|
||||
* @param buffer_size The buffer data
|
||||
*/
|
||||
void Nextion::add_addt_command_to_queue(NextionComponentBase *component) {
|
||||
if ((!this->is_setup() && !this->ignore_is_setup_) || this->is_sleeping())
|
||||
return;
|
||||
|
||||
nextion::NextionQueue *nextion_queue = new nextion::NextionQueue;
|
||||
|
||||
nextion_queue->component = new nextion::NextionComponentBase;
|
||||
nextion_queue->queue_time = millis();
|
||||
|
||||
size_t buffer_to_send = component->get_wave_buffer_size() < 255 ? component->get_wave_buffer_size()
|
||||
: 255; // ADDT command can only send 255
|
||||
|
||||
std::string command = "addt " + to_string(component->get_component_id()) + "," +
|
||||
to_string(component->get_wave_channel_id()) + "," + to_string(buffer_to_send);
|
||||
if (this->send_command_(command)) {
|
||||
this->nextion_queue_.push_back(nextion_queue);
|
||||
}
|
||||
}
|
||||
|
||||
void Nextion::set_writer(const nextion_writer_t &writer) { this->writer_ = writer; }
|
||||
|
||||
ESPDEPRECATED("set_wait_for_ack(bool) is deprecated and has no effect")
|
||||
void Nextion::set_wait_for_ack(bool wait_for_ack) { ESP_LOGE(TAG, "This command is depreciated"); }
|
||||
|
||||
} // namespace nextion
|
||||
} // namespace esphome
|
||||
|
||||
@ -1,9 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include <deque>
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/components/uart/uart.h"
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
#include "nextion_base.h"
|
||||
#include "nextion_component.h"
|
||||
#include "esphome/components/display/display_color_utils.h"
|
||||
|
||||
#if defined(USE_ETHERNET) || defined(USE_WIFI)
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include <HTTPClient.h>
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
#include <ESP8266HTTPClient.h>
|
||||
#include <WiFiClientSecure.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef USE_TIME
|
||||
#include "esphome/components/time/real_time_clock.h"
|
||||
@ -12,12 +24,14 @@
|
||||
namespace esphome {
|
||||
namespace nextion {
|
||||
|
||||
class NextionTouchComponent;
|
||||
class Nextion;
|
||||
class NextionComponentBase;
|
||||
|
||||
using nextion_writer_t = std::function<void(Nextion &)>;
|
||||
|
||||
class Nextion : public PollingComponent, public uart::UARTDevice {
|
||||
static const std::string COMMAND_DELIMITER{static_cast<char>(255), static_cast<char>(255), static_cast<char>(255)};
|
||||
|
||||
class Nextion : public NextionBase, public PollingComponent, public uart::UARTDevice {
|
||||
public:
|
||||
/**
|
||||
* Set the text of a component to a static string.
|
||||
@ -73,9 +87,20 @@ class Nextion : public PollingComponent, public uart::UARTDevice {
|
||||
*
|
||||
* This will change the image of the component `pic` to the image with ID `4`.
|
||||
*/
|
||||
void set_component_picture(const char *component, const char *picture) {
|
||||
this->send_command_printf("%s.val=%s", component, picture);
|
||||
}
|
||||
void set_component_picture(const char *component, const char *picture);
|
||||
/**
|
||||
* Set the background color of a component.
|
||||
* @param component The component name.
|
||||
* @param color The color (as a uint32_t).
|
||||
*
|
||||
* Example:
|
||||
* ```cpp
|
||||
* it.set_component_background_color("button", 0xFF0000);
|
||||
* ```
|
||||
*
|
||||
* This will change the background color of the component `button` to red.
|
||||
*/
|
||||
void set_component_background_color(const char *component, uint32_t color);
|
||||
/**
|
||||
* Set the background color of a component.
|
||||
* @param component The component name.
|
||||
@ -83,7 +108,7 @@ class Nextion : public PollingComponent, public uart::UARTDevice {
|
||||
*
|
||||
* Example:
|
||||
* ```cpp
|
||||
* it.set_component_background_color("button", "17013");
|
||||
* it.set_component_background_color("button", "RED");
|
||||
* ```
|
||||
*
|
||||
* This will change the background color of the component `button` to blue.
|
||||
@ -91,6 +116,33 @@ class Nextion : public PollingComponent, public uart::UARTDevice {
|
||||
* Nextion HMI colors.
|
||||
*/
|
||||
void set_component_background_color(const char *component, const char *color);
|
||||
/**
|
||||
* Set the background color of a component.
|
||||
* @param component The component name.
|
||||
* @param color The color (as Color).
|
||||
*
|
||||
* Example:
|
||||
* ```cpp
|
||||
* it.set_component_background_color("button", color);
|
||||
* ```
|
||||
*
|
||||
* This will change the background color of the component `button` to what color contains.
|
||||
*/
|
||||
void set_component_background_color(const char *component, Color color) override;
|
||||
/**
|
||||
* Set the pressed background color of a component.
|
||||
* @param component The component name.
|
||||
* @param color The color (as a int).
|
||||
*
|
||||
* Example:
|
||||
* ```cpp
|
||||
* it.set_component_pressed_background_color("button", 0xFF0000 );
|
||||
* ```
|
||||
*
|
||||
* This will change the pressed background color of the component `button` to red. This is the background color that
|
||||
* is shown when the component is pressed.
|
||||
*/
|
||||
void set_component_pressed_background_color(const char *component, uint32_t color);
|
||||
/**
|
||||
* Set the pressed background color of a component.
|
||||
* @param component The component name.
|
||||
@ -98,7 +150,7 @@ class Nextion : public PollingComponent, public uart::UARTDevice {
|
||||
*
|
||||
* Example:
|
||||
* ```cpp
|
||||
* it.set_component_pressed_background_color("button", "17013");
|
||||
* it.set_component_pressed_background_color("button", "RED");
|
||||
* ```
|
||||
*
|
||||
* This will change the pressed background color of the component `button` to blue. This is the background color that
|
||||
@ -107,6 +159,63 @@ class Nextion : public PollingComponent, public uart::UARTDevice {
|
||||
* colors.
|
||||
*/
|
||||
void set_component_pressed_background_color(const char *component, const char *color);
|
||||
/**
|
||||
* Set the pressed background color of a component.
|
||||
* @param component The component name.
|
||||
* @param color The color (as Color).
|
||||
*
|
||||
* Example:
|
||||
* ```cpp
|
||||
* it.set_component_pressed_background_color("button", color);
|
||||
* ```
|
||||
*
|
||||
* This will change the pressed background color of the component `button` to blue. This is the background color that
|
||||
* is shown when the component is pressed. Use this [color
|
||||
* picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to Nextion HMI
|
||||
* colors.
|
||||
*/
|
||||
void set_component_pressed_background_color(const char *component, Color color) override;
|
||||
|
||||
/**
|
||||
* Set the picture id of a component.
|
||||
* @param component The component name.
|
||||
* @param pic_id The picture ID.
|
||||
*
|
||||
* Example:
|
||||
* ```cpp
|
||||
* it.set_component_pic("textview", 1);
|
||||
* ```
|
||||
*
|
||||
* This will change the picture id of the component `textview`.
|
||||
*/
|
||||
void set_component_pic(const char *component, uint8_t pic_id);
|
||||
/**
|
||||
* Set the background picture id of component.
|
||||
* @param component The component name.
|
||||
* @param pic_id The picture ID.
|
||||
*
|
||||
* Example:
|
||||
* ```cpp
|
||||
* it.set_component_picc("textview", 1);
|
||||
* ```
|
||||
*
|
||||
* This will change the background picture id of the component `textview`.
|
||||
*/
|
||||
void set_component_picc(const char *component, uint8_t pic_id);
|
||||
|
||||
/**
|
||||
* Set the font color of a component.
|
||||
* @param component The component name.
|
||||
* @param color The color (as a uint32_t ).
|
||||
*
|
||||
* Example:
|
||||
* ```cpp
|
||||
* it.set_component_font_color("textview", 0xFF0000);
|
||||
* ```
|
||||
*
|
||||
* This will change the font color of the component `textview` to a red color.
|
||||
*/
|
||||
void set_component_font_color(const char *component, uint32_t color);
|
||||
/**
|
||||
* Set the font color of a component.
|
||||
* @param component The component name.
|
||||
@ -114,7 +223,7 @@ class Nextion : public PollingComponent, public uart::UARTDevice {
|
||||
*
|
||||
* Example:
|
||||
* ```cpp
|
||||
* it.set_component_font_color("textview", "17013");
|
||||
* it.set_component_font_color("textview", "RED");
|
||||
* ```
|
||||
*
|
||||
* This will change the font color of the component `textview` to a blue color.
|
||||
@ -122,6 +231,34 @@ class Nextion : public PollingComponent, public uart::UARTDevice {
|
||||
* Nextion HMI colors.
|
||||
*/
|
||||
void set_component_font_color(const char *component, const char *color);
|
||||
/**
|
||||
* Set the font color of a component.
|
||||
* @param component The component name.
|
||||
* @param color The color (as Color).
|
||||
*
|
||||
* Example:
|
||||
* ```cpp
|
||||
* it.set_component_font_color("textview", color);
|
||||
* ```
|
||||
*
|
||||
* This will change the font color of the component `textview` to a blue color.
|
||||
* Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to
|
||||
* Nextion HMI colors.
|
||||
*/
|
||||
void set_component_font_color(const char *component, Color color) override;
|
||||
/**
|
||||
* Set the pressed font color of a component.
|
||||
* @param component The component name.
|
||||
* @param color The color (as a uint32_t).
|
||||
*
|
||||
* Example:
|
||||
* ```cpp
|
||||
* it.set_component_pressed_font_color("button", 0xFF0000);
|
||||
* ```
|
||||
*
|
||||
* This will change the pressed font color of the component `button` to a red.
|
||||
*/
|
||||
void set_component_pressed_font_color(const char *component, uint32_t color);
|
||||
/**
|
||||
* Set the pressed font color of a component.
|
||||
* @param component The component name.
|
||||
@ -129,7 +266,7 @@ class Nextion : public PollingComponent, public uart::UARTDevice {
|
||||
*
|
||||
* Example:
|
||||
* ```cpp
|
||||
* it.set_component_pressed_font_color("button", "17013");
|
||||
* it.set_component_pressed_font_color("button", "RED");
|
||||
* ```
|
||||
*
|
||||
* This will change the pressed font color of the component `button` to a blue color.
|
||||
@ -137,6 +274,21 @@ class Nextion : public PollingComponent, public uart::UARTDevice {
|
||||
* Nextion HMI colors.
|
||||
*/
|
||||
void set_component_pressed_font_color(const char *component, const char *color);
|
||||
/**
|
||||
* Set the pressed font color of a component.
|
||||
* @param component The component name.
|
||||
* @param color The color (as Color).
|
||||
*
|
||||
* Example:
|
||||
* ```cpp
|
||||
* it.set_component_pressed_font_color("button", color);
|
||||
* ```
|
||||
*
|
||||
* This will change the pressed font color of the component `button` to a blue color.
|
||||
* Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to
|
||||
* Nextion HMI colors.
|
||||
*/
|
||||
void set_component_pressed_font_color(const char *component, Color color) override;
|
||||
/**
|
||||
* Set the coordinates of a component on screen.
|
||||
* @param component The component name.
|
||||
@ -163,7 +315,7 @@ class Nextion : public PollingComponent, public uart::UARTDevice {
|
||||
*
|
||||
* Changes the font of the component named `textveiw`. Font IDs are set in the Nextion Editor.
|
||||
*/
|
||||
void set_component_font(const char *component, uint8_t font_id);
|
||||
void set_component_font(const char *component, uint8_t font_id) override;
|
||||
#ifdef USE_TIME
|
||||
/**
|
||||
* Send the current time to the nextion display.
|
||||
@ -195,7 +347,7 @@ class Nextion : public PollingComponent, public uart::UARTDevice {
|
||||
*
|
||||
* Hides the component named `button`.
|
||||
*/
|
||||
void hide_component(const char *component);
|
||||
void hide_component(const char *component) override;
|
||||
/**
|
||||
* Show a component.
|
||||
* @param component The component name.
|
||||
@ -207,7 +359,7 @@ class Nextion : public PollingComponent, public uart::UARTDevice {
|
||||
*
|
||||
* Shows the component named `button`.
|
||||
*/
|
||||
void show_component(const char *component);
|
||||
void show_component(const char *component) override;
|
||||
/**
|
||||
* Enable touch for a component.
|
||||
* @param component The component name.
|
||||
@ -239,6 +391,7 @@ class Nextion : public PollingComponent, public uart::UARTDevice {
|
||||
* @param value The value to write.
|
||||
*/
|
||||
void add_waveform_data(int component_id, uint8_t channel_number, uint8_t value);
|
||||
void open_waveform_channel(int component_id, uint8_t channel_number, uint8_t value);
|
||||
/**
|
||||
* Display a picture at coordinates.
|
||||
* @param picture_id The picture id.
|
||||
@ -263,7 +416,7 @@ class Nextion : public PollingComponent, public uart::UARTDevice {
|
||||
*
|
||||
* Example:
|
||||
* ```cpp
|
||||
* fill_area(50, 50, 100, 100, "17013");
|
||||
* fill_area(50, 50, 100, 100, "RED");
|
||||
* ```
|
||||
*
|
||||
* Fills an area that starts at x coordiante `50` and y coordinate `50` with a height of `100` and width of `100` with
|
||||
@ -271,6 +424,24 @@ class Nextion : public PollingComponent, public uart::UARTDevice {
|
||||
* convert color codes to Nextion HMI colors
|
||||
*/
|
||||
void fill_area(int x1, int y1, int width, int height, const char *color);
|
||||
/**
|
||||
* Fill a rectangle with a color.
|
||||
* @param x1 The starting x coordinate.
|
||||
* @param y1 The starting y coordinate.
|
||||
* @param width The width to draw.
|
||||
* @param height The height to draw.
|
||||
* @param color The color to draw with (as Color).
|
||||
*
|
||||
* Example:
|
||||
* ```cpp
|
||||
* fill_area(50, 50, 100, 100, color);
|
||||
* ```
|
||||
*
|
||||
* Fills an area that starts at x coordiante `50` and y coordinate `50` with a height of `100` and width of `100` with
|
||||
* the color of blue. Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to
|
||||
* convert color codes to Nextion HMI colors
|
||||
*/
|
||||
void fill_area(int x1, int y1, int width, int height, Color color);
|
||||
/**
|
||||
* Draw a line on the screen.
|
||||
* @param x1 The starting x coordinate.
|
||||
@ -290,6 +461,25 @@ class Nextion : public PollingComponent, public uart::UARTDevice {
|
||||
* colors.
|
||||
*/
|
||||
void line(int x1, int y1, int x2, int y2, const char *color);
|
||||
/**
|
||||
* Draw a line on the screen.
|
||||
* @param x1 The starting x coordinate.
|
||||
* @param y1 The starting y coordinate.
|
||||
* @param x2 The ending x coordinate.
|
||||
* @param y2 The ending y coordinate.
|
||||
* @param color The color to draw with (as Color).
|
||||
*
|
||||
* Example:
|
||||
* ```cpp
|
||||
* it.line(50, 50, 75, 75, "17013");
|
||||
* ```
|
||||
*
|
||||
* Makes a line that starts at x coordinate `50` and y coordinate `50` and ends at x coordinate `75` and y coordinate
|
||||
* `75` with the color of blue. Use this [color
|
||||
* picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to Nextion HMI
|
||||
* colors.
|
||||
*/
|
||||
void line(int x1, int y1, int x2, int y2, Color color);
|
||||
/**
|
||||
* Draw a rectangle outline.
|
||||
* @param x1 The starting x coordinate.
|
||||
@ -309,6 +499,25 @@ class Nextion : public PollingComponent, public uart::UARTDevice {
|
||||
* colors.
|
||||
*/
|
||||
void rectangle(int x1, int y1, int width, int height, const char *color);
|
||||
/**
|
||||
* Draw a rectangle outline.
|
||||
* @param x1 The starting x coordinate.
|
||||
* @param y1 The starting y coordinate.
|
||||
* @param width The width of the rectangle.
|
||||
* @param height The height of the rectangle.
|
||||
* @param color The color to draw with (as Color).
|
||||
*
|
||||
* Example:
|
||||
* ```cpp
|
||||
* it.rectangle(25, 35, 40, 50, "17013");
|
||||
* ```
|
||||
*
|
||||
* Makes a outline of a rectangle that starts at x coordinate `25` and y coordinate `35` and has a width of `40` and a
|
||||
* length of `50` with color of blue. Use this [color
|
||||
* picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to Nextion HMI
|
||||
* colors.
|
||||
*/
|
||||
void rectangle(int x1, int y1, int width, int height, Color color);
|
||||
/**
|
||||
* Draw a circle outline
|
||||
* @param center_x The center x coordinate.
|
||||
@ -317,6 +526,14 @@ class Nextion : public PollingComponent, public uart::UARTDevice {
|
||||
* @param color The color to draw with (as a string).
|
||||
*/
|
||||
void circle(int center_x, int center_y, int radius, const char *color);
|
||||
/**
|
||||
* Draw a circle outline
|
||||
* @param center_x The center x coordinate.
|
||||
* @param center_y The center y coordinate.
|
||||
* @param radius The circle radius.
|
||||
* @param color The color to draw with (as Color).
|
||||
*/
|
||||
void circle(int center_x, int center_y, int radius, Color color);
|
||||
/**
|
||||
* Draw a filled circled.
|
||||
* @param center_x The center x coordinate.
|
||||
@ -334,19 +551,36 @@ class Nextion : public PollingComponent, public uart::UARTDevice {
|
||||
* Nextion HMI colors.
|
||||
*/
|
||||
void filled_circle(int center_x, int center_y, int radius, const char *color);
|
||||
|
||||
/** Set the brightness of the backlight.
|
||||
*
|
||||
* @param brightness The brightness, from 0 to 100.
|
||||
/**
|
||||
* Draw a filled circled.
|
||||
* @param center_x The center x coordinate.
|
||||
* @param center_y The center y coordinate.
|
||||
* @param radius The circle radius.
|
||||
* @param color The color to draw with (as Color).
|
||||
*
|
||||
* Example:
|
||||
* ```cpp
|
||||
* it.set_backlight_brightness(30);
|
||||
* it.filled_cricle(25, 25, 10, color);
|
||||
* ```
|
||||
*
|
||||
* Makes a filled circle at the x cordinates `25` and y coordinate `25` with a radius of `10` with a color of blue.
|
||||
* Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to
|
||||
* Nextion HMI colors.
|
||||
*/
|
||||
void filled_circle(int center_x, int center_y, int radius, Color color);
|
||||
|
||||
/** Set the brightness of the backlight.
|
||||
*
|
||||
* @param brightness The brightness percentage from 0 to 1.0.
|
||||
*
|
||||
* Example:
|
||||
* ```cpp
|
||||
* it.set_backlight_brightness(.3);
|
||||
* ```
|
||||
*
|
||||
* Changes the brightness of the display to 30%.
|
||||
*/
|
||||
void set_backlight_brightness(uint8_t brightness);
|
||||
void set_backlight_brightness(float brightness);
|
||||
/**
|
||||
* Set the touch sleep timeout of the display.
|
||||
* @param timeout Timeout in seconds.
|
||||
@ -360,10 +594,46 @@ class Nextion : public PollingComponent, public uart::UARTDevice {
|
||||
* `thup`.
|
||||
*/
|
||||
void set_touch_sleep_timeout(uint16_t timeout);
|
||||
/**
|
||||
* Sets which page Nextion loads when exiting sleep mode. Note this can be set even when Nextion is in sleep mode.
|
||||
* @param page_id The page id, from 0 to the lage page in Nextion. Set 255 (not set to any existing page) to
|
||||
* wakes up to current page.
|
||||
*
|
||||
* Example:
|
||||
* ```cpp
|
||||
* it.set_wake_up_page(2);
|
||||
* ```
|
||||
*
|
||||
* The display will wake up to page 2.
|
||||
*/
|
||||
void set_wake_up_page(uint8_t page_id = 255);
|
||||
/**
|
||||
* Sets if Nextion should auto-wake from sleep when touch press occurs.
|
||||
* @param auto_wake True or false. When auto_wake is true and Nextion is in sleep mode,
|
||||
* the first touch will only trigger the auto wake mode and not trigger a Touch Event.
|
||||
*
|
||||
* Example:
|
||||
* ```cpp
|
||||
* it.set_auto_wake_on_touch(true);
|
||||
* ```
|
||||
*
|
||||
* The display will wake up by touch.
|
||||
*/
|
||||
void set_auto_wake_on_touch(bool auto_wake);
|
||||
/**
|
||||
* Sets Nextion mode between sleep and awake
|
||||
* @param True or false. Sleep=true to enter sleep mode or sleep=false to exit sleep mode.
|
||||
*/
|
||||
void sleep(bool sleep);
|
||||
|
||||
// ========== INTERNAL METHODS ==========
|
||||
// (In most use cases you won't need these)
|
||||
void register_touch_component(NextionTouchComponent *obj) { this->touch_.push_back(obj); }
|
||||
void register_touch_component(NextionComponentBase *obj) { this->touch_.push_back(obj); }
|
||||
void register_switch_component(NextionComponentBase *obj) { this->switchtype_.push_back(obj); }
|
||||
void register_binarysensor_component(NextionComponentBase *obj) { this->binarysensortype_.push_back(obj); }
|
||||
void register_sensor_component(NextionComponentBase *obj) { this->sensortype_.push_back(obj); }
|
||||
void register_textsensor_component(NextionComponentBase *obj) { this->textsensortype_.push_back(obj); }
|
||||
|
||||
void setup() override;
|
||||
void set_brightness(float brightness) { this->brightness_ = brightness; }
|
||||
float get_setup_priority() const override;
|
||||
@ -371,11 +641,9 @@ class Nextion : public PollingComponent, public uart::UARTDevice {
|
||||
void loop() override;
|
||||
void set_writer(const nextion_writer_t &writer);
|
||||
|
||||
/**
|
||||
* Manually send a raw command to the display and don't wait for an acknowledgement packet.
|
||||
* @param command The command to write, for example "vis b0,0".
|
||||
*/
|
||||
void send_command_no_ack(const char *command);
|
||||
// This function has been deprecated
|
||||
void set_wait_for_ack(bool wait_for_ack);
|
||||
|
||||
/**
|
||||
* Manually send a raw formatted command to the display.
|
||||
* @param format The printf-style command format, like "vis %s,0"
|
||||
@ -384,28 +652,199 @@ class Nextion : public PollingComponent, public uart::UARTDevice {
|
||||
*/
|
||||
bool send_command_printf(const char *format, ...) __attribute__((format(printf, 2, 3)));
|
||||
|
||||
void set_wait_for_ack(bool wait_for_ack);
|
||||
#ifdef USE_TFT_UPLOAD
|
||||
/**
|
||||
* Set the tft file URL. https seems problamtic with arduino..
|
||||
*/
|
||||
void set_tft_url(const std::string &tft_url) { this->tft_url_ = tft_url; }
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Upload the tft file and softreset the Nextion
|
||||
*/
|
||||
void upload_tft();
|
||||
void dump_config() override;
|
||||
|
||||
/**
|
||||
* Softreset the Nextion
|
||||
*/
|
||||
void soft_reset();
|
||||
|
||||
/** Add a callback to be notified of sleep state changes.
|
||||
*
|
||||
* @param callback The void() callback.
|
||||
*/
|
||||
void add_sleep_state_callback(std::function<void()> &&callback);
|
||||
|
||||
/** Add a callback to be notified of wake state changes.
|
||||
*
|
||||
* @param callback The void() callback.
|
||||
*/
|
||||
void add_wake_state_callback(std::function<void()> &&callback);
|
||||
|
||||
/** Add a callback to be notified when the nextion completes its initialize setup.
|
||||
*
|
||||
* @param callback The void() callback.
|
||||
*/
|
||||
void add_setup_state_callback(std::function<void()> &&callback);
|
||||
|
||||
void update_all_components();
|
||||
|
||||
/**
|
||||
* @brief Set the nextion sensor state object.
|
||||
*
|
||||
* @param[in] queue_type
|
||||
* Index of NextionQueueType.
|
||||
*
|
||||
* @param[in] name
|
||||
* Component/variable name.
|
||||
*
|
||||
* @param[in] state
|
||||
* State to set.
|
||||
*/
|
||||
void set_nextion_sensor_state(int queue_type, const std::string &name, float state);
|
||||
void set_nextion_sensor_state(NextionQueueType queue_type, const std::string &name, float state);
|
||||
void set_nextion_text_state(const std::string &name, const std::string &state);
|
||||
|
||||
void add_no_result_to_queue_with_set(NextionComponentBase *component, int state_value) override;
|
||||
void add_no_result_to_queue_with_set(const std::string &variable_name, const std::string &variable_name_to_send,
|
||||
int state_value) override;
|
||||
|
||||
void add_no_result_to_queue_with_set(NextionComponentBase *component, const std::string &state_value) override;
|
||||
void add_no_result_to_queue_with_set(const std::string &variable_name, const std::string &variable_name_to_send,
|
||||
const std::string &state_value) override;
|
||||
|
||||
void add_to_get_queue(NextionComponentBase *component) override;
|
||||
|
||||
void add_addt_command_to_queue(NextionComponentBase *component) override;
|
||||
|
||||
void update_components_by_prefix(const std::string &prefix);
|
||||
|
||||
void set_touch_sleep_timeout_internal(uint32_t touch_sleep_timeout) {
|
||||
this->touch_sleep_timeout_ = touch_sleep_timeout;
|
||||
}
|
||||
void set_wake_up_page_internal(uint8_t wake_up_page) { this->wake_up_page_ = wake_up_page; }
|
||||
void set_auto_wake_on_touch_internal(bool auto_wake_on_touch) { this->auto_wake_on_touch_ = auto_wake_on_touch; }
|
||||
|
||||
protected:
|
||||
bool ack_();
|
||||
bool read_until_ack_();
|
||||
std::deque<NextionQueue *> nextion_queue_;
|
||||
uint16_t recv_ret_string_(std::string &response, uint32_t timeout, bool recv_flag);
|
||||
void all_components_send_state_(bool force_update = false);
|
||||
uint64_t comok_sent_ = 0;
|
||||
bool remove_from_q_(bool report_empty = true);
|
||||
/**
|
||||
* @brief
|
||||
* Sends commands ignoring of the Nextion has been setup.
|
||||
*/
|
||||
bool ignore_is_setup_ = false;
|
||||
bool nextion_reports_is_setup_ = false;
|
||||
uint8_t nextion_event_;
|
||||
|
||||
void process_nextion_commands_();
|
||||
void process_serial_();
|
||||
bool is_updating_ = false;
|
||||
uint32_t touch_sleep_timeout_ = 0;
|
||||
int wake_up_page_ = -1;
|
||||
bool auto_wake_on_touch_ = true;
|
||||
|
||||
/**
|
||||
* Manually send a raw command to the display and don't wait for an acknowledgement packet.
|
||||
* @param command The command to write, for example "vis b0,0".
|
||||
*/
|
||||
bool send_command_(const std::string &command);
|
||||
void add_no_result_to_queue_(const std::string &variable_name);
|
||||
bool add_no_result_to_queue_with_ignore_sleep_printf_(const std::string &variable_name, const char *format, ...)
|
||||
__attribute__((format(printf, 3, 4)));
|
||||
void add_no_result_to_queue_with_command_(const std::string &variable_name, const std::string &command);
|
||||
|
||||
bool add_no_result_to_queue_with_printf_(const std::string &variable_name, const char *format, ...)
|
||||
__attribute__((format(printf, 3, 4)));
|
||||
|
||||
void add_no_result_to_queue_with_set_internal_(const std::string &variable_name,
|
||||
const std::string &variable_name_to_send, int state_value,
|
||||
bool is_sleep_safe = false);
|
||||
|
||||
void add_no_result_to_queue_with_set_internal_(const std::string &variable_name,
|
||||
const std::string &variable_name_to_send,
|
||||
const std::string &state_value, bool is_sleep_safe = false);
|
||||
|
||||
#ifdef USE_TFT_UPLOAD
|
||||
#if defined(USE_ETHERNET) || defined(USE_WIFI)
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
WiFiClient *wifi_client_{nullptr};
|
||||
BearSSL::WiFiClientSecure *wifi_client_secure_{nullptr};
|
||||
WiFiClient *get_wifi_client_();
|
||||
#endif
|
||||
|
||||
/**
|
||||
* will request chunk_size chunks from the web server
|
||||
* and send each to the nextion
|
||||
* @param int contentLength Total size of the file
|
||||
* @param uint32_t chunk_size
|
||||
* @return true if success, false for failure.
|
||||
*/
|
||||
int content_length_ = 0;
|
||||
int tft_size_ = 0;
|
||||
int upload_by_chunks_(HTTPClient *http, int range_start);
|
||||
|
||||
bool upload_with_range_(uint32_t range_start, uint32_t range_end);
|
||||
|
||||
/**
|
||||
* start update tft file to nextion.
|
||||
*
|
||||
* @param const uint8_t *file_buf
|
||||
* @param size_t buf_size
|
||||
* @return true if success, false for failure.
|
||||
*/
|
||||
bool upload_from_buffer_(const uint8_t *file_buf, size_t buf_size);
|
||||
void upload_end_();
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
bool get_is_connected_() { return this->is_connected_; }
|
||||
|
||||
bool check_connect_();
|
||||
|
||||
std::vector<NextionComponentBase *> touch_;
|
||||
std::vector<NextionComponentBase *> switchtype_;
|
||||
std::vector<NextionComponentBase *> sensortype_;
|
||||
std::vector<NextionComponentBase *> textsensortype_;
|
||||
std::vector<NextionComponentBase *> binarysensortype_;
|
||||
CallbackManager<void()> setup_callback_{};
|
||||
CallbackManager<void()> sleep_callback_{};
|
||||
CallbackManager<void()> wake_callback_{};
|
||||
|
||||
std::vector<NextionTouchComponent *> touch_;
|
||||
optional<nextion_writer_t> writer_;
|
||||
bool wait_for_ack_{true};
|
||||
float brightness_{1.0};
|
||||
|
||||
std::string device_model_;
|
||||
std::string firmware_version_;
|
||||
std::string serial_number_;
|
||||
std::string flash_size_;
|
||||
|
||||
void remove_front_no_sensors_();
|
||||
|
||||
#ifdef USE_TFT_UPLOAD
|
||||
std::string tft_url_;
|
||||
uint8_t *transfer_buffer_{nullptr};
|
||||
size_t transfer_buffer_size_;
|
||||
bool upload_first_chunk_sent_ = false;
|
||||
#endif
|
||||
|
||||
#ifdef NEXTION_PROTOCOL_LOG
|
||||
void print_queue_members_();
|
||||
#endif
|
||||
void reset_(bool reset_nextion = true);
|
||||
|
||||
std::string command_data_;
|
||||
bool is_connected_ = false;
|
||||
uint32_t startup_override_ms_ = 8000;
|
||||
uint32_t max_q_age_ms_ = 8000;
|
||||
uint32_t started_ms_ = 0;
|
||||
bool sent_setup_commands_ = false;
|
||||
};
|
||||
|
||||
class NextionTouchComponent : public binary_sensor::BinarySensorInitiallyOff {
|
||||
public:
|
||||
void set_page_id(uint8_t page_id) { page_id_ = page_id; }
|
||||
void set_component_id(uint8_t component_id) { component_id_ = component_id; }
|
||||
void process(uint8_t page_id, uint8_t component_id, bool on);
|
||||
|
||||
protected:
|
||||
uint8_t page_id_;
|
||||
uint8_t component_id_;
|
||||
};
|
||||
|
||||
} // namespace nextion
|
||||
} // namespace esphome
|
||||
|
||||
58
esphome/components/nextion/nextion_base.h
Normal file
58
esphome/components/nextion/nextion_base.h
Normal file
@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/color.h"
|
||||
#include "nextion_component_base.h"
|
||||
namespace esphome {
|
||||
namespace nextion {
|
||||
|
||||
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
|
||||
#define NEXTION_PROTOCOL_LOG
|
||||
#endif
|
||||
|
||||
#ifdef NEXTION_PROTOCOL_LOG
|
||||
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
|
||||
#define ESP_LOGN(tag, ...) ESP_LOGVV(tag, __VA_ARGS__)
|
||||
#else
|
||||
#define ESP_LOGN(tag, ...) ESP_LOGD(tag, __VA_ARGS__)
|
||||
#endif
|
||||
#else
|
||||
#define ESP_LOGN(tag, ...) \
|
||||
{}
|
||||
#endif
|
||||
|
||||
class NextionBase;
|
||||
|
||||
class NextionBase {
|
||||
public:
|
||||
virtual void add_no_result_to_queue_with_set(NextionComponentBase *component, int state_value) = 0;
|
||||
virtual void add_no_result_to_queue_with_set(const std::string &variable_name,
|
||||
const std::string &variable_name_to_send, int state_value) = 0;
|
||||
|
||||
virtual void add_no_result_to_queue_with_set(NextionComponentBase *component, const std::string &state_value) = 0;
|
||||
virtual void add_no_result_to_queue_with_set(const std::string &variable_name,
|
||||
const std::string &variable_name_to_send,
|
||||
const std::string &state_value) = 0;
|
||||
|
||||
virtual void add_addt_command_to_queue(NextionComponentBase *component) = 0;
|
||||
|
||||
virtual void add_to_get_queue(NextionComponentBase *component) = 0;
|
||||
|
||||
virtual void set_component_background_color(const char *component, Color color) = 0;
|
||||
virtual void set_component_pressed_background_color(const char *component, Color color) = 0;
|
||||
virtual void set_component_font_color(const char *component, Color color) = 0;
|
||||
virtual void set_component_pressed_font_color(const char *component, Color color) = 0;
|
||||
virtual void set_component_font(const char *component, uint8_t font_id) = 0;
|
||||
|
||||
virtual void show_component(const char *component) = 0;
|
||||
virtual void hide_component(const char *component) = 0;
|
||||
|
||||
bool is_sleeping() { return this->is_sleeping_; }
|
||||
bool is_setup() { return this->is_setup_; }
|
||||
|
||||
protected:
|
||||
bool is_setup_ = false;
|
||||
bool is_sleeping_ = false;
|
||||
};
|
||||
|
||||
} // namespace nextion
|
||||
} // namespace esphome
|
||||
234
esphome/components/nextion/nextion_commands.cpp
Normal file
234
esphome/components/nextion/nextion_commands.cpp
Normal file
@ -0,0 +1,234 @@
|
||||
#include "nextion.h"
|
||||
#include "esphome/core/util.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace nextion {
|
||||
static const char *const TAG = "nextion";
|
||||
|
||||
// Sleep safe commands
|
||||
void Nextion::soft_reset() { this->send_command_("rest"); }
|
||||
|
||||
void Nextion::set_wake_up_page(uint8_t page_id) {
|
||||
if (page_id > 255) {
|
||||
ESP_LOGD(TAG, "Wake up page of bounds, range 0-255");
|
||||
return;
|
||||
}
|
||||
this->add_no_result_to_queue_with_set_internal_("wake_up_page", "wup", page_id, true);
|
||||
}
|
||||
|
||||
void Nextion::set_touch_sleep_timeout(uint16_t timeout) {
|
||||
if (timeout < 3 || timeout > 65535) {
|
||||
ESP_LOGD(TAG, "Sleep timeout out of bounds, range 3-65535");
|
||||
return;
|
||||
}
|
||||
|
||||
this->add_no_result_to_queue_with_set_internal_("touch_sleep_timeout", "thsp", timeout, true);
|
||||
}
|
||||
|
||||
void Nextion::sleep(bool sleep) {
|
||||
if (sleep) { // Set sleep
|
||||
this->is_sleeping_ = true;
|
||||
this->add_no_result_to_queue_with_set_internal_("sleep", "sleep", 1, true);
|
||||
} else { // Turn off sleep. Wait for a sleep_wake return before setting sleep off
|
||||
this->add_no_result_to_queue_with_set_internal_("sleep_wake", "sleep", 0, true);
|
||||
}
|
||||
}
|
||||
// End sleep safe commands
|
||||
|
||||
// Set Colors
|
||||
void Nextion::set_component_background_color(const char *component, uint32_t color) {
|
||||
this->add_no_result_to_queue_with_printf_("set_component_background_color", "%s.bco=%d", component, color);
|
||||
}
|
||||
|
||||
void Nextion::set_component_background_color(const char *component, const char *color) {
|
||||
this->add_no_result_to_queue_with_printf_("set_component_background_color", "%s.bco=%s", component, color);
|
||||
}
|
||||
|
||||
void Nextion::set_component_background_color(const char *component, Color color) {
|
||||
this->add_no_result_to_queue_with_printf_("set_component_background_color", "%s.bco=%d", component,
|
||||
display::ColorUtil::color_to_565(color));
|
||||
}
|
||||
|
||||
void Nextion::set_component_pressed_background_color(const char *component, uint32_t color) {
|
||||
this->add_no_result_to_queue_with_printf_("set_component_pressed_background_color", "%s.bco2=%d", component, color);
|
||||
}
|
||||
|
||||
void Nextion::set_component_pressed_background_color(const char *component, const char *color) {
|
||||
this->add_no_result_to_queue_with_printf_("set_component_pressed_background_color", "%s.bco2=%s", component, color);
|
||||
}
|
||||
|
||||
void Nextion::set_component_pressed_background_color(const char *component, Color color) {
|
||||
this->add_no_result_to_queue_with_printf_("set_component_pressed_background_color", "%s.bco2=%d", component,
|
||||
display::ColorUtil::color_to_565(color));
|
||||
}
|
||||
|
||||
void Nextion::set_component_pic(const char *component, uint8_t pic_id) {
|
||||
this->add_no_result_to_queue_with_printf_("set_component_pic", "%s.pic=%d", component, pic_id);
|
||||
}
|
||||
|
||||
void Nextion::set_component_picc(const char *component, uint8_t pic_id) {
|
||||
this->add_no_result_to_queue_with_printf_("set_component_pic", "%s.picc=%d", component, pic_id);
|
||||
}
|
||||
|
||||
void Nextion::set_component_font_color(const char *component, uint32_t color) {
|
||||
this->add_no_result_to_queue_with_printf_("set_component_font_color", "%s.pco=%d", component, color);
|
||||
}
|
||||
|
||||
void Nextion::set_component_font_color(const char *component, const char *color) {
|
||||
this->add_no_result_to_queue_with_printf_("set_component_font_color", "%s.pco=%s", component, color);
|
||||
}
|
||||
|
||||
void Nextion::set_component_font_color(const char *component, Color color) {
|
||||
this->add_no_result_to_queue_with_printf_("set_component_font_color", "%s.pco=%d", component,
|
||||
display::ColorUtil::color_to_565(color));
|
||||
}
|
||||
|
||||
void Nextion::set_component_pressed_font_color(const char *component, uint32_t color) {
|
||||
this->add_no_result_to_queue_with_printf_("set_component_pressed_font_color", "%s.pco2=%d", component, color);
|
||||
}
|
||||
|
||||
void Nextion::set_component_pressed_font_color(const char *component, const char *color) {
|
||||
this->add_no_result_to_queue_with_printf_("set_component_pressed_font_color", " %s.pco2=%s", component, color);
|
||||
}
|
||||
|
||||
void Nextion::set_component_pressed_font_color(const char *component, Color color) {
|
||||
this->add_no_result_to_queue_with_printf_("set_component_pressed_font_color", "%s.pco2=%d", component,
|
||||
display::ColorUtil::color_to_565(color));
|
||||
}
|
||||
|
||||
void Nextion::set_component_text_printf(const char *component, const char *format, ...) {
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
char buffer[256];
|
||||
int ret = vsnprintf(buffer, sizeof(buffer), format, arg);
|
||||
va_end(arg);
|
||||
if (ret > 0)
|
||||
this->set_component_text(component, buffer);
|
||||
}
|
||||
|
||||
// General Nextion
|
||||
void Nextion::goto_page(const char *page) { this->add_no_result_to_queue_with_printf_("goto_page", "page %s", page); }
|
||||
|
||||
void Nextion::set_backlight_brightness(float brightness) {
|
||||
if (brightness < 0 || brightness > 1.0) {
|
||||
ESP_LOGD(TAG, "Brightness out of bounds, percentage range 0-1.0");
|
||||
return;
|
||||
}
|
||||
this->add_no_result_to_queue_with_set("backlight_brightness", "dim", static_cast<int>(brightness * 100));
|
||||
}
|
||||
|
||||
void Nextion::set_auto_wake_on_touch(bool auto_wake) {
|
||||
this->add_no_result_to_queue_with_set("auto_wake_on_touch", "thup", auto_wake ? 1 : 0);
|
||||
}
|
||||
|
||||
// General Component
|
||||
void Nextion::set_component_font(const char *component, uint8_t font_id) {
|
||||
this->add_no_result_to_queue_with_printf_("set_component_font", "%s.font=%d", component, font_id);
|
||||
}
|
||||
|
||||
void Nextion::hide_component(const char *component) {
|
||||
this->add_no_result_to_queue_with_printf_("hide_component", "vis %s,0", component);
|
||||
}
|
||||
|
||||
void Nextion::show_component(const char *component) {
|
||||
this->add_no_result_to_queue_with_printf_("show_component", "vis %s,1", component);
|
||||
}
|
||||
|
||||
void Nextion::enable_component_touch(const char *component) {
|
||||
this->add_no_result_to_queue_with_printf_("enable_component_touch", "tsw %s,1", component);
|
||||
}
|
||||
|
||||
void Nextion::disable_component_touch(const char *component) {
|
||||
this->add_no_result_to_queue_with_printf_("disable_component_touch", "tsw %s,0", component);
|
||||
}
|
||||
|
||||
void Nextion::set_component_picture(const char *component, const char *picture) {
|
||||
this->add_no_result_to_queue_with_printf_("set_component_picture", "%s.val=%s", component, picture);
|
||||
}
|
||||
|
||||
void Nextion::set_component_text(const char *component, const char *text) {
|
||||
this->add_no_result_to_queue_with_printf_("set_component_text", "%s.txt=\"%s\"", component, text);
|
||||
}
|
||||
|
||||
void Nextion::set_component_value(const char *component, int value) {
|
||||
this->add_no_result_to_queue_with_printf_("set_component_value", "%s.val=%d", component, value);
|
||||
}
|
||||
|
||||
void Nextion::add_waveform_data(int component_id, uint8_t channel_number, uint8_t value) {
|
||||
this->add_no_result_to_queue_with_printf_("add_waveform_data", "add %d,%u,%u", component_id, channel_number, value);
|
||||
}
|
||||
|
||||
void Nextion::open_waveform_channel(int component_id, uint8_t channel_number, uint8_t value) {
|
||||
this->add_no_result_to_queue_with_printf_("open_waveform_channel", "addt %d,%u,%u", component_id, channel_number,
|
||||
value);
|
||||
}
|
||||
|
||||
void Nextion::set_component_coordinates(const char *component, int x, int y) {
|
||||
this->add_no_result_to_queue_with_printf_("set_component_coordinates command 1", "%s.xcen=%d", component, x);
|
||||
this->add_no_result_to_queue_with_printf_("set_component_coordinates command 2", "%s.ycen=%d", component, y);
|
||||
}
|
||||
|
||||
// Drawing
|
||||
void Nextion::display_picture(int picture_id, int x_start, int y_start) {
|
||||
this->add_no_result_to_queue_with_printf_("display_picture", "pic %d %d %d", x_start, y_start, picture_id);
|
||||
}
|
||||
|
||||
void Nextion::fill_area(int x1, int y1, int width, int height, const char *color) {
|
||||
this->add_no_result_to_queue_with_printf_("fill_area", "fill %d,%d,%d,%d,%s", x1, y1, width, height, color);
|
||||
}
|
||||
|
||||
void Nextion::fill_area(int x1, int y1, int width, int height, Color color) {
|
||||
this->add_no_result_to_queue_with_printf_("fill_area", "fill %d,%d,%d,%d,%d", x1, y1, width, height,
|
||||
display::ColorUtil::color_to_565(color));
|
||||
}
|
||||
|
||||
void Nextion::line(int x1, int y1, int x2, int y2, const char *color) {
|
||||
this->add_no_result_to_queue_with_printf_("line", "line %d,%d,%d,%d,%s", x1, y1, x2, y2, color);
|
||||
}
|
||||
|
||||
void Nextion::line(int x1, int y1, int x2, int y2, Color color) {
|
||||
this->add_no_result_to_queue_with_printf_("line", "line %d,%d,%d,%d,%d", x1, y1, x2, y2,
|
||||
display::ColorUtil::color_to_565(color));
|
||||
}
|
||||
|
||||
void Nextion::rectangle(int x1, int y1, int width, int height, const char *color) {
|
||||
this->add_no_result_to_queue_with_printf_("draw", "draw %d,%d,%d,%d,%s", x1, y1, x1 + width, y1 + height, color);
|
||||
}
|
||||
|
||||
void Nextion::rectangle(int x1, int y1, int width, int height, Color color) {
|
||||
this->add_no_result_to_queue_with_printf_("draw", "draw %d,%d,%d,%d,%d", x1, y1, x1 + width, y1 + height,
|
||||
display::ColorUtil::color_to_565(color));
|
||||
}
|
||||
|
||||
void Nextion::circle(int center_x, int center_y, int radius, const char *color) {
|
||||
this->add_no_result_to_queue_with_printf_("cir", "cir %d,%d,%d,%s", center_x, center_y, radius, color);
|
||||
}
|
||||
|
||||
void Nextion::circle(int center_x, int center_y, int radius, Color color) {
|
||||
this->add_no_result_to_queue_with_printf_("cir", "cir %d,%d,%d,%d", center_x, center_y, radius,
|
||||
display::ColorUtil::color_to_565(color));
|
||||
}
|
||||
|
||||
void Nextion::filled_circle(int center_x, int center_y, int radius, const char *color) {
|
||||
this->add_no_result_to_queue_with_printf_("cirs", "cirs %d,%d,%d,%s", center_x, center_y, radius, color);
|
||||
}
|
||||
|
||||
void Nextion::filled_circle(int center_x, int center_y, int radius, Color color) {
|
||||
this->add_no_result_to_queue_with_printf_("cirs", "cirs %d,%d,%d,%d", center_x, center_y, radius,
|
||||
display::ColorUtil::color_to_565(color));
|
||||
}
|
||||
|
||||
#ifdef USE_TIME
|
||||
void Nextion::set_nextion_rtc_time(time::ESPTime time) {
|
||||
this->add_no_result_to_queue_with_printf_("rtc0", "rtc0=%u", time.year);
|
||||
this->add_no_result_to_queue_with_printf_("rtc1", "rtc1=%u", time.month);
|
||||
this->add_no_result_to_queue_with_printf_("rtc2", "rtc2=%u", time.day_of_month);
|
||||
this->add_no_result_to_queue_with_printf_("rtc3", "rtc3=%u", time.hour);
|
||||
this->add_no_result_to_queue_with_printf_("rtc4", "rtc4=%u", time.minute);
|
||||
this->add_no_result_to_queue_with_printf_("rtc5", "rtc5=%u", time.second);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace nextion
|
||||
} // namespace esphome
|
||||
116
esphome/components/nextion/nextion_component.cpp
Normal file
116
esphome/components/nextion/nextion_component.cpp
Normal file
@ -0,0 +1,116 @@
|
||||
#include "nextion_component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace nextion {
|
||||
|
||||
void NextionComponent::set_background_color(Color bco) {
|
||||
if (this->variable_name_ == this->variable_name_to_send_) {
|
||||
return; // This is a variable. no need to set color
|
||||
}
|
||||
this->bco_ = bco;
|
||||
this->bco_needs_update_ = true;
|
||||
this->bco_is_set_ = true;
|
||||
this->update_component_settings();
|
||||
}
|
||||
|
||||
void NextionComponent::set_background_pressed_color(Color bco2) {
|
||||
if (this->variable_name_ == this->variable_name_to_send_) {
|
||||
return; // This is a variable. no need to set color
|
||||
}
|
||||
|
||||
this->bco2_ = bco2;
|
||||
this->bco2_needs_update_ = true;
|
||||
this->bco2_is_set_ = true;
|
||||
this->update_component_settings();
|
||||
}
|
||||
|
||||
void NextionComponent::set_foreground_color(Color pco) {
|
||||
if (this->variable_name_ == this->variable_name_to_send_) {
|
||||
return; // This is a variable. no need to set color
|
||||
}
|
||||
this->pco_ = pco;
|
||||
this->pco_needs_update_ = true;
|
||||
this->pco_is_set_ = true;
|
||||
this->update_component_settings();
|
||||
}
|
||||
|
||||
void NextionComponent::set_foreground_pressed_color(Color pco2) {
|
||||
if (this->variable_name_ == this->variable_name_to_send_) {
|
||||
return; // This is a variable. no need to set color
|
||||
}
|
||||
this->pco2_ = pco2;
|
||||
this->pco2_needs_update_ = true;
|
||||
this->pco2_is_set_ = true;
|
||||
this->update_component_settings();
|
||||
}
|
||||
|
||||
void NextionComponent::set_font_id(uint8_t font_id) {
|
||||
if (this->variable_name_ == this->variable_name_to_send_) {
|
||||
return; // This is a variable. no need to set color
|
||||
}
|
||||
this->font_id_ = font_id;
|
||||
this->font_id_needs_update_ = true;
|
||||
this->font_id_is_set_ = true;
|
||||
this->update_component_settings();
|
||||
}
|
||||
|
||||
void NextionComponent::set_visible(bool visible) {
|
||||
if (this->variable_name_ == this->variable_name_to_send_) {
|
||||
return; // This is a variable. no need to set color
|
||||
}
|
||||
this->visible_ = visible;
|
||||
this->visible_needs_update_ = true;
|
||||
this->visible_is_set_ = true;
|
||||
this->update_component_settings();
|
||||
}
|
||||
|
||||
void NextionComponent::update_component_settings(bool force_update) {
|
||||
if (this->nextion_->is_sleeping() || !this->nextion_->is_setup() || !this->visible_is_set_ ||
|
||||
(!this->visible_needs_update_ && !this->visible_)) {
|
||||
this->needs_to_send_update_ = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->visible_needs_update_ || (force_update && this->visible_is_set_)) {
|
||||
std::string name_to_send = this->variable_name_;
|
||||
|
||||
size_t pos = name_to_send.find_last_of('.');
|
||||
if (pos != std::string::npos) {
|
||||
name_to_send = name_to_send.substr(pos + 1);
|
||||
}
|
||||
|
||||
this->visible_needs_update_ = false;
|
||||
|
||||
if (this->visible_) {
|
||||
this->nextion_->show_component(name_to_send.c_str());
|
||||
this->send_state_to_nextion();
|
||||
} else {
|
||||
this->nextion_->hide_component(name_to_send.c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (this->bco_needs_update_ || (force_update && this->bco2_is_set_)) {
|
||||
this->nextion_->set_component_background_color(this->variable_name_.c_str(), this->bco_);
|
||||
this->bco_needs_update_ = false;
|
||||
}
|
||||
if (this->bco2_needs_update_ || (force_update && this->bco2_is_set_)) {
|
||||
this->nextion_->set_component_pressed_background_color(this->variable_name_.c_str(), this->bco2_);
|
||||
this->bco2_needs_update_ = false;
|
||||
}
|
||||
if (this->pco_needs_update_ || (force_update && this->pco_is_set_)) {
|
||||
this->nextion_->set_component_font_color(this->variable_name_.c_str(), this->pco_);
|
||||
this->pco_needs_update_ = false;
|
||||
}
|
||||
if (this->pco2_needs_update_ || (force_update && this->pco2_is_set_)) {
|
||||
this->nextion_->set_component_pressed_font_color(this->variable_name_.c_str(), this->pco2_);
|
||||
this->pco2_needs_update_ = false;
|
||||
}
|
||||
|
||||
if (this->font_id_needs_update_ || (force_update && this->font_id_is_set_)) {
|
||||
this->nextion_->set_component_font(this->variable_name_.c_str(), this->font_id_);
|
||||
this->font_id_needs_update_ = false;
|
||||
}
|
||||
}
|
||||
} // namespace nextion
|
||||
} // namespace esphome
|
||||
49
esphome/components/nextion/nextion_component.h
Normal file
49
esphome/components/nextion/nextion_component.h
Normal file
@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/color.h"
|
||||
#include "nextion_base.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace nextion {
|
||||
class NextionComponent;
|
||||
|
||||
class NextionComponent : public NextionComponentBase {
|
||||
public:
|
||||
void update_component_settings() override { this->update_component_settings(false); };
|
||||
|
||||
void update_component_settings(bool force_update) override;
|
||||
|
||||
void set_background_color(Color bco);
|
||||
void set_background_pressed_color(Color bco2);
|
||||
void set_foreground_color(Color pco);
|
||||
void set_foreground_pressed_color(Color pco2);
|
||||
void set_font_id(uint8_t font_id);
|
||||
void set_visible(bool visible);
|
||||
|
||||
protected:
|
||||
NextionBase *nextion_;
|
||||
|
||||
bool bco_needs_update_ = false;
|
||||
bool bco_is_set_ = false;
|
||||
Color bco_;
|
||||
bool bco2_needs_update_ = false;
|
||||
bool bco2_is_set_ = false;
|
||||
Color bco2_;
|
||||
bool pco_needs_update_ = false;
|
||||
bool pco_is_set_ = false;
|
||||
Color pco_;
|
||||
bool pco2_needs_update_ = false;
|
||||
bool pco2_is_set_ = false;
|
||||
Color pco2_;
|
||||
uint8_t font_id_ = 0;
|
||||
bool font_id_needs_update_ = false;
|
||||
bool font_id_is_set_ = false;
|
||||
|
||||
bool visible_ = true;
|
||||
bool visible_needs_update_ = false;
|
||||
bool visible_is_set_ = false;
|
||||
|
||||
// void send_state_to_nextion() = 0;
|
||||
};
|
||||
} // namespace nextion
|
||||
} // namespace esphome
|
||||
95
esphome/components/nextion/nextion_component_base.h
Normal file
95
esphome/components/nextion/nextion_component_base.h
Normal file
@ -0,0 +1,95 @@
|
||||
#pragma once
|
||||
#include <utility>
|
||||
#include "esphome/core/defines.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace nextion {
|
||||
|
||||
enum NextionQueueType {
|
||||
NO_RESULT = 0,
|
||||
SENSOR = 1,
|
||||
BINARY_SENSOR = 2,
|
||||
SWITCH = 3,
|
||||
TEXT_SENSOR = 4,
|
||||
WAVEFORM_SENSOR = 5,
|
||||
};
|
||||
|
||||
static const char *const NEXTION_QUEUE_TYPE_STRINGS[] = {"NO_RESULT", "SENSOR", "BINARY_SENSOR",
|
||||
"SWITCH", "TEXT_SENSOR", "WAVEFORM_SENSOR"};
|
||||
|
||||
class NextionComponentBase;
|
||||
|
||||
class NextionQueue {
|
||||
public:
|
||||
virtual ~NextionQueue() = default;
|
||||
NextionComponentBase *component;
|
||||
uint32_t queue_time = 0;
|
||||
};
|
||||
|
||||
class NextionComponentBase {
|
||||
public:
|
||||
virtual ~NextionComponentBase() = default;
|
||||
|
||||
void set_variable_name(const std::string &variable_name, const std::string &variable_name_to_send = "") {
|
||||
variable_name_ = variable_name;
|
||||
if (variable_name_to_send.empty()) {
|
||||
variable_name_to_send_ = variable_name;
|
||||
} else {
|
||||
variable_name_to_send_ = variable_name_to_send;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void update_component_settings(){};
|
||||
virtual void update_component_settings(bool force_update){};
|
||||
|
||||
virtual void update_component(){};
|
||||
virtual void process_sensor(const std::string &variable_name, int state){};
|
||||
virtual void process_touch(uint8_t page_id, uint8_t component_id, bool on){};
|
||||
virtual void process_text(const std::string &variable_name, const std::string &text_value){};
|
||||
virtual void process_bool(const std::string &variable_name, bool on){};
|
||||
|
||||
virtual void set_state(float state){};
|
||||
virtual void set_state(float state, bool publish){};
|
||||
virtual void set_state(float state, bool publish, bool send_to_nextion){};
|
||||
|
||||
virtual void set_state(bool state){};
|
||||
virtual void set_state(bool state, bool publish){};
|
||||
virtual void set_state(bool state, bool publish, bool send_to_nextion){};
|
||||
|
||||
virtual void set_state(const std::string &state) {}
|
||||
virtual void set_state(const std::string &state, bool publish) {}
|
||||
virtual void set_state(const std::string &state, bool publish, bool send_to_nextion){};
|
||||
|
||||
uint8_t get_component_id() { return this->component_id_; }
|
||||
void set_component_id(uint8_t component_id) { component_id_ = component_id; }
|
||||
|
||||
uint8_t get_wave_channel_id() { return this->wave_chan_id_; }
|
||||
void set_wave_channel_id(uint8_t wave_chan_id) { this->wave_chan_id_ = wave_chan_id; }
|
||||
|
||||
std::vector<uint8_t> get_wave_buffer() { return this->wave_buffer_; }
|
||||
size_t get_wave_buffer_size() { return this->wave_buffer_.size(); }
|
||||
|
||||
std::string get_variable_name() { return this->variable_name_; }
|
||||
std::string get_variable_name_to_send() { return this->variable_name_to_send_; }
|
||||
virtual NextionQueueType get_queue_type() { return NextionQueueType::NO_RESULT; }
|
||||
virtual std::string get_queue_type_string() { return NEXTION_QUEUE_TYPE_STRINGS[this->get_queue_type()]; }
|
||||
virtual void set_state_from_int(int state_value, bool publish, bool send_to_nextion){};
|
||||
virtual void set_state_from_string(const std::string &state_value, bool publish, bool send_to_nextion){};
|
||||
virtual void send_state_to_nextion(){};
|
||||
bool get_needs_to_send_update() { return this->needs_to_send_update_; }
|
||||
uint8_t get_wave_chan_id() { return this->wave_chan_id_; }
|
||||
void set_wave_max_length(int wave_max_length) { this->wave_max_length_ = wave_max_length; }
|
||||
|
||||
protected:
|
||||
std::string variable_name_;
|
||||
std::string variable_name_to_send_;
|
||||
|
||||
uint8_t component_id_ = 0;
|
||||
uint8_t wave_chan_id_ = UINT8_MAX;
|
||||
std::vector<uint8_t> wave_buffer_;
|
||||
int wave_max_length_ = 255;
|
||||
|
||||
bool needs_to_send_update_;
|
||||
};
|
||||
} // namespace nextion
|
||||
} // namespace esphome
|
||||
343
esphome/components/nextion/nextion_upload.cpp
Normal file
343
esphome/components/nextion/nextion_upload.cpp
Normal file
@ -0,0 +1,343 @@
|
||||
|
||||
#include "nextion.h"
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/util.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace nextion {
|
||||
static const char *const TAG = "nextion_upload";
|
||||
|
||||
#if defined(USE_TFT_UPLOAD) && (defined(USE_ETHERNET) || defined(USE_WIFI))
|
||||
|
||||
// Followed guide
|
||||
// https://unofficialnextion.com/t/nextion-upload-protocol-v1-2-the-fast-one/1044/2
|
||||
|
||||
int Nextion::upload_by_chunks_(HTTPClient *http, int range_start) {
|
||||
int range_end = 0;
|
||||
|
||||
if (range_start == 0 && this->transfer_buffer_size_ > 16384) { // Start small at the first run in case of a big skip
|
||||
range_end = 16384 - 1;
|
||||
} else {
|
||||
range_end = range_start + this->transfer_buffer_size_ - 1;
|
||||
}
|
||||
|
||||
if (range_end > this->tft_size_)
|
||||
range_end = this->tft_size_;
|
||||
|
||||
bool begin_status = false;
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
begin_status = http->begin(this->tft_url_.c_str());
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
#ifndef CLANG_TIDY
|
||||
http->setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
|
||||
http->setRedirectLimit(3);
|
||||
begin_status = http->begin(*this->get_wifi_client_(), this->tft_url_.c_str());
|
||||
#endif
|
||||
#endif
|
||||
|
||||
char range_header[64];
|
||||
sprintf(range_header, "bytes=%d-%d", range_start, range_end);
|
||||
|
||||
ESP_LOGD(TAG, "Requesting range: %s", range_header);
|
||||
|
||||
int tries = 1;
|
||||
int code = 0;
|
||||
while (tries <= 5) {
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
begin_status = http->begin(this->tft_url_.c_str());
|
||||
#endif
|
||||
#ifndef CLANG_TIDY
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
begin_status = http->begin(*this->get_wifi_client_(), this->tft_url_.c_str());
|
||||
#endif
|
||||
#endif
|
||||
|
||||
++tries;
|
||||
if (!begin_status) {
|
||||
ESP_LOGD(TAG, "upload_by_chunks_: connection failed");
|
||||
continue;
|
||||
}
|
||||
|
||||
http->addHeader("Range", range_header);
|
||||
|
||||
code = http->GET();
|
||||
if (code == 200 || code == 206) {
|
||||
break;
|
||||
}
|
||||
ESP_LOGW(TAG, "HTTP Request failed; URL: %s; Error: %s, retries(%d/5)", this->tft_url_.c_str(),
|
||||
HTTPClient::errorToString(code).c_str(), tries);
|
||||
http->end();
|
||||
App.feed_wdt();
|
||||
delay(500); // NOLINT
|
||||
}
|
||||
|
||||
if (tries > 5) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string recv_string;
|
||||
size_t size = 0;
|
||||
int sent = 0;
|
||||
int range = range_end - range_start;
|
||||
|
||||
while (sent < range) {
|
||||
size = http->getStreamPtr()->available();
|
||||
if (!size) {
|
||||
App.feed_wdt();
|
||||
delay(0);
|
||||
continue;
|
||||
}
|
||||
int c = http->getStreamPtr()->readBytes(
|
||||
&this->transfer_buffer_[sent], ((size > this->transfer_buffer_size_) ? this->transfer_buffer_size_ : size));
|
||||
sent += c;
|
||||
}
|
||||
http->end();
|
||||
ESP_LOGN(TAG, "this->content_length_ %d sent %d", this->content_length_, sent);
|
||||
for (uint32_t i = 0; i < range; i += 4096) {
|
||||
this->write_array(&this->transfer_buffer_[i], 4096);
|
||||
this->content_length_ -= 4096;
|
||||
ESP_LOGN(TAG, "this->content_length_ %d range %d range_end %d range_start %d", this->content_length_, range,
|
||||
range_end, range_start);
|
||||
|
||||
if (!this->upload_first_chunk_sent_) {
|
||||
this->upload_first_chunk_sent_ = true;
|
||||
delay(500); // NOLINT
|
||||
App.feed_wdt();
|
||||
}
|
||||
|
||||
this->recv_ret_string_(recv_string, 2048, true);
|
||||
if (recv_string[0] == 0x08) {
|
||||
uint32_t result = 0;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
result += static_cast<uint8_t>(recv_string[i + 1]) << (8 * i);
|
||||
}
|
||||
if (result > 0) {
|
||||
ESP_LOGD(TAG, "Nextion reported new range %d", result);
|
||||
this->content_length_ = this->tft_size_ - result;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
recv_string.clear();
|
||||
}
|
||||
return range_end + 1;
|
||||
}
|
||||
|
||||
void Nextion::upload_tft() {
|
||||
if (this->is_updating_) {
|
||||
ESP_LOGD(TAG, "Currently updating");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!network_is_connected()) {
|
||||
ESP_LOGD(TAG, "network is not connected");
|
||||
return;
|
||||
}
|
||||
|
||||
this->is_updating_ = true;
|
||||
|
||||
HTTPClient http;
|
||||
http.setTimeout(15000); // Yes 15 seconds.... Helps 8266s along
|
||||
bool begin_status = false;
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
begin_status = http.begin(this->tft_url_.c_str());
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
#ifndef CLANG_TIDY
|
||||
http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
|
||||
http.setRedirectLimit(3);
|
||||
begin_status = http.begin(*this->get_wifi_client_(), this->tft_url_.c_str());
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (!begin_status) {
|
||||
this->is_updating_ = false;
|
||||
ESP_LOGD(TAG, "connection failed");
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
if (psramFound())
|
||||
free(this->transfer_buffer_);
|
||||
else
|
||||
#endif
|
||||
delete this->transfer_buffer_;
|
||||
return;
|
||||
} else {
|
||||
ESP_LOGD(TAG, "Connected");
|
||||
}
|
||||
|
||||
http.addHeader("Range", "bytes=0-255");
|
||||
const char *header_names[] = {"Content-Range"};
|
||||
http.collectHeaders(header_names, 1);
|
||||
ESP_LOGD(TAG, "Requesting URL: %s", this->tft_url_.c_str());
|
||||
|
||||
http.setReuse(true);
|
||||
// try up to 5 times. DNS sometimes needs a second try or so
|
||||
int tries = 1;
|
||||
int code = http.GET();
|
||||
delay(100); // NOLINT
|
||||
|
||||
App.feed_wdt();
|
||||
while (code != 200 && code != 206 && tries <= 5) {
|
||||
ESP_LOGW(TAG, "HTTP Request failed; URL: %s; Error: %s, retrying (%d/5)", this->tft_url_.c_str(),
|
||||
HTTPClient::errorToString(code).c_str(), tries);
|
||||
|
||||
delay(250); // NOLINT
|
||||
App.feed_wdt();
|
||||
code = http.GET();
|
||||
++tries;
|
||||
}
|
||||
|
||||
if ((code != 200 && code != 206) || tries > 5) {
|
||||
this->upload_end_();
|
||||
}
|
||||
|
||||
String content_range_string = http.header("Content-Range");
|
||||
content_range_string.remove(0, 12);
|
||||
this->content_length_ = content_range_string.toInt();
|
||||
this->tft_size_ = content_length_;
|
||||
http.end();
|
||||
|
||||
if (this->content_length_ < 4096) {
|
||||
ESP_LOGE(TAG, "Failed to get file size");
|
||||
this->upload_end_();
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Updating Nextion %s...", this->device_model_.c_str());
|
||||
// The Nextion will ignore the update command if it is sleeping
|
||||
|
||||
this->send_command_("sleep=0");
|
||||
this->set_backlight_brightness(1.0);
|
||||
delay(250); // NOLINT
|
||||
|
||||
App.feed_wdt();
|
||||
|
||||
char command[128];
|
||||
// Tells the Nextion the content length of the tft file and baud rate it will be sent at
|
||||
// Once the Nextion accepts the command it will wait until the file is successfully uploaded
|
||||
// If it fails for any reason a power cycle of the display will be needed
|
||||
sprintf(command, "whmi-wris %d,%d,1", this->content_length_, this->parent_->get_baud_rate());
|
||||
|
||||
// Clear serial receive buffer
|
||||
uint8_t d;
|
||||
while (this->available()) {
|
||||
this->read_byte(&d);
|
||||
};
|
||||
|
||||
this->send_command_(command);
|
||||
|
||||
App.feed_wdt();
|
||||
|
||||
std::string response;
|
||||
ESP_LOGD(TAG, "Waiting for upgrade response");
|
||||
this->recv_ret_string_(response, 2000, true); // This can take some time to return
|
||||
|
||||
// The Nextion display will, if it's ready to accept data, send a 0x05 byte.
|
||||
ESP_LOGD(TAG, "Upgrade response is %s %zu", response.c_str(), response.length());
|
||||
|
||||
for (int i = 0; i < response.length(); i++) {
|
||||
ESP_LOGD(TAG, "Available %d : 0x%02X", i, response[i]);
|
||||
}
|
||||
|
||||
if (response.find(0x05) != std::string::npos) {
|
||||
ESP_LOGD(TAG, "preparation for tft update done");
|
||||
} else {
|
||||
ESP_LOGD(TAG, "preparation for tft update failed %d \"%s\"", response[0], response.c_str());
|
||||
this->upload_end_();
|
||||
}
|
||||
|
||||
// Nextion wants 4096 bytes at a time. Make chunk_size a multiple of 4096
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
uint32_t chunk_size = 8192;
|
||||
if (psramFound()) {
|
||||
chunk_size = this->content_length_;
|
||||
} else {
|
||||
if (ESP.getFreeHeap() > 40960) { // 32K to keep on hand
|
||||
int chunk = int((ESP.getFreeHeap() - 32768) / 4096);
|
||||
chunk_size = chunk * 4096;
|
||||
chunk_size = chunk_size > 65536 ? 65536 : chunk_size;
|
||||
} else if (ESP.getFreeHeap() < 10240) {
|
||||
chunk_size = 4096;
|
||||
}
|
||||
}
|
||||
#else
|
||||
uint32_t chunk_size = ESP.getFreeHeap() < 10240 ? 4096 : 8192;
|
||||
#endif
|
||||
|
||||
if (this->transfer_buffer_ == nullptr) {
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
if (psramFound()) {
|
||||
ESP_LOGD(TAG, "Allocating PSRAM buffer size %d, Free PSRAM size is %u", chunk_size, ESP.getFreePsram());
|
||||
this->transfer_buffer_ = (uint8_t *) ps_malloc(chunk_size);
|
||||
if (this->transfer_buffer_ == nullptr) {
|
||||
ESP_LOGE(TAG, "Could not allocate buffer size %d!", chunk_size);
|
||||
this->upload_end_();
|
||||
}
|
||||
} else {
|
||||
#endif
|
||||
ESP_LOGD(TAG, "Allocating buffer size %d, Heap size is %u", chunk_size, ESP.getFreeHeap());
|
||||
this->transfer_buffer_ = new uint8_t[chunk_size];
|
||||
if (!this->transfer_buffer_) { // Try a smaller size
|
||||
ESP_LOGD(TAG, "Could not allocate buffer size: %d trying 4096 instead", chunk_size);
|
||||
chunk_size = 4096;
|
||||
ESP_LOGD(TAG, "Allocating %d buffer", chunk_size);
|
||||
this->transfer_buffer_ = new uint8_t[chunk_size];
|
||||
|
||||
if (!this->transfer_buffer_)
|
||||
this->upload_end_();
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
this->transfer_buffer_size_ = chunk_size;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Updating tft from \"%s\" with a file size of %d using %zu chunksize, Heap Size %d",
|
||||
this->tft_url_.c_str(), this->content_length_, this->transfer_buffer_size_, ESP.getFreeHeap());
|
||||
|
||||
int result = 0;
|
||||
while (this->content_length_ > 0) {
|
||||
result = this->upload_by_chunks_(&http, result);
|
||||
if (result < 0) {
|
||||
ESP_LOGD(TAG, "Error updating Nextion!");
|
||||
this->upload_end_();
|
||||
}
|
||||
App.feed_wdt();
|
||||
ESP_LOGD(TAG, "Heap Size %d, Bytes left %d", ESP.getFreeHeap(), this->content_length_);
|
||||
}
|
||||
ESP_LOGD(TAG, "Succesfully updated Nextion!");
|
||||
|
||||
this->upload_end_();
|
||||
}
|
||||
|
||||
void Nextion::upload_end_() {
|
||||
ESP_LOGD(TAG, "Restarting Nextion");
|
||||
this->soft_reset();
|
||||
delay(1500); // NOLINT
|
||||
ESP_LOGD(TAG, "Restarting esphome");
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
WiFiClient *Nextion::get_wifi_client_() {
|
||||
if (this->tft_url_.compare(0, 6, "https:") == 0) {
|
||||
if (this->wifi_client_secure_ == nullptr) {
|
||||
this->wifi_client_secure_ = new BearSSL::WiFiClientSecure();
|
||||
this->wifi_client_secure_->setInsecure();
|
||||
this->wifi_client_secure_->setBufferSizes(512, 512);
|
||||
}
|
||||
return this->wifi_client_secure_;
|
||||
}
|
||||
|
||||
if (this->wifi_client_ == nullptr) {
|
||||
this->wifi_client_ = new WiFiClient();
|
||||
}
|
||||
return this->wifi_client_;
|
||||
}
|
||||
#endif
|
||||
|
||||
#else
|
||||
void Nextion::upload_tft() { ESP_LOGW(TAG, "tft_url, WIFI or Ethernet components are needed. Cannot upload."); }
|
||||
#endif
|
||||
} // namespace nextion
|
||||
} // namespace esphome
|
||||
99
esphome/components/nextion/sensor/__init__.py
Normal file
99
esphome/components/nextion/sensor/__init__.py
Normal file
@ -0,0 +1,99 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor
|
||||
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
UNIT_EMPTY,
|
||||
ICON_EMPTY,
|
||||
CONF_COMPONENT_ID,
|
||||
DEVICE_CLASS_EMPTY,
|
||||
)
|
||||
from .. import nextion_ns, CONF_NEXTION_ID
|
||||
|
||||
from ..base_component import (
|
||||
setup_component_core_,
|
||||
CONFIG_SENSOR_COMPONENT_SCHEMA,
|
||||
CONF_VARIABLE_NAME,
|
||||
CONF_COMPONENT_NAME,
|
||||
CONF_PRECISION,
|
||||
CONF_WAVE_CHANNEL_ID,
|
||||
CONF_WAVE_MAX_VALUE,
|
||||
CONF_WAVEFORM_SEND_LAST_VALUE,
|
||||
CONF_WAVE_MAX_LENGTH,
|
||||
)
|
||||
|
||||
|
||||
CODEOWNERS = ["@senexcrenshaw"]
|
||||
|
||||
NextionSensor = nextion_ns.class_("NextionSensor", sensor.Sensor, cg.PollingComponent)
|
||||
|
||||
|
||||
def CheckWaveID(value):
|
||||
value = cv.int_(value)
|
||||
if value < 0 or value > 3:
|
||||
raise cv.Invalid(f"Valid range for {CONF_WAVE_CHANNEL_ID} is 0-3")
|
||||
return value
|
||||
|
||||
|
||||
def _validate(config):
|
||||
if CONF_WAVE_CHANNEL_ID in config and CONF_COMPONENT_ID not in config:
|
||||
raise cv.Invalid(
|
||||
f"{CONF_COMPONENT_ID} is required when {CONF_WAVE_CHANNEL_ID} is set"
|
||||
)
|
||||
|
||||
return config
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
sensor.sensor_schema(UNIT_EMPTY, ICON_EMPTY, 2, DEVICE_CLASS_EMPTY)
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(NextionSensor),
|
||||
cv.Optional(CONF_PRECISION, default=0): cv.int_range(min=0, max=8),
|
||||
cv.Optional(CONF_WAVE_CHANNEL_ID): CheckWaveID,
|
||||
cv.Optional(CONF_COMPONENT_ID): cv.uint8_t,
|
||||
cv.Optional(CONF_WAVE_MAX_LENGTH, default=255): cv.int_range(
|
||||
min=1, max=1024
|
||||
),
|
||||
cv.Optional(CONF_WAVE_MAX_VALUE, default=100): cv.int_range(
|
||||
min=1, max=1024
|
||||
),
|
||||
cv.Optional(CONF_WAVEFORM_SEND_LAST_VALUE, default=True): cv.boolean,
|
||||
}
|
||||
)
|
||||
.extend(CONFIG_SENSOR_COMPONENT_SCHEMA)
|
||||
.extend(cv.polling_component_schema("never")),
|
||||
cv.has_exactly_one_key(CONF_COMPONENT_ID, CONF_COMPONENT_NAME, CONF_VARIABLE_NAME),
|
||||
_validate,
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
|
||||
hub = await cg.get_variable(config[CONF_NEXTION_ID])
|
||||
var = cg.new_Pvariable(config[CONF_ID], hub)
|
||||
await cg.register_component(var, config)
|
||||
await sensor.register_sensor(var, config)
|
||||
|
||||
cg.add(hub.register_sensor_component(var))
|
||||
|
||||
await setup_component_core_(var, config, ".val")
|
||||
|
||||
if CONF_PRECISION in config:
|
||||
cg.add(var.set_precision(config[CONF_PRECISION]))
|
||||
|
||||
if CONF_COMPONENT_ID in config:
|
||||
cg.add(var.set_component_id(config[CONF_COMPONENT_ID]))
|
||||
|
||||
if CONF_WAVE_CHANNEL_ID in config:
|
||||
cg.add(var.set_wave_channel_id(config[CONF_WAVE_CHANNEL_ID]))
|
||||
|
||||
if CONF_WAVEFORM_SEND_LAST_VALUE in config:
|
||||
cg.add(var.set_waveform_send_last_value(config[CONF_WAVEFORM_SEND_LAST_VALUE]))
|
||||
|
||||
if CONF_WAVE_MAX_VALUE in config:
|
||||
cg.add(var.set_wave_max_value(config[CONF_WAVE_MAX_VALUE]))
|
||||
|
||||
if CONF_WAVE_MAX_LENGTH in config:
|
||||
cg.add(var.set_wave_max_length(config[CONF_WAVE_MAX_LENGTH]))
|
||||
110
esphome/components/nextion/sensor/nextion_sensor.cpp
Normal file
110
esphome/components/nextion/sensor/nextion_sensor.cpp
Normal file
@ -0,0 +1,110 @@
|
||||
#include "nextion_sensor.h"
|
||||
#include "esphome/core/util.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace nextion {
|
||||
|
||||
static const char *const TAG = "nextion_sensor";
|
||||
|
||||
void NextionSensor::process_sensor(const std::string &variable_name, int state) {
|
||||
if (!this->nextion_->is_setup())
|
||||
return;
|
||||
|
||||
if (this->wave_chan_id_ == UINT8_MAX && this->variable_name_ == variable_name) {
|
||||
this->publish_state(state);
|
||||
ESP_LOGD(TAG, "Processed sensor \"%s\" state %d", variable_name.c_str(), state);
|
||||
}
|
||||
}
|
||||
|
||||
void NextionSensor::add_to_wave_buffer(float state) {
|
||||
this->needs_to_send_update_ = true;
|
||||
|
||||
int wave_state = (int) ((state / (float) this->wave_maxvalue_) * 100);
|
||||
|
||||
wave_buffer_.push_back(wave_state);
|
||||
|
||||
if (this->wave_buffer_.size() > this->wave_max_length_) {
|
||||
this->wave_buffer_.erase(this->wave_buffer_.begin());
|
||||
}
|
||||
}
|
||||
|
||||
void NextionSensor::update() {
|
||||
if (!this->nextion_->is_setup())
|
||||
return;
|
||||
|
||||
if (this->wave_chan_id_ == UINT8_MAX) {
|
||||
this->nextion_->add_to_get_queue(this);
|
||||
} else {
|
||||
if (this->send_last_value_) {
|
||||
this->add_to_wave_buffer(this->last_value_);
|
||||
}
|
||||
|
||||
this->wave_update_();
|
||||
}
|
||||
}
|
||||
|
||||
void NextionSensor::set_state(float state, bool publish, bool send_to_nextion) {
|
||||
if (!this->nextion_->is_setup())
|
||||
return;
|
||||
|
||||
if (isnan(state))
|
||||
return;
|
||||
|
||||
if (this->wave_chan_id_ == UINT8_MAX) {
|
||||
if (send_to_nextion) {
|
||||
if (this->nextion_->is_sleeping() || !this->visible_) {
|
||||
this->needs_to_send_update_ = true;
|
||||
} else {
|
||||
this->needs_to_send_update_ = false;
|
||||
|
||||
if (this->precision_ > 0) {
|
||||
double to_multiply = pow(10, this->precision_);
|
||||
int state_value = (int) (state * to_multiply);
|
||||
|
||||
this->nextion_->add_no_result_to_queue_with_set(this, (int) state_value);
|
||||
} else {
|
||||
this->nextion_->add_no_result_to_queue_with_set(this, (int) state);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (this->send_last_value_) {
|
||||
this->last_value_ = state; // Update will handle setting the buffer
|
||||
} else {
|
||||
this->add_to_wave_buffer(state);
|
||||
}
|
||||
}
|
||||
|
||||
if (this->wave_chan_id_ == UINT8_MAX) {
|
||||
if (publish) {
|
||||
this->publish_state(state);
|
||||
} else {
|
||||
this->raw_state = state;
|
||||
this->state = state;
|
||||
this->has_state_ = true;
|
||||
}
|
||||
}
|
||||
this->update_component_settings();
|
||||
|
||||
ESP_LOGN(TAG, "Wrote state for sensor \"%s\" state %lf", this->variable_name_.c_str(), state);
|
||||
}
|
||||
|
||||
void NextionSensor::wave_update_() {
|
||||
if (this->nextion_->is_sleeping() || this->wave_buffer_.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef NEXTION_PROTOCOL_LOG
|
||||
size_t buffer_to_send =
|
||||
this->wave_buffer_.size() < 255 ? this->wave_buffer_.size() : 255; // ADDT command can only send 255
|
||||
|
||||
ESP_LOGN(TAG, "wave_update send %zu of %zu value(s) to wave nextion component id %d and wave channel id %d",
|
||||
buffer_to_send, this->wave_buffer_.size(), this->component_id_, this->wave_chan_id_);
|
||||
#endif
|
||||
|
||||
this->nextion_->add_addt_command_to_queue(this);
|
||||
}
|
||||
|
||||
} // namespace nextion
|
||||
} // namespace esphome
|
||||
49
esphome/components/nextion/sensor/nextion_sensor.h
Normal file
49
esphome/components/nextion/sensor/nextion_sensor.h
Normal file
@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "../nextion_component.h"
|
||||
#include "../nextion_base.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace nextion {
|
||||
class NextionSensor;
|
||||
|
||||
class NextionSensor : public NextionComponent, public sensor::Sensor, public PollingComponent {
|
||||
public:
|
||||
NextionSensor(NextionBase *nextion) { this->nextion_ = nextion; }
|
||||
void send_state_to_nextion() override { this->set_state(this->state, false, true); };
|
||||
|
||||
void update_component() override { this->update(); }
|
||||
void update() override;
|
||||
void add_to_wave_buffer(float state);
|
||||
void set_precision(uint8_t precision) { this->precision_ = precision; }
|
||||
void set_component_id(uint8_t component_id) { component_id_ = component_id; }
|
||||
void set_wave_channel_id(uint8_t wave_chan_id) { this->wave_chan_id_ = wave_chan_id; }
|
||||
void set_wave_max_value(uint32_t wave_maxvalue) { this->wave_maxvalue_ = wave_maxvalue; }
|
||||
void process_sensor(const std::string &variable_name, int state) override;
|
||||
|
||||
void set_state(float state) override { this->set_state(state, true, true); }
|
||||
void set_state(float state, bool publish) override { this->set_state(state, publish, true); }
|
||||
void set_state(float state, bool publish, bool send_to_nextion) override;
|
||||
|
||||
void set_waveform_send_last_value(bool send_last_value) { this->send_last_value_ = send_last_value; }
|
||||
uint8_t get_wave_chan_id() { return this->wave_chan_id_; }
|
||||
void set_wave_max_length(int wave_max_length) { this->wave_max_length_ = wave_max_length; }
|
||||
NextionQueueType get_queue_type() override {
|
||||
return this->wave_chan_id_ == UINT8_MAX ? NextionQueueType::SENSOR : NextionQueueType::WAVEFORM_SENSOR;
|
||||
}
|
||||
void set_state_from_string(const std::string &state_value, bool publish, bool send_to_nextion) override {}
|
||||
void set_state_from_int(int state_value, bool publish, bool send_to_nextion) override {
|
||||
this->set_state(state_value, publish, send_to_nextion);
|
||||
}
|
||||
|
||||
protected:
|
||||
uint8_t precision_ = 0;
|
||||
uint32_t wave_maxvalue_ = 255;
|
||||
|
||||
float last_value_ = 0;
|
||||
bool send_last_value_ = true;
|
||||
void wave_update_();
|
||||
};
|
||||
} // namespace nextion
|
||||
} // namespace esphome
|
||||
39
esphome/components/nextion/switch/__init__.py
Normal file
39
esphome/components/nextion/switch/__init__.py
Normal file
@ -0,0 +1,39 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import switch
|
||||
|
||||
from esphome.const import CONF_ID
|
||||
from .. import nextion_ns, CONF_NEXTION_ID
|
||||
|
||||
from ..base_component import (
|
||||
setup_component_core_,
|
||||
CONF_COMPONENT_NAME,
|
||||
CONF_VARIABLE_NAME,
|
||||
CONFIG_SWITCH_COMPONENT_SCHEMA,
|
||||
)
|
||||
|
||||
CODEOWNERS = ["@senexcrenshaw"]
|
||||
|
||||
NextionSwitch = nextion_ns.class_("NextionSwitch", switch.Switch, cg.PollingComponent)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
switch.SWITCH_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(NextionSwitch),
|
||||
}
|
||||
)
|
||||
.extend(CONFIG_SWITCH_COMPONENT_SCHEMA)
|
||||
.extend(cv.polling_component_schema("never")),
|
||||
cv.has_exactly_one_key(CONF_COMPONENT_NAME, CONF_VARIABLE_NAME),
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
hub = await cg.get_variable(config[CONF_NEXTION_ID])
|
||||
var = cg.new_Pvariable(config[CONF_ID], hub)
|
||||
await cg.register_component(var, config)
|
||||
await switch.register_switch(var, config)
|
||||
|
||||
cg.add(hub.register_switch_component(var))
|
||||
|
||||
await setup_component_core_(var, config, ".val")
|
||||
52
esphome/components/nextion/switch/nextion_switch.cpp
Normal file
52
esphome/components/nextion/switch/nextion_switch.cpp
Normal file
@ -0,0 +1,52 @@
|
||||
#include "nextion_switch.h"
|
||||
#include "esphome/core/util.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace nextion {
|
||||
|
||||
static const char *const TAG = "nextion_switch";
|
||||
|
||||
void NextionSwitch::process_bool(const std::string &variable_name, bool on) {
|
||||
if (!this->nextion_->is_setup())
|
||||
return;
|
||||
if (this->variable_name_ == variable_name) {
|
||||
this->publish_state(on);
|
||||
|
||||
ESP_LOGD(TAG, "Processed switch \"%s\" state %s", variable_name.c_str(), state ? "ON" : "OFF");
|
||||
}
|
||||
}
|
||||
|
||||
void NextionSwitch::update() {
|
||||
if (!this->nextion_->is_setup())
|
||||
return;
|
||||
this->nextion_->add_to_get_queue(this);
|
||||
}
|
||||
|
||||
void NextionSwitch::set_state(bool state, bool publish, bool send_to_nextion) {
|
||||
if (!this->nextion_->is_setup())
|
||||
return;
|
||||
|
||||
if (send_to_nextion) {
|
||||
if (this->nextion_->is_sleeping() || !this->visible_) {
|
||||
this->needs_to_send_update_ = true;
|
||||
} else {
|
||||
this->needs_to_send_update_ = false;
|
||||
this->nextion_->add_no_result_to_queue_with_set(this, (int) state);
|
||||
}
|
||||
}
|
||||
if (publish) {
|
||||
this->publish_state(state);
|
||||
} else {
|
||||
this->state = state;
|
||||
}
|
||||
|
||||
this->update_component_settings();
|
||||
|
||||
ESP_LOGN(TAG, "Updated switch \"%s\" state %s", this->variable_name_.c_str(), ONOFF(state));
|
||||
}
|
||||
|
||||
void NextionSwitch::write_state(bool state) { this->set_state(state); }
|
||||
|
||||
} // namespace nextion
|
||||
} // namespace esphome
|
||||
34
esphome/components/nextion/switch/nextion_switch.h
Normal file
34
esphome/components/nextion/switch/nextion_switch.h
Normal file
@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/switch/switch.h"
|
||||
#include "../nextion_component.h"
|
||||
#include "../nextion_base.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace nextion {
|
||||
class NextionSwitch;
|
||||
|
||||
class NextionSwitch : public NextionComponent, public switch_::Switch, public PollingComponent {
|
||||
public:
|
||||
NextionSwitch(NextionBase *nextion) { this->nextion_ = nextion; }
|
||||
|
||||
void update() override;
|
||||
void update_component() override { this->update(); }
|
||||
void process_bool(const std::string &variable_name, bool on) override;
|
||||
|
||||
void set_state(bool state) override { this->set_state(state, true, true); }
|
||||
void set_state(bool state, bool publish) override { this->set_state(state, publish, true); }
|
||||
void set_state(bool state, bool publish, bool send_to_nextion) override;
|
||||
|
||||
void send_state_to_nextion() override { this->set_state(this->state, false, true); };
|
||||
NextionQueueType get_queue_type() override { return NextionQueueType::SWITCH; }
|
||||
void set_state_from_string(const std::string &state_value, bool publish, bool send_to_nextion) override {}
|
||||
void set_state_from_int(int state_value, bool publish, bool send_to_nextion) override {
|
||||
this->set_state(state_value != 0, publish, send_to_nextion);
|
||||
}
|
||||
|
||||
protected:
|
||||
void write_state(bool state) override;
|
||||
};
|
||||
} // namespace nextion
|
||||
} // namespace esphome
|
||||
38
esphome/components/nextion/text_sensor/__init__.py
Normal file
38
esphome/components/nextion/text_sensor/__init__.py
Normal file
@ -0,0 +1,38 @@
|
||||
from esphome.components import text_sensor
|
||||
import esphome.config_validation as cv
|
||||
import esphome.codegen as cg
|
||||
from esphome.const import CONF_ID
|
||||
|
||||
from .. import nextion_ns, CONF_NEXTION_ID
|
||||
|
||||
from ..base_component import (
|
||||
setup_component_core_,
|
||||
CONFIG_TEXT_COMPONENT_SCHEMA,
|
||||
)
|
||||
|
||||
CODEOWNERS = ["@senexcrenshaw"]
|
||||
|
||||
NextionTextSensor = nextion_ns.class_(
|
||||
"NextionTextSensor", text_sensor.TextSensor, cg.PollingComponent
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
text_sensor.TEXT_SENSOR_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(NextionTextSensor),
|
||||
}
|
||||
)
|
||||
.extend(CONFIG_TEXT_COMPONENT_SCHEMA)
|
||||
.extend(cv.polling_component_schema("never"))
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
hub = await cg.get_variable(config[CONF_NEXTION_ID])
|
||||
var = cg.new_Pvariable(config[CONF_ID], hub)
|
||||
await cg.register_component(var, config)
|
||||
await text_sensor.register_text_sensor(var, config)
|
||||
|
||||
cg.add(hub.register_textsensor_component(var))
|
||||
|
||||
await setup_component_core_(var, config, ".txt")
|
||||
@ -0,0 +1,49 @@
|
||||
#include "nextion_textsensor.h"
|
||||
#include "esphome/core/util.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace nextion {
|
||||
static const char *const TAG = "nextion_textsensor";
|
||||
|
||||
void NextionTextSensor::process_text(const std::string &variable_name, const std::string &text_value) {
|
||||
if (!this->nextion_->is_setup())
|
||||
return;
|
||||
if (this->variable_name_ == variable_name) {
|
||||
this->publish_state(text_value);
|
||||
ESP_LOGD(TAG, "Processed text_sensor \"%s\" state \"%s\"", variable_name.c_str(), text_value.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void NextionTextSensor::update() {
|
||||
if (!this->nextion_->is_setup())
|
||||
return;
|
||||
this->nextion_->add_to_get_queue(this);
|
||||
}
|
||||
|
||||
void NextionTextSensor::set_state(const std::string &state, bool publish, bool send_to_nextion) {
|
||||
if (!this->nextion_->is_setup())
|
||||
return;
|
||||
|
||||
if (send_to_nextion) {
|
||||
if (this->nextion_->is_sleeping() || !this->visible_) {
|
||||
this->needs_to_send_update_ = true;
|
||||
} else {
|
||||
this->nextion_->add_no_result_to_queue_with_set(this, state);
|
||||
}
|
||||
}
|
||||
|
||||
if (publish) {
|
||||
this->publish_state(state);
|
||||
} else {
|
||||
this->state = state;
|
||||
this->has_state_ = true;
|
||||
}
|
||||
|
||||
this->update_component_settings();
|
||||
|
||||
ESP_LOGN(TAG, "Wrote state for text_sensor \"%s\" state \"%s\"", this->variable_name_.c_str(), state.c_str());
|
||||
}
|
||||
|
||||
} // namespace nextion
|
||||
} // namespace esphome
|
||||
32
esphome/components/nextion/text_sensor/nextion_textsensor.h
Normal file
32
esphome/components/nextion/text_sensor/nextion_textsensor.h
Normal file
@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/text_sensor/text_sensor.h"
|
||||
#include "../nextion_component.h"
|
||||
#include "../nextion_base.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace nextion {
|
||||
class NextionTextSensor;
|
||||
|
||||
class NextionTextSensor : public NextionComponent, public text_sensor::TextSensor, public PollingComponent {
|
||||
public:
|
||||
NextionTextSensor(NextionBase *nextion) { this->nextion_ = nextion; }
|
||||
void update() override;
|
||||
void update_component() override { this->update(); }
|
||||
void on_state_changed(const std::string &state);
|
||||
|
||||
void process_text(const std::string &variable_name, const std::string &text_value) override;
|
||||
|
||||
void set_state(const std::string &state, bool publish) override { this->set_state(state, publish, true); }
|
||||
void set_state(const std::string &state) override { this->set_state(state, true, true); }
|
||||
void set_state(const std::string &state, bool publish, bool send_to_nextion) override;
|
||||
|
||||
void send_state_to_nextion() override { this->set_state(this->state, false, true); };
|
||||
NextionQueueType get_queue_type() override { return NextionQueueType::TEXT_SENSOR; }
|
||||
void set_state_from_int(int state_value, bool publish, bool send_to_nextion) override {}
|
||||
void set_state_from_string(const std::string &state_value, bool publish, bool send_to_nextion) override {
|
||||
this->set_state(state_value, publish, send_to_nextion);
|
||||
}
|
||||
};
|
||||
} // namespace nextion
|
||||
} // namespace esphome
|
||||
@ -56,6 +56,7 @@ class ESP8266SoftwareSerial {
|
||||
class UARTComponent : public Component, public Stream {
|
||||
public:
|
||||
void set_baud_rate(uint32_t baud_rate) { baud_rate_ = baud_rate; }
|
||||
uint32_t get_baud_rate() const { return baud_rate_; }
|
||||
|
||||
uint32_t get_config();
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from helpers import git_ls_files, filter_changed
|
||||
import codecs
|
||||
import collections
|
||||
import fnmatch
|
||||
@ -12,7 +13,6 @@ import functools
|
||||
import argparse
|
||||
|
||||
sys.path.append(os.path.dirname(__file__))
|
||||
from helpers import git_ls_files, filter_changed
|
||||
|
||||
|
||||
def find_all(a_str, sub):
|
||||
@ -562,6 +562,7 @@ def lint_inclusive_language(fname, match):
|
||||
"esphome/components/number/number.h",
|
||||
"esphome/components/output/binary_output.h",
|
||||
"esphome/components/output/float_output.h",
|
||||
"esphome/components/nextion/nextion_base.h",
|
||||
"esphome/components/sensor/sensor.h",
|
||||
"esphome/components/stepper/stepper.h",
|
||||
"esphome/components/switch/switch.h",
|
||||
|
||||
@ -1055,10 +1055,6 @@ binary_sensor:
|
||||
pin: GPIO27
|
||||
threshold: 1000
|
||||
id: btn_left
|
||||
- platform: nextion
|
||||
page_id: 0
|
||||
component_id: 2
|
||||
name: 'Nextion Component 2 Touch'
|
||||
- platform: template
|
||||
name: 'Garage Door Open'
|
||||
id: garage_door
|
||||
@ -1882,11 +1878,6 @@ display:
|
||||
intensity: 3
|
||||
lambda: |-
|
||||
it.print("1234");
|
||||
- platform: nextion
|
||||
uart_id: uart0
|
||||
lambda: |-
|
||||
it.set_component_value("gauge", 50);
|
||||
it.set_component_text("textview", "Hello World!");
|
||||
- platform: pcd8544
|
||||
cs_pin: GPIO23
|
||||
dc_pin: GPIO23
|
||||
|
||||
@ -269,6 +269,7 @@ wled:
|
||||
|
||||
adalight:
|
||||
|
||||
|
||||
sensor:
|
||||
- platform: apds9960
|
||||
type: proximity
|
||||
@ -534,6 +535,15 @@ sensor:
|
||||
export_reactive_energy:
|
||||
name: 'Export Reactive Energy'
|
||||
|
||||
- platform: nextion
|
||||
id: testnumber
|
||||
name: 'testnumber'
|
||||
variable_name: testnumber
|
||||
- platform: nextion
|
||||
id: testwave
|
||||
name: 'testwave'
|
||||
component_id: 2
|
||||
wave_channel_id: 1
|
||||
time:
|
||||
- platform: homeassistant
|
||||
|
||||
@ -605,7 +615,14 @@ binary_sensor:
|
||||
binary_sensors:
|
||||
- id: custom_binary_sensor
|
||||
name: Custom Binary Sensor
|
||||
|
||||
- platform: nextion
|
||||
page_id: 0
|
||||
component_id: 2
|
||||
name: 'Nextion Component 2 Touch'
|
||||
- platform: nextion
|
||||
id: r0_sensor
|
||||
name: 'R0 Sensor'
|
||||
component_name: page0.r0
|
||||
globals:
|
||||
- id: my_global_string
|
||||
type: std::string
|
||||
@ -653,6 +670,11 @@ text_sensor:
|
||||
text_sensors:
|
||||
- id: custom_text_sensor
|
||||
name: Custom Text Sensor
|
||||
- platform: nextion
|
||||
name: text0
|
||||
id: text0
|
||||
update_interval: 4s
|
||||
component_name: text0
|
||||
|
||||
script:
|
||||
- id: my_script
|
||||
@ -704,6 +726,10 @@ switch:
|
||||
switches:
|
||||
- id: custom_switch
|
||||
name: Custom Switch
|
||||
- platform: nextion
|
||||
id: r0
|
||||
name: 'R0 Switch'
|
||||
component_name: page0.r0
|
||||
|
||||
custom_component:
|
||||
lambda: |-
|
||||
@ -1086,6 +1112,16 @@ display:
|
||||
id: my_matrix
|
||||
lambda: |-
|
||||
it.printdigit("hello");
|
||||
- platform: nextion
|
||||
uart_id: uart1
|
||||
tft_url: 'http://esphome.io/default35.tft'
|
||||
update_interval: 5s
|
||||
on_sleep:
|
||||
then:
|
||||
lambda: 'ESP_LOGD("display","Display went to sleep");'
|
||||
on_wake:
|
||||
then:
|
||||
lambda: 'ESP_LOGD("display","Display woke up");'
|
||||
|
||||
http_request:
|
||||
useragent: esphome/device
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user