1
0
mirror of https://github.com/esphome/esphome.git synced 2025-11-01 23:51:47 +00:00

Compare commits

...

73 Commits

Author SHA1 Message Date
Otto Winter
5f27757039 Merge pull request #2123 from esphome/bump-1.20.4
1.20.4
2021-08-04 17:54:53 +02:00
Otto winter
532907219b Bump version to v1.20.4 2021-08-04 17:46:10 +02:00
Otto Winter
eeaba74553 Fix external components not refreshing with default or high refresh time (#2122) 2021-08-04 17:46:10 +02:00
brambo123
dd637582a4 Fix time.on_time triggering if time jumped back (#1806)
Co-authored-by: Otto winter <otto@otto-winter.com>
2021-08-04 17:46:10 +02:00
Carlos Garcia Saura
b0d12aeea1 [duty_cycle] initialize two missing variables (#2088) 2021-08-04 17:46:09 +02:00
Łukasz Śliwiński
bdbd813455 Use proper schema for the analog pin shorthand (#2103)
The wrong error message is displayed like:
> GPIO17 (TOUT) is an analog-only pin on the ESP8266.
in case of the analog pin validation because the method `shorthand_analog_pin` uses wrong `GPIO_FULL_INPUT_PIN_SCHEMA` instead of `GPIO_FULL_ANALOG_PIN_SCHEMA`.
2021-08-04 17:46:09 +02:00
Paul Monigatti
a6fac2b175 Fix min/max keys in MQTT Number to match Home Assistant (#2102) 2021-08-04 17:46:09 +02:00
Guillermo Ruffino
5ce923ea90 fix diplay trigger missing base class (#2099) 2021-08-04 17:46:08 +02:00
Otto Winter
29f0508dc2 Fix PID climate breaks when restoring old modes (#2086) 2021-08-04 17:46:08 +02:00
Otto Winter
3ffa59f0cd Fix climate restore schema changed resulting in invalid restore (#2068)
Co-authored-by: Stefan Agner <stefan@agner.ch>
2021-08-04 17:46:08 +02:00
WeekendWarrior1
790d6ef94c Move configure_rmt() into setup() (#2028) 2021-08-04 17:46:08 +02:00
WeekendWarrior1
7828f48b9a Correctly invert esp32 RMT TX (#2022) 2021-08-04 17:46:07 +02:00
Jesse Hills
9fbb3659a6 Merge pull request #2098 from esphome/bump-1.20.3
1.20.3
2021-07-30 15:46:22 +12:00
Jesse Hills
fee446c28a Bump version to v1.20.3 2021-07-30 11:00:10 +12:00
Jesse Hills
1d56f0b035 Set pulse meter total to use state class measurement and last reset type auto (#2097) 2021-07-30 11:00:10 +12:00
Jesse Hills
341fddb9aa Merge pull request #2091 from esphome/bump-1.20.2
1.20.2
2021-07-29 20:42:05 +12:00
Jesse Hills
456824669f Bump version to v1.20.2 2021-07-29 19:51:17 +12:00
Jesse Hills
62f3039d82 Use sensor_schema for total_daily_energy (#2090)
Co-authored-by: Otto Winter <otto@otto-winter.com>
2021-07-29 19:51:17 +12:00
Jesse Hills
be4c718859 HLW8012 - Dump energy sensor config (#2082) 2021-07-29 19:51:17 +12:00
Jesse Hills
c2f9ed7c59 Bump esphome dashboard to 20210728.0 (#2081) 2021-07-29 19:51:17 +12:00
John K. Luebs
bfac6607d1 More Tuya MCU robustness (#2080) 2021-07-29 19:51:17 +12:00
Jesse Hills
e43dcded62 Merge pull request #2074 from esphome/bump-1.20.1
1.20.1
2021-07-27 11:00:48 +12:00
Jesse Hills
887081fd71 Bump version to v1.20.1 2021-07-27 09:43:05 +12:00
Otto Winter
71ded24fce Fix MQTT climate custom fan modes without regular ones (#2071) 2021-07-27 09:43:05 +12:00
Chris Nussbaum
1e2a9e8348 Couple more updates for the Tuya component (#2065)
Co-authored-by: Chris Nussbaum <chris.nussbaum@protolabs.com>
2021-07-27 09:43:05 +12:00
buxtronix
64a3aa7092 Log warning about lack of support for Anova nano (#2063)
Co-authored-by: Ben Buxton <bb@cactii.net>
2021-07-27 09:43:05 +12:00
carstenschroeder
fda8dd4ce3 Fixes new auto mode COOL and HEAT after #1994 (#2053) 2021-07-27 09:43:04 +12:00
Sergey V. DUDANOV
1efabd27d8 midea_ac: fix presets implementation (#2054) 2021-07-27 09:43:04 +12:00
Maurice Makaay
caa651e55b Accept change as proposed by black. (#2055) 2021-07-27 09:43:04 +12:00
Jesse Hills
10a6e9b4ee Merge pull request #2051 from esphome/bump-1.20.0
1.20.0
2021-07-22 08:32:30 +12:00
Jesse Hills
4b8ec44262 Bump version to v1.20.0 2021-07-22 07:55:49 +12:00
Jesse Hills
bd74ed4bc0 Merge branch 'beta' into bump-1.20.0 2021-07-22 07:55:49 +12:00
Jesse Hills
d01f296420 Merge pull request #2048 from esphome/bump-1.20.0b6
1.20.0b6
2021-07-21 11:35:27 +12:00
Jesse Hills
27112e2ace Bump version to v1.20.0b6 2021-07-21 10:52:48 +12:00
Sean Vig
837930234f Remove superfluous polling on ADS1115 (#2015) 2021-07-21 10:52:48 +12:00
Jesse Hills
e19aa3bbe0 Adding last_reset_type to sensors that should support it. (#2039) 2021-07-21 10:52:48 +12:00
Oxan van Leeuwen
35b5c1ed56 Fix white value transition for addressable lights (#2045) 2021-07-21 10:52:48 +12:00
Jesse Hills
c9d93ff685 Merge pull request #2044 from esphome/bump-1.20.0b5
1.20.0b5
2021-07-20 17:27:53 +12:00
Jesse Hills
fa72990a63 Bump version to v1.20.0b5 2021-07-20 17:09:58 +12:00
Otto Winter
e5afb1c4ea ESP32 ADC use esp-idf (#2024)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-07-20 17:09:57 +12:00
Sean Vig
73ead5f328 Correct ADS1115 handling of multiple sensors in continuous mode (#2016)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-07-20 17:09:57 +12:00
Paulus Schoutsen
5c57b51378 Bump dashboard to 20210719.0 (#2043) 2021-07-20 17:09:57 +12:00
Sergey V. DUDANOV
e25935ef21 midea_ac: Fix turbo mode. Preset BOOST. (#2029) 2021-07-20 17:09:57 +12:00
Jesse Hills
c7a52c3894 Add restore_value to template number (#2041) 2021-07-20 17:09:57 +12:00
Jesse Hills
53a4689ed1 Merge pull request #2040 from esphome/bump-1.20.0b4
1.20.0b4
2021-07-20 12:29:15 +12:00
Jesse Hills
0a82e6e792 Bump version to v1.20.0b4 2021-07-20 10:28:23 +12:00
Jesse Hills
98855e4123 Number and Template Number updates (#2036)
Co-authored-by: Otto winter <otto@otto-winter.com>
2021-07-20 10:28:22 +12:00
Jesse Hills
a91e6a6bdf Merge pull request #1959 from esphome/bump-1.19.4
1.19.4
2021-06-24 13:31:01 +12:00
Jesse Hills
8600620305 Bump version to v1.19.4 2021-06-24 12:49:45 +12:00
Jesse Hills
96721f305f Bump dashboard to 20210623.0 (#1958) 2021-06-24 12:49:45 +12:00
Otto Winter
2bf70d7d00 Compat argv parsing improvements (#1952) 2021-06-24 12:49:45 +12:00
Otto Winter
1d8c170f48 Add climate preset NONE again (#1951) 2021-06-24 12:49:45 +12:00
Otto Winter
6009c7edb4 Disallow power_save_mode NONE if used together with BLE (#1950) 2021-06-24 12:49:44 +12:00
Otto Winter
e3f36c033e API raise minor version for climate changes (#1947) 2021-06-24 12:49:44 +12:00
Otto Winter
d4eb0f1655 Rework climate traits (#1941)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-06-24 12:49:36 +12:00
Jesse Hills
e20ec00071 Merge pull request #1956 from esphome/bump-1.19.3
1.19.3
2021-06-23 20:16:42 +12:00
Jesse Hills
150114d774 Bump version to v1.19.3 2021-06-23 19:39:37 +12:00
Jesse Hills
89dfa5ea82 Bump esphome-dashboard to 20210622.0 (#1955) 2021-06-23 19:39:31 +12:00
Jesse Hills
97aa930ad2 Merge pull request #1943 from esphome/bump-1.19.2
1.19.2
2021-06-21 14:59:11 +12:00
Jesse Hills
2a5def10e7 Bump version to v1.19.2 2021-06-21 14:40:05 +12:00
Jesse Hills
969834e037 Fix bad climate control enum (#1942) 2021-06-21 14:40:05 +12:00
Jesse Hills
d73a44c504 Allow wifi setup to proceed when there is no sta or ap (#1931) 2021-06-21 14:40:05 +12:00
Sergey V. DUDANOV
8aec092ab6 Fix midea_ac query frame (#1940) 2021-06-21 14:40:05 +12:00
Chris Nussbaum
4fa959ba45 Don't send Tuya commands while currently receiving a message (#1886)
Co-authored-by: Chris Nussbaum <chris.nussbaum@protolabs.com>
2021-06-21 14:40:05 +12:00
Jesse Hills
b43712d78d Merge pull request #1936 from esphome/bump-1.19.1
1.19.1
2021-06-18 12:10:30 +12:00
Jesse Hills
01904a0f10 Bump version to v1.19.1 2021-06-18 11:52:02 +12:00
Jesse Hills
dd875e7529 Replace CLIMATE_MODE_AUTO with CLIMATE_MODE_HEAT_COOL in most cases (#1933) 2021-06-18 11:52:02 +12:00
Otto Winter
f1dcf0f0b8 Improve config final validation (#1917) 2021-06-18 11:52:02 +12:00
Sergey V. DUDANOV
a045d001bf Fix: midea_ac: fixed query status frame (#1922) 2021-06-18 11:52:01 +12:00
Paulus Schoutsen
066c1022d0 Update dashboard to 20210617.0 (#1930) 2021-06-18 11:52:01 +12:00
Jesse Hills
59c192becc Merge pull request #1923 from esphome/bump-1.19.0
1.19.0
2021-06-17 06:09:09 +12:00
Jesse Hills
a800816750 Merge branch 'beta' into bump-1.19.0 2021-06-17 05:59:02 +12:00
Jesse Hills
970563e07b Bump version to v1.19.0 2021-06-16 21:00:51 +12:00
51 changed files with 531 additions and 259 deletions

View File

@@ -11,7 +11,30 @@ namespace adc {
static const char *const TAG = "adc";
#ifdef ARDUINO_ARCH_ESP32
void ADCSensor::set_attenuation(adc_attenuation_t attenuation) { this->attenuation_ = attenuation; }
void ADCSensor::set_attenuation(adc_atten_t attenuation) { this->attenuation_ = attenuation; }
inline adc1_channel_t gpio_to_adc1(uint8_t pin) {
switch (pin) {
case 36:
return ADC1_CHANNEL_0;
case 37:
return ADC1_CHANNEL_1;
case 38:
return ADC1_CHANNEL_2;
case 39:
return ADC1_CHANNEL_3;
case 32:
return ADC1_CHANNEL_4;
case 33:
return ADC1_CHANNEL_5;
case 34:
return ADC1_CHANNEL_6;
case 35:
return ADC1_CHANNEL_7;
default:
return ADC1_CHANNEL_MAX;
}
}
#endif
void ADCSensor::setup() {
@@ -21,7 +44,9 @@ void ADCSensor::setup() {
#endif
#ifdef ARDUINO_ARCH_ESP32
analogSetPinAttenuation(this->pin_, this->attenuation_);
adc1_config_channel_atten(gpio_to_adc1(pin_), attenuation_);
adc1_config_width(ADC_WIDTH_BIT_12);
adc_gpio_init(ADC_UNIT_1, (adc_channel_t) gpio_to_adc1(pin_));
#endif
}
void ADCSensor::dump_config() {
@@ -36,18 +61,20 @@ void ADCSensor::dump_config() {
#ifdef ARDUINO_ARCH_ESP32
ESP_LOGCONFIG(TAG, " Pin: %u", this->pin_);
switch (this->attenuation_) {
case ADC_0db:
case ADC_ATTEN_DB_0:
ESP_LOGCONFIG(TAG, " Attenuation: 0db (max 1.1V)");
break;
case ADC_2_5db:
case ADC_ATTEN_DB_2_5:
ESP_LOGCONFIG(TAG, " Attenuation: 2.5db (max 1.5V)");
break;
case ADC_6db:
case ADC_ATTEN_DB_6:
ESP_LOGCONFIG(TAG, " Attenuation: 6db (max 2.2V)");
break;
case ADC_11db:
case ADC_ATTEN_DB_11:
ESP_LOGCONFIG(TAG, " Attenuation: 11db (max 3.9V)");
break;
default: // This is to satisfy the unused ADC_ATTEN_MAX
break;
}
#endif
LOG_UPDATE_INTERVAL(this);
@@ -60,20 +87,23 @@ void ADCSensor::update() {
}
float ADCSensor::sample() {
#ifdef ARDUINO_ARCH_ESP32
float value_v = analogRead(this->pin_) / 4095.0f; // NOLINT
int raw = adc1_get_raw(gpio_to_adc1(pin_));
float value_v = raw / 4095.0f;
switch (this->attenuation_) {
case ADC_0db:
case ADC_ATTEN_DB_0:
value_v *= 1.1;
break;
case ADC_2_5db:
case ADC_ATTEN_DB_2_5:
value_v *= 1.5;
break;
case ADC_6db:
case ADC_ATTEN_DB_6:
value_v *= 2.2;
break;
case ADC_11db:
case ADC_ATTEN_DB_11:
value_v *= 3.9;
break;
default: // This is to satisfy the unused ADC_ATTEN_MAX
break;
}
return value_v;
#endif

View File

@@ -6,6 +6,10 @@
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/voltage_sampler/voltage_sampler.h"
#ifdef ARDUINO_ARCH_ESP32
#include "driver/adc.h"
#endif
namespace esphome {
namespace adc {
@@ -13,7 +17,7 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
public:
#ifdef ARDUINO_ARCH_ESP32
/// Set the attenuation for this pin. Only available on the ESP32.
void set_attenuation(adc_attenuation_t attenuation);
void set_attenuation(adc_atten_t attenuation);
#endif
/// Update adc values.
@@ -34,7 +38,7 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
uint8_t pin_;
#ifdef ARDUINO_ARCH_ESP32
adc_attenuation_t attenuation_{ADC_0db};
adc_atten_t attenuation_{ADC_ATTEN_DB_0};
#endif
};

View File

@@ -16,10 +16,10 @@ from esphome.const import (
AUTO_LOAD = ["voltage_sampler"]
ATTENUATION_MODES = {
"0db": cg.global_ns.ADC_0db,
"2.5db": cg.global_ns.ADC_2_5db,
"6db": cg.global_ns.ADC_6db,
"11db": cg.global_ns.ADC_11db,
"0db": cg.global_ns.ADC_ATTEN_DB_0,
"2.5db": cg.global_ns.ADC_ATTEN_DB_2_5,
"6db": cg.global_ns.ADC_ATTEN_DB_6,
"11db": cg.global_ns.ADC_ATTEN_DB_11,
}

View File

@@ -64,11 +64,6 @@ void ADS1115Component::setup() {
return;
}
this->prev_config_ = config;
for (auto *sensor : this->sensors_) {
this->set_interval(sensor->get_name(), sensor->update_interval(),
[this, sensor] { this->request_measurement(sensor); });
}
}
void ADS1115Component::dump_config() {
ESP_LOGCONFIG(TAG, "Setting up ADS1115...");
@@ -107,17 +102,22 @@ float ADS1115Component::request_measurement(ADS1115Sensor *sensor) {
}
this->prev_config_ = config;
// about 1.6 ms with 860 samples per second
// about 1.2 ms with 860 samples per second
delay(2);
uint32_t start = millis();
while (this->read_byte_16(ADS1115_REGISTER_CONFIG, &config) && (config >> 15) == 0) {
if (millis() - start > 100) {
ESP_LOGW(TAG, "Reading ADS1115 timed out");
this->status_set_warning();
return NAN;
// in continuous mode, conversion will always be running, rely on the delay
// to ensure conversion is taking place with the correct settings
// can we use the rdy pin to trigger when a conversion is done?
if (!this->continuous_mode_) {
uint32_t start = millis();
while (this->read_byte_16(ADS1115_REGISTER_CONFIG, &config) && (config >> 15) == 0) {
if (millis() - start > 100) {
ESP_LOGW(TAG, "Reading ADS1115 timed out");
this->status_set_warning();
return NAN;
}
yield();
}
yield();
}
}

View File

@@ -60,6 +60,7 @@ void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_
auto chr = this->parent_->get_characteristic(ANOVA_SERVICE_UUID, ANOVA_CHARACTERISTIC_UUID);
if (chr == nullptr) {
ESP_LOGW(TAG, "[%s] No control service found at device, not an Anova..?", this->get_name().c_str());
ESP_LOGW(TAG, "[%s] Note, this component does not currently support Anova Nano.", this->get_name().c_str());
break;
}
this->char_handle_ = chr->handle;

View File

@@ -422,6 +422,12 @@ enum SensorStateClass {
STATE_CLASS_MEASUREMENT = 1;
}
enum SensorLastResetType {
LAST_RESET_NONE = 0;
LAST_RESET_NEVER = 1;
LAST_RESET_AUTO = 2;
}
message ListEntitiesSensorResponse {
option (id) = 16;
option (source) = SOURCE_SERVER;
@@ -438,6 +444,7 @@ message ListEntitiesSensorResponse {
bool force_update = 8;
string device_class = 9;
SensorStateClass state_class = 10;
SensorLastResetType last_reset_type = 11;
}
message SensorStateResponse {
option (id) = 25;

View File

@@ -399,6 +399,7 @@ bool APIConnection::send_sensor_info(sensor::Sensor *sensor) {
msg.force_update = sensor->get_force_update();
msg.device_class = sensor->get_device_class();
msg.state_class = static_cast<enums::SensorStateClass>(sensor->state_class);
msg.last_reset_type = static_cast<enums::SensorLastResetType>(sensor->last_reset_type);
return this->send_list_entities_sensor_response(msg);
}
@@ -570,11 +571,11 @@ bool APIConnection::send_number_info(number::Number *number) {
msg.object_id = number->get_object_id();
msg.name = number->get_name();
msg.unique_id = get_default_unique_id("number", number);
msg.icon = number->get_icon();
msg.icon = number->traits.get_icon();
msg.min_value = number->get_min_value();
msg.max_value = number->get_max_value();
msg.step = number->get_step();
msg.min_value = number->traits.get_min_value();
msg.max_value = number->traits.get_max_value();
msg.step = number->traits.get_step();
return this->send_list_entities_number_response(msg);
}

View File

@@ -72,6 +72,18 @@ template<> const char *proto_enum_to_string<enums::SensorStateClass>(enums::Sens
return "UNKNOWN";
}
}
template<> const char *proto_enum_to_string<enums::SensorLastResetType>(enums::SensorLastResetType value) {
switch (value) {
case enums::LAST_RESET_NONE:
return "LAST_RESET_NONE";
case enums::LAST_RESET_NEVER:
return "LAST_RESET_NEVER";
case enums::LAST_RESET_AUTO:
return "LAST_RESET_AUTO";
default:
return "UNKNOWN";
}
}
template<> const char *proto_enum_to_string<enums::LogLevel>(enums::LogLevel value) {
switch (value) {
case enums::LOG_LEVEL_NONE:
@@ -1592,6 +1604,10 @@ bool ListEntitiesSensorResponse::decode_varint(uint32_t field_id, ProtoVarInt va
this->state_class = value.as_enum<enums::SensorStateClass>();
return true;
}
case 11: {
this->last_reset_type = value.as_enum<enums::SensorLastResetType>();
return true;
}
default:
return false;
}
@@ -1647,6 +1663,7 @@ void ListEntitiesSensorResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(8, this->force_update);
buffer.encode_string(9, this->device_class);
buffer.encode_enum<enums::SensorStateClass>(10, this->state_class);
buffer.encode_enum<enums::SensorLastResetType>(11, this->last_reset_type);
}
void ListEntitiesSensorResponse::dump_to(std::string &out) const {
char buffer[64];
@@ -1692,6 +1709,10 @@ void ListEntitiesSensorResponse::dump_to(std::string &out) const {
out.append(" state_class: ");
out.append(proto_enum_to_string<enums::SensorStateClass>(this->state_class));
out.append("\n");
out.append(" last_reset_type: ");
out.append(proto_enum_to_string<enums::SensorLastResetType>(this->last_reset_type));
out.append("\n");
out.append("}");
}
bool SensorStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {

View File

@@ -36,6 +36,11 @@ enum SensorStateClass : uint32_t {
STATE_CLASS_NONE = 0,
STATE_CLASS_MEASUREMENT = 1,
};
enum SensorLastResetType : uint32_t {
LAST_RESET_NONE = 0,
LAST_RESET_NEVER = 1,
LAST_RESET_AUTO = 2,
};
enum LogLevel : uint32_t {
LOG_LEVEL_NONE = 0,
LOG_LEVEL_ERROR = 1,
@@ -429,6 +434,7 @@ class ListEntitiesSensorResponse : public ProtoMessage {
bool force_update{false};
std::string device_class{};
enums::SensorStateClass state_class{};
enums::SensorLastResetType last_reset_type{};
void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override;

View File

@@ -21,6 +21,7 @@ from esphome.const import (
ICON_EMPTY,
ICON_LIGHTBULB,
ICON_CURRENT_AC,
LAST_RESET_TYPE_AUTO,
STATE_CLASS_MEASUREMENT,
UNIT_HERTZ,
UNIT_VOLT,
@@ -91,10 +92,20 @@ ATM90E32_PHASE_SCHEMA = cv.Schema(
STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_FORWARD_ACTIVE_ENERGY): sensor.sensor_schema(
UNIT_WATT_HOURS, ICON_EMPTY, 2, DEVICE_CLASS_ENERGY, STATE_CLASS_MEASUREMENT
UNIT_WATT_HOURS,
ICON_EMPTY,
2,
DEVICE_CLASS_ENERGY,
STATE_CLASS_MEASUREMENT,
LAST_RESET_TYPE_AUTO,
),
cv.Optional(CONF_REVERSE_ACTIVE_ENERGY): sensor.sensor_schema(
UNIT_WATT_HOURS, ICON_EMPTY, 2, DEVICE_CLASS_ENERGY, STATE_CLASS_MEASUREMENT
UNIT_WATT_HOURS,
ICON_EMPTY,
2,
DEVICE_CLASS_ENERGY,
STATE_CLASS_MEASUREMENT,
LAST_RESET_TYPE_AUTO,
),
cv.Optional(CONF_GAIN_VOLTAGE, default=7305): cv.uint16_t,
cv.Optional(CONF_GAIN_CT, default=27961): cv.uint16_t,

View File

@@ -312,8 +312,12 @@ void Climate::add_on_state_callback(std::function<void()> &&callback) {
this->state_callback_.add(std::move(callback));
}
// Random 32bit value; If this changes existing restore preferences are invalidated
static const uint32_t RESTORE_STATE_VERSION = 0x848EA6ADUL;
optional<ClimateDeviceRestoreState> Climate::restore_state_() {
this->rtc_ = global_preferences.make_preference<ClimateDeviceRestoreState>(this->get_object_id_hash());
this->rtc_ =
global_preferences.make_preference<ClimateDeviceRestoreState>(this->get_object_id_hash() ^ RESTORE_STATE_VERSION);
ClimateDeviceRestoreState recovered{};
if (!this->rtc_.load(&recovered))
return {};

View File

@@ -120,6 +120,7 @@ class ClimateCall {
};
/// Struct used to save the state of the climate device in restore memory.
/// Make sure to update RESTORE_STATE_VERSION when changing the struct entries.
struct ClimateDeviceRestoreState {
ClimateMode mode;
bool uses_custom_fan_mode{false};

View File

@@ -31,7 +31,9 @@ DisplayPageShowPrevAction = display_ns.class_(
DisplayIsDisplayingPageCondition = display_ns.class_(
"DisplayIsDisplayingPageCondition", automation.Condition
)
DisplayOnPageChangeTrigger = display_ns.class_("DisplayOnPageChangeTrigger")
DisplayOnPageChangeTrigger = display_ns.class_(
"DisplayOnPageChangeTrigger", automation.Trigger
)
CONF_ON_PAGE_CHANGE = "on_page_change"

View File

@@ -13,6 +13,7 @@ void DutyCycleSensor::setup() {
this->store_.pin = this->pin_->to_isr();
this->store_.last_level = this->pin_->digital_read();
this->last_update_ = micros();
this->store_.last_interrupt = micros();
this->pin_->attach_interrupt(DutyCycleSensorStore::gpio_intr, &this->store_, CHANGE);
}

View File

@@ -29,7 +29,7 @@ class DutyCycleSensor : public sensor::Sensor, public PollingComponent {
protected:
GPIOPin *pin_;
DutyCycleSensorStore store_;
DutyCycleSensorStore store_{};
uint32_t last_update_;
};

View File

@@ -109,9 +109,9 @@ def _compute_destination_path(key: str) -> Path:
return base_dir / h.hexdigest()[:8]
def _run_git_command(cmd):
def _run_git_command(cmd, cwd=None):
try:
ret = subprocess.run(cmd, capture_output=True, check=False)
ret = subprocess.run(cmd, cwd=cwd, capture_output=True, check=False)
except FileNotFoundError as err:
raise cv.Invalid(
"git is not installed but required for external_components.\n"
@@ -147,18 +147,20 @@ def _process_git_config(config: dict, refresh) -> str:
age = datetime.datetime.now() - datetime.datetime.fromtimestamp(
file_timestamp.stat().st_mtime
)
if age.seconds > refresh.total_seconds:
if age.total_seconds() > refresh.total_seconds:
_LOGGER.info("Updating %s", key)
_LOGGER.debug("Location: %s", repo_dir)
# Stash local changes (if any)
_run_git_command(["git", "stash", "push", "--include-untracked"])
_run_git_command(
["git", "stash", "push", "--include-untracked"], str(repo_dir)
)
# Fetch remote ref
cmd = ["git", "fetch", "--", "origin"]
if CONF_REF in config:
cmd.append(config[CONF_REF])
_run_git_command(cmd)
_run_git_command(cmd, str(repo_dir))
# Hard reset to FETCH_HEAD (short-lived git ref corresponding to most recent fetch)
_run_git_command(["git", "reset", "--hard", "FETCH_HEAD"])
_run_git_command(["git", "reset", "--hard", "FETCH_HEAD"], str(repo_dir))
if (repo_dir / "esphome" / "components").is_dir():
components_dir = repo_dir / "esphome" / "components"

View File

@@ -15,6 +15,7 @@ from esphome.const import (
DEVICE_CLASS_VOLTAGE,
ICON_CURRENT_AC,
ICON_EMPTY,
LAST_RESET_TYPE_AUTO,
STATE_CLASS_MEASUREMENT,
STATE_CLASS_NONE,
UNIT_AMPERE,
@@ -121,14 +122,16 @@ CONFIG_SCHEMA = (
ICON_EMPTY,
2,
DEVICE_CLASS_ENERGY,
STATE_CLASS_NONE,
STATE_CLASS_MEASUREMENT,
LAST_RESET_TYPE_AUTO,
),
cv.Optional(CONF_TOTAL_ENERGY_PRODUCTION): sensor.sensor_schema(
UNIT_KILOWATT_HOURS,
ICON_EMPTY,
0,
DEVICE_CLASS_ENERGY,
STATE_CLASS_NONE,
STATE_CLASS_MEASUREMENT,
LAST_RESET_TYPE_AUTO,
),
cv.Optional(CONF_TOTAL_GENERATION_TIME): sensor.sensor_schema(
UNIT_HOURS,

View File

@@ -45,6 +45,7 @@ void HLW8012Component::dump_config() {
LOG_SENSOR(" ", "Voltage", this->voltage_sensor_)
LOG_SENSOR(" ", "Current", this->current_sensor_)
LOG_SENSOR(" ", "Power", this->power_sensor_)
LOG_SENSOR(" ", "Energy", this->energy_sensor_)
}
float HLW8012Component::get_setup_priority() const { return setup_priority::DATA; }
void HLW8012Component::update() {

View File

@@ -19,8 +19,8 @@ from esphome.const import (
DEVICE_CLASS_POWER,
DEVICE_CLASS_VOLTAGE,
ICON_EMPTY,
LAST_RESET_TYPE_AUTO,
STATE_CLASS_MEASUREMENT,
STATE_CLASS_NONE,
UNIT_VOLT,
UNIT_AMPERE,
UNIT_WATT,
@@ -67,7 +67,12 @@ CONFIG_SCHEMA = cv.Schema(
UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
),
cv.Optional(CONF_ENERGY): sensor.sensor_schema(
UNIT_WATT_HOURS, ICON_EMPTY, 1, DEVICE_CLASS_ENERGY, STATE_CLASS_NONE
UNIT_WATT_HOURS,
ICON_EMPTY,
1,
DEVICE_CLASS_ENERGY,
STATE_CLASS_MEASUREMENT,
LAST_RESET_TYPE_AUTO,
),
cv.Optional(CONF_CURRENT_RESISTOR, default=0.001): cv.resistance,
cv.Optional(CONF_VOLTAGE_DIVIDER, default=2351): cv.positive_float,

View File

@@ -68,10 +68,7 @@ void AddressableLight::write_state(LightState *state) {
// our transition will handle brightness, disable brightness in correction.
this->correction_.set_local_brightness(255);
uint8_t orig_w = target_color.w;
target_color *= static_cast<uint8_t>(roundf(end_values.get_brightness() * end_values.get_state() * 255.0f));
// w is not scaled by brightness
target_color.w = orig_w;
float denom = (1.0f - new_smoothed);
float alpha = denom == 0.0f ? 0.0f : (new_smoothed - prev_smoothed) / denom;

View File

@@ -100,7 +100,7 @@ bool MideaAC::allow_preset(climate::ClimatePreset preset) const {
ESP_LOGD(TAG, "BOOST preset is only available in HEAT or COOL mode");
}
break;
case climate::CLIMATE_PRESET_HOME:
case climate::CLIMATE_PRESET_NONE:
return true;
default:
break;
@@ -191,7 +191,7 @@ climate::ClimateTraits MideaAC::traits() {
if (traits_swing_both_)
traits.add_supported_swing_mode(climate::CLIMATE_SWING_BOTH);
traits.set_supported_presets({
climate::CLIMATE_PRESET_HOME,
climate::CLIMATE_PRESET_NONE,
});
if (traits_preset_eco_)
traits.add_supported_preset(climate::CLIMATE_PRESET_ECO);

View File

@@ -6,6 +6,9 @@
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/midea_dongle/midea_dongle.h"
#include "esphome/components/climate/climate.h"
#include "esphome/components/midea_dongle/midea_dongle.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/core/component.h"
#include "midea_frame.h"
namespace esphome {

View File

@@ -86,18 +86,17 @@ void PropertiesFrame::set_mode(climate::ClimateMode mode) {
}
optional<climate::ClimatePreset> PropertiesFrame::get_preset() const {
if (this->get_eco_mode()) {
if (this->get_eco_mode())
return climate::CLIMATE_PRESET_ECO;
} else if (this->get_sleep_mode()) {
if (this->get_sleep_mode())
return climate::CLIMATE_PRESET_SLEEP;
} else if (this->get_turbo_mode()) {
if (this->get_turbo_mode())
return climate::CLIMATE_PRESET_BOOST;
} else {
return climate::CLIMATE_PRESET_HOME;
}
return climate::CLIMATE_PRESET_NONE;
}
void PropertiesFrame::set_preset(climate::ClimatePreset preset) {
this->clear_presets();
switch (preset) {
case climate::CLIMATE_PRESET_ECO:
this->set_eco_mode(true);
@@ -113,14 +112,21 @@ void PropertiesFrame::set_preset(climate::ClimatePreset preset) {
}
}
void PropertiesFrame::clear_presets() {
this->set_eco_mode(false);
this->set_sleep_mode(false);
this->set_turbo_mode(false);
this->set_freeze_protection_mode(false);
}
bool PropertiesFrame::is_custom_preset() const { return this->get_freeze_protection_mode(); }
const std::string &PropertiesFrame::get_custom_preset() const { return midea_ac::MIDEA_FREEZE_PROTECTION_PRESET; };
void PropertiesFrame::set_custom_preset(const std::string &preset) {
if (preset == MIDEA_FREEZE_PROTECTION_PRESET) {
this->clear_presets();
if (preset == MIDEA_FREEZE_PROTECTION_PRESET)
this->set_freeze_protection_mode(true);
}
}
bool PropertiesFrame::is_custom_fan_mode() const {

View File

@@ -102,8 +102,11 @@ class PropertiesFrame : public midea_dongle::BaseFrame {
void set_sleep_mode(bool state) { this->set_bytemask_(20, 0x01, state); }
/* TURBO MODE */
bool get_turbo_mode() const { return this->pbuf_[18] & 0x20; }
void set_turbo_mode(bool state) { this->set_bytemask_(18, 0x20, state); }
bool get_turbo_mode() const { return this->pbuf_[18] & 0x20 || this->pbuf_[20] & 0x02; }
void set_turbo_mode(bool state) {
this->set_bytemask_(18, 0x20, state);
this->set_bytemask_(20, 0x02, state);
}
/* FREEZE PROTECTION */
bool get_freeze_protection_mode() const { return this->pbuf_[31] & 0x80; }
@@ -112,6 +115,7 @@ class PropertiesFrame : public midea_dongle::BaseFrame {
/* PRESET */
optional<climate::ClimatePreset> get_preset() const;
void set_preset(climate::ClimatePreset preset);
void clear_presets();
bool is_custom_preset() const;
const std::string &get_custom_preset() const;

View File

@@ -72,7 +72,7 @@ void MQTTClimateComponent::send_discovery(JsonObject &root, mqtt::SendDiscoveryC
root["act_t"] = this->get_action_state_topic();
}
if (traits.get_supports_fan_modes()) {
if (traits.get_supports_fan_modes() || !traits.get_supported_custom_fan_modes().empty()) {
// fan_mode_command_topic
root["fan_mode_cmd_t"] = this->get_fan_mode_command_topic();
// fan_mode_state_topic

View File

@@ -3,10 +3,6 @@
#ifdef USE_NUMBER
#ifdef USE_DEEP_SLEEP
#include "esphome/components/deep_sleep/deep_sleep_component.h"
#endif
namespace esphome {
namespace mqtt {
@@ -20,7 +16,7 @@ void MQTTNumberComponent::setup() {
this->subscribe(this->get_command_topic_(), [this](const std::string &topic, const std::string &state) {
auto val = parse_float(state);
if (!val.has_value()) {
ESP_LOGE(TAG, "Can't convert '%s' to number!", state.c_str());
ESP_LOGW(TAG, "Can't convert '%s' to number!", state.c_str());
return;
}
auto call = this->number_->make_call();
@@ -39,8 +35,15 @@ std::string MQTTNumberComponent::component_type() const { return "number"; }
std::string MQTTNumberComponent::friendly_name() const { return this->number_->get_name(); }
void MQTTNumberComponent::send_discovery(JsonObject &root, mqtt::SendDiscoveryConfig &config) {
if (!this->number_->get_icon().empty())
root["icon"] = this->number_->get_icon();
const auto &traits = number_->traits;
// https://www.home-assistant.io/integrations/number.mqtt/
if (!traits.get_icon().empty())
root["icon"] = traits.get_icon();
root["min"] = traits.get_min_value();
root["max"] = traits.get_max_value();
root["step"] = traits.get_step();
config.command_topic = true;
}
bool MQTTNumberComponent::send_initial_state() {
if (this->number_->has_state()) {
@@ -51,8 +54,9 @@ bool MQTTNumberComponent::send_initial_state() {
}
bool MQTTNumberComponent::is_internal() { return this->number_->is_internal(); }
bool MQTTNumberComponent::publish_state(float value) {
int8_t accuracy = this->number_->get_accuracy_decimals();
return this->publish(this->get_state_topic_(), value_accuracy_to_string(value, accuracy));
char buffer[64];
snprintf(buffer, sizeof(buffer), "%f", value);
return this->publish(this->get_state_topic_(), buffer);
}
} // namespace mqtt

View File

@@ -1,3 +1,4 @@
from typing import Optional
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
@@ -66,12 +67,18 @@ NUMBER_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend(
)
async def setup_number_core_(var, config):
async def setup_number_core_(
var, config, *, min_value: float, max_value: float, step: Optional[float]
):
cg.add(var.set_name(config[CONF_NAME]))
if CONF_INTERNAL in config:
cg.add(var.set_internal(config[CONF_INTERNAL]))
cg.add(var.set_icon(config[CONF_ICON]))
cg.add(var.traits.set_icon(config[CONF_ICON]))
cg.add(var.traits.set_min_value(min_value))
cg.add(var.traits.set_max_value(max_value))
if step is not None:
cg.add(var.traits.set_step(step))
for conf in config.get(CONF_ON_VALUE, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
@@ -92,16 +99,24 @@ async def setup_number_core_(var, config):
await mqtt.register_mqtt_component(mqtt_, config)
async def register_number(var, config):
async def register_number(
var, config, *, min_value: float, max_value: float, step: Optional[float] = None
):
if not CORE.has_id(config[CONF_ID]):
var = cg.Pvariable(config[CONF_ID], var)
cg.add(cg.App.register_number(var))
await setup_number_core_(var, config)
await setup_number_core_(
var, config, min_value=min_value, max_value=max_value, step=step
)
async def new_number(config):
async def new_number(
config, *, min_value: float, max_value: float, step: Optional[float] = None
):
var = cg.new_Pvariable(config[CONF_ID])
await register_number(var, config)
await register_number(
var, config, min_value=min_value, max_value=max_value, step=step
)
return var

View File

@@ -8,67 +8,38 @@ static const char *const TAG = "number";
void NumberCall::perform() {
ESP_LOGD(TAG, "'%s' - Setting", this->parent_->get_name().c_str());
if (this->value_.has_value()) {
auto value = *this->value_;
uint8_t accuracy = this->parent_->get_accuracy_decimals();
float min_value = this->parent_->get_min_value();
if (value < min_value) {
ESP_LOGW(TAG, " Value %s must not be less than minimum %s", value_accuracy_to_string(value, accuracy).c_str(),
value_accuracy_to_string(min_value, accuracy).c_str());
this->value_.reset();
return;
}
float max_value = this->parent_->get_max_value();
if (value > max_value) {
ESP_LOGW(TAG, " Value %s must not be larger than maximum %s", value_accuracy_to_string(value, accuracy).c_str(),
value_accuracy_to_string(max_value, accuracy).c_str());
this->value_.reset();
return;
}
ESP_LOGD(TAG, " Value: %s", value_accuracy_to_string(*this->value_, accuracy).c_str());
this->parent_->set(*this->value_);
if (!this->value_.has_value() || isnan(*this->value_)) {
ESP_LOGW(TAG, "No value set for NumberCall");
return;
}
const auto &traits = this->parent_->traits;
auto value = *this->value_;
float min_value = traits.get_min_value();
if (value < min_value) {
ESP_LOGW(TAG, " Value %f must not be less than minimum %f", value, min_value);
return;
}
float max_value = traits.get_max_value();
if (value > max_value) {
ESP_LOGW(TAG, " Value %f must not be greater than maximum %f", value, max_value);
return;
}
ESP_LOGD(TAG, " Value: %f", *this->value_);
this->parent_->control(*this->value_);
}
NumberCall &NumberCall::set_value(float value) {
this->value_ = value;
return *this;
}
const optional<float> &NumberCall::get_value() const { return this->value_; }
NumberCall Number::make_call() { return NumberCall(this); }
void Number::publish_state(float state) {
this->has_state_ = true;
this->state = state;
ESP_LOGD(TAG, "'%s': Sending state %.5f", this->get_name().c_str(), state);
ESP_LOGD(TAG, "'%s': Sending state %f", this->get_name().c_str(), state);
this->state_callback_.call(state);
}
uint32_t Number::update_interval() { return 0; }
Number::Number(const std::string &name) : Nameable(name), state(NAN) {}
Number::Number() : Number("") {}
void Number::add_on_state_callback(std::function<void(float)> &&callback) {
this->state_callback_.add(std::move(callback));
}
void Number::set_icon(const std::string &icon) { this->icon_ = icon; }
std::string Number::get_icon() { return *this->icon_; }
int8_t Number::get_accuracy_decimals() {
// use printf %g to find number of digits based on step
char buf[32];
sprintf(buf, "%.5g", this->step_);
std::string str{buf};
size_t dot_pos = str.find('.');
if (dot_pos == std::string::npos)
return 0;
return str.length() - dot_pos - 1;
}
float Number::get_state() const { return this->state; }
bool Number::has_state() const { return this->has_state_; }
uint32_t Number::hash_base() { return 2282307003UL; }

View File

@@ -9,8 +9,8 @@ namespace number {
#define LOG_NUMBER(prefix, type, obj) \
if ((obj) != nullptr) { \
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, (obj)->get_name().c_str()); \
if (!(obj)->get_icon().empty()) { \
ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, (obj)->get_icon().c_str()); \
if (!(obj)->traits.get_icon().empty()) { \
ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, (obj)->traits.get_icon().c_str()); \
} \
}
@@ -19,72 +19,56 @@ class Number;
class NumberCall {
public:
explicit NumberCall(Number *parent) : parent_(parent) {}
NumberCall &set_value(float value);
void perform();
const optional<float> &get_value() const;
NumberCall &set_value(float value) {
value_ = value;
return *this;
}
const optional<float> &get_value() const { return value_; }
protected:
Number *const parent_;
optional<float> value_;
};
class NumberTraits {
public:
void set_min_value(float min_value) { min_value_ = min_value; }
float get_min_value() const { return min_value_; }
void set_max_value(float max_value) { max_value_ = max_value; }
float get_max_value() const { return max_value_; }
void set_step(float step) { step_ = step; }
float get_step() const { return step_; }
void set_icon(std::string icon) { icon_ = std::move(icon); }
const std::string &get_icon() const { return icon_; }
protected:
float min_value_ = NAN;
float max_value_ = NAN;
float step_ = NAN;
std::string icon_;
};
/** Base-class for all numbers.
*
* A number can use publish_state to send out a new value.
*/
class Number : public Nameable {
public:
explicit Number();
explicit Number(const std::string &name);
/** Manually set the icon of this number. By default the number's default defined by icon() is used.
*
* @param icon The icon, for example "mdi:flash". "" to disable.
*/
void set_icon(const std::string &icon);
/// Get the Home Assistant Icon. Uses the manual override if specified or the default value instead.
std::string get_icon();
/// Getter-syntax for .state.
float get_state() const;
/// Get the accuracy in decimals. Based on the step value.
int8_t get_accuracy_decimals();
/** Publish the current state to the front-end.
*/
void publish_state(float state);
NumberCall make_call();
// ========== INTERNAL METHODS ==========
// (In most use cases you won't need these)
/// Add a callback that will be called every time the state changes.
void add_on_state_callback(std::function<void(float)> &&callback);
/** This member variable stores the last state.
*
* On startup, when no state is available yet, this is NAN (not-a-number) and the validity
* can be checked using has_state().
*
* This is exposed through a member variable for ease of use in esphome lambdas.
*/
float state;
void publish_state(float state);
NumberCall make_call() { return NumberCall(this); }
void set(float value) { make_call().set_value(value).perform(); }
void add_on_state_callback(std::function<void(float)> &&callback);
NumberTraits traits;
/// Return whether this number has gotten a full state yet.
bool has_state() const;
/// Return with which interval the number is polled. Return 0 for non-polling mode.
virtual uint32_t update_interval();
void set_min_value(float min_value) { this->min_value_ = min_value; }
void set_max_value(float max_value) { this->max_value_ = max_value; }
void set_step(float step) { this->step_ = step; }
float get_min_value() const { return this->min_value_; }
float get_max_value() const { return this->max_value_; }
float get_step() const { return this->step_; }
bool has_state() const { return has_state_; }
protected:
friend class NumberCall;
@@ -95,17 +79,12 @@ class Number : public Nameable {
*
* @param value The value as validated by the NumberCall.
*/
virtual void set(float value) = 0;
virtual void control(float value) = 0;
uint32_t hash_base() override;
CallbackManager<void(float)> state_callback_;
/// Override the icon advertised to Home Assistant, otherwise number's icon will be used.
optional<std::string> icon_;
bool has_state_{false};
float step_{1.0};
float min_value_{0};
float max_value_{100};
};
} // namespace number

View File

@@ -35,9 +35,9 @@ void PIDClimate::control(const climate::ClimateCall &call) {
if (call.get_target_temperature().has_value())
this->target_temperature = *call.get_target_temperature();
// If switching to non-auto mode, set output immediately
if (this->mode != climate::CLIMATE_MODE_HEAT_COOL)
this->handle_non_auto_mode_();
// If switching to off mode, set output immediately
if (this->mode == climate::CLIMATE_MODE_OFF)
this->write_output_(0.0f);
this->publish_state();
}
@@ -98,15 +98,6 @@ void PIDClimate::write_output_(float value) {
}
this->pid_computed_callback_.call();
}
void PIDClimate::handle_non_auto_mode_() {
// in non-auto mode, switch directly to appropriate action
// - OFF mode -> Output at 0%
if (this->mode == climate::CLIMATE_MODE_OFF) {
this->write_output_(0.0);
} else {
assert(false);
}
}
void PIDClimate::update_pid_() {
float value;
if (isnan(this->current_temperature) || isnan(this->target_temperature)) {
@@ -135,7 +126,7 @@ void PIDClimate::update_pid_() {
}
if (this->mode == climate::CLIMATE_MODE_OFF) {
this->handle_non_auto_mode_();
this->write_output_(0.0);
} else {
this->write_output_(value);
}

View File

@@ -56,7 +56,6 @@ class PIDClimate : public climate::Climate, public Component {
bool supports_heat_() const { return this->heat_output_ != nullptr; }
void write_output_(float value);
void handle_non_auto_mode_();
/// The sensor used for getting the current temperature
sensor::Sensor *sensor_;

View File

@@ -11,8 +11,8 @@ from esphome.const import (
CONF_TOTAL,
CONF_VALUE,
ICON_PULSE,
LAST_RESET_TYPE_AUTO,
STATE_CLASS_MEASUREMENT,
STATE_CLASS_NONE,
UNIT_PULSES,
UNIT_PULSES_PER_MINUTE,
DEVICE_CLASS_EMPTY,
@@ -59,7 +59,12 @@ CONFIG_SCHEMA = sensor.sensor_schema(
cv.Optional(CONF_INTERNAL_FILTER, default="13us"): validate_internal_filter,
cv.Optional(CONF_TIMEOUT, default="5min"): validate_timeout,
cv.Optional(CONF_TOTAL): sensor.sensor_schema(
UNIT_PULSES, ICON_PULSE, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE
UNIT_PULSES,
ICON_PULSE,
0,
DEVICE_CLASS_EMPTY,
STATE_CLASS_MEASUREMENT,
LAST_RESET_TYPE_AUTO,
),
}
)

View File

@@ -12,8 +12,8 @@ from esphome.const import (
DEVICE_CLASS_POWER,
DEVICE_CLASS_VOLTAGE,
ICON_EMPTY,
LAST_RESET_TYPE_AUTO,
STATE_CLASS_MEASUREMENT,
STATE_CLASS_NONE,
UNIT_VOLT,
UNIT_AMPERE,
UNIT_WATT,
@@ -47,7 +47,8 @@ CONFIG_SCHEMA = (
ICON_EMPTY,
0,
DEVICE_CLASS_ENERGY,
STATE_CLASS_NONE,
STATE_CLASS_MEASUREMENT,
LAST_RESET_TYPE_AUTO,
),
}
)

View File

@@ -17,8 +17,8 @@ from esphome.const import (
DEVICE_CLASS_ENERGY,
ICON_EMPTY,
ICON_CURRENT_AC,
LAST_RESET_TYPE_AUTO,
STATE_CLASS_MEASUREMENT,
STATE_CLASS_NONE,
UNIT_HERTZ,
UNIT_VOLT,
UNIT_AMPERE,
@@ -54,7 +54,8 @@ CONFIG_SCHEMA = (
ICON_EMPTY,
0,
DEVICE_CLASS_ENERGY,
STATE_CLASS_NONE,
STATE_CLASS_MEASUREMENT,
LAST_RESET_TYPE_AUTO,
),
cv.Optional(CONF_FREQUENCY): sensor.sensor_schema(
UNIT_HERTZ,

View File

@@ -41,6 +41,7 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase,
bool initialized_{false};
std::vector<rmt_item32_t> rmt_temp_;
esp_err_t error_code_{ESP_OK};
bool inverted_{false};
#endif
uint8_t carrier_duty_percent_{50};
};

View File

@@ -9,7 +9,7 @@ namespace remote_transmitter {
static const char *const TAG = "remote_transmitter";
void RemoteTransmitterComponent::setup() {}
void RemoteTransmitterComponent::setup() { this->configure_rmt(); }
void RemoteTransmitterComponent::dump_config() {
ESP_LOGCONFIG(TAG, "Remote Transmitter...");
@@ -50,6 +50,7 @@ void RemoteTransmitterComponent::configure_rmt() {
} else {
c.tx_config.carrier_level = RMT_CARRIER_LEVEL_LOW;
c.tx_config.idle_level = RMT_IDLE_LEVEL_HIGH;
this->inverted_ = true;
}
esp_err_t error = rmt_config(&c);
@@ -95,10 +96,10 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen
val -= item;
if (rmt_i % 2 == 0) {
rmt_item.level0 = static_cast<uint32_t>(level);
rmt_item.level0 = static_cast<uint32_t>(level ^ this->inverted_);
rmt_item.duration0 = static_cast<uint32_t>(item);
} else {
rmt_item.level1 = static_cast<uint32_t>(level);
rmt_item.level1 = static_cast<uint32_t>(level ^ this->inverted_);
rmt_item.duration1 = static_cast<uint32_t>(item);
this->rmt_temp_.push_back(rmt_item);
}

View File

@@ -25,8 +25,8 @@ from esphome.const import (
ICON_CURRENT_AC,
ICON_EMPTY,
ICON_FLASH,
LAST_RESET_TYPE_AUTO,
STATE_CLASS_MEASUREMENT,
STATE_CLASS_NONE,
UNIT_AMPERE,
UNIT_DEGREES,
UNIT_EMPTY,
@@ -88,24 +88,36 @@ CONFIG_SCHEMA = (
STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_IMPORT_ACTIVE_ENERGY): sensor.sensor_schema(
UNIT_WATT_HOURS, ICON_EMPTY, 2, DEVICE_CLASS_ENERGY, STATE_CLASS_NONE
UNIT_WATT_HOURS,
ICON_EMPTY,
2,
DEVICE_CLASS_ENERGY,
STATE_CLASS_MEASUREMENT,
LAST_RESET_TYPE_AUTO,
),
cv.Optional(CONF_EXPORT_ACTIVE_ENERGY): sensor.sensor_schema(
UNIT_WATT_HOURS, ICON_EMPTY, 2, DEVICE_CLASS_ENERGY, STATE_CLASS_NONE
UNIT_WATT_HOURS,
ICON_EMPTY,
2,
DEVICE_CLASS_ENERGY,
STATE_CLASS_MEASUREMENT,
LAST_RESET_TYPE_AUTO,
),
cv.Optional(CONF_IMPORT_REACTIVE_ENERGY): sensor.sensor_schema(
UNIT_VOLT_AMPS_REACTIVE_HOURS,
ICON_EMPTY,
2,
DEVICE_CLASS_ENERGY,
STATE_CLASS_NONE,
STATE_CLASS_MEASUREMENT,
LAST_RESET_TYPE_AUTO,
),
cv.Optional(CONF_EXPORT_REACTIVE_ENERGY): sensor.sensor_schema(
UNIT_VOLT_AMPS_REACTIVE_HOURS,
ICON_EMPTY,
2,
DEVICE_CLASS_ENERGY,
STATE_CLASS_NONE,
STATE_CLASS_MEASUREMENT,
LAST_RESET_TYPE_AUTO,
),
}
)

View File

@@ -17,6 +17,7 @@ from esphome.const import (
CONF_ICON,
CONF_ID,
CONF_INTERNAL,
CONF_LAST_RESET_TYPE,
CONF_ON_RAW_VALUE,
CONF_ON_VALUE,
CONF_ON_VALUE_RANGE,
@@ -30,6 +31,9 @@ from esphome.const import (
CONF_NAME,
CONF_MQTT_ID,
CONF_FORCE_UPDATE,
LAST_RESET_TYPE_AUTO,
LAST_RESET_TYPE_NEVER,
LAST_RESET_TYPE_NONE,
UNIT_EMPTY,
ICON_EMPTY,
DEVICE_CLASS_EMPTY,
@@ -79,6 +83,15 @@ STATE_CLASSES = {
}
validate_state_class = cv.enum(STATE_CLASSES, lower=True, space="_")
LastResetTypes = sensor_ns.enum("LastResetType")
LAST_RESET_TYPES = {
LAST_RESET_TYPE_NONE: LastResetTypes.LAST_RESET_TYPE_NONE,
LAST_RESET_TYPE_NEVER: LastResetTypes.LAST_RESET_TYPE_NEVER,
LAST_RESET_TYPE_AUTO: LastResetTypes.LAST_RESET_TYPE_AUTO,
}
validate_last_reset_type = cv.enum(LAST_RESET_TYPES, lower=True, space="_")
IS_PLATFORM_COMPONENT = True
@@ -168,6 +181,7 @@ SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend(
cv.Optional(CONF_ACCURACY_DECIMALS): accuracy_decimals,
cv.Optional(CONF_DEVICE_CLASS): device_class,
cv.Optional(CONF_STATE_CLASS): validate_state_class,
cv.Optional(CONF_LAST_RESET_TYPE): validate_last_reset_type,
cv.Optional(CONF_FORCE_UPDATE, default=False): cv.boolean,
cv.Optional(CONF_EXPIRE_AFTER): cv.All(
cv.requires_component("mqtt"),
@@ -202,6 +216,7 @@ def sensor_schema(
accuracy_decimals_: int,
device_class_: Optional[str] = DEVICE_CLASS_EMPTY,
state_class_: Optional[str] = STATE_CLASS_NONE,
last_reset_type_: Optional[str] = LAST_RESET_TYPE_NONE,
) -> cv.Schema:
schema = SENSOR_SCHEMA
if unit_of_measurement_ != UNIT_EMPTY:
@@ -230,6 +245,14 @@ def sensor_schema(
schema = schema.extend(
{cv.Optional(CONF_STATE_CLASS, default=state_class_): validate_state_class}
)
if last_reset_type_ != LAST_RESET_TYPE_NONE:
schema = schema.extend(
{
cv.Optional(
CONF_LAST_RESET_TYPE, default=last_reset_type_
): validate_last_reset_type
}
)
return schema
@@ -479,6 +502,8 @@ async def setup_sensor_core_(var, config):
cg.add(var.set_icon(config[CONF_ICON]))
if CONF_ACCURACY_DECIMALS in config:
cg.add(var.set_accuracy_decimals(config[CONF_ACCURACY_DECIMALS]))
if CONF_LAST_RESET_TYPE in config:
cg.add(var.set_last_reset_type(config[CONF_LAST_RESET_TYPE]))
cg.add(var.set_force_update(config[CONF_FORCE_UPDATE]))
if config.get(CONF_FILTERS): # must exist and not be empty
filters = await build_filters(config[CONF_FILTERS])

View File

@@ -16,6 +16,18 @@ const char *state_class_to_string(StateClass state_class) {
}
}
const char *last_reset_type_to_string(LastResetType last_reset_type) {
switch (last_reset_type) {
case LAST_RESET_TYPE_NEVER:
return "never";
case LAST_RESET_TYPE_AUTO:
return "auto";
case LAST_RESET_TYPE_NONE:
default:
return "";
}
}
void Sensor::publish_state(float state) {
this->raw_state = state;
this->raw_callback_.call(state);
@@ -64,6 +76,7 @@ void Sensor::set_state_class(const std::string &state_class) {
ESP_LOGW(TAG, "'%s' - Unrecognized state class %s", this->get_name().c_str(), state_class.c_str());
}
}
void Sensor::set_last_reset_type(LastResetType last_reset_type) { this->last_reset_type = last_reset_type; }
std::string Sensor::get_unit_of_measurement() {
if (this->unit_of_measurement_.has_value())
return *this->unit_of_measurement_;

View File

@@ -14,6 +14,10 @@ namespace sensor {
ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, (obj)->get_device_class().c_str()); \
} \
ESP_LOGCONFIG(TAG, "%s State Class: '%s'", prefix, state_class_to_string((obj)->state_class)); \
if ((obj)->state_class == sensor::STATE_CLASS_MEASUREMENT && \
(obj)->last_reset_type != sensor::LAST_RESET_TYPE_NONE) { \
ESP_LOGCONFIG(TAG, "%s Last Reset Type: '%s'", prefix, last_reset_type_to_string((obj)->last_reset_type)); \
} \
ESP_LOGCONFIG(TAG, "%s Unit of Measurement: '%s'", prefix, (obj)->get_unit_of_measurement().c_str()); \
ESP_LOGCONFIG(TAG, "%s Accuracy Decimals: %d", prefix, (obj)->get_accuracy_decimals()); \
if (!(obj)->get_icon().empty()) { \
@@ -37,6 +41,20 @@ enum StateClass : uint8_t {
const char *state_class_to_string(StateClass state_class);
/**
* Sensor last reset types
*/
enum LastResetType : uint8_t {
/// This sensor does not support resetting. ie, it is not accumulative
LAST_RESET_TYPE_NONE = 0,
/// This sensor is expected to never reset its value
LAST_RESET_TYPE_NEVER = 1,
/// This sensor may reset and Home Assistant will watch for this
LAST_RESET_TYPE_AUTO = 2,
};
const char *last_reset_type_to_string(LastResetType last_reset_type);
/** Base-class for all sensors.
*
* A sensor has unit of measurement and can use publish_state to send out a new value with the specified accuracy.
@@ -155,6 +173,12 @@ class Sensor : public Nameable {
*/
virtual std::string device_class();
// The Last reset type of this sensor
LastResetType last_reset_type{LAST_RESET_TYPE_NONE};
/// Manually set the Home Assistant last reset type for this sensor.
void set_last_reset_type(LastResetType last_reset_type);
/** A unique ID for this sensor, empty for no unique id. See unique ID requirements:
* https://developers.home-assistant.io/docs/en/entity_registry_index.html#unique-id-requirements
*

View File

@@ -4,10 +4,12 @@ import esphome.config_validation as cv
from esphome.components import number
from esphome.const import (
CONF_ID,
CONF_INITIAL_VALUE,
CONF_LAMBDA,
CONF_MAX_VALUE,
CONF_MIN_VALUE,
CONF_OPTIMISTIC,
CONF_RESTORE_VALUE,
CONF_STEP,
)
from .. import template_ns
@@ -25,6 +27,17 @@ def validate_min_max(config):
return config
def validate(config):
if CONF_LAMBDA in config:
if CONF_OPTIMISTIC in config:
raise cv.Invalid("optimistic cannot be used with lambda")
if CONF_INITIAL_VALUE in config:
raise cv.Invalid("initial_value cannot be used with lambda")
if CONF_RESTORE_VALUE in config:
raise cv.Invalid("restore_value cannot be used with lambda")
return config
CONFIG_SCHEMA = cv.All(
number.NUMBER_SCHEMA.extend(
{
@@ -33,31 +46,43 @@ CONFIG_SCHEMA = cv.All(
cv.Required(CONF_MIN_VALUE): cv.float_,
cv.Required(CONF_STEP): cv.positive_float,
cv.Optional(CONF_LAMBDA): cv.returning_lambda,
cv.Optional(CONF_OPTIMISTIC, default=False): cv.boolean,
cv.Optional(CONF_OPTIMISTIC): cv.boolean,
cv.Optional(CONF_SET_ACTION): automation.validate_automation(single=True),
cv.Optional(CONF_INITIAL_VALUE): cv.float_,
cv.Optional(CONF_RESTORE_VALUE): cv.boolean,
}
).extend(cv.polling_component_schema("60s")),
validate_min_max,
validate,
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await number.register_number(var, config)
await number.register_number(
var,
config,
min_value=config[CONF_MIN_VALUE],
max_value=config[CONF_MAX_VALUE],
step=config[CONF_STEP],
)
if CONF_LAMBDA in config:
template_ = await cg.process_lambda(
config[CONF_LAMBDA], [], return_type=cg.optional.template(float)
)
cg.add(var.set_template(template_))
else:
if CONF_OPTIMISTIC in config:
cg.add(var.set_optimistic(config[CONF_OPTIMISTIC]))
if CONF_INITIAL_VALUE in config:
cg.add(var.set_initial_value(config[CONF_INITIAL_VALUE]))
if CONF_RESTORE_VALUE in config:
cg.add(var.set_restore_value(config[CONF_RESTORE_VALUE]))
if CONF_SET_ACTION in config:
await automation.build_automation(
var.get_set_trigger(), [(float, "x")], config[CONF_SET_ACTION]
)
cg.add(var.set_optimistic(config[CONF_OPTIMISTIC]))
cg.add(var.set_min_value(config[CONF_MIN_VALUE]))
cg.add(var.set_max_value(config[CONF_MAX_VALUE]))
cg.add(var.set_step(config[CONF_STEP]))

View File

@@ -6,34 +6,50 @@ namespace template_ {
static const char *const TAG = "template.number";
TemplateNumber::TemplateNumber() : set_trigger_(new Trigger<float>()) {}
void TemplateNumber::setup() {
if (this->f_.has_value())
return;
float value;
if (!this->restore_value_) {
value = this->initial_value_;
} else {
this->pref_ = global_preferences.make_preference<float>(this->get_object_id_hash());
if (!this->pref_.load(&value)) {
if (!isnan(this->initial_value_))
value = this->initial_value_;
else
value = this->traits.get_min_value();
}
}
this->publish_state(value);
}
void TemplateNumber::update() {
if (!this->f_.has_value())
return;
auto val = (*this->f_)();
if (val.has_value()) {
this->publish_state(*val);
}
if (!val.has_value())
return;
this->publish_state(*val);
}
void TemplateNumber::set(float value) {
void TemplateNumber::control(float value) {
this->set_trigger_->trigger(value);
if (this->optimistic_)
this->publish_state(value);
if (this->restore_value_)
this->pref_.save(&value);
}
float TemplateNumber::get_setup_priority() const { return setup_priority::HARDWARE; }
void TemplateNumber::set_template(std::function<optional<float>()> &&f) { this->f_ = f; }
void TemplateNumber::dump_config() {
LOG_NUMBER("", "Template Number", this);
ESP_LOGCONFIG(TAG, " Optimistic: %s", YESNO(this->optimistic_));
LOG_UPDATE_INTERVAL(this);
}
void TemplateNumber::set_optimistic(bool optimistic) { this->optimistic_ = optimistic; }
Trigger<float> *TemplateNumber::get_set_trigger() const { return this->set_trigger_; };
} // namespace template_
} // namespace esphome

View File

@@ -3,27 +3,34 @@
#include "esphome/components/number/number.h"
#include "esphome/core/automation.h"
#include "esphome/core/component.h"
#include "esphome/core/preferences.h"
namespace esphome {
namespace template_ {
class TemplateNumber : public number::Number, public PollingComponent {
public:
TemplateNumber();
void set_template(std::function<optional<float>()> &&f);
void set_template(std::function<optional<float>()> &&f) { this->f_ = f; }
void setup() override;
void update() override;
void dump_config() override;
float get_setup_priority() const override;
float get_setup_priority() const override { return setup_priority::HARDWARE; }
Trigger<float> *get_set_trigger() const;
void set_optimistic(bool optimistic);
Trigger<float> *get_set_trigger() const { return set_trigger_; }
void set_optimistic(bool optimistic) { optimistic_ = optimistic; }
void set_initial_value(float initial_value) { initial_value_ = initial_value; }
void set_restore_value(bool restore_value) { this->restore_value_ = restore_value; }
protected:
void set(float value) override;
void control(float value) override;
bool optimistic_{false};
Trigger<float> *set_trigger_;
float initial_value_{NAN};
bool restore_value_{false};
Trigger<float> *set_trigger_ = new Trigger<float>();
optional<std::function<optional<float>()>> f_;
ESPPreferenceObject pref_;
};
} // namespace template_

View File

@@ -22,7 +22,10 @@ void CronTrigger::loop() {
return;
if (this->last_check_.has_value()) {
if (*this->last_check_ >= time) {
if (*this->last_check_ > time && this->last_check_->timestamp - time.timestamp > 900) {
// We went back in time (a lot), probably caused by time synchronization
ESP_LOGW(TAG, "Time has jumped back!");
} else if (*this->last_check_ >= time) {
// already handled this one
return;
}

View File

@@ -1,7 +1,15 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor, time
from esphome.const import CONF_ID, CONF_TIME_ID
from esphome.const import (
CONF_ID,
CONF_TIME_ID,
DEVICE_CLASS_ENERGY,
ICON_EMPTY,
LAST_RESET_TYPE_AUTO,
STATE_CLASS_MEASUREMENT,
UNIT_EMPTY,
)
DEPENDENCIES = ["time"]
@@ -11,13 +19,24 @@ TotalDailyEnergy = total_daily_energy_ns.class_(
"TotalDailyEnergy", sensor.Sensor, cg.Component
)
CONFIG_SCHEMA = sensor.SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(TotalDailyEnergy),
cv.GenerateID(CONF_TIME_ID): cv.use_id(time.RealTimeClock),
cv.Required(CONF_POWER_ID): cv.use_id(sensor.Sensor),
}
).extend(cv.COMPONENT_SCHEMA)
CONFIG_SCHEMA = (
sensor.sensor_schema(
UNIT_EMPTY,
ICON_EMPTY,
0,
DEVICE_CLASS_ENERGY,
STATE_CLASS_MEASUREMENT,
LAST_RESET_TYPE_AUTO,
)
.extend(
{
cv.GenerateID(): cv.declare_id(TotalDailyEnergy),
cv.GenerateID(CONF_TIME_ID): cv.use_id(time.RealTimeClock),
cv.Required(CONF_POWER_ID): cv.use_id(sensor.Sensor),
}
)
.extend(cv.COMPONENT_SCHEMA)
)
async def to_code(config):

View File

@@ -7,10 +7,11 @@ namespace esphome {
namespace tuya {
static const char *const TAG = "tuya";
static const int COMMAND_DELAY = 50;
static const int COMMAND_DELAY = 10;
static const int RECEIVE_TIMEOUT = 300;
void Tuya::setup() {
this->set_interval("heartbeat", 10000, [this] { this->send_empty_command_(TuyaCommandType::HEARTBEAT); });
this->set_interval("heartbeat", 15000, [this] { this->send_empty_command_(TuyaCommandType::HEARTBEAT); });
}
void Tuya::loop() {
@@ -113,11 +114,19 @@ void Tuya::handle_char_(uint8_t c) {
this->rx_message_.push_back(c);
if (!this->validate_message_()) {
this->rx_message_.clear();
} else {
this->last_rx_char_timestamp_ = millis();
}
}
void Tuya::handle_command_(uint8_t command, uint8_t version, const uint8_t *buffer, size_t len) {
switch ((TuyaCommandType) command) {
TuyaCommandType command_type = (TuyaCommandType) command;
if (this->expected_response_.has_value() && this->expected_response_ == command_type) {
this->expected_response_.reset();
}
switch (command_type) {
case TuyaCommandType::HEARTBEAT:
ESP_LOGV(TAG, "MCU Heartbeat (0x%02X)", buffer[0]);
this->protocol_version_ = version;
@@ -316,6 +325,25 @@ void Tuya::send_raw_command_(TuyaCommand command) {
uint8_t version = 0;
this->last_command_timestamp_ = millis();
switch (command.cmd) {
case TuyaCommandType::HEARTBEAT:
this->expected_response_ = TuyaCommandType::HEARTBEAT;
break;
case TuyaCommandType::PRODUCT_QUERY:
this->expected_response_ = TuyaCommandType::PRODUCT_QUERY;
break;
case TuyaCommandType::CONF_QUERY:
this->expected_response_ = TuyaCommandType::CONF_QUERY;
break;
case TuyaCommandType::DATAPOINT_DELIVER:
this->expected_response_ = TuyaCommandType::DATAPOINT_REPORT;
break;
case TuyaCommandType::DATAPOINT_QUERY:
this->expected_response_ = TuyaCommandType::DATAPOINT_REPORT;
break;
default:
break;
}
ESP_LOGV(TAG, "Sending Tuya: CMD=0x%02X VERSION=%u DATA=[%s] INIT_STATE=%u", static_cast<uint8_t>(command.cmd),
version, hexencode(command.payload).c_str(), static_cast<uint8_t>(this->init_state_));
@@ -331,9 +359,20 @@ void Tuya::send_raw_command_(TuyaCommand command) {
}
void Tuya::process_command_queue_() {
uint32_t delay = millis() - this->last_command_timestamp_;
uint32_t now = millis();
uint32_t delay = now - this->last_command_timestamp_;
if (now - this->last_rx_char_timestamp_ > RECEIVE_TIMEOUT) {
this->rx_message_.clear();
}
if (this->expected_response_.has_value() && delay > RECEIVE_TIMEOUT) {
this->expected_response_.reset();
}
// Left check of delay since last command in case theres ever a command sent by calling send_raw_command_ directly
if (delay > COMMAND_DELAY && !this->command_queue_.empty() && this->rx_message_.empty()) {
if (delay > COMMAND_DELAY && !this->command_queue_.empty() && this->rx_message_.empty() &&
!this->expected_response_.has_value()) {
this->send_raw_command_(command_queue_.front());
this->command_queue_.erase(command_queue_.begin());
}
@@ -345,7 +384,7 @@ void Tuya::send_command_(const TuyaCommand &command) {
}
void Tuya::send_empty_command_(TuyaCommandType command) {
send_command_(TuyaCommand{.cmd = command, .payload = std::vector<uint8_t>{0x04}});
send_command_(TuyaCommand{.cmd = command, .payload = std::vector<uint8_t>{}});
}
void Tuya::send_wifi_status_() {

View File

@@ -107,12 +107,14 @@ class Tuya : public Component, public uart::UARTDevice {
int gpio_status_ = -1;
int gpio_reset_ = -1;
uint32_t last_command_timestamp_ = 0;
uint32_t last_rx_char_timestamp_ = 0;
std::string product_ = "";
std::vector<TuyaDatapointListener> listeners_;
std::vector<TuyaDatapoint> datapoints_;
std::vector<uint8_t> rx_message_;
std::vector<uint8_t> ignore_mcu_update_on_datapoints_{};
std::vector<TuyaCommand> command_queue_;
optional<TuyaCommandType> expected_response_{};
uint8_t wifi_status_ = -1;
};

View File

@@ -614,8 +614,9 @@ void WebServer::handle_number_request(AsyncWebServerRequest *request, const UrlM
std::string WebServer::number_json(number::Number *obj, float value) {
return json::build_json([obj, value](JsonObject &root) {
root["id"] = "number-" + obj->get_object_id();
std::string state = value_accuracy_to_string(value, obj->get_accuracy_decimals());
root["state"] = state;
char buffer[64];
snprintf(buffer, sizeof(buffer), "%f", value);
root["state"] = buffer;
root["value"] = value;
});
}

View File

@@ -1,6 +1,6 @@
"""Constants used by esphome."""
__version__ = "1.20.0b3"
__version__ = "1.20.4"
ESP_PLATFORM_ESP32 = "ESP32"
ESP_PLATFORM_ESP8266 = "ESP8266"
@@ -297,6 +297,7 @@ CONF_KEY = "key"
CONF_LAMBDA = "lambda"
CONF_LAST_CONFIDENCE = "last_confidence"
CONF_LAST_FINGER_ID = "last_finger_id"
CONF_LAST_RESET_TYPE = "last_reset_type"
CONF_LATITUDE = "latitude"
CONF_LENGTH = "length"
CONF_LEVEL = "level"
@@ -785,3 +786,10 @@ STATE_CLASS_NONE = ""
# The state represents a measurement in present time
STATE_CLASS_MEASUREMENT = "measurement"
# This sensor does not support resetting. ie, it is not accumulative
LAST_RESET_TYPE_NONE = ""
# This sensor is expected to never reset its value
LAST_RESET_TYPE_NEVER = "never"
# This sensor may reset and Home Assistant will watch for this
LAST_RESET_TYPE_AUTO = "auto"

View File

@@ -1104,7 +1104,7 @@ def shorthand_input_pullup_pin(value):
def shorthand_analog_pin(value):
value = analog_pin(value)
return GPIO_FULL_INPUT_PIN_SCHEMA({CONF_NUMBER: value})
return GPIO_FULL_ANALOG_PIN_SCHEMA({CONF_NUMBER: value})
def validate_has_interrupt(value):

View File

@@ -11,4 +11,4 @@ ifaddr==0.1.7
platformio==5.1.1
esptool==2.8
click==7.1.2
esphome-dashboard==20210623.0
esphome-dashboard==20210728.0