diff --git a/esphome/components/binary_sensor/__init__.py b/esphome/components/binary_sensor/__init__.py index 9f92207b19..c391e12895 100644 --- a/esphome/components/binary_sensor/__init__.py +++ b/esphome/components/binary_sensor/__init__.py @@ -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): diff --git a/esphome/components/binary_sensor/filter.cpp b/esphome/components/binary_sensor/filter.cpp index b7ac2c4a79..f4612d62e9 100644 --- a/esphome/components/binary_sensor/filter.cpp +++ b/esphome/components/binary_sensor/filter.cpp @@ -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 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 DelayedOnFilter::new_value(bool value, bool is_initial) { if (value) { @@ -46,6 +59,7 @@ optional DelayedOffFilter::new_value(bool value, bool is_initial) { return true; } } + float DelayedOffFilter::get_setup_priority() const { return setup_priority::HARDWARE; } optional InvertFilter::new_value(bool value, bool is_initial) { return !value; } diff --git a/esphome/components/binary_sensor/filter.h b/esphome/components/binary_sensor/filter.h index d1e9a0d23a..0b54251cda 100644 --- a/esphome/components/binary_sensor/filter.h +++ b/esphome/components/binary_sensor/filter.h @@ -25,6 +25,18 @@ class Filter { Deduplicator dedup_; }; +class DelayedOnOffFilter : public Filter, public Component { + public: + explicit DelayedOnOffFilter(uint32_t delay); + + optional 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); diff --git a/esphome/components/gps/gps.cpp b/esphome/components/gps/gps.cpp index 0391a9a955..26371565f3 100644 --- a/esphome/components/gps/gps.cpp +++ b/esphome/components/gps/gps.cpp @@ -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 diff --git a/esphome/components/gps/gps.h b/esphome/components/gps/gps.h index 7d845d1bed..84a9248bc6 100644 --- a/esphome/components/gps/gps.h +++ b/esphome/components/gps/gps.h @@ -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: diff --git a/esphome/components/gps/time/gps_time.cpp b/esphome/components/gps/time/gps_time.cpp index c6aa8adc67..468ad09bac 100644 --- a/esphome/components/gps/time/gps_time.cpp +++ b/esphome/components/gps/time/gps_time.cpp @@ -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 diff --git a/esphome/components/gps/time/gps_time.h b/esphome/components/gps/time/gps_time.h index b09aee364f..f6462be3e0 100644 --- a/esphome/components/gps/time/gps_time.h +++ b/esphome/components/gps/time/gps_time.h @@ -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}; }; diff --git a/esphome/components/time/__init__.py b/esphome/components/time/__init__.py index 2097be3a26..ca1ac375ba 100644 --- a/esphome/components/time/__init__.py +++ b/esphome/components/time/__init__.py @@ -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 diff --git a/esphome/components/time/real_time_clock.cpp b/esphome/components/time/real_time_clock.cpp index 96722229b1..cb66dc3ce6 100644 --- a/esphome/components/time/real_time_clock.cpp +++ b/esphome/components/time/real_time_clock.cpp @@ -84,12 +84,12 @@ template 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; diff --git a/esphome/components/time_based/cover.py b/esphome/components/time_based/cover.py index 85f606e6cc..6a7c9b6835 100644 --- a/esphome/components/time_based/cover.py +++ b/esphome/components/time_based/cover.py @@ -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])) diff --git a/esphome/components/time_based/time_based_cover.cpp b/esphome/components/time_based/time_based_cover.cpp index bbc887debc..c353b552d3 100644 --- a/esphome/components/time_based/time_based_cover.cpp +++ b/esphome/components/time_based/time_based_cover.cpp @@ -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_()) { - this->start_direction_(COVER_OPERATION_IDLE); + 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_(); diff --git a/esphome/components/time_based/time_based_cover.h b/esphome/components/time_based/time_based_cover.h index 60819d797b..be3a55c546 100644 --- a/esphome/components/time_based/time_based_cover.h +++ b/esphome/components/time_based/time_based_cover.h @@ -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 diff --git a/esphome/components/uart/uart.cpp b/esphome/components/uart/uart.cpp index 5e0bc3277e..ea15af5053 100644 --- a/esphome/components/uart/uart.cpp +++ b/esphome/components/uart/uart.cpp @@ -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(); } diff --git a/esphome/components/uart/uart.h b/esphome/components/uart/uart.h index f642d4ee81..93caaf3006 100644 --- a/esphome/components/uart/uart.h +++ b/esphome/components/uart/uart.h @@ -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}; diff --git a/esphome/components/xiaomi_ble/xiaomi_ble.cpp b/esphome/components/xiaomi_ble/xiaomi_ble.cpp index 4367b5fd1e..5bb6709e5f 100644 --- a/esphome/components/xiaomi_ble/xiaomi_ble.cpp +++ b/esphome/components/xiaomi_ble/xiaomi_ble.cpp @@ -83,8 +83,9 @@ optional 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 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()); diff --git a/esphome/components/xiaomi_ble/xiaomi_ble.h b/esphome/components/xiaomi_ble/xiaomi_ble.h index 058a89927b..b8b602ecef 100644 --- a/esphome/components/xiaomi_ble/xiaomi_ble.h +++ b/esphome/components/xiaomi_ble/xiaomi_ble.h @@ -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 temperature; optional humidity; optional battery_level; diff --git a/esphome/components/xiaomi_lywsd02/__init__.py b/esphome/components/xiaomi_lywsd02/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/xiaomi_lywsd02/sensor.py b/esphome/components/xiaomi_lywsd02/sensor.py new file mode 100644 index 0000000000..8e4d59316b --- /dev/null +++ b/esphome/components/xiaomi_lywsd02/sensor.py @@ -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)) diff --git a/esphome/components/xiaomi_lywsd02/xiaomi_lywsd02.cpp b/esphome/components/xiaomi_lywsd02/xiaomi_lywsd02.cpp new file mode 100644 index 0000000000..cd77c133a5 --- /dev/null +++ b/esphome/components/xiaomi_lywsd02/xiaomi_lywsd02.cpp @@ -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 diff --git a/esphome/components/xiaomi_lywsd02/xiaomi_lywsd02.h b/esphome/components/xiaomi_lywsd02/xiaomi_lywsd02.h new file mode 100644 index 0000000000..9b8aba1bb0 --- /dev/null +++ b/esphome/components/xiaomi_lywsd02/xiaomi_lywsd02.h @@ -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 diff --git a/esphome/dashboard/static/fonts/LICENSE b/esphome/dashboard/static/fonts/LICENSE new file mode 100644 index 0000000000..7a4a3ea242 --- /dev/null +++ b/esphome/dashboard/static/fonts/LICENSE @@ -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. \ No newline at end of file diff --git a/esphome/dashboard/static/fonts/MaterialIcons-Regular.eot b/esphome/dashboard/static/fonts/MaterialIcons-Regular.eot deleted file mode 100644 index 70508ebabc..0000000000 Binary files a/esphome/dashboard/static/fonts/MaterialIcons-Regular.eot and /dev/null differ diff --git a/esphome/dashboard/static/fonts/MaterialIcons-Regular.ttf b/esphome/dashboard/static/fonts/MaterialIcons-Regular.ttf deleted file mode 100644 index 7015564ad1..0000000000 Binary files a/esphome/dashboard/static/fonts/MaterialIcons-Regular.ttf and /dev/null differ diff --git a/esphome/dashboard/static/fonts/material-icons.css b/esphome/dashboard/static/fonts/material-icons.css index 2270c09d01..51f2e0a0d1 100644 --- a/esphome/dashboard/static/fonts/material-icons.css +++ b/esphome/dashboard/static/fonts/material-icons.css @@ -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 { diff --git a/script/ci-custom.py b/script/ci-custom.py index 9827248480..5ea7229863 100755 --- a/script/ci-custom.py +++ b/script/ci-custom.py @@ -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 = []