mirror of
https://github.com/esphome/esphome.git
synced 2024-11-21 11:37:27 +01:00
Support for the AirThings Wave Plus (#1656)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
parent
58350b6c99
commit
140ef791aa
@ -14,6 +14,8 @@ esphome/core/* @esphome/core
|
||||
esphome/components/ac_dimmer/* @glmnet
|
||||
esphome/components/adc/* @esphome/core
|
||||
esphome/components/addressable_light/* @justfalter
|
||||
esphome/components/airthings_ble/* @jeromelaban
|
||||
esphome/components/airthings_wave_plus/* @jeromelaban
|
||||
esphome/components/am43/* @buxtronix
|
||||
esphome/components/am43/cover/* @buxtronix
|
||||
esphome/components/animation/* @syndlex
|
||||
|
23
esphome/components/airthings_ble/__init__.py
Normal file
23
esphome/components/airthings_ble/__init__.py
Normal file
@ -0,0 +1,23 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import esp32_ble_tracker
|
||||
from esphome.const import CONF_ID
|
||||
|
||||
DEPENDENCIES = ["esp32_ble_tracker"]
|
||||
CODEOWNERS = ["@jeromelaban"]
|
||||
|
||||
airthings_ble_ns = cg.esphome_ns.namespace("airthings_ble")
|
||||
AirthingsListener = airthings_ble_ns.class_(
|
||||
"AirthingsListener", esp32_ble_tracker.ESPBTDeviceListener
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(AirthingsListener),
|
||||
}
|
||||
).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield esp32_ble_tracker.register_ble_device(var, config)
|
33
esphome/components/airthings_ble/airthings_listener.cpp
Normal file
33
esphome/components/airthings_ble/airthings_listener.cpp
Normal file
@ -0,0 +1,33 @@
|
||||
#include "airthings_listener.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace airthings_ble {
|
||||
|
||||
static const char *TAG = "airthings_ble";
|
||||
|
||||
bool AirthingsListener::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
|
||||
for (auto &it : device.get_manufacturer_datas()) {
|
||||
if (it.uuid == esp32_ble_tracker::ESPBTUUID::from_uint32(0x0334)) {
|
||||
if (it.data.size() < 4)
|
||||
continue;
|
||||
|
||||
uint32_t sn = it.data[0];
|
||||
sn |= ((uint32_t) it.data[1] << 8);
|
||||
sn |= ((uint32_t) it.data[2] << 16);
|
||||
sn |= ((uint32_t) it.data[3] << 24);
|
||||
|
||||
ESP_LOGD(TAG, "Found AirThings device Serial:%u (MAC: %s)", sn, device.address_str().c_str());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace airthings_ble
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
20
esphome/components/airthings_ble/airthings_listener.h
Normal file
20
esphome/components/airthings_ble/airthings_listener.h
Normal file
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||
#include <BLEDevice.h>
|
||||
|
||||
namespace esphome {
|
||||
namespace airthings_ble {
|
||||
|
||||
class AirthingsListener : public esp32_ble_tracker::ESPBTDeviceListener {
|
||||
public:
|
||||
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
|
||||
};
|
||||
|
||||
} // namespace airthings_ble
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
1
esphome/components/airthings_wave_plus/__init__.py
Normal file
1
esphome/components/airthings_wave_plus/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
CODEOWNERS = ["@jeromelaban"]
|
142
esphome/components/airthings_wave_plus/airthings_wave_plus.cpp
Normal file
142
esphome/components/airthings_wave_plus/airthings_wave_plus.cpp
Normal file
@ -0,0 +1,142 @@
|
||||
#include "airthings_wave_plus.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace 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 = espbt::ClientState::Established;
|
||||
|
||||
request_read_values_();
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_GATTC_READ_CHAR_EVT: {
|
||||
if (param->read.conn_id != this->parent()->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) {
|
||||
auto value = (WavePlusReadings *) raw_value;
|
||||
|
||||
if (sizeof(WavePlusReadings) <= value_len) {
|
||||
ESP_LOGD(TAG, "version = %d", value->version);
|
||||
|
||||
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)) {
|
||||
this->radon_sensor_->publish_state(value->radon);
|
||||
}
|
||||
if (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)) {
|
||||
this->co2_sensor_->publish_state(value->co2);
|
||||
}
|
||||
if (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);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Invalid payload version (%d != 1, newer version or not a Wave Plus?)", value->version);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool AirthingsWavePlus::is_valid_radon_value_(short radon) { return 0 <= radon && radon <= 16383; }
|
||||
|
||||
bool AirthingsWavePlus::is_valid_voc_value_(short voc) { return 0 <= voc && voc <= 16383; }
|
||||
|
||||
bool AirthingsWavePlus::is_valid_co2_value_(short co2) { return 0 <= co2 && co2 <= 16383; }
|
||||
|
||||
void AirthingsWavePlus::loop() {}
|
||||
|
||||
void AirthingsWavePlus::update() {
|
||||
if (this->node_state != espbt::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()->gattc_if, this->parent()->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() {
|
||||
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_);
|
||||
}
|
||||
|
||||
AirthingsWavePlus::AirthingsWavePlus() : PollingComponent(10000) {
|
||||
auto service_bt = *BLEUUID::fromString(std::string("b42e1c08-ade7-11e4-89d3-123b93f75cba")).getNative();
|
||||
auto characteristic_bt = *BLEUUID::fromString(std::string("b42e2a68-ade7-11e4-89d3-123b93f75cba")).getNative();
|
||||
|
||||
service_uuid = espbt::ESPBTUUID::from_uuid(service_bt);
|
||||
sensors_data_characteristic_uuid = espbt::ESPBTUUID::from_uuid(characteristic_bt);
|
||||
}
|
||||
|
||||
void AirthingsWavePlus::setup() {}
|
||||
|
||||
} // namespace airthings_wave_plus
|
||||
} // namespace esphome
|
||||
|
||||
#endif // ARDUINO_ARCH_ESP32
|
79
esphome/components/airthings_wave_plus/airthings_wave_plus.h
Normal file
79
esphome/components/airthings_wave_plus/airthings_wave_plus.h
Normal file
@ -0,0 +1,79 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#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/log.h"
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include <esp_gattc_api.h>
|
||||
#include <BLEDevice.h>
|
||||
|
||||
using namespace esphome::ble_client;
|
||||
|
||||
namespace esphome {
|
||||
namespace airthings_wave_plus {
|
||||
|
||||
static const char *TAG = "airthings_wave_plus";
|
||||
|
||||
class AirthingsWavePlus : public PollingComponent, public BLEClientNode {
|
||||
public:
|
||||
AirthingsWavePlus();
|
||||
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
void update() override;
|
||||
void loop() 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_(short radon);
|
||||
bool is_valid_voc_value_(short voc);
|
||||
bool is_valid_co2_value_(short co2);
|
||||
|
||||
void read_sensors_(uint8_t *value, uint16_t value_len);
|
||||
void request_read_values_();
|
||||
|
||||
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;
|
||||
espbt::ESPBTUUID service_uuid;
|
||||
espbt::ESPBTUUID sensors_data_characteristic_uuid;
|
||||
|
||||
struct WavePlusReadings {
|
||||
uint8_t version;
|
||||
uint8_t humidity;
|
||||
uint8_t ambientLight;
|
||||
uint8_t unused01;
|
||||
uint16_t radon;
|
||||
uint16_t radon_lt;
|
||||
uint16_t temperature;
|
||||
uint16_t pressure;
|
||||
uint16_t co2;
|
||||
uint16_t voc;
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace airthings_wave_plus
|
||||
} // namespace esphome
|
||||
|
||||
#endif // ARDUINO_ARCH_ESP32
|
116
esphome/components/airthings_wave_plus/sensor.py
Normal file
116
esphome/components/airthings_wave_plus/sensor.py
Normal file
@ -0,0 +1,116 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor, ble_client
|
||||
|
||||
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"]
|
||||
|
||||
airthings_wave_plus_ns = cg.esphome_ns.namespace("airthings_wave_plus")
|
||||
AirthingsWavePlus = airthings_wave_plus_ns.class_(
|
||||
"AirthingsWavePlus", cg.PollingComponent, ble_client.BLEClientNode
|
||||
)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
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("5mins"))
|
||||
.extend(ble_client.BLE_CLIENT_SCHEMA)
|
||||
)
|
||||
|
||||
|
||||
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_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))
|
@ -507,6 +507,8 @@ CONF_PROTOCOL = "protocol"
|
||||
CONF_PULL_MODE = "pull_mode"
|
||||
CONF_PULSE_LENGTH = "pulse_length"
|
||||
CONF_QOS = "qos"
|
||||
CONF_RADON = "radon"
|
||||
CONF_RADON_LONG_TERM = "radon_long_term"
|
||||
CONF_RANDOM = "random"
|
||||
CONF_RANGE = "range"
|
||||
CONF_RANGE_FROM = "range_from"
|
||||
@ -732,6 +734,7 @@ ICON_PERCENT = "mdi:percent"
|
||||
ICON_POWER = "mdi:power"
|
||||
ICON_PULSE = "mdi:pulse"
|
||||
ICON_RADIATOR = "mdi:radiator"
|
||||
ICON_RADIOACTIVE = "mdi:radioactive"
|
||||
ICON_RESTART = "mdi:restart"
|
||||
ICON_ROTATE_RIGHT = "mdi:rotate-right"
|
||||
ICON_RULER = "mdi:ruler"
|
||||
@ -753,6 +756,7 @@ ICON_WEATHER_WINDY = "mdi:weather-windy"
|
||||
ICON_WIFI = "mdi:wifi"
|
||||
|
||||
UNIT_AMPERE = "A"
|
||||
UNIT_BECQUEREL_PER_CUBIC_METER = "Bq/m³"
|
||||
UNIT_CELSIUS = "°C"
|
||||
UNIT_COUNT_DECILITRE = "/dL"
|
||||
UNIT_COUNTS_PER_CUBIC_METER = "#/m³"
|
||||
|
@ -246,6 +246,24 @@ sensor:
|
||||
id: freezer_temp_source
|
||||
reference_voltage: 3.19
|
||||
number: 0
|
||||
- platform: airthings_wave_plus
|
||||
ble_client_id: airthings01
|
||||
update_interval: 5min
|
||||
temperature:
|
||||
name: "Wave Plus Temperature"
|
||||
radon:
|
||||
name: "Wave Plus Radon"
|
||||
radon_long_term:
|
||||
name: "Wave Plus Radon Long Term"
|
||||
pressure:
|
||||
name: "Wave Plus Pressure"
|
||||
humidity:
|
||||
name: "Wave Plus Humidity"
|
||||
co2:
|
||||
name: "Wave Plus CO2"
|
||||
tvoc:
|
||||
name: "Wave Plus VOC"
|
||||
|
||||
time:
|
||||
- platform: homeassistant
|
||||
on_time:
|
||||
@ -334,6 +352,12 @@ esp32_ble_tracker:
|
||||
- lambda: !lambda |-
|
||||
ESP_LOGD("main", "Length of manufacturer data is %i", x.size());
|
||||
|
||||
ble_client:
|
||||
- mac_address: 01:02:03:04:05:06
|
||||
id: airthings01
|
||||
|
||||
airthings_ble:
|
||||
|
||||
#esp32_ble_beacon:
|
||||
# type: iBeacon
|
||||
# uuid: 'c29ce823-e67a-4e71-bff2-abaa32e77a98'
|
||||
@ -431,3 +455,4 @@ interval:
|
||||
- logger.log: 'Interval Run'
|
||||
|
||||
display:
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user