mirror of
https://github.com/esphome/esphome.git
synced 2024-11-26 17:05:21 +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
|
# Filters
|
||||||
Filter = binary_sensor_ns.class_('Filter')
|
Filter = binary_sensor_ns.class_('Filter')
|
||||||
|
DelayedOnOffFilter = binary_sensor_ns.class_('DelayedOnOffFilter', Filter, cg.Component)
|
||||||
DelayedOnFilter = binary_sensor_ns.class_('DelayedOnFilter', Filter, cg.Component)
|
DelayedOnFilter = binary_sensor_ns.class_('DelayedOnFilter', Filter, cg.Component)
|
||||||
DelayedOffFilter = binary_sensor_ns.class_('DelayedOffFilter', Filter, cg.Component)
|
DelayedOffFilter = binary_sensor_ns.class_('DelayedOffFilter', Filter, cg.Component)
|
||||||
InvertFilter = binary_sensor_ns.class_('InvertFilter', Filter)
|
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)
|
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,
|
@FILTER_REGISTRY.register('delayed_on', DelayedOnFilter,
|
||||||
cv.positive_time_period_milliseconds)
|
cv.positive_time_period_milliseconds)
|
||||||
def delayed_on_filter_to_code(config, filter_id):
|
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);
|
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) {}
|
DelayedOnFilter::DelayedOnFilter(uint32_t delay) : delay_(delay) {}
|
||||||
optional<bool> DelayedOnFilter::new_value(bool value, bool is_initial) {
|
optional<bool> DelayedOnFilter::new_value(bool value, bool is_initial) {
|
||||||
if (value) {
|
if (value) {
|
||||||
|
@ -46,6 +59,7 @@ optional<bool> DelayedOffFilter::new_value(bool value, bool is_initial) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float DelayedOffFilter::get_setup_priority() const { return setup_priority::HARDWARE; }
|
float DelayedOffFilter::get_setup_priority() const { return setup_priority::HARDWARE; }
|
||||||
|
|
||||||
optional<bool> InvertFilter::new_value(bool value, bool is_initial) { return !value; }
|
optional<bool> InvertFilter::new_value(bool value, bool is_initial) { return !value; }
|
||||||
|
|
|
@ -25,6 +25,18 @@ class Filter {
|
||||||
Deduplicator<bool> dedup_;
|
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 {
|
class DelayedOnFilter : public Filter, public Component {
|
||||||
public:
|
public:
|
||||||
explicit DelayedOnFilter(uint32_t delay);
|
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(); }
|
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 gps
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -27,14 +27,7 @@ class GPS : public Component, public uart::UARTDevice {
|
||||||
this->listeners_.push_back(listener);
|
this->listeners_.push_back(listener);
|
||||||
}
|
}
|
||||||
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
||||||
void loop() override {
|
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_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TinyGPSPlus &get_tiny_gps() { return this->tiny_gps_; }
|
TinyGPSPlus &get_tiny_gps() { return this->tiny_gps_; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -6,5 +6,29 @@ namespace gps {
|
||||||
|
|
||||||
static const char *TAG = "gps.time";
|
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 gps
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -18,20 +18,7 @@ class GPSTime : public time::RealTimeClock, public GPSListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void from_tiny_gps_(TinyGPSPlus &tiny_gps) {
|
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;
|
|
||||||
}
|
|
||||||
bool has_time_{false};
|
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))
|
_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 "
|
_LOGGER.info("Detected timezone '%s' with UTC offset %s and daylight savings time from "
|
||||||
"%s to %s",
|
"%s to %s",
|
||||||
tzname_off, _tz_timedelta(utcoffset_off), dst_begins_local.strftime("%x %X"),
|
tzname_off, _tz_timedelta(utcoffset_off),
|
||||||
dst_ends_local.strftime("%x %X"))
|
dst_begins_local.strftime("%d %B %X"),
|
||||||
|
dst_ends_local.strftime("%d %B %X"))
|
||||||
return tzbase + tzext
|
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 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};
|
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))
|
if (month == 2 && is_leap_year(year))
|
||||||
days_in_month = 29;
|
return 29;
|
||||||
return days_in_month;
|
return days;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESPTime::increment_second() {
|
void ESPTime::increment_second() {
|
||||||
|
@ -127,13 +127,13 @@ void ESPTime::recalc_timestamp_utc(bool use_day_of_year) {
|
||||||
return;
|
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;
|
res += is_leap_year(i) ? 366 : 365;
|
||||||
|
|
||||||
if (use_day_of_year) {
|
if (use_day_of_year) {
|
||||||
res += this->day_of_year - 1;
|
res += this->day_of_year - 1;
|
||||||
} else {
|
} 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 += days_in_month(i, this->year);
|
||||||
|
|
||||||
res += this->day_of_month - 1;
|
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')
|
time_based_ns = cg.esphome_ns.namespace('time_based')
|
||||||
TimeBasedCover = time_based_ns.class_('TimeBasedCover', cover.Cover, cg.Component)
|
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({
|
CONFIG_SCHEMA = cover.COVER_SCHEMA.extend({
|
||||||
cv.GenerateID(): cv.declare_id(TimeBasedCover),
|
cv.GenerateID(): cv.declare_id(TimeBasedCover),
|
||||||
cv.Required(CONF_STOP_ACTION): automation.validate_automation(single=True),
|
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_ACTION): automation.validate_automation(single=True),
|
||||||
cv.Required(CONF_CLOSE_DURATION): cv.positive_time_period_milliseconds,
|
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)
|
}).extend(cv.COMPONENT_SCHEMA)
|
||||||
|
|
||||||
|
|
||||||
|
@ -32,3 +36,5 @@ def to_code(config):
|
||||||
|
|
||||||
cg.add(var.set_close_duration(config[CONF_CLOSE_DURATION]))
|
cg.add(var.set_close_duration(config[CONF_CLOSE_DURATION]))
|
||||||
yield automation.build_automation(var.get_close_trigger(), [], config[CONF_CLOSE_ACTION])
|
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
|
// Recompute position every loop cycle
|
||||||
this->recompute_position_();
|
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->start_direction_(COVER_OPERATION_IDLE);
|
||||||
|
}
|
||||||
this->publish_state();
|
this->publish_state();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send current position every second
|
// 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->publish_state(false);
|
||||||
this->last_publish_time_ = now;
|
this->last_publish_time_ = now;
|
||||||
}
|
}
|
||||||
|
@ -57,6 +62,12 @@ void TimeBasedCover::control(const CoverCall &call) {
|
||||||
auto pos = *call.get_position();
|
auto pos = *call.get_position();
|
||||||
if (pos == this->position) {
|
if (pos == this->position) {
|
||||||
// already at target
|
// 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 {
|
} else {
|
||||||
auto op = pos < this->position ? COVER_OPERATION_CLOSING : COVER_OPERATION_OPENING;
|
auto op = pos < this->position ? COVER_OPERATION_CLOSING : COVER_OPERATION_OPENING;
|
||||||
this->target_position_ = pos;
|
this->target_position_ = pos;
|
||||||
|
@ -82,7 +93,7 @@ bool TimeBasedCover::is_at_target_() const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void TimeBasedCover::start_direction_(CoverOperation dir) {
|
void TimeBasedCover::start_direction_(CoverOperation dir) {
|
||||||
if (dir == this->current_operation)
|
if (dir == this->current_operation && dir != COVER_OPERATION_IDLE)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this->recompute_position_();
|
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_open_duration(uint32_t open_duration) { this->open_duration_ = open_duration; }
|
||||||
void set_close_duration(uint32_t close_duration) { this->close_duration_ = close_duration; }
|
void set_close_duration(uint32_t close_duration) { this->close_duration_ = close_duration; }
|
||||||
cover::CoverTraits get_traits() override;
|
cover::CoverTraits get_traits() override;
|
||||||
|
void set_has_built_in_endstop(bool value) { this->has_built_in_endstop_ = value; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void control(const cover::CoverCall &call) override;
|
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 start_dir_time_{0};
|
||||||
uint32_t last_publish_time_{0};
|
uint32_t last_publish_time_{0};
|
||||||
float target_position_{0};
|
float target_position_{0};
|
||||||
|
bool has_built_in_endstop_{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace time_based
|
} // namespace time_based
|
||||||
|
|
|
@ -291,12 +291,12 @@ void ICACHE_RAM_ATTR HOT ESP8266SoftwareSerial::write_byte(uint8_t data) {
|
||||||
this->write_bit_(true, &wait, start);
|
this->write_bit_(true, &wait, start);
|
||||||
enable_interrupts();
|
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)
|
while (ESP.getCycleCount() - start < *wait)
|
||||||
;
|
;
|
||||||
*wait += this->bit_time_;
|
*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);
|
this->wait_(wait, start);
|
||||||
return this->rx_pin_->digital_read();
|
return this->rx_pin_->digital_read();
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,9 +24,9 @@ class ESP8266SoftwareSerial {
|
||||||
protected:
|
protected:
|
||||||
static void gpio_intr(ESP8266SoftwareSerial *arg);
|
static void gpio_intr(ESP8266SoftwareSerial *arg);
|
||||||
|
|
||||||
inline void wait_(uint32_t *wait, const uint32_t &start);
|
void wait_(uint32_t *wait, const uint32_t &start);
|
||||||
inline bool read_bit_(uint32_t *wait, const uint32_t &start);
|
bool read_bit_(uint32_t *wait, const uint32_t &start);
|
||||||
inline void write_bit_(bool 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};
|
uint32_t bit_time_{0};
|
||||||
uint8_t *rx_buffer_{nullptr};
|
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_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_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");
|
// ESP_LOGVV(TAG, "Xiaomi no magic bytes");
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -101,7 +102,12 @@ optional<XiaomiParseResult> parse_xiaomi(const esp32_ble_tracker::ESPBTDevice &d
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
XiaomiParseResult result;
|
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);
|
bool success = parse_xiaomi_data_byte(raw_type, data, data_length, result);
|
||||||
if (!success)
|
if (!success)
|
||||||
return {};
|
return {};
|
||||||
|
@ -113,7 +119,12 @@ bool XiaomiListener::parse_device(const esp32_ble_tracker::ESPBTDevice &device)
|
||||||
if (!res.has_value())
|
if (!res.has_value())
|
||||||
return false;
|
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());
|
ESP_LOGD(TAG, "Got Xiaomi %s (%s):", name, device.address_str().c_str());
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ namespace esphome {
|
||||||
namespace xiaomi_ble {
|
namespace xiaomi_ble {
|
||||||
|
|
||||||
struct XiaomiParseResult {
|
struct XiaomiParseResult {
|
||||||
enum { TYPE_MIJIA, TYPE_MIFLORA } type;
|
enum { TYPE_MIJIA, TYPE_MIFLORA, TYPE_LYWSD02 } type;
|
||||||
optional<float> temperature;
|
optional<float> temperature;
|
||||||
optional<float> humidity;
|
optional<float> humidity;
|
||||||
optional<float> battery_level;
|
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-family: 'Material Icons';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
src: url(MaterialIcons-Regular.eot); /* For IE6-8 */
|
|
||||||
src: local('Material Icons'),
|
src: local('Material Icons'),
|
||||||
local('MaterialIcons-Regular'),
|
local('MaterialIcons-Regular'),
|
||||||
url(MaterialIcons-Regular.woff2) format('woff2'),
|
url(MaterialIcons-Regular.woff2) format('woff2'),
|
||||||
url(MaterialIcons-Regular.woff) format('woff'),
|
url(MaterialIcons-Regular.woff) format('woff');
|
||||||
url(MaterialIcons-Regular.ttf) format('truetype');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.material-icons {
|
.material-icons {
|
||||||
|
|
|
@ -33,9 +33,9 @@ files.sort()
|
||||||
|
|
||||||
file_types = ('.h', '.c', '.cpp', '.tcc', '.yaml', '.yml', '.ini', '.txt', '.ico', '.svg',
|
file_types = ('.h', '.c', '.cpp', '.tcc', '.yaml', '.yml', '.ini', '.txt', '.ico', '.svg',
|
||||||
'.py', '.html', '.js', '.md', '.sh', '.css', '.proto', '.conf', '.cfg',
|
'.py', '.html', '.js', '.md', '.sh', '.css', '.proto', '.conf', '.cfg',
|
||||||
'.eot', '.ttf', '.woff', '.woff2')
|
'.woff', '.woff2', '')
|
||||||
cpp_include = ('*.h', '*.c', '*.cpp', '*.tcc')
|
cpp_include = ('*.h', '*.c', '*.cpp', '*.tcc')
|
||||||
ignore_types = ('.ico', '.eot', '.ttf', '.woff', '.woff2')
|
ignore_types = ('.ico', '.woff', '.woff2', '')
|
||||||
|
|
||||||
LINT_FILE_CHECKS = []
|
LINT_FILE_CHECKS = []
|
||||||
LINT_CONTENT_CHECKS = []
|
LINT_CONTENT_CHECKS = []
|
||||||
|
|
Loading…
Reference in a new issue