This commit is contained in:
Otto Winter 2019-05-08 09:58:03 +02:00
parent c3d4ef284d
commit 521c080989
No known key found for this signature in database
GPG key ID: DB66C0BE6013F97E
41 changed files with 396 additions and 148 deletions

View file

@ -48,6 +48,11 @@ void ADCSensor::dump_config() {
} }
float ADCSensor::get_setup_priority() const { return setup_priority::DATA; } float ADCSensor::get_setup_priority() const { return setup_priority::DATA; }
void ADCSensor::update() { void ADCSensor::update() {
float value_v = this->sample();
ESP_LOGD(TAG, "'%s': Got voltage=%.2fV", this->get_name().c_str(), value_v);
this->publish_state(value_v);
}
float ADCSensor::sample() {
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
float value_v = analogRead(this->pin_) / 4095.0f; float value_v = analogRead(this->pin_) / 4095.0f;
switch (this->attenuation_) { switch (this->attenuation_) {
@ -64,19 +69,16 @@ void ADCSensor::update() {
value_v *= 3.9; value_v *= 3.9;
break; break;
} }
return value_v;
#endif #endif
#ifdef ARDUINO_ARCH_ESP8266 #ifdef ARDUINO_ARCH_ESP8266
#ifdef USE_ADC_SENSOR_VCC #ifdef USE_ADC_SENSOR_VCC
float value_v = ESP.getVcc() / 1024.0f; return ESP.getVcc() / 1024.0f;
#else #else
float value_v = analogRead(this->pin_) / 1024.0f; return analogRead(this->pin_) / 1024.0f;
#endif #endif
#endif #endif
ESP_LOGD(TAG, "'%s': Got voltage=%.2fV", this->get_name().c_str(), value_v);
this->publish_state(value_v);
} }
#ifdef ARDUINO_ARCH_ESP8266 #ifdef ARDUINO_ARCH_ESP8266
std::string ADCSensor::unique_id() { return get_mac_address() + "-adc"; } std::string ADCSensor::unique_id() { return get_mac_address() + "-adc"; }

View file

@ -3,19 +3,23 @@
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/core/esphal.h" #include "esphome/core/esphal.h"
#include "esphome/components/sensor/sensor.h" #include "esphome/components/sensor/sensor.h"
#include "esphome/components/voltage_sampler/voltage_sampler.h"
namespace esphome { namespace esphome {
namespace adc { namespace adc {
class ADCSensor : public sensor::Sensor, public PollingComponent { #ifdef USE_ADC_SENSOR_VCC
ADC_MODE(ADC_VCC)
#endif
class ADCSensor : public sensor::Sensor, public PollingComponent,
public voltage_sampler::VoltageSampler {
public: public:
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
/// Set the attenuation for this pin. Only available on the ESP32. /// Set the attenuation for this pin. Only available on the ESP32.
void set_attenuation(adc_attenuation_t attenuation); void set_attenuation(adc_attenuation_t attenuation);
#endif #endif
// ========== INTERNAL METHODS ==========
// (In most use cases you won't need these)
/// Update adc values. /// Update adc values.
void update() override; void update() override;
/// Setup ADc /// Setup ADc
@ -24,6 +28,7 @@ class ADCSensor : public sensor::Sensor, public PollingComponent {
/// `HARDWARE_LATE` setup priority. /// `HARDWARE_LATE` setup priority.
float get_setup_priority() const override; float get_setup_priority() const override;
void set_pin(uint8_t pin) { this->pin_ = pin; } void set_pin(uint8_t pin) { this->pin_ = pin; }
float sample() override;
#ifdef ARDUINO_ARCH_ESP8266 #ifdef ARDUINO_ARCH_ESP8266
std::string unique_id() override; std::string unique_id() override;

View file

@ -1,9 +1,12 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome import pins from esphome import pins
from esphome.components import sensor from esphome.components import sensor, voltage_sampler
from esphome.const import CONF_ATTENUATION, CONF_ID, CONF_PIN, ICON_FLASH, UNIT_VOLT from esphome.const import CONF_ATTENUATION, CONF_ID, CONF_PIN, ICON_FLASH, UNIT_VOLT
AUTO_LOAD = ['voltage_sampler']
ATTENUATION_MODES = { ATTENUATION_MODES = {
'0db': cg.global_ns.ADC_0db, '0db': cg.global_ns.ADC_0db,
'2.5db': cg.global_ns.ADC_2_5db, '2.5db': cg.global_ns.ADC_2_5db,
@ -20,7 +23,8 @@ def validate_adc_pin(value):
adc_ns = cg.esphome_ns.namespace('adc') adc_ns = cg.esphome_ns.namespace('adc')
ADCSensor = adc_ns.class_('ADCSensor', sensor.PollingSensorComponent) ADCSensor = adc_ns.class_('ADCSensor', sensor.Sensor, cg.PollingComponent,
voltage_sampler.VoltageSampler)
CONFIG_SCHEMA = sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 2).extend({ CONFIG_SCHEMA = sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 2).extend({
cv.GenerateID(): cv.declare_id(ADCSensor), cv.GenerateID(): cv.declare_id(ADCSensor),
@ -37,7 +41,6 @@ def to_code(config):
if config[CONF_PIN] == 'VCC': if config[CONF_PIN] == 'VCC':
cg.add_define('USE_ADC_SENSOR_VCC') cg.add_define('USE_ADC_SENSOR_VCC')
cg.add_global(cg.global_ns.ADC_MODE(cg.global_ns.ADC_VCC))
else: else:
cg.add(var.set_pin(config[CONF_PIN])) cg.add(var.set_pin(config[CONF_PIN]))

View file

@ -4,7 +4,7 @@ from esphome.components import i2c
from esphome.const import CONF_ID from esphome.const import CONF_ID
DEPENDENCIES = ['i2c'] DEPENDENCIES = ['i2c']
AUTO_LOAD = ['sensor'] AUTO_LOAD = ['sensor', 'voltage_sampler']
MULTI_CONF = True MULTI_CONF = True
ads1115_ns = cg.esphome_ns.namespace('ads1115') ads1115_ns = cg.esphome_ns.namespace('ads1115')

View file

@ -59,7 +59,9 @@ void ADS1115Component::setup() {
} }
for (auto *sensor : this->sensors_) { for (auto *sensor : this->sensors_) {
this->set_interval(sensor->get_name(), sensor->update_interval(), this->set_interval(sensor->get_name(), sensor->update_interval(),
[this, sensor] { this->request_measurement_(sensor); }); [this, sensor] {
this->request_measurement_(sensor);
});
} }
} }
void ADS1115Component::dump_config() { void ADS1115Component::dump_config() {
@ -76,11 +78,11 @@ void ADS1115Component::dump_config() {
} }
} }
float ADS1115Component::get_setup_priority() const { return setup_priority::DATA; } float ADS1115Component::get_setup_priority() const { return setup_priority::DATA; }
void ADS1115Component::request_measurement_(ADS1115Sensor *sensor) { float ADS1115Component::request_measurement_(ADS1115Sensor *sensor) {
uint16_t config; uint16_t config;
if (!this->read_byte_16(ADS1115_REGISTER_CONFIG, &config)) { if (!this->read_byte_16(ADS1115_REGISTER_CONFIG, &config)) {
this->status_set_warning(); this->status_set_warning();
return; return NAN;
} }
// Multiplexer // Multiplexer
// 0bxBBBxxxxxxxxxxxx // 0bxBBBxxxxxxxxxxxx
@ -96,7 +98,7 @@ void ADS1115Component::request_measurement_(ADS1115Sensor *sensor) {
if (!this->write_byte_16(ADS1115_REGISTER_CONFIG, config)) { if (!this->write_byte_16(ADS1115_REGISTER_CONFIG, config)) {
this->status_set_warning(); this->status_set_warning();
return; return NAN;
} }
// about 1.6 ms with 860 samples per second // about 1.6 ms with 860 samples per second
@ -107,7 +109,7 @@ void ADS1115Component::request_measurement_(ADS1115Sensor *sensor) {
if (millis() - start > 100) { if (millis() - start > 100) {
ESP_LOGW(TAG, "Reading ADS1115 timed out"); ESP_LOGW(TAG, "Reading ADS1115 timed out");
this->status_set_warning(); this->status_set_warning();
return; return NAN;
} }
yield(); yield();
} }
@ -115,7 +117,7 @@ void ADS1115Component::request_measurement_(ADS1115Sensor *sensor) {
uint16_t raw_conversion; uint16_t raw_conversion;
if (!this->read_byte_16(ADS1115_REGISTER_CONVERSION, &raw_conversion)) { if (!this->read_byte_16(ADS1115_REGISTER_CONVERSION, &raw_conversion)) {
this->status_set_warning(); this->status_set_warning();
return; return NAN;
} }
auto signed_conversion = static_cast<int16_t>(raw_conversion); auto signed_conversion = static_cast<int16_t>(raw_conversion);
@ -143,16 +145,24 @@ void ADS1115Component::request_measurement_(ADS1115Sensor *sensor) {
millivolts = NAN; millivolts = NAN;
} }
float v = millivolts / 1000.0f;
ESP_LOGD(TAG, "'%s': Got Voltage=%fV", sensor->get_name().c_str(), v);
sensor->publish_state(v);
this->status_clear_warning(); this->status_clear_warning();
return millivolts / 1e4f;
} }
uint8_t ADS1115Sensor::get_multiplexer() const { return this->multiplexer_; } uint8_t ADS1115Sensor::get_multiplexer() const { return this->multiplexer_; }
void ADS1115Sensor::set_multiplexer(ADS1115Multiplexer multiplexer) { this->multiplexer_ = multiplexer; } void ADS1115Sensor::set_multiplexer(ADS1115Multiplexer multiplexer) { this->multiplexer_ = multiplexer; }
uint8_t ADS1115Sensor::get_gain() const { return this->gain_; } uint8_t ADS1115Sensor::get_gain() const { return this->gain_; }
void ADS1115Sensor::set_gain(ADS1115Gain gain) { this->gain_ = gain; } void ADS1115Sensor::set_gain(ADS1115Gain gain) { this->gain_ = gain; }
float ADS1115Sensor::sample() {
return this->parent_->request_measurement_(this);
}
void ADS1115Sensor::update() {
float v = this->parent_->request_measurement_(this);
if (!isnan(v)) {
ESP_LOGD(TAG, "'%s': Got Voltage=%fV", this->get_name().c_str(), v);
this->publish_state(v);
}
}
} // namespace ads1115 } // namespace ads1115
} // namespace esphome } // namespace esphome

View file

@ -3,6 +3,7 @@
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h" #include "esphome/components/sensor/sensor.h"
#include "esphome/components/i2c/i2c.h" #include "esphome/components/i2c/i2c.h"
#include "esphome/components/voltage_sampler/voltage_sampler.h"
namespace esphome { namespace esphome {
namespace ads1115 { namespace ads1115 {
@ -38,28 +39,30 @@ class ADS1115Component : public Component, public i2c::I2CDevice {
/// HARDWARE_LATE setup priority /// HARDWARE_LATE setup priority
float get_setup_priority() const override; float get_setup_priority() const override;
protected:
/// Helper method to request a measurement from a sensor. /// Helper method to request a measurement from a sensor.
void request_measurement_(ADS1115Sensor *sensor); float request_measurement_(ADS1115Sensor *sensor);
protected:
std::vector<ADS1115Sensor *> sensors_; std::vector<ADS1115Sensor *> sensors_;
}; };
/// Internal holder class that is in instance of Sensor so that the hub can create individual sensors. /// Internal holder class that is in instance of Sensor so that the hub can create individual sensors.
class ADS1115Sensor : public sensor::Sensor { class ADS1115Sensor : public sensor::Sensor, public PollingComponent,
public voltage_sampler::VoltageSampler {
public: public:
ADS1115Sensor(ADS1115Component *parent) : parent_(parent) {}
void update() override;
void set_multiplexer(ADS1115Multiplexer multiplexer); void set_multiplexer(ADS1115Multiplexer multiplexer);
void set_gain(ADS1115Gain gain); void set_gain(ADS1115Gain gain);
// ========== INTERNAL METHODS ========== float sample() override;
// (In most use cases you won't need these)
uint8_t get_multiplexer() const; uint8_t get_multiplexer() const;
uint8_t get_gain() const; uint8_t get_gain() const;
protected: protected:
ADS1115Component *parent_;
ADS1115Multiplexer multiplexer_; ADS1115Multiplexer multiplexer_;
ADS1115Gain gain_; ADS1115Gain gain_;
uint32_t update_interval_;
}; };
} // namespace ads1115 } // namespace ads1115

View file

@ -1,6 +1,6 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import sensor from esphome.components import sensor, voltage_sampler
from esphome.components.ads1115 import ADS1115Component from esphome.components.ads1115 import ADS1115Component
from esphome.const import CONF_GAIN, CONF_MULTIPLEXER, ICON_FLASH, UNIT_VOLT, CONF_ID from esphome.const import CONF_GAIN, CONF_MULTIPLEXER, ICON_FLASH, UNIT_VOLT, CONF_ID
from esphome.py_compat import string_types from esphome.py_compat import string_types
@ -40,7 +40,8 @@ def validate_gain(value):
return cv.enum(GAIN)(value) return cv.enum(GAIN)(value)
ADS1115Sensor = ads1115_ns.class_('ADS1115Sensor', sensor.Sensor) ADS1115Sensor = ads1115_ns.class_('ADS1115Sensor', sensor.Sensor, cg.PollingComponent,
voltage_sampler.VoltageSampler)
CONF_ADS1115_ID = 'ads1115_id' CONF_ADS1115_ID = 'ads1115_id'
CONFIG_SCHEMA = sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 3).extend({ CONFIG_SCHEMA = sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 3).extend({
@ -52,11 +53,12 @@ CONFIG_SCHEMA = sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 3).extend({
def to_code(config): def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) paren = yield cg.get_variable(config[CONF_ADS1115_ID])
var = cg.new_Pvariable(config[CONF_ID], paren)
yield sensor.register_sensor(var, config) yield sensor.register_sensor(var, config)
yield cg.register_component(var, config)
cg.add(var.set_multiplexer(config[CONF_MULTIPLEXER])) cg.add(var.set_multiplexer(config[CONF_MULTIPLEXER]))
cg.add(var.set_gain(config[CONF_GAIN])) cg.add(var.set_gain(config[CONF_GAIN]))
hub = yield cg.get_variable(config[CONF_ADS1115_ID]) cg.add(paren.register_sensor(var))
cg.add(hub.register_sensor(var))

View file

@ -260,7 +260,6 @@ APIConnection::APIConnection(AsyncClient *client, APIServer *parent)
} }
APIConnection::~APIConnection() { delete this->client_; } APIConnection::~APIConnection() { delete this->client_; }
void APIConnection::on_error_(int8_t error) { void APIConnection::on_error_(int8_t error) {
ESP_LOGD(TAG, "Error from client '%s': %d", this->client_info_.c_str(), error);
// disconnect will also be called, nothing to do here // disconnect will also be called, nothing to do here
this->remove_ = true; this->remove_ = true;
} }

View file

@ -0,0 +1,18 @@
import esphome.config_validation as cv
import esphome.codegen as cg
from esphome.components import esp32_ble_tracker
from esphome.const import CONF_ID
DEPENDENCIES = ['esp32_ble_tracker']
ble_ibeacon_ns = cg.esphome_ns.namespace('ble_ibeacon')
IBeaconListener = ble_ibeacon_ns.class_('IBeaconListener', esp32_ble_tracker.ESPBTDeviceListener)
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(IBeaconListener),
}).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)

View file

@ -1,24 +1,24 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import binary_sensor from esphome.components import binary_sensor, esp32_ble_tracker
from esphome.components.esp32_ble_tracker import CONF_ESP32_BLE_ID, ESP_BLE_DEVICE_SCHEMA, \ from esphome.const import CONF_MAC_ADDRESS, CONF_ID
ESPBTDeviceListener
from esphome.const import CONF_MAC_ADDRESS, CONF_NAME, CONF_ID
DEPENDENCIES = ['esp32_ble_tracker'] DEPENDENCIES = ['esp32_ble_tracker']
ble_presence_ns = cg.esphome_ns.namespace('ble_presence') ble_presence_ns = cg.esphome_ns.namespace('ble_presence')
BLEPresenceDevice = ble_presence_ns.class_('BLEPresenceDevice', binary_sensor.BinarySensor, BLEPresenceDevice = ble_presence_ns.class_('BLEPresenceDevice', binary_sensor.BinarySensor,
cg.Component, ESPBTDeviceListener) cg.Component, esp32_ble_tracker.ESPBTDeviceListener)
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({ CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({
cv.GenerateID(): cv.declare_id(BLEPresenceDevice), cv.GenerateID(): cv.declare_id(BLEPresenceDevice),
cv.Required(CONF_MAC_ADDRESS): cv.mac_address, cv.Required(CONF_MAC_ADDRESS): cv.mac_address,
}).extend(ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) }).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA)
def to_code(config): def to_code(config):
hub = yield cg.get_variable(config[CONF_ESP32_BLE_ID]) var = cg.new_Pvariable(config[CONF_ID])
var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME], hub, config[CONF_MAC_ADDRESS].as_hex)
yield cg.register_component(var, config) yield cg.register_component(var, config)
yield esp32_ble_tracker.register_ble_device(var, config)
yield binary_sensor.register_binary_sensor(var, config) yield binary_sensor.register_binary_sensor(var, config)
cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))

View file

@ -13,9 +13,9 @@ class BLEPresenceDevice : public binary_sensor::BinarySensor,
public esp32_ble_tracker::ESPBTDeviceListener, public esp32_ble_tracker::ESPBTDeviceListener,
public Component { public Component {
public: public:
BLEPresenceDevice(const std::string &name, esp32_ble_tracker::ESP32BLETracker *parent, uint64_t address) void set_address(uint64_t address) {
: binary_sensor::BinarySensor(name), esp32_ble_tracker::ESPBTDeviceListener(parent), address_(address) {} address_ = address;
}
void on_scan_end() override { void on_scan_end() override {
if (!this->found_) if (!this->found_)
this->publish_state(false); this->publish_state(false);
@ -29,7 +29,6 @@ class BLEPresenceDevice : public binary_sensor::BinarySensor,
} }
return false; return false;
} }
void setup() override { this->setup_ble(); }
void dump_config() override; void dump_config() override;
float get_setup_priority() const override { return setup_priority::DATA; } float get_setup_priority() const override { return setup_priority::DATA; }

View file

@ -11,9 +11,9 @@ namespace ble_rssi {
class BLERSSISensor : public sensor::Sensor, public esp32_ble_tracker::ESPBTDeviceListener, public Component { class BLERSSISensor : public sensor::Sensor, public esp32_ble_tracker::ESPBTDeviceListener, public Component {
public: public:
BLERSSISensor(const std::string &name, esp32_ble_tracker::ESP32BLETracker *parent, uint64_t address) void set_address(uint64_t address) {
: sensor::Sensor(name), esp32_ble_tracker::ESPBTDeviceListener(parent), address_(address) {} address_ = address;
}
void on_scan_end() override { void on_scan_end() override {
if (!this->found_) if (!this->found_)
this->publish_state(NAN); this->publish_state(NAN);
@ -27,7 +27,6 @@ class BLERSSISensor : public sensor::Sensor, public esp32_ble_tracker::ESPBTDevi
} }
return false; return false;
} }
void setup() override { this->setup_ble(); }
void dump_config() override; void dump_config() override;
float get_setup_priority() const override { return setup_priority::DATA; } float get_setup_priority() const override { return setup_priority::DATA; }

View file

@ -1,24 +1,24 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import sensor from esphome.components import sensor, esp32_ble_tracker
from esphome.components.esp32_ble_tracker import CONF_ESP32_BLE_ID, ESPBTDeviceListener, \ from esphome.const import CONF_MAC_ADDRESS, CONF_ID, UNIT_DECIBEL, ICON_SIGNAL
ESP_BLE_DEVICE_SCHEMA
from esphome.const import CONF_MAC_ADDRESS, CONF_NAME, CONF_ID, UNIT_DECIBEL, ICON_SIGNAL
DEPENDENCIES = ['esp32_ble_tracker'] DEPENDENCIES = ['esp32_ble_tracker']
ble_rssi_ns = cg.esphome_ns.namespace('ble_rssi') ble_rssi_ns = cg.esphome_ns.namespace('ble_rssi')
BLERSSISensor = ble_rssi_ns.class_('BLERSSISensor', sensor.Sensor, cg.Component, BLERSSISensor = ble_rssi_ns.class_('BLERSSISensor', sensor.Sensor, cg.Component,
ESPBTDeviceListener) esp32_ble_tracker.ESPBTDeviceListener)
CONFIG_SCHEMA = sensor.sensor_schema(UNIT_DECIBEL, ICON_SIGNAL, 0).extend({ CONFIG_SCHEMA = sensor.sensor_schema(UNIT_DECIBEL, ICON_SIGNAL, 0).extend({
cv.GenerateID(): cv.declare_id(BLERSSISensor), cv.GenerateID(): cv.declare_id(BLERSSISensor),
cv.Required(CONF_MAC_ADDRESS): cv.mac_address, cv.Required(CONF_MAC_ADDRESS): cv.mac_address,
}).extend(ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) }).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA)
def to_code(config): def to_code(config):
hub = yield cg.get_variable(config[CONF_ESP32_BLE_ID]) var = cg.new_Pvariable(config[CONF_ID])
var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME], hub, config[CONF_MAC_ADDRESS].as_hex)
yield cg.register_component(var, config) yield cg.register_component(var, config)
yield esp32_ble_tracker.register_ble_device(var, config)
yield sensor.register_sensor(var, config) yield sensor.register_sensor(var, config)
cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))

View file

@ -1,9 +1,10 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.const import CONF_ID, CONF_SCAN_INTERVAL, ESP_PLATFORM_ESP32 from esphome.const import CONF_ID, CONF_SCAN_INTERVAL, ESP_PLATFORM_ESP32
from esphome.core import coroutine
ESP_PLATFORMS = [ESP_PLATFORM_ESP32] ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
AUTO_LOAD = ['xiaomi_ble'] AUTO_LOAD = ['xiaomi_ble', 'ble_ibeacon']
CONF_ESP32_BLE_ID = 'esp32_ble_id' CONF_ESP32_BLE_ID = 'esp32_ble_id'
esp32_ble_tracker_ns = cg.esphome_ns.namespace('esp32_ble_tracker') esp32_ble_tracker_ns = cg.esphome_ns.namespace('esp32_ble_tracker')
@ -24,3 +25,10 @@ def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config) yield cg.register_component(var, config)
cg.add(var.set_scan_interval(config[CONF_SCAN_INTERVAL])) cg.add(var.set_scan_interval(config[CONF_SCAN_INTERVAL]))
@coroutine
def register_ble_device(var, config):
paren = yield cg.get_variable(config[CONF_ESP32_BLE_ID])
cg.add(paren.register_listener(var))
yield var

View file

@ -201,6 +201,22 @@ void ESP32BLETracker::gap_scan_result(const esp_ble_gap_cb_param_t::ble_scan_res
} }
} }
std::string hexencode(const std::string &raw_data) {
char buf[20];
std::string res;
for (size_t i = 0; i < raw_data.size(); i++) {
if (i + 1 != raw_data.size()) {
sprintf(buf, "0x%02X.", static_cast<uint8_t>(raw_data[i]));
} else {
sprintf(buf, "0x%02X ", static_cast<uint8_t>(raw_data[i]));
}
res += buf;
}
sprintf(buf, "(%zu)", raw_data.size());
res += buf;
return res;
}
ESPBTUUID::ESPBTUUID() : uuid_() {} ESPBTUUID::ESPBTUUID() : uuid_() {}
ESPBTUUID ESPBTUUID::from_uint16(uint16_t uuid) { ESPBTUUID ESPBTUUID::from_uint16(uint16_t uuid) {
ESPBTUUID ret; ESPBTUUID ret;
@ -259,12 +275,22 @@ std::string ESPBTUUID::to_string() {
return sbuf; return sbuf;
} }
ESPBLEiBeacon::ESPBLEiBeacon(const uint8_t *data) { memcpy(&this->beacon_data_, data, sizeof(beacon_data_)); }
optional<ESPBLEiBeacon> ESPBLEiBeacon::from_manufacturer_data(const std::string &data) {
if (data.size() != 25)
return {};
if (data[0] != 0x4C || data[1] != 0x00)
return {};
return ESPBLEiBeacon(reinterpret_cast<const uint8_t *>(data.data()));
}
void ESPBTDevice::parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param &param) { void ESPBTDevice::parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param &param) {
for (uint8_t i = 0; i < ESP_BD_ADDR_LEN; i++) for (uint8_t i = 0; i < ESP_BD_ADDR_LEN; i++)
this->address_[i] = param.bda[i]; this->address_[i] = param.bda[i];
this->address_type_ = param.ble_addr_type; this->address_type_ = param.ble_addr_type;
this->rssi_ = param.rssi; this->rssi_ = param.rssi;
this->parse_adv(param); this->parse_adv_(param);
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE #ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
ESP_LOGVV(TAG, "Parse Result:"); ESP_LOGVV(TAG, "Parse Result:");
@ -287,7 +313,7 @@ void ESPBTDevice::parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_e
this->address_[2], this->address_[3], this->address_[4], this->address_[5], address_type); this->address_[2], this->address_[3], this->address_[4], this->address_[5], address_type);
ESP_LOGVV(TAG, " RSSI: %d", this->rssi_); ESP_LOGVV(TAG, " RSSI: %d", this->rssi_);
ESP_LOGVV(TAG, " Name: %s", this->name_.c_str()); ESP_LOGVV(TAG, " Name: '%s'", this->name_.c_str());
if (this->tx_power_.has_value()) { if (this->tx_power_.has_value()) {
ESP_LOGVV(TAG, " TX Power: %d", *this->tx_power_); ESP_LOGVV(TAG, " TX Power: %d", *this->tx_power_);
} }
@ -300,26 +326,18 @@ void ESPBTDevice::parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_e
for (auto uuid : this->service_uuids_) { for (auto uuid : this->service_uuids_) {
ESP_LOGVV(TAG, " Service UUID: %s", uuid.to_string().c_str()); ESP_LOGVV(TAG, " Service UUID: %s", uuid.to_string().c_str());
} }
ESP_LOGVV(TAG, " Manufacturer data: '%s'", this->manufacturer_data_.c_str()); ESP_LOGVV(TAG, " Manufacturer data: %s", hexencode(this->manufacturer_data_).c_str());
ESP_LOGVV(TAG, " Service data: '%s'", this->service_data_.c_str()); ESP_LOGVV(TAG, " Service data: %s", hexencode(this->service_data_).c_str());
if (this->service_data_uuid_.has_value()) { if (this->service_data_uuid_.has_value()) {
ESP_LOGVV(TAG, " Service Data UUID: %s", this->service_data_uuid_->to_string().c_str()); ESP_LOGVV(TAG, " Service Data UUID: %s", this->service_data_uuid_->to_string().c_str());
} }
char buffer[200]; ESP_LOGVV(TAG, "Adv data: %s",
size_t off = 0; hexencode(std::string(reinterpret_cast<const char *>(param.ble_adv), param.adv_data_len)).c_str());
for (uint8_t i = 0; i < param.adv_data_len; i++) {
int ret = snprintf(buffer + off, sizeof(buffer) - off, "%02X.", param.ble_adv[i]);
if (ret < 0) {
break;
}
off += ret;
}
ESP_LOGVV(TAG, "Adv data: %s (%u bytes)", buffer, param.adv_data_len);
#endif #endif
} }
void ESPBTDevice::parse_adv(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param &param) { void ESPBTDevice::parse_adv_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param &param) {
size_t offset = 0; size_t offset = 0;
const uint8_t *payload = param.ble_adv; const uint8_t *payload = param.ble_adv;
uint8_t len = param.adv_data_len; uint8_t len = param.adv_data_len;
@ -472,8 +490,6 @@ void ESP32BLETracker::print_bt_device_info(const ESPBTDevice &device) {
} }
} }
void ESPBTDeviceListener::setup_ble() { this->parent_->add_listener(this); }
} // namespace esp32_ble_tracker } // namespace esp32_ble_tracker
} // namespace esphome } // namespace esphome

View file

@ -31,12 +31,31 @@ class ESPBTUUID {
esp_bt_uuid_t uuid_; esp_bt_uuid_t uuid_;
}; };
class ESPBLEiBeacon {
public:
ESPBLEiBeacon(const uint8_t *data);
static optional<ESPBLEiBeacon> from_manufacturer_data(const std::string &data);
uint16_t get_major() { return reverse_bits_16(this->beacon_data_.major); }
uint16_t get_minor() { return reverse_bits_16(this->beacon_data_.minor); }
int8_t get_signal_power() { return this->beacon_data_.signal_power; }
ESPBTUUID get_uuid() { return ESPBTUUID::from_raw(this->beacon_data_.proximity_uuid); }
protected:
struct {
uint16_t manufacturer_id;
uint8_t sub_type;
uint8_t proximity_uuid[16];
uint16_t major;
uint16_t minor;
int8_t signal_power;
} PACKED beacon_data_;
};
class ESPBTDevice { class ESPBTDevice {
public: public:
void parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param &param); void parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param &param);
void parse_adv(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param &param);
std::string address_str() const; std::string address_str() const;
uint64_t address_uint64() const; uint64_t address_uint64() const;
@ -51,8 +70,13 @@ class ESPBTDevice {
const std::string &get_manufacturer_data() const; const std::string &get_manufacturer_data() const;
const std::string &get_service_data() const; const std::string &get_service_data() const;
const optional<ESPBTUUID> &get_service_data_uuid() const; const optional<ESPBTUUID> &get_service_data_uuid() const;
const optional<ESPBLEiBeacon> get_ibeacon() const {
return ESPBLEiBeacon::from_manufacturer_data(this->manufacturer_data_);
}
protected: protected:
void parse_adv_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param &param);
esp_bd_addr_t address_{ esp_bd_addr_t address_{
0, 0,
}; };
@ -72,28 +96,30 @@ class ESP32BLETracker;
class ESPBTDeviceListener { class ESPBTDeviceListener {
public: public:
ESPBTDeviceListener(ESP32BLETracker *parent) : parent_(parent) {}
void setup_ble();
virtual void on_scan_end() {} virtual void on_scan_end() {}
virtual bool parse_device(const ESPBTDevice &device) = 0; virtual bool parse_device(const ESPBTDevice &device) = 0;
void set_parent(ESP32BLETracker *parent) {
parent_ = parent;
}
protected: protected:
ESP32BLETracker *parent_; ESP32BLETracker *parent_{nullptr};
}; };
class ESP32BLETracker : public Component { class ESP32BLETracker : public Component {
public: public:
void set_scan_interval(uint32_t scan_interval); void set_scan_interval(uint32_t scan_interval);
// ========== INTERNAL METHODS ==========
// (In most use cases you won't need these)
/// Setup the FreeRTOS task and the Bluetooth stack. /// Setup the FreeRTOS task and the Bluetooth stack.
void setup() override; void setup() override;
void dump_config() override; void dump_config() override;
void loop() override; void loop() override;
void add_listener(ESPBTDeviceListener *listener) { this->listeners_.push_back(listener); } void register_listener(ESPBTDeviceListener *listener) {
listener->set_parent(this);
this->listeners_.push_back(listener);
}
void print_bt_device_info(const ESPBTDevice &device); void print_bt_device_info(const ESPBTDevice &device);

View file

@ -112,7 +112,7 @@ template<typename... Ts> class AddressableSet : public Action<Ts...> {
void play(Ts... x) override { void play(Ts... x) override {
auto *out = (AddressableLight *) this->parent_->get_output(); auto *out = (AddressableLight *) this->parent_->get_output();
int32_t range_from = this->range_from_.value_or(x..., 0); int32_t range_from = this->range_from_.value_or(x..., 0);
int32_t range_to = this->range_to_.value_or(x..., out->size()); int32_t range_to = this->range_to_.value_or(x..., out->size() - 1) + 1;
auto range = out->range(range_from, range_to); auto range = out->range(range_from, range_to);
if (this->red_.has_value()) if (this->red_.has_value())
range.set_red(this->red_.value(x...)); range.set_red(this->red_.value(x...));

View file

@ -3,8 +3,9 @@ import esphome.config_validation as cv
from esphome import automation from esphome import automation
from esphome.const import CONF_ID, CONF_TRANSITION_LENGTH, CONF_STATE, CONF_FLASH_LENGTH, \ from esphome.const import CONF_ID, CONF_TRANSITION_LENGTH, CONF_STATE, CONF_FLASH_LENGTH, \
CONF_EFFECT, CONF_BRIGHTNESS, CONF_RED, CONF_GREEN, CONF_BLUE, CONF_WHITE, \ CONF_EFFECT, CONF_BRIGHTNESS, CONF_RED, CONF_GREEN, CONF_BLUE, CONF_WHITE, \
CONF_COLOR_TEMPERATURE CONF_COLOR_TEMPERATURE, CONF_RANGE_FROM, CONF_RANGE_TO
from .types import DimRelativeAction, ToggleAction, LightState, LightControlAction from .types import DimRelativeAction, ToggleAction, LightState, LightControlAction, \
AddressableLightState, AddressableSet
@automation.register_action('light.toggle', ToggleAction, automation.maybe_simple_id({ @automation.register_action('light.toggle', ToggleAction, automation.maybe_simple_id({
@ -87,7 +88,7 @@ def light_control_to_code(config, action_id, template_arg, args):
CONF_RELATIVE_BRIGHTNESS = 'relative_brightness' CONF_RELATIVE_BRIGHTNESS = 'relative_brightness'
LIGHT_DIM_RELATIVE_ACTION_SCHEMA = cv.Schema({ LIGHT_DIM_RELATIVE_ACTION_SCHEMA = cv.Schema({
cv.Required(CONF_ID): cv.use_id(LightState), cv.Required(CONF_ID): cv.use_id(LightState),
cv.Required(CONF_RELATIVE_BRIGHTNESS): cv.templatable(cv.percentage), cv.Required(CONF_RELATIVE_BRIGHTNESS): cv.templatable(cv.possibly_negative_percentage),
cv.Optional(CONF_TRANSITION_LENGTH): cv.templatable(cv.positive_time_period_milliseconds), cv.Optional(CONF_TRANSITION_LENGTH): cv.templatable(cv.positive_time_period_milliseconds),
}) })
@ -103,3 +104,41 @@ def light_dim_relative_to_code(config, action_id, template_arg, args):
templ = yield cg.templatable(config[CONF_TRANSITION_LENGTH], args, cg.uint32) templ = yield cg.templatable(config[CONF_TRANSITION_LENGTH], args, cg.uint32)
cg.add(var.set_transition_length(templ)) cg.add(var.set_transition_length(templ))
yield var yield var
LIGHT_ADDRESSABLE_SET_ACTION_SCHEMA = cv.Schema({
cv.Required(CONF_ID): cv.use_id(AddressableLightState),
cv.Optional(CONF_RANGE_FROM): cv.templatable(cv.positive_int),
cv.Optional(CONF_RANGE_TO): cv.templatable(cv.positive_int),
cv.Optional(CONF_RED): cv.templatable(cv.percentage),
cv.Optional(CONF_GREEN): cv.templatable(cv.percentage),
cv.Optional(CONF_BLUE): cv.templatable(cv.percentage),
cv.Optional(CONF_WHITE): cv.templatable(cv.percentage),
})
@automation.register_action('light.addressable_set', AddressableSet,
LIGHT_ADDRESSABLE_SET_ACTION_SCHEMA)
def light_addressable_set_to_code(config, action_id, template_arg, args):
paren = yield cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren)
if CONF_RANGE_FROM in config:
templ = yield cg.templatable(config[CONF_RANGE_FROM], args, cg.int32)
cg.add(var.set_range_from(templ))
if CONF_RANGE_TO in config:
templ = yield cg.templatable(config[CONF_RANGE_TO], args, cg.int32)
cg.add(var.set_range_to(templ))
rgbw_to_exp = lambda x: int(round(x * 255))
if CONF_RED in config:
templ = yield cg.templatable(config[CONF_RED], args, cg.uint8, to_exp=rgbw_to_exp)
cg.add(var.set_red(templ))
if CONF_GREEN in config:
templ = yield cg.templatable(config[CONF_GREEN], args, cg.uint8, to_exp=rgbw_to_exp)
cg.add(var.set_green(templ))
if CONF_BLUE in config:
templ = yield cg.templatable(config[CONF_BLUE], args, cg.uint8, to_exp=rgbw_to_exp)
cg.add(var.set_blue(templ))
if CONF_WHITE in config:
templ = yield cg.templatable(config[CONF_WHITE], args, cg.uint8, to_exp=rgbw_to_exp)
cg.add(var.set_white(templ))
yield var

View file

@ -15,6 +15,7 @@ LightColorValues = light_ns.class_('LightColorValues')
ToggleAction = light_ns.class_('ToggleAction', automation.Action) ToggleAction = light_ns.class_('ToggleAction', automation.Action)
LightControlAction = light_ns.class_('LightControlAction', automation.Action) LightControlAction = light_ns.class_('LightControlAction', automation.Action)
DimRelativeAction = light_ns.class_('DimRelativeAction', automation.Action) DimRelativeAction = light_ns.class_('DimRelativeAction', automation.Action)
AddressableSet = light_ns.class_('AddressableSet', automation.Action)
# Effects # Effects
LightEffect = light_ns.class_('LightEffect') LightEffect = light_ns.class_('LightEffect')

View file

@ -71,6 +71,7 @@ def validate_local_no_higher_than_global(value):
Logger = logger_ns.class_('Logger', cg.Component) Logger = logger_ns.class_('Logger', cg.Component)
CONF_ESP8266_STORE_LOG_STRINGS_IN_FLASH = 'esp8266_store_log_strings_in_flash'
CONFIG_SCHEMA = cv.All(cv.Schema({ CONFIG_SCHEMA = cv.All(cv.Schema({
cv.GenerateID(): cv.declare_id(Logger), cv.GenerateID(): cv.declare_id(Logger),
cv.Optional(CONF_BAUD_RATE, default=115200): cv.positive_int, cv.Optional(CONF_BAUD_RATE, default=115200): cv.positive_int,
@ -79,7 +80,10 @@ CONFIG_SCHEMA = cv.All(cv.Schema({
cv.Optional(CONF_LEVEL, default='DEBUG'): is_log_level, cv.Optional(CONF_LEVEL, default='DEBUG'): is_log_level,
cv.Optional(CONF_LOGS, default={}): cv.Schema({ cv.Optional(CONF_LOGS, default={}): cv.Schema({
cv.string: is_log_level, cv.string: is_log_level,
}) }),
cv.SplitDefault(CONF_ESP8266_STORE_LOG_STRINGS_IN_FLASH, esp8266=True):
cv.All(cv.only_on_esp8266, cv.boolean),
}).extend(cv.COMPONENT_SCHEMA), validate_local_no_higher_than_global) }).extend(cv.COMPONENT_SCHEMA), validate_local_no_higher_than_global)
@ -126,7 +130,7 @@ def to_code(config):
cg.add_build_flag('-DCORE_DEBUG_LEVEL=5') cg.add_build_flag('-DCORE_DEBUG_LEVEL=5')
if CORE.is_esp32 and is_at_least_very_verbose: if CORE.is_esp32 and is_at_least_very_verbose:
cg.add_build_flag('-DENABLE_I2C_DEBUG_BUFFER') cg.add_build_flag('-DENABLE_I2C_DEBUG_BUFFER')
if CORE.is_esp8266: if config.get(CONF_ESP8266_STORE_LOG_STRINGS_IN_FLASH):
cg.add_build_flag('-DUSE_STORE_LOG_STR_IN_FLASH') cg.add_build_flag('-DUSE_STORE_LOG_STR_IN_FLASH')
# Register at end for safe mode # Register at end for safe mode

View file

@ -99,17 +99,18 @@ void MPU6050Component::dump_config() {
void MPU6050Component::update() { void MPU6050Component::update() {
ESP_LOGV(TAG, " Updating MPU6050..."); ESP_LOGV(TAG, " Updating MPU6050...");
uint16_t data[7]; uint16_t raw_data[7];
if (!this->read_bytes_16(MPU6050_REGISTER_ACCEL_XOUT_H, data, 7)) { if (!this->read_bytes_16(MPU6050_REGISTER_ACCEL_XOUT_H, raw_data, 7)) {
this->status_set_warning(); this->status_set_warning();
return; return;
} }
auto *data = reinterpret_cast<int16_t *>(raw_data);
float accel_x = data[0] * MPU6050_RANGE_PER_DIGIT_2G * GRAVITY_EARTH; float accel_x = data[0] * MPU6050_RANGE_PER_DIGIT_2G * GRAVITY_EARTH;
float accel_y = data[1] * MPU6050_RANGE_PER_DIGIT_2G * GRAVITY_EARTH; float accel_y = data[1] * MPU6050_RANGE_PER_DIGIT_2G * GRAVITY_EARTH;
float accel_z = data[2] * MPU6050_RANGE_PER_DIGIT_2G * GRAVITY_EARTH; float accel_z = data[2] * MPU6050_RANGE_PER_DIGIT_2G * GRAVITY_EARTH;
float temperature = data[3] / 340.0f + 36.53f; float temperature = raw_data[3] / 340.0f + 36.53f;
float gyro_x = data[4] * MPU6050_SCALE_DPS_PER_DIGIT_2000; float gyro_x = data[4] * MPU6050_SCALE_DPS_PER_DIGIT_2000;
float gyro_y = data[5] * MPU6050_SCALE_DPS_PER_DIGIT_2000; float gyro_y = data[5] * MPU6050_SCALE_DPS_PER_DIGIT_2000;

View file

@ -51,8 +51,6 @@ sensor_ns = cg.esphome_ns.namespace('sensor')
Sensor = sensor_ns.class_('Sensor', cg.Nameable) Sensor = sensor_ns.class_('Sensor', cg.Nameable)
SensorPtr = Sensor.operator('ptr') SensorPtr = Sensor.operator('ptr')
PollingSensorComponent = sensor_ns.class_('PollingSensorComponent', cg.PollingComponent, Sensor)
# Triggers # Triggers
SensorStateTrigger = sensor_ns.class_('SensorStateTrigger', automation.Trigger.template(cg.float_)) SensorStateTrigger = sensor_ns.class_('SensorStateTrigger', automation.Trigger.template(cg.float_))
SensorRawStateTrigger = sensor_ns.class_('SensorRawStateTrigger', SensorRawStateTrigger = sensor_ns.class_('SensorRawStateTrigger',

View file

@ -11,16 +11,6 @@ namespace time {
static const char *TAG = "time"; static const char *TAG = "time";
RealTimeClock::RealTimeClock() = default; RealTimeClock::RealTimeClock() = default;
ESPTime RealTimeClock::now() {
time_t t = ::time(nullptr);
struct tm *c_tm = ::localtime(&t);
return ESPTime::from_tm(c_tm, t);
}
ESPTime RealTimeClock::utcnow() {
time_t t = ::time(nullptr);
struct tm *c_tm = ::gmtime(&t);
return ESPTime::from_tm(c_tm, t);
}
void RealTimeClock::call_setup() { void RealTimeClock::call_setup() {
this->setup_internal_(); this->setup_internal_();
setenv("TZ", this->timezone_.c_str(), 1); setenv("TZ", this->timezone_.c_str(), 1);
@ -44,7 +34,7 @@ size_t ESPTime::strftime(char *buffer, size_t buffer_len, const char *format) {
struct tm c_tm = this->to_c_tm(); struct tm c_tm = this->to_c_tm();
return ::strftime(buffer, buffer_len, format, &c_tm); return ::strftime(buffer, buffer_len, format, &c_tm);
} }
ESPTime ESPTime::from_tm(struct tm *c_tm, time_t c_time) { ESPTime ESPTime::from_c_tm(struct tm *c_tm, time_t c_time) {
return ESPTime{.second = uint8_t(c_tm->tm_sec), return ESPTime{.second = uint8_t(c_tm->tm_sec),
.minute = uint8_t(c_tm->tm_min), .minute = uint8_t(c_tm->tm_min),
.hour = uint8_t(c_tm->tm_hour), .hour = uint8_t(c_tm->tm_hour),

View file

@ -52,7 +52,26 @@ struct ESPTime {
bool in_range() const; bool in_range() const;
static ESPTime from_tm(struct tm *c_tm, time_t c_time); static ESPTime from_c_tm(struct tm *c_tm, time_t c_time);
/** Convert an epoch timestamp to an ESPTime instance of local time.
*
* @param epoch Seconds since 1st January 1970. In UTC.
* @return The generated ESPTime
*/
static ESPTime from_epoch_local(time_t epoch) {
struct tm *c_tm = ::localtime(&epoch);
return ESPTime::from_c_tm(c_tm, epoch);
}
/** Convert an epoch timestamp to an ESPTime instance of UTC time.
*
* @param epoch Seconds since 1st January 1970. In UTC.
* @return The generated ESPTime
*/
static ESPTime from_epoch_utc(time_t epoch) {
struct tm *c_tm = ::gmtime(&epoch);
return ESPTime::from_c_tm(c_tm, epoch);
}
struct tm to_c_tm(); struct tm to_c_tm();
@ -81,10 +100,19 @@ class RealTimeClock : public Component {
std::string get_timezone() { return this->timezone_; } std::string get_timezone() { return this->timezone_; }
/// Get the time in the currently defined timezone. /// Get the time in the currently defined timezone.
ESPTime now(); ESPTime now() {
return ESPTime::from_epoch_utc(this->timestamp_now());
}
/// Get the time without any time zone or DST corrections. /// Get the time without any time zone or DST corrections.
ESPTime utcnow(); ESPTime utcnow() {
return ESPTime::from_epoch_local(this->timestamp_now());
}
/// Get the current time as the UTC epoch since January 1st 1970.
time_t timestamp_now() {
return ::time(nullptr);
}
void call_setup() override; void call_setup() override;

View file

@ -0,0 +1,4 @@
import esphome.codegen as cg
voltage_sampler_ns = cg.esphome_ns.namespace('voltage_sampler')
VoltageSampler = voltage_sampler_ns.class_('VoltageSampler')

View file

@ -0,0 +1,16 @@
#pragma once
#include "esphome/core/component.h"
namespace esphome {
namespace voltage_sampler {
/// Abstract interface for components to request voltage (usually ADC readings)
class VoltageSampler {
public:
/// Get a voltage reading, in V.
virtual float sample() = 0;
};
} // namespace voltage_sampler
} // namespace esphome

View file

@ -468,10 +468,6 @@ std::string WiFiComponent::format_mac_addr(const uint8_t *mac) {
sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
return buf; return buf;
} }
void WiFiComponent::on_safe_shutdown() {
// Disable WiFi interface on shutdown
this->wifi_mode_(false, false);
}
bool sta_field_equal(const uint8_t *field_a, const uint8_t *field_b, int len) { bool sta_field_equal(const uint8_t *field_a, const uint8_t *field_b, int len) {
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {

View file

@ -164,8 +164,6 @@ class WiFiComponent : public Component {
/// Reconnect WiFi if required. /// Reconnect WiFi if required.
void loop() override; void loop() override;
void on_safe_shutdown() override;
bool has_sta() const; bool has_sta() const;
bool has_ap() const; bool has_ap() const;

View file

@ -173,6 +173,8 @@ bool WiFiComponent::wifi_sta_connect_(WiFiAP ap) {
return false; return false;
} }
this->wifi_apply_hostname_();
err = esp_wifi_connect(); err = esp_wifi_connect();
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_LOGW(TAG, "esp_wifi_connect failed! %d", err); ESP_LOGW(TAG, "esp_wifi_connect failed! %d", err);

View file

@ -196,6 +196,8 @@ bool WiFiComponent::wifi_sta_connect_(WiFiAP ap) {
return false; return false;
} }
this->wifi_apply_hostname_();
ETS_UART_INTR_DISABLE(); ETS_UART_INTR_DISABLE();
ret = wifi_station_connect(); ret = wifi_station_connect();
ETS_UART_INTR_ENABLE(); ETS_UART_INTR_ENABLE();

View file

@ -1,20 +1,18 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components.esp32_ble_tracker import CONF_ESP32_BLE_ID, ESPBTDeviceListener, \ from esphome.components import esp32_ble_tracker
ESP_BLE_DEVICE_SCHEMA
from esphome.const import CONF_ID from esphome.const import CONF_ID
DEPENDENCIES = ['esp32_ble_tracker'] DEPENDENCIES = ['esp32_ble_tracker']
xiaomi_ble_ns = cg.esphome_ns.namespace('xiaomi_ble') xiaomi_ble_ns = cg.esphome_ns.namespace('xiaomi_ble')
XiaomiListener = xiaomi_ble_ns.class_('XiaomiListener', cg.Component, ESPBTDeviceListener) XiaomiListener = xiaomi_ble_ns.class_('XiaomiListener', esp32_ble_tracker.ESPBTDeviceListener)
CONFIG_SCHEMA = cv.Schema({ CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(XiaomiListener), cv.GenerateID(): cv.declare_id(XiaomiListener),
}).extend(ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) }).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
def to_code(config): def to_code(config):
hub = yield cg.get_variable(config[CONF_ESP32_BLE_ID]) var = cg.new_Pvariable(config[CONF_ID])
var = cg.new_Pvariable(config[CONF_ID], hub) yield esp32_ble_tracker.register_ble_device(var, config)
yield cg.register_component(var, config)

View file

@ -138,8 +138,6 @@ bool XiaomiListener::parse_device(const esp32_ble_tracker::ESPBTDevice &device)
return true; return true;
} }
void XiaomiListener::setup() { this->setup_ble(); }
XiaomiListener::XiaomiListener(esp32_ble_tracker::ESP32BLETracker *parent) : ESPBTDeviceListener(parent) {}
} // namespace xiaomi_ble } // namespace xiaomi_ble
} // namespace esphome } // namespace esphome

View file

@ -22,11 +22,9 @@ bool parse_xiaomi_data_byte(uint8_t data_type, const uint8_t *data, uint8_t data
optional<XiaomiParseResult> parse_xiaomi(const esp32_ble_tracker::ESPBTDevice &device); optional<XiaomiParseResult> parse_xiaomi(const esp32_ble_tracker::ESPBTDevice &device);
class XiaomiListener : public Component, public esp32_ble_tracker::ESPBTDeviceListener { class XiaomiListener : public esp32_ble_tracker::ESPBTDeviceListener {
public: public:
XiaomiListener(esp32_ble_tracker::ESP32BLETracker *parent);
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override; bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
void setup() override;
}; };
} // namespace xiaomi_ble } // namespace xiaomi_ble

View file

@ -1,8 +1,6 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import sensor from esphome.components import sensor, esp32_ble_tracker
from esphome.components.esp32_ble_tracker import CONF_ESP32_BLE_ID, ESPBTDeviceListener, \
ESP_BLE_DEVICE_SCHEMA
from esphome.const import CONF_BATTERY_LEVEL, CONF_MAC_ADDRESS, CONF_TEMPERATURE, \ from esphome.const import CONF_BATTERY_LEVEL, CONF_MAC_ADDRESS, CONF_TEMPERATURE, \
UNIT_CELSIUS, ICON_THERMOMETER, UNIT_PERCENT, ICON_WATER_PERCENT, ICON_BATTERY, CONF_ID, \ UNIT_CELSIUS, ICON_THERMOMETER, UNIT_PERCENT, ICON_WATER_PERCENT, ICON_BATTERY, CONF_ID, \
CONF_MOISTURE, CONF_ILLUMINANCE, ICON_BRIGHTNESS_5, UNIT_LUX, CONF_CONDUCTIVITY, \ CONF_MOISTURE, CONF_ILLUMINANCE, ICON_BRIGHTNESS_5, UNIT_LUX, CONF_CONDUCTIVITY, \
@ -12,7 +10,8 @@ DEPENDENCIES = ['esp32_ble_tracker']
AUTO_LOAD = ['xiaomi_ble'] AUTO_LOAD = ['xiaomi_ble']
xiaomi_miflora_ns = cg.esphome_ns.namespace('xiaomi_miflora') xiaomi_miflora_ns = cg.esphome_ns.namespace('xiaomi_miflora')
XiaomiMiflora = xiaomi_miflora_ns.class_('XiaomiMiflora', ESPBTDeviceListener, cg.Component) XiaomiMiflora = xiaomi_miflora_ns.class_('XiaomiMiflora', esp32_ble_tracker.ESPBTDeviceListener,
cg.Component)
CONFIG_SCHEMA = cv.Schema({ CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(XiaomiMiflora), cv.GenerateID(): cv.declare_id(XiaomiMiflora),
@ -23,13 +22,15 @@ CONFIG_SCHEMA = cv.Schema({
cv.Optional(CONF_CONDUCTIVITY): cv.Optional(CONF_CONDUCTIVITY):
sensor.sensor_schema(UNIT_MICROSIEMENS_PER_CENTIMETER, ICON_FLOWER, 0), sensor.sensor_schema(UNIT_MICROSIEMENS_PER_CENTIMETER, ICON_FLOWER, 0),
cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_BATTERY, 0), cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_BATTERY, 0),
}).extend(ESP_BLE_DEVICE_SCHEMA) }).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA)
def to_code(config): def to_code(config):
hub = yield cg.get_variable(config[CONF_ESP32_BLE_ID]) var = cg.new_Pvariable(config[CONF_ID])
var = cg.new_Pvariable(config[CONF_ID], hub, config[CONF_MAC_ADDRESS].as_hex)
yield cg.register_component(var, config) yield cg.register_component(var, config)
yield esp32_ble_tracker.register_ble_device(var, config)
cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
if CONF_TEMPERATURE in config: if CONF_TEMPERATURE in config:
sens = yield sensor.new_sensor(config[CONF_TEMPERATURE]) sens = yield sensor.new_sensor(config[CONF_TEMPERATURE])

View file

@ -12,8 +12,9 @@ namespace xiaomi_miflora {
class XiaomiMiflora : public Component, public esp32_ble_tracker::ESPBTDeviceListener { class XiaomiMiflora : public Component, public esp32_ble_tracker::ESPBTDeviceListener {
public: public:
XiaomiMiflora(esp32_ble_tracker::ESP32BLETracker *parent, uint64_t address) void set_address(uint64_t address) {
: ESPBTDeviceListener(parent), address_(address) {} address_ = address;
}
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override { bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override {
if (device.address_uint64() != this->address_) if (device.address_uint64() != this->address_)

View file

@ -1,8 +1,6 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import sensor from esphome.components import sensor, esp32_ble_tracker
from esphome.components.esp32_ble_tracker import CONF_ESP32_BLE_ID, ESPBTDeviceListener, \
ESP_BLE_DEVICE_SCHEMA
from esphome.const import CONF_BATTERY_LEVEL, CONF_HUMIDITY, CONF_MAC_ADDRESS, CONF_TEMPERATURE, \ from esphome.const import CONF_BATTERY_LEVEL, CONF_HUMIDITY, CONF_MAC_ADDRESS, CONF_TEMPERATURE, \
UNIT_CELSIUS, ICON_THERMOMETER, UNIT_PERCENT, ICON_WATER_PERCENT, ICON_BATTERY, CONF_ID UNIT_CELSIUS, ICON_THERMOMETER, UNIT_PERCENT, ICON_WATER_PERCENT, ICON_BATTERY, CONF_ID
@ -10,7 +8,8 @@ DEPENDENCIES = ['esp32_ble_tracker']
AUTO_LOAD = ['xiaomi_ble'] AUTO_LOAD = ['xiaomi_ble']
xiaomi_mijia_ns = cg.esphome_ns.namespace('xiaomi_mijia') xiaomi_mijia_ns = cg.esphome_ns.namespace('xiaomi_mijia')
XiaomiMijia = xiaomi_mijia_ns.class_('XiaomiMijia', ESPBTDeviceListener, cg.Component) XiaomiMijia = xiaomi_mijia_ns.class_('XiaomiMijia', esp32_ble_tracker.ESPBTDeviceListener,
cg.Component)
CONFIG_SCHEMA = cv.Schema({ CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(XiaomiMijia), cv.GenerateID(): cv.declare_id(XiaomiMijia),
@ -18,13 +17,15 @@ CONFIG_SCHEMA = cv.Schema({
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1),
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1), cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1),
cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_BATTERY, 0), cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_BATTERY, 0),
}).extend(ESP_BLE_DEVICE_SCHEMA) }).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA)
def to_code(config): def to_code(config):
hub = yield cg.get_variable(config[CONF_ESP32_BLE_ID]) var = cg.new_Pvariable(config[CONF_ID])
var = cg.new_Pvariable(config[CONF_ID], hub, config[CONF_MAC_ADDRESS].as_hex)
yield cg.register_component(var, config) yield cg.register_component(var, config)
yield esp32_ble_tracker.register_ble_device(var, config)
cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
if CONF_TEMPERATURE in config: if CONF_TEMPERATURE in config:
sens = yield sensor.new_sensor(config[CONF_TEMPERATURE]) sens = yield sensor.new_sensor(config[CONF_TEMPERATURE])

View file

@ -12,8 +12,9 @@ namespace xiaomi_mijia {
class XiaomiMijia : public Component, public esp32_ble_tracker::ESPBTDeviceListener { class XiaomiMijia : public Component, public esp32_ble_tracker::ESPBTDeviceListener {
public: public:
XiaomiMijia(esp32_ble_tracker::ESP32BLETracker *parent, uint64_t address) void set_address(uint64_t address) {
: ESPBTDeviceListener(parent), address_(address) {} address_ = address;
}
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override { bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override {
if (device.address_uint64() != this->address_) if (device.address_uint64() != this->address_)

View file

@ -317,6 +317,8 @@ CONF_PULSE_LENGTH = 'pulse_length'
CONF_QOS = 'qos' CONF_QOS = 'qos'
CONF_RANDOM = 'random' CONF_RANDOM = 'random'
CONF_RANGE = 'range' CONF_RANGE = 'range'
CONF_RANGE_FROM = 'range_from'
CONF_RANGE_TO = 'range_to'
CONF_RATE = 'rate' CONF_RATE = 'rate'
CONF_RAW = 'raw' CONF_RAW = 'raw'
CONF_REBOOT_TIMEOUT = 'reboot_timeout' CONF_REBOOT_TIMEOUT = 'reboot_timeout'

View file

@ -18,6 +18,7 @@
#define HOT __attribute__((hot)) #define HOT __attribute__((hot))
#define ESPDEPRECATED(msg) __attribute__((deprecated(msg))) #define ESPDEPRECATED(msg) __attribute__((deprecated(msg)))
#define ALWAYS_INLINE __attribute__((always_inline)) #define ALWAYS_INLINE __attribute__((always_inline))
#define PACKED __attribute__((packed))
namespace esphome { namespace esphome {

View file

@ -0,0 +1,79 @@
#!/usr/bin/env python
import codecs
import json
import os
import re
import sys
root_path = os.path.abspath(os.path.normpath(os.path.join(__file__, '..', '..')))
basepath = os.path.join(root_path, 'esphome')
def walk_files(path):
for root, _, files in os.walk(path):
for name in files:
yield os.path.join(root, name)
def shlex_quote(s):
if not s:
return u"''"
if re.search(r'[^\w@%+=:,./-]', s) is None:
return s
return u"'" + s.replace(u"'", u"'\"'\"'") + u"'"
def build_compile_commands():
gcc_flags_json = os.path.join(root_path, '.gcc-flags.json')
if not os.path.isfile(gcc_flags_json):
print("Could not find {} file which is required for clang-tidy.")
print('Please run "pio init --ide atom" in the root esphome folder to generate that file.')
sys.exit(1)
with codecs.open(gcc_flags_json, 'r', encoding='utf-8') as f:
gcc_flags = json.load(f)
exec_path = gcc_flags['execPath']
include_paths = gcc_flags['gccIncludePaths'].split(',')
includes = ['-I{}'.format(p) for p in include_paths]
cpp_flags = gcc_flags['gccDefaultCppFlags'].split(' ')
defines = [flag for flag in cpp_flags if flag.startswith('-D')]
command = [exec_path]
command.extend(includes)
command.extend(defines)
command.append('-std=gnu++11')
command.append('-Wall')
command.append('-Wno-delete-non-virtual-dtor')
command.append('-Wno-unused-variable')
command.append('-Wunreachable-code')
source_files = []
for path in walk_files(basepath):
filetypes = ('.cpp',)
ext = os.path.splitext(path)[1]
if ext in filetypes:
source_files.append(os.path.abspath(path))
source_files.sort()
compile_commands = [{
'directory': root_path,
'command': ' '.join(shlex_quote(x) for x in (command + ['-o', p + '.o', '-c', p])),
'file': p
} for p in source_files]
compile_commands_json = os.path.join(root_path, 'compile_commands.json')
if os.path.isfile(compile_commands_json):
with codecs.open(compile_commands_json, 'r', encoding='utf-8') as f:
try:
if json.load(f) == compile_commands:
return
except:
pass
with codecs.open(compile_commands_json, 'w', encoding='utf-8') as f:
json.dump(compile_commands, f, indent=2)
def main():
build_compile_commands()
print("Done.")
if __name__ == '__main__':
main()

View file

@ -45,6 +45,8 @@ def run_tidy(args, tmpdir, queue, lock, failed_files):
invocation.append('-p=.') invocation.append('-p=.')
if args.quiet: if args.quiet:
invocation.append('-quiet') invocation.append('-quiet')
for arg in ['-Wfor-loop-analysis', '-Wshadow-field', '-Wshadow-field-in-constructor']:
invocation.append('-extra-arg={}'.format(arg))
invocation.append(os.path.abspath(path)) invocation.append(os.path.abspath(path))
invocation_s = ' '.join(shlex_quote(x) for x in invocation) invocation_s = ' '.join(shlex_quote(x) for x in invocation)
@ -135,9 +137,6 @@ def build_compile_commands():
command.append('-Wall') command.append('-Wall')
command.append('-Wno-delete-non-virtual-dtor') command.append('-Wno-delete-non-virtual-dtor')
command.append('-Wno-unused-variable') command.append('-Wno-unused-variable')
command.append('-Wfor-loop-analysis')
command.append('-Wshadow-field')
command.append('-Wshadow-field-in-constructor')
command.append('-Wunreachable-code') command.append('-Wunreachable-code')
source_files = [] source_files = []