1
0
mirror of https://github.com/esphome/esphome.git synced 2025-11-01 15:41:52 +00:00

Compare commits

...

15 Commits

Author SHA1 Message Date
Otto Winter
5f535e9756 Bump version to v1.14.0b5 2019-10-31 23:46:18 +01:00
Otto Winter
70faeb2fa8 Fix some binary_sensor not having an initial state (#819)
Fixes https://github.com/home-assistant/home-assistant/issues/28384
2019-10-31 23:46:10 +01:00
Otto Winter
469c0db981 Fix fan oscillating (#818)
Fixes https://github.com/esphome/issues/issues/783
2019-10-31 23:46:09 +01:00
Otto Winter
440e428aa4 Scheduler fixes (#813)
* Scheduler fixes

Fixes https://github.com/esphome/issues/issues/789, fixes https://github.com/esphome/issues/issues/788

Also changes to use unique_ptr - this should be much safer than the raw pointers form before (though the scoping rules might cause some issues, but looking closely I didn't find anything)

* Disable debugging

* Format
2019-10-31 23:46:09 +01:00
Otto Winter
dde70c95a4 Allow TimePeriod for time_period_str_unit (#815) 2019-10-31 23:46:09 +01:00
Otto Winter
09d1846261 Print update interval for pulse counter (#816) 2019-10-31 23:46:09 +01:00
Otto Winter
34d26a517d Uppercase ESPHome (#814) 2019-10-31 23:46:09 +01:00
Nikolay Vasilchuk
d24b88271c [Hotfix] Dashboard authentication on Py3 (#812)
* Fix

* Review fix

* Reverted first fix
2019-10-31 23:46:08 +01:00
Antoine GRÉA
f22115792a Add check if middle_text is too short (#811)
* Add check if middle_text is too short

* Use int division as suggested
2019-10-31 23:46:08 +01:00
Otto Winter
82a30558e1 Fix web server transition length truncated (#807)
Fixes https://github.com/esphome/issues/issues/772
2019-10-31 23:46:08 +01:00
Otto Winter
847fe5adca Fix modbus register (#806)
Fixes https://github.com/esphome/feature-requests/issues/49#issuecomment-546555289

Co-Authored-By: tsunglung <tsunglung@users.noreply.github.com>
2019-10-31 23:46:08 +01:00
Otto Winter
775b51c6a1 AS3935 Use normal pin polling for IRQ (#805)
* AS3935 Use normal pin polling for IRQ

See also https://github.com/esphome/feature-requests/issues/452

* Fix tests
2019-10-31 23:46:08 +01:00
Otto Winter
e0ad5a9009 Add Tuya message for no datapoints (#804)
See also https://github.com/esphome/feature-requests/issues/352#issuecomment-546579206
2019-10-31 23:46:07 +01:00
Otto Winter
1bf01a9081 Warn when UART and logger operating on same bus (#803) 2019-10-31 23:46:07 +01:00
Pavel Golovin
6ae59bb43d Fujitsu General climate new component (#677)
* new Fujitsu-General climate component

* Refactor out climate_ir

CC @glmnet

Refactored out climate_ir python files too.
Fixed invalid namespace name for climate_ir.

* Add namespace lint check

* Refactor Fujitsu Climate to climate_ir


Co-authored-by: Otto Winter <otto@otto-winter.com>
2019-10-31 23:46:07 +01:00
39 changed files with 518 additions and 200 deletions

View File

@@ -364,7 +364,7 @@ def command_update_all(args):
def print_bar(middle_text):
middle_text = " {} ".format(middle_text)
width = len(click.unstyle(middle_text))
half_line = "=" * ((twidth - width) / 2)
half_line = "=" * ((twidth - width) // 2)
click.echo("%s%s%s" % (half_line, middle_text, half_line))
for f in files:

View File

@@ -1,12 +1,11 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import pins
from esphome.const import CONF_PIN, CONF_INDOOR, CONF_WATCHDOG_THRESHOLD, \
from esphome.const import CONF_INDOOR, CONF_WATCHDOG_THRESHOLD, \
CONF_NOISE_LEVEL, CONF_SPIKE_REJECTION, CONF_LIGHTNING_THRESHOLD, \
CONF_MASK_DISTURBER, CONF_DIV_RATIO, CONF_CAPACITANCE
from esphome.core import coroutine
AUTO_LOAD = ['sensor', 'binary_sensor']
MULTI_CONF = True
@@ -15,10 +14,10 @@ CONF_AS3935_ID = 'as3935_id'
as3935_ns = cg.esphome_ns.namespace('as3935')
AS3935 = as3935_ns.class_('AS3935Component', cg.Component)
CONF_IRQ_PIN = 'irq_pin'
AS3935_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(AS3935),
cv.Required(CONF_PIN): cv.All(pins.internal_gpio_input_pin_schema,
pins.validate_has_interrupt),
cv.Required(CONF_IRQ_PIN): pins.gpio_input_pin_schema,
cv.Optional(CONF_INDOOR, default=True): cv.boolean,
cv.Optional(CONF_NOISE_LEVEL, default=2): cv.int_range(min=1, max=7),
@@ -35,8 +34,8 @@ AS3935_SCHEMA = cv.Schema({
def setup_as3935(var, config):
yield cg.register_component(var, config)
pin = yield cg.gpio_pin_expression(config[CONF_PIN])
cg.add(var.set_pin(pin))
irq_pin = yield cg.gpio_pin_expression(config[CONF_IRQ_PIN])
cg.add(var.set_irq_pin(irq_pin))
cg.add(var.set_indoor(config[CONF_INDOOR]))
cg.add(var.set_noise_level(config[CONF_NOISE_LEVEL]))
cg.add(var.set_watchdog_threshold(config[CONF_WATCHDOG_THRESHOLD]))

View File

@@ -9,10 +9,8 @@ static const char *TAG = "as3935";
void AS3935Component::setup() {
ESP_LOGCONFIG(TAG, "Setting up AS3935...");
this->pin_->setup();
this->store_.pin = this->pin_->to_isr();
LOG_PIN(" Interrupt Pin: ", this->pin_);
this->pin_->attach_interrupt(AS3935ComponentStore::gpio_intr, &this->store_, RISING);
this->irq_pin_->setup();
LOG_PIN(" IRQ Pin: ", this->irq_pin_);
// Write properties to sensor
this->write_indoor(this->indoor_);
@@ -27,13 +25,13 @@ void AS3935Component::setup() {
void AS3935Component::dump_config() {
ESP_LOGCONFIG(TAG, "AS3935:");
LOG_PIN(" Interrupt Pin: ", this->pin_);
LOG_PIN(" Interrupt Pin: ", this->irq_pin_);
}
float AS3935Component::get_setup_priority() const { return setup_priority::DATA; }
void AS3935Component::loop() {
if (!this->store_.interrupt)
if (!this->irq_pin_->digital_read())
return;
uint8_t int_value = this->read_interrupt_register_();
@@ -53,7 +51,6 @@ void AS3935Component::loop() {
this->energy_sensor_->publish_state(energy);
}
this->thunder_alert_binary_sensor_->publish_state(false);
this->store_.interrupt = false;
}
void AS3935Component::write_indoor(bool indoor) {
@@ -222,7 +219,5 @@ uint8_t AS3935Component::read_register_(uint8_t reg, uint8_t mask) {
return value;
}
void ICACHE_RAM_ATTR AS3935ComponentStore::gpio_intr(AS3935ComponentStore *arg) { arg->interrupt = true; }
} // namespace as3935
} // namespace esphome

View File

@@ -50,14 +50,6 @@ enum AS3935Values {
NOISE_INT = 0x01
};
/// Store data in a class that doesn't use multiple-inheritance (vtables in flash)
struct AS3935ComponentStore {
volatile bool interrupt;
ISRInternalGPIOPin *pin;
static void gpio_intr(AS3935ComponentStore *arg);
};
class AS3935Component : public Component {
public:
void setup() override;
@@ -65,7 +57,7 @@ class AS3935Component : public Component {
float get_setup_priority() const override;
void loop() override;
void set_pin(GPIOPin *pin) { pin_ = pin; }
void set_irq_pin(GPIOPin *irq_pin) { irq_pin_ = irq_pin; }
void set_distance_sensor(sensor::Sensor *distance_sensor) { distance_sensor_ = distance_sensor; }
void set_energy_sensor(sensor::Sensor *energy_sensor) { energy_sensor_ = energy_sensor; }
void set_thunder_alert_binary_sensor(binary_sensor::BinarySensor *thunder_alert_binary_sensor) {
@@ -102,8 +94,7 @@ class AS3935Component : public Component {
sensor::Sensor *distance_sensor_;
sensor::Sensor *energy_sensor_;
binary_sensor::BinarySensor *thunder_alert_binary_sensor_;
GPIOPin *pin_;
AS3935ComponentStore store_;
GPIOPin *irq_pin_;
bool indoor_;
uint8_t noise_level_;

View File

@@ -24,4 +24,4 @@ def to_code(config):
if CONF_OSCILLATION_OUTPUT in config:
oscillation_output = yield cg.get_variable(config[CONF_OSCILLATION_OUTPUT])
cg.add(var.set_oscillation(oscillation_output))
cg.add(var.set_oscillating(oscillation_output))

View File

@@ -23,6 +23,7 @@ IS_PLATFORM_COMPONENT = True
binary_sensor_ns = cg.esphome_ns.namespace('binary_sensor')
BinarySensor = binary_sensor_ns.class_('BinarySensor', cg.Nameable)
BinarySensorInitiallyOff = binary_sensor_ns.class_('BinarySensorInitiallyOff', BinarySensor)
BinarySensorPtr = BinarySensor.operator('ptr')
# Triggers

View File

@@ -67,7 +67,7 @@ class BinarySensor : public Nameable {
void send_state_internal(bool state, bool is_initial);
/// Return whether this binary sensor has outputted a state.
bool has_state() const;
virtual bool has_state() const;
virtual bool is_status_binary_sensor() const;
@@ -86,5 +86,10 @@ class BinarySensor : public Nameable {
Deduplicator<bool> publish_dedup_;
};
class BinarySensorInitiallyOff : public BinarySensor {
public:
bool has_state() const override { return true; }
};
} // namespace binary_sensor
} // namespace esphome

View File

@@ -9,7 +9,7 @@
namespace esphome {
namespace ble_presence {
class BLEPresenceDevice : public binary_sensor::BinarySensor,
class BLEPresenceDevice : public binary_sensor::BinarySensorInitiallyOff,
public esp32_ble_tracker::ESPBTDeviceListener,
public Component {
public:

View File

@@ -0,0 +1,41 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import climate, remote_transmitter, remote_receiver, sensor, remote_base
from esphome.components.remote_base import CONF_RECEIVER_ID, CONF_TRANSMITTER_ID
from esphome.const import CONF_SUPPORTS_COOL, CONF_SUPPORTS_HEAT, CONF_SENSOR
from esphome.core import coroutine
AUTO_LOAD = ['sensor', 'remote_base']
climate_ir_ns = cg.esphome_ns.namespace('climate_ir')
ClimateIR = climate_ir_ns.class_('ClimateIR', climate.Climate, cg.Component,
remote_base.RemoteReceiverListener)
CLIMATE_IR_SCHEMA = climate.CLIMATE_SCHEMA.extend({
cv.GenerateID(CONF_TRANSMITTER_ID): cv.use_id(remote_transmitter.RemoteTransmitterComponent),
cv.Optional(CONF_SUPPORTS_COOL, default=True): cv.boolean,
cv.Optional(CONF_SUPPORTS_HEAT, default=True): cv.boolean,
cv.Optional(CONF_SENSOR): cv.use_id(sensor.Sensor),
}).extend(cv.COMPONENT_SCHEMA)
CLIMATE_IR_WITH_RECEIVER_SCHEMA = CLIMATE_IR_SCHEMA.extend({
cv.Optional(CONF_RECEIVER_ID): cv.use_id(remote_receiver.RemoteReceiverComponent),
})
@coroutine
def register_climate_ir(var, config):
yield cg.register_component(var, config)
yield climate.register_climate(var, config)
cg.add(var.set_supports_cool(config[CONF_SUPPORTS_COOL]))
cg.add(var.set_supports_heat(config[CONF_SUPPORTS_HEAT]))
if CONF_SENSOR in config:
sens = yield cg.get_variable(config[CONF_SENSOR])
cg.add(var.set_sensor(sens))
if CONF_RECEIVER_ID in config:
receiver = yield cg.get_variable(config[CONF_RECEIVER_ID])
cg.add(receiver.register_listener(var))
transmitter = yield cg.get_variable(config[CONF_TRANSMITTER_ID])
cg.add(var.set_transmitter(transmitter))

View File

@@ -2,7 +2,7 @@
#include "esphome/core/log.h"
namespace esphome {
namespace climate {
namespace climate_ir {
static const char *TAG = "climate_ir";
@@ -63,5 +63,5 @@ void ClimateIR::dump_config() {
ESP_LOGCONFIG(TAG, " Supports COOL: %s", YESNO(this->supports_cool_));
}
} // namespace climate
} // namespace climate_ir
} // namespace esphome

View File

@@ -6,7 +6,7 @@
#include "esphome/components/sensor/sensor.h"
namespace esphome {
namespace climate {
namespace climate_ir {
/* A base for climate which works by sending (and receiving) IR codes
@@ -42,7 +42,7 @@ class ClimateIR : public climate::Climate, public Component, public remote_base:
climate::ClimateTraits traits() override;
/// Transmit via IR the state of this climate controller.
virtual void transmit_state() {}
virtual void transmit_state() = 0;
bool supports_cool_{true};
bool supports_heat_{true};
@@ -50,5 +50,6 @@ class ClimateIR : public climate::Climate, public Component, public remote_base:
remote_transmitter::RemoteTransmitterComponent *transmitter_;
sensor::Sensor *sensor_{nullptr};
};
} // namespace climate
} // namespace climate_ir
} // namespace esphome

View File

@@ -1,37 +1,18 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import climate, remote_transmitter, remote_receiver, sensor
from esphome.components.remote_base import CONF_TRANSMITTER_ID, CONF_RECEIVER_ID
from esphome.const import CONF_ID, CONF_SENSOR, CONF_SUPPORTS_COOL, CONF_SUPPORTS_HEAT
from esphome.components import climate_ir
from esphome.const import CONF_ID
AUTO_LOAD = ['sensor', 'climate_ir']
AUTO_LOAD = ['climate_ir']
coolix_ns = cg.esphome_ns.namespace('coolix')
CoolixClimate = coolix_ns.class_('CoolixClimate', climate.Climate, cg.Component)
CoolixClimate = coolix_ns.class_('CoolixClimate', climate_ir.ClimateIR)
CONFIG_SCHEMA = cv.All(climate.CLIMATE_SCHEMA.extend({
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend({
cv.GenerateID(): cv.declare_id(CoolixClimate),
cv.GenerateID(CONF_TRANSMITTER_ID): cv.use_id(remote_transmitter.RemoteTransmitterComponent),
cv.Optional(CONF_RECEIVER_ID): cv.use_id(remote_receiver.RemoteReceiverComponent),
cv.Optional(CONF_SUPPORTS_COOL, default=True): cv.boolean,
cv.Optional(CONF_SUPPORTS_HEAT, default=True): cv.boolean,
cv.Optional(CONF_SENSOR): cv.use_id(sensor.Sensor),
}).extend(cv.COMPONENT_SCHEMA))
})
def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config)
yield climate.register_climate(var, config)
cg.add(var.set_supports_cool(config[CONF_SUPPORTS_COOL]))
cg.add(var.set_supports_heat(config[CONF_SUPPORTS_HEAT]))
if CONF_SENSOR in config:
sens = yield cg.get_variable(config[CONF_SENSOR])
cg.add(var.set_sensor(sens))
if CONF_RECEIVER_ID in config:
receiver = yield cg.get_variable(config[CONF_RECEIVER_ID])
cg.add(receiver.register_listener(var))
transmitter = yield cg.get_variable(config[CONF_TRANSMITTER_ID])
cg.add(var.set_transmitter(transmitter))
yield climate_ir.register_climate_ir(var, config)

View File

@@ -9,9 +9,9 @@ namespace coolix {
const uint8_t COOLIX_TEMP_MIN = 17; // Celsius
const uint8_t COOLIX_TEMP_MAX = 30; // Celsius
class CoolixClimate : public climate::ClimateIR {
class CoolixClimate : public climate_ir::ClimateIR {
public:
CoolixClimate() : climate::ClimateIR(COOLIX_TEMP_MIN, COOLIX_TEMP_MAX) {}
CoolixClimate() : climate_ir::ClimateIR(COOLIX_TEMP_MIN, COOLIX_TEMP_MAX) {}
protected:
/// Transmit via IR the state of this climate controller.

View File

@@ -0,0 +1,18 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import climate_ir
from esphome.const import CONF_ID
AUTO_LOAD = ['climate_ir']
fujitsu_general_ns = cg.esphome_ns.namespace('fujitsu_general')
FujitsuGeneralClimate = fujitsu_general_ns.class_('FujitsuGeneralClimate', climate_ir.ClimateIR)
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_SCHEMA.extend({
cv.GenerateID(): cv.declare_id(FujitsuGeneralClimate),
})
def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
yield climate_ir.register_climate_ir(var, config)

View File

@@ -0,0 +1,212 @@
#include "fujitsu_general.h"
namespace esphome {
namespace fujitsu_general {
static const char *TAG = "fujitsu_general.climate";
// Control packet
const uint16_t FUJITSU_GENERAL_STATE_LENGTH = 16;
const uint8_t FUJITSU_GENERAL_BASE_BYTE0 = 0x14;
const uint8_t FUJITSU_GENERAL_BASE_BYTE1 = 0x63;
const uint8_t FUJITSU_GENERAL_BASE_BYTE2 = 0x00;
const uint8_t FUJITSU_GENERAL_BASE_BYTE3 = 0x10;
const uint8_t FUJITSU_GENERAL_BASE_BYTE4 = 0x10;
const uint8_t FUJITSU_GENERAL_BASE_BYTE5 = 0xFE;
const uint8_t FUJITSU_GENERAL_BASE_BYTE6 = 0x09;
const uint8_t FUJITSU_GENERAL_BASE_BYTE7 = 0x30;
// Temperature and POWER ON
const uint8_t FUJITSU_GENERAL_POWER_ON_MASK_BYTE8 = 0b00000001;
const uint8_t FUJITSU_GENERAL_BASE_BYTE8 = 0x40;
// Mode
const uint8_t FUJITSU_GENERAL_MODE_AUTO_BYTE9 = 0x00;
const uint8_t FUJITSU_GENERAL_MODE_HEAT_BYTE9 = 0x04;
const uint8_t FUJITSU_GENERAL_MODE_COOL_BYTE9 = 0x01;
const uint8_t FUJITSU_GENERAL_MODE_DRY_BYTE9 = 0x02;
const uint8_t FUJITSU_GENERAL_MODE_FAN_BYTE9 = 0x03;
const uint8_t FUJITSU_GENERAL_MODE_10C_BYTE9 = 0x0B;
const uint8_t FUJITSU_GENERAL_BASE_BYTE9 = 0x01;
// Fan speed and swing
const uint8_t FUJITSU_GENERAL_FAN_AUTO_BYTE10 = 0x00;
const uint8_t FUJITSU_GENERAL_FAN_HIGH_BYTE10 = 0x01;
const uint8_t FUJITSU_GENERAL_FAN_MEDIUM_BYTE10 = 0x02;
const uint8_t FUJITSU_GENERAL_FAN_LOW_BYTE10 = 0x03;
const uint8_t FUJITSU_GENERAL_FAN_SILENT_BYTE10 = 0x04;
const uint8_t FUJITSU_GENERAL_SWING_MASK_BYTE10 = 0b00010000;
const uint8_t FUJITSU_GENERAL_BASE_BYTE10 = 0x00;
const uint8_t FUJITSU_GENERAL_BASE_BYTE11 = 0x00;
const uint8_t FUJITSU_GENERAL_BASE_BYTE12 = 0x00;
const uint8_t FUJITSU_GENERAL_BASE_BYTE13 = 0x00;
// Outdoor Unit Low Noise
const uint8_t FUJITSU_GENERAL_OUTDOOR_UNIT_LOW_NOISE_BYTE14 = 0xA0;
const uint8_t FUJITSU_GENERAL_BASE_BYTE14 = 0x20;
// CRC
const uint8_t FUJITSU_GENERAL_BASE_BYTE15 = 0x6F;
// Power off packet is specific
const uint16_t FUJITSU_GENERAL_OFF_LENGTH = 7;
const uint8_t FUJITSU_GENERAL_OFF_BYTE0 = FUJITSU_GENERAL_BASE_BYTE0;
const uint8_t FUJITSU_GENERAL_OFF_BYTE1 = FUJITSU_GENERAL_BASE_BYTE1;
const uint8_t FUJITSU_GENERAL_OFF_BYTE2 = FUJITSU_GENERAL_BASE_BYTE2;
const uint8_t FUJITSU_GENERAL_OFF_BYTE3 = FUJITSU_GENERAL_BASE_BYTE3;
const uint8_t FUJITSU_GENERAL_OFF_BYTE4 = FUJITSU_GENERAL_BASE_BYTE4;
const uint8_t FUJITSU_GENERAL_OFF_BYTE5 = 0x02;
const uint8_t FUJITSU_GENERAL_OFF_BYTE6 = 0xFD;
const uint8_t FUJITSU_GENERAL_TEMP_MAX = 30; // Celsius
const uint8_t FUJITSU_GENERAL_TEMP_MIN = 16; // Celsius
const uint16_t FUJITSU_GENERAL_HEADER_MARK = 3300;
const uint16_t FUJITSU_GENERAL_HEADER_SPACE = 1600;
const uint16_t FUJITSU_GENERAL_BIT_MARK = 420;
const uint16_t FUJITSU_GENERAL_ONE_SPACE = 1200;
const uint16_t FUJITSU_GENERAL_ZERO_SPACE = 420;
const uint16_t FUJITSU_GENERAL_TRL_MARK = 420;
const uint16_t FUJITSU_GENERAL_TRL_SPACE = 8000;
const uint32_t FUJITSU_GENERAL_CARRIER_FREQUENCY = 38000;
FujitsuGeneralClimate::FujitsuGeneralClimate() : ClimateIR(FUJITSU_GENERAL_TEMP_MIN, FUJITSU_GENERAL_TEMP_MAX, 1) {}
void FujitsuGeneralClimate::transmit_state() {
if (this->mode == climate::CLIMATE_MODE_OFF) {
this->transmit_off_();
return;
}
uint8_t remote_state[FUJITSU_GENERAL_STATE_LENGTH] = {0};
remote_state[0] = FUJITSU_GENERAL_BASE_BYTE0;
remote_state[1] = FUJITSU_GENERAL_BASE_BYTE1;
remote_state[2] = FUJITSU_GENERAL_BASE_BYTE2;
remote_state[3] = FUJITSU_GENERAL_BASE_BYTE3;
remote_state[4] = FUJITSU_GENERAL_BASE_BYTE4;
remote_state[5] = FUJITSU_GENERAL_BASE_BYTE5;
remote_state[6] = FUJITSU_GENERAL_BASE_BYTE6;
remote_state[7] = FUJITSU_GENERAL_BASE_BYTE7;
remote_state[8] = FUJITSU_GENERAL_BASE_BYTE8;
remote_state[9] = FUJITSU_GENERAL_BASE_BYTE9;
remote_state[10] = FUJITSU_GENERAL_BASE_BYTE10;
remote_state[11] = FUJITSU_GENERAL_BASE_BYTE11;
remote_state[12] = FUJITSU_GENERAL_BASE_BYTE12;
remote_state[13] = FUJITSU_GENERAL_BASE_BYTE13;
remote_state[14] = FUJITSU_GENERAL_BASE_BYTE14;
remote_state[15] = FUJITSU_GENERAL_BASE_BYTE15;
// Set temperature
uint8_t safecelsius = std::max((uint8_t) this->target_temperature, FUJITSU_GENERAL_TEMP_MIN);
safecelsius = std::min(safecelsius, FUJITSU_GENERAL_TEMP_MAX);
remote_state[8] = (byte) safecelsius - 16;
remote_state[8] = remote_state[8] << 4;
// If not powered - set power on flag
if (!this->power_) {
remote_state[8] = (byte) remote_state[8] | FUJITSU_GENERAL_POWER_ON_MASK_BYTE8;
}
// Set mode
switch (this->mode) {
case climate::CLIMATE_MODE_COOL:
remote_state[9] = FUJITSU_GENERAL_MODE_COOL_BYTE9;
break;
case climate::CLIMATE_MODE_HEAT:
remote_state[9] = FUJITSU_GENERAL_MODE_HEAT_BYTE9;
break;
case climate::CLIMATE_MODE_AUTO:
default:
remote_state[9] = FUJITSU_GENERAL_MODE_AUTO_BYTE9;
break;
// TODO: CLIMATE_MODE_FAN_ONLY, CLIMATE_MODE_DRY, CLIMATE_MODE_10C are missing in esphome
}
// TODO: missing support for fan speed
remote_state[10] = FUJITSU_GENERAL_FAN_AUTO_BYTE10;
// TODO: missing support for swing
// remote_state[10] = (byte) remote_state[10] | FUJITSU_GENERAL_SWING_MASK_BYTE10;
// TODO: missing support for outdoor unit low noise
// remote_state[14] = (byte) remote_state[14] | FUJITSU_GENERAL_OUTDOOR_UNIT_LOW_NOISE_BYTE14;
// CRC
remote_state[15] = 0;
for (int i = 7; i < 15; i++) {
remote_state[15] += (byte) remote_state[i]; // Addiction
}
remote_state[15] = 0x100 - remote_state[15]; // mod 256
auto transmit = this->transmitter_->transmit();
auto data = transmit.get_data();
data->set_carrier_frequency(FUJITSU_GENERAL_CARRIER_FREQUENCY);
// Header
data->mark(FUJITSU_GENERAL_HEADER_MARK);
data->space(FUJITSU_GENERAL_HEADER_SPACE);
// Data
for (uint8_t i : remote_state) {
// Send all Bits from Byte Data in Reverse Order
for (uint8_t mask = 00000001; mask > 0; mask <<= 1) { // iterate through bit mask
data->mark(FUJITSU_GENERAL_BIT_MARK);
bool bit = i & mask;
data->space(bit ? FUJITSU_GENERAL_ONE_SPACE : FUJITSU_GENERAL_ZERO_SPACE);
// Next bits
}
}
// Footer
data->mark(FUJITSU_GENERAL_TRL_MARK);
data->space(FUJITSU_GENERAL_TRL_SPACE);
transmit.perform();
this->power_ = true;
}
void FujitsuGeneralClimate::transmit_off_() {
uint8_t remote_state[FUJITSU_GENERAL_OFF_LENGTH] = {0};
remote_state[0] = FUJITSU_GENERAL_OFF_BYTE0;
remote_state[1] = FUJITSU_GENERAL_OFF_BYTE1;
remote_state[2] = FUJITSU_GENERAL_OFF_BYTE2;
remote_state[3] = FUJITSU_GENERAL_OFF_BYTE3;
remote_state[4] = FUJITSU_GENERAL_OFF_BYTE4;
remote_state[5] = FUJITSU_GENERAL_OFF_BYTE5;
remote_state[6] = FUJITSU_GENERAL_OFF_BYTE6;
auto transmit = this->transmitter_->transmit();
auto data = transmit.get_data();
data->set_carrier_frequency(FUJITSU_GENERAL_CARRIER_FREQUENCY);
// Header
data->mark(FUJITSU_GENERAL_HEADER_MARK);
data->space(FUJITSU_GENERAL_HEADER_SPACE);
// Data
for (uint8_t i : remote_state) {
// Send all Bits from Byte Data in Reverse Order
for (uint8_t mask = 00000001; mask > 0; mask <<= 1) { // iterate through bit mask
data->mark(FUJITSU_GENERAL_BIT_MARK);
bool bit = i & mask;
data->space(bit ? FUJITSU_GENERAL_ONE_SPACE : FUJITSU_GENERAL_ZERO_SPACE);
// Next bits
}
}
// Footer
data->mark(FUJITSU_GENERAL_TRL_MARK);
data->space(FUJITSU_GENERAL_TRL_SPACE);
transmit.perform();
this->power_ = false;
}
} // namespace fujitsu_general
} // namespace esphome

View File

@@ -0,0 +1,24 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/automation.h"
#include "esphome/components/climate_ir/climate_ir.h"
namespace esphome {
namespace fujitsu_general {
class FujitsuGeneralClimate : public climate_ir::ClimateIR {
public:
FujitsuGeneralClimate();
protected:
/// Transmit via IR the state of this climate controller.
void transmit_state() override;
/// Transmit via IR power off command.
void transmit_off_();
bool power_{false};
};
} // namespace fujitsu_general
} // namespace esphome

View File

@@ -30,6 +30,7 @@ class Logger : public Component {
/// Manually set the baud rate for serial, set to 0 to disable.
void set_baud_rate(uint32_t baud_rate);
uint32_t get_baud_rate() const { return baud_rate_; }
/// Get the UART used by the logger.
UARTSelection get_uart() const;

View File

@@ -41,3 +41,4 @@ def register_modbus_device(var, config):
parent = yield cg.get_variable(config[CONF_MODBUS_ID])
cg.add(var.set_parent(parent))
cg.add(var.set_address(config[CONF_ADDRESS]))
cg.add(parent.register_device(var))

View File

@@ -394,7 +394,7 @@ class Nextion : public PollingComponent, public uart::UARTDevice {
bool wait_for_ack_{true};
};
class NextionTouchComponent : public binary_sensor::BinarySensor {
class NextionTouchComponent : public binary_sensor::BinarySensorInitiallyOff {
public:
void set_page_id(uint8_t page_id) { page_id_ = page_id; }
void set_component_id(uint8_t component_id) { component_id_ = component_id; }

View File

@@ -149,6 +149,7 @@ void PulseCounterSensor::dump_config() {
ESP_LOGCONFIG(TAG, " Rising Edge: %s", EDGE_MODE_TO_STRING[this->storage_.rising_edge_mode]);
ESP_LOGCONFIG(TAG, " Falling Edge: %s", EDGE_MODE_TO_STRING[this->storage_.falling_edge_mode]);
ESP_LOGCONFIG(TAG, " Filtering pulses shorter than %u µs", this->storage_.filter_us);
LOG_UPDATE_INTERVAL(this);
}
void PulseCounterSensor::update() {

View File

@@ -28,7 +28,7 @@ class RDM6300Component : public Component, public uart::UARTDevice {
uint32_t last_id_{0};
};
class RDM6300BinarySensor : public binary_sensor::BinarySensor {
class RDM6300BinarySensor : public binary_sensor::BinarySensorInitiallyOff {
public:
void set_id(uint32_t id) { id_ = id; }

View File

@@ -267,11 +267,11 @@ class RemoteReceiverBase : public RemoteComponentBase {
uint8_t tolerance_{25};
};
class RemoteReceiverBinarySensorBase : public binary_sensor::BinarySensor,
class RemoteReceiverBinarySensorBase : public binary_sensor::BinarySensorInitiallyOff,
public Component,
public RemoteReceiverListener {
public:
explicit RemoteReceiverBinarySensorBase() : BinarySensor() {}
explicit RemoteReceiverBinarySensorBase() : BinarySensorInitiallyOff() {}
void dump_config() override;
virtual bool matches(RemoteReceiveData src) = 0;
bool on_receive(RemoteReceiveData src) override {

View File

@@ -29,4 +29,4 @@ def to_code(config):
if CONF_OSCILLATION_OUTPUT in config:
oscillation_output = yield cg.get_variable(config[CONF_OSCILLATION_OUTPUT])
cg.add(var.set_oscillation(oscillation_output))
cg.add(var.set_oscillating(oscillation_output))

View File

@@ -30,7 +30,7 @@ void StatusBinarySensor::loop() {
this->publish_state(status);
}
void StatusBinarySensor::setup() { this->publish_state(false); }
void StatusBinarySensor::setup() { this->publish_initial_state(false); }
void StatusBinarySensor::dump_config() { LOG_BINARY_SENSOR("", "Status Binary Sensor", this); }
} // namespace status

View File

@@ -1,37 +1,18 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import climate, remote_transmitter, remote_receiver, sensor
from esphome.components.remote_base import CONF_TRANSMITTER_ID, CONF_RECEIVER_ID
from esphome.const import CONF_ID, CONF_SENSOR, CONF_SUPPORTS_COOL, CONF_SUPPORTS_HEAT
from esphome.components import climate_ir
from esphome.const import CONF_ID
AUTO_LOAD = ['sensor', 'climate_ir']
AUTO_LOAD = ['climate_ir']
tcl112_ns = cg.esphome_ns.namespace('tcl112')
Tcl112Climate = tcl112_ns.class_('Tcl112Climate', climate.Climate, cg.Component)
Tcl112Climate = tcl112_ns.class_('Tcl112Climate', climate_ir.ClimateIR)
CONFIG_SCHEMA = cv.All(climate.CLIMATE_SCHEMA.extend({
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend({
cv.GenerateID(): cv.declare_id(Tcl112Climate),
cv.GenerateID(CONF_TRANSMITTER_ID): cv.use_id(remote_transmitter.RemoteTransmitterComponent),
cv.Optional(CONF_RECEIVER_ID): cv.use_id(remote_receiver.RemoteReceiverComponent),
cv.Optional(CONF_SUPPORTS_COOL, default=True): cv.boolean,
cv.Optional(CONF_SUPPORTS_HEAT, default=True): cv.boolean,
cv.Optional(CONF_SENSOR): cv.use_id(sensor.Sensor),
}).extend(cv.COMPONENT_SCHEMA))
})
def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config)
yield climate.register_climate(var, config)
cg.add(var.set_supports_cool(config[CONF_SUPPORTS_COOL]))
cg.add(var.set_supports_heat(config[CONF_SUPPORTS_HEAT]))
if CONF_SENSOR in config:
sens = yield cg.get_variable(config[CONF_SENSOR])
cg.add(var.set_sensor(sens))
if CONF_RECEIVER_ID in config:
receiver = yield cg.get_variable(config[CONF_RECEIVER_ID])
cg.add(receiver.register_listener(var))
transmitter = yield cg.get_variable(config[CONF_TRANSMITTER_ID])
cg.add(var.set_transmitter(transmitter))
yield climate_ir.register_climate_ir(var, config)

View File

@@ -9,9 +9,9 @@ namespace tcl112 {
const float TCL112_TEMP_MAX = 31.0;
const float TCL112_TEMP_MIN = 16.0;
class Tcl112Climate : public climate::ClimateIR {
class Tcl112Climate : public climate_ir::ClimateIR {
public:
Tcl112Climate() : climate::ClimateIR(TCL112_TEMP_MIN, TCL112_TEMP_MAX, .5f) {}
Tcl112Climate() : climate_ir::ClimateIR(TCL112_TEMP_MIN, TCL112_TEMP_MAX, .5f) {}
protected:
/// Transmit via IR the state of this climate controller.

View File

@@ -36,6 +36,9 @@ void Tuya::dump_config() {
else
ESP_LOGCONFIG(TAG, " Datapoint %d: unknown", info.id);
}
if (this->datapoints_.empty()) {
ESP_LOGCONFIG(TAG, " Received no datapoints! Please make sure this is a supported Tuya device.");
}
this->check_uart_settings(9600);
}

View File

@@ -2,6 +2,11 @@
#include "esphome/core/log.h"
#include "esphome/core/helpers.h"
#include "esphome/core/application.h"
#include "esphome/core/defines.h"
#ifdef USE_LOGGER
#include "esphome/components/logger/logger.h"
#endif
namespace esphome {
namespace uart {
@@ -41,6 +46,12 @@ void UARTComponent::dump_config() {
}
ESP_LOGCONFIG(TAG, " Baud Rate: %u baud", this->baud_rate_);
ESP_LOGCONFIG(TAG, " Stop bits: %u", this->stop_bits_);
#ifdef USE_LOGGER
if (this->hw_serial_ == &Serial && logger::global_logger->get_baud_rate() != 0) {
ESP_LOGW(TAG, " You're using the same serial port for logging and the UART component. Please "
"disable logging over the serial port by setting logger->baud_rate to 0.");
}
#endif
}
void UARTComponent::write_byte(uint8_t data) {
@@ -145,6 +156,13 @@ void UARTComponent::dump_config() {
} else {
ESP_LOGCONFIG(TAG, " Using software serial");
}
#ifdef USE_LOGGER
if (this->hw_serial_ == &Serial && logger::global_logger->get_baud_rate() != 0) {
ESP_LOGW(TAG, " You're using the same serial port for logging and the UART component. Please "
"disable logging over the serial port by setting logger->baud_rate to 0.");
}
#endif
}
void UARTComponent::write_byte(uint8_t data) {

View File

@@ -416,11 +416,15 @@ void WebServer::handle_light_request(AsyncWebServerRequest *request, UrlMatch ma
if (request->hasParam("color_temp"))
call.set_color_temperature(request->getParam("color_temp")->value().toFloat());
if (request->hasParam("flash"))
call.set_flash_length((uint32_t) request->getParam("flash")->value().toFloat() * 1000);
if (request->hasParam("flash")) {
float length_s = request->getParam("flash")->value().toFloat();
call.set_flash_length(static_cast<uint32_t>(length_s * 1000));
}
if (request->hasParam("transition"))
call.set_transition_length((uint32_t) request->getParam("transition")->value().toFloat() * 1000);
if (request->hasParam("transition")) {
float length_s = request->getParam("transition")->value().toFloat();
call.set_transition_length(static_cast<uint32_t>(length_s * 1000));
}
if (request->hasParam("effect")) {
const char *effect = request->getParam("effect")->value().c_str();

View File

@@ -461,6 +461,8 @@ def time_period_str_unit(value):
if isinstance(value, int):
raise Invalid("Don't know what '{0}' means as it has no time *unit*! Did you mean "
"'{0}s'?".format(value))
if isinstance(value, TimePeriod):
value = str(value)
if not isinstance(value, string_types):
raise Invalid("Expected string for time period with unit.")

View File

@@ -3,7 +3,7 @@
MAJOR_VERSION = 1
MINOR_VERSION = 14
PATCH_VERSION = '0b4'
PATCH_VERSION = '0b5'
__short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION)
__version__ = '{}.{}'.format(__short_version__, PATCH_VERSION)

View File

@@ -97,7 +97,7 @@ void Application::loop() {
if (this->dump_config_at_ >= 0 && this->dump_config_at_ < this->components_.size()) {
if (this->dump_config_at_ == 0) {
ESP_LOGI(TAG, "esphome version " ESPHOME_VERSION " compiled on %s", this->compilation_time_.c_str());
ESP_LOGI(TAG, "ESPHome version " ESPHOME_VERSION " compiled on %s", this->compilation_time_.c_str());
}
this->components_[this->dump_config_at_]->dump_config();

View File

@@ -9,6 +9,9 @@ static const char *TAG = "scheduler";
static const uint32_t SCHEDULER_DONT_RUN = 4294967295UL;
// Uncomment to debug scheduler
// #define ESPHOME_DEBUG_SCHEDULER
void HOT Scheduler::set_timeout(Component *component, const std::string &name, uint32_t timeout,
std::function<void()> &&func) {
const uint32_t now = this->millis_();
@@ -21,7 +24,7 @@ void HOT Scheduler::set_timeout(Component *component, const std::string &name, u
ESP_LOGVV(TAG, "set_timeout(name='%s', timeout=%u)", name.c_str(), timeout);
auto *item = new SchedulerItem();
auto item = make_unique<SchedulerItem>();
item->component = component;
item->name = name;
item->type = SchedulerItem::TIMEOUT;
@@ -30,7 +33,7 @@ void HOT Scheduler::set_timeout(Component *component, const std::string &name, u
item->last_execution_major = this->millis_major_;
item->f = std::move(func);
item->remove = false;
this->push_(item);
this->push_(std::move(item));
}
bool HOT Scheduler::cancel_timeout(Component *component, const std::string &name) {
return this->cancel_item_(component, name, SchedulerItem::TIMEOUT);
@@ -52,7 +55,7 @@ void HOT Scheduler::set_interval(Component *component, const std::string &name,
ESP_LOGVV(TAG, "set_interval(name='%s', interval=%u, offset=%u)", name.c_str(), interval, offset);
auto *item = new SchedulerItem();
auto item = make_unique<SchedulerItem>();
item->component = component;
item->name = name;
item->type = SchedulerItem::INTERVAL;
@@ -63,7 +66,7 @@ void HOT Scheduler::set_interval(Component *component, const std::string &name,
item->last_execution_major--;
item->f = std::move(func);
item->remove = false;
this->push_(item);
this->push_(std::move(item));
}
bool HOT Scheduler::cancel_interval(Component *component, const std::string &name) {
return this->cancel_item_(component, name, SchedulerItem::INTERVAL);
@@ -71,7 +74,7 @@ bool HOT Scheduler::cancel_interval(Component *component, const std::string &nam
optional<uint32_t> HOT Scheduler::next_schedule_in() {
if (this->empty_())
return {};
auto *item = this->items_[0];
auto &item = this->items_[0];
const uint32_t now = this->millis_();
uint32_t next_time = item->last_execution + item->interval;
if (next_time < now)
@@ -82,98 +85,103 @@ void ICACHE_RAM_ATTR HOT Scheduler::call() {
const uint32_t now = this->millis_();
this->process_to_add();
// Uncomment for debugging the scheduler:
#ifdef ESPHOME_DEBUG_SCHEDULER
static uint32_t last_print = 0;
// if (random_uint32() % 400 == 0) {
// std::vector<SchedulerItem *> old_items = this->items_;
// ESP_LOGVV(TAG, "Items: count=%u, now=%u", this->items_.size(), now);
// while (!this->empty_()) {
// auto *item = this->items_[0];
// const char *type = item->type == SchedulerItem::INTERVAL ? "interval" : "timeout";
// ESP_LOGVV(TAG, " %s '%s' interval=%u last_execution=%u (%u) next=%u",
// type, item->name.c_str(), item->interval, item->last_execution, item->last_execution_major,
// item->last_execution + item->interval);
// this->pop_raw_();
// }
// ESP_LOGVV(TAG, "\n");
// this->items_ = old_items;
//}
if (now - last_print > 2000) {
last_print = now;
std::vector<std::unique_ptr<SchedulerItem>> old_items;
ESP_LOGVV(TAG, "Items: count=%u, now=%u", this->items_.size(), now);
while (!this->empty_()) {
auto item = std::move(this->items_[0]);
const char *type = item->type == SchedulerItem::INTERVAL ? "interval" : "timeout";
ESP_LOGVV(TAG, " %s '%s' interval=%u last_execution=%u (%u) next=%u (%u)", type, item->name.c_str(),
item->interval, item->last_execution, item->last_execution_major, item->next_execution(),
item->next_execution_major());
this->pop_raw_();
old_items.push_back(std::move(item));
}
ESP_LOGVV(TAG, "\n");
this->items_ = std::move(old_items);
}
#endif // ESPHOME_DEBUG_SCHEDULER
while (!this->empty_()) {
// Don't copy-by value yet
auto *item = this->items_[0];
if ((now - item->last_execution) < item->interval)
// Not reached timeout yet, done for this call
break;
uint8_t major = item->last_execution_major;
if (item->last_execution > now)
major++;
if (major != this->millis_major_)
break;
// use scoping to indicate visibility of `item` variable
{
// Don't copy-by value yet
auto &item = this->items_[0];
if ((now - item->last_execution) < item->interval)
// Not reached timeout yet, done for this call
break;
uint8_t major = item->next_execution_major();
if (this->millis_major_ - major > 1)
break;
// Don't run on failed components
if (item->component != nullptr && item->component->is_failed()) {
this->pop_raw_();
delete item;
continue;
}
// Don't run on failed components
if (item->component != nullptr && item->component->is_failed()) {
this->pop_raw_();
continue;
}
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
const char *type = item->type == SchedulerItem::INTERVAL ? "interval" : "timeout";
ESP_LOGVV(TAG, "Running %s '%s' with interval=%u last_execution=%u (now=%u)", type, item->name.c_str(),
item->interval, item->last_execution, now);
const char *type = item->type == SchedulerItem::INTERVAL ? "interval" : "timeout";
ESP_LOGVV(TAG, "Running %s '%s' with interval=%u last_execution=%u (now=%u)", type, item->name.c_str(),
item->interval, item->last_execution, now);
#endif
// Warning: During f(), a lot of stuff can happen, including:
// - timeouts/intervals get added, potentially invalidating vector pointers
// - timeouts/intervals get cancelled
item->f();
// Only pop after function call, this ensures we were reachable
// during the function call and know if we were cancelled.
this->pop_raw_();
if (item->remove) {
// We were removed/cancelled in the function call, stop
delete item;
continue;
// Warning: During f(), a lot of stuff can happen, including:
// - timeouts/intervals get added, potentially invalidating vector pointers
// - timeouts/intervals get cancelled
item->f();
}
if (item->type == SchedulerItem::INTERVAL) {
if (item->interval != 0) {
const uint32_t before = item->last_execution;
const uint32_t amount = (now - item->last_execution) / item->interval;
item->last_execution += amount * item->interval;
if (item->last_execution < before)
item->last_execution_major++;
{
// new scope, item from before might have been moved in the vector
auto item = std::move(this->items_[0]);
// Only pop after function call, this ensures we were reachable
// during the function call and know if we were cancelled.
this->pop_raw_();
if (item->remove) {
// We were removed/cancelled in the function call, stop
continue;
}
if (item->type == SchedulerItem::INTERVAL) {
if (item->interval != 0) {
const uint32_t before = item->last_execution;
const uint32_t amount = (now - item->last_execution) / item->interval;
item->last_execution += amount * item->interval;
if (item->last_execution < before)
item->last_execution_major++;
}
this->push_(std::move(item));
}
this->push_(item);
} else {
delete item;
}
}
this->process_to_add();
}
void HOT Scheduler::process_to_add() {
for (auto *it : this->to_add_) {
for (auto &it : this->to_add_) {
if (it->remove) {
delete it;
continue;
}
this->items_.push_back(it);
this->items_.push_back(std::move(it));
std::push_heap(this->items_.begin(), this->items_.end(), SchedulerItem::cmp);
}
this->to_add_.clear();
}
void HOT Scheduler::cleanup_() {
while (!this->items_.empty()) {
auto item = this->items_[0];
auto &item = this->items_[0];
if (!item->remove)
return;
delete item;
this->pop_raw_();
}
}
@@ -181,15 +189,15 @@ void HOT Scheduler::pop_raw_() {
std::pop_heap(this->items_.begin(), this->items_.end(), SchedulerItem::cmp);
this->items_.pop_back();
}
void HOT Scheduler::push_(Scheduler::SchedulerItem *item) { this->to_add_.push_back(item); }
void HOT Scheduler::push_(std::unique_ptr<Scheduler::SchedulerItem> item) { this->to_add_.push_back(std::move(item)); }
bool HOT Scheduler::cancel_item_(Component *component, const std::string &name, Scheduler::SchedulerItem::Type type) {
bool ret = false;
for (auto *it : this->items_)
for (auto &it : this->items_)
if (it->component == component && it->name == name && it->type == type) {
it->remove = true;
ret = true;
}
for (auto *it : this->to_add_)
for (auto &it : this->to_add_)
if (it->component == component && it->name == name && it->type == type) {
it->remove = true;
ret = true;
@@ -206,21 +214,30 @@ uint32_t Scheduler::millis_() {
return now;
}
bool HOT Scheduler::SchedulerItem::cmp(Scheduler::SchedulerItem *a, Scheduler::SchedulerItem *b) {
bool HOT Scheduler::SchedulerItem::cmp(const std::unique_ptr<SchedulerItem> &a,
const std::unique_ptr<SchedulerItem> &b) {
// min-heap
// return true if *a* will happen after *b*
uint32_t a_next_exec = a->last_execution + a->timeout;
uint8_t a_next_exec_major = a->last_execution_major;
if (a_next_exec < a->last_execution)
a_next_exec_major++;
uint32_t b_next_exec = b->last_execution + b->timeout;
uint8_t b_next_exec_major = b->last_execution_major;
if (b_next_exec < b->last_execution)
b_next_exec_major++;
uint32_t a_next_exec = a->next_execution();
uint8_t a_next_exec_major = a->next_execution_major();
uint32_t b_next_exec = b->next_execution();
uint8_t b_next_exec_major = b->next_execution_major();
if (a_next_exec_major != b_next_exec_major) {
return a_next_exec_major > b_next_exec_major;
// The "major" calculation is quite complicated.
// Basically, we need to check if the major value lies in the future or
//
// Here are some cases to think about:
// Format: a_major,b_major -> expected result (a-b, b-a)
// a=255,b=0 -> false (255, 1)
// a=0,b=1 -> false (255, 1)
// a=1,b=0 -> true (1, 255)
// a=0,b=255 -> true (1, 255)
uint8_t diff1 = a_next_exec_major - b_next_exec_major;
uint8_t diff2 = b_next_exec_major - a_next_exec_major;
return diff1 < diff2;
}
return a_next_exec > b_next_exec;

View File

@@ -2,6 +2,7 @@
#include "esphome/core/component.h"
#include <vector>
#include <memory>
namespace esphome {
@@ -34,21 +35,30 @@ class Scheduler {
bool remove;
uint8_t last_execution_major;
static bool cmp(SchedulerItem *a, SchedulerItem *b);
inline uint32_t next_execution() { return this->last_execution + this->timeout; }
inline uint8_t next_execution_major() {
uint32_t next_exec = this->next_execution();
uint8_t next_exec_major = this->last_execution_major;
if (next_exec < this->last_execution)
next_exec_major++;
return next_exec_major;
}
static bool cmp(const std::unique_ptr<SchedulerItem> &a, const std::unique_ptr<SchedulerItem> &b);
};
uint32_t millis_();
void cleanup_();
void pop_raw_();
void push_(SchedulerItem *item);
void push_(std::unique_ptr<SchedulerItem> item);
bool cancel_item_(Component *component, const std::string &name, SchedulerItem::Type type);
bool empty_() {
this->cleanup_();
return this->items_.empty();
}
std::vector<SchedulerItem *> items_;
std::vector<SchedulerItem *> to_add_;
std::vector<std::unique_ptr<SchedulerItem>> items_;
std::vector<std::unique_ptr<SchedulerItem>> to_add_;
uint32_t last_millis_{0};
uint8_t millis_major_{0};
};

View File

@@ -29,7 +29,7 @@ import tornado.websocket
from esphome import const, util
from esphome.__main__ import get_serial_ports
from esphome.helpers import mkdir_p, get_bool_env, run_system_command
from esphome.py_compat import IS_PY2, decode_text
from esphome.py_compat import IS_PY2, decode_text, encode_text
from esphome.storage_json import EsphomeStorageJSON, StorageJSON, \
esphome_storage_path, ext_storage_path, trash_storage_path
from esphome.util import shlex_quote
@@ -85,12 +85,11 @@ class DashboardSettings(object):
def check_password(self, username, password):
if not self.using_auth:
return True
if username != self.username:
return False
if IS_PY2:
password = hmac.new(password).digest()
else:
password = hmac.new(password.encode()).digest()
return username == self.username and hmac.compare_digest(self.password_digest, password)
password_digest = hmac.new(encode_text(password)).digest()
return hmac.compare_digest(self.password_digest, password_digest)
def rel_path(self, *args):
return os.path.join(self.config_dir, *args)
@@ -610,8 +609,8 @@ class LoginHandler(BaseHandler):
'X-HASSIO-KEY': os.getenv('HASSIO_TOKEN'),
}
data = {
'username': str(self.get_argument('username', '')),
'password': str(self.get_argument('password', ''))
'username': decode_text(self.get_argument('username', '')),
'password': decode_text(self.get_argument('password', ''))
}
try:
req = requests.post('http://hassio/auth', headers=headers, data=data)
@@ -628,8 +627,8 @@ class LoginHandler(BaseHandler):
self.render_login_page(error="Invalid username or password")
def post_native_login(self):
username = str(self.get_argument("username", '').encode('utf-8'))
password = str(self.get_argument("password", '').encode('utf-8'))
username = decode_text(self.get_argument("username", ''))
password = decode_text(self.get_argument("password", ''))
if settings.check_password(username, password):
self.set_secure_cookie("authenticated", cookie_authenticated_yes)
self.redirect("/")

View File

@@ -298,6 +298,19 @@ def lint_relative_py_import(fname):
' from . import abc_ns\n\n')
@lint_content_check(include=['esphome/components/*.h', 'esphome/components/*.cpp',
'esphome/components/*.tcc'])
def lint_namespace(fname, content):
expected_name = re.match(r'^esphome/components/([^/]+)/.*',
fname.replace(os.path.sep, '/')).group(1)
search = 'namespace {}'.format(expected_name)
if search in content:
return None
return 'Invalid namespace found in C++ file. All integration C++ files should put all ' \
'functions in a separate namespace that matches the integration\'s name. ' \
'Please make sure the file contains {}'.format(highlight(search))
@lint_content_find_check('"esphome.h"', include=cpp_include, exclude=['tests/custom.h'])
def lint_esphome_h(fname):
return ("File contains reference to 'esphome.h' - This file is "

View File

@@ -172,7 +172,7 @@ dallas:
as3935_spi:
cs_pin: GPIO12
pin: GPIO13
irq_pin: GPIO13
sensor:
- platform: adc

View File

@@ -54,7 +54,7 @@ deep_sleep:
sleep_duration: 50s
as3935_i2c:
pin: GPIO12
irq_pin: GPIO12
sensor: