mirror of
https://github.com/esphome/esphome.git
synced 2024-11-27 17:27:59 +01:00
Merge branch 'dev' into pr/tomaszduda23/7049
This commit is contained in:
commit
2c48466243
63 changed files with 1144 additions and 199 deletions
|
@ -37,6 +37,7 @@ esphome/components/am43/sensor/* @buxtronix
|
|||
esphome/components/analog_threshold/* @ianchi
|
||||
esphome/components/animation/* @syndlex
|
||||
esphome/components/anova/* @buxtronix
|
||||
esphome/components/apds9306/* @aodrenah
|
||||
esphome/components/api/* @OttoWinter
|
||||
esphome/components/as5600/* @ammmze
|
||||
esphome/components/as5600/sensor/* @ammmze
|
||||
|
@ -216,6 +217,7 @@ esphome/components/lock/* @esphome/core
|
|||
esphome/components/logger/* @esphome/core
|
||||
esphome/components/ltr390/* @latonita @sjtrny
|
||||
esphome/components/ltr_als_ps/* @latonita
|
||||
esphome/components/m5stack_8angle/* @rnauber
|
||||
esphome/components/matrix_keypad/* @ssieb
|
||||
esphome/components/max31865/* @DAVe3283
|
||||
esphome/components/max44009/* @berfenger
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import esphome.config_validation as cv
|
||||
|
||||
CONFIG_SCHEMA = CONFIG_SCHEMA = cv.invalid(
|
||||
CONFIG_SCHEMA = cv.invalid(
|
||||
"The ade7953 sensor component has been renamed to ade7953_i2c."
|
||||
)
|
||||
|
|
4
esphome/components/apds9306/__init__.py
Normal file
4
esphome/components/apds9306/__init__.py
Normal file
|
@ -0,0 +1,4 @@
|
|||
# Based on this datasheet:
|
||||
# https://www.mouser.ca/datasheet/2/678/AVGO_S_A0002854364_1-2574547.pdf
|
||||
|
||||
CODEOWNERS = ["@aodrenah"]
|
151
esphome/components/apds9306/apds9306.cpp
Normal file
151
esphome/components/apds9306/apds9306.cpp
Normal file
|
@ -0,0 +1,151 @@
|
|||
// Based on this datasheet:
|
||||
// https://www.mouser.ca/datasheet/2/678/AVGO_S_A0002854364_1-2574547.pdf
|
||||
|
||||
#include "apds9306.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace apds9306 {
|
||||
|
||||
static const char *const TAG = "apds9306";
|
||||
|
||||
enum { // APDS9306 registers
|
||||
APDS9306_MAIN_CTRL = 0x00,
|
||||
APDS9306_ALS_MEAS_RATE = 0x04,
|
||||
APDS9306_ALS_GAIN = 0x05,
|
||||
APDS9306_PART_ID = 0x06,
|
||||
APDS9306_MAIN_STATUS = 0x07,
|
||||
APDS9306_CLEAR_DATA_0 = 0x0A, // LSB
|
||||
APDS9306_CLEAR_DATA_1 = 0x0B,
|
||||
APDS9306_CLEAR_DATA_2 = 0x0C, // MSB
|
||||
APDS9306_ALS_DATA_0 = 0x0D, // LSB
|
||||
APDS9306_ALS_DATA_1 = 0x0E,
|
||||
APDS9306_ALS_DATA_2 = 0x0F, // MSB
|
||||
APDS9306_INT_CFG = 0x19,
|
||||
APDS9306_INT_PERSISTENCE = 0x1A,
|
||||
APDS9306_ALS_THRES_UP_0 = 0x21, // LSB
|
||||
APDS9306_ALS_THRES_UP_1 = 0x22,
|
||||
APDS9306_ALS_THRES_UP_2 = 0x23, // MSB
|
||||
APDS9306_ALS_THRES_LOW_0 = 0x24, // LSB
|
||||
APDS9306_ALS_THRES_LOW_1 = 0x25,
|
||||
APDS9306_ALS_THRES_LOW_2 = 0x26, // MSB
|
||||
APDS9306_ALS_THRES_VAR = 0x27
|
||||
};
|
||||
|
||||
#define APDS9306_ERROR_CHECK(func, error) \
|
||||
if (!(func)) { \
|
||||
ESP_LOGE(TAG, error); \
|
||||
this->mark_failed(); \
|
||||
return; \
|
||||
}
|
||||
#define APDS9306_WARNING_CHECK(func, warning) \
|
||||
if (!(func)) { \
|
||||
ESP_LOGW(TAG, warning); \
|
||||
this->status_set_warning(); \
|
||||
return; \
|
||||
}
|
||||
#define APDS9306_WRITE_BYTE(reg, value) \
|
||||
ESP_LOGV(TAG, "Writing 0x%02x to 0x%02x", value, reg); \
|
||||
if (!this->write_byte(reg, value)) { \
|
||||
ESP_LOGE(TAG, "Failed writing 0x%02x to 0x%02x", value, reg); \
|
||||
this->mark_failed(); \
|
||||
return; \
|
||||
}
|
||||
|
||||
void APDS9306::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up APDS9306...");
|
||||
|
||||
uint8_t id;
|
||||
if (!this->read_byte(APDS9306_PART_ID, &id)) { // Part ID register
|
||||
this->error_code_ = COMMUNICATION_FAILED;
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
if (id != 0xB1 && id != 0xB3) { // 0xB1 for APDS9306 0xB3 for APDS9306-065
|
||||
this->error_code_ = WRONG_ID;
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
// ALS resolution and measurement, see datasheet or init.py for options
|
||||
uint8_t als_meas_rate = ((this->bit_width_ & 0x07) << 4) | (this->measurement_rate_ & 0x07);
|
||||
APDS9306_WRITE_BYTE(APDS9306_ALS_MEAS_RATE, als_meas_rate);
|
||||
|
||||
// ALS gain, see datasheet or init.py for options
|
||||
uint8_t als_gain = (this->gain_ & 0x07);
|
||||
APDS9306_WRITE_BYTE(APDS9306_ALS_GAIN, als_gain);
|
||||
|
||||
// Set to standby mode
|
||||
APDS9306_WRITE_BYTE(APDS9306_MAIN_CTRL, 0x00);
|
||||
|
||||
// Check for data, clear main status
|
||||
uint8_t status;
|
||||
APDS9306_WARNING_CHECK(this->read_byte(APDS9306_MAIN_STATUS, &status), "Reading MAIN STATUS failed.");
|
||||
|
||||
// Set to active mode
|
||||
APDS9306_WRITE_BYTE(APDS9306_MAIN_CTRL, 0x02);
|
||||
|
||||
ESP_LOGCONFIG(TAG, "APDS9306 setup complete");
|
||||
}
|
||||
|
||||
void APDS9306::dump_config() {
|
||||
LOG_SENSOR("", "APDS9306", this);
|
||||
LOG_I2C_DEVICE(this);
|
||||
|
||||
if (this->is_failed()) {
|
||||
switch (this->error_code_) {
|
||||
case COMMUNICATION_FAILED:
|
||||
ESP_LOGE(TAG, "Communication with APDS9306 failed!");
|
||||
break;
|
||||
case WRONG_ID:
|
||||
ESP_LOGE(TAG, "APDS9306 has invalid id!");
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(TAG, "Setting up APDS9306 registers failed!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ESP_LOGCONFIG(TAG, " Gain: %u", AMBIENT_LIGHT_GAIN_VALUES[this->gain_]);
|
||||
ESP_LOGCONFIG(TAG, " Measurement rate: %u", MEASUREMENT_RATE_VALUES[this->measurement_rate_]);
|
||||
ESP_LOGCONFIG(TAG, " Measurement Resolution/Bit width: %d", MEASUREMENT_BIT_WIDTH_VALUES[this->bit_width_]);
|
||||
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
}
|
||||
|
||||
void APDS9306::update() {
|
||||
// Check for new data
|
||||
uint8_t status;
|
||||
APDS9306_WARNING_CHECK(this->read_byte(APDS9306_MAIN_STATUS, &status), "Reading MAIN STATUS failed.");
|
||||
|
||||
this->status_clear_warning();
|
||||
|
||||
if (!(status &= 0b00001000)) { // No new data
|
||||
return;
|
||||
}
|
||||
|
||||
// Set to standby mode
|
||||
APDS9306_WRITE_BYTE(APDS9306_MAIN_CTRL, 0x00);
|
||||
|
||||
// Clear MAIN STATUS
|
||||
APDS9306_WARNING_CHECK(this->read_byte(APDS9306_MAIN_STATUS, &status), "Reading MAIN STATUS failed.");
|
||||
|
||||
uint8_t als_data[3];
|
||||
APDS9306_WARNING_CHECK(this->read_bytes(APDS9306_ALS_DATA_0, als_data, 3), "Reading ALS data has failed.");
|
||||
|
||||
// Set to active mode
|
||||
APDS9306_WRITE_BYTE(APDS9306_MAIN_CTRL, 0x02);
|
||||
|
||||
uint32_t light_level = 0x00 | encode_uint24(als_data[2], als_data[1], als_data[0]);
|
||||
|
||||
float lux = ((float) light_level / AMBIENT_LIGHT_GAIN_VALUES[this->gain_]) *
|
||||
(100.0f / MEASUREMENT_RATE_VALUES[this->measurement_rate_]);
|
||||
|
||||
ESP_LOGD(TAG, "Got illuminance=%.1flx from", lux);
|
||||
this->publish_state(lux);
|
||||
}
|
||||
|
||||
} // namespace apds9306
|
||||
} // namespace esphome
|
66
esphome/components/apds9306/apds9306.h
Normal file
66
esphome/components/apds9306/apds9306.h
Normal file
|
@ -0,0 +1,66 @@
|
|||
// Based on this datasheet:
|
||||
// https://www.mouser.ca/datasheet/2/678/AVGO_S_A0002854364_1-2574547.pdf
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace apds9306 {
|
||||
|
||||
enum MeasurementBitWidth : uint8_t {
|
||||
MEASUREMENT_BIT_WIDTH_20 = 0,
|
||||
MEASUREMENT_BIT_WIDTH_19 = 1,
|
||||
MEASUREMENT_BIT_WIDTH_18 = 2,
|
||||
MEASUREMENT_BIT_WIDTH_17 = 3,
|
||||
MEASUREMENT_BIT_WIDTH_16 = 4,
|
||||
MEASUREMENT_BIT_WIDTH_13 = 5,
|
||||
};
|
||||
static const uint8_t MEASUREMENT_BIT_WIDTH_VALUES[] = {20, 19, 18, 17, 16, 13};
|
||||
|
||||
enum MeasurementRate : uint8_t {
|
||||
MEASUREMENT_RATE_25 = 0,
|
||||
MEASUREMENT_RATE_50 = 1,
|
||||
MEASUREMENT_RATE_100 = 2,
|
||||
MEASUREMENT_RATE_200 = 3,
|
||||
MEASUREMENT_RATE_500 = 4,
|
||||
MEASUREMENT_RATE_1000 = 5,
|
||||
MEASUREMENT_RATE_2000 = 6,
|
||||
};
|
||||
static const uint16_t MEASUREMENT_RATE_VALUES[] = {25, 50, 100, 200, 500, 1000, 2000};
|
||||
|
||||
enum AmbientLightGain : uint8_t {
|
||||
AMBIENT_LIGHT_GAIN_1 = 0,
|
||||
AMBIENT_LIGHT_GAIN_3 = 1,
|
||||
AMBIENT_LIGHT_GAIN_6 = 2,
|
||||
AMBIENT_LIGHT_GAIN_9 = 3,
|
||||
AMBIENT_LIGHT_GAIN_18 = 4,
|
||||
};
|
||||
static const uint8_t AMBIENT_LIGHT_GAIN_VALUES[] = {1, 3, 6, 9, 18};
|
||||
|
||||
class APDS9306 : public sensor::Sensor, public PollingComponent, public i2c::I2CDevice {
|
||||
public:
|
||||
void setup() override;
|
||||
float get_setup_priority() const override { return setup_priority::BUS; }
|
||||
void dump_config() override;
|
||||
void update() override;
|
||||
void set_bit_width(MeasurementBitWidth bit_width) { this->bit_width_ = bit_width; }
|
||||
void set_measurement_rate(MeasurementRate measurement_rate) { this->measurement_rate_ = measurement_rate; }
|
||||
void set_ambient_light_gain(AmbientLightGain gain) { this->gain_ = gain; }
|
||||
|
||||
protected:
|
||||
enum ErrorCode {
|
||||
NONE = 0,
|
||||
COMMUNICATION_FAILED,
|
||||
WRONG_ID,
|
||||
} error_code_{NONE};
|
||||
|
||||
MeasurementBitWidth bit_width_;
|
||||
MeasurementRate measurement_rate_;
|
||||
AmbientLightGain gain_;
|
||||
};
|
||||
|
||||
} // namespace apds9306
|
||||
} // namespace esphome
|
95
esphome/components/apds9306/sensor.py
Normal file
95
esphome/components/apds9306/sensor.py
Normal file
|
@ -0,0 +1,95 @@
|
|||
# Based on this datasheet:
|
||||
# https://www.mouser.ca/datasheet/2/678/AVGO_S_A0002854364_1-2574547.pdf
|
||||
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import i2c, sensor
|
||||
from esphome.const import (
|
||||
CONF_GAIN,
|
||||
DEVICE_CLASS_ILLUMINANCE,
|
||||
ICON_LIGHTBULB,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_LUX,
|
||||
)
|
||||
|
||||
DEPENDENCIES = ["i2c"]
|
||||
|
||||
CONF_APDS9306_ID = "apds9306_id"
|
||||
CONF_BIT_WIDTH = "bit_width"
|
||||
CONF_MEASUREMENT_RATE = "measurement_rate"
|
||||
|
||||
apds9306_ns = cg.esphome_ns.namespace("apds9306")
|
||||
APDS9306 = apds9306_ns.class_(
|
||||
"APDS9306", sensor.Sensor, cg.PollingComponent, i2c.I2CDevice
|
||||
)
|
||||
|
||||
MeasurementBitWidth = apds9306_ns.enum("MeasurementBitWidth")
|
||||
MeasurementRate = apds9306_ns.enum("MeasurementRate")
|
||||
AmbientLightGain = apds9306_ns.enum("AmbientLightGain")
|
||||
|
||||
MEASUREMENT_BIT_WIDTHS = {
|
||||
20: MeasurementBitWidth.MEASUREMENT_BIT_WIDTH_20,
|
||||
19: MeasurementBitWidth.MEASUREMENT_BIT_WIDTH_19,
|
||||
18: MeasurementBitWidth.MEASUREMENT_BIT_WIDTH_18,
|
||||
17: MeasurementBitWidth.MEASUREMENT_BIT_WIDTH_17,
|
||||
16: MeasurementBitWidth.MEASUREMENT_BIT_WIDTH_16,
|
||||
13: MeasurementBitWidth.MEASUREMENT_BIT_WIDTH_13,
|
||||
}
|
||||
|
||||
MEASUREMENT_RATES = {
|
||||
25: MeasurementRate.MEASUREMENT_RATE_25,
|
||||
50: MeasurementRate.MEASUREMENT_RATE_50,
|
||||
100: MeasurementRate.MEASUREMENT_RATE_100,
|
||||
200: MeasurementRate.MEASUREMENT_RATE_200,
|
||||
500: MeasurementRate.MEASUREMENT_RATE_500,
|
||||
1000: MeasurementRate.MEASUREMENT_RATE_1000,
|
||||
2000: MeasurementRate.MEASUREMENT_RATE_2000,
|
||||
}
|
||||
|
||||
AMBIENT_LIGHT_GAINS = {
|
||||
1: AmbientLightGain.AMBIENT_LIGHT_GAIN_1,
|
||||
3: AmbientLightGain.AMBIENT_LIGHT_GAIN_3,
|
||||
6: AmbientLightGain.AMBIENT_LIGHT_GAIN_6,
|
||||
9: AmbientLightGain.AMBIENT_LIGHT_GAIN_9,
|
||||
18: AmbientLightGain.AMBIENT_LIGHT_GAIN_18,
|
||||
}
|
||||
|
||||
|
||||
def _validate_measurement_rate(value):
|
||||
value = cv.positive_time_period_milliseconds(value)
|
||||
return cv.enum(MEASUREMENT_RATES, int=True)(value.total_milliseconds)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
sensor.sensor_schema(
|
||||
APDS9306,
|
||||
unit_of_measurement=UNIT_LUX,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_ILLUMINANCE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
icon=ICON_LIGHTBULB,
|
||||
)
|
||||
.extend(
|
||||
{
|
||||
cv.Optional(CONF_GAIN, default="1"): cv.enum(AMBIENT_LIGHT_GAINS, int=True),
|
||||
cv.Optional(CONF_BIT_WIDTH, default="18"): cv.enum(
|
||||
MEASUREMENT_BIT_WIDTHS, int=True
|
||||
),
|
||||
cv.Optional(
|
||||
CONF_MEASUREMENT_RATE, default="100ms"
|
||||
): _validate_measurement_rate,
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("60s"))
|
||||
.extend(i2c.i2c_device_schema(0x52))
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = await sensor.new_sensor(config)
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
cg.add(var.set_bit_width(config[CONF_BIT_WIDTH]))
|
||||
cg.add(var.set_measurement_rate(config[CONF_MEASUREMENT_RATE]))
|
||||
cg.add(var.set_ambient_light_gain(config[CONF_GAIN]))
|
|
@ -2,6 +2,6 @@ import esphome.config_validation as cv
|
|||
|
||||
CODEOWNERS = ["@latonita"]
|
||||
|
||||
CONFIG_SCHEMA = CONFIG_SCHEMA = cv.invalid(
|
||||
CONFIG_SCHEMA = cv.invalid(
|
||||
"The bmp3xx sensor component has been renamed to bmp3xx_i2c."
|
||||
)
|
||||
|
|
|
@ -2,6 +2,6 @@ import esphome.config_validation as cv
|
|||
|
||||
CODEOWNERS = ["@latonita"]
|
||||
|
||||
CONFIG_SCHEMA = CONFIG_SCHEMA = cv.invalid(
|
||||
CONFIG_SCHEMA = cv.invalid(
|
||||
"The ens160 sensor component has been renamed to ens160_i2c."
|
||||
)
|
||||
|
|
|
@ -7,10 +7,10 @@ from esphome.components.esp32 import add_idf_sdkconfig_option, get_esp32_variant
|
|||
|
||||
DEPENDENCIES = ["esp32"]
|
||||
CODEOWNERS = ["@jesserockz", "@Rapsssito"]
|
||||
CONFLICTS_WITH = ["esp32_ble_beacon"]
|
||||
|
||||
CONF_BLE_ID = "ble_id"
|
||||
CONF_IO_CAPABILITY = "io_capability"
|
||||
CONF_ADVERTISING_CYCLE_TIME = "advertising_cycle_time"
|
||||
|
||||
NO_BLUETOOTH_VARIANTS = [const.VARIANT_ESP32S2]
|
||||
|
||||
|
@ -34,6 +34,19 @@ IO_CAPABILITY = {
|
|||
"display_yes_no": IoCapability.IO_CAP_IO,
|
||||
}
|
||||
|
||||
esp_power_level_t = cg.global_ns.enum("esp_power_level_t")
|
||||
|
||||
TX_POWER_LEVELS = {
|
||||
-12: esp_power_level_t.ESP_PWR_LVL_N12,
|
||||
-9: esp_power_level_t.ESP_PWR_LVL_N9,
|
||||
-6: esp_power_level_t.ESP_PWR_LVL_N6,
|
||||
-3: esp_power_level_t.ESP_PWR_LVL_N3,
|
||||
0: esp_power_level_t.ESP_PWR_LVL_N0,
|
||||
3: esp_power_level_t.ESP_PWR_LVL_P3,
|
||||
6: esp_power_level_t.ESP_PWR_LVL_P6,
|
||||
9: esp_power_level_t.ESP_PWR_LVL_P9,
|
||||
}
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(ESP32BLE),
|
||||
|
@ -41,6 +54,9 @@ CONFIG_SCHEMA = cv.Schema(
|
|||
IO_CAPABILITY, lower=True
|
||||
),
|
||||
cv.Optional(CONF_ENABLE_ON_BOOT, default=True): cv.boolean,
|
||||
cv.Optional(
|
||||
CONF_ADVERTISING_CYCLE_TIME, default="10s"
|
||||
): cv.positive_time_period_milliseconds,
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
|
@ -58,6 +74,7 @@ async def to_code(config):
|
|||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
cg.add(var.set_enable_on_boot(config[CONF_ENABLE_ON_BOOT]))
|
||||
cg.add(var.set_io_capability(config[CONF_IO_CAPABILITY]))
|
||||
cg.add(var.set_advertising_cycle_time(config[CONF_ADVERTISING_CYCLE_TIME]))
|
||||
await cg.register_component(var, config)
|
||||
|
||||
if CORE.using_esp_idf:
|
||||
|
|
|
@ -78,6 +78,11 @@ void ESP32BLE::advertising_set_manufacturer_data(const std::vector<uint8_t> &dat
|
|||
this->advertising_start();
|
||||
}
|
||||
|
||||
void ESP32BLE::advertising_register_raw_advertisement_callback(std::function<void(bool)> &&callback) {
|
||||
this->advertising_init_();
|
||||
this->advertising_->register_raw_advertisement_callback(std::move(callback));
|
||||
}
|
||||
|
||||
void ESP32BLE::advertising_add_service_uuid(ESPBTUUID uuid) {
|
||||
this->advertising_init_();
|
||||
this->advertising_->add_service_uuid(uuid);
|
||||
|
@ -102,7 +107,7 @@ bool ESP32BLE::ble_pre_setup_() {
|
|||
void ESP32BLE::advertising_init_() {
|
||||
if (this->advertising_ != nullptr)
|
||||
return;
|
||||
this->advertising_ = new BLEAdvertising(); // NOLINT(cppcoreguidelines-owning-memory)
|
||||
this->advertising_ = new BLEAdvertising(this->advertising_cycle_time_); // NOLINT(cppcoreguidelines-owning-memory)
|
||||
|
||||
this->advertising_->set_scan_response(true);
|
||||
this->advertising_->set_min_preferred_interval(0x06);
|
||||
|
@ -312,6 +317,9 @@ void ESP32BLE::loop() {
|
|||
delete ble_event; // NOLINT(cppcoreguidelines-owning-memory)
|
||||
ble_event = this->ble_events_.pop();
|
||||
}
|
||||
if (this->advertising_ != nullptr) {
|
||||
this->advertising_->loop();
|
||||
}
|
||||
}
|
||||
|
||||
void ESP32BLE::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
#include "ble_advertising.h"
|
||||
#include "ble_uuid.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/defines.h"
|
||||
|
@ -76,6 +78,11 @@ class ESP32BLE : public Component {
|
|||
public:
|
||||
void set_io_capability(IoCapability io_capability) { this->io_cap_ = (esp_ble_io_cap_t) io_capability; }
|
||||
|
||||
void set_advertising_cycle_time(uint32_t advertising_cycle_time) {
|
||||
this->advertising_cycle_time_ = advertising_cycle_time;
|
||||
}
|
||||
uint32_t get_advertising_cycle_time() const { return this->advertising_cycle_time_; }
|
||||
|
||||
void enable();
|
||||
void disable();
|
||||
bool is_active();
|
||||
|
@ -89,6 +96,7 @@ class ESP32BLE : public Component {
|
|||
void advertising_set_manufacturer_data(const std::vector<uint8_t> &data);
|
||||
void advertising_add_service_uuid(ESPBTUUID uuid);
|
||||
void advertising_remove_service_uuid(ESPBTUUID uuid);
|
||||
void advertising_register_raw_advertisement_callback(std::function<void(bool)> &&callback);
|
||||
|
||||
void register_gap_event_handler(GAPEventHandler *handler) { this->gap_event_handlers_.push_back(handler); }
|
||||
void register_gattc_event_handler(GATTcEventHandler *handler) { this->gattc_event_handlers_.push_back(handler); }
|
||||
|
@ -121,6 +129,7 @@ class ESP32BLE : public Component {
|
|||
Queue<BLEEvent> ble_events_;
|
||||
BLEAdvertising *advertising_;
|
||||
esp_ble_io_cap_t io_cap_{ESP_IO_CAP_NONE};
|
||||
uint32_t advertising_cycle_time_;
|
||||
bool enable_on_boot_;
|
||||
};
|
||||
|
||||
|
|
|
@ -10,9 +10,9 @@
|
|||
namespace esphome {
|
||||
namespace esp32_ble {
|
||||
|
||||
static const char *const TAG = "esp32_ble";
|
||||
static const char *const TAG = "esp32_ble.advertising";
|
||||
|
||||
BLEAdvertising::BLEAdvertising() {
|
||||
BLEAdvertising::BLEAdvertising(uint32_t advertising_cycle_time) : advertising_cycle_time_(advertising_cycle_time) {
|
||||
this->advertising_data_.set_scan_rsp = false;
|
||||
this->advertising_data_.include_name = true;
|
||||
this->advertising_data_.include_txpower = true;
|
||||
|
@ -64,7 +64,7 @@ void BLEAdvertising::set_manufacturer_data(const std::vector<uint8_t> &data) {
|
|||
}
|
||||
}
|
||||
|
||||
void BLEAdvertising::start() {
|
||||
esp_err_t BLEAdvertising::services_advertisement_() {
|
||||
int num_services = this->advertising_uuids_.size();
|
||||
if (num_services == 0) {
|
||||
this->advertising_data_.service_uuid_len = 0;
|
||||
|
@ -87,8 +87,8 @@ void BLEAdvertising::start() {
|
|||
this->advertising_data_.include_txpower = !this->scan_response_;
|
||||
err = esp_ble_gap_config_adv_data(&this->advertising_data_);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_ble_gap_config_adv_data failed (Advertising): %d", err);
|
||||
return;
|
||||
ESP_LOGE(TAG, "esp_ble_gap_config_adv_data failed (Advertising): %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
if (this->scan_response_) {
|
||||
|
@ -101,8 +101,8 @@ void BLEAdvertising::start() {
|
|||
this->scan_response_data_.flag = 0;
|
||||
err = esp_ble_gap_config_adv_data(&this->scan_response_data_);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_ble_gap_config_adv_data failed (Scan response): %d", err);
|
||||
return;
|
||||
ESP_LOGE(TAG, "esp_ble_gap_config_adv_data failed (Scan response): %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,8 +113,18 @@ void BLEAdvertising::start() {
|
|||
|
||||
err = esp_ble_gap_start_advertising(&this->advertising_params_);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_ble_gap_start_advertising failed: %d", err);
|
||||
return;
|
||||
ESP_LOGE(TAG, "esp_ble_gap_start_advertising failed: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void BLEAdvertising::start() {
|
||||
if (this->current_adv_index_ == -1) {
|
||||
this->services_advertisement_();
|
||||
} else {
|
||||
this->raw_advertisements_callbacks_[this->current_adv_index_](true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -124,6 +134,29 @@ void BLEAdvertising::stop() {
|
|||
ESP_LOGE(TAG, "esp_ble_gap_stop_advertising failed: %d", err);
|
||||
return;
|
||||
}
|
||||
if (this->current_adv_index_ != -1) {
|
||||
this->raw_advertisements_callbacks_[this->current_adv_index_](false);
|
||||
}
|
||||
}
|
||||
|
||||
void BLEAdvertising::loop() {
|
||||
if (this->raw_advertisements_callbacks_.empty()) {
|
||||
return;
|
||||
}
|
||||
const uint32_t now = millis();
|
||||
if (now - this->last_advertisement_time_ > this->advertising_cycle_time_) {
|
||||
this->stop();
|
||||
this->current_adv_index_ += 1;
|
||||
if (this->current_adv_index_ >= this->raw_advertisements_callbacks_.size()) {
|
||||
this->current_adv_index_ = -1;
|
||||
}
|
||||
this->start();
|
||||
this->last_advertisement_time_ = now;
|
||||
}
|
||||
}
|
||||
|
||||
void BLEAdvertising::register_raw_advertisement_callback(std::function<void(bool)> &&callback) {
|
||||
this->raw_advertisements_callbacks_.push_back(std::move(callback));
|
||||
}
|
||||
|
||||
} // namespace esp32_ble
|
||||
|
|
|
@ -1,20 +1,31 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
#include <esp_bt.h>
|
||||
#include <esp_gap_ble_api.h>
|
||||
#include <esp_gatts_api.h>
|
||||
|
||||
namespace esphome {
|
||||
namespace esp32_ble {
|
||||
|
||||
using raw_adv_data_t = struct {
|
||||
uint8_t *data;
|
||||
size_t length;
|
||||
esp_power_level_t power_level;
|
||||
};
|
||||
|
||||
class ESPBTUUID;
|
||||
|
||||
class BLEAdvertising {
|
||||
public:
|
||||
BLEAdvertising();
|
||||
BLEAdvertising(uint32_t advertising_cycle_time);
|
||||
|
||||
void loop();
|
||||
|
||||
void add_service_uuid(ESPBTUUID uuid);
|
||||
void remove_service_uuid(ESPBTUUID uuid);
|
||||
|
@ -22,16 +33,25 @@ class BLEAdvertising {
|
|||
void set_min_preferred_interval(uint16_t interval) { this->advertising_data_.min_interval = interval; }
|
||||
void set_manufacturer_data(const std::vector<uint8_t> &data);
|
||||
void set_service_data(const std::vector<uint8_t> &data);
|
||||
void register_raw_advertisement_callback(std::function<void(bool)> &&callback);
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
protected:
|
||||
esp_err_t services_advertisement_();
|
||||
|
||||
bool scan_response_;
|
||||
esp_ble_adv_data_t advertising_data_;
|
||||
esp_ble_adv_data_t scan_response_data_;
|
||||
esp_ble_adv_params_t advertising_params_;
|
||||
std::vector<ESPBTUUID> advertising_uuids_;
|
||||
|
||||
std::vector<std::function<void(bool)>> raw_advertisements_callbacks_;
|
||||
|
||||
const uint32_t advertising_cycle_time_;
|
||||
uint32_t last_advertisement_time_{0};
|
||||
int8_t current_adv_index_{-1}; // -1 means standard scan response
|
||||
};
|
||||
|
||||
} // namespace esp32_ble
|
||||
|
|
|
@ -1,16 +1,21 @@
|
|||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components.esp32_ble import CONF_BLE_ID
|
||||
from esphome.const import CONF_ID, CONF_TYPE, CONF_UUID, CONF_TX_POWER
|
||||
from esphome.core import CORE, TimePeriod
|
||||
from esphome.components.esp32 import add_idf_sdkconfig_option
|
||||
from esphome.components import esp32_ble
|
||||
|
||||
AUTO_LOAD = ["esp32_ble"]
|
||||
DEPENDENCIES = ["esp32"]
|
||||
CONFLICTS_WITH = ["esp32_ble_tracker"]
|
||||
|
||||
esp32_ble_beacon_ns = cg.esphome_ns.namespace("esp32_ble_beacon")
|
||||
ESP32BLEBeacon = esp32_ble_beacon_ns.class_("ESP32BLEBeacon", cg.Component)
|
||||
|
||||
ESP32BLEBeacon = esp32_ble_beacon_ns.class_(
|
||||
"ESP32BLEBeacon",
|
||||
cg.Component,
|
||||
esp32_ble.GAPEventHandler,
|
||||
cg.Parented.template(esp32_ble.ESP32BLE),
|
||||
)
|
||||
CONF_MAJOR = "major"
|
||||
CONF_MINOR = "minor"
|
||||
CONF_MIN_INTERVAL = "min_interval"
|
||||
|
@ -28,6 +33,7 @@ CONFIG_SCHEMA = cv.All(
|
|||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(ESP32BLEBeacon),
|
||||
cv.GenerateID(CONF_BLE_ID): cv.use_id(esp32_ble.ESP32BLE),
|
||||
cv.Required(CONF_TYPE): cv.one_of("IBEACON", upper=True),
|
||||
cv.Required(CONF_UUID): cv.uuid,
|
||||
cv.Optional(CONF_MAJOR, default=10167): cv.uint16_t,
|
||||
|
@ -48,7 +54,7 @@ CONFIG_SCHEMA = cv.All(
|
|||
min=-128, max=0
|
||||
),
|
||||
cv.Optional(CONF_TX_POWER, default="3dBm"): cv.All(
|
||||
cv.decibel, cv.one_of(-12, -9, -6, -3, 0, 3, 6, 9, int=True)
|
||||
cv.decibel, cv.enum(esp32_ble.TX_POWER_LEVELS, int=True)
|
||||
),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA),
|
||||
|
@ -62,6 +68,10 @@ async def to_code(config):
|
|||
uuid = config[CONF_UUID].hex
|
||||
uuid_arr = [cg.RawExpression(f"0x{uuid[i:i + 2]}") for i in range(0, len(uuid), 2)]
|
||||
var = cg.new_Pvariable(config[CONF_ID], uuid_arr)
|
||||
|
||||
parent = await cg.get_variable(config[esp32_ble.CONF_BLE_ID])
|
||||
cg.add(parent.register_gap_event_handler(var))
|
||||
|
||||
await cg.register_component(var, config)
|
||||
cg.add(var.set_major(config[CONF_MAJOR]))
|
||||
cg.add(var.set_minor(config[CONF_MINOR]))
|
||||
|
|
|
@ -3,14 +3,16 @@
|
|||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
#include <nvs_flash.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <esp_bt_main.h>
|
||||
#include <esp_bt.h>
|
||||
#include <freertos/task.h>
|
||||
#include <esp_bt_main.h>
|
||||
#include <esp_gap_ble_api.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <nvs_flash.h>
|
||||
#include <cstring>
|
||||
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
#include <esp32-hal-bt.h>
|
||||
|
@ -21,20 +23,6 @@ namespace esp32_ble_beacon {
|
|||
|
||||
static const char *const TAG = "esp32_ble_beacon";
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
static esp_ble_adv_params_t ble_adv_params = {
|
||||
.adv_int_min = 0x20,
|
||||
.adv_int_max = 0x40,
|
||||
.adv_type = ADV_TYPE_NONCONN_IND,
|
||||
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
|
||||
.peer_addr = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||
.peer_addr_type = BLE_ADDR_TYPE_PUBLIC,
|
||||
.channel_map = ADV_CHNL_ALL,
|
||||
.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
|
||||
};
|
||||
|
||||
#define ENDIAN_CHANGE_U16(x) ((((x) &0xFF00) >> 8) + (((x) &0xFF) << 8))
|
||||
|
||||
static const esp_ble_ibeacon_head_t IBEACON_COMMON_HEAD = {
|
||||
.flags = {0x02, 0x01, 0x06}, .length = 0x1A, .type = 0xFF, .company_id = {0x4C, 0x00}, .beacon_type = {0x02, 0x15}};
|
||||
|
||||
|
@ -53,117 +41,62 @@ void ESP32BLEBeacon::dump_config() {
|
|||
" UUID: %s, Major: %u, Minor: %u, Min Interval: %ums, Max Interval: %ums, Measured Power: %d"
|
||||
", TX Power: %ddBm",
|
||||
uuid, this->major_, this->minor_, this->min_interval_, this->max_interval_, this->measured_power_,
|
||||
this->tx_power_);
|
||||
(this->tx_power_ * 3) - 12);
|
||||
}
|
||||
|
||||
float ESP32BLEBeacon::get_setup_priority() const { return setup_priority::AFTER_BLUETOOTH; }
|
||||
|
||||
void ESP32BLEBeacon::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up ESP32 BLE beacon...");
|
||||
global_esp32_ble_beacon = this;
|
||||
this->ble_adv_params_ = {
|
||||
.adv_int_min = static_cast<uint16_t>(this->min_interval_ / 0.625f),
|
||||
.adv_int_max = static_cast<uint16_t>(this->max_interval_ / 0.625f),
|
||||
.adv_type = ADV_TYPE_NONCONN_IND,
|
||||
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
|
||||
.peer_addr = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||
.peer_addr_type = BLE_ADDR_TYPE_PUBLIC,
|
||||
.channel_map = ADV_CHNL_ALL,
|
||||
.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
|
||||
};
|
||||
|
||||
xTaskCreatePinnedToCore(ESP32BLEBeacon::ble_core_task,
|
||||
"ble_task", // name
|
||||
10000, // stack size (in words)
|
||||
nullptr, // input params
|
||||
1, // priority
|
||||
nullptr, // Handle, not needed
|
||||
0 // core
|
||||
);
|
||||
global_ble->advertising_register_raw_advertisement_callback([this](bool advertise) {
|
||||
this->advertising_ = advertise;
|
||||
if (advertise) {
|
||||
this->on_advertise_();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
float ESP32BLEBeacon::get_setup_priority() const { return setup_priority::BLUETOOTH; }
|
||||
void ESP32BLEBeacon::ble_core_task(void *params) {
|
||||
ble_setup();
|
||||
|
||||
while (true) {
|
||||
delay(1000); // NOLINT
|
||||
}
|
||||
}
|
||||
|
||||
void ESP32BLEBeacon::ble_setup() {
|
||||
ble_adv_params.adv_int_min = static_cast<uint16_t>(global_esp32_ble_beacon->min_interval_ / 0.625f);
|
||||
ble_adv_params.adv_int_max = static_cast<uint16_t>(global_esp32_ble_beacon->max_interval_ / 0.625f);
|
||||
|
||||
// Initialize non-volatile storage for the bluetooth controller
|
||||
esp_err_t err = nvs_flash_init();
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "nvs_flash_init failed: %d", err);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
if (!btStart()) {
|
||||
ESP_LOGE(TAG, "btStart failed: %d", esp_bt_controller_get_status());
|
||||
return;
|
||||
}
|
||||
#else
|
||||
if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_ENABLED) {
|
||||
// start bt controller
|
||||
if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) {
|
||||
esp_bt_controller_config_t cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
|
||||
err = esp_bt_controller_init(&cfg);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_bt_controller_init failed: %s", esp_err_to_name(err));
|
||||
return;
|
||||
}
|
||||
while (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE)
|
||||
;
|
||||
}
|
||||
if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_INITED) {
|
||||
err = esp_bt_controller_enable(ESP_BT_MODE_BLE);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_bt_controller_enable failed: %s", esp_err_to_name(err));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_ENABLED) {
|
||||
ESP_LOGE(TAG, "esp bt controller enable failed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
|
||||
|
||||
err = esp_bluedroid_init();
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_bluedroid_init failed: %d", err);
|
||||
return;
|
||||
}
|
||||
err = esp_bluedroid_enable();
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_bluedroid_enable failed: %d", err);
|
||||
return;
|
||||
}
|
||||
err = esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_ADV,
|
||||
static_cast<esp_power_level_t>((global_esp32_ble_beacon->tx_power_ + 12) / 3));
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_ble_tx_power_set failed: %s", esp_err_to_name(err));
|
||||
return;
|
||||
}
|
||||
err = esp_ble_gap_register_callback(ESP32BLEBeacon::gap_event_handler);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_ble_gap_register_callback failed: %d", err);
|
||||
return;
|
||||
}
|
||||
|
||||
void ESP32BLEBeacon::on_advertise_() {
|
||||
esp_ble_ibeacon_t ibeacon_adv_data;
|
||||
memcpy(&ibeacon_adv_data.ibeacon_head, &IBEACON_COMMON_HEAD, sizeof(esp_ble_ibeacon_head_t));
|
||||
memcpy(&ibeacon_adv_data.ibeacon_vendor.proximity_uuid, global_esp32_ble_beacon->uuid_.data(),
|
||||
memcpy(&ibeacon_adv_data.ibeacon_vendor.proximity_uuid, this->uuid_.data(),
|
||||
sizeof(ibeacon_adv_data.ibeacon_vendor.proximity_uuid));
|
||||
ibeacon_adv_data.ibeacon_vendor.minor = ENDIAN_CHANGE_U16(global_esp32_ble_beacon->minor_);
|
||||
ibeacon_adv_data.ibeacon_vendor.major = ENDIAN_CHANGE_U16(global_esp32_ble_beacon->major_);
|
||||
ibeacon_adv_data.ibeacon_vendor.measured_power = static_cast<uint8_t>(global_esp32_ble_beacon->measured_power_);
|
||||
ibeacon_adv_data.ibeacon_vendor.minor = byteswap(this->minor_);
|
||||
ibeacon_adv_data.ibeacon_vendor.major = byteswap(this->major_);
|
||||
ibeacon_adv_data.ibeacon_vendor.measured_power = static_cast<uint8_t>(this->measured_power_);
|
||||
|
||||
esp_ble_gap_config_adv_data_raw((uint8_t *) &ibeacon_adv_data, sizeof(ibeacon_adv_data));
|
||||
ESP_LOGD(TAG, "Setting BLE TX power");
|
||||
esp_err_t err = esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_ADV, this->tx_power_);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "esp_ble_tx_power_set failed: %s", esp_err_to_name(err));
|
||||
}
|
||||
err = esp_ble_gap_config_adv_data_raw((uint8_t *) &ibeacon_adv_data, sizeof(ibeacon_adv_data));
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_ble_gap_config_adv_data_raw failed: %s", esp_err_to_name(err));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void ESP32BLEBeacon::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
|
||||
if (!this->advertising_)
|
||||
return;
|
||||
|
||||
esp_err_t err;
|
||||
switch (event) {
|
||||
case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT: {
|
||||
err = esp_ble_gap_start_advertising(&ble_adv_params);
|
||||
err = esp_ble_gap_start_advertising(&this->ble_adv_params_);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_ble_gap_start_advertising failed: %d", err);
|
||||
ESP_LOGE(TAG, "esp_ble_gap_start_advertising failed: %s", esp_err_to_name(err));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -181,6 +114,7 @@ void ESP32BLEBeacon::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap
|
|||
} else {
|
||||
ESP_LOGD(TAG, "BLE stopped advertising successfully");
|
||||
}
|
||||
// this->advertising_ = false;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -188,8 +122,6 @@ void ESP32BLEBeacon::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap
|
|||
}
|
||||
}
|
||||
|
||||
ESP32BLEBeacon *global_esp32_ble_beacon = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
||||
} // namespace esp32_ble_beacon
|
||||
} // namespace esphome
|
||||
|
||||
|
|
|
@ -1,39 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
#include "esphome/components/esp32_ble/ble.h"
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
#include <esp_gap_ble_api.h>
|
||||
#include <esp_bt.h>
|
||||
#include <esp_gap_ble_api.h>
|
||||
|
||||
namespace esphome {
|
||||
namespace esp32_ble_beacon {
|
||||
|
||||
// NOLINTNEXTLINE(modernize-use-using)
|
||||
typedef struct {
|
||||
using esp_ble_ibeacon_head_t = struct {
|
||||
uint8_t flags[3];
|
||||
uint8_t length;
|
||||
uint8_t type;
|
||||
uint8_t company_id[2];
|
||||
uint8_t beacon_type[2];
|
||||
} __attribute__((packed)) esp_ble_ibeacon_head_t;
|
||||
} __attribute__((packed));
|
||||
|
||||
// NOLINTNEXTLINE(modernize-use-using)
|
||||
typedef struct {
|
||||
using esp_ble_ibeacon_vendor_t = struct {
|
||||
uint8_t proximity_uuid[16];
|
||||
uint16_t major;
|
||||
uint16_t minor;
|
||||
uint8_t measured_power;
|
||||
} __attribute__((packed)) esp_ble_ibeacon_vendor_t;
|
||||
} __attribute__((packed));
|
||||
|
||||
// NOLINTNEXTLINE(modernize-use-using)
|
||||
typedef struct {
|
||||
using esp_ble_ibeacon_t = struct {
|
||||
esp_ble_ibeacon_head_t ibeacon_head;
|
||||
esp_ble_ibeacon_vendor_t ibeacon_vendor;
|
||||
} __attribute__((packed)) esp_ble_ibeacon_t;
|
||||
} __attribute__((packed));
|
||||
|
||||
class ESP32BLEBeacon : public Component {
|
||||
using namespace esp32_ble;
|
||||
|
||||
class ESP32BLEBeacon : public Component, public GAPEventHandler, public Parented<ESP32BLE> {
|
||||
public:
|
||||
explicit ESP32BLEBeacon(const std::array<uint8_t, 16> &uuid) : uuid_(uuid) {}
|
||||
|
||||
|
@ -46,12 +46,11 @@ class ESP32BLEBeacon : public Component {
|
|||
void set_min_interval(uint16_t val) { this->min_interval_ = val; }
|
||||
void set_max_interval(uint16_t val) { this->max_interval_ = val; }
|
||||
void set_measured_power(int8_t val) { this->measured_power_ = val; }
|
||||
void set_tx_power(int8_t val) { this->tx_power_ = val; }
|
||||
void set_tx_power(esp_power_level_t val) { this->tx_power_ = val; }
|
||||
void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override;
|
||||
|
||||
protected:
|
||||
static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param);
|
||||
static void ble_core_task(void *params);
|
||||
static void ble_setup();
|
||||
void on_advertise_();
|
||||
|
||||
std::array<uint8_t, 16> uuid_;
|
||||
uint16_t major_{};
|
||||
|
@ -59,12 +58,11 @@ class ESP32BLEBeacon : public Component {
|
|||
uint16_t min_interval_{};
|
||||
uint16_t max_interval_{};
|
||||
int8_t measured_power_{};
|
||||
int8_t tx_power_{};
|
||||
esp_power_level_t tx_power_{};
|
||||
esp_ble_adv_params_t ble_adv_params_;
|
||||
bool advertising_{false};
|
||||
};
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
extern ESP32BLEBeacon *global_esp32_ble_beacon;
|
||||
|
||||
} // namespace esp32_ble_beacon
|
||||
} // namespace esphome
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ from esphome.components.esp32 import add_idf_sdkconfig_option
|
|||
|
||||
AUTO_LOAD = ["esp32_ble"]
|
||||
CODEOWNERS = ["@jesserockz", "@clydebarrow", "@Rapsssito"]
|
||||
CONFLICTS_WITH = ["esp32_ble_beacon"]
|
||||
DEPENDENCIES = ["esp32"]
|
||||
|
||||
CONF_MANUFACTURER = "manufacturer"
|
||||
|
|
|
@ -6,7 +6,6 @@ from esphome.const import CONF_ID
|
|||
|
||||
AUTO_LOAD = ["esp32_ble_server"]
|
||||
CODEOWNERS = ["@jesserockz"]
|
||||
CONFLICTS_WITH = ["esp32_ble_beacon"]
|
||||
DEPENDENCIES = ["wifi", "esp32"]
|
||||
|
||||
CONF_AUTHORIZED_DURATION = "authorized_duration"
|
||||
|
|
|
@ -8,6 +8,7 @@ from esphome.const import (
|
|||
CONF_PROTOCOL,
|
||||
CONF_VISUAL,
|
||||
)
|
||||
from esphome.core import CORE
|
||||
|
||||
CODEOWNERS = ["@rob-deutsch"]
|
||||
|
||||
|
@ -127,3 +128,5 @@ def to_code(config):
|
|||
cg.add(var.set_min_temperature(config[CONF_MIN_TEMPERATURE]))
|
||||
|
||||
cg.add_library("tonia/HeatpumpIR", "1.0.27")
|
||||
if CORE.is_libretiny:
|
||||
CORE.add_platformio_option("lib_ignore", "IRremoteESP8266")
|
||||
|
|
|
@ -52,6 +52,7 @@ std::shared_ptr<HttpContainer> HttpRequestIDF::start(std::string url, std::strin
|
|||
config.timeout_ms = this->timeout_;
|
||||
config.disable_auto_redirect = !this->follow_redirects_;
|
||||
config.max_redirection_count = this->redirect_limit_;
|
||||
config.auth_type = HTTP_AUTH_TYPE_BASIC;
|
||||
#if CONFIG_MBEDTLS_CERTIFICATE_BUNDLE
|
||||
if (secure) {
|
||||
config.crt_bundle_attach = esp_crt_bundle_attach;
|
||||
|
|
|
@ -174,7 +174,8 @@ size_t I2SAudioMicrophone::read(int16_t *buf, size_t len) {
|
|||
size_t samples_read = bytes_read / sizeof(int32_t);
|
||||
samples.resize(samples_read);
|
||||
for (size_t i = 0; i < samples_read; i++) {
|
||||
samples[i] = reinterpret_cast<int32_t *>(buf)[i] >> 16;
|
||||
int32_t temp = reinterpret_cast<int32_t *>(buf)[i] >> 14;
|
||||
samples[i] = clamp<int16_t>(temp, INT16_MIN, INT16_MAX);
|
||||
}
|
||||
memcpy(buf, samples.data(), samples_read * sizeof(int16_t));
|
||||
return samples_read * sizeof(int16_t);
|
||||
|
|
|
@ -92,7 +92,9 @@ static const uint8_t ILI9XXX_GMCTRN1 = 0xE1;
|
|||
|
||||
static const uint8_t ILI9XXX_CSCON = 0xF0;
|
||||
static const uint8_t ILI9XXX_ADJCTL3 = 0xF7;
|
||||
static const uint8_t ILI9XXX_DELAY = 0xFF; // followed by one byte of delay time in ms
|
||||
static const uint8_t ILI9XXX_DELAY_FLAG = 0xFF;
|
||||
// special marker for delay - command byte reprents ms, length byte is an impossible value
|
||||
#define ILI9XXX_DELAY(ms) ((uint8_t) ((ms) | 0x80)), ILI9XXX_DELAY_FLAG
|
||||
|
||||
} // namespace ili9xxx
|
||||
} // namespace esphome
|
||||
|
|
|
@ -34,8 +34,8 @@ void ILI9XXXDisplay::setup() {
|
|||
ESP_LOGD(TAG, "Setting up ILI9xxx");
|
||||
|
||||
this->setup_pins_();
|
||||
this->init_lcd(this->init_sequence_);
|
||||
this->init_lcd(this->extra_init_sequence_.data());
|
||||
this->init_lcd_(this->init_sequence_);
|
||||
this->init_lcd_(this->extra_init_sequence_.data());
|
||||
switch (this->pixel_mode_) {
|
||||
case PIXEL_MODE_16:
|
||||
if (this->is_18bitdisplay_) {
|
||||
|
@ -405,42 +405,29 @@ void ILI9XXXDisplay::reset_() {
|
|||
}
|
||||
}
|
||||
|
||||
void ILI9XXXDisplay::init_lcd(const uint8_t *addr) {
|
||||
void ILI9XXXDisplay::init_lcd_(const uint8_t *addr) {
|
||||
if (addr == nullptr)
|
||||
return;
|
||||
uint8_t cmd, x, num_args;
|
||||
while ((cmd = *addr++) != 0) {
|
||||
x = *addr++;
|
||||
if (cmd == ILI9XXX_DELAY) {
|
||||
ESP_LOGD(TAG, "Delay %dms", x);
|
||||
delay(x);
|
||||
if (x == ILI9XXX_DELAY_FLAG) {
|
||||
cmd &= 0x7F;
|
||||
ESP_LOGV(TAG, "Delay %dms", cmd);
|
||||
delay(cmd);
|
||||
} else {
|
||||
num_args = x & 0x7F;
|
||||
ESP_LOGD(TAG, "Command %02X, length %d, bits %02X", cmd, num_args, *addr);
|
||||
ESP_LOGV(TAG, "Command %02X, length %d, bits %02X", cmd, num_args, *addr);
|
||||
this->send_command(cmd, addr, num_args);
|
||||
addr += num_args;
|
||||
if (x & 0x80) {
|
||||
ESP_LOGD(TAG, "Delay 150ms");
|
||||
ESP_LOGV(TAG, "Delay 150ms");
|
||||
delay(150); // NOLINT
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ILI9XXXGC9A01A::init_lcd(const uint8_t *addr) {
|
||||
if (addr == nullptr)
|
||||
return;
|
||||
uint8_t cmd, x, num_args;
|
||||
while ((cmd = *addr++) != 0) {
|
||||
x = *addr++;
|
||||
num_args = x & 0x7F;
|
||||
this->send_command(cmd, addr, num_args);
|
||||
addr += num_args;
|
||||
if (x & 0x80)
|
||||
delay(150); // NOLINT
|
||||
}
|
||||
}
|
||||
|
||||
// Tell the display controller where we want to draw pixels.
|
||||
void ILI9XXXDisplay::set_addr_window_(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) {
|
||||
x1 += this->offset_x_;
|
||||
|
|
|
@ -33,7 +33,9 @@ class ILI9XXXDisplay : public display::DisplayBuffer,
|
|||
uint8_t cmd, num_args, bits;
|
||||
const uint8_t *addr = init_sequence;
|
||||
while ((cmd = *addr++) != 0) {
|
||||
num_args = *addr++ & 0x7F;
|
||||
num_args = *addr++;
|
||||
if (num_args == ILI9XXX_DELAY_FLAG)
|
||||
continue;
|
||||
bits = *addr;
|
||||
switch (cmd) {
|
||||
case ILI9XXX_MADCTL: {
|
||||
|
@ -50,13 +52,10 @@ class ILI9XXXDisplay : public display::DisplayBuffer,
|
|||
break;
|
||||
}
|
||||
|
||||
case ILI9XXX_DELAY:
|
||||
continue; // no args to skip
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
addr += num_args;
|
||||
addr += (num_args & 0x7F);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -109,7 +108,7 @@ class ILI9XXXDisplay : public display::DisplayBuffer,
|
|||
|
||||
virtual void set_madctl();
|
||||
void display_();
|
||||
virtual void init_lcd(const uint8_t *addr);
|
||||
void init_lcd_(const uint8_t *addr);
|
||||
void set_addr_window_(uint16_t x, uint16_t y, uint16_t x2, uint16_t y2);
|
||||
void reset_();
|
||||
|
||||
|
@ -269,7 +268,6 @@ class ILI9XXXS3BoxLite : public ILI9XXXDisplay {
|
|||
class ILI9XXXGC9A01A : public ILI9XXXDisplay {
|
||||
public:
|
||||
ILI9XXXGC9A01A() : ILI9XXXDisplay(INITCMD_GC9A01A, 240, 240, true) {}
|
||||
void init_lcd(const uint8_t *addr) override;
|
||||
};
|
||||
|
||||
//----------- ILI9XXX_24_TFT display --------------
|
||||
|
|
|
@ -372,9 +372,9 @@ static const uint8_t PROGMEM INITCMD_GC9A01A[] = {
|
|||
|
||||
static const uint8_t PROGMEM INITCMD_ST7735[] = {
|
||||
ILI9XXX_SWRESET, 0, // Soft reset, then delay 10ms
|
||||
ILI9XXX_DELAY, 10,
|
||||
ILI9XXX_DELAY(10),
|
||||
ILI9XXX_SLPOUT , 0, // Exit Sleep, delay
|
||||
ILI9XXX_DELAY, 10,
|
||||
ILI9XXX_DELAY(10),
|
||||
ILI9XXX_PIXFMT , 1, 0x05,
|
||||
ILI9XXX_FRMCTR1, 3, // 4: Frame rate control, 3 args + delay:
|
||||
0x01, 0x2C, 0x2D, // Rate = fosc/(1x2+40) * (LINE+2C+2D)
|
||||
|
@ -415,9 +415,9 @@ static const uint8_t PROGMEM INITCMD_ST7735[] = {
|
|||
0x00, 0x00, 0x02, 0x10,
|
||||
ILI9XXX_MADCTL , 1, 0x00, // Memory Access Control, BGR
|
||||
ILI9XXX_NORON , 0,
|
||||
ILI9XXX_DELAY, 10,
|
||||
ILI9XXX_DELAY(10),
|
||||
ILI9XXX_DISPON , 0, // Display on
|
||||
ILI9XXX_DELAY, 10,
|
||||
ILI9XXX_DELAY(10),
|
||||
00, // endo of list
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import esphome.config_validation as cv
|
||||
|
||||
CONFIG_SCHEMA = CONFIG_SCHEMA = cv.invalid(
|
||||
CONFIG_SCHEMA = cv.invalid(
|
||||
"The kalman_combinator sensor has moved.\nPlease use the combination platform instead with type: kalman.\n"
|
||||
"See https://esphome.io/components/sensor/combination.html"
|
||||
)
|
||||
|
|
33
esphome/components/m5stack_8angle/__init__.py
Normal file
33
esphome/components/m5stack_8angle/__init__.py
Normal file
|
@ -0,0 +1,33 @@
|
|||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import i2c
|
||||
from esphome.const import CONF_ID
|
||||
|
||||
|
||||
DEPENDENCIES = ["i2c"]
|
||||
CODEOWNERS = ["@rnauber"]
|
||||
MULTI_CONF = True
|
||||
|
||||
CONF_M5STACK_8ANGLE_ID = "m5stack_8angle_id"
|
||||
|
||||
m5stack_8angle_ns = cg.esphome_ns.namespace("m5stack_8angle")
|
||||
M5Stack8AngleComponent = m5stack_8angle_ns.class_(
|
||||
"M5Stack8AngleComponent",
|
||||
i2c.I2CDevice,
|
||||
cg.Component,
|
||||
)
|
||||
|
||||
AnalogBits = m5stack_8angle_ns.enum("AnalogBits")
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(M5Stack8AngleComponent),
|
||||
}
|
||||
).extend(i2c.i2c_device_schema(0x43))
|
||||
|
||||
|
||||
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)
|
30
esphome/components/m5stack_8angle/binary_sensor/__init__.py
Normal file
30
esphome/components/m5stack_8angle/binary_sensor/__init__.py
Normal file
|
@ -0,0 +1,30 @@
|
|||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import binary_sensor
|
||||
|
||||
from .. import M5Stack8AngleComponent, m5stack_8angle_ns, CONF_M5STACK_8ANGLE_ID
|
||||
|
||||
|
||||
M5Stack8AngleSwitchBinarySensor = m5stack_8angle_ns.class_(
|
||||
"M5Stack8AngleSwitchBinarySensor",
|
||||
binary_sensor.BinarySensor,
|
||||
cg.PollingComponent,
|
||||
)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_M5STACK_8ANGLE_ID): cv.use_id(M5Stack8AngleComponent),
|
||||
}
|
||||
)
|
||||
.extend(binary_sensor.binary_sensor_schema(M5Stack8AngleSwitchBinarySensor))
|
||||
.extend(cv.polling_component_schema("10s"))
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
hub = await cg.get_variable(config[CONF_M5STACK_8ANGLE_ID])
|
||||
sens = await binary_sensor.new_binary_sensor(config)
|
||||
cg.add(sens.set_parent(hub))
|
||||
await cg.register_component(sens, config)
|
|
@ -0,0 +1,17 @@
|
|||
#include "m5stack_8angle_binary_sensor.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace m5stack_8angle {
|
||||
|
||||
void M5Stack8AngleSwitchBinarySensor::update() {
|
||||
int8_t out = this->parent_->read_switch();
|
||||
if (out == -1) {
|
||||
this->status_set_warning("Could not read binary sensor state from M5Stack 8Angle.");
|
||||
return;
|
||||
}
|
||||
this->publish_state(out != 0);
|
||||
this->status_clear_warning();
|
||||
}
|
||||
|
||||
} // namespace m5stack_8angle
|
||||
} // namespace esphome
|
|
@ -0,0 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
#include "../m5stack_8angle.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace m5stack_8angle {
|
||||
|
||||
class M5Stack8AngleSwitchBinarySensor : public binary_sensor::BinarySensor,
|
||||
public PollingComponent,
|
||||
public Parented<M5Stack8AngleComponent> {
|
||||
public:
|
||||
void update() override;
|
||||
};
|
||||
|
||||
} // namespace m5stack_8angle
|
||||
} // namespace esphome
|
31
esphome/components/m5stack_8angle/light/__init__.py
Normal file
31
esphome/components/m5stack_8angle/light/__init__.py
Normal file
|
@ -0,0 +1,31 @@
|
|||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import light
|
||||
|
||||
from esphome.const import CONF_OUTPUT_ID
|
||||
|
||||
from .. import M5Stack8AngleComponent, m5stack_8angle_ns, CONF_M5STACK_8ANGLE_ID
|
||||
|
||||
|
||||
M5Stack8AngleLightsComponent = m5stack_8angle_ns.class_(
|
||||
"M5Stack8AngleLightOutput",
|
||||
light.AddressableLight,
|
||||
)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
light.ADDRESSABLE_LIGHT_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(CONF_M5STACK_8ANGLE_ID): cv.use_id(M5Stack8AngleComponent),
|
||||
cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(M5Stack8AngleLightsComponent),
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
hub = await cg.get_variable(config[CONF_M5STACK_8ANGLE_ID])
|
||||
lights = cg.new_Pvariable(config[CONF_OUTPUT_ID])
|
||||
await light.register_light(lights, config)
|
||||
await cg.register_component(lights, config)
|
||||
cg.add(lights.set_parent(hub))
|
|
@ -0,0 +1,45 @@
|
|||
#include "m5stack_8angle_light.h"
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace m5stack_8angle {
|
||||
|
||||
static const char *const TAG = "m5stack_8angle.light";
|
||||
|
||||
void M5Stack8AngleLightOutput::setup() {
|
||||
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
|
||||
this->buf_ = allocator.allocate(M5STACK_8ANGLE_NUM_LEDS * M5STACK_8ANGLE_BYTES_PER_LED);
|
||||
if (this->buf_ == nullptr) {
|
||||
ESP_LOGE(TAG, "Failed to allocate buffer of size %u", M5STACK_8ANGLE_NUM_LEDS * M5STACK_8ANGLE_BYTES_PER_LED);
|
||||
this->mark_failed();
|
||||
return;
|
||||
};
|
||||
memset(this->buf_, 0xFF, M5STACK_8ANGLE_NUM_LEDS * M5STACK_8ANGLE_BYTES_PER_LED);
|
||||
|
||||
this->effect_data_ = allocator.allocate(M5STACK_8ANGLE_NUM_LEDS);
|
||||
if (this->effect_data_ == nullptr) {
|
||||
ESP_LOGE(TAG, "Failed to allocate effect data of size %u", M5STACK_8ANGLE_NUM_LEDS);
|
||||
this->mark_failed();
|
||||
return;
|
||||
};
|
||||
memset(this->effect_data_, 0x00, M5STACK_8ANGLE_NUM_LEDS);
|
||||
}
|
||||
|
||||
void M5Stack8AngleLightOutput::write_state(light::LightState *state) {
|
||||
for (int i = 0; i < M5STACK_8ANGLE_NUM_LEDS;
|
||||
i++) { // write one LED at a time, otherwise the message will be truncated
|
||||
this->parent_->write_register(M5STACK_8ANGLE_REGISTER_RGB_24B + i * M5STACK_8ANGLE_BYTES_PER_LED,
|
||||
this->buf_ + i * M5STACK_8ANGLE_BYTES_PER_LED, M5STACK_8ANGLE_BYTES_PER_LED);
|
||||
}
|
||||
}
|
||||
|
||||
light::ESPColorView M5Stack8AngleLightOutput::get_view_internal(int32_t index) const {
|
||||
size_t pos = index * M5STACK_8ANGLE_BYTES_PER_LED;
|
||||
// red, green, blue, white, effect_data, color_correction
|
||||
return {this->buf_ + pos, this->buf_ + pos + 1, this->buf_ + pos + 2,
|
||||
nullptr, this->effect_data_ + index, &this->correction_};
|
||||
}
|
||||
|
||||
} // namespace m5stack_8angle
|
||||
} // namespace esphome
|
|
@ -0,0 +1,37 @@
|
|||
#pragma once
|
||||
|
||||
#include "esphome/components/light/addressable_light.h"
|
||||
#include "esphome/components/light/light_output.h"
|
||||
|
||||
#include "../m5stack_8angle.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace m5stack_8angle {
|
||||
|
||||
static const uint8_t M5STACK_8ANGLE_NUM_LEDS = 9;
|
||||
static const uint8_t M5STACK_8ANGLE_BYTES_PER_LED = 4;
|
||||
|
||||
class M5Stack8AngleLightOutput : public light::AddressableLight, public Parented<M5Stack8AngleComponent> {
|
||||
public:
|
||||
void setup() override;
|
||||
|
||||
void write_state(light::LightState *state) override;
|
||||
|
||||
int32_t size() const override { return M5STACK_8ANGLE_NUM_LEDS; }
|
||||
light::LightTraits get_traits() override {
|
||||
auto traits = light::LightTraits();
|
||||
traits.set_supported_color_modes({light::ColorMode::RGB});
|
||||
return traits;
|
||||
};
|
||||
|
||||
void clear_effect_data() override { memset(this->effect_data_, 0x00, M5STACK_8ANGLE_NUM_LEDS); };
|
||||
|
||||
protected:
|
||||
light::ESPColorView get_view_internal(int32_t index) const override;
|
||||
|
||||
uint8_t *buf_{nullptr};
|
||||
uint8_t *effect_data_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace m5stack_8angle
|
||||
} // namespace esphome
|
74
esphome/components/m5stack_8angle/m5stack_8angle.cpp
Normal file
74
esphome/components/m5stack_8angle/m5stack_8angle.cpp
Normal file
|
@ -0,0 +1,74 @@
|
|||
#include "m5stack_8angle.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace m5stack_8angle {
|
||||
|
||||
static const char *const TAG = "m5stack_8angle";
|
||||
|
||||
void M5Stack8AngleComponent::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up M5STACK_8ANGLE...");
|
||||
i2c::ErrorCode err;
|
||||
|
||||
err = this->read(nullptr, 0);
|
||||
if (err != i2c::NO_ERROR) {
|
||||
ESP_LOGE(TAG, "I2C error %02X...", err);
|
||||
this->mark_failed();
|
||||
return;
|
||||
};
|
||||
|
||||
err = this->read_register(M5STACK_8ANGLE_REGISTER_FW_VERSION, &this->fw_version_, 1);
|
||||
if (err != i2c::NO_ERROR) {
|
||||
ESP_LOGE(TAG, "I2C error %02X...", err);
|
||||
this->mark_failed();
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
void M5Stack8AngleComponent::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "M5STACK_8ANGLE:");
|
||||
LOG_I2C_DEVICE(this);
|
||||
ESP_LOGCONFIG(TAG, " Firmware version: %d ", this->fw_version_);
|
||||
}
|
||||
|
||||
float M5Stack8AngleComponent::read_knob_pos(uint8_t channel, AnalogBits bits) {
|
||||
int32_t raw_pos = this->read_knob_pos_raw(channel, bits);
|
||||
if (raw_pos == -1) {
|
||||
return NAN;
|
||||
}
|
||||
return (float) raw_pos / ((1 << bits) - 1);
|
||||
}
|
||||
|
||||
int32_t M5Stack8AngleComponent::read_knob_pos_raw(uint8_t channel, AnalogBits bits) {
|
||||
uint16_t knob_pos = 0;
|
||||
i2c::ErrorCode err;
|
||||
if (bits == BITS_8) {
|
||||
err = this->read_register(M5STACK_8ANGLE_REGISTER_ANALOG_INPUT_8B + channel, (uint8_t *) &knob_pos, 1);
|
||||
} else if (bits == BITS_12) {
|
||||
err = this->read_register(M5STACK_8ANGLE_REGISTER_ANALOG_INPUT_12B + (channel * 2), (uint8_t *) &knob_pos, 2);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Invalid number of bits: %d", bits);
|
||||
return -1;
|
||||
}
|
||||
if (err == i2c::NO_ERROR) {
|
||||
return knob_pos;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int8_t M5Stack8AngleComponent::read_switch() {
|
||||
uint8_t out;
|
||||
i2c::ErrorCode err = this->read_register(M5STACK_8ANGLE_REGISTER_DIGITAL_INPUT, (uint8_t *) &out, 1);
|
||||
if (err == i2c::NO_ERROR) {
|
||||
return out ? 1 : 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
float M5Stack8AngleComponent::get_setup_priority() const { return setup_priority::DATA; }
|
||||
|
||||
} // namespace m5stack_8angle
|
||||
} // namespace esphome
|
34
esphome/components/m5stack_8angle/m5stack_8angle.h
Normal file
34
esphome/components/m5stack_8angle/m5stack_8angle.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
#pragma once
|
||||
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace m5stack_8angle {
|
||||
|
||||
static const uint8_t M5STACK_8ANGLE_REGISTER_ANALOG_INPUT_12B = 0x00;
|
||||
static const uint8_t M5STACK_8ANGLE_REGISTER_ANALOG_INPUT_8B = 0x10;
|
||||
static const uint8_t M5STACK_8ANGLE_REGISTER_DIGITAL_INPUT = 0x20;
|
||||
static const uint8_t M5STACK_8ANGLE_REGISTER_RGB_24B = 0x30;
|
||||
static const uint8_t M5STACK_8ANGLE_REGISTER_FW_VERSION = 0xFE;
|
||||
|
||||
enum AnalogBits : uint8_t {
|
||||
BITS_8 = 8,
|
||||
BITS_12 = 12,
|
||||
};
|
||||
|
||||
class M5Stack8AngleComponent : public i2c::I2CDevice, public Component {
|
||||
public:
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override;
|
||||
float read_knob_pos(uint8_t channel, AnalogBits bits = AnalogBits::BITS_8);
|
||||
int32_t read_knob_pos_raw(uint8_t channel, AnalogBits bits = AnalogBits::BITS_8);
|
||||
int8_t read_switch();
|
||||
|
||||
protected:
|
||||
uint8_t fw_version_;
|
||||
};
|
||||
|
||||
} // namespace m5stack_8angle
|
||||
} // namespace esphome
|
66
esphome/components/m5stack_8angle/sensor/__init__.py
Normal file
66
esphome/components/m5stack_8angle/sensor/__init__.py
Normal file
|
@ -0,0 +1,66 @@
|
|||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor
|
||||
|
||||
from esphome.const import (
|
||||
CONF_BIT_DEPTH,
|
||||
CONF_CHANNEL,
|
||||
CONF_RAW,
|
||||
ICON_ROTATE_RIGHT,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
|
||||
from .. import (
|
||||
AnalogBits,
|
||||
M5Stack8AngleComponent,
|
||||
m5stack_8angle_ns,
|
||||
CONF_M5STACK_8ANGLE_ID,
|
||||
)
|
||||
|
||||
|
||||
M5Stack8AngleKnobSensor = m5stack_8angle_ns.class_(
|
||||
"M5Stack8AngleKnobSensor",
|
||||
sensor.Sensor,
|
||||
cg.PollingComponent,
|
||||
)
|
||||
|
||||
|
||||
BIT_DEPTHS = {
|
||||
8: AnalogBits.BITS_8,
|
||||
12: AnalogBits.BITS_12,
|
||||
}
|
||||
|
||||
_validate_bits = cv.float_with_unit("bits", "bit")
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(M5Stack8AngleKnobSensor),
|
||||
cv.GenerateID(CONF_M5STACK_8ANGLE_ID): cv.use_id(M5Stack8AngleComponent),
|
||||
cv.Required(CONF_CHANNEL): cv.int_range(min=1, max=8),
|
||||
cv.Optional(CONF_BIT_DEPTH, default="8bit"): cv.All(
|
||||
_validate_bits, cv.enum(BIT_DEPTHS)
|
||||
),
|
||||
cv.Optional(CONF_RAW, default=False): cv.boolean,
|
||||
}
|
||||
)
|
||||
.extend(
|
||||
sensor.sensor_schema(
|
||||
M5Stack8AngleKnobSensor,
|
||||
accuracy_decimals=2,
|
||||
icon=ICON_ROTATE_RIGHT,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
)
|
||||
.extend(cv.polling_component_schema("10s"))
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = await sensor.new_sensor(config)
|
||||
await cg.register_component(var, config)
|
||||
await cg.register_parented(var, config[CONF_M5STACK_8ANGLE_ID])
|
||||
cg.add(var.set_channel(config[CONF_CHANNEL] - 1))
|
||||
cg.add(var.set_bit_depth(BIT_DEPTHS[config[CONF_BIT_DEPTH]]))
|
||||
cg.add(var.set_raw(config[CONF_RAW]))
|
|
@ -0,0 +1,24 @@
|
|||
#include "m5stack_8angle_sensor.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace m5stack_8angle {
|
||||
|
||||
void M5Stack8AngleKnobSensor::update() {
|
||||
if (this->parent_ != nullptr) {
|
||||
int32_t raw_pos = this->parent_->read_knob_pos_raw(this->channel_, this->bits_);
|
||||
if (raw_pos == -1) {
|
||||
this->status_set_warning("Could not read knob position from M5Stack 8Angle.");
|
||||
return;
|
||||
}
|
||||
if (this->raw_) {
|
||||
this->publish_state(raw_pos);
|
||||
} else {
|
||||
float knob_pos = (float) raw_pos / ((1 << this->bits_) - 1);
|
||||
this->publish_state(knob_pos);
|
||||
}
|
||||
this->status_clear_warning();
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace m5stack_8angle
|
||||
} // namespace esphome
|
|
@ -0,0 +1,27 @@
|
|||
#pragma once
|
||||
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
#include "../m5stack_8angle.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace m5stack_8angle {
|
||||
|
||||
class M5Stack8AngleKnobSensor : public sensor::Sensor,
|
||||
public PollingComponent,
|
||||
public Parented<M5Stack8AngleComponent> {
|
||||
public:
|
||||
void update() override;
|
||||
void set_channel(uint8_t channel) { this->channel_ = channel; };
|
||||
void set_bit_depth(AnalogBits bits) { this->bits_ = bits; };
|
||||
void set_raw(bool raw) { this->raw_ = raw; };
|
||||
|
||||
protected:
|
||||
uint8_t channel_;
|
||||
AnalogBits bits_;
|
||||
bool raw_;
|
||||
};
|
||||
|
||||
} // namespace m5stack_8angle
|
||||
} // namespace esphome
|
|
@ -1,8 +1,16 @@
|
|||
import binascii
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome import automation
|
||||
from esphome.components import modbus
|
||||
from esphome.const import CONF_ADDRESS, CONF_ID, CONF_NAME, CONF_LAMBDA, CONF_OFFSET
|
||||
from esphome.const import (
|
||||
CONF_ADDRESS,
|
||||
CONF_ID,
|
||||
CONF_NAME,
|
||||
CONF_LAMBDA,
|
||||
CONF_OFFSET,
|
||||
CONF_TRIGGER_ID,
|
||||
)
|
||||
from esphome.cpp_helpers import logging
|
||||
from .const import (
|
||||
CONF_BITMASK,
|
||||
|
@ -12,6 +20,7 @@ from .const import (
|
|||
CONF_CUSTOM_COMMAND,
|
||||
CONF_FORCE_NEW_RANGE,
|
||||
CONF_MODBUS_CONTROLLER_ID,
|
||||
CONF_ON_COMMAND_SENT,
|
||||
CONF_REGISTER_COUNT,
|
||||
CONF_REGISTER_TYPE,
|
||||
CONF_RESPONSE_SIZE,
|
||||
|
@ -97,6 +106,10 @@ TYPE_REGISTER_MAP = {
|
|||
"FP32_R": 2,
|
||||
}
|
||||
|
||||
ModbusCommandSentTrigger = modbus_controller_ns.class_(
|
||||
"ModbusCommandSentTrigger", automation.Trigger.template(cg.int_, cg.int_)
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
ModbusServerRegisterSchema = cv.Schema(
|
||||
|
@ -120,13 +133,19 @@ CONFIG_SCHEMA = cv.All(
|
|||
cv.Optional(
|
||||
CONF_SERVER_REGISTERS,
|
||||
): cv.ensure_list(ModbusServerRegisterSchema),
|
||||
cv.Optional(CONF_ON_COMMAND_SENT): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
ModbusCommandSentTrigger
|
||||
),
|
||||
}
|
||||
),
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("60s"))
|
||||
.extend(modbus.modbus_device_schema(0x01))
|
||||
)
|
||||
|
||||
|
||||
ModbusItemBaseSchema = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_MODBUS_CONTROLLER_ID): cv.use_id(ModbusController),
|
||||
|
@ -254,6 +273,11 @@ async def to_code(config):
|
|||
)
|
||||
)
|
||||
await register_modbus_device(var, config)
|
||||
for conf in config.get(CONF_ON_COMMAND_SENT, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(
|
||||
trigger, [(int, "function_code"), (int, "address")], conf
|
||||
)
|
||||
|
||||
|
||||
async def register_modbus_device(var, config):
|
||||
|
|
19
esphome/components/modbus_controller/automation.h
Normal file
19
esphome/components/modbus_controller/automation.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/components/modbus_controller/modbus_controller.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace modbus_controller {
|
||||
|
||||
class ModbusCommandSentTrigger : public Trigger<int, int> {
|
||||
public:
|
||||
ModbusCommandSentTrigger(ModbusController *a_modbuscontroller) {
|
||||
a_modbuscontroller->add_on_command_sent_callback(
|
||||
[this](int function_code, int address) { this->trigger(function_code, address); });
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace modbus_controller
|
||||
} // namespace esphome
|
|
@ -6,6 +6,7 @@ CONF_CUSTOM_COMMAND = "custom_command"
|
|||
CONF_FORCE_NEW_RANGE = "force_new_range"
|
||||
CONF_MODBUS_CONTROLLER_ID = "modbus_controller_id"
|
||||
CONF_MODBUS_FUNCTIONCODE = "modbus_functioncode"
|
||||
CONF_ON_COMMAND_SENT = "on_command_sent"
|
||||
CONF_RAW_ENCODE = "raw_encode"
|
||||
CONF_REGISTER_COUNT = "register_count"
|
||||
CONF_REGISTER_TYPE = "register_type"
|
||||
|
|
|
@ -43,7 +43,11 @@ bool ModbusController::send_next_command_() {
|
|||
ESP_LOGV(TAG, "Sending next modbus command to device %d register 0x%02X count %d", this->address_,
|
||||
command->register_address, command->register_count);
|
||||
command->send();
|
||||
|
||||
this->last_command_timestamp_ = millis();
|
||||
|
||||
this->command_sent_callback_.call((int) command->function_code, command->register_address);
|
||||
|
||||
// remove from queue if no handler is defined
|
||||
if (!command->on_data_func) {
|
||||
command_queue_.pop_front();
|
||||
|
@ -659,5 +663,9 @@ int64_t payload_to_number(const std::vector<uint8_t> &data, SensorValueType sens
|
|||
return value;
|
||||
}
|
||||
|
||||
void ModbusController::add_on_command_sent_callback(std::function<void(int, int)> &&callback) {
|
||||
this->command_sent_callback_.add(std::move(callback));
|
||||
}
|
||||
|
||||
} // namespace modbus_controller
|
||||
} // namespace esphome
|
||||
|
|
|
@ -456,6 +456,8 @@ class ModbusController : public PollingComponent, public modbus::ModbusDevice {
|
|||
size_t get_command_queue_length() { return command_queue_.size(); }
|
||||
/// get if the module is offline, didn't respond the last command
|
||||
bool get_module_offline() { return module_offline_; }
|
||||
/// Set callback for commands
|
||||
void add_on_command_sent_callback(std::function<void(int, int)> &&callback);
|
||||
|
||||
protected:
|
||||
/// parse sensormap_ and create range of sequential addresses
|
||||
|
@ -488,6 +490,7 @@ class ModbusController : public PollingComponent, public modbus::ModbusDevice {
|
|||
bool module_offline_;
|
||||
/// how many updates to skip if module is offline
|
||||
uint16_t offline_skip_updates_;
|
||||
CallbackManager<void(int, int)> command_sent_callback_{};
|
||||
};
|
||||
|
||||
/** Convert vector<uint8_t> response payload to float.
|
||||
|
|
|
@ -61,6 +61,7 @@ def AUTO_LOAD():
|
|||
return ["json"]
|
||||
|
||||
|
||||
CONF_DISCOVER_IP = "discover_ip"
|
||||
CONF_IDF_SEND_ASYNC = "idf_send_async"
|
||||
CONF_SKIP_CERT_CN_CHECK = "skip_cert_cn_check"
|
||||
|
||||
|
@ -225,6 +226,7 @@ CONFIG_SCHEMA = cv.All(
|
|||
cv.boolean, cv.one_of("CLEAN", upper=True)
|
||||
),
|
||||
cv.Optional(CONF_DISCOVERY_RETAIN, default=True): cv.boolean,
|
||||
cv.Optional(CONF_DISCOVER_IP, default=True): cv.boolean,
|
||||
cv.Optional(
|
||||
CONF_DISCOVERY_PREFIX, default="homeassistant"
|
||||
): cv.publish_topic,
|
||||
|
@ -328,8 +330,12 @@ async def to_code(config):
|
|||
discovery_prefix = config[CONF_DISCOVERY_PREFIX]
|
||||
discovery_unique_id_generator = config[CONF_DISCOVERY_UNIQUE_ID_GENERATOR]
|
||||
discovery_object_id_generator = config[CONF_DISCOVERY_OBJECT_ID_GENERATOR]
|
||||
discover_ip = config[CONF_DISCOVER_IP]
|
||||
|
||||
if not discovery:
|
||||
discovery_prefix = ""
|
||||
|
||||
if not discovery and not discover_ip:
|
||||
cg.add(var.disable_discovery())
|
||||
elif discovery == "CLEAN":
|
||||
cg.add(
|
||||
|
@ -338,6 +344,7 @@ async def to_code(config):
|
|||
discovery_unique_id_generator,
|
||||
discovery_object_id_generator,
|
||||
discovery_retain,
|
||||
discover_ip,
|
||||
True,
|
||||
)
|
||||
)
|
||||
|
@ -348,6 +355,7 @@ async def to_code(config):
|
|||
discovery_unique_id_generator,
|
||||
discovery_object_id_generator,
|
||||
discovery_retain,
|
||||
discover_ip,
|
||||
)
|
||||
)
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ void MQTTClientComponent::setup() {
|
|||
}
|
||||
#endif
|
||||
|
||||
if (this->is_discovery_enabled()) {
|
||||
if (this->is_discovery_ip_enabled()) {
|
||||
this->subscribe(
|
||||
"esphome/discover", [this](const std::string &topic, const std::string &payload) { this->send_device_info_(); },
|
||||
2);
|
||||
|
@ -82,7 +82,7 @@ void MQTTClientComponent::setup() {
|
|||
}
|
||||
|
||||
void MQTTClientComponent::send_device_info_() {
|
||||
if (!this->is_connected() or !this->is_discovery_enabled()) {
|
||||
if (!this->is_connected() or !this->is_discovery_ip_enabled()) {
|
||||
return;
|
||||
}
|
||||
std::string topic = "esphome/discover/";
|
||||
|
@ -99,6 +99,9 @@ void MQTTClientComponent::send_device_info_() {
|
|||
}
|
||||
}
|
||||
root["name"] = App.get_name();
|
||||
if (!App.get_friendly_name().empty()) {
|
||||
root["friendly_name"] = App.get_friendly_name();
|
||||
}
|
||||
#ifdef USE_API
|
||||
root["port"] = api::global_api_server->get_port();
|
||||
#endif
|
||||
|
@ -130,6 +133,10 @@ void MQTTClientComponent::send_device_info_() {
|
|||
#ifdef USE_DASHBOARD_IMPORT
|
||||
root["package_import_url"] = dashboard_import::get_package_import_url();
|
||||
#endif
|
||||
|
||||
#ifdef USE_API_NOISE
|
||||
root["api_encryption"] = "Noise_NNpsk0_25519_ChaChaPoly_SHA256";
|
||||
#endif
|
||||
},
|
||||
2, this->discovery_info_.retain);
|
||||
}
|
||||
|
@ -140,6 +147,9 @@ void MQTTClientComponent::dump_config() {
|
|||
this->ip_.str().c_str());
|
||||
ESP_LOGCONFIG(TAG, " Username: " LOG_SECRET("'%s'"), this->credentials_.username.c_str());
|
||||
ESP_LOGCONFIG(TAG, " Client ID: " LOG_SECRET("'%s'"), this->credentials_.client_id.c_str());
|
||||
if (this->is_discovery_ip_enabled()) {
|
||||
ESP_LOGCONFIG(TAG, " Discovery IP enabled");
|
||||
}
|
||||
if (!this->discovery_info_.prefix.empty()) {
|
||||
ESP_LOGCONFIG(TAG, " Discovery prefix: '%s'", this->discovery_info_.prefix.c_str());
|
||||
ESP_LOGCONFIG(TAG, " Discovery retain: %s", YESNO(this->discovery_info_.retain));
|
||||
|
@ -581,6 +591,7 @@ void MQTTClientComponent::disable_shutdown_message() {
|
|||
this->recalculate_availability_();
|
||||
}
|
||||
bool MQTTClientComponent::is_discovery_enabled() const { return !this->discovery_info_.prefix.empty(); }
|
||||
bool MQTTClientComponent::is_discovery_ip_enabled() const { return this->discovery_info_.discover_ip; }
|
||||
const Availability &MQTTClientComponent::get_availability() { return this->availability_; }
|
||||
void MQTTClientComponent::recalculate_availability_() {
|
||||
if (this->birth_message_.topic.empty() || this->birth_message_.topic != this->last_will_.topic) {
|
||||
|
@ -606,8 +617,9 @@ void MQTTClientComponent::set_shutdown_message(MQTTMessage &&message) { this->sh
|
|||
|
||||
void MQTTClientComponent::set_discovery_info(std::string &&prefix, MQTTDiscoveryUniqueIdGenerator unique_id_generator,
|
||||
MQTTDiscoveryObjectIdGenerator object_id_generator, bool retain,
|
||||
bool clean) {
|
||||
bool discover_ip, bool clean) {
|
||||
this->discovery_info_.prefix = std::move(prefix);
|
||||
this->discovery_info_.discover_ip = discover_ip;
|
||||
this->discovery_info_.unique_id_generator = unique_id_generator;
|
||||
this->discovery_info_.object_id_generator = object_id_generator;
|
||||
this->discovery_info_.retain = retain;
|
||||
|
|
|
@ -79,6 +79,7 @@ enum MQTTDiscoveryObjectIdGenerator {
|
|||
struct MQTTDiscoveryInfo {
|
||||
std::string prefix; ///< The Home Assistant discovery prefix. Empty means disabled.
|
||||
bool retain; ///< Whether to retain discovery messages.
|
||||
bool discover_ip; ///< Enable the Home Assistant device discovery.
|
||||
bool clean;
|
||||
MQTTDiscoveryUniqueIdGenerator unique_id_generator;
|
||||
MQTTDiscoveryObjectIdGenerator object_id_generator;
|
||||
|
@ -122,12 +123,14 @@ class MQTTClientComponent : public Component {
|
|||
* @param retain Whether to retain discovery messages.
|
||||
*/
|
||||
void set_discovery_info(std::string &&prefix, MQTTDiscoveryUniqueIdGenerator unique_id_generator,
|
||||
MQTTDiscoveryObjectIdGenerator object_id_generator, bool retain, bool clean = false);
|
||||
MQTTDiscoveryObjectIdGenerator object_id_generator, bool retain, bool discover_ip,
|
||||
bool clean = false);
|
||||
/// Get Home Assistant discovery info.
|
||||
const MQTTDiscoveryInfo &get_discovery_info() const;
|
||||
/// Globally disable Home Assistant discovery.
|
||||
void disable_discovery();
|
||||
bool is_discovery_enabled() const;
|
||||
bool is_discovery_ip_enabled() const;
|
||||
|
||||
#if ASYNC_TCP_SSL_ENABLED
|
||||
/** Add a SSL fingerprint to use for TCP SSL connections to the MQTT broker.
|
||||
|
@ -290,6 +293,7 @@ class MQTTClientComponent : public Component {
|
|||
MQTTDiscoveryInfo discovery_info_{
|
||||
.prefix = "homeassistant",
|
||||
.retain = true,
|
||||
.discover_ip = true,
|
||||
.clean = false,
|
||||
.unique_id_generator = MQTT_LEGACY_UNIQUE_ID_GENERATOR,
|
||||
.object_id_generator = MQTT_NONE_OBJECT_ID_GENERATOR,
|
||||
|
|
|
@ -27,7 +27,7 @@ bool SmlFile::setup_node(SmlNode *node) {
|
|||
uint8_t parse_length = length;
|
||||
if (has_extended_length) {
|
||||
length = (length << 4) + (this->buffer_[this->pos_ + 1] & 0x0f);
|
||||
parse_length = length - 1;
|
||||
parse_length = length;
|
||||
this->pos_ += 1;
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,9 @@ bool SmlFile::setup_node(SmlNode *node) {
|
|||
node->type = type & 0x07;
|
||||
node->nodes.clear();
|
||||
node->value_bytes.clear();
|
||||
if (this->buffer_[this->pos_] == 0x00) { // end of message
|
||||
|
||||
// if the list is a has_extended_length list with e.g. 16 elements this is a 0x00 byte but not the end of message
|
||||
if (!has_extended_length && this->buffer_[this->pos_] == 0x00) { // end of message
|
||||
this->pos_ += 1;
|
||||
} else if (is_list) { // list
|
||||
this->pos_ += 1;
|
||||
|
|
|
@ -832,7 +832,6 @@ def time_of_day(value):
|
|||
|
||||
|
||||
def date_time(date: bool, time: bool):
|
||||
|
||||
pattern_str = r"^" # Start of string
|
||||
if date:
|
||||
pattern_str += r"\d{4}-\d{1,2}-\d{1,2}"
|
||||
|
@ -2038,6 +2037,7 @@ def require_framework_version(
|
|||
esp32_arduino=None,
|
||||
esp8266_arduino=None,
|
||||
rp2040_arduino=None,
|
||||
host=None,
|
||||
max_version=False,
|
||||
extra_message=None,
|
||||
):
|
||||
|
@ -2072,6 +2072,13 @@ def require_framework_version(
|
|||
msg += f". {extra_message}"
|
||||
raise Invalid(msg)
|
||||
required = rp2040_arduino
|
||||
elif CORE.is_host and framework == "host":
|
||||
if host is None:
|
||||
msg = "This feature is incompatible with host platform"
|
||||
if extra_message:
|
||||
msg += f". {extra_message}"
|
||||
raise Invalid(msg)
|
||||
required = host
|
||||
else:
|
||||
raise Invalid(
|
||||
f"""
|
||||
|
|
12
tests/components/apds9306/common.yaml
Normal file
12
tests/components/apds9306/common.yaml
Normal file
|
@ -0,0 +1,12 @@
|
|||
i2c:
|
||||
- id: i2c_apds9306
|
||||
scl: ${scl_pin}
|
||||
sda: ${sda_pin}
|
||||
|
||||
sensor:
|
||||
- platform: apds9306
|
||||
name: "APDS9306 Light Level"
|
||||
gain: 3
|
||||
bit_width: 16
|
||||
measurement_rate: 2000ms
|
||||
update_interval: 60s
|
5
tests/components/apds9306/test.esp32-ard.yaml
Normal file
5
tests/components/apds9306/test.esp32-ard.yaml
Normal file
|
@ -0,0 +1,5 @@
|
|||
substitutions:
|
||||
scl_pin: GPIO22
|
||||
sda_pin: GPIO21
|
||||
|
||||
<<: !include common.yaml
|
5
tests/components/apds9306/test.esp32-c3-ard.yaml
Normal file
5
tests/components/apds9306/test.esp32-c3-ard.yaml
Normal file
|
@ -0,0 +1,5 @@
|
|||
substitutions:
|
||||
scl_pin: GPIO5
|
||||
sda_pin: GPIO4
|
||||
|
||||
<<: !include common.yaml
|
5
tests/components/apds9306/test.esp32-c3-idf.yaml
Normal file
5
tests/components/apds9306/test.esp32-c3-idf.yaml
Normal file
|
@ -0,0 +1,5 @@
|
|||
substitutions:
|
||||
scl_pin: GPIO5
|
||||
sda_pin: GPIO4
|
||||
|
||||
<<: !include common.yaml
|
5
tests/components/apds9306/test.esp32-idf.yaml
Normal file
5
tests/components/apds9306/test.esp32-idf.yaml
Normal file
|
@ -0,0 +1,5 @@
|
|||
substitutions:
|
||||
scl_pin: GPIO22
|
||||
sda_pin: GPIO21
|
||||
|
||||
<<: !include common.yaml
|
5
tests/components/apds9306/test.esp8266-ard.yaml
Normal file
5
tests/components/apds9306/test.esp8266-ard.yaml
Normal file
|
@ -0,0 +1,5 @@
|
|||
substitutions:
|
||||
scl_pin: GPIO5
|
||||
sda_pin: GPIO4
|
||||
|
||||
<<: !include common.yaml
|
5
tests/components/apds9306/test.rp2040-ard.yaml
Normal file
5
tests/components/apds9306/test.rp2040-ard.yaml
Normal file
|
@ -0,0 +1,5 @@
|
|||
substitutions:
|
||||
scl_pin: GPIO5
|
||||
sda_pin: GPIO4
|
||||
|
||||
<<: !include common.yaml
|
|
@ -3,6 +3,13 @@ remote_transmitter:
|
|||
carrier_duty_percent: 50%
|
||||
|
||||
climate:
|
||||
- platform: heatpumpir
|
||||
protocol: mitsubishi_heavy_zm
|
||||
horizontal_default: left
|
||||
vertical_default: up
|
||||
name: HeatpumpIR Climate
|
||||
min_temperature: 18
|
||||
max_temperature: 30
|
||||
- platform: heatpumpir
|
||||
protocol: daikin
|
||||
horizontal_default: mleft
|
||||
|
|
30
tests/components/m5stack_8angle/common.yaml
Normal file
30
tests/components/m5stack_8angle/common.yaml
Normal file
|
@ -0,0 +1,30 @@
|
|||
i2c:
|
||||
sda: 0
|
||||
scl: 1
|
||||
id: bus_external
|
||||
|
||||
m5stack_8angle:
|
||||
i2c_id: bus_external
|
||||
id: m5stack_8angle_base
|
||||
|
||||
light:
|
||||
- platform: m5stack_8angle
|
||||
m5stack_8angle_id: m5stack_8angle_base
|
||||
id: m8_angle_leds
|
||||
name: Lights
|
||||
effects:
|
||||
- addressable_scan:
|
||||
|
||||
sensor:
|
||||
- platform: m5stack_8angle
|
||||
m5stack_8angle_id: m5stack_8angle_base
|
||||
channel: 1
|
||||
name: Knob 1
|
||||
- platform: m5stack_8angle
|
||||
m5stack_8angle_id: m5stack_8angle_base
|
||||
channel: 2
|
||||
name: Knob 2
|
||||
binary_sensor:
|
||||
- platform: m5stack_8angle
|
||||
m5stack_8angle_id: m5stack_8angle_base
|
||||
name: Switch
|
1
tests/components/m5stack_8angle/test.esp32-ard.yaml
Normal file
1
tests/components/m5stack_8angle/test.esp32-ard.yaml
Normal file
|
@ -0,0 +1 @@
|
|||
<<: !include common.yaml
|
1
tests/components/m5stack_8angle/test.esp32-c3-ard.yaml
Normal file
1
tests/components/m5stack_8angle/test.esp32-c3-ard.yaml
Normal file
|
@ -0,0 +1 @@
|
|||
<<: !include common.yaml
|
1
tests/components/m5stack_8angle/test.esp32-c3-idf.yaml
Normal file
1
tests/components/m5stack_8angle/test.esp32-c3-idf.yaml
Normal file
|
@ -0,0 +1 @@
|
|||
<<: !include common.yaml
|
1
tests/components/m5stack_8angle/test.esp32-idf.yaml
Normal file
1
tests/components/m5stack_8angle/test.esp32-idf.yaml
Normal file
|
@ -0,0 +1 @@
|
|||
<<: !include common.yaml
|
1
tests/components/m5stack_8angle/test.esp8266-ard.yaml
Normal file
1
tests/components/m5stack_8angle/test.esp8266-ard.yaml
Normal file
|
@ -0,0 +1 @@
|
|||
<<: !include common.yaml
|
1
tests/components/m5stack_8angle/test.rp2040-ard.yaml
Normal file
1
tests/components/m5stack_8angle/test.rp2040-ard.yaml
Normal file
|
@ -0,0 +1 @@
|
|||
<<: !include common.yaml
|
Loading…
Reference in a new issue