Browse Source

Implemented the I2C messgage to event type parser.

pull/4/head
Maurice Makaay 3 years ago
parent
commit
b340ede5b3
4 changed files with 165 additions and 21 deletions
  1. +4
    -3
      __init__.py
  2. +150
    -10
      front_panel_hal.h
  3. +5
    -2
      light/automation.h
  4. +6
    -6
      light/light_output.h

+ 4
- 3
__init__.py View File

@ -7,9 +7,8 @@ from esphome.components.i2c import I2CComponent, I2CDevice
from esphome.core import coroutine from esphome.core import coroutine
from esphome.core import CORE from esphome.core import CORE
from esphome.const import ( from esphome.const import (
CONF_ID, CONF_RED, CONF_GREEN, CONF_BLUE, CONF_WHITE, CONF_TRIGGER_PIN,
CONF_SDA, CONF_SCL, CONF_OUTPUT_ID, CONF_TRIGGER_ID, CONF_PIN,
CONF_FREQUENCY, CONF_CHANNEL, CONF_PLATFORM,
CONF_RED, CONF_GREEN, CONF_BLUE, CONF_WHITE, CONF_TRIGGER_PIN,
CONF_SDA, CONF_SCL, CONF_ADDRESS, CONF_PLATFORM
) )
CODEOWNERS = ["@mmakaay"] CODEOWNERS = ["@mmakaay"]
@ -58,6 +57,7 @@ def make_config_schema():
cv.GenerateID(CONF_FP_I2C_ID): cv.use_id(I2CComponent), cv.GenerateID(CONF_FP_I2C_ID): cv.use_id(I2CComponent),
cv.Optional(CONF_SDA, default="GPIO21"): pins.validate_gpio_pin, cv.Optional(CONF_SDA, default="GPIO21"): pins.validate_gpio_pin,
cv.Optional(CONF_SCL, default="GPIO19"): pins.validate_gpio_pin, cv.Optional(CONF_SCL, default="GPIO19"): pins.validate_gpio_pin,
cv.Optional(CONF_ADDRESS, default="0x2C"): cv.i2c_address,
cv.Optional(CONF_TRIGGER_PIN, default="GPIO16"): cv.All( cv.Optional(CONF_TRIGGER_PIN, default="GPIO16"): cv.All(
pins.validate_gpio_pin, pins.validate_gpio_pin,
pins.validate_has_interrupt pins.validate_has_interrupt
@ -119,6 +119,7 @@ def make_front_panel_hal(config):
cg.add(fp_i2c_var.set_scl_pin(config[CONF_SCL])) cg.add(fp_i2c_var.set_scl_pin(config[CONF_SCL]))
cg.add(fp_i2c_var.set_scan(True)) cg.add(fp_i2c_var.set_scan(True))
cg.add(fp_hal.set_i2c_parent(fp_i2c_var)) cg.add(fp_hal.set_i2c_parent(fp_i2c_var))
cg.add(fp_hal.set_i2c_address(config[CONF_ADDRESS]))
def to_code(config): def to_code(config):
# Dirty little hack to make the ESPHome component loader inlcude # Dirty little hack to make the ESPHome component loader inlcude


+ 150
- 10
front_panel_hal.h View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <array>
#include "common.h" #include "common.h"
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/core/esphal.h" #include "esphome/core/esphal.h"
@ -9,39 +10,178 @@ namespace esphome {
namespace yeelight { namespace yeelight {
namespace bs2 { namespace bs2 {
static const uint8_t MSG_LEN = 7;
using MSG = uint8_t[7];
static const MSG READY_FOR_EV = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 };
static const MSG TURN_ON = { 0x02, 0x03, 0x5E, 0x00, 0x64, 0x00, 0x00 };
static const MSG TURN_OFF = { 0x02, 0x03, 0x0C, 0x00, 0x64, 0x00, 0x00 };
static const MSG SET_LEVEL_1 = { 0x02, 0x03, 0x5E, 0x00, 0x64, 0x00, 0x00 };
static const MSG SET_LEVEL_2 = { 0x02, 0x03, 0x5F, 0x00, 0x64, 0x00, 0x00 };
static const MSG SET_LEVEL_3 = { 0x02, 0x03, 0x5F, 0x80, 0x64, 0x00, 0x00 };
static const MSG SET_LEVEL_4 = { 0x02, 0x03, 0x5F, 0xC0, 0x64, 0x00, 0x00 };
static const MSG SET_LEVEL_5 = { 0x02, 0x03, 0x5F, 0xE0, 0x64, 0x00, 0x00 };
static const MSG SET_LEVEL_6 = { 0x02, 0x03, 0x5F, 0xF0, 0x64, 0x00, 0x00 };
static const MSG SET_LEVEL_7 = { 0x02, 0x03, 0x5F, 0xF8, 0x64, 0x00, 0x00 };
static const MSG SET_LEVEL_8 = { 0x02, 0x03, 0x5F, 0xFC, 0x64, 0x00, 0x00 };
static const MSG SET_LEVEL_9 = { 0x02, 0x03, 0x5F, 0xFE, 0x64, 0x00, 0x00 };
static const MSG SET_LEVEL_10 = { 0x02, 0x03, 0x5F, 0xFF, 0x64, 0x00, 0x00 };
enum FrontPanelButton {
ButtonUnknown,
ButtonPower,
ButtonColor,
ButtonSlider
};
enum FrontPanelEventType {
TypeUnknown,
TypeTouch,
TypeRelease
};
class FrontPanelEvent {
public:
bool valid;
FrontPanelEventType type;
FrontPanelButton button;
uint8_t level;
MSG message;
void parse(uint8_t *m) {
memcpy(message, m, MSG_LEN);
type = TypeUnknown;
button = ButtonUnknown;
valid = true;
level = 0;
// All events start with 04:04:01:00.
if (m[0] != 0x04 || m[1] != 0x04 || m[2] != 0x01 || m[3] != 0x00) {
valid = false;
return;
}
// Next byte determines the button that is touched.
// All remaining bytes determine the event for that button.
switch (m[4]) {
case 0x01:
button = ButtonPower;
if (m[5] == 0x01 && m[6] == 0x03) {
type = TypeTouch;
} else if (m[5] == 0x02 && m[6] == 0x04) {
type = TypeRelease;
} else {
valid = false;
}
break;
case 0x02:
button = ButtonColor;
if (m[5] == 0x01 && m[6] == 0x04) {
type = TypeTouch;
} else if (m[5] == 0x02 && m[6] == 0x05) {
type = TypeRelease;
} else {
valid = false;
}
break;
case 0x03:
case 0x04:
button = ButtonSlider;
type = m[4] == 0x03 ? TypeTouch : TypeRelease;
if ((m[6] - m[5] - m[4] - 0x01) != 0) {
valid = false;
} else if (m[5] > 0x16 || m[5] < 0x01) {
valid = false;
} else {
level = 0x17 - m[5];
}
break;
default:
valid = false;
return;
}
}
void log() {
if (button == ButtonSlider) {
ESP_LOGI(TAG, "Event %0x:%0x:%0x:%0x:%0x:%0x:%0x => ok=%s, button=%s, type=%s, level=%d",
message[0], message[1], message[2], message[3], message[4],
message[5], message[6],
(valid ? "Y" : "N"), button_str_(), type_str_(), level);
} else {
ESP_LOGI(TAG, "Event %0x:%0x:%0x:%0x:%0x:%0x:%0x => ok=%s, button=%s, type=%s",
message[0], message[1], message[2], message[3], message[4],
message[5], message[6],
(valid ? "Y" : "N"), button_str_(), type_str_());
}
}
protected:
const char *button_str_() {
switch (button) {
case ButtonPower: return "POWER"; break;
case ButtonColor: return "COLOR"; break;
case ButtonSlider: return "SLIDER"; break;
default: return "ERROR"; break;
}
}
const char *type_str_() {
switch (type) {
case TypeTouch: return "TOUCH"; break;
case TypeRelease: return "RELEASE"; break;
default: return "ERROR";
}
}
};
class FrontPanelHAL : public Component, public i2c::I2CDevice { class FrontPanelHAL : public Component, public i2c::I2CDevice {
public: public:
void set_trigger_pin(GPIOPin *pin) { i2c_trigger_pin_ = pin; }
FrontPanelEvent ev;
void set_trigger_pin(GPIOPin *pin) { trigger_pin_ = pin; }
void setup() { void setup() {
ESP_LOGCONFIG(TAG, "Setting up I2C trigger pin interrupt..."); ESP_LOGCONFIG(TAG, "Setting up I2C trigger pin interrupt...");
this->i2c_trigger_pin_->setup();
this->i2c_trigger_pin_->attach_interrupt(
trigger_pin_->setup();
trigger_pin_->attach_interrupt(
FrontPanelHAL::isr, this, FALLING); FrontPanelHAL::isr, this, FALLING);
} }
void dump_config() { void dump_config() {
ESP_LOGCONFIG(TAG, "I2C"); ESP_LOGCONFIG(TAG, "I2C");
LOG_PIN(" Interrupt pin: ", this->i2c_trigger_pin_);
LOG_PIN(" Interrupt pin: ", trigger_pin_);
} }
void loop() { void loop() {
if (this->queue_length > 0) {
this->queue_length--;
ESP_LOGD(TAG, "EVENT!");
if (queue_length_ > 0) {
queue_length_ = 0;
read_event_();
} }
} }
protected: protected:
// The GPIO pin that is used by the front panel to notify the ESP that // The GPIO pin that is used by the front panel to notify the ESP that
// a touch/release event can be read using I2C. // a touch/release event can be read using I2C.
GPIOPin *i2c_trigger_pin_;
GPIOPin *trigger_pin_;
// The ISR that is used for handling event interrupts. // The ISR that is used for handling event interrupts.
static void isr(FrontPanelHAL *store); static void isr(FrontPanelHAL *store);
// The number of unhandled event interrupts. // The number of unhandled event interrupts.
volatile int queue_length = 0;
volatile int queue_length_ = 0;
uint8_t message[MSG_LEN];
void read_event_() {
if (!write_bytes_raw(READY_FOR_EV, MSG_LEN)) {
return;
}
if (!read_bytes_raw(message, MSG_LEN)) {
return;
}
ev.parse(message);
ev.log();
}
}; };
/** /**
@ -54,7 +194,7 @@ protected:
* on this counter. * on this counter.
*/ */
void ICACHE_RAM_ATTR HOT FrontPanelHAL::isr(FrontPanelHAL *store) { void ICACHE_RAM_ATTR HOT FrontPanelHAL::isr(FrontPanelHAL *store) {
store->queue_length++;
store->queue_length_++;
} }
} // namespace bs2 } // namespace bs2


+ 5
- 2
light/automation.h View File

@ -13,10 +13,13 @@ class BrightnessTrigger : public Trigger<float> {
public: public:
explicit BrightnessTrigger(YeelightBS2LightOutput *parent) { explicit BrightnessTrigger(YeelightBS2LightOutput *parent) {
parent->add_on_state_callback([this](light::LightColorValues values) { parent->add_on_state_callback([this](light::LightColorValues values) {
auto new_brightness = values.get_state() == 0 ? 0.0f : values.get_brightness();
auto new_brightness = values.get_brightness();
if (values.get_state() == 0) {
new_brightness = 0.0f;
}
new_brightness = roundf(new_brightness * 100.0f) / 100.0f; new_brightness = roundf(new_brightness * 100.0f) / 100.0f;
if (last_brightness_ != new_brightness) { if (last_brightness_ != new_brightness) {
this->trigger(new_brightness);
trigger(new_brightness);
last_brightness_ = new_brightness; last_brightness_ = new_brightness;
} }
}); });


+ 6
- 6
light/light_output.h View File

@ -40,7 +40,7 @@ public:
} }
void add_on_state_callback(std::function<void(light::LightColorValues)> &&callback) { void add_on_state_callback(std::function<void(light::LightColorValues)> &&callback) {
this->state_callback_.add(std::move(callback));
state_callback_.add(std::move(callback));
} }
/** /**
@ -81,7 +81,7 @@ public:
if (values.get_state() == 0) if (values.get_state() == 0)
light_->turn_off(); light_->turn_off();
this->state_callback_.call(values);
state_callback_.call(values);
} }
protected: protected:
@ -116,10 +116,10 @@ public:
output->set_transformer_inspector(this); output->set_transformer_inspector(this);
} }
bool is_active() { return this->transformer_ != nullptr; }
bool is_transition() { return this->transformer_->is_transition(); }
light::LightColorValues get_end_values() { return this->transformer_->get_end_values(); }
float get_progress() { return this->transformer_->get_progress(); }
bool is_active() { return transformer_ != nullptr; }
bool is_transition() { return transformer_->is_transition(); }
light::LightColorValues get_end_values() { return transformer_->get_end_values(); }
float get_progress() { return transformer_->get_progress(); }
}; };
} // namespace bs2 } // namespace bs2


Loading…
Cancel
Save