1
0
mirror of https://github.com/esphome/esphome.git synced 2025-11-01 23:51:47 +00:00

Compare commits

...

32 Commits

Author SHA1 Message Date
Jesse Hills
feee075122 Merge pull request #5077 from esphome/bump-2023.6.5
2023.6.5
2023-07-10 13:53:25 +12:00
Jesse Hills
a77cf1beec Bump version to 2023.6.5 2023-07-10 11:24:49 +12:00
Kevin P. Fleming
d7bfdd0efc binary_sensor: Validate max_length for on_click/on_double_click (#5068) 2023-07-10 11:24:49 +12:00
J. Nick Koston
62aee36f82 Fix bulk and single Bluetooth parser coexistence (#5073) 2023-07-10 11:24:49 +12:00
Jesse Hills
0709367587 Merge pull request #5047 from esphome/bump-2023.6.4
2023.6.4
2023-07-04 14:42:36 +12:00
Jesse Hills
98277f6ceb Bump version to 2023.6.4 2023-07-04 14:03:58 +12:00
Jesse Hills
8dd509ba53 Update webserver to ea86d81 (#5023) 2023-07-04 14:03:57 +12:00
J. Nick Koston
8df455f55b Advertise noise is enabled (#5034) 2023-07-04 14:03:57 +12:00
Graham Brown
36782f13bf Add alarm to reserved ids (#5042) 2023-07-04 14:03:57 +12:00
Sergey Dudanov
e823067a6b fix template binary_sensor publish_initial_state option (#5033) 2023-07-04 14:03:57 +12:00
Ryan DeShone
c3ef12d580 [SCD30] Disable negative temperature offset (#4850) 2023-07-04 14:03:57 +12:00
Jesse Hills
321155eb40 Merge pull request #5020 from esphome/bump-2023.6.3
2023.6.3
2023-06-29 07:26:05 +12:00
Jesse Hills
d34c074b92 Bump version to 2023.6.3 2023-06-28 12:35:16 +12:00
Jesse Hills
abc8e903c1 Add CONFIG_BT_BLE_42_FEATURES_SUPPORTED for ble (#5008) 2023-06-28 12:35:15 +12:00
F.D.Castel
832ba38f1b Fixes compressed downloads (#5014)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2023-06-28 12:35:15 +12:00
esphomebot
70de2f5278 Synchronise Device Classes from Home Assistant (#5018) 2023-06-28 12:35:15 +12:00
Jesse Hills
604d4eec79 Update webserver to 56d73b5 (#5007) 2023-06-28 12:35:14 +12:00
Jesse Hills
b806eb6a61 Merge pull request #4997 from esphome/bump-2023.6.2
2023.6.2
2023-06-23 22:47:41 +12:00
Jesse Hills
39948db59a Bump version to 2023.6.2 2023-06-23 17:17:43 +12:00
Jesse Hills
fbfb4e2a73 Fix rp2040 pio tool download (#4994) 2023-06-23 17:17:43 +12:00
Samuel Sieb
595ac84779 remove unused static declarations (#4993) 2023-06-23 17:17:43 +12:00
Jesse Hills
746f72a279 Merge pull request #4992 from esphome/bump-2023.6.1
2023.6.1
2023-06-23 08:50:39 +12:00
Jesse Hills
dec6f04499 Bump version to 2023.6.1 2023-06-23 07:34:55 +12:00
Kamil Trzciński
a90d266017 display: fix white screen on binary displays (#4991) 2023-06-23 07:34:54 +12:00
Jimmy Hedman
df9fcf9850 Make ethernet_info work with esp-idf framework (#4976) 2023-06-23 07:34:54 +12:00
Jesse Hills
5ef9cd5f86 Merge pull request #4990 from esphome/bump-2023.6.0
2023.6.0
2023-06-22 17:15:13 +12:00
Jesse Hills
ceca91d1e7 Bump version to 2023.6.0 2023-06-22 13:39:10 +12:00
Jesse Hills
bc7c11be96 Merge pull request #4989 from esphome/bump-2023.6.0b7
2023.6.0b7
2023-06-22 13:31:03 +12:00
Jesse Hills
a2734330e1 Bump version to 2023.6.0b7 2023-06-22 11:50:46 +12:00
F.D.Castel
ef8180c8a8 dashboard: Adds "compressed=1" to /download.bin endpoint. (...) (#4966)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2023-06-22 11:50:45 +12:00
J. Nick Koston
cd773a1dec Migrate VOC sensors that use ppb to use volatile_organic_compounds_parts device class (#4982) 2023-06-22 11:50:45 +12:00
Kevin P. Fleming
244a212592 airthings_wave: refactor to eliminate code duplication (#4910) 2023-06-22 11:50:45 +12:00
40 changed files with 1177 additions and 1056 deletions

View File

@@ -17,6 +17,7 @@ esphome/components/adc/* @esphome/core
esphome/components/adc128s102/* @DeerMaximum
esphome/components/addressable_light/* @justfalter
esphome/components/airthings_ble/* @jeromelaban
esphome/components/airthings_wave_base/* @jeromelaban @ncareau
esphome/components/airthings_wave_mini/* @ncareau
esphome/components/airthings_wave_plus/* @jeromelaban
esphome/components/alarm_control_panel/* @grahambrown11

View File

@@ -0,0 +1,83 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor, ble_client
from esphome.const import (
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_PRESSURE,
STATE_CLASS_MEASUREMENT,
UNIT_PERCENT,
UNIT_CELSIUS,
UNIT_HECTOPASCAL,
CONF_HUMIDITY,
CONF_TVOC,
CONF_PRESSURE,
CONF_TEMPERATURE,
DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS,
UNIT_PARTS_PER_BILLION,
ICON_RADIATOR,
)
CODEOWNERS = ["@ncareau", "@jeromelaban"]
DEPENDENCIES = ["ble_client"]
airthings_wave_base_ns = cg.esphome_ns.namespace("airthings_wave_base")
AirthingsWaveBase = airthings_wave_base_ns.class_(
"AirthingsWaveBase", cg.PollingComponent, ble_client.BLEClientNode
)
BASE_SCHEMA = (
sensor.SENSOR_SCHEMA.extend(
{
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
unit_of_measurement=UNIT_PERCENT,
device_class=DEVICE_CLASS_HUMIDITY,
state_class=STATE_CLASS_MEASUREMENT,
accuracy_decimals=0,
),
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=2,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
unit_of_measurement=UNIT_HECTOPASCAL,
accuracy_decimals=1,
device_class=DEVICE_CLASS_PRESSURE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_TVOC): sensor.sensor_schema(
unit_of_measurement=UNIT_PARTS_PER_BILLION,
icon=ICON_RADIATOR,
accuracy_decimals=0,
device_class=DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS,
state_class=STATE_CLASS_MEASUREMENT,
),
}
)
.extend(cv.polling_component_schema("5min"))
.extend(ble_client.BLE_CLIENT_SCHEMA)
)
async def wave_base_to_code(var, config):
await cg.register_component(var, config)
await ble_client.register_ble_node(var, config)
if CONF_HUMIDITY in config:
sens = await sensor.new_sensor(config[CONF_HUMIDITY])
cg.add(var.set_humidity(sens))
if CONF_TEMPERATURE in config:
sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
cg.add(var.set_temperature(sens))
if CONF_PRESSURE in config:
sens = await sensor.new_sensor(config[CONF_PRESSURE])
cg.add(var.set_pressure(sens))
if CONF_TVOC in config:
sens = await sensor.new_sensor(config[CONF_TVOC])
cg.add(var.set_tvoc(sens))

View File

@@ -0,0 +1,83 @@
#include "airthings_wave_base.h"
#ifdef USE_ESP32
namespace esphome {
namespace airthings_wave_base {
static const char *const TAG = "airthings_wave_base";
void AirthingsWaveBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) {
switch (event) {
case ESP_GATTC_OPEN_EVT: {
if (param->open.status == ESP_GATT_OK) {
ESP_LOGI(TAG, "Connected successfully!");
}
break;
}
case ESP_GATTC_DISCONNECT_EVT: {
ESP_LOGW(TAG, "Disconnected!");
break;
}
case ESP_GATTC_SEARCH_CMPL_EVT: {
this->handle_ = 0;
auto *chr = this->parent()->get_characteristic(this->service_uuid_, this->sensors_data_characteristic_uuid_);
if (chr == nullptr) {
ESP_LOGW(TAG, "No sensor characteristic found at service %s char %s", this->service_uuid_.to_string().c_str(),
this->sensors_data_characteristic_uuid_.to_string().c_str());
break;
}
this->handle_ = chr->handle;
this->node_state = esp32_ble_tracker::ClientState::ESTABLISHED;
this->request_read_values_();
break;
}
case ESP_GATTC_READ_CHAR_EVT: {
if (param->read.conn_id != this->parent()->get_conn_id())
break;
if (param->read.status != ESP_GATT_OK) {
ESP_LOGW(TAG, "Error reading char at handle %d, status=%d", param->read.handle, param->read.status);
break;
}
if (param->read.handle == this->handle_) {
this->read_sensors(param->read.value, param->read.value_len);
}
break;
}
default:
break;
}
}
bool AirthingsWaveBase::is_valid_voc_value_(uint16_t voc) { return 0 <= voc && voc <= 16383; }
void AirthingsWaveBase::update() {
if (this->node_state != esp32_ble_tracker::ClientState::ESTABLISHED) {
if (!this->parent()->enabled) {
ESP_LOGW(TAG, "Reconnecting to device");
this->parent()->set_enabled(true);
this->parent()->connect();
} else {
ESP_LOGW(TAG, "Connection in progress");
}
}
}
void AirthingsWaveBase::request_read_values_() {
auto status = esp_ble_gattc_read_char(this->parent()->get_gattc_if(), this->parent()->get_conn_id(), this->handle_,
ESP_GATT_AUTH_REQ_NONE);
if (status) {
ESP_LOGW(TAG, "Error sending read request for sensor, status=%d", status);
}
}
} // namespace airthings_wave_base
} // namespace esphome
#endif // USE_ESP32

View File

@@ -0,0 +1,50 @@
#pragma once
#ifdef USE_ESP32
#include <esp_gattc_api.h>
#include <algorithm>
#include <iterator>
#include "esphome/components/ble_client/ble_client.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/core/component.h"
#include "esphome/core/log.h"
namespace esphome {
namespace airthings_wave_base {
class AirthingsWaveBase : public PollingComponent, public ble_client::BLEClientNode {
public:
AirthingsWaveBase() = default;
void update() override;
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) override;
void set_temperature(sensor::Sensor *temperature) { temperature_sensor_ = temperature; }
void set_humidity(sensor::Sensor *humidity) { humidity_sensor_ = humidity; }
void set_pressure(sensor::Sensor *pressure) { pressure_sensor_ = pressure; }
void set_tvoc(sensor::Sensor *tvoc) { tvoc_sensor_ = tvoc; }
protected:
bool is_valid_voc_value_(uint16_t voc);
virtual void read_sensors(uint8_t *value, uint16_t value_len) = 0;
void request_read_values_();
sensor::Sensor *temperature_sensor_{nullptr};
sensor::Sensor *humidity_sensor_{nullptr};
sensor::Sensor *pressure_sensor_{nullptr};
sensor::Sensor *tvoc_sensor_{nullptr};
uint16_t handle_;
esp32_ble_tracker::ESPBTUUID service_uuid_;
esp32_ble_tracker::ESPBTUUID sensors_data_characteristic_uuid_;
};
} // namespace airthings_wave_base
} // namespace esphome
#endif // USE_ESP32

View File

@@ -7,105 +7,47 @@ namespace airthings_wave_mini {
static const char *const TAG = "airthings_wave_mini";
void AirthingsWaveMini::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) {
switch (event) {
case ESP_GATTC_OPEN_EVT: {
if (param->open.status == ESP_GATT_OK) {
ESP_LOGI(TAG, "Connected successfully!");
}
break;
}
case ESP_GATTC_DISCONNECT_EVT: {
ESP_LOGW(TAG, "Disconnected!");
break;
}
case ESP_GATTC_SEARCH_CMPL_EVT: {
this->handle_ = 0;
auto *chr = this->parent()->get_characteristic(service_uuid_, sensors_data_characteristic_uuid_);
if (chr == nullptr) {
ESP_LOGW(TAG, "No sensor characteristic found at service %s char %s", service_uuid_.to_string().c_str(),
sensors_data_characteristic_uuid_.to_string().c_str());
break;
}
this->handle_ = chr->handle;
this->node_state = esp32_ble_tracker::ClientState::ESTABLISHED;
request_read_values_();
break;
}
case ESP_GATTC_READ_CHAR_EVT: {
if (param->read.conn_id != this->parent()->get_conn_id())
break;
if (param->read.status != ESP_GATT_OK) {
ESP_LOGW(TAG, "Error reading char at handle %d, status=%d", param->read.handle, param->read.status);
break;
}
if (param->read.handle == this->handle_) {
read_sensors_(param->read.value, param->read.value_len);
}
break;
}
default:
break;
}
}
void AirthingsWaveMini::read_sensors_(uint8_t *raw_value, uint16_t value_len) {
void AirthingsWaveMini::read_sensors(uint8_t *raw_value, uint16_t value_len) {
auto *value = (WaveMiniReadings *) raw_value;
if (sizeof(WaveMiniReadings) <= value_len) {
this->humidity_sensor_->publish_state(value->humidity / 100.0f);
this->pressure_sensor_->publish_state(value->pressure / 50.0f);
this->temperature_sensor_->publish_state(value->temperature / 100.0f - 273.15f);
if (is_valid_voc_value_(value->voc)) {
if (this->humidity_sensor_ != nullptr) {
this->humidity_sensor_->publish_state(value->humidity / 100.0f);
}
if (this->pressure_sensor_ != nullptr) {
this->pressure_sensor_->publish_state(value->pressure / 50.0f);
}
if (this->temperature_sensor_ != nullptr) {
this->temperature_sensor_->publish_state(value->temperature / 100.0f - 273.15f);
}
if ((this->tvoc_sensor_ != nullptr) && this->is_valid_voc_value_(value->voc)) {
this->tvoc_sensor_->publish_state(value->voc);
}
// This instance must not stay connected
// so other clients can connect to it (e.g. the
// mobile app).
parent()->set_enabled(false);
}
}
bool AirthingsWaveMini::is_valid_voc_value_(uint16_t voc) { return 0 <= voc && voc <= 16383; }
void AirthingsWaveMini::update() {
if (this->node_state != esp32_ble_tracker::ClientState::ESTABLISHED) {
if (!parent()->enabled) {
ESP_LOGW(TAG, "Reconnecting to device");
parent()->set_enabled(true);
parent()->connect();
} else {
ESP_LOGW(TAG, "Connection in progress");
}
}
}
void AirthingsWaveMini::request_read_values_() {
auto status = esp_ble_gattc_read_char(this->parent()->get_gattc_if(), this->parent()->get_conn_id(), this->handle_,
ESP_GATT_AUTH_REQ_NONE);
if (status) {
ESP_LOGW(TAG, "Error sending read request for sensor, status=%d", status);
this->parent()->set_enabled(false);
}
}
void AirthingsWaveMini::dump_config() {
// these really don't belong here, but there doesn't seem to be a
// practical way to have the base class use LOG_SENSOR and include
// the TAG from this component
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
LOG_SENSOR(" ", "Pressure", this->pressure_sensor_);
LOG_SENSOR(" ", "TVOC", this->tvoc_sensor_);
}
AirthingsWaveMini::AirthingsWaveMini()
: PollingComponent(10000),
service_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(SERVICE_UUID)),
sensors_data_characteristic_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(CHARACTERISTIC_UUID)) {}
AirthingsWaveMini::AirthingsWaveMini() {
this->service_uuid_ = esp32_ble_tracker::ESPBTUUID::from_raw(SERVICE_UUID);
this->sensors_data_characteristic_uuid_ = esp32_ble_tracker::ESPBTUUID::from_raw(CHARACTERISTIC_UUID);
}
} // namespace airthings_wave_mini
} // namespace esphome

View File

@@ -2,14 +2,7 @@
#ifdef USE_ESP32
#include <esp_gattc_api.h>
#include <algorithm>
#include <iterator>
#include "esphome/components/ble_client/ble_client.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/core/component.h"
#include "esphome/core/log.h"
#include "esphome/components/airthings_wave_base/airthings_wave_base.h"
namespace esphome {
namespace airthings_wave_mini {
@@ -17,35 +10,14 @@ namespace airthings_wave_mini {
static const char *const SERVICE_UUID = "b42e3882-ade7-11e4-89d3-123b93f75cba";
static const char *const CHARACTERISTIC_UUID = "b42e3b98-ade7-11e4-89d3-123b93f75cba";
class AirthingsWaveMini : public PollingComponent, public ble_client::BLEClientNode {
class AirthingsWaveMini : public airthings_wave_base::AirthingsWaveBase {
public:
AirthingsWaveMini();
void dump_config() override;
void update() override;
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) override;
void set_temperature(sensor::Sensor *temperature) { temperature_sensor_ = temperature; }
void set_humidity(sensor::Sensor *humidity) { humidity_sensor_ = humidity; }
void set_pressure(sensor::Sensor *pressure) { pressure_sensor_ = pressure; }
void set_tvoc(sensor::Sensor *tvoc) { tvoc_sensor_ = tvoc; }
protected:
bool is_valid_voc_value_(uint16_t voc);
void read_sensors_(uint8_t *value, uint16_t value_len);
void request_read_values_();
sensor::Sensor *temperature_sensor_{nullptr};
sensor::Sensor *humidity_sensor_{nullptr};
sensor::Sensor *pressure_sensor_{nullptr};
sensor::Sensor *tvoc_sensor_{nullptr};
uint16_t handle_;
esp32_ble_tracker::ESPBTUUID service_uuid_;
esp32_ble_tracker::ESPBTUUID sensors_data_characteristic_uuid_;
void read_sensors(uint8_t *value, uint16_t value_len) override;
struct WaveMiniReadings {
uint16_t unused01;

View File

@@ -1,82 +1,28 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor, ble_client
from esphome.components import airthings_wave_base
from esphome.const import (
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_PRESSURE,
STATE_CLASS_MEASUREMENT,
UNIT_PERCENT,
UNIT_CELSIUS,
UNIT_HECTOPASCAL,
CONF_ID,
CONF_HUMIDITY,
CONF_TVOC,
CONF_PRESSURE,
CONF_TEMPERATURE,
UNIT_PARTS_PER_BILLION,
ICON_RADIATOR,
)
DEPENDENCIES = ["ble_client"]
DEPENDENCIES = airthings_wave_base.DEPENDENCIES
AUTO_LOAD = ["airthings_wave_base"]
airthings_wave_mini_ns = cg.esphome_ns.namespace("airthings_wave_mini")
AirthingsWaveMini = airthings_wave_mini_ns.class_(
"AirthingsWaveMini", cg.PollingComponent, ble_client.BLEClientNode
"AirthingsWaveMini", airthings_wave_base.AirthingsWaveBase
)
CONFIG_SCHEMA = cv.All(
cv.Schema(
{
cv.GenerateID(): cv.declare_id(AirthingsWaveMini),
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
unit_of_measurement=UNIT_PERCENT,
device_class=DEVICE_CLASS_HUMIDITY,
state_class=STATE_CLASS_MEASUREMENT,
accuracy_decimals=2,
),
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=2,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
unit_of_measurement=UNIT_HECTOPASCAL,
accuracy_decimals=2,
device_class=DEVICE_CLASS_PRESSURE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_TVOC): sensor.sensor_schema(
unit_of_measurement=UNIT_PARTS_PER_BILLION,
icon=ICON_RADIATOR,
accuracy_decimals=0,
state_class=STATE_CLASS_MEASUREMENT,
),
}
)
.extend(cv.polling_component_schema("5min"))
.extend(ble_client.BLE_CLIENT_SCHEMA),
CONFIG_SCHEMA = airthings_wave_base.BASE_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(AirthingsWaveMini),
}
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await ble_client.register_ble_node(var, config)
if CONF_HUMIDITY in config:
sens = await sensor.new_sensor(config[CONF_HUMIDITY])
cg.add(var.set_humidity(sens))
if CONF_TEMPERATURE in config:
sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
cg.add(var.set_temperature(sens))
if CONF_PRESSURE in config:
sens = await sensor.new_sensor(config[CONF_PRESSURE])
cg.add(var.set_pressure(sens))
if CONF_TVOC in config:
sens = await sensor.new_sensor(config[CONF_TVOC])
cg.add(var.set_tvoc(sens))
await airthings_wave_base.wave_base_to_code(var, config)

View File

@@ -7,55 +7,7 @@ namespace airthings_wave_plus {
static const char *const TAG = "airthings_wave_plus";
void AirthingsWavePlus::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) {
switch (event) {
case ESP_GATTC_OPEN_EVT: {
if (param->open.status == ESP_GATT_OK) {
ESP_LOGI(TAG, "Connected successfully!");
}
break;
}
case ESP_GATTC_DISCONNECT_EVT: {
ESP_LOGW(TAG, "Disconnected!");
break;
}
case ESP_GATTC_SEARCH_CMPL_EVT: {
this->handle_ = 0;
auto *chr = this->parent()->get_characteristic(service_uuid_, sensors_data_characteristic_uuid_);
if (chr == nullptr) {
ESP_LOGW(TAG, "No sensor characteristic found at service %s char %s", service_uuid_.to_string().c_str(),
sensors_data_characteristic_uuid_.to_string().c_str());
break;
}
this->handle_ = chr->handle;
this->node_state = esp32_ble_tracker::ClientState::ESTABLISHED;
request_read_values_();
break;
}
case ESP_GATTC_READ_CHAR_EVT: {
if (param->read.conn_id != this->parent()->get_conn_id())
break;
if (param->read.status != ESP_GATT_OK) {
ESP_LOGW(TAG, "Error reading char at handle %d, status=%d", param->read.handle, param->read.status);
break;
}
if (param->read.handle == this->handle_) {
read_sensors_(param->read.value, param->read.value_len);
}
break;
}
default:
break;
}
}
void AirthingsWavePlus::read_sensors_(uint8_t *raw_value, uint16_t value_len) {
void AirthingsWavePlus::read_sensors(uint8_t *raw_value, uint16_t value_len) {
auto *value = (WavePlusReadings *) raw_value;
if (sizeof(WavePlusReadings) <= value_len) {
@@ -64,26 +16,38 @@ void AirthingsWavePlus::read_sensors_(uint8_t *raw_value, uint16_t value_len) {
if (value->version == 1) {
ESP_LOGD(TAG, "ambient light = %d", value->ambientLight);
this->humidity_sensor_->publish_state(value->humidity / 2.0f);
if (is_valid_radon_value_(value->radon)) {
if (this->humidity_sensor_ != nullptr) {
this->humidity_sensor_->publish_state(value->humidity / 2.0f);
}
if ((this->radon_sensor_ != nullptr) && this->is_valid_radon_value_(value->radon)) {
this->radon_sensor_->publish_state(value->radon);
}
if (is_valid_radon_value_(value->radon_lt)) {
if ((this->radon_long_term_sensor_ != nullptr) && this->is_valid_radon_value_(value->radon_lt)) {
this->radon_long_term_sensor_->publish_state(value->radon_lt);
}
this->temperature_sensor_->publish_state(value->temperature / 100.0f);
this->pressure_sensor_->publish_state(value->pressure / 50.0f);
if (is_valid_co2_value_(value->co2)) {
if (this->temperature_sensor_ != nullptr) {
this->temperature_sensor_->publish_state(value->temperature / 100.0f);
}
if (this->pressure_sensor_ != nullptr) {
this->pressure_sensor_->publish_state(value->pressure / 50.0f);
}
if ((this->co2_sensor_ != nullptr) && this->is_valid_co2_value_(value->co2)) {
this->co2_sensor_->publish_state(value->co2);
}
if (is_valid_voc_value_(value->voc)) {
if ((this->tvoc_sensor_ != nullptr) && this->is_valid_voc_value_(value->voc)) {
this->tvoc_sensor_->publish_state(value->voc);
}
// This instance must not stay connected
// so other clients can connect to it (e.g. the
// mobile app).
parent()->set_enabled(false);
this->parent()->set_enabled(false);
} else {
ESP_LOGE(TAG, "Invalid payload version (%d != 1, newer version or not a Wave Plus?)", value->version);
}
@@ -92,44 +56,26 @@ void AirthingsWavePlus::read_sensors_(uint8_t *raw_value, uint16_t value_len) {
bool AirthingsWavePlus::is_valid_radon_value_(uint16_t radon) { return 0 <= radon && radon <= 16383; }
bool AirthingsWavePlus::is_valid_voc_value_(uint16_t voc) { return 0 <= voc && voc <= 16383; }
bool AirthingsWavePlus::is_valid_co2_value_(uint16_t co2) { return 0 <= co2 && co2 <= 16383; }
void AirthingsWavePlus::update() {
if (this->node_state != esp32_ble_tracker::ClientState::ESTABLISHED) {
if (!parent()->enabled) {
ESP_LOGW(TAG, "Reconnecting to device");
parent()->set_enabled(true);
parent()->connect();
} else {
ESP_LOGW(TAG, "Connection in progress");
}
}
}
void AirthingsWavePlus::request_read_values_() {
auto status = esp_ble_gattc_read_char(this->parent()->get_gattc_if(), this->parent()->get_conn_id(), this->handle_,
ESP_GATT_AUTH_REQ_NONE);
if (status) {
ESP_LOGW(TAG, "Error sending read request for sensor, status=%d", status);
}
}
void AirthingsWavePlus::dump_config() {
// these really don't belong here, but there doesn't seem to be a
// practical way to have the base class use LOG_SENSOR and include
// the TAG from this component
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
LOG_SENSOR(" ", "Radon", this->radon_sensor_);
LOG_SENSOR(" ", "Radon Long Term", this->radon_long_term_sensor_);
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
LOG_SENSOR(" ", "Pressure", this->pressure_sensor_);
LOG_SENSOR(" ", "CO2", this->co2_sensor_);
LOG_SENSOR(" ", "TVOC", this->tvoc_sensor_);
LOG_SENSOR(" ", "Radon", this->radon_sensor_);
LOG_SENSOR(" ", "Radon Long Term", this->radon_long_term_sensor_);
LOG_SENSOR(" ", "CO2", this->co2_sensor_);
}
AirthingsWavePlus::AirthingsWavePlus()
: PollingComponent(10000),
service_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(SERVICE_UUID)),
sensors_data_characteristic_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(CHARACTERISTIC_UUID)) {}
AirthingsWavePlus::AirthingsWavePlus() {
this->service_uuid_ = esp32_ble_tracker::ESPBTUUID::from_raw(SERVICE_UUID);
this->sensors_data_characteristic_uuid_ = esp32_ble_tracker::ESPBTUUID::from_raw(CHARACTERISTIC_UUID);
}
} // namespace airthings_wave_plus
} // namespace esphome

View File

@@ -2,14 +2,7 @@
#ifdef USE_ESP32
#include <esp_gattc_api.h>
#include <algorithm>
#include <iterator>
#include "esphome/components/ble_client/ble_client.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/core/component.h"
#include "esphome/core/log.h"
#include "esphome/components/airthings_wave_base/airthings_wave_base.h"
namespace esphome {
namespace airthings_wave_plus {
@@ -17,43 +10,25 @@ namespace airthings_wave_plus {
static const char *const SERVICE_UUID = "b42e1c08-ade7-11e4-89d3-123b93f75cba";
static const char *const CHARACTERISTIC_UUID = "b42e2a68-ade7-11e4-89d3-123b93f75cba";
class AirthingsWavePlus : public PollingComponent, public ble_client::BLEClientNode {
class AirthingsWavePlus : public airthings_wave_base::AirthingsWaveBase {
public:
AirthingsWavePlus();
void dump_config() override;
void update() override;
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) override;
void set_temperature(sensor::Sensor *temperature) { temperature_sensor_ = temperature; }
void set_radon(sensor::Sensor *radon) { radon_sensor_ = radon; }
void set_radon_long_term(sensor::Sensor *radon_long_term) { radon_long_term_sensor_ = radon_long_term; }
void set_humidity(sensor::Sensor *humidity) { humidity_sensor_ = humidity; }
void set_pressure(sensor::Sensor *pressure) { pressure_sensor_ = pressure; }
void set_co2(sensor::Sensor *co2) { co2_sensor_ = co2; }
void set_tvoc(sensor::Sensor *tvoc) { tvoc_sensor_ = tvoc; }
protected:
bool is_valid_radon_value_(uint16_t radon);
bool is_valid_voc_value_(uint16_t voc);
bool is_valid_co2_value_(uint16_t co2);
void read_sensors_(uint8_t *value, uint16_t value_len);
void request_read_values_();
void read_sensors(uint8_t *value, uint16_t value_len) override;
sensor::Sensor *temperature_sensor_{nullptr};
sensor::Sensor *radon_sensor_{nullptr};
sensor::Sensor *radon_long_term_sensor_{nullptr};
sensor::Sensor *humidity_sensor_{nullptr};
sensor::Sensor *pressure_sensor_{nullptr};
sensor::Sensor *co2_sensor_{nullptr};
sensor::Sensor *tvoc_sensor_{nullptr};
uint16_t handle_;
esp32_ble_tracker::ESPBTUUID service_uuid_;
esp32_ble_tracker::ESPBTUUID sensors_data_characteristic_uuid_;
struct WavePlusReadings {
uint8_t version;

View File

@@ -1,116 +1,64 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor, ble_client
from esphome.components import sensor, airthings_wave_base
from esphome.const import (
DEVICE_CLASS_CARBON_DIOXIDE,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_PRESSURE,
STATE_CLASS_MEASUREMENT,
UNIT_PERCENT,
UNIT_CELSIUS,
UNIT_HECTOPASCAL,
ICON_RADIOACTIVE,
CONF_ID,
CONF_RADON,
CONF_RADON_LONG_TERM,
CONF_HUMIDITY,
CONF_TVOC,
CONF_CO2,
CONF_PRESSURE,
CONF_TEMPERATURE,
UNIT_BECQUEREL_PER_CUBIC_METER,
UNIT_PARTS_PER_MILLION,
UNIT_PARTS_PER_BILLION,
ICON_RADIATOR,
)
DEPENDENCIES = ["ble_client"]
DEPENDENCIES = airthings_wave_base.DEPENDENCIES
AUTO_LOAD = ["airthings_wave_base"]
airthings_wave_plus_ns = cg.esphome_ns.namespace("airthings_wave_plus")
AirthingsWavePlus = airthings_wave_plus_ns.class_(
"AirthingsWavePlus", cg.PollingComponent, ble_client.BLEClientNode
"AirthingsWavePlus", airthings_wave_base.AirthingsWaveBase
)
CONFIG_SCHEMA = cv.All(
cv.Schema(
{
cv.GenerateID(): cv.declare_id(AirthingsWavePlus),
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
unit_of_measurement=UNIT_PERCENT,
device_class=DEVICE_CLASS_HUMIDITY,
state_class=STATE_CLASS_MEASUREMENT,
accuracy_decimals=0,
),
cv.Optional(CONF_RADON): sensor.sensor_schema(
unit_of_measurement=UNIT_BECQUEREL_PER_CUBIC_METER,
icon=ICON_RADIOACTIVE,
accuracy_decimals=0,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_RADON_LONG_TERM): sensor.sensor_schema(
unit_of_measurement=UNIT_BECQUEREL_PER_CUBIC_METER,
icon=ICON_RADIOACTIVE,
accuracy_decimals=0,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=2,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
unit_of_measurement=UNIT_HECTOPASCAL,
accuracy_decimals=1,
device_class=DEVICE_CLASS_PRESSURE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_CO2): sensor.sensor_schema(
unit_of_measurement=UNIT_PARTS_PER_MILLION,
accuracy_decimals=0,
device_class=DEVICE_CLASS_CARBON_DIOXIDE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_TVOC): sensor.sensor_schema(
unit_of_measurement=UNIT_PARTS_PER_BILLION,
icon=ICON_RADIATOR,
accuracy_decimals=0,
state_class=STATE_CLASS_MEASUREMENT,
),
}
)
.extend(cv.polling_component_schema("5min"))
.extend(ble_client.BLE_CLIENT_SCHEMA),
CONFIG_SCHEMA = airthings_wave_base.BASE_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(AirthingsWavePlus),
cv.Optional(CONF_RADON): sensor.sensor_schema(
unit_of_measurement=UNIT_BECQUEREL_PER_CUBIC_METER,
icon=ICON_RADIOACTIVE,
accuracy_decimals=0,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_RADON_LONG_TERM): sensor.sensor_schema(
unit_of_measurement=UNIT_BECQUEREL_PER_CUBIC_METER,
icon=ICON_RADIOACTIVE,
accuracy_decimals=0,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_CO2): sensor.sensor_schema(
unit_of_measurement=UNIT_PARTS_PER_MILLION,
accuracy_decimals=0,
device_class=DEVICE_CLASS_CARBON_DIOXIDE,
state_class=STATE_CLASS_MEASUREMENT,
),
}
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await airthings_wave_base.wave_base_to_code(var, config)
await ble_client.register_ble_node(var, config)
if CONF_HUMIDITY in config:
sens = await sensor.new_sensor(config[CONF_HUMIDITY])
cg.add(var.set_humidity(sens))
if CONF_RADON in config:
sens = await sensor.new_sensor(config[CONF_RADON])
cg.add(var.set_radon(sens))
if CONF_RADON_LONG_TERM in config:
sens = await sensor.new_sensor(config[CONF_RADON_LONG_TERM])
cg.add(var.set_radon_long_term(sens))
if CONF_TEMPERATURE in config:
sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
cg.add(var.set_temperature(sens))
if CONF_PRESSURE in config:
sens = await sensor.new_sensor(config[CONF_PRESSURE])
cg.add(var.set_pressure(sens))
if CONF_CO2 in config:
sens = await sensor.new_sensor(config[CONF_CO2])
cg.add(var.set_co2(sens))
if CONF_TVOC in config:
sens = await sensor.new_sensor(config[CONF_TVOC])
cg.add(var.set_tvoc(sens))

View File

@@ -323,6 +323,18 @@ def validate_multi_click_timing(value):
validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
def validate_click_timing(value):
for v in value:
min_length = v.get(CONF_MIN_LENGTH)
max_length = v.get(CONF_MAX_LENGTH)
if max_length < min_length:
raise cv.Invalid(
f"Max length ({max_length}) must be larger than min length ({min_length})."
)
return value
BINARY_SENSOR_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
{
cv.GenerateID(): cv.declare_id(BinarySensor),
@@ -342,27 +354,33 @@ BINARY_SENSOR_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).ex
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ReleaseTrigger),
}
),
cv.Optional(CONF_ON_CLICK): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ClickTrigger),
cv.Optional(
CONF_MIN_LENGTH, default="50ms"
): cv.positive_time_period_milliseconds,
cv.Optional(
CONF_MAX_LENGTH, default="350ms"
): cv.positive_time_period_milliseconds,
}
cv.Optional(CONF_ON_CLICK): cv.All(
automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ClickTrigger),
cv.Optional(
CONF_MIN_LENGTH, default="50ms"
): cv.positive_time_period_milliseconds,
cv.Optional(
CONF_MAX_LENGTH, default="350ms"
): cv.positive_time_period_milliseconds,
}
),
validate_click_timing,
),
cv.Optional(CONF_ON_DOUBLE_CLICK): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DoubleClickTrigger),
cv.Optional(
CONF_MIN_LENGTH, default="50ms"
): cv.positive_time_period_milliseconds,
cv.Optional(
CONF_MAX_LENGTH, default="350ms"
): cv.positive_time_period_milliseconds,
}
cv.Optional(CONF_ON_DOUBLE_CLICK): cv.All(
automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DoubleClickTrigger),
cv.Optional(
CONF_MIN_LENGTH, default="50ms"
): cv.positive_time_period_milliseconds,
cv.Optional(
CONF_MAX_LENGTH, default="350ms"
): cv.positive_time_period_milliseconds,
}
),
validate_click_timing,
),
cv.Optional(CONF_ON_MULTI_CLICK): automation.validate_automation(
{

View File

@@ -275,6 +275,10 @@ esp_err_t BluetoothConnection::notify_characteristic(uint16_t handle, bool enabl
return ESP_OK;
}
esp32_ble_tracker::AdvertisementParserType BluetoothConnection::get_advertisement_parser_type() {
return this->proxy_->get_advertisement_parser_type();
}
} // namespace bluetooth_proxy
} // namespace esphome

View File

@@ -14,6 +14,7 @@ class BluetoothConnection : public esp32_ble_client::BLEClientBase {
bool gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) override;
void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override;
esp32_ble_tracker::AdvertisementParserType get_advertisement_parser_type() override;
esp_err_t read_characteristic(uint16_t handle);
esp_err_t write_characteristic(uint16_t handle, const std::string &data, bool response);

View File

@@ -198,6 +198,12 @@ void BluetoothProxy::loop() {
}
}
esp32_ble_tracker::AdvertisementParserType BluetoothProxy::get_advertisement_parser_type() {
if (this->raw_advertisements_)
return esp32_ble_tracker::AdvertisementParserType::RAW_ADVERTISEMENTS;
return esp32_ble_tracker::AdvertisementParserType::PARSED_ADVERTISEMENTS;
}
BluetoothConnection *BluetoothProxy::get_connection_(uint64_t address, bool reserve) {
for (auto *connection : this->connections_) {
if (connection->get_address() == address)
@@ -435,6 +441,7 @@ void BluetoothProxy::subscribe_api_connection(api::APIConnection *api_connection
}
this->api_connection_ = api_connection;
this->raw_advertisements_ = flags & BluetoothProxySubscriptionFlag::SUBSCRIPTION_RAW_ADVERTISEMENTS;
this->parent_->recalculate_advertisement_parser_types();
}
void BluetoothProxy::unsubscribe_api_connection(api::APIConnection *api_connection) {
@@ -444,6 +451,7 @@ void BluetoothProxy::unsubscribe_api_connection(api::APIConnection *api_connecti
}
this->api_connection_ = nullptr;
this->raw_advertisements_ = false;
this->parent_->recalculate_advertisement_parser_types();
}
void BluetoothProxy::send_device_connection(uint64_t address, bool connected, uint16_t mtu, esp_err_t error) {

View File

@@ -51,6 +51,7 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com
bool parse_devices(esp_ble_gap_cb_param_t::ble_scan_result_evt_param *advertisements, size_t count) override;
void dump_config() override;
void loop() override;
esp32_ble_tracker::AdvertisementParserType get_advertisement_parser_type() override;
void register_connection(BluetoothConnection *connection) {
this->connections_.push_back(connection);

View File

@@ -12,6 +12,7 @@ from esphome.const import (
CONF_TRIGGER_ID,
CONF_MQTT_ID,
DEVICE_CLASS_EMPTY,
DEVICE_CLASS_IDENTIFY,
DEVICE_CLASS_RESTART,
DEVICE_CLASS_UPDATE,
)
@@ -24,6 +25,7 @@ IS_PLATFORM_COMPONENT = True
DEVICE_CLASSES = [
DEVICE_CLASS_EMPTY,
DEVICE_CLASS_IDENTIFY,
DEVICE_CLASS_RESTART,
DEVICE_CLASS_UPDATE,
]

View File

@@ -6,7 +6,7 @@ from esphome.const import (
ICON_RADIATOR,
ICON_RESTART,
DEVICE_CLASS_CARBON_DIOXIDE,
DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS,
DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS,
STATE_CLASS_MEASUREMENT,
UNIT_PARTS_PER_MILLION,
UNIT_PARTS_PER_BILLION,
@@ -43,7 +43,7 @@ CONFIG_SCHEMA = (
unit_of_measurement=UNIT_PARTS_PER_BILLION,
icon=ICON_RADIATOR,
accuracy_decimals=0,
device_class=DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS,
device_class=DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_VERSION): text_sensor.text_sensor_schema(

View File

@@ -16,7 +16,7 @@ namespace display {
static const char *const TAG = "display";
const Color COLOR_OFF(0, 0, 0, 255);
const Color COLOR_OFF(0, 0, 0, 0);
const Color COLOR_ON(255, 255, 255, 255);
void Rect::expand(int16_t horizontal, int16_t vertical) {

View File

@@ -55,3 +55,4 @@ async def to_code(config):
if CORE.using_esp_idf:
add_idf_sdkconfig_option("CONFIG_BT_ENABLED", True)
add_idf_sdkconfig_option("CONFIG_BT_BLE_42_FEATURES_SUPPORTED", True)

View File

@@ -107,16 +107,16 @@ void ESP32BLETracker::loop() {
ESP_LOGW(TAG, "Too many BLE events to process. Some devices may not show up.");
}
bool bulk_parsed = false;
for (auto *listener : this->listeners_) {
bulk_parsed |= listener->parse_devices(this->scan_result_buffer_, this->scan_result_index_);
}
for (auto *client : this->clients_) {
bulk_parsed |= client->parse_devices(this->scan_result_buffer_, this->scan_result_index_);
if (this->raw_advertisements_) {
for (auto *listener : this->listeners_) {
listener->parse_devices(this->scan_result_buffer_, this->scan_result_index_);
}
for (auto *client : this->clients_) {
client->parse_devices(this->scan_result_buffer_, this->scan_result_index_);
}
}
if (!bulk_parsed) {
if (this->parse_advertisements_) {
for (size_t i = 0; i < index; i++) {
ESPBTDevice device;
device.parse_scan_rst(this->scan_result_buffer_[i]);
@@ -284,6 +284,32 @@ void ESP32BLETracker::end_of_scan_() {
void ESP32BLETracker::register_client(ESPBTClient *client) {
client->app_id = ++this->app_id_;
this->clients_.push_back(client);
this->recalculate_advertisement_parser_types();
}
void ESP32BLETracker::register_listener(ESPBTDeviceListener *listener) {
listener->set_parent(this);
this->listeners_.push_back(listener);
this->recalculate_advertisement_parser_types();
}
void ESP32BLETracker::recalculate_advertisement_parser_types() {
this->raw_advertisements_ = false;
this->parse_advertisements_ = false;
for (auto *listener : this->listeners_) {
if (listener->get_advertisement_parser_type() == AdvertisementParserType::PARSED_ADVERTISEMENTS) {
this->parse_advertisements_ = true;
} else {
this->raw_advertisements_ = true;
}
}
for (auto *client : this->clients_) {
if (client->get_advertisement_parser_type() == AdvertisementParserType::PARSED_ADVERTISEMENTS) {
this->parse_advertisements_ = true;
} else {
this->raw_advertisements_ = true;
}
}
}
void ESP32BLETracker::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {

View File

@@ -27,6 +27,11 @@ using namespace esp32_ble;
using adv_data_t = std::vector<uint8_t>;
enum AdvertisementParserType {
PARSED_ADVERTISEMENTS,
RAW_ADVERTISEMENTS,
};
struct ServiceData {
ESPBTUUID uuid;
adv_data_t data;
@@ -116,6 +121,9 @@ class ESPBTDeviceListener {
virtual bool parse_devices(esp_ble_gap_cb_param_t::ble_scan_result_evt_param *advertisements, size_t count) {
return false;
};
virtual AdvertisementParserType get_advertisement_parser_type() {
return AdvertisementParserType::PARSED_ADVERTISEMENTS;
};
void set_parent(ESP32BLETracker *parent) { parent_ = parent; }
protected:
@@ -184,12 +192,9 @@ class ESP32BLETracker : public Component, public GAPEventHandler, public GATTcEv
void loop() override;
void register_listener(ESPBTDeviceListener *listener) {
listener->set_parent(this);
this->listeners_.push_back(listener);
}
void register_listener(ESPBTDeviceListener *listener);
void register_client(ESPBTClient *client);
void recalculate_advertisement_parser_types();
void print_bt_device_info(const ESPBTDevice &device);
@@ -231,6 +236,8 @@ class ESP32BLETracker : public Component, public GAPEventHandler, public GATTcEv
bool scan_continuous_;
bool scan_active_;
bool scanner_idle_;
bool raw_advertisements_{false};
bool parse_advertisements_{false};
SemaphoreHandle_t scan_result_lock_;
SemaphoreHandle_t scan_end_lock_;
size_t scan_result_index_{0};

View File

@@ -1,7 +1,7 @@
#include "ethernet_info_text_sensor.h"
#include "esphome/core/log.h"
#ifdef USE_ESP32_FRAMEWORK_ARDUINO
#ifdef USE_ESP32
namespace esphome {
namespace ethernet_info {
@@ -13,4 +13,4 @@ void IPAddressEthernetInfo::dump_config() { LOG_TEXT_SENSOR("", "EthernetInfo IP
} // namespace ethernet_info
} // namespace esphome
#endif // USE_ESP32_FRAMEWORK_ARDUINO
#endif // USE_ESP32

View File

@@ -4,7 +4,7 @@
#include "esphome/components/text_sensor/text_sensor.h"
#include "esphome/components/ethernet/ethernet_component.h"
#ifdef USE_ESP32_FRAMEWORK_ARDUINO
#ifdef USE_ESP32
namespace esphome {
namespace ethernet_info {
@@ -30,4 +30,4 @@ class IPAddressEthernetInfo : public PollingComponent, public text_sensor::TextS
} // namespace ethernet_info
} // namespace esphome
#endif // USE_ESP32_FRAMEWORK_ARDUINO
#endif // USE_ESP32

View File

@@ -57,6 +57,10 @@ void MDNSComponent::compile_records_() {
service.txt_records.push_back({"network", "ethernet"});
#endif
#ifdef USE_API_NOISE
service.txt_records.push_back({"api_encryption", "Noise_NNpsk0_25519_ChaChaPoly_SHA256"});
#endif
#ifdef ESPHOME_PROJECT_NAME
service.txt_records.push_back({"project_name", ESPHOME_PROJECT_NAME});
service.txt_records.push_back({"project_version", ESPHOME_PROJECT_VERSION});

View File

@@ -16,8 +16,7 @@ from esphome.const import (
KEY_TARGET_PLATFORM,
)
from esphome.core import CORE, coroutine_with_priority, EsphomeError
from esphome.helpers import mkdir_p, write_file
import esphome.platformio_api as api
from esphome.helpers import mkdir_p, write_file, copy_file_if_changed
from .const import KEY_BOARD, KEY_PIO_FILES, KEY_RP2040, rp2040_ns
@@ -193,25 +192,20 @@ def generate_pio_files() -> bool:
pio_path = CORE.relative_build_path(f"src/pio/{key}.pio")
mkdir_p(os.path.dirname(pio_path))
write_file(pio_path, data)
_LOGGER.info("Assembling PIO assembly code")
retval = api.run_platformio_cli(
"pkg",
"exec",
"--package",
"earlephilhower/tool-pioasm-rp2040-earlephilhower",
"--",
"pioasm",
pio_path,
pio_path + ".h",
)
includes.append(f"pio/{key}.pio.h")
if retval != 0:
raise EsphomeError("PIO assembly failed")
write_file(
CORE.relative_build_path("src/pio_includes.h"),
"#pragma once\n" + "\n".join([f'#include "{include}"' for include in includes]),
)
dir = os.path.dirname(__file__)
build_pio_file = os.path.join(dir, "build_pio.py.script")
copy_file_if_changed(
build_pio_file,
CORE.relative_build_path("build_pio.py"),
)
return True

View File

@@ -0,0 +1,47 @@
"""
Custom pioasm compiler script for platformio.
(c) 2022 by P.Z.
Sourced 2023/06/23 from https://gist.github.com/hexeguitar/f4533bc697c956ac1245b6843e2ef438
Modified by jesserockz 2023/06/23
"""
from os.path import join
import glob
import sys
import subprocess
# pylint: disable=E0602
Import("env") # noqa
from SCons.Script import ARGUMENTS
platform = env.PioPlatform()
PROJ_SRC = env["PROJECT_SRC_DIR"]
PIO_FILES = glob.glob(join(PROJ_SRC, "**", "*.pio"), recursive=True)
verbose = bool(int(ARGUMENTS.get("PIOVERBOSE", "0")))
if PIO_FILES:
if verbose:
print("==============================================")
print("PIO ASSEMBLY COMPILER")
try:
PIOASM_DIR = platform.get_package_dir("tool-pioasm-rp2040-earlephilhower")
except:
print("tool-pioasm-rp2040-earlephilhower not supported on your system!")
sys.exit()
PIOASM_EXE = join(PIOASM_DIR, "pioasm")
if verbose:
print("PIO files found:")
for filename in PIO_FILES:
if verbose:
print(f" {filename}")
subprocess.run([PIOASM_EXE, "-o", "c-sdk", filename, f"{filename}.h"])
if verbose:
print("==============================================")

View File

@@ -0,0 +1,40 @@
import platform
import esphome.codegen as cg
DEPENDENCIES = ["rp2040"]
PIOASM_REPO_VERSION = "1.5.0-b"
PIOASM_REPO_BASE = f"https://github.com/earlephilhower/pico-quick-toolchain/releases/download/{PIOASM_REPO_VERSION}"
PIOASM_VERSION = "pioasm-2e6142b.230216"
PIOASM_DOWNLOADS = {
"linux": {
"aarch64": f"aarch64-linux-gnu.{PIOASM_VERSION}.tar.gz",
"armv7l": f"arm-linux-gnueabihf.{PIOASM_VERSION}.tar.gz",
"x86_64": f"x86_64-linux-gnu.{PIOASM_VERSION}.tar.gz",
},
"windows": {
"amd64": f"x86_64-w64-mingw32.{PIOASM_VERSION}.zip",
},
"darwin": {
"x86_64": f"x86_64-apple-darwin14.{PIOASM_VERSION}.tar.gz",
"arm64": f"x86_64-apple-darwin14.{PIOASM_VERSION}.tar.gz",
},
}
async def to_code(config):
# cg.add_platformio_option(
# "platform_packages",
# [
# "earlephilhower/tool-pioasm-rp2040-earlephilhower",
# ],
# )
file = PIOASM_DOWNLOADS[platform.system().lower()][platform.machine().lower()]
cg.add_platformio_option(
"platform_packages",
[f"earlephilhower/tool-pioasm-rp2040-earlephilhower@{PIOASM_REPO_BASE}/{file}"],
)
cg.add_platformio_option("extra_scripts", ["pre:build_pio.py"])

View File

@@ -127,6 +127,7 @@ def time_to_cycles(time_us):
CONF_PIO = "pio"
AUTO_LOAD = ["rp2040_pio"]
CODEOWNERS = ["@Papa-DMan"]
DEPENDENCIES = ["rp2040"]
@@ -265,9 +266,3 @@ async def to_code(config):
time_to_cycles(config[CONF_BIT1_LOW]),
),
)
cg.add_platformio_option(
"platform_packages",
[
"earlephilhower/tool-pioasm-rp2040-earlephilhower",
],
)

View File

@@ -42,13 +42,18 @@ void SCD30Component::setup() {
ESP_LOGD(TAG, "SCD30 Firmware v%0d.%02d", (uint16_t(raw_firmware_version[0]) >> 8),
uint16_t(raw_firmware_version[0] & 0xFF));
if (this->temperature_offset_ != 0) {
if (!this->write_command(SCD30_CMD_TEMPERATURE_OFFSET, (uint16_t) (temperature_offset_ * 100.0))) {
ESP_LOGE(TAG, "Sensor SCD30 error setting temperature offset.");
this->error_code_ = MEASUREMENT_INIT_FAILED;
this->mark_failed();
return;
}
uint16_t temp_offset;
if (this->temperature_offset_ > 0) {
temp_offset = (this->temperature_offset_ * 100);
} else {
temp_offset = 0;
}
if (!this->write_command(SCD30_CMD_TEMPERATURE_OFFSET, temp_offset)) {
ESP_LOGE(TAG, "Sensor SCD30 error setting temperature offset.");
this->error_code_ = MEASUREMENT_INIT_FAILED;
this->mark_failed();
return;
}
#ifdef USE_ESP32
// According ESP32 clock stretching is typically 30ms and up to 150ms "due to

View File

@@ -68,7 +68,10 @@ CONFIG_SCHEMA = (
cv.int_range(min=0, max=0xFFFF, max_included=False),
),
cv.Optional(CONF_AMBIENT_PRESSURE_COMPENSATION, default=0): cv.pressure,
cv.Optional(CONF_TEMPERATURE_OFFSET): cv.temperature,
cv.Optional(CONF_TEMPERATURE_OFFSET): cv.All(
cv.temperature,
cv.float_range(min=0, max=655.35),
),
cv.Optional(CONF_UPDATE_INTERVAL, default="60s"): cv.All(
cv.positive_time_period_seconds,
cv.Range(

View File

@@ -11,7 +11,7 @@ from esphome.const import (
CONF_TVOC,
ICON_RADIATOR,
DEVICE_CLASS_CARBON_DIOXIDE,
DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS,
DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS,
STATE_CLASS_MEASUREMENT,
UNIT_PARTS_PER_MILLION,
UNIT_PARTS_PER_BILLION,
@@ -49,7 +49,7 @@ CONFIG_SCHEMA = (
unit_of_measurement=UNIT_PARTS_PER_BILLION,
icon=ICON_RADIATOR,
accuracy_decimals=0,
device_class=DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS,
device_class=DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_ECO2_BASELINE): sensor.sensor_schema(

View File

@@ -6,11 +6,21 @@ namespace template_ {
static const char *const TAG = "template.binary_sensor";
void TemplateBinarySensor::loop() {
if (!this->f_.has_value())
void TemplateBinarySensor::setup() {
if (!this->publish_initial_state_)
return;
auto s = (*this->f_)();
if (this->f_ != nullptr) {
this->publish_initial_state(*this->f_());
} else {
this->publish_initial_state(false);
}
}
void TemplateBinarySensor::loop() {
if (this->f_ == nullptr)
return;
auto s = this->f_();
if (s.has_value()) {
this->publish_state(*s);
}

View File

@@ -10,13 +10,14 @@ class TemplateBinarySensor : public Component, public binary_sensor::BinarySenso
public:
void set_template(std::function<optional<bool>()> &&f) { this->f_ = f; }
void setup() override;
void loop() override;
void dump_config() override;
float get_setup_priority() const override { return setup_priority::HARDWARE; }
protected:
optional<std::function<optional<bool>()>> f_{};
std::function<optional<bool>()> f_{nullptr};
};
} // namespace template_

File diff suppressed because it is too large Load Diff

View File

@@ -108,6 +108,7 @@ ROOT_CONFIG_PATH = object()
RESERVED_IDS = [
# C++ keywords http://en.cppreference.com/w/cpp/keyword
"alarm",
"alignas",
"alignof",
"and",

View File

@@ -1,6 +1,6 @@
"""Constants used by esphome."""
__version__ = "2023.6.0b6"
__version__ = "2023.6.5"
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
VALID_SUBSTITUTIONS_CHARACTERS = (
@@ -968,6 +968,7 @@ DEVICE_CLASS_GAS = "gas"
DEVICE_CLASS_GATE = "gate"
DEVICE_CLASS_HEAT = "heat"
DEVICE_CLASS_HUMIDITY = "humidity"
DEVICE_CLASS_IDENTIFY = "identify"
DEVICE_CLASS_ILLUMINANCE = "illuminance"
DEVICE_CLASS_IRRADIANCE = "irradiance"
DEVICE_CLASS_LIGHT = "light"

View File

@@ -2,6 +2,16 @@
namespace esphome {
static bool is_leap_year(uint32_t year) { return (year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0); }
static uint8_t days_in_month(uint8_t month, uint16_t year) {
static const uint8_t DAYS_IN_MONTH[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
uint8_t days = DAYS_IN_MONTH[month];
if (month == 2 && is_leap_year(year))
return 29;
return days;
}
size_t ESPTime::strftime(char *buffer, size_t buffer_len, const char *format) {
struct tm c_tm = this->to_c_tm();
return ::strftime(buffer, buffer_len, format, &c_tm);
@@ -158,14 +168,4 @@ template<typename T> bool increment_time_value(T &current, uint16_t begin, uint1
return false;
}
static bool is_leap_year(uint32_t year) { return (year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0); }
static uint8_t days_in_month(uint8_t month, uint16_t year) {
static const uint8_t DAYS_IN_MONTH[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
uint8_t days = DAYS_IN_MONTH[month];
if (month == 2 && is_leap_year(year))
return 29;
return days;
}
} // namespace esphome

View File

@@ -8,10 +8,6 @@ namespace esphome {
template<typename T> bool increment_time_value(T &current, uint16_t begin, uint16_t end);
static bool is_leap_year(uint32_t year);
static uint8_t days_in_month(uint8_t month, uint16_t year);
/// A more user-friendly version of struct tm from time.h
struct ESPTime {
/** seconds after the minute [0-60]

View File

@@ -3,6 +3,7 @@ import binascii
import codecs
import collections
import functools
import gzip
import hashlib
import hmac
import json
@@ -485,6 +486,7 @@ class DownloadBinaryRequestHandler(BaseHandler):
@bind_config
def get(self, configuration=None):
type = self.get_argument("type", "firmware.bin")
compressed = self.get_argument("compressed", "0") == "1"
storage_path = ext_storage_path(settings.config_dir, configuration)
storage_json = StorageJSON.load(storage_path)
@@ -534,6 +536,8 @@ class DownloadBinaryRequestHandler(BaseHandler):
self.send_error(404)
return
filename = filename + ".gz" if compressed else filename
self.set_header("Content-Type", "application/octet-stream")
self.set_header("Content-Disposition", f'attachment; filename="{filename}"')
self.set_header("Cache-Control", "no-cache")
@@ -542,11 +546,11 @@ class DownloadBinaryRequestHandler(BaseHandler):
return
with open(path, "rb") as f:
while True:
data = f.read(16384)
if not data:
break
self.write(data)
data = f.read()
if compressed:
data = gzip.compress(data, 9)
self.write(data)
self.finish()

View File

@@ -312,7 +312,8 @@ sensor:
id: freezer_temp_source
reference_voltage: 3.19
number: 0
- platform: airthings_wave_plus
- id: airthingswp
platform: airthings_wave_plus
ble_client_id: airthings01
update_interval: 5min
temperature:
@@ -329,7 +330,8 @@ sensor:
name: Wave Plus CO2
tvoc:
name: Wave Plus VOC
- platform: airthings_wave_mini
- id: airthingswm
platform: airthings_wave_mini
ble_client_id: airthingsmini01
update_interval: 5min
temperature: