Merge branch 'dev' of https://github.com/esphome/esphome into lvgl

This commit is contained in:
clydebarrow 2024-05-13 19:06:57 +10:00
commit 7b9de4a627
15 changed files with 448 additions and 8 deletions

View File

@ -135,6 +135,7 @@ esphome/components/fs3000/* @kahrendt
esphome/components/ft5x06/* @clydebarrow esphome/components/ft5x06/* @clydebarrow
esphome/components/ft63x6/* @gpambrozio esphome/components/ft63x6/* @gpambrozio
esphome/components/gcja5/* @gcormier esphome/components/gcja5/* @gcormier
esphome/components/gdk101/* @Szewcson
esphome/components/globals/* @esphome/core esphome/components/globals/* @esphome/core
esphome/components/gp8403/* @jesserockz esphome/components/gp8403/* @jesserockz
esphome/components/gpio/* @esphome/core esphome/components/gpio/* @esphome/core

View File

@ -0,0 +1,32 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c
from esphome.const import CONF_ID
CODEOWNERS = ["@Szewcson"]
DEPENDENCIES = ["i2c"]
MULTI_CONF = True
CONF_GDK101_ID = "gdk101_id"
gdk101_ns = cg.esphome_ns.namespace("gdk101")
GDK101Component = gdk101_ns.class_(
"GDK101Component", cg.PollingComponent, i2c.I2CDevice
)
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(GDK101Component),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(0x18))
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)

View File

@ -0,0 +1,29 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import binary_sensor
from esphome.const import (
CONF_VIBRATIONS,
DEVICE_CLASS_VIBRATION,
ENTITY_CATEGORY_DIAGNOSTIC,
ICON_VIBRATE,
)
from . import CONF_GDK101_ID, GDK101Component
DEPENDENCIES = ["gdk101"]
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_GDK101_ID): cv.use_id(GDK101Component),
cv.Required(CONF_VIBRATIONS): binary_sensor.binary_sensor_schema(
device_class=DEVICE_CLASS_VIBRATION,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
icon=ICON_VIBRATE,
),
}
)
async def to_code(config):
hub = await cg.get_variable(config[CONF_GDK101_ID])
var = await binary_sensor.new_binary_sensor(config[CONF_VIBRATIONS])
cg.add(hub.set_vibration_binary_sensor(var))

View File

@ -0,0 +1,189 @@
#include "gdk101.h"
#include "esphome/core/hal.h"
#include "esphome/core/log.h"
namespace esphome {
namespace gdk101 {
static const char *const TAG = "gdk101";
static const uint8_t NUMBER_OF_READ_RETRIES = 5;
void GDK101Component::update() {
uint8_t data[2];
if (!this->read_dose_1m_(data)) {
this->status_set_warning("Failed to read dose 1m");
return;
}
if (!this->read_dose_10m_(data)) {
this->status_set_warning("Failed to read dose 10m");
return;
}
if (!this->read_status_(data)) {
this->status_set_warning("Failed to read status");
return;
}
if (!this->read_measurement_duration_(data)) {
this->status_set_warning("Failed to read measurement duration");
return;
}
this->status_clear_warning();
}
void GDK101Component::setup() {
uint8_t data[2];
ESP_LOGCONFIG(TAG, "Setting up GDK101...");
// first, reset the sensor
if (!this->reset_sensor_(data)) {
this->status_set_error("Reset failed!");
this->mark_failed();
return;
}
// sensor should acknowledge success of the reset procedure
if (data[0] != 1) {
this->status_set_error("Reset not acknowledged!");
this->mark_failed();
return;
}
delay(10);
// read firmware version
if (!this->read_fw_version_(data)) {
this->status_set_error("Failed to read firmware version");
this->mark_failed();
return;
}
}
void GDK101Component::dump_config() {
ESP_LOGCONFIG(TAG, "GDK101:");
LOG_I2C_DEVICE(this);
if (this->is_failed()) {
ESP_LOGE(TAG, "Communication with GDK101 failed!");
}
#ifdef USE_SENSOR
LOG_SENSOR(" ", "Firmware Version", this->fw_version_sensor_);
LOG_SENSOR(" ", "Average Radaition Dose per 1 minute", this->rad_1m_sensor_);
LOG_SENSOR(" ", "Average Radaition Dose per 10 minutes", this->rad_10m_sensor_);
LOG_SENSOR(" ", "Status", this->status_sensor_);
LOG_SENSOR(" ", "Measurement Duration", this->measurement_duration_sensor_);
#endif // USE_SENSOR
#ifdef USE_BINARY_SENSOR
LOG_BINARY_SENSOR(" ", "Vibration Status", this->vibration_binary_sensor_);
#endif // USE_BINARY_SENSOR
}
float GDK101Component::get_setup_priority() const { return setup_priority::DATA; }
bool GDK101Component::read_bytes_with_retry_(uint8_t a_register, uint8_t *data, uint8_t len) {
uint8_t retry = NUMBER_OF_READ_RETRIES;
bool status = false;
while (!status && retry) {
status = this->read_bytes(a_register, data, len);
retry--;
}
return status;
}
bool GDK101Component::reset_sensor_(uint8_t *data) {
// It looks like reset is not so well designed in that sensor
// After sending reset command it looks that sensor start performing reset and is unresponsible during read
// after a while we can send another reset command and read "0x01" as confirmation
// Documentation not going in to such details unfortunately
if (!this->read_bytes_with_retry_(GDK101_REG_RESET, data, 2)) {
ESP_LOGE(TAG, "Updating GDK101 failed!");
return false;
}
return true;
}
bool GDK101Component::read_dose_1m_(uint8_t *data) {
#ifdef USE_SENSOR
if (this->rad_1m_sensor_ != nullptr) {
if (!this->read_bytes(GDK101_REG_READ_1MIN_AVG, data, 2)) {
ESP_LOGE(TAG, "Updating GDK101 failed!");
return false;
}
const float dose = data[0] + (data[1] / 100.0f);
this->rad_1m_sensor_->publish_state(dose);
}
#endif // USE_SENSOR
return true;
}
bool GDK101Component::read_dose_10m_(uint8_t *data) {
#ifdef USE_SENSOR
if (this->rad_10m_sensor_ != nullptr) {
if (!this->read_bytes(GDK101_REG_READ_10MIN_AVG, data, 2)) {
ESP_LOGE(TAG, "Updating GDK101 failed!");
return false;
}
const float dose = data[0] + (data[1] / 100.0f);
this->rad_10m_sensor_->publish_state(dose);
}
#endif // USE_SENSOR
return true;
}
bool GDK101Component::read_status_(uint8_t *data) {
if (!this->read_bytes(GDK101_REG_READ_STATUS, data, 2)) {
ESP_LOGE(TAG, "Updating GDK101 failed!");
return false;
}
#ifdef USE_SENSOR
if (this->status_sensor_ != nullptr) {
this->status_sensor_->publish_state(data[0]);
}
#endif // USE_SENSOR
#ifdef USE_BINARY_SENSOR
if (this->vibration_binary_sensor_ != nullptr) {
this->vibration_binary_sensor_->publish_state(data[1]);
}
#endif // USE_BINARY_SENSOR
return true;
}
bool GDK101Component::read_fw_version_(uint8_t *data) {
#ifdef USE_SENSOR
if (this->fw_version_sensor_ != nullptr) {
if (!this->read_bytes(GDK101_REG_READ_FIRMWARE, data, 2)) {
ESP_LOGE(TAG, "Updating GDK101 failed!");
return false;
}
const float fw_version = data[0] + (data[1] / 10.0f);
this->fw_version_sensor_->publish_state(fw_version);
}
#endif // USE_SENSOR
return true;
}
bool GDK101Component::read_measurement_duration_(uint8_t *data) {
#ifdef USE_SENSOR
if (this->measurement_duration_sensor_ != nullptr) {
if (!this->read_bytes(GDK101_REG_READ_MEASURING_TIME, data, 2)) {
ESP_LOGE(TAG, "Updating GDK101 failed!");
return false;
}
const float meas_time = (data[0] * 60) + data[1];
this->measurement_duration_sensor_->publish_state(meas_time);
}
#endif // USE_SENSOR
return true;
}
} // namespace gdk101
} // namespace esphome

View File

@ -0,0 +1,52 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/defines.h"
#ifdef USE_SENSOR
#include "esphome/components/sensor/sensor.h"
#endif // USE_SENSOR
#ifdef USE_BINARY_SENSOR
#include "esphome/components/binary_sensor/binary_sensor.h"
#endif // USE_BINARY_SENSOR
#include "esphome/components/i2c/i2c.h"
namespace esphome {
namespace gdk101 {
static const uint8_t GDK101_REG_READ_FIRMWARE = 0xB4; // Firmware version
static const uint8_t GDK101_REG_RESET = 0xA0; // Reset register - reading its value triggers reset
static const uint8_t GDK101_REG_READ_STATUS = 0xB0; // Status register
static const uint8_t GDK101_REG_READ_MEASURING_TIME = 0xB1; // Mesuring time
static const uint8_t GDK101_REG_READ_10MIN_AVG = 0xB2; // Average radiation dose per 10 min
static const uint8_t GDK101_REG_READ_1MIN_AVG = 0xB3; // Average radiation dose per 1 min
class GDK101Component : public PollingComponent, public i2c::I2CDevice {
#ifdef USE_SENSOR
SUB_SENSOR(rad_1m)
SUB_SENSOR(rad_10m)
SUB_SENSOR(status)
SUB_SENSOR(fw_version)
SUB_SENSOR(measurement_duration)
#endif // USE_SENSOR
#ifdef USE_BINARY_SENSOR
SUB_BINARY_SENSOR(vibration)
#endif // USE_BINARY_SENSOR
public:
void setup() override;
void dump_config() override;
float get_setup_priority() const override;
void update() override;
protected:
bool read_bytes_with_retry_(uint8_t a_register, uint8_t *data, uint8_t len);
bool reset_sensor_(uint8_t *data);
bool read_dose_1m_(uint8_t *data);
bool read_dose_10m_(uint8_t *data);
bool read_status_(uint8_t *data);
bool read_fw_version_(uint8_t *data);
bool read_measurement_duration_(uint8_t *data);
};
} // namespace gdk101
} // namespace esphome

View File

@ -0,0 +1,83 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor
from esphome.const import (
DEVICE_CLASS_DURATION,
DEVICE_CLASS_EMPTY,
ENTITY_CATEGORY_DIAGNOSTIC,
CONF_MEASUREMENT_DURATION,
CONF_STATUS,
CONF_VERSION,
ICON_RADIOACTIVE,
ICON_TIMER,
STATE_CLASS_MEASUREMENT,
STATE_CLASS_TOTAL_INCREASING,
UNIT_MICROSILVERTS_PER_HOUR,
UNIT_SECOND,
)
from . import CONF_GDK101_ID, GDK101Component
CONF_RADIATION_DOSE_PER_1M = "radiation_dose_per_1m"
CONF_RADIATION_DOSE_PER_10M = "radiation_dose_per_10m"
DEPENDENCIES = ["gdk101"]
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_GDK101_ID): cv.use_id(GDK101Component),
cv.Optional(CONF_RADIATION_DOSE_PER_1M): sensor.sensor_schema(
icon=ICON_RADIOACTIVE,
unit_of_measurement=UNIT_MICROSILVERTS_PER_HOUR,
accuracy_decimals=2,
device_class=DEVICE_CLASS_EMPTY,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_RADIATION_DOSE_PER_10M): sensor.sensor_schema(
icon=ICON_RADIOACTIVE,
unit_of_measurement=UNIT_MICROSILVERTS_PER_HOUR,
accuracy_decimals=2,
device_class=DEVICE_CLASS_EMPTY,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_VERSION): sensor.sensor_schema(
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
accuracy_decimals=1,
),
cv.Optional(CONF_STATUS): sensor.sensor_schema(
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
accuracy_decimals=0,
),
cv.Optional(CONF_MEASUREMENT_DURATION): sensor.sensor_schema(
unit_of_measurement=UNIT_SECOND,
icon=ICON_TIMER,
accuracy_decimals=0,
state_class=STATE_CLASS_TOTAL_INCREASING,
device_class=DEVICE_CLASS_DURATION,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
),
}
)
async def to_code(config):
hub = await cg.get_variable(config[CONF_GDK101_ID])
if radiation_dose_per_1m := config.get(CONF_RADIATION_DOSE_PER_1M):
sens = await sensor.new_sensor(radiation_dose_per_1m)
cg.add(hub.set_rad_1m_sensor(sens))
if radiation_dose_per_10m := config.get(CONF_RADIATION_DOSE_PER_10M):
sens = await sensor.new_sensor(radiation_dose_per_10m)
cg.add(hub.set_rad_10m_sensor(sens))
if version_config := config.get(CONF_VERSION):
sens = await sensor.new_sensor(version_config)
cg.add(hub.set_fw_version_sensor(sens))
if status_config := config.get(CONF_STATUS):
sens = await sensor.new_sensor(status_config)
cg.add(hub.set_status_sensor(sens))
if measurement_duration_config := config.get(CONF_MEASUREMENT_DURATION):
sens = await sensor.new_sensor(measurement_duration_config)
cg.add(hub.set_measurement_duration_sensor(sens))

View File

@ -96,6 +96,9 @@ void TimeBasedCover::control(const CoverCall &call) {
} }
} else { } else {
auto op = pos < this->position ? COVER_OPERATION_CLOSING : COVER_OPERATION_OPENING; auto op = pos < this->position ? COVER_OPERATION_CLOSING : COVER_OPERATION_OPENING;
if (this->manual_control_ && (pos == COVER_OPEN || pos == COVER_CLOSED)) {
this->position = pos == COVER_CLOSED ? COVER_OPEN : COVER_CLOSED;
}
this->target_position_ = pos; this->target_position_ = pos;
this->start_direction_(op); this->start_direction_(op);
} }

View File

@ -94,10 +94,10 @@ class VoiceAssistant : public Component {
uint32_t get_feature_flags() const { uint32_t get_feature_flags() const {
uint32_t flags = 0; uint32_t flags = 0;
flags |= VoiceAssistantFeature::FEATURE_VOICE_ASSISTANT; flags |= VoiceAssistantFeature::FEATURE_VOICE_ASSISTANT;
flags |= VoiceAssistantFeature::FEATURE_API_AUDIO;
#ifdef USE_SPEAKER #ifdef USE_SPEAKER
if (this->speaker_ != nullptr) { if (this->speaker_ != nullptr) {
flags |= VoiceAssistantFeature::FEATURE_SPEAKER; flags |= VoiceAssistantFeature::FEATURE_SPEAKER;
flags |= VoiceAssistantFeature::FEATURE_API_AUDIO;
} }
#endif #endif
return flags; return flags;

View File

@ -884,6 +884,7 @@ CONF_VALUE_FONT = "value_font"
CONF_VARIABLES = "variables" CONF_VARIABLES = "variables"
CONF_VARIANT = "variant" CONF_VARIANT = "variant"
CONF_VERSION = "version" CONF_VERSION = "version"
CONF_VIBRATIONS = "vibrations"
CONF_VISIBLE = "visible" CONF_VISIBLE = "visible"
CONF_VISUAL = "visual" CONF_VISUAL = "visual"
CONF_VOLTAGE = "voltage" CONF_VOLTAGE = "voltage"
@ -983,6 +984,7 @@ ICON_SIGNAL_DISTANCE_VARIANT = "mdi:signal"
ICON_THERMOMETER = "mdi:thermometer" ICON_THERMOMETER = "mdi:thermometer"
ICON_TIMELAPSE = "mdi:timelapse" ICON_TIMELAPSE = "mdi:timelapse"
ICON_TIMER = "mdi:timer-outline" ICON_TIMER = "mdi:timer-outline"
ICON_VIBRATE = "mdi:vibrate"
ICON_WATER = "mdi:water" ICON_WATER = "mdi:water"
ICON_WATER_PERCENT = "mdi:water-percent" ICON_WATER_PERCENT = "mdi:water-percent"
ICON_WEATHER_SUNSET = "mdi:weather-sunset" ICON_WEATHER_SUNSET = "mdi:weather-sunset"
@ -1024,6 +1026,7 @@ UNIT_METER_PER_SECOND_SQUARED = "m/s²"
UNIT_MICROGRAMS_PER_CUBIC_METER = "µg/m³" UNIT_MICROGRAMS_PER_CUBIC_METER = "µg/m³"
UNIT_MICROMETER = "µm" UNIT_MICROMETER = "µm"
UNIT_MICROSIEMENS_PER_CENTIMETER = "µS/cm" UNIT_MICROSIEMENS_PER_CENTIMETER = "µS/cm"
UNIT_MICROSILVERTS_PER_HOUR = "µSv/h"
UNIT_MICROTESLA = "µT" UNIT_MICROTESLA = "µT"
UNIT_MILLIGRAMS_PER_CUBIC_METER = "mg/m³" UNIT_MILLIGRAMS_PER_CUBIC_METER = "mg/m³"
UNIT_MILLISECOND = "ms" UNIT_MILLISECOND = "ms"

View File

@ -2,7 +2,7 @@ import abc
import inspect import inspect
import math import math
import re import re
from collections.abc import Generator, Sequence from collections.abc import Sequence
from typing import Any, Callable, Optional, Union from typing import Any, Callable, Optional, Union
from esphome.core import ( from esphome.core import (
@ -477,6 +477,7 @@ def variable(
:param rhs: The expression to place on the right hand side of the assignment. :param rhs: The expression to place on the right hand side of the assignment.
:param type_: Manually define a type for the variable, only use this when it's not possible :param type_: Manually define a type for the variable, only use this when it's not possible
to do so during config validation phase (for example because of template arguments). to do so during config validation phase (for example because of template arguments).
:param register: If true register the variable with the core
:return: The new variable as a MockObj. :return: The new variable as a MockObj.
""" """
@ -492,9 +493,7 @@ def variable(
return obj return obj
def with_local_variable( def with_local_variable(id_: ID, rhs: SafeExpType, callback: Callable, *args) -> None:
id_: ID, rhs: SafeExpType, callback: Callable[["MockObj"], None], *args
) -> None:
"""Declare a new variable, not pointer type, in the code generation, within a scoped block """Declare a new variable, not pointer type, in the code generation, within a scoped block
The variable is only usable within the callback The variable is only usable within the callback
The callback cannot be async. The callback cannot be async.
@ -599,6 +598,7 @@ def add_library(name: str, version: Optional[str], repository: Optional[str] = N
:param name: The name of the library (for example 'AsyncTCP') :param name: The name of the library (for example 'AsyncTCP')
:param version: The version of the library, may be None. :param version: The version of the library, may be None.
:param repository: The repository for the library
""" """
CORE.add_library(Library(name, version, repository)) CORE.add_library(Library(name, version, repository))
@ -654,7 +654,7 @@ async def process_lambda(
parameters: list[tuple[SafeExpType, str]], parameters: list[tuple[SafeExpType, str]],
capture: str = "=", capture: str = "=",
return_type: SafeExpType = None, return_type: SafeExpType = None,
) -> Generator[LambdaExpression, None, None]: ) -> Union[LambdaExpression, None]:
"""Process the given lambda value into a LambdaExpression. """Process the given lambda value into a LambdaExpression.
This is a coroutine because lambdas can depend on other IDs, This is a coroutine because lambdas can depend on other IDs,
@ -673,7 +673,7 @@ async def process_lambda(
) )
if value is None: if value is None:
return return None
parts = value.parts[:] parts = value.parts[:]
for i, id in enumerate(value.requires_ids): for i, id in enumerate(value.requires_ids):
full_id, var = await get_variable_with_full_id(id) full_id, var = await get_variable_with_full_id(id)
@ -712,7 +712,7 @@ async def templatable(
value: Any, value: Any,
args: list[tuple[SafeExpType, str]], args: list[tuple[SafeExpType, str]],
output_type: Optional[SafeExpType], output_type: Optional[SafeExpType],
to_exp: Any = None, to_exp: Union[Callable, dict] = None,
): ):
"""Generate code for a templatable config option. """Generate code for a templatable config option.

View File

@ -0,0 +1,28 @@
i2c:
id: i2c_bus
sda: ${i2c_sda}
scl: ${i2c_scl}
gdk101:
id: my_gdk101
i2c_id: i2c_bus
sensor:
- platform: gdk101
gdk101_id: my_gdk101
radiation_dose_per_1m:
name: Radiation Dose @ 1 min
radiation_dose_per_10m:
name: Radiation Dose @ 10 min
status:
name: Status
version:
name: FW Version
measurement_duration:
name: Measuring Time
binary_sensor:
- platform: gdk101
gdk101_id: my_gdk101
vibrations:
name: Vibrations

View File

@ -0,0 +1,5 @@
substitutions:
i2c_scl: GPIO16
i2c_sda: GPIO17
<<: !include common.yaml

View File

@ -0,0 +1,5 @@
substitutions:
i2c_scl: GPIO16
i2c_sda: GPIO17
<<: !include common.yaml

View File

@ -0,0 +1,5 @@
substitutions:
i2c_scl: GPIO5
i2c_sda: GPIO4
<<: !include common.yaml

View File

@ -0,0 +1,5 @@
substitutions:
i2c_scl: GPIO5
i2c_sda: GPIO4
<<: !include common.yaml