Skip to content

Commit

Permalink
add component for CardKB
Browse files Browse the repository at this point in the history
  • Loading branch information
Samuel Sieb committed Jan 31, 2024
1 parent 4f6af89 commit 2d25ecf
Show file tree
Hide file tree
Showing 6 changed files with 184 additions and 0 deletions.
20 changes: 20 additions & 0 deletions components/cardkb/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# CardKB: https://shop.m5stack.com/products/cardkb-mini-keyboard

This component is for the CardKB device. Define a `cardkb` component then add `binary_sensor`s to handle individual keys.
If you want automatic handling for multiple keys, e.g. PIN entry, use the `key_collector` component.

Example:
```yaml
cardkb:
- id: mykb #optional

binary_sensor:
- platform: cardkb
cardkb_id: mykb #optional if only one keyboard
id: a_key
key: a
- platform: cardkb
id: enter_key
key: 13
```
27 changes: 27 additions & 0 deletions components/cardkb/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c, key_provider
from esphome.const import CONF_ID

CODEOWNERS = ["@ssieb"]
DEPENDENCIES = ["i2c"]

AUTO_LOAD = [ "key_provider" ]

MULTI_CONF = True

cardkb_ns = cg.esphome_ns.namespace('cardkb')
CardKB = cardkb_ns.class_('CardKB', cg.Component, i2c.I2CDevice, key_provider.KeyProvider)

CONF_CARDKB_ID = 'cardkb_id'

CONFIG_SCHEMA = i2c.i2c_device_schema(None).extend(
{
cv.GenerateID(): cv.declare_id(CardKB),
}
)

async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
35 changes: 35 additions & 0 deletions components/cardkb/binary_sensor/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import binary_sensor
from esphome.const import CONF_ID, CONF_KEY
from .. import CardKB, cardkb_ns, CONF_CARDKB_ID

DEPENDENCIES = ['cardkb']

CardKBBinarySensor = cardkb_ns.class_('CardKBBinarySensor', binary_sensor.BinarySensor)

def key(value):
if isinstance(value, str):
if len(value) == 1:
return ord(value[0])
elif isinstance(value, int):
if value > 0 and value <= 255:
return value
raise cv.Invalid("Number must be between 1 and 255")
raise cv.Invalid("Must be a string with one character or a number")

CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(CardKBBinarySensor),
cv.GenerateID(CONF_CARDKB_ID): cv.use_id(CardKB),
cv.Required(CONF_KEY): key,
}
)


async def to_code(config):
key = config[CONF_KEY]
var = cg.new_Pvariable(config[CONF_ID], config[CONF_KEY])
await binary_sensor.register_binary_sensor(var, config)
cardkb = await cg.get_variable(config[CONF_CARDKB_ID])
cg.add(cardkb.register_listener(var))
28 changes: 28 additions & 0 deletions components/cardkb/binary_sensor/cardkb_binary_sensor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#pragma once

#include "esphome/components/cardkb/cardkb.h"
#include "esphome/components/binary_sensor/binary_sensor.h"

namespace esphome {
namespace cardkb {

class CardKBBinarySensor : public CardKBListener, public binary_sensor::BinarySensor {
public:
CardKBBinarySensor(uint8_t key) : key_(key) {};

void key_pressed(uint8_t key) override {
if (key == this->key_)
this->publish_state(true);
}

void key_released(uint8_t key) override {
if (key == this->key_)
this->publish_state(false);
}

protected:
uint8_t key_;
};

} // namespace cardkb
} // namespace esphome
42 changes: 42 additions & 0 deletions components/cardkb/cardkb.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#include "cardkb.h"
#include "esphome/core/hal.h"
#include "esphome/core/log.h"

namespace esphome {
namespace cardkb {

static const char *TAG = "cardkb";

void CardKB::loop() {
uint8_t c;
static uint32_t last_error = 0;
c = this->pressed_key_;
if (c) {
for (auto &listener : this->listeners_)
listener->key_released(c);
this->pressed_key_ = 0;
}
i2c::ErrorCode res = this->read(&c, 1);
if ((res != i2c::NO_ERROR) || (c == 0))
return;
if (c < 32)
ESP_LOGD(TAG, "keycode '%d' pressed", c);
else
ESP_LOGD(TAG, "key '%c' pressed", c);
for (auto &listener : this->listeners_)
listener->key_pressed(c);
this->pressed_key_ = c;
this->send_key_(c);
}

void CardKB::dump_config() {
ESP_LOGCONFIG(TAG, "CardKB:");
}

void CardKB::register_listener(CardKBListener *listener) {
this->listeners_.push_back(listener);
}

} // namespace cardkb
} // namespace esphome

32 changes: 32 additions & 0 deletions components/cardkb/cardkb.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#pragma once

#include "esphome/components/i2c/i2c.h"
#include "esphome/components/key_provider/key_provider.h"
#include "esphome/core/component.h"

namespace esphome {
namespace cardkb {

class CardKBListener {
public:
virtual void key_pressed(uint8_t key) {};
virtual void key_released(uint8_t key) {};
};

class CardKB : public key_provider::KeyProvider, public Component, public i2c::I2CDevice {
public:
void loop() override;
void dump_config() override;

void register_listener(CardKBListener *listener);

protected:
int pressed_key_ = -1;

std::vector<CardKBListener *> listeners_{};
};

} // namespace cardkb
} // namespace esphome


0 comments on commit 2d25ecf

Please sign in to comment.