mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +00:00 
			
		
		
		
	Nextion upload and sensors (#1464)
Co-authored-by: Senex Crenshaw <senexcrenshaw@gmail.com>
This commit is contained in:
		| @@ -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) | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user