mirror of
https://github.com/esphome/esphome.git
synced 2024-11-10 09:17:46 +01:00
Merge branch 'dev' of https://github.com/esphome/esphome into dev
This commit is contained in:
commit
5dfb33ebee
25 changed files with 454 additions and 48 deletions
|
@ -41,6 +41,7 @@ BinarySensorCondition = binary_sensor_ns.class_('BinarySensorCondition', Conditi
|
|||
|
||||
# Filters
|
||||
Filter = binary_sensor_ns.class_('Filter')
|
||||
DelayedOnOffFilter = binary_sensor_ns.class_('DelayedOnOffFilter', Filter, cg.Component)
|
||||
DelayedOnFilter = binary_sensor_ns.class_('DelayedOnFilter', Filter, cg.Component)
|
||||
DelayedOffFilter = binary_sensor_ns.class_('DelayedOffFilter', Filter, cg.Component)
|
||||
InvertFilter = binary_sensor_ns.class_('InvertFilter', Filter)
|
||||
|
@ -55,6 +56,14 @@ def invert_filter_to_code(config, filter_id):
|
|||
yield cg.new_Pvariable(filter_id)
|
||||
|
||||
|
||||
@FILTER_REGISTRY.register('delayed_on_off', DelayedOnOffFilter,
|
||||
cv.positive_time_period_milliseconds)
|
||||
def delayed_on_off_filter_to_code(config, filter_id):
|
||||
var = cg.new_Pvariable(filter_id, config)
|
||||
yield cg.register_component(var, {})
|
||||
yield var
|
||||
|
||||
|
||||
@FILTER_REGISTRY.register('delayed_on', DelayedOnFilter,
|
||||
cv.positive_time_period_milliseconds)
|
||||
def delayed_on_filter_to_code(config, filter_id):
|
||||
|
|
|
@ -23,6 +23,19 @@ void Filter::input(bool value, bool is_initial) {
|
|||
this->output(*b, is_initial);
|
||||
}
|
||||
}
|
||||
|
||||
DelayedOnOffFilter::DelayedOnOffFilter(uint32_t delay) : delay_(delay) {}
|
||||
optional<bool> DelayedOnOffFilter::new_value(bool value, bool is_initial) {
|
||||
if (value) {
|
||||
this->set_timeout("ON_OFF", this->delay_, [this, is_initial]() { this->output(true, is_initial); });
|
||||
} else {
|
||||
this->set_timeout("ON_OFF", this->delay_, [this, is_initial]() { this->output(false, is_initial); });
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
float DelayedOnOffFilter::get_setup_priority() const { return setup_priority::HARDWARE; }
|
||||
|
||||
DelayedOnFilter::DelayedOnFilter(uint32_t delay) : delay_(delay) {}
|
||||
optional<bool> DelayedOnFilter::new_value(bool value, bool is_initial) {
|
||||
if (value) {
|
||||
|
@ -46,6 +59,7 @@ optional<bool> DelayedOffFilter::new_value(bool value, bool is_initial) {
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
float DelayedOffFilter::get_setup_priority() const { return setup_priority::HARDWARE; }
|
||||
|
||||
optional<bool> InvertFilter::new_value(bool value, bool is_initial) { return !value; }
|
||||
|
|
|
@ -25,6 +25,18 @@ class Filter {
|
|||
Deduplicator<bool> dedup_;
|
||||
};
|
||||
|
||||
class DelayedOnOffFilter : public Filter, public Component {
|
||||
public:
|
||||
explicit DelayedOnOffFilter(uint32_t delay);
|
||||
|
||||
optional<bool> new_value(bool value, bool is_initial) override;
|
||||
|
||||
float get_setup_priority() const override;
|
||||
|
||||
protected:
|
||||
uint32_t delay_;
|
||||
};
|
||||
|
||||
class DelayedOnFilter : public Filter, public Component {
|
||||
public:
|
||||
explicit DelayedOnFilter(uint32_t delay);
|
||||
|
|
|
@ -8,5 +8,41 @@ static const char *TAG = "gps";
|
|||
|
||||
TinyGPSPlus &GPSListener::get_tiny_gps() { return this->parent_->get_tiny_gps(); }
|
||||
|
||||
void GPS::loop() {
|
||||
while (this->available() && !this->has_time_) {
|
||||
if (this->tiny_gps_.encode(this->read())) {
|
||||
if (tiny_gps_.location.isUpdated()) {
|
||||
ESP_LOGD(TAG, "Location:");
|
||||
ESP_LOGD(TAG, " Lat: %f", tiny_gps_.location.lat());
|
||||
ESP_LOGD(TAG, " Lon: %f", tiny_gps_.location.lng());
|
||||
}
|
||||
|
||||
if (tiny_gps_.speed.isUpdated()) {
|
||||
ESP_LOGD(TAG, "Speed:");
|
||||
ESP_LOGD(TAG, " %f km/h", tiny_gps_.speed.kmph());
|
||||
}
|
||||
if (tiny_gps_.course.isUpdated()) {
|
||||
ESP_LOGD(TAG, "Course:");
|
||||
ESP_LOGD(TAG, " %f °", tiny_gps_.course.deg());
|
||||
}
|
||||
if (tiny_gps_.altitude.isUpdated()) {
|
||||
ESP_LOGD(TAG, "Altitude:");
|
||||
ESP_LOGD(TAG, " %f m", tiny_gps_.altitude.meters());
|
||||
}
|
||||
if (tiny_gps_.satellites.isUpdated()) {
|
||||
ESP_LOGD(TAG, "Satellites:");
|
||||
ESP_LOGD(TAG, " %d", tiny_gps_.satellites.value());
|
||||
}
|
||||
if (tiny_gps_.satellites.isUpdated()) {
|
||||
ESP_LOGD(TAG, "HDOP:");
|
||||
ESP_LOGD(TAG, " %.2f", tiny_gps_.hdop.hdop());
|
||||
}
|
||||
|
||||
for (auto *listener : this->listeners_)
|
||||
listener->on_update(this->tiny_gps_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gps
|
||||
} // namespace esphome
|
||||
|
|
|
@ -27,14 +27,7 @@ class GPS : public Component, public uart::UARTDevice {
|
|||
this->listeners_.push_back(listener);
|
||||
}
|
||||
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
||||
void loop() override {
|
||||
while (this->available() && !this->has_time_) {
|
||||
if (this->tiny_gps_.encode(this->read())) {
|
||||
for (auto *listener : this->listeners_)
|
||||
listener->on_update(this->tiny_gps_);
|
||||
}
|
||||
}
|
||||
}
|
||||
void loop() override;
|
||||
TinyGPSPlus &get_tiny_gps() { return this->tiny_gps_; }
|
||||
|
||||
protected:
|
||||
|
|
|
@ -6,5 +6,29 @@ namespace gps {
|
|||
|
||||
static const char *TAG = "gps.time";
|
||||
|
||||
void GPSTime::from_tiny_gps_(TinyGPSPlus &tiny_gps) {
|
||||
if (!tiny_gps.time.isValid() || !tiny_gps.date.isValid())
|
||||
return;
|
||||
if (!tiny_gps.time.isUpdated() || !tiny_gps.date.isUpdated())
|
||||
return;
|
||||
if (tiny_gps.date.year() < 2019)
|
||||
return;
|
||||
|
||||
time::ESPTime val{};
|
||||
val.year = tiny_gps.date.year();
|
||||
val.month = tiny_gps.date.month();
|
||||
val.day_of_month = tiny_gps.date.day();
|
||||
// Set these to valid value for recalc_timestamp_utc - it's not used for calculation
|
||||
val.day_of_week = 1;
|
||||
val.day_of_year = 1;
|
||||
|
||||
val.hour = tiny_gps.time.hour();
|
||||
val.minute = tiny_gps.time.minute();
|
||||
val.second = tiny_gps.time.second();
|
||||
val.recalc_timestamp_utc(false);
|
||||
this->synchronize_epoch_(val.timestamp);
|
||||
this->has_time_ = true;
|
||||
}
|
||||
|
||||
} // namespace gps
|
||||
} // namespace esphome
|
||||
|
|
|
@ -18,20 +18,7 @@ class GPSTime : public time::RealTimeClock, public GPSListener {
|
|||
}
|
||||
|
||||
protected:
|
||||
void from_tiny_gps_(TinyGPSPlus &tiny_gps) {
|
||||
if (!tiny_gps.time.isValid() || !tiny_gps.date.isValid())
|
||||
return;
|
||||
time::ESPTime val{};
|
||||
val.year = tiny_gps.date.year();
|
||||
val.month = tiny_gps.date.month();
|
||||
val.day_of_month = tiny_gps.date.day();
|
||||
val.hour = tiny_gps.time.hour();
|
||||
val.minute = tiny_gps.time.minute();
|
||||
val.second = tiny_gps.time.second();
|
||||
val.recalc_timestamp_utc(false);
|
||||
this->synchronize_epoch_(val.timestamp);
|
||||
this->has_time_ = true;
|
||||
}
|
||||
void from_tiny_gps_(TinyGPSPlus &tiny_gps);
|
||||
bool has_time_{false};
|
||||
};
|
||||
|
||||
|
|
|
@ -115,8 +115,9 @@ def convert_tz(pytz_obj):
|
|||
_tz_dst_str(dst_begins_local), _tz_dst_str(dst_ends_local))
|
||||
_LOGGER.info("Detected timezone '%s' with UTC offset %s and daylight savings time from "
|
||||
"%s to %s",
|
||||
tzname_off, _tz_timedelta(utcoffset_off), dst_begins_local.strftime("%x %X"),
|
||||
dst_ends_local.strftime("%x %X"))
|
||||
tzname_off, _tz_timedelta(utcoffset_off),
|
||||
dst_begins_local.strftime("%d %B %X"),
|
||||
dst_ends_local.strftime("%d %B %X"))
|
||||
return tzbase + tzext
|
||||
|
||||
|
||||
|
|
|
@ -84,12 +84,12 @@ template<typename T> bool increment_time_value(T ¤t, uint16_t begin, uint1
|
|||
|
||||
static bool is_leap_year(uint32_t year) { return (year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0); }
|
||||
|
||||
static bool days_in_month(uint8_t month, uint16_t year) {
|
||||
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_in_month = DAYS_IN_MONTH[month];
|
||||
uint8_t days = DAYS_IN_MONTH[month];
|
||||
if (month == 2 && is_leap_year(year))
|
||||
days_in_month = 29;
|
||||
return days_in_month;
|
||||
return 29;
|
||||
return days;
|
||||
}
|
||||
|
||||
void ESPTime::increment_second() {
|
||||
|
@ -127,13 +127,13 @@ void ESPTime::recalc_timestamp_utc(bool use_day_of_year) {
|
|||
return;
|
||||
}
|
||||
|
||||
for (uint16_t i = 1970; i < this->year; i++)
|
||||
for (int i = 1970; i < this->year; i++)
|
||||
res += is_leap_year(i) ? 366 : 365;
|
||||
|
||||
if (use_day_of_year) {
|
||||
res += this->day_of_year - 1;
|
||||
} else {
|
||||
for (uint8_t i = 1; i < this->month; ++i)
|
||||
for (int i = 1; i < this->month; i++)
|
||||
res += days_in_month(i, this->year);
|
||||
|
||||
res += this->day_of_month - 1;
|
||||
|
|
|
@ -8,6 +8,8 @@ from esphome.const import CONF_CLOSE_ACTION, CONF_CLOSE_DURATION, CONF_ID, CONF_
|
|||
time_based_ns = cg.esphome_ns.namespace('time_based')
|
||||
TimeBasedCover = time_based_ns.class_('TimeBasedCover', cover.Cover, cg.Component)
|
||||
|
||||
CONF_HAS_BUILT_IN_ENDSTOP = 'has_built_in_endstop'
|
||||
|
||||
CONFIG_SCHEMA = cover.COVER_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_id(TimeBasedCover),
|
||||
cv.Required(CONF_STOP_ACTION): automation.validate_automation(single=True),
|
||||
|
@ -17,6 +19,8 @@ CONFIG_SCHEMA = cover.COVER_SCHEMA.extend({
|
|||
|
||||
cv.Required(CONF_CLOSE_ACTION): automation.validate_automation(single=True),
|
||||
cv.Required(CONF_CLOSE_DURATION): cv.positive_time_period_milliseconds,
|
||||
|
||||
cv.Optional(CONF_HAS_BUILT_IN_ENDSTOP, default=False): cv.boolean,
|
||||
}).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
|
||||
|
@ -32,3 +36,5 @@ def to_code(config):
|
|||
|
||||
cg.add(var.set_close_duration(config[CONF_CLOSE_DURATION]))
|
||||
yield automation.build_automation(var.get_close_trigger(), [], config[CONF_CLOSE_ACTION])
|
||||
|
||||
cg.add(var.set_has_built_in_endstop(config[CONF_HAS_BUILT_IN_ENDSTOP]))
|
||||
|
|
|
@ -30,13 +30,18 @@ void TimeBasedCover::loop() {
|
|||
// Recompute position every loop cycle
|
||||
this->recompute_position_();
|
||||
|
||||
if (this->current_operation != COVER_OPERATION_IDLE && this->is_at_target_()) {
|
||||
if (this->is_at_target_()) {
|
||||
if (this->has_built_in_endstop_ && (this->target_position_ == COVER_OPEN || this->target_position_ == COVER_CLOSED)) {
|
||||
// Don't trigger stop, let the cover stop by itself.
|
||||
this->current_operation = COVER_OPERATION_IDLE;
|
||||
} else {
|
||||
this->start_direction_(COVER_OPERATION_IDLE);
|
||||
}
|
||||
this->publish_state();
|
||||
}
|
||||
|
||||
// Send current position every second
|
||||
if (this->current_operation != COVER_OPERATION_IDLE && now - this->last_publish_time_ > 1000) {
|
||||
if (now - this->last_publish_time_ > 1000) {
|
||||
this->publish_state(false);
|
||||
this->last_publish_time_ = now;
|
||||
}
|
||||
|
@ -57,6 +62,12 @@ void TimeBasedCover::control(const CoverCall &call) {
|
|||
auto pos = *call.get_position();
|
||||
if (pos == this->position) {
|
||||
// already at target
|
||||
// for covers with built in end stop, we should send the command again
|
||||
if (this->has_built_in_endstop_ && (pos == COVER_OPEN || pos == COVER_CLOSED)) {
|
||||
auto op = pos == COVER_CLOSED ? COVER_OPERATION_CLOSING : COVER_OPERATION_OPENING;
|
||||
this->target_position_ = pos;
|
||||
this->start_direction_(op);
|
||||
}
|
||||
} else {
|
||||
auto op = pos < this->position ? COVER_OPERATION_CLOSING : COVER_OPERATION_OPENING;
|
||||
this->target_position_ = pos;
|
||||
|
@ -82,7 +93,7 @@ bool TimeBasedCover::is_at_target_() const {
|
|||
}
|
||||
}
|
||||
void TimeBasedCover::start_direction_(CoverOperation dir) {
|
||||
if (dir == this->current_operation)
|
||||
if (dir == this->current_operation && dir != COVER_OPERATION_IDLE)
|
||||
return;
|
||||
|
||||
this->recompute_position_();
|
||||
|
|
|
@ -20,6 +20,7 @@ class TimeBasedCover : public cover::Cover, public Component {
|
|||
void set_open_duration(uint32_t open_duration) { this->open_duration_ = open_duration; }
|
||||
void set_close_duration(uint32_t close_duration) { this->close_duration_ = close_duration; }
|
||||
cover::CoverTraits get_traits() override;
|
||||
void set_has_built_in_endstop(bool value) { this->has_built_in_endstop_ = value; }
|
||||
|
||||
protected:
|
||||
void control(const cover::CoverCall &call) override;
|
||||
|
@ -41,6 +42,7 @@ class TimeBasedCover : public cover::Cover, public Component {
|
|||
uint32_t start_dir_time_{0};
|
||||
uint32_t last_publish_time_{0};
|
||||
float target_position_{0};
|
||||
bool has_built_in_endstop_{false};
|
||||
};
|
||||
|
||||
} // namespace time_based
|
||||
|
|
|
@ -291,12 +291,12 @@ void ICACHE_RAM_ATTR HOT ESP8266SoftwareSerial::write_byte(uint8_t data) {
|
|||
this->write_bit_(true, &wait, start);
|
||||
enable_interrupts();
|
||||
}
|
||||
void ESP8266SoftwareSerial::wait_(uint32_t *wait, const uint32_t &start) {
|
||||
void ICACHE_RAM_ATTR ESP8266SoftwareSerial::wait_(uint32_t *wait, const uint32_t &start) {
|
||||
while (ESP.getCycleCount() - start < *wait)
|
||||
;
|
||||
*wait += this->bit_time_;
|
||||
}
|
||||
bool ESP8266SoftwareSerial::read_bit_(uint32_t *wait, const uint32_t &start) {
|
||||
bool ICACHE_RAM_ATTR ESP8266SoftwareSerial::read_bit_(uint32_t *wait, const uint32_t &start) {
|
||||
this->wait_(wait, start);
|
||||
return this->rx_pin_->digital_read();
|
||||
}
|
||||
|
|
|
@ -24,9 +24,9 @@ class ESP8266SoftwareSerial {
|
|||
protected:
|
||||
static void gpio_intr(ESP8266SoftwareSerial *arg);
|
||||
|
||||
inline void wait_(uint32_t *wait, const uint32_t &start);
|
||||
inline bool read_bit_(uint32_t *wait, const uint32_t &start);
|
||||
inline void write_bit_(bool bit, uint32_t *wait, const uint32_t &start);
|
||||
void wait_(uint32_t *wait, const uint32_t &start);
|
||||
bool read_bit_(uint32_t *wait, const uint32_t &start);
|
||||
void write_bit_(bool bit, uint32_t *wait, const uint32_t &start);
|
||||
|
||||
uint32_t bit_time_{0};
|
||||
uint8_t *rx_buffer_{nullptr};
|
||||
|
|
|
@ -83,8 +83,9 @@ optional<XiaomiParseResult> parse_xiaomi(const esp32_ble_tracker::ESPBTDevice &d
|
|||
|
||||
bool is_mijia = (raw[1] & 0x20) == 0x20 && raw[2] == 0xAA && raw[3] == 0x01;
|
||||
bool is_miflora = (raw[1] & 0x20) == 0x20 && raw[2] == 0x98 && raw[3] == 0x00;
|
||||
bool is_lywsd02 = (raw[1] & 0x20) == 0x20 && raw[2] == 0x5b && raw[3] == 0x04;
|
||||
|
||||
if (!is_mijia && !is_miflora) {
|
||||
if (!is_mijia && !is_miflora && !is_lywsd02) {
|
||||
// ESP_LOGVV(TAG, "Xiaomi no magic bytes");
|
||||
return {};
|
||||
}
|
||||
|
@ -101,7 +102,12 @@ optional<XiaomiParseResult> parse_xiaomi(const esp32_ble_tracker::ESPBTDevice &d
|
|||
return {};
|
||||
}
|
||||
XiaomiParseResult result;
|
||||
result.type = is_miflora ? XiaomiParseResult::TYPE_MIFLORA : XiaomiParseResult::TYPE_MIJIA;
|
||||
result.type = XiaomiParseResult::TYPE_MIFLORA;
|
||||
if (is_mijia) {
|
||||
result.type = XiaomiParseResult::TYPE_MIJIA;
|
||||
} else if (is_lywsd02) {
|
||||
result.type = XiaomiParseResult::TYPE_LYWSD02;
|
||||
}
|
||||
bool success = parse_xiaomi_data_byte(raw_type, data, data_length, result);
|
||||
if (!success)
|
||||
return {};
|
||||
|
@ -113,7 +119,12 @@ bool XiaomiListener::parse_device(const esp32_ble_tracker::ESPBTDevice &device)
|
|||
if (!res.has_value())
|
||||
return false;
|
||||
|
||||
const char *name = res->type == XiaomiParseResult::TYPE_MIFLORA ? "Mi Flora" : "Mi Jia";
|
||||
const char *name = "Mi Flora";
|
||||
if (res->type == XiaomiParseResult::TYPE_MIJIA) {
|
||||
name = "Mi Jia";
|
||||
} else if (res->type == XiaomiParseResult::TYPE_LYWSD02) {
|
||||
name = "LYWSD02";
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Got Xiaomi %s (%s):", name, device.address_str().c_str());
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ namespace esphome {
|
|||
namespace xiaomi_ble {
|
||||
|
||||
struct XiaomiParseResult {
|
||||
enum { TYPE_MIJIA, TYPE_MIFLORA } type;
|
||||
enum { TYPE_MIJIA, TYPE_MIFLORA, TYPE_LYWSD02 } type;
|
||||
optional<float> temperature;
|
||||
optional<float> humidity;
|
||||
optional<float> battery_level;
|
||||
|
|
0
esphome/components/xiaomi_lywsd02/__init__.py
Normal file
0
esphome/components/xiaomi_lywsd02/__init__.py
Normal file
34
esphome/components/xiaomi_lywsd02/sensor.py
Normal file
34
esphome/components/xiaomi_lywsd02/sensor.py
Normal file
|
@ -0,0 +1,34 @@
|
|||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor, esp32_ble_tracker
|
||||
from esphome.const import CONF_HUMIDITY, CONF_MAC_ADDRESS, CONF_TEMPERATURE, \
|
||||
UNIT_CELSIUS, ICON_THERMOMETER, UNIT_PERCENT, ICON_WATER_PERCENT, CONF_ID
|
||||
|
||||
DEPENDENCIES = ['esp32_ble_tracker']
|
||||
AUTO_LOAD = ['xiaomi_ble']
|
||||
|
||||
xiaomi_lywsd02_ns = cg.esphome_ns.namespace('xiaomi_lywsd02')
|
||||
XiaomiLYWSD02 = xiaomi_lywsd02_ns.class_('XiaomiLYWSD02', esp32_ble_tracker.ESPBTDeviceListener,
|
||||
cg.Component)
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema({
|
||||
cv.GenerateID(): cv.declare_id(XiaomiLYWSD02),
|
||||
cv.Required(CONF_MAC_ADDRESS): cv.mac_address,
|
||||
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),
|
||||
}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
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:
|
||||
sens = yield sensor.new_sensor(config[CONF_TEMPERATURE])
|
||||
cg.add(var.set_temperature(sens))
|
||||
if CONF_HUMIDITY in config:
|
||||
sens = yield sensor.new_sensor(config[CONF_HUMIDITY])
|
||||
cg.add(var.set_humidity(sens))
|
20
esphome/components/xiaomi_lywsd02/xiaomi_lywsd02.cpp
Normal file
20
esphome/components/xiaomi_lywsd02/xiaomi_lywsd02.cpp
Normal file
|
@ -0,0 +1,20 @@
|
|||
#include "xiaomi_lywsd02.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace xiaomi_lywsd02 {
|
||||
|
||||
static const char *TAG = "xiaomi_lywsd02";
|
||||
|
||||
void XiaomiLYWSD02::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "Xiaomi LYWSD02");
|
||||
LOG_SENSOR(" ", "Temperature", this->temperature_);
|
||||
LOG_SENSOR(" ", "Humidity", this->humidity_);
|
||||
}
|
||||
|
||||
} // namespace xiaomi_lywsd02
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
46
esphome/components/xiaomi_lywsd02/xiaomi_lywsd02.h
Normal file
46
esphome/components/xiaomi_lywsd02/xiaomi_lywsd02.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||
#include "esphome/components/xiaomi_ble/xiaomi_ble.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace xiaomi_lywsd02 {
|
||||
|
||||
class XiaomiLYWSD02 : public Component, public esp32_ble_tracker::ESPBTDeviceListener {
|
||||
public:
|
||||
void set_address(uint64_t address) { address_ = address; }
|
||||
|
||||
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override {
|
||||
if (device.address_uint64() != this->address_)
|
||||
return false;
|
||||
|
||||
auto res = xiaomi_ble::parse_xiaomi(device);
|
||||
if (!res.has_value())
|
||||
return false;
|
||||
|
||||
if (res->temperature.has_value() && this->temperature_ != nullptr)
|
||||
this->temperature_->publish_state(*res->temperature);
|
||||
if (res->humidity.has_value() && this->humidity_ != nullptr)
|
||||
this->humidity_->publish_state(*res->humidity);
|
||||
return true;
|
||||
}
|
||||
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
void set_temperature(sensor::Sensor *temperature) { temperature_ = temperature; }
|
||||
void set_humidity(sensor::Sensor *humidity) { humidity_ = humidity; }
|
||||
|
||||
protected:
|
||||
uint64_t address_;
|
||||
sensor::Sensor *temperature_{nullptr};
|
||||
sensor::Sensor *humidity_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace xiaomi_lywsd02
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
202
esphome/dashboard/static/fonts/LICENSE
Normal file
202
esphome/dashboard/static/fonts/LICENSE
Normal file
|
@ -0,0 +1,202 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
Binary file not shown.
Binary file not shown.
|
@ -2,12 +2,10 @@
|
|||
font-family: 'Material Icons';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url(MaterialIcons-Regular.eot); /* For IE6-8 */
|
||||
src: local('Material Icons'),
|
||||
local('MaterialIcons-Regular'),
|
||||
url(MaterialIcons-Regular.woff2) format('woff2'),
|
||||
url(MaterialIcons-Regular.woff) format('woff'),
|
||||
url(MaterialIcons-Regular.ttf) format('truetype');
|
||||
url(MaterialIcons-Regular.woff) format('woff');
|
||||
}
|
||||
|
||||
.material-icons {
|
||||
|
|
|
@ -33,9 +33,9 @@ files.sort()
|
|||
|
||||
file_types = ('.h', '.c', '.cpp', '.tcc', '.yaml', '.yml', '.ini', '.txt', '.ico', '.svg',
|
||||
'.py', '.html', '.js', '.md', '.sh', '.css', '.proto', '.conf', '.cfg',
|
||||
'.eot', '.ttf', '.woff', '.woff2')
|
||||
'.woff', '.woff2', '')
|
||||
cpp_include = ('*.h', '*.c', '*.cpp', '*.tcc')
|
||||
ignore_types = ('.ico', '.eot', '.ttf', '.woff', '.woff2')
|
||||
ignore_types = ('.ico', '.woff', '.woff2', '')
|
||||
|
||||
LINT_FILE_CHECKS = []
|
||||
LINT_CONTENT_CHECKS = []
|
||||
|
|
Loading…
Reference in a new issue