mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 15:12:06 +00:00 
			
		
		
		
	Add qr code support for displays (#2952)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
This commit is contained in:
		
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							ef832becf1
						
					
				
				
					commit
					a718ac7ee0
				
			| @@ -137,6 +137,7 @@ esphome/components/preferences/* @esphome/core | ||||
| esphome/components/psram/* @esphome/core | ||||
| esphome/components/pulse_meter/* @stevebaxter | ||||
| esphome/components/pvvx_mithermometer/* @pasiz | ||||
| esphome/components/qr_code/* @wjtje | ||||
| esphome/components/rc522/* @glmnet | ||||
| esphome/components/rc522_i2c/* @glmnet | ||||
| esphome/components/rc522_spi/* @glmnet | ||||
|   | ||||
| @@ -252,6 +252,12 @@ void DisplayBuffer::legend(int x, int y, graph::Graph *graph, Color color_on) { | ||||
| } | ||||
| #endif  // USE_GRAPH | ||||
|  | ||||
| #ifdef USE_QR_CODE | ||||
| void DisplayBuffer::qr_code(int x, int y, qr_code::QrCode *qr_code, Color color_on, int scale) { | ||||
|   qr_code->draw(this, x, y, color_on, scale); | ||||
| } | ||||
| #endif  // USE_QR_CODE | ||||
|  | ||||
| void DisplayBuffer::get_text_bounds(int x, int y, const char *text, Font *font, TextAlign align, int *x1, int *y1, | ||||
|                                     int *width, int *height) { | ||||
|   int x_offset, baseline; | ||||
|   | ||||
| @@ -14,6 +14,10 @@ | ||||
| #include "esphome/components/graph/graph.h" | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_QR_CODE | ||||
| #include "esphome/components/qr_code/qr_code.h" | ||||
| #endif | ||||
|  | ||||
| namespace esphome { | ||||
| namespace display { | ||||
|  | ||||
| @@ -307,6 +311,17 @@ class DisplayBuffer { | ||||
|   void legend(int x, int y, graph::Graph *graph, Color color_on = COLOR_ON); | ||||
| #endif  // USE_GRAPH | ||||
|  | ||||
| #ifdef USE_QR_CODE | ||||
|   /** Draw the `qr_code` with the top-left corner at [x,y] to the screen. | ||||
|    * | ||||
|    * @param x The x coordinate of the upper left corner. | ||||
|    * @param y The y coordinate of the upper left corner. | ||||
|    * @param qr_code The qr_code to draw | ||||
|    * @param color_on The color to replace in binary images for the on bits. | ||||
|    */ | ||||
|   void qr_code(int x, int y, qr_code::QrCode *qr_code, Color color_on = COLOR_ON, int scale = 1); | ||||
| #endif | ||||
|  | ||||
|   /** Get the text bounds of the given string. | ||||
|    * | ||||
|    * @param x The x coordinate to place the string at, can be 0 if only interested in dimensions. | ||||
|   | ||||
							
								
								
									
										41
									
								
								esphome/components/qr_code/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								esphome/components/qr_code/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| import esphome.config_validation as cv | ||||
| import esphome.codegen as cg | ||||
| from esphome.const import CONF_ID, CONF_VALUE | ||||
|  | ||||
| CONF_SCALE = "scale" | ||||
| CONF_ECC = "ecc" | ||||
|  | ||||
| CODEOWNERS = ["@wjtje"] | ||||
|  | ||||
| DEPENDENCIES = ["display"] | ||||
| MULTI_CONF = True | ||||
|  | ||||
| qr_code_ns = cg.esphome_ns.namespace("qr_code") | ||||
| QRCode = qr_code_ns.class_("QrCode", cg.Component) | ||||
|  | ||||
| qrcodegen_Ecc = cg.esphome_ns.enum("qrcodegen_Ecc") | ||||
| ECC = { | ||||
|     "LOW": qrcodegen_Ecc.qrcodegen_Ecc_LOW, | ||||
|     "MEDIUM": qrcodegen_Ecc.qrcodegen_Ecc_MEDIUM, | ||||
|     "QUARTILE": qrcodegen_Ecc.qrcodegen_Ecc_QUARTILE, | ||||
|     "HIGH": qrcodegen_Ecc.qrcodegen_Ecc_HIGH, | ||||
| } | ||||
|  | ||||
| CONFIG_SCHEMA = cv.Schema( | ||||
|     { | ||||
|         cv.Required(CONF_ID): cv.declare_id(QRCode), | ||||
|         cv.Required(CONF_VALUE): cv.string, | ||||
|         cv.Optional(CONF_ECC, default="LOW"): cv.enum(ECC, upper=True), | ||||
|     } | ||||
| ) | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     cg.add_library("wjtje/qr-code-generator-library", "^1.7.0") | ||||
|  | ||||
|     var = cg.new_Pvariable(config[CONF_ID]) | ||||
|     cg.add(var.set_value(config[CONF_VALUE])) | ||||
|     cg.add(var.set_ecc(ECC[config[CONF_ECC]])) | ||||
|     await cg.register_component(var, config) | ||||
|  | ||||
|     cg.add_define("USE_QR_CODE") | ||||
							
								
								
									
										55
									
								
								esphome/components/qr_code/qr_code.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								esphome/components/qr_code/qr_code.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | ||||
| #include "qr_code.h" | ||||
| #include "esphome/components/display/display_buffer.h" | ||||
| #include "esphome/core/color.h" | ||||
| #include "esphome/core/log.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace qr_code { | ||||
|  | ||||
| static const char *const TAG = "qr_code"; | ||||
|  | ||||
| void QrCode::dump_config() { | ||||
|   ESP_LOGCONFIG(TAG, "QR code:"); | ||||
|   ESP_LOGCONFIG(TAG, "  Value: '%s'", this->value_.c_str()); | ||||
| } | ||||
|  | ||||
| void QrCode::set_value(const std::string &value) { | ||||
|   this->value_ = value; | ||||
|   this->needs_update_ = true; | ||||
| } | ||||
|  | ||||
| void QrCode::set_ecc(qrcodegen_Ecc ecc) { | ||||
|   this->ecc_ = ecc; | ||||
|   this->needs_update_ = true; | ||||
| } | ||||
|  | ||||
| void QrCode::generate_qr_code() { | ||||
|   ESP_LOGV(TAG, "Generating QR code..."); | ||||
|   uint8_t tempbuffer[qrcodegen_BUFFER_LEN_MAX]; | ||||
|  | ||||
|   if (!qrcodegen_encodeText(this->value_.c_str(), tempbuffer, this->qr_, this->ecc_, qrcodegen_VERSION_MIN, | ||||
|                             qrcodegen_VERSION_MAX, qrcodegen_Mask_AUTO, true)) { | ||||
|     ESP_LOGE(TAG, "Failed to generate QR code"); | ||||
|   } | ||||
| } | ||||
|  | ||||
| void QrCode::draw(display::DisplayBuffer *buff, uint16_t x_offset, uint16_t y_offset, Color color, int scale) { | ||||
|   ESP_LOGV(TAG, "Drawing QR code at (%d, %d)", x_offset, y_offset); | ||||
|  | ||||
|   if (this->needs_update_) { | ||||
|     this->generate_qr_code(); | ||||
|     this->needs_update_ = false; | ||||
|   } | ||||
|  | ||||
|   uint8_t qrcode_width = qrcodegen_getSize(this->qr_); | ||||
|  | ||||
|   for (int y = 0; y < qrcode_width * scale; y++) { | ||||
|     for (int x = 0; x < qrcode_width * scale; x++) { | ||||
|       if (qrcodegen_getModule(this->qr_, x / scale, y / scale)) { | ||||
|         buff->draw_pixel_at(x_offset + x, y_offset + y, color); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| }  // namespace qr_code | ||||
| }  // namespace esphome | ||||
							
								
								
									
										34
									
								
								esphome/components/qr_code/qr_code.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								esphome/components/qr_code/qr_code.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| #pragma once | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/color.h" | ||||
|  | ||||
| #include <cstdint> | ||||
|  | ||||
| #include "qrcodegen.h" | ||||
|  | ||||
| namespace esphome { | ||||
| // forward declare DisplayBuffer | ||||
| namespace display { | ||||
| class DisplayBuffer; | ||||
| }  // namespace display | ||||
|  | ||||
| namespace qr_code { | ||||
| class QrCode : public Component { | ||||
|  public: | ||||
|   void draw(display::DisplayBuffer *buff, uint16_t x_offset, uint16_t y_offset, Color color, int scale); | ||||
|  | ||||
|   void dump_config() override; | ||||
|  | ||||
|   void set_value(const std::string &value); | ||||
|   void set_ecc(qrcodegen_Ecc ecc); | ||||
|  | ||||
|   void generate_qr_code(); | ||||
|  | ||||
|  protected: | ||||
|   std::string value_; | ||||
|   qrcodegen_Ecc ecc_; | ||||
|   bool needs_update_ = true; | ||||
|   uint8_t qr_[qrcodegen_BUFFER_LEN_MAX]; | ||||
| }; | ||||
| }  // namespace qr_code | ||||
| }  // namespace esphome | ||||
| @@ -32,6 +32,7 @@ | ||||
| #define USE_OTA_PASSWORD | ||||
| #define USE_OTA_STATE_CALLBACK | ||||
| #define USE_POWER_SUPPLY | ||||
| #define USE_QR_CODE | ||||
| #define USE_SELECT | ||||
| #define USE_SENSOR | ||||
| #define USE_STATUS_LED | ||||
|   | ||||
| @@ -37,6 +37,7 @@ lib_deps = | ||||
|     makuna/NeoPixelBus@2.6.9               ; neopixelbus | ||||
|     esphome/Improv@1.1.0                   ; improv_serial / esp32_improv | ||||
|     bblanchon/ArduinoJson@6.18.5           ; json | ||||
|     wjtje/qr-code-generator-library@1.7.0  ; qr_code | ||||
| build_flags = | ||||
|     -DESPHOME_LOG_LEVEL=ESPHOME_LOG_LEVEL_VERY_VERBOSE | ||||
| src_filter = | ||||
|   | ||||
| @@ -2154,6 +2154,7 @@ display: | ||||
|     pages: | ||||
|       - id: page1 | ||||
|         lambda: |- | ||||
|           it.qr_code(0, 0, id(homepage_qr)); | ||||
|           it.rectangle(0, 0, it.get_width(), it.get_height()); | ||||
|       - id: page2 | ||||
|         lambda: |- | ||||
| @@ -2577,3 +2578,7 @@ select: | ||||
|       - one | ||||
|       - two | ||||
|     optimistic: true | ||||
|  | ||||
| qr_code: | ||||
|   - id: homepage_qr | ||||
|     value: https://esphome.io/index.html | ||||
|   | ||||
| @@ -1351,6 +1351,10 @@ daly_bms: | ||||
|   update_interval: 20s | ||||
|   uart_id: uart1 | ||||
|  | ||||
| qr_code: | ||||
|   - id: homepage_qr | ||||
|     value: https://esphome.io/index.html | ||||
|  | ||||
| button: | ||||
|   - platform: output | ||||
|     id: output_button | ||||
|   | ||||
		Reference in New Issue
	
	Block a user