mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	[sx1509] add support for keys (#8413)
Co-authored-by: Samuel Sieb <samuel@sieb.net> Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
		| @@ -1,6 +1,6 @@ | |||||||
| from esphome import pins | from esphome import automation, pins | ||||||
| import esphome.codegen as cg | import esphome.codegen as cg | ||||||
| from esphome.components import i2c | from esphome.components import i2c, key_provider | ||||||
| import esphome.config_validation as cv | import esphome.config_validation as cv | ||||||
| from esphome.const import ( | from esphome.const import ( | ||||||
|     CONF_ID, |     CONF_ID, | ||||||
| @@ -8,13 +8,16 @@ from esphome.const import ( | |||||||
|     CONF_INVERTED, |     CONF_INVERTED, | ||||||
|     CONF_MODE, |     CONF_MODE, | ||||||
|     CONF_NUMBER, |     CONF_NUMBER, | ||||||
|  |     CONF_ON_KEY, | ||||||
|     CONF_OPEN_DRAIN, |     CONF_OPEN_DRAIN, | ||||||
|     CONF_OUTPUT, |     CONF_OUTPUT, | ||||||
|     CONF_PULLDOWN, |     CONF_PULLDOWN, | ||||||
|     CONF_PULLUP, |     CONF_PULLUP, | ||||||
|  |     CONF_TRIGGER_ID, | ||||||
| ) | ) | ||||||
|  |  | ||||||
| CONF_KEYPAD = "keypad" | CONF_KEYPAD = "keypad" | ||||||
|  | CONF_KEYS = "keys" | ||||||
| CONF_KEY_ROWS = "key_rows" | CONF_KEY_ROWS = "key_rows" | ||||||
| CONF_KEY_COLUMNS = "key_columns" | CONF_KEY_COLUMNS = "key_columns" | ||||||
| CONF_SLEEP_TIME = "sleep_time" | CONF_SLEEP_TIME = "sleep_time" | ||||||
| @@ -22,22 +25,47 @@ CONF_SCAN_TIME = "scan_time" | |||||||
| CONF_DEBOUNCE_TIME = "debounce_time" | CONF_DEBOUNCE_TIME = "debounce_time" | ||||||
| CONF_SX1509_ID = "sx1509_id" | CONF_SX1509_ID = "sx1509_id" | ||||||
|  |  | ||||||
|  | AUTO_LOAD = ["key_provider"] | ||||||
| DEPENDENCIES = ["i2c"] | DEPENDENCIES = ["i2c"] | ||||||
| MULTI_CONF = True | MULTI_CONF = True | ||||||
|  |  | ||||||
| sx1509_ns = cg.esphome_ns.namespace("sx1509") | sx1509_ns = cg.esphome_ns.namespace("sx1509") | ||||||
|  |  | ||||||
| SX1509Component = sx1509_ns.class_("SX1509Component", cg.Component, i2c.I2CDevice) | SX1509Component = sx1509_ns.class_( | ||||||
|  |     "SX1509Component", cg.Component, i2c.I2CDevice, key_provider.KeyProvider | ||||||
|  | ) | ||||||
| SX1509GPIOPin = sx1509_ns.class_("SX1509GPIOPin", cg.GPIOPin) | SX1509GPIOPin = sx1509_ns.class_("SX1509GPIOPin", cg.GPIOPin) | ||||||
|  | SX1509KeyTrigger = sx1509_ns.class_( | ||||||
|  |     "SX1509KeyTrigger", automation.Trigger.template(cg.uint8) | ||||||
|  | ) | ||||||
|  |  | ||||||
| KEYPAD_SCHEMA = cv.Schema( |  | ||||||
|  | def check_keys(config): | ||||||
|  |     if CONF_KEYS in config: | ||||||
|  |         if len(config[CONF_KEYS]) != config[CONF_KEY_ROWS] * config[CONF_KEY_COLUMNS]: | ||||||
|  |             raise cv.Invalid( | ||||||
|  |                 "The number of key codes must equal the number of rows * columns" | ||||||
|  |             ) | ||||||
|  |     return config | ||||||
|  |  | ||||||
|  |  | ||||||
|  | KEYPAD_SCHEMA = cv.All( | ||||||
|  |     cv.Schema( | ||||||
|         { |         { | ||||||
|         cv.Required(CONF_KEY_ROWS): cv.int_range(min=1, max=8), |             cv.Required(CONF_KEY_ROWS): cv.int_range(min=2, max=8), | ||||||
|             cv.Required(CONF_KEY_COLUMNS): cv.int_range(min=1, max=8), |             cv.Required(CONF_KEY_COLUMNS): cv.int_range(min=1, max=8), | ||||||
|             cv.Optional(CONF_SLEEP_TIME): cv.int_range(min=128, max=8192), |             cv.Optional(CONF_SLEEP_TIME): cv.int_range(min=128, max=8192), | ||||||
|             cv.Optional(CONF_SCAN_TIME): cv.int_range(min=1, max=128), |             cv.Optional(CONF_SCAN_TIME): cv.int_range(min=1, max=128), | ||||||
|             cv.Optional(CONF_DEBOUNCE_TIME): cv.int_range(min=1, max=64), |             cv.Optional(CONF_DEBOUNCE_TIME): cv.int_range(min=1, max=64), | ||||||
|  |             cv.Optional(CONF_KEYS): cv.string, | ||||||
|  |             cv.Optional(CONF_ON_KEY): automation.validate_automation( | ||||||
|  |                 { | ||||||
|  |                     cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SX1509KeyTrigger), | ||||||
|                 } |                 } | ||||||
|  |             ), | ||||||
|  |         } | ||||||
|  |     ), | ||||||
|  |     check_keys, | ||||||
| ) | ) | ||||||
|  |  | ||||||
| CONFIG_SCHEMA = ( | CONFIG_SCHEMA = ( | ||||||
| @@ -56,17 +84,22 @@ async def to_code(config): | |||||||
|     var = cg.new_Pvariable(config[CONF_ID]) |     var = cg.new_Pvariable(config[CONF_ID]) | ||||||
|     await cg.register_component(var, config) |     await cg.register_component(var, config) | ||||||
|     await i2c.register_i2c_device(var, config) |     await i2c.register_i2c_device(var, config) | ||||||
|     if CONF_KEYPAD in config: |     if conf := config.get(CONF_KEYPAD): | ||||||
|         keypad = config[CONF_KEYPAD] |         cg.add(var.set_rows_cols(conf[CONF_KEY_ROWS], conf[CONF_KEY_COLUMNS])) | ||||||
|         cg.add(var.set_rows_cols(keypad[CONF_KEY_ROWS], keypad[CONF_KEY_COLUMNS])) |  | ||||||
|         if ( |         if ( | ||||||
|             CONF_SLEEP_TIME in keypad |             CONF_SLEEP_TIME in conf | ||||||
|             and CONF_SCAN_TIME in keypad |             and CONF_SCAN_TIME in conf | ||||||
|             and CONF_DEBOUNCE_TIME in keypad |             and CONF_DEBOUNCE_TIME in conf | ||||||
|         ): |         ): | ||||||
|             cg.add(var.set_sleep_time(keypad[CONF_SLEEP_TIME])) |             cg.add(var.set_sleep_time(conf[CONF_SLEEP_TIME])) | ||||||
|             cg.add(var.set_scan_time(keypad[CONF_SCAN_TIME])) |             cg.add(var.set_scan_time(conf[CONF_SCAN_TIME])) | ||||||
|             cg.add(var.set_debounce_time(keypad[CONF_DEBOUNCE_TIME])) |             cg.add(var.set_debounce_time(conf[CONF_DEBOUNCE_TIME])) | ||||||
|  |         if keys := conf.get(CONF_KEYS): | ||||||
|  |             cg.add(var.set_keys(keys)) | ||||||
|  |         for tconf in conf.get(CONF_ON_KEY, []): | ||||||
|  |             trigger = cg.new_Pvariable(tconf[CONF_TRIGGER_ID]) | ||||||
|  |             cg.add(var.register_key_trigger(trigger)) | ||||||
|  |             await automation.build_automation(trigger, [(cg.uint8, "x")], tconf) | ||||||
|  |  | ||||||
|  |  | ||||||
| def validate_mode(value): | def validate_mode(value): | ||||||
|   | |||||||
| @@ -48,6 +48,30 @@ void SX1509Component::loop() { | |||||||
|     uint16_t key_data = this->read_key_data(); |     uint16_t key_data = this->read_key_data(); | ||||||
|     for (auto *binary_sensor : this->keypad_binary_sensors_) |     for (auto *binary_sensor : this->keypad_binary_sensors_) | ||||||
|       binary_sensor->process(key_data); |       binary_sensor->process(key_data); | ||||||
|  |     if (this->keys_.empty()) | ||||||
|  |       return; | ||||||
|  |     if (key_data == 0) { | ||||||
|  |       this->last_key_ = 0; | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     int row, col; | ||||||
|  |     for (row = 0; row < 7; row++) { | ||||||
|  |       if (key_data & (1 << row)) | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     for (col = 8; col < 15; col++) { | ||||||
|  |       if (key_data & (1 << col)) | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     col -= 8; | ||||||
|  |     uint8_t key = this->keys_[row * this->cols_ + col]; | ||||||
|  |     if (key == this->last_key_) | ||||||
|  |       return; | ||||||
|  |     this->last_key_ = key; | ||||||
|  |     ESP_LOGV(TAG, "row %d, col %d, key '%c'", row, col, key); | ||||||
|  |     for (auto &trigger : this->key_triggers_) | ||||||
|  |       trigger->trigger(key); | ||||||
|  |     this->send_key_(key); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -230,9 +254,9 @@ void SX1509Component::setup_keypad_() { | |||||||
|   scan_time_bits &= 0b111;  // Scan time is bits 2:0 |   scan_time_bits &= 0b111;  // Scan time is bits 2:0 | ||||||
|   temp_byte = sleep_time_ | scan_time_bits; |   temp_byte = sleep_time_ | scan_time_bits; | ||||||
|   this->write_byte(REG_KEY_CONFIG_1, temp_byte); |   this->write_byte(REG_KEY_CONFIG_1, temp_byte); | ||||||
|   rows_ = (rows_ - 1) & 0b111;  // 0 = off, 0b001 = 2 rows, 0b111 = 8 rows, etc. |   temp_byte = ((this->rows_ - 1) & 0b111) << 3;  // 0 = off, 0b001 = 2 rows, 0b111 = 8 rows, etc. | ||||||
|   cols_ = (cols_ - 1) & 0b111;  // 0b000 = 1 column, ob111 = 8 columns, etc. |   temp_byte |= (this->cols_ - 1) & 0b111;        // 0b000 = 1 column, ob111 = 8 columns, etc. | ||||||
|   this->write_byte(REG_KEY_CONFIG_2, (rows_ << 3) | cols_); |   this->write_byte(REG_KEY_CONFIG_2, temp_byte); | ||||||
| } | } | ||||||
|  |  | ||||||
| uint16_t SX1509Component::read_key_data() { | uint16_t SX1509Component::read_key_data() { | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include "esphome/components/i2c/i2c.h" | #include "esphome/components/i2c/i2c.h" | ||||||
|  | #include "esphome/components/key_provider/key_provider.h" | ||||||
| #include "esphome/core/component.h" | #include "esphome/core/component.h" | ||||||
| #include "esphome/core/hal.h" | #include "esphome/core/hal.h" | ||||||
| #include "sx1509_gpio_pin.h" | #include "sx1509_gpio_pin.h" | ||||||
| @@ -27,7 +28,9 @@ class SX1509Processor { | |||||||
|   virtual void process(uint16_t data){}; |   virtual void process(uint16_t data){}; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| class SX1509Component : public Component, public i2c::I2CDevice { | class SX1509KeyTrigger : public Trigger<uint8_t> {}; | ||||||
|  |  | ||||||
|  | class SX1509Component : public Component, public i2c::I2CDevice, public key_provider::KeyProvider { | ||||||
|  public: |  public: | ||||||
|   SX1509Component() = default; |   SX1509Component() = default; | ||||||
|  |  | ||||||
| @@ -47,12 +50,14 @@ class SX1509Component : public Component, public i2c::I2CDevice { | |||||||
|     this->cols_ = cols; |     this->cols_ = cols; | ||||||
|     this->has_keypad_ = true; |     this->has_keypad_ = true; | ||||||
|   }; |   }; | ||||||
|  |   void set_keys(std::string keys) { this->keys_ = std::move(keys); }; | ||||||
|   void set_sleep_time(uint16_t sleep_time) { this->sleep_time_ = sleep_time; }; |   void set_sleep_time(uint16_t sleep_time) { this->sleep_time_ = sleep_time; }; | ||||||
|   void set_scan_time(uint8_t scan_time) { this->scan_time_ = scan_time; }; |   void set_scan_time(uint8_t scan_time) { this->scan_time_ = scan_time; }; | ||||||
|   void set_debounce_time(uint8_t debounce_time = 1) { this->debounce_time_ = debounce_time; }; |   void set_debounce_time(uint8_t debounce_time = 1) { this->debounce_time_ = debounce_time; }; | ||||||
|   void register_keypad_binary_sensor(SX1509Processor *binary_sensor) { |   void register_keypad_binary_sensor(SX1509Processor *binary_sensor) { | ||||||
|     this->keypad_binary_sensors_.push_back(binary_sensor); |     this->keypad_binary_sensors_.push_back(binary_sensor); | ||||||
|   } |   } | ||||||
|  |   void register_key_trigger(SX1509KeyTrigger *trig) { this->key_triggers_.push_back(trig); }; | ||||||
|   void setup_led_driver(uint8_t pin); |   void setup_led_driver(uint8_t pin); | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
| @@ -65,10 +70,13 @@ class SX1509Component : public Component, public i2c::I2CDevice { | |||||||
|   bool has_keypad_ = false; |   bool has_keypad_ = false; | ||||||
|   uint8_t rows_ = 0; |   uint8_t rows_ = 0; | ||||||
|   uint8_t cols_ = 0; |   uint8_t cols_ = 0; | ||||||
|  |   std::string keys_; | ||||||
|   uint16_t sleep_time_ = 128; |   uint16_t sleep_time_ = 128; | ||||||
|   uint8_t scan_time_ = 1; |   uint8_t scan_time_ = 1; | ||||||
|   uint8_t debounce_time_ = 1; |   uint8_t debounce_time_ = 1; | ||||||
|  |   uint8_t last_key_ = 0; | ||||||
|   std::vector<SX1509Processor *> keypad_binary_sensors_; |   std::vector<SX1509Processor *> keypad_binary_sensors_; | ||||||
|  |   std::vector<SX1509KeyTrigger *> key_triggers_; | ||||||
|  |  | ||||||
|   uint32_t last_loop_timestamp_ = 0; |   uint32_t last_loop_timestamp_ = 0; | ||||||
|   const uint32_t min_loop_period_ = 15;  // ms |   const uint32_t min_loop_period_ = 15;  // ms | ||||||
|   | |||||||
| @@ -6,6 +6,12 @@ i2c: | |||||||
| sx1509: | sx1509: | ||||||
|   - id: sx1509_hub |   - id: sx1509_hub | ||||||
|     address: 0x3E |     address: 0x3E | ||||||
|  |     keypad: | ||||||
|  |       key_rows: 2 | ||||||
|  |       key_columns: 2 | ||||||
|  |       keys: abcd | ||||||
|  |       on_key: | ||||||
|  |         - lambda: ESP_LOGD("test", "got key '%c'", x); | ||||||
|  |  | ||||||
| binary_sensor: | binary_sensor: | ||||||
|   - platform: gpio |   - platform: gpio | ||||||
| @@ -13,6 +19,11 @@ binary_sensor: | |||||||
|     pin: |     pin: | ||||||
|       sx1509: sx1509_hub |       sx1509: sx1509_hub | ||||||
|       number: 3 |       number: 3 | ||||||
|  |   - platform: sx1509 | ||||||
|  |     sx1509_id: sx1509_hub | ||||||
|  |     name: "keypadkey_0" | ||||||
|  |     row: 0 | ||||||
|  |     col: 0 | ||||||
|  |  | ||||||
| switch: | switch: | ||||||
|   - platform: gpio |   - platform: gpio | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user