binary sensors

This commit is contained in:
Anton Viktorov 2024-05-24 13:43:04 +02:00
parent 193a98bfb6
commit 71b0cfbd1f
4 changed files with 117 additions and 39 deletions

View file

@ -1,18 +1,65 @@
# import esphome.codegen as cg
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import binary_sensor
from esphome.const import (
CONF_ACTIVE,
CONF_NAME,
DEVICE_CLASS_VIBRATION,
ICON_VIBRATE,
)
from . import MSA3xxComponent, CONF_MSA3XX_ID
CODEOWNERS = ["@latonita"]
DEPENDENCIES = ["msa3xx"]
CONF_TAP = "tap"
CONF_DOUBLE_TAP = "double_tap"
ICON_TAP = "mdi:gesture-tap"
ICON_DOUBLE_TAP = "mdi:gesture-double-tap"
CONFIG_SCHEMA = cv.All(
cv.Schema(
{
cv.GenerateID(CONF_MSA3XX_ID): cv.use_id(MSA3xxComponent),
cv.Optional(CONF_TAP): cv.maybe_simple_value(
binary_sensor.binary_sensor_schema(
device_class=DEVICE_CLASS_VIBRATION,
icon=ICON_TAP,
),
key=CONF_NAME,
),
cv.Optional(CONF_DOUBLE_TAP): cv.maybe_simple_value(
binary_sensor.binary_sensor_schema(
device_class=DEVICE_CLASS_VIBRATION,
icon=ICON_DOUBLE_TAP,
),
key=CONF_NAME,
),
cv.Optional(CONF_ACTIVE): cv.maybe_simple_value(
binary_sensor.binary_sensor_schema(
device_class=DEVICE_CLASS_VIBRATION,
icon=ICON_VIBRATE,
),
key=CONF_NAME,
),
}
).extend(cv.COMPONENT_SCHEMA)
)
# async def to_code(config):
async def to_code(config):
hub = await cg.get_variable(config[CONF_MSA3XX_ID])
if CONF_TAP in config:
sens = await binary_sensor.new_binary_sensor(config[CONF_TAP])
cg.add(hub.set_tap_binary_sensor(sens))
if CONF_DOUBLE_TAP in config:
sens = await binary_sensor.new_binary_sensor(config[CONF_DOUBLE_TAP])
cg.add(hub.set_double_tap_binary_sensor(sens))
if CONF_ACTIVE in config:
sens = await binary_sensor.new_binary_sensor(config[CONF_ACTIVE])
cg.add(hub.set_active_binary_sensor(sens))

View file

@ -1,5 +1,6 @@
#include "msa3xx.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
namespace esphome {
@ -16,6 +17,10 @@ const float G_OFFSET_MAX = 4.5f; // -127...127 LSB = +- 0.4953g = +- 4.857 m/s
const uint8_t RESOLUTION[] = {14, 12, 10, 8};
const uint32_t TAP_COOLDOWN_MS = 500;
const uint32_t DOUBLE_TAP_COOLDOWN_MS = 500;
const uint32_t ACTIVITY_COOLDOWN_MS = 500;
const char *model_to_string(Model model) {
switch (model) {
case Model::MSA301:
@ -166,6 +171,12 @@ void MSA3xxComponent::dump_config() {
YESNO(this->swap_.y_polarity), YESNO(this->swap_.z_polarity), YESNO(this->swap_.x_y_swap));
LOG_UPDATE_INTERVAL(this);
#ifdef USE_BINARY_SENSOR
LOG_BINARY_SENSOR(" ", "Tap", this->tap_binary_sensor_);
LOG_BINARY_SENSOR(" ", "Double Tap", this->double_tap_binary_sensor_);
LOG_BINARY_SENSOR(" ", "Active", this->active_binary_sensor_);
#endif
#ifdef USE_SENSOR
LOG_SENSOR(" ", "Acceleration X", this->acceleration_x_sensor_);
LOG_SENSOR(" ", "Acceleration Y", this->acceleration_y_sensor_);
@ -226,17 +237,14 @@ void MSA3xxComponent::loop() {
return;
}
RegMotionInterrupt old_motion_int = this->status_.motion_int;
if (!this->read_data_() || !this->read_motion_status_()) {
this->status_set_warning();
return;
}
// ESP_LOGVV(TAG, "Got raw data {x=%5d, y=%5d, z=%5d}, accel={x=%+1.3f m/s², y=%+1.3f m/s², z=%+1.3f m/s²}",
// this->data_.lsb_x, this->data_.lsb_y, this->data_.lsb_z, this->data_.x, this->data_.y, this->data_.z);
// ESP_LOGV(TAG, "Orientation XY(%s), Z(%s)", orientation_xy_to_string(this->status_.orientation.orient_xy),
// orientation_z_to_string(this->status_.orientation.orient_z));
this->process_interrupts_();
this->process_motions_(old_motion_int);
}
void MSA3xxComponent::update() {
@ -263,17 +271,18 @@ void MSA3xxComponent::update() {
#ifdef USE_TEXT_SENSOR
if (this->orientation_xy_text_sensor_ != nullptr &&
this->status_.orientation.orient_xy != this->status_.orientation_old.orient_xy) {
(this->status_.orientation.orient_xy != this->status_.orientation_old.orient_xy ||
this->status_.never_published)) {
this->orientation_xy_text_sensor_->publish_state(orientation_xy_to_string(this->status_.orientation.orient_xy));
}
if (this->orientation_z_text_sensor_ != nullptr &&
this->status_.orientation.orient_z != this->status_.orientation_old.orient_z) {
(this->status_.orientation.orient_z != this->status_.orientation_old.orient_z || this->status_.never_published)) {
this->orientation_z_text_sensor_->publish_state(orientation_z_to_string(this->status_.orientation.orient_z));
}
this->status_.orientation_old = this->status_.orientation;
#endif
this->status_.never_published = false;
this->status_clear_warning();
}
float MSA3xxComponent::get_setup_priority() const { return setup_priority::DATA; }
@ -359,30 +368,37 @@ int64_t MSA3xxComponent::twos_complement_(uint64_t value, uint8_t bits) {
}
}
void MSA3xxComponent::process_interrupts_() {
if (this->status_.motion_int.raw == 0) {
return;
void binary_event_debounce(bool state, bool old_state, uint32_t now, uint32_t &last_ms, Trigger<> &trigger,
uint32_t cooldown_ms, binary_sensor::BinarySensor *bs, const char *desc) {
if (state && now - last_ms > cooldown_ms) {
ESP_LOGV(TAG, "%s detected", desc);
trigger.trigger();
last_ms = now;
if (bs != nullptr) {
bs->publish_state(true);
}
} else if (!state && now - last_ms > cooldown_ms && bs != nullptr) {
bs->publish_state(false);
}
}
if (this->status_.motion_int.single_tap_interrupt) {
ESP_LOGW(TAG, "Single Tap detected");
this->tap_trigger_.trigger();
}
void MSA3xxComponent::process_motions_(RegMotionInterrupt old) {
uint32_t now = millis();
if (this->status_.motion_int.double_tap_interrupt) {
ESP_LOGW(TAG, "Double Tap detected");
this->double_tap_trigger_.trigger();
}
binary_event_debounce(this->status_.motion_int.single_tap_interrupt, old.single_tap_interrupt, now,
this->status_.last_tap_ms, this->tap_trigger_, TAP_COOLDOWN_MS, this->tap_binary_sensor_,
"Tap");
binary_event_debounce(this->status_.motion_int.double_tap_interrupt, old.double_tap_interrupt, now,
this->status_.last_double_tap_ms, this->double_tap_trigger_, DOUBLE_TAP_COOLDOWN_MS,
this->double_tap_binary_sensor_, "Double Tap");
binary_event_debounce(this->status_.motion_int.active_interrupt, old.active_interrupt, now,
this->status_.last_action_ms, this->active_trigger_, ACTIVITY_COOLDOWN_MS,
this->active_binary_sensor_, "Activity");
if (this->status_.motion_int.orientation_interrupt) {
ESP_LOGW(TAG, "Orientation changed");
ESP_LOGVV(TAG, "Orientation changed");
this->orientation_trigger_.trigger();
}
if (this->status_.motion_int.active_interrupt) {
ESP_LOGW(TAG, "Activity detected");
this->active_trigger_.trigger();
}
}
} // namespace msa3xx

View file

@ -1,6 +1,7 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/binary_sensor/binary_sensor.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/text_sensor/text_sensor.h"
#include "esphome/components/i2c/i2c.h"
@ -118,7 +119,7 @@ union Orientation {
OrientationXY xy : 2;
bool z : 1;
uint8_t reserved : 5;
};
} __attribute__((packed));
uint8_t raw;
};
@ -133,7 +134,7 @@ union RegMotionInterrupt {
bool single_tap_interrupt : 1;
bool orientation_interrupt : 1;
bool reserved_7 : 1;
};
} __attribute__((packed));
uint8_t raw;
};
@ -144,7 +145,7 @@ union RegOrientationStatus {
OrientationXY orient_xy : 2;
bool orient_z : 1;
uint8_t reserved_7 : 1;
};
} __attribute__((packed));
uint8_t raw{0x00};
};
@ -154,7 +155,7 @@ union RegRangeResolution {
Range range : 2;
Resolution resolution : 2;
uint8_t reserved_2 : 4;
};
} __attribute__((packed));
uint8_t raw{0x00};
};
@ -166,7 +167,7 @@ union RegOutputDataRate {
bool z_axis_disable : 1;
bool y_axis_disable : 1;
bool x_axis_disable : 1;
};
} __attribute__((packed));
uint8_t raw{0xde};
};
@ -177,7 +178,7 @@ union RegPowerModeBandwidth {
Bandwidth low_power_bandwidth : 4;
uint8_t reserved_5 : 1;
PowerMode power_mode : 2;
};
} __attribute__((packed));
uint8_t raw{0xde};
};
@ -189,7 +190,7 @@ union RegSwapPolarity {
bool y_polarity : 1;
bool x_polarity : 1;
uint8_t reserved : 4;
};
} __attribute__((packed));
uint8_t raw{0};
};
@ -200,7 +201,7 @@ union RegTapDuration {
uint8_t reserved : 3;
bool tap_shock : 1;
bool tap_quiet : 1;
};
} __attribute__((packed));
uint8_t raw{0x04};
};
@ -221,6 +222,12 @@ class MSA3xxComponent : public PollingComponent, public i2c::I2CDevice {
void set_resolution(Resolution resolution) { this->resolution_ = resolution; }
void set_transform(bool mirror_x, bool mirror_y, bool mirror_z, bool swap_xy);
#ifdef USE_BINARY_SENSOR
SUB_BINARY_SENSOR(tap)
SUB_BINARY_SENSOR(double_tap)
SUB_BINARY_SENSOR(active)
#endif
#ifdef USE_SENSOR
SUB_SENSOR(acceleration_x)
SUB_SENSOR(acceleration_y)
@ -263,6 +270,12 @@ class MSA3xxComponent : public PollingComponent, public i2c::I2CDevice {
RegMotionInterrupt motion_int;
RegOrientationStatus orientation;
RegOrientationStatus orientation_old;
uint32_t last_tap_ms{0};
uint32_t last_double_tap_ms{0};
uint32_t last_action_ms{0};
bool never_published{true};
} status_{};
void setup_odr_(DataRate rate);
@ -284,7 +297,7 @@ class MSA3xxComponent : public PollingComponent, public i2c::I2CDevice {
Trigger<> freefall_trigger_;
Trigger<> active_trigger_;
void process_interrupts_();
void process_motions_(RegMotionInterrupt old);
};
} // namespace msa3xx

View file

@ -12,16 +12,18 @@ DEPENDENCIES = ["msa3xx"]
CONF_ORIENTATION_XY = "orientation_xy"
CONF_ORIENTATION_Z = "orientation_z"
ICON_SCREEN_ROTATION = "mdi:screen-rotation"
CONFIG_SCHEMA = cv.All(
cv.Schema(
{
cv.GenerateID(CONF_MSA3XX_ID): cv.use_id(MSA3xxComponent),
cv.Optional(CONF_ORIENTATION_XY): cv.maybe_simple_value(
text_sensor.text_sensor_schema(),
text_sensor.text_sensor_schema(icon=ICON_SCREEN_ROTATION),
key=CONF_NAME,
),
cv.Optional(CONF_ORIENTATION_Z): cv.maybe_simple_value(
text_sensor.text_sensor_schema(),
text_sensor.text_sensor_schema(icon=ICON_SCREEN_ROTATION),
key=CONF_NAME,
),
}