mirror of
https://github.com/esphome/esphome.git
synced 2024-11-23 23:48:11 +01:00
Merge branch 'dev' into mcuboot_support
This commit is contained in:
commit
182c637e07
106 changed files with 1767 additions and 306 deletions
2
.github/actions/restore-python/action.yml
vendored
2
.github/actions/restore-python/action.yml
vendored
|
@ -17,7 +17,7 @@ runs:
|
||||||
steps:
|
steps:
|
||||||
- name: Set up Python ${{ inputs.python-version }}
|
- name: Set up Python ${{ inputs.python-version }}
|
||||||
id: python
|
id: python
|
||||||
uses: actions/setup-python@v5.1.1
|
uses: actions/setup-python@v5.2.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ inputs.python-version }}
|
python-version: ${{ inputs.python-version }}
|
||||||
- name: Restore Python virtual environment
|
- name: Restore Python virtual environment
|
||||||
|
|
2
.github/workflows/ci-api-proto.yml
vendored
2
.github/workflows/ci-api-proto.yml
vendored
|
@ -23,7 +23,7 @@ jobs:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4.1.7
|
uses: actions/checkout@v4.1.7
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v5.1.0
|
uses: actions/setup-python@v5.2.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.11"
|
python-version: "3.11"
|
||||||
|
|
||||||
|
|
2
.github/workflows/ci-docker.yml
vendored
2
.github/workflows/ci-docker.yml
vendored
|
@ -42,7 +42,7 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.7
|
- uses: actions/checkout@v4.1.7
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v5.1.0
|
uses: actions/setup-python@v5.2.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.9"
|
python-version: "3.9"
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
|
|
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
|
@ -41,7 +41,7 @@ jobs:
|
||||||
run: echo key="${{ hashFiles('requirements.txt', 'requirements_optional.txt', 'requirements_test.txt') }}" >> $GITHUB_OUTPUT
|
run: echo key="${{ hashFiles('requirements.txt', 'requirements_optional.txt', 'requirements_test.txt') }}" >> $GITHUB_OUTPUT
|
||||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||||
id: python
|
id: python
|
||||||
uses: actions/setup-python@v5.1.0
|
uses: actions/setup-python@v5.2.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||||
- name: Restore Python virtual environment
|
- name: Restore Python virtual environment
|
||||||
|
|
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
|
@ -53,7 +53,7 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.7
|
- uses: actions/checkout@v4.1.7
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v5.1.0
|
uses: actions/setup-python@v5.2.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
- name: Set up python environment
|
- name: Set up python environment
|
||||||
|
@ -85,7 +85,7 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.7
|
- uses: actions/checkout@v4.1.7
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v5.1.0
|
uses: actions/setup-python@v5.2.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.9"
|
python-version: "3.9"
|
||||||
|
|
||||||
|
@ -141,7 +141,7 @@ jobs:
|
||||||
echo name=$(cat /tmp/platform) >> $GITHUB_OUTPUT
|
echo name=$(cat /tmp/platform) >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Upload digests
|
- name: Upload digests
|
||||||
uses: actions/upload-artifact@v4.3.4
|
uses: actions/upload-artifact@v4.4.0
|
||||||
with:
|
with:
|
||||||
name: digests-${{ steps.sanitize.outputs.name }}
|
name: digests-${{ steps.sanitize.outputs.name }}
|
||||||
path: /tmp/digests
|
path: /tmp/digests
|
||||||
|
|
2
.github/workflows/sync-device-classes.yml
vendored
2
.github/workflows/sync-device-classes.yml
vendored
|
@ -22,7 +22,7 @@ jobs:
|
||||||
path: lib/home-assistant
|
path: lib/home-assistant
|
||||||
|
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@v5.1.0
|
uses: actions/setup-python@v5.2.0
|
||||||
with:
|
with:
|
||||||
python-version: 3.12
|
python-version: 3.12
|
||||||
|
|
||||||
|
|
|
@ -83,6 +83,7 @@ esphome/components/cap1188/* @mreditor97
|
||||||
esphome/components/captive_portal/* @OttoWinter
|
esphome/components/captive_portal/* @OttoWinter
|
||||||
esphome/components/ccs811/* @habbie
|
esphome/components/ccs811/* @habbie
|
||||||
esphome/components/cd74hc4067/* @asoehlke
|
esphome/components/cd74hc4067/* @asoehlke
|
||||||
|
esphome/components/ch422g/* @jesterret
|
||||||
esphome/components/climate/* @esphome/core
|
esphome/components/climate/* @esphome/core
|
||||||
esphome/components/climate_ir/* @glmnet
|
esphome/components/climate_ir/* @glmnet
|
||||||
esphome/components/color_temperature/* @jesserockz
|
esphome/components/color_temperature/* @jesserockz
|
||||||
|
@ -423,6 +424,7 @@ esphome/components/tuya/switch/* @jesserockz
|
||||||
esphome/components/tuya/text_sensor/* @dentra
|
esphome/components/tuya/text_sensor/* @dentra
|
||||||
esphome/components/uart/* @esphome/core
|
esphome/components/uart/* @esphome/core
|
||||||
esphome/components/uart/button/* @ssieb
|
esphome/components/uart/button/* @ssieb
|
||||||
|
esphome/components/udp/* @clydebarrow
|
||||||
esphome/components/ufire_ec/* @pvizeli
|
esphome/components/ufire_ec/* @pvizeli
|
||||||
esphome/components/ufire_ise/* @pvizeli
|
esphome/components/ufire_ise/* @pvizeli
|
||||||
esphome/components/ultrasonic/* @OttoWinter
|
esphome/components/ultrasonic/* @OttoWinter
|
||||||
|
|
|
@ -34,8 +34,8 @@ RUN \
|
||||||
python3-wheel=0.38.4-2 \
|
python3-wheel=0.38.4-2 \
|
||||||
iputils-ping=3:20221126-1 \
|
iputils-ping=3:20221126-1 \
|
||||||
git=1:2.39.2-1.1 \
|
git=1:2.39.2-1.1 \
|
||||||
curl=7.88.1-10+deb12u6 \
|
curl=7.88.1-10+deb12u7 \
|
||||||
openssh-client=1:9.2p1-2+deb12u2 \
|
openssh-client=1:9.2p1-2+deb12u3 \
|
||||||
python3-cffi=1.15.1-5 \
|
python3-cffi=1.15.1-5 \
|
||||||
libcairo2=1.16.0-7 \
|
libcairo2=1.16.0-7 \
|
||||||
libmagic1=1:5.44-3 \
|
libmagic1=1:5.44-3 \
|
||||||
|
@ -49,7 +49,7 @@ RUN \
|
||||||
zlib1g-dev=1:1.2.13.dfsg-1 \
|
zlib1g-dev=1:1.2.13.dfsg-1 \
|
||||||
libjpeg-dev=1:2.1.5-2 \
|
libjpeg-dev=1:2.1.5-2 \
|
||||||
libfreetype-dev=2.12.1+dfsg-5+deb12u3 \
|
libfreetype-dev=2.12.1+dfsg-5+deb12u3 \
|
||||||
libssl-dev=3.0.13-1~deb12u1 \
|
libssl-dev=3.0.14-1~deb12u1 \
|
||||||
libffi-dev=3.4.4-1 \
|
libffi-dev=3.4.4-1 \
|
||||||
libopenjp2-7=2.5.0-2 \
|
libopenjp2-7=2.5.0-2 \
|
||||||
libtiff6=4.5.0-6+deb12u1 \
|
libtiff6=4.5.0-6+deb12u1 \
|
||||||
|
|
|
@ -54,6 +54,9 @@ bool BluetoothProxy::parse_devices(esp_ble_gap_cb_param_t::ble_scan_result_evt_p
|
||||||
}
|
}
|
||||||
|
|
||||||
resp.advertisements.push_back(std::move(adv));
|
resp.advertisements.push_back(std::move(adv));
|
||||||
|
|
||||||
|
ESP_LOGV(TAG, "Proxying raw packet from %02X:%02X:%02X:%02X:%02X:%02X, length %d. RSSI: %d dB", result.bda[0],
|
||||||
|
result.bda[1], result.bda[2], result.bda[3], result.bda[4], result.bda[5], length, result.rssi);
|
||||||
}
|
}
|
||||||
ESP_LOGV(TAG, "Proxying %d packets", count);
|
ESP_LOGV(TAG, "Proxying %d packets", count);
|
||||||
this->api_connection_->send_bluetooth_le_raw_advertisements_response(resp);
|
this->api_connection_->send_bluetooth_le_raw_advertisements_response(resp);
|
||||||
|
@ -87,6 +90,8 @@ void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &devi
|
||||||
void BluetoothProxy::dump_config() {
|
void BluetoothProxy::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, "Bluetooth Proxy:");
|
ESP_LOGCONFIG(TAG, "Bluetooth Proxy:");
|
||||||
ESP_LOGCONFIG(TAG, " Active: %s", YESNO(this->active_));
|
ESP_LOGCONFIG(TAG, " Active: %s", YESNO(this->active_));
|
||||||
|
ESP_LOGCONFIG(TAG, " Connections: %d", this->connections_.size());
|
||||||
|
ESP_LOGCONFIG(TAG, " Raw advertisements: %s", YESNO(this->raw_advertisements_));
|
||||||
}
|
}
|
||||||
|
|
||||||
int BluetoothProxy::get_bluetooth_connections_free() {
|
int BluetoothProxy::get_bluetooth_connections_free() {
|
||||||
|
|
67
esphome/components/ch422g/__init__.py
Normal file
67
esphome/components/ch422g/__init__.py
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
from esphome import pins
|
||||||
|
import esphome.codegen as cg
|
||||||
|
from esphome.components import i2c
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.const import (
|
||||||
|
CONF_ID,
|
||||||
|
CONF_INPUT,
|
||||||
|
CONF_INVERTED,
|
||||||
|
CONF_MODE,
|
||||||
|
CONF_NUMBER,
|
||||||
|
CONF_OUTPUT,
|
||||||
|
CONF_RESTORE_VALUE,
|
||||||
|
)
|
||||||
|
|
||||||
|
CODEOWNERS = ["@jesterret"]
|
||||||
|
DEPENDENCIES = ["i2c"]
|
||||||
|
MULTI_CONF = True
|
||||||
|
ch422g_ns = cg.esphome_ns.namespace("ch422g")
|
||||||
|
|
||||||
|
CH422GComponent = ch422g_ns.class_("CH422GComponent", cg.Component, i2c.I2CDevice)
|
||||||
|
CH422GGPIOPin = ch422g_ns.class_(
|
||||||
|
"CH422GGPIOPin", cg.GPIOPin, cg.Parented.template(CH422GComponent)
|
||||||
|
)
|
||||||
|
|
||||||
|
CONF_CH422G = "ch422g"
|
||||||
|
CONFIG_SCHEMA = (
|
||||||
|
cv.Schema(
|
||||||
|
{
|
||||||
|
cv.Required(CONF_ID): cv.declare_id(CH422GComponent),
|
||||||
|
cv.Optional(CONF_RESTORE_VALUE, default=False): cv.boolean,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(cv.COMPONENT_SCHEMA)
|
||||||
|
.extend(i2c.i2c_device_schema(0x24))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
cg.add(var.set_restore_value(config[CONF_RESTORE_VALUE]))
|
||||||
|
await cg.register_component(var, config)
|
||||||
|
await i2c.register_i2c_device(var, config)
|
||||||
|
|
||||||
|
|
||||||
|
CH422G_PIN_SCHEMA = pins.gpio_base_schema(
|
||||||
|
CH422GGPIOPin,
|
||||||
|
cv.int_range(min=0, max=7),
|
||||||
|
modes=[CONF_INPUT, CONF_OUTPUT],
|
||||||
|
).extend(
|
||||||
|
{
|
||||||
|
cv.Required(CONF_CH422G): cv.use_id(CH422GComponent),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pins.PIN_SCHEMA_REGISTRY.register(CONF_CH422G, CH422G_PIN_SCHEMA)
|
||||||
|
async def ch422g_pin_to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
parent = await cg.get_variable(config[CONF_CH422G])
|
||||||
|
|
||||||
|
cg.add(var.set_parent(parent))
|
||||||
|
|
||||||
|
num = config[CONF_NUMBER]
|
||||||
|
cg.add(var.set_pin(num))
|
||||||
|
cg.add(var.set_inverted(config[CONF_INVERTED]))
|
||||||
|
cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE])))
|
||||||
|
return var
|
122
esphome/components/ch422g/ch422g.cpp
Normal file
122
esphome/components/ch422g/ch422g.cpp
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
#include "ch422g.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace ch422g {
|
||||||
|
|
||||||
|
const uint8_t CH422G_REG_IN = 0x26;
|
||||||
|
const uint8_t CH422G_REG_OUT = 0x38;
|
||||||
|
const uint8_t OUT_REG_DEFAULT_VAL = 0xdf;
|
||||||
|
|
||||||
|
static const char *const TAG = "ch422g";
|
||||||
|
|
||||||
|
void CH422GComponent::setup() {
|
||||||
|
ESP_LOGCONFIG(TAG, "Setting up CH422G...");
|
||||||
|
// Test to see if device exists
|
||||||
|
if (!this->read_inputs_()) {
|
||||||
|
ESP_LOGE(TAG, "CH422G not detected at 0x%02X", this->address_);
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// restore defaults over whatever got saved on last boot
|
||||||
|
if (!this->restore_value_) {
|
||||||
|
this->write_output_(OUT_REG_DEFAULT_VAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "Initialization complete. Warning: %d, Error: %d", this->status_has_warning(),
|
||||||
|
this->status_has_error());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CH422GComponent::loop() {
|
||||||
|
// Clear all the previously read flags.
|
||||||
|
this->pin_read_cache_ = 0x00;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CH422GComponent::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG, "CH422G:");
|
||||||
|
LOG_I2C_DEVICE(this)
|
||||||
|
if (this->is_failed()) {
|
||||||
|
ESP_LOGE(TAG, "Communication with CH422G failed!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ch422g doesn't have any flag support (needs docs?)
|
||||||
|
void CH422GComponent::pin_mode(uint8_t pin, gpio::Flags flags) {}
|
||||||
|
|
||||||
|
bool CH422GComponent::digital_read(uint8_t pin) {
|
||||||
|
if (this->pin_read_cache_ == 0 || this->pin_read_cache_ & (1 << pin)) {
|
||||||
|
// Read values on first access or in case it's being read again in the same loop
|
||||||
|
this->read_inputs_();
|
||||||
|
}
|
||||||
|
|
||||||
|
this->pin_read_cache_ |= (1 << pin);
|
||||||
|
return this->state_mask_ & (1 << pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CH422GComponent::digital_write(uint8_t pin, bool value) {
|
||||||
|
if (value) {
|
||||||
|
this->write_output_(this->state_mask_ | (1 << pin));
|
||||||
|
} else {
|
||||||
|
this->write_output_(this->state_mask_ & ~(1 << pin));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CH422GComponent::read_inputs_() {
|
||||||
|
if (this->is_failed()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t temp = 0;
|
||||||
|
if ((this->last_error_ = this->read(&temp, 1)) != esphome::i2c::ERROR_OK) {
|
||||||
|
this->status_set_warning(str_sprintf("read_inputs_(): I2C I/O error: %d", (int) this->last_error_).c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t output = 0;
|
||||||
|
if ((this->last_error_ = this->bus_->read(CH422G_REG_IN, &output, 1)) != esphome::i2c::ERROR_OK) {
|
||||||
|
this->status_set_warning(str_sprintf("read_inputs_(): I2C I/O error: %d", (int) this->last_error_).c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->state_mask_ = output;
|
||||||
|
this->status_clear_warning();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CH422GComponent::write_output_(uint8_t value) {
|
||||||
|
const uint8_t temp = 1;
|
||||||
|
if ((this->last_error_ = this->write(&temp, 1, false)) != esphome::i2c::ERROR_OK) {
|
||||||
|
this->status_set_warning(str_sprintf("write_output_(): I2C I/O error: %d", (int) this->last_error_).c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t write_mask = value;
|
||||||
|
if ((this->last_error_ = this->bus_->write(CH422G_REG_OUT, &write_mask, 1)) != esphome::i2c::ERROR_OK) {
|
||||||
|
this->status_set_warning(
|
||||||
|
str_sprintf("write_output_(): I2C I/O error: %d for write_mask: %d", (int) this->last_error_, (int) write_mask)
|
||||||
|
.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->state_mask_ = value;
|
||||||
|
this->status_clear_warning();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
float CH422GComponent::get_setup_priority() const { return setup_priority::IO; }
|
||||||
|
|
||||||
|
// Run our loop() method very early in the loop, so that we cache read values
|
||||||
|
// before other components call our digital_read() method.
|
||||||
|
float CH422GComponent::get_loop_priority() const { return 9.0f; } // Just after WIFI
|
||||||
|
|
||||||
|
void CH422GGPIOPin::setup() { pin_mode(flags_); }
|
||||||
|
void CH422GGPIOPin::pin_mode(gpio::Flags flags) { this->parent_->pin_mode(this->pin_, flags); }
|
||||||
|
bool CH422GGPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; }
|
||||||
|
|
||||||
|
void CH422GGPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); }
|
||||||
|
std::string CH422GGPIOPin::dump_summary() const { return str_sprintf("EXIO%u via CH422G", pin_); }
|
||||||
|
|
||||||
|
} // namespace ch422g
|
||||||
|
} // namespace esphome
|
70
esphome/components/ch422g/ch422g.h
Normal file
70
esphome/components/ch422g/ch422g.h
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
|
#include "esphome/components/i2c/i2c.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace ch422g {
|
||||||
|
|
||||||
|
class CH422GComponent : public Component, public i2c::I2CDevice {
|
||||||
|
public:
|
||||||
|
CH422GComponent() = default;
|
||||||
|
|
||||||
|
/// Check i2c availability and setup masks
|
||||||
|
void setup() override;
|
||||||
|
/// Poll for input changes periodically
|
||||||
|
void loop() override;
|
||||||
|
/// Helper function to read the value of a pin.
|
||||||
|
bool digital_read(uint8_t pin);
|
||||||
|
/// Helper function to write the value of a pin.
|
||||||
|
void digital_write(uint8_t pin, bool value);
|
||||||
|
/// Helper function to set the pin mode of a pin.
|
||||||
|
void pin_mode(uint8_t pin, gpio::Flags flags);
|
||||||
|
|
||||||
|
float get_setup_priority() const override;
|
||||||
|
|
||||||
|
float get_loop_priority() const override;
|
||||||
|
|
||||||
|
void dump_config() override;
|
||||||
|
|
||||||
|
void set_restore_value(bool restore_value) { this->restore_value_ = restore_value; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool read_inputs_();
|
||||||
|
|
||||||
|
bool write_output_(uint8_t value);
|
||||||
|
|
||||||
|
/// The mask to write as output state - 1 means HIGH, 0 means LOW
|
||||||
|
uint8_t state_mask_{0x00};
|
||||||
|
/// Flags to check if read previously during this loop
|
||||||
|
uint8_t pin_read_cache_ = {0x00};
|
||||||
|
/// Storage for last I2C error seen
|
||||||
|
esphome::i2c::ErrorCode last_error_;
|
||||||
|
/// Whether we want to override stored values on expander
|
||||||
|
bool restore_value_{false};
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Helper class to expose a CH422G pin as an internal input GPIO pin.
|
||||||
|
class CH422GGPIOPin : public GPIOPin {
|
||||||
|
public:
|
||||||
|
void setup() override;
|
||||||
|
void pin_mode(gpio::Flags flags) override;
|
||||||
|
bool digital_read() override;
|
||||||
|
void digital_write(bool value) override;
|
||||||
|
std::string dump_summary() const override;
|
||||||
|
|
||||||
|
void set_parent(CH422GComponent *parent) { parent_ = parent; }
|
||||||
|
void set_pin(uint8_t pin) { pin_ = pin; }
|
||||||
|
void set_inverted(bool inverted) { inverted_ = inverted; }
|
||||||
|
void set_flags(gpio::Flags flags) { flags_ = flags; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
CH422GComponent *parent_;
|
||||||
|
uint8_t pin_;
|
||||||
|
bool inverted_;
|
||||||
|
gpio::Flags flags_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ch422g
|
||||||
|
} // namespace esphome
|
|
@ -1,15 +1,15 @@
|
||||||
|
from esphome import automation, core
|
||||||
|
from esphome.automation import maybe_simple_id
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome import core, automation
|
|
||||||
from esphome.automation import maybe_simple_id
|
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_AUTO_CLEAR_ENABLED,
|
CONF_AUTO_CLEAR_ENABLED,
|
||||||
|
CONF_FROM,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
CONF_LAMBDA,
|
CONF_LAMBDA,
|
||||||
CONF_PAGES,
|
|
||||||
CONF_PAGE_ID,
|
CONF_PAGE_ID,
|
||||||
|
CONF_PAGES,
|
||||||
CONF_ROTATION,
|
CONF_ROTATION,
|
||||||
CONF_FROM,
|
|
||||||
CONF_TO,
|
CONF_TO,
|
||||||
CONF_TRIGGER_ID,
|
CONF_TRIGGER_ID,
|
||||||
)
|
)
|
||||||
|
@ -195,3 +195,4 @@ async def display_is_displaying_page_to_code(config, condition_id, template_arg,
|
||||||
@coroutine_with_priority(100.0)
|
@coroutine_with_priority(100.0)
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
cg.add_global(display_ns.using)
|
cg.add_global(display_ns.using)
|
||||||
|
cg.add_define("USE_DISPLAY")
|
||||||
|
|
|
@ -1,18 +1,23 @@
|
||||||
import esphome.codegen as cg
|
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome import pins
|
from esphome import pins
|
||||||
|
import esphome.codegen as cg
|
||||||
from esphome.components import canbus
|
from esphome.components import canbus
|
||||||
from esphome.const import CONF_ID, CONF_RX_PIN, CONF_TX_PIN
|
from esphome.components.canbus import CONF_BIT_RATE, CanbusComponent, CanSpeed
|
||||||
from esphome.components.canbus import CanbusComponent, CanSpeed, CONF_BIT_RATE
|
|
||||||
|
|
||||||
from esphome.components.esp32 import get_esp32_variant
|
from esphome.components.esp32 import get_esp32_variant
|
||||||
from esphome.components.esp32.const import (
|
from esphome.components.esp32.const import (
|
||||||
VARIANT_ESP32,
|
VARIANT_ESP32,
|
||||||
VARIANT_ESP32S2,
|
|
||||||
VARIANT_ESP32S3,
|
|
||||||
VARIANT_ESP32C3,
|
VARIANT_ESP32C3,
|
||||||
VARIANT_ESP32C6,
|
VARIANT_ESP32C6,
|
||||||
VARIANT_ESP32H2,
|
VARIANT_ESP32H2,
|
||||||
|
VARIANT_ESP32S2,
|
||||||
|
VARIANT_ESP32S3,
|
||||||
|
)
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.const import (
|
||||||
|
CONF_ID,
|
||||||
|
CONF_RX_PIN,
|
||||||
|
CONF_RX_QUEUE_LEN,
|
||||||
|
CONF_TX_PIN,
|
||||||
|
CONF_TX_QUEUE_LEN,
|
||||||
)
|
)
|
||||||
|
|
||||||
CODEOWNERS = ["@Sympatron"]
|
CODEOWNERS = ["@Sympatron"]
|
||||||
|
@ -77,6 +82,8 @@ CONFIG_SCHEMA = canbus.CANBUS_SCHEMA.extend(
|
||||||
cv.Optional(CONF_BIT_RATE, default="125KBPS"): validate_bit_rate,
|
cv.Optional(CONF_BIT_RATE, default="125KBPS"): validate_bit_rate,
|
||||||
cv.Required(CONF_RX_PIN): pins.internal_gpio_input_pin_number,
|
cv.Required(CONF_RX_PIN): pins.internal_gpio_input_pin_number,
|
||||||
cv.Required(CONF_TX_PIN): pins.internal_gpio_output_pin_number,
|
cv.Required(CONF_TX_PIN): pins.internal_gpio_output_pin_number,
|
||||||
|
cv.Optional(CONF_RX_QUEUE_LEN): cv.uint32_t,
|
||||||
|
cv.Optional(CONF_TX_QUEUE_LEN): cv.uint32_t,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -87,3 +94,7 @@ async def to_code(config):
|
||||||
|
|
||||||
cg.add(var.set_rx(config[CONF_RX_PIN]))
|
cg.add(var.set_rx(config[CONF_RX_PIN]))
|
||||||
cg.add(var.set_tx(config[CONF_TX_PIN]))
|
cg.add(var.set_tx(config[CONF_TX_PIN]))
|
||||||
|
if (rx_queue_len := config.get(CONF_RX_QUEUE_LEN)) is not None:
|
||||||
|
cg.add(var.set_rx_queue_len(rx_queue_len))
|
||||||
|
if (tx_queue_len := config.get(CONF_TX_QUEUE_LEN)) is not None:
|
||||||
|
cg.add(var.set_tx_queue_len(tx_queue_len))
|
||||||
|
|
|
@ -69,6 +69,13 @@ static bool get_bitrate(canbus::CanSpeed bitrate, twai_timing_config_t *t_config
|
||||||
bool ESP32Can::setup_internal() {
|
bool ESP32Can::setup_internal() {
|
||||||
twai_general_config_t g_config =
|
twai_general_config_t g_config =
|
||||||
TWAI_GENERAL_CONFIG_DEFAULT((gpio_num_t) this->tx_, (gpio_num_t) this->rx_, TWAI_MODE_NORMAL);
|
TWAI_GENERAL_CONFIG_DEFAULT((gpio_num_t) this->tx_, (gpio_num_t) this->rx_, TWAI_MODE_NORMAL);
|
||||||
|
if (this->tx_queue_len_.has_value()) {
|
||||||
|
g_config.tx_queue_len = this->tx_queue_len_.value();
|
||||||
|
}
|
||||||
|
if (this->rx_queue_len_.has_value()) {
|
||||||
|
g_config.rx_queue_len = this->rx_queue_len_.value();
|
||||||
|
}
|
||||||
|
|
||||||
twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL();
|
twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL();
|
||||||
twai_timing_config_t t_config;
|
twai_timing_config_t t_config;
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,8 @@ class ESP32Can : public canbus::Canbus {
|
||||||
public:
|
public:
|
||||||
void set_rx(int rx) { rx_ = rx; }
|
void set_rx(int rx) { rx_ = rx; }
|
||||||
void set_tx(int tx) { tx_ = tx; }
|
void set_tx(int tx) { tx_ = tx; }
|
||||||
|
void set_tx_queue_len(uint32_t tx_queue_len) { this->tx_queue_len_ = tx_queue_len; }
|
||||||
|
void set_rx_queue_len(uint32_t rx_queue_len) { this->rx_queue_len_ = rx_queue_len; }
|
||||||
ESP32Can(){};
|
ESP32Can(){};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -21,6 +23,8 @@ class ESP32Can : public canbus::Canbus {
|
||||||
|
|
||||||
int rx_{-1};
|
int rx_{-1};
|
||||||
int tx_{-1};
|
int tx_{-1};
|
||||||
|
optional<uint32_t> tx_queue_len_{};
|
||||||
|
optional<uint32_t> rx_queue_len_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace esp32_can
|
} // namespace esp32_can
|
||||||
|
|
|
@ -472,13 +472,13 @@ void EthernetComponent::start_connect_() {
|
||||||
if (err != ESP_ERR_ESP_NETIF_DHCP_ALREADY_STARTED) {
|
if (err != ESP_ERR_ESP_NETIF_DHCP_ALREADY_STARTED) {
|
||||||
ESPHL_ERROR_CHECK(err, "DHCPC start error");
|
ESPHL_ERROR_CHECK(err, "DHCPC start error");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
#if USE_NETWORK_IPV6
|
#if USE_NETWORK_IPV6
|
||||||
err = esp_netif_create_ip6_linklocal(this->eth_netif_);
|
err = esp_netif_create_ip6_linklocal(this->eth_netif_);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESPHL_ERROR_CHECK(err, "Enable IPv6 link local failed");
|
ESPHL_ERROR_CHECK(err, "Enable IPv6 link local failed");
|
||||||
}
|
}
|
||||||
#endif /* USE_NETWORK_IPV6 */
|
#endif /* USE_NETWORK_IPV6 */
|
||||||
}
|
|
||||||
|
|
||||||
this->connect_begin_ = millis();
|
this->connect_begin_ = millis();
|
||||||
this->status_set_warning();
|
this->status_set_warning();
|
||||||
|
|
|
@ -1,43 +1,35 @@
|
||||||
|
import functools
|
||||||
import hashlib
|
import hashlib
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import functools
|
|
||||||
from pathlib import Path
|
|
||||||
import os
|
import os
|
||||||
|
from pathlib import Path
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from packaging import version
|
from packaging import version
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from esphome import core
|
from esphome import core, external_files
|
||||||
from esphome import external_files
|
|
||||||
import esphome.config_validation as cv
|
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.helpers import (
|
import esphome.config_validation as cv
|
||||||
copy_file_if_changed,
|
|
||||||
cpp_string_escape,
|
|
||||||
)
|
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_FAMILY,
|
CONF_FAMILY,
|
||||||
CONF_FILE,
|
CONF_FILE,
|
||||||
CONF_GLYPHS,
|
CONF_GLYPHS,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
|
CONF_PATH,
|
||||||
CONF_RAW_DATA_ID,
|
CONF_RAW_DATA_ID,
|
||||||
CONF_TYPE,
|
|
||||||
CONF_REFRESH,
|
CONF_REFRESH,
|
||||||
CONF_SIZE,
|
CONF_SIZE,
|
||||||
CONF_PATH,
|
CONF_TYPE,
|
||||||
CONF_WEIGHT,
|
|
||||||
CONF_URL,
|
CONF_URL,
|
||||||
|
CONF_WEIGHT,
|
||||||
)
|
)
|
||||||
from esphome.core import (
|
from esphome.core import CORE, HexInt
|
||||||
CORE,
|
from esphome.helpers import copy_file_if_changed, cpp_string_escape
|
||||||
HexInt,
|
|
||||||
)
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
DOMAIN = "font"
|
DOMAIN = "font"
|
||||||
DEPENDENCIES = ["display"]
|
|
||||||
MULTI_CONF = True
|
MULTI_CONF = True
|
||||||
|
|
||||||
CODEOWNERS = ["@esphome/core", "@clydebarrow"]
|
CODEOWNERS = ["@esphome/core", "@clydebarrow"]
|
||||||
|
@ -400,10 +392,7 @@ class EFont:
|
||||||
|
|
||||||
|
|
||||||
def convert_bitmap_to_pillow_font(filepath):
|
def convert_bitmap_to_pillow_font(filepath):
|
||||||
from PIL import (
|
from PIL import BdfFontFile, PcfFontFile
|
||||||
PcfFontFile,
|
|
||||||
BdfFontFile,
|
|
||||||
)
|
|
||||||
|
|
||||||
local_bitmap_font_file = external_files.compute_local_file_dir(
|
local_bitmap_font_file = external_files.compute_local_file_dir(
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
#include "font.h"
|
#include "font.h"
|
||||||
|
|
||||||
|
#include "esphome/core/color.h"
|
||||||
#include "esphome/core/hal.h"
|
#include "esphome/core/hal.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
#include "esphome/core/color.h"
|
|
||||||
#include "esphome/components/display/display_buffer.h"
|
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace font {
|
namespace font {
|
||||||
|
@ -68,6 +67,7 @@ int Font::match_next_glyph(const uint8_t *str, int *match_length) {
|
||||||
return -1;
|
return -1;
|
||||||
return lo;
|
return lo;
|
||||||
}
|
}
|
||||||
|
#ifdef USE_DISPLAY
|
||||||
void Font::measure(const char *str, int *width, int *x_offset, int *baseline, int *height) {
|
void Font::measure(const char *str, int *width, int *x_offset, int *baseline, int *height) {
|
||||||
*baseline = this->baseline_;
|
*baseline = this->baseline_;
|
||||||
*height = this->height_;
|
*height = this->height_;
|
||||||
|
@ -164,6 +164,7 @@ void Font::print(int x_start, int y_start, display::Display *display, Color colo
|
||||||
i += match_length;
|
i += match_length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace font
|
} // namespace font
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "esphome/core/datatypes.h"
|
|
||||||
#include "esphome/core/color.h"
|
#include "esphome/core/color.h"
|
||||||
#include "esphome/components/display/display_buffer.h"
|
#include "esphome/core/datatypes.h"
|
||||||
|
#include "esphome/core/defines.h"
|
||||||
|
#ifdef USE_DISPLAY
|
||||||
|
#include "esphome/components/display/display.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace font {
|
namespace font {
|
||||||
|
@ -38,7 +41,11 @@ class Glyph {
|
||||||
const GlyphData *glyph_data_;
|
const GlyphData *glyph_data_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Font : public display::BaseFont {
|
class Font
|
||||||
|
#ifdef USE_DISPLAY
|
||||||
|
: public display::BaseFont
|
||||||
|
#endif
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
/** Construct the font with the given glyphs.
|
/** Construct the font with the given glyphs.
|
||||||
*
|
*
|
||||||
|
@ -50,9 +57,11 @@ class Font : public display::BaseFont {
|
||||||
|
|
||||||
int match_next_glyph(const uint8_t *str, int *match_length);
|
int match_next_glyph(const uint8_t *str, int *match_length);
|
||||||
|
|
||||||
|
#ifdef USE_DISPLAY
|
||||||
void print(int x_start, int y_start, display::Display *display, Color color, const char *text,
|
void print(int x_start, int y_start, display::Display *display, Color color, const char *text,
|
||||||
Color background) override;
|
Color background) override;
|
||||||
void measure(const char *str, int *width, int *x_offset, int *baseline, int *height) override;
|
void measure(const char *str, int *width, int *x_offset, int *baseline, int *height) override;
|
||||||
|
#endif
|
||||||
inline int get_baseline() { return this->baseline_; }
|
inline int get_baseline() { return this->baseline_; }
|
||||||
inline int get_height() { return this->height_; }
|
inline int get_height() { return this->height_; }
|
||||||
inline int get_bpp() { return this->bpp_; }
|
inline int get_bpp() { return this->bpp_; }
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
import esphome.codegen as cg
|
|
||||||
import esphome.config_validation as cv
|
|
||||||
|
|
||||||
from esphome import pins
|
from esphome import pins
|
||||||
|
import esphome.codegen as cg
|
||||||
from esphome.components import i2c, touchscreen
|
from esphome.components import i2c, touchscreen
|
||||||
from esphome.const import CONF_INTERRUPT_PIN, CONF_ID
|
import esphome.config_validation as cv
|
||||||
from .. import gt911_ns
|
from esphome.const import CONF_ID, CONF_INTERRUPT_PIN, CONF_RESET_PIN
|
||||||
|
|
||||||
|
from .. import gt911_ns
|
||||||
|
|
||||||
GT911ButtonListener = gt911_ns.class_("GT911ButtonListener")
|
GT911ButtonListener = gt911_ns.class_("GT911ButtonListener")
|
||||||
GT911Touchscreen = gt911_ns.class_(
|
GT911Touchscreen = gt911_ns.class_(
|
||||||
|
@ -18,6 +17,7 @@ CONFIG_SCHEMA = touchscreen.TOUCHSCREEN_SCHEMA.extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(GT911Touchscreen),
|
cv.GenerateID(): cv.declare_id(GT911Touchscreen),
|
||||||
cv.Optional(CONF_INTERRUPT_PIN): pins.internal_gpio_input_pin_schema,
|
cv.Optional(CONF_INTERRUPT_PIN): pins.internal_gpio_input_pin_schema,
|
||||||
|
cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema,
|
||||||
}
|
}
|
||||||
).extend(i2c.i2c_device_schema(0x5D))
|
).extend(i2c.i2c_device_schema(0x5D))
|
||||||
|
|
||||||
|
@ -29,3 +29,5 @@ async def to_code(config):
|
||||||
|
|
||||||
if interrupt_pin := config.get(CONF_INTERRUPT_PIN):
|
if interrupt_pin := config.get(CONF_INTERRUPT_PIN):
|
||||||
cg.add(var.set_interrupt_pin(await cg.gpio_pin_expression(interrupt_pin)))
|
cg.add(var.set_interrupt_pin(await cg.gpio_pin_expression(interrupt_pin)))
|
||||||
|
if reset_pin := config.get(CONF_RESET_PIN):
|
||||||
|
cg.add(var.set_reset_pin(await cg.gpio_pin_expression(reset_pin)))
|
||||||
|
|
|
@ -26,6 +26,23 @@ static const size_t MAX_BUTTONS = 4; // max number of buttons scanned
|
||||||
void GT911Touchscreen::setup() {
|
void GT911Touchscreen::setup() {
|
||||||
i2c::ErrorCode err;
|
i2c::ErrorCode err;
|
||||||
ESP_LOGCONFIG(TAG, "Setting up GT911 Touchscreen...");
|
ESP_LOGCONFIG(TAG, "Setting up GT911 Touchscreen...");
|
||||||
|
if (this->reset_pin_ != nullptr) {
|
||||||
|
this->reset_pin_->setup();
|
||||||
|
this->reset_pin_->digital_write(false);
|
||||||
|
if (this->interrupt_pin_ != nullptr) {
|
||||||
|
// The interrupt pin is used as an input during reset to select the I2C address.
|
||||||
|
this->interrupt_pin_->pin_mode(gpio::FLAG_OUTPUT);
|
||||||
|
this->interrupt_pin_->setup();
|
||||||
|
this->interrupt_pin_->digital_write(false);
|
||||||
|
}
|
||||||
|
delay(2);
|
||||||
|
this->reset_pin_->digital_write(true);
|
||||||
|
delay(50); // NOLINT
|
||||||
|
if (this->interrupt_pin_ != nullptr) {
|
||||||
|
this->interrupt_pin_->pin_mode(gpio::FLAG_INPUT);
|
||||||
|
this->interrupt_pin_->setup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// check the configuration of the int line.
|
// check the configuration of the int line.
|
||||||
uint8_t data[4];
|
uint8_t data[4];
|
||||||
|
|
|
@ -19,12 +19,14 @@ class GT911Touchscreen : public touchscreen::Touchscreen, public i2c::I2CDevice
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
|
|
||||||
void set_interrupt_pin(InternalGPIOPin *pin) { this->interrupt_pin_ = pin; }
|
void set_interrupt_pin(InternalGPIOPin *pin) { this->interrupt_pin_ = pin; }
|
||||||
|
void set_reset_pin(GPIOPin *pin) { this->reset_pin_ = pin; }
|
||||||
void register_button_listener(GT911ButtonListener *listener) { this->button_listeners_.push_back(listener); }
|
void register_button_listener(GT911ButtonListener *listener) { this->button_listeners_.push_back(listener); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void update_touches() override;
|
void update_touches() override;
|
||||||
|
|
||||||
InternalGPIOPin *interrupt_pin_{};
|
InternalGPIOPin *interrupt_pin_{};
|
||||||
|
GPIOPin *reset_pin_{};
|
||||||
std::vector<GT911ButtonListener *> button_listeners_;
|
std::vector<GT911ButtonListener *> button_listeners_;
|
||||||
uint8_t button_state_{0xFF}; // last button state. Initial FF guarantees first update.
|
uint8_t button_state_{0xFF}; // last button state. Initial FF guarantees first update.
|
||||||
};
|
};
|
||||||
|
|
|
@ -70,72 +70,62 @@ void MICS4514Component::update() {
|
||||||
|
|
||||||
if (this->carbon_monoxide_sensor_ != nullptr) {
|
if (this->carbon_monoxide_sensor_ != nullptr) {
|
||||||
float co = 0.0f;
|
float co = 0.0f;
|
||||||
if (red_f <= 0.425f) {
|
if (red_f > 3.4f) {
|
||||||
co = (0.425f - red_f) / 0.000405f;
|
co = 0.0;
|
||||||
if (co < 1.0f)
|
} else if (red_f < 0.01) {
|
||||||
co = 0.0f;
|
co = 1000.0;
|
||||||
if (co > 1000.0f)
|
} else {
|
||||||
co = 1000.0f;
|
co = 4.2 / pow(red_f, 1.2);
|
||||||
}
|
}
|
||||||
this->carbon_monoxide_sensor_->publish_state(co);
|
this->carbon_monoxide_sensor_->publish_state(co);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->nitrogen_dioxide_sensor_ != nullptr) {
|
if (this->nitrogen_dioxide_sensor_ != nullptr) {
|
||||||
float nitrogendioxide = 0.0f;
|
float nitrogendioxide = 0.0f;
|
||||||
if (ox_f >= 1.1f) {
|
if (ox_f < 0.3f) {
|
||||||
nitrogendioxide = (ox_f - 0.045f) / 6.13f;
|
nitrogendioxide = 0.0;
|
||||||
if (nitrogendioxide < 0.1f)
|
} else {
|
||||||
nitrogendioxide = 0.0f;
|
nitrogendioxide = 0.164 * pow(ox_f, 0.975);
|
||||||
if (nitrogendioxide > 10.0f)
|
|
||||||
nitrogendioxide = 10.0f;
|
|
||||||
}
|
}
|
||||||
this->nitrogen_dioxide_sensor_->publish_state(nitrogendioxide);
|
this->nitrogen_dioxide_sensor_->publish_state(nitrogendioxide);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->methane_sensor_ != nullptr) {
|
if (this->methane_sensor_ != nullptr) {
|
||||||
float methane = 0.0f;
|
float methane = 0.0f;
|
||||||
if (red_f <= 0.786f) {
|
if (red_f > 0.9f || red_f < 0.5) { // outside the range->unlikely
|
||||||
methane = (0.786f - red_f) / 0.000023f;
|
methane = 0.0;
|
||||||
if (methane < 1000.0f)
|
} else {
|
||||||
methane = 0.0f;
|
methane = 630 / pow(red_f, 4.4);
|
||||||
if (methane > 25000.0f)
|
|
||||||
methane = 25000.0f;
|
|
||||||
}
|
}
|
||||||
this->methane_sensor_->publish_state(methane);
|
this->methane_sensor_->publish_state(methane);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->ethanol_sensor_ != nullptr) {
|
if (this->ethanol_sensor_ != nullptr) {
|
||||||
float ethanol = 0.0f;
|
float ethanol = 0.0f;
|
||||||
if (red_f <= 0.306f) {
|
if (red_f > 1.0f || red_f < 0.02) { // outside the range->unlikely
|
||||||
ethanol = (0.306f - red_f) / 0.00057f;
|
ethanol = 0.0;
|
||||||
if (ethanol < 10.0f)
|
} else {
|
||||||
ethanol = 0.0f;
|
ethanol = 1.52 / pow(red_f, 1.55);
|
||||||
if (ethanol > 500.0f)
|
|
||||||
ethanol = 500.0f;
|
|
||||||
}
|
}
|
||||||
this->ethanol_sensor_->publish_state(ethanol);
|
this->ethanol_sensor_->publish_state(ethanol);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->hydrogen_sensor_ != nullptr) {
|
if (this->hydrogen_sensor_ != nullptr) {
|
||||||
float hydrogen = 0.0f;
|
float hydrogen = 0.0f;
|
||||||
if (red_f <= 0.279f) {
|
if (red_f > 0.9f || red_f < 0.02) { // outside the range->unlikely
|
||||||
hydrogen = (0.279f - red_f) / 0.00026f;
|
hydrogen = 0.0;
|
||||||
if (hydrogen < 1.0f)
|
} else {
|
||||||
hydrogen = 0.0f;
|
hydrogen = 0.85 / pow(red_f, 1.75);
|
||||||
if (hydrogen > 1000.0f)
|
|
||||||
hydrogen = 1000.0f;
|
|
||||||
}
|
}
|
||||||
this->hydrogen_sensor_->publish_state(hydrogen);
|
this->hydrogen_sensor_->publish_state(hydrogen);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->ammonia_sensor_ != nullptr) {
|
if (this->ammonia_sensor_ != nullptr) {
|
||||||
float ammonia = 0.0f;
|
float ammonia = 0.0f;
|
||||||
if (red_f <= 0.8f) {
|
if (red_f > 0.98f || red_f < 0.2532) { // outside the ammonia range->unlikely
|
||||||
ammonia = (0.8f - red_f) / 0.0015f;
|
ammonia = 0.0;
|
||||||
if (ammonia < 1.0f)
|
} else {
|
||||||
ammonia = 0.0f;
|
ammonia = 0.9 / pow(red_f, 4.6);
|
||||||
if (ammonia > 500.0f)
|
|
||||||
ammonia = 500.0f;
|
|
||||||
}
|
}
|
||||||
this->ammonia_sensor_->publish_state(ammonia);
|
this->ammonia_sensor_->publish_state(ammonia);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import esphome.config_validation as cv
|
||||||
from esphome import pins
|
from esphome import pins
|
||||||
from esphome.components import display
|
from esphome.components import display
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
|
CONF_ENABLE_PIN,
|
||||||
CONF_HSYNC_PIN,
|
CONF_HSYNC_PIN,
|
||||||
CONF_RESET_PIN,
|
CONF_RESET_PIN,
|
||||||
CONF_DATA_PINS,
|
CONF_DATA_PINS,
|
||||||
|
@ -112,6 +113,7 @@ CONFIG_SCHEMA = cv.All(
|
||||||
cv.Required(CONF_PCLK_PIN): pins.internal_gpio_output_pin_schema,
|
cv.Required(CONF_PCLK_PIN): pins.internal_gpio_output_pin_schema,
|
||||||
cv.Required(CONF_HSYNC_PIN): pins.internal_gpio_output_pin_schema,
|
cv.Required(CONF_HSYNC_PIN): pins.internal_gpio_output_pin_schema,
|
||||||
cv.Required(CONF_VSYNC_PIN): pins.internal_gpio_output_pin_schema,
|
cv.Required(CONF_VSYNC_PIN): pins.internal_gpio_output_pin_schema,
|
||||||
|
cv.Optional(CONF_ENABLE_PIN): pins.gpio_output_pin_schema,
|
||||||
cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema,
|
cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema,
|
||||||
cv.Optional(CONF_HSYNC_PULSE_WIDTH, default=10): cv.int_,
|
cv.Optional(CONF_HSYNC_PULSE_WIDTH, default=10): cv.int_,
|
||||||
cv.Optional(CONF_HSYNC_BACK_PORCH, default=10): cv.int_,
|
cv.Optional(CONF_HSYNC_BACK_PORCH, default=10): cv.int_,
|
||||||
|
@ -164,6 +166,10 @@ async def to_code(config):
|
||||||
cg.add(var.add_data_pin(data_pin, index))
|
cg.add(var.add_data_pin(data_pin, index))
|
||||||
index += 1
|
index += 1
|
||||||
|
|
||||||
|
if enable_pin := config.get(CONF_ENABLE_PIN):
|
||||||
|
enable = await cg.gpio_pin_expression(enable_pin)
|
||||||
|
cg.add(var.set_enable_pin(enable))
|
||||||
|
|
||||||
if reset_pin := config.get(CONF_RESET_PIN):
|
if reset_pin := config.get(CONF_RESET_PIN):
|
||||||
reset = await cg.gpio_pin_expression(reset_pin)
|
reset = await cg.gpio_pin_expression(reset_pin)
|
||||||
cg.add(var.set_reset_pin(reset))
|
cg.add(var.set_reset_pin(reset))
|
||||||
|
|
|
@ -104,12 +104,30 @@ void RpiDpiRgb::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, " Height: %u", this->height_);
|
ESP_LOGCONFIG(TAG, " Height: %u", this->height_);
|
||||||
ESP_LOGCONFIG(TAG, " Width: %u", this->width_);
|
ESP_LOGCONFIG(TAG, " Width: %u", this->width_);
|
||||||
LOG_PIN(" DE Pin: ", this->de_pin_);
|
LOG_PIN(" DE Pin: ", this->de_pin_);
|
||||||
|
LOG_PIN(" Enable Pin: ", this->enable_pin_);
|
||||||
LOG_PIN(" Reset Pin: ", this->reset_pin_);
|
LOG_PIN(" Reset Pin: ", this->reset_pin_);
|
||||||
size_t data_pin_count = sizeof(this->data_pins_) / sizeof(this->data_pins_[0]);
|
size_t data_pin_count = sizeof(this->data_pins_) / sizeof(this->data_pins_[0]);
|
||||||
for (size_t i = 0; i != data_pin_count; i++)
|
for (size_t i = 0; i != data_pin_count; i++)
|
||||||
ESP_LOGCONFIG(TAG, " Data pin %d: %s", i, (this->data_pins_[i])->dump_summary().c_str());
|
ESP_LOGCONFIG(TAG, " Data pin %d: %s", i, (this->data_pins_[i])->dump_summary().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RpiDpiRgb::reset_display_() const {
|
||||||
|
if (this->reset_pin_ != nullptr) {
|
||||||
|
this->reset_pin_->setup();
|
||||||
|
this->reset_pin_->digital_write(false);
|
||||||
|
if (this->enable_pin_ != nullptr) {
|
||||||
|
this->enable_pin_->setup();
|
||||||
|
this->enable_pin_->digital_write(false);
|
||||||
|
}
|
||||||
|
delay(1);
|
||||||
|
this->reset_pin_->digital_write(true);
|
||||||
|
if (this->enable_pin_ != nullptr) {
|
||||||
|
delay(11);
|
||||||
|
this->enable_pin_->digital_write(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace rpi_dpi_rgb
|
} // namespace rpi_dpi_rgb
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@ class RpiDpiRgb : public display::Display {
|
||||||
void set_pclk_pin(InternalGPIOPin *pclk_pin) { this->pclk_pin_ = pclk_pin; }
|
void set_pclk_pin(InternalGPIOPin *pclk_pin) { this->pclk_pin_ = pclk_pin; }
|
||||||
void set_vsync_pin(InternalGPIOPin *vsync_pin) { this->vsync_pin_ = vsync_pin; }
|
void set_vsync_pin(InternalGPIOPin *vsync_pin) { this->vsync_pin_ = vsync_pin; }
|
||||||
void set_hsync_pin(InternalGPIOPin *hsync_pin) { this->hsync_pin_ = hsync_pin; }
|
void set_hsync_pin(InternalGPIOPin *hsync_pin) { this->hsync_pin_ = hsync_pin; }
|
||||||
|
void set_enable_pin(GPIOPin *enable_pin) { this->enable_pin_ = enable_pin; }
|
||||||
void set_reset_pin(GPIOPin *reset_pin) { this->reset_pin_ = reset_pin; }
|
void set_reset_pin(GPIOPin *reset_pin) { this->reset_pin_ = reset_pin; }
|
||||||
void set_width(uint16_t width) { this->width_ = width; }
|
void set_width(uint16_t width) { this->width_ = width; }
|
||||||
void set_dimensions(uint16_t width, uint16_t height) {
|
void set_dimensions(uint16_t width, uint16_t height) {
|
||||||
|
@ -62,10 +63,12 @@ class RpiDpiRgb : public display::Display {
|
||||||
protected:
|
protected:
|
||||||
int get_width_internal() override { return this->width_; }
|
int get_width_internal() override { return this->width_; }
|
||||||
int get_height_internal() override { return this->height_; }
|
int get_height_internal() override { return this->height_; }
|
||||||
|
void reset_display_() const;
|
||||||
InternalGPIOPin *de_pin_{nullptr};
|
InternalGPIOPin *de_pin_{nullptr};
|
||||||
InternalGPIOPin *pclk_pin_{nullptr};
|
InternalGPIOPin *pclk_pin_{nullptr};
|
||||||
InternalGPIOPin *hsync_pin_{nullptr};
|
InternalGPIOPin *hsync_pin_{nullptr};
|
||||||
InternalGPIOPin *vsync_pin_{nullptr};
|
InternalGPIOPin *vsync_pin_{nullptr};
|
||||||
|
GPIOPin *enable_pin_{nullptr};
|
||||||
GPIOPin *reset_pin_{nullptr};
|
GPIOPin *reset_pin_{nullptr};
|
||||||
InternalGPIOPin *data_pins_[16] = {};
|
InternalGPIOPin *data_pins_[16] = {};
|
||||||
uint16_t hsync_front_porch_ = 8;
|
uint16_t hsync_front_porch_ = 8;
|
||||||
|
|
|
@ -1,47 +1,39 @@
|
||||||
import esphome.codegen as cg
|
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome import pins
|
from esphome import pins
|
||||||
from esphome.components import (
|
import esphome.codegen as cg
|
||||||
spi,
|
from esphome.components import display, spi
|
||||||
display,
|
from esphome.components.esp32 import const, only_on_variant
|
||||||
)
|
|
||||||
from esphome.const import (
|
|
||||||
CONF_DC_PIN,
|
|
||||||
CONF_HSYNC_PIN,
|
|
||||||
CONF_RESET_PIN,
|
|
||||||
CONF_DATA_PINS,
|
|
||||||
CONF_ID,
|
|
||||||
CONF_DIMENSIONS,
|
|
||||||
CONF_VSYNC_PIN,
|
|
||||||
CONF_WIDTH,
|
|
||||||
CONF_HEIGHT,
|
|
||||||
CONF_LAMBDA,
|
|
||||||
CONF_MIRROR_X,
|
|
||||||
CONF_MIRROR_Y,
|
|
||||||
CONF_COLOR_ORDER,
|
|
||||||
CONF_TRANSFORM,
|
|
||||||
CONF_OFFSET_HEIGHT,
|
|
||||||
CONF_OFFSET_WIDTH,
|
|
||||||
CONF_INVERT_COLORS,
|
|
||||||
CONF_RED,
|
|
||||||
CONF_GREEN,
|
|
||||||
CONF_BLUE,
|
|
||||||
CONF_NUMBER,
|
|
||||||
CONF_IGNORE_STRAPPING_WARNING,
|
|
||||||
)
|
|
||||||
|
|
||||||
from esphome.components.esp32 import (
|
|
||||||
only_on_variant,
|
|
||||||
const,
|
|
||||||
)
|
|
||||||
from esphome.components.rpi_dpi_rgb.display import (
|
from esphome.components.rpi_dpi_rgb.display import (
|
||||||
CONF_PCLK_FREQUENCY,
|
CONF_PCLK_FREQUENCY,
|
||||||
CONF_PCLK_INVERTED,
|
CONF_PCLK_INVERTED,
|
||||||
)
|
)
|
||||||
from .init_sequences import (
|
import esphome.config_validation as cv
|
||||||
ST7701S_INITS,
|
from esphome.const import (
|
||||||
cmd,
|
CONF_BLUE,
|
||||||
|
CONF_COLOR_ORDER,
|
||||||
|
CONF_DATA_PINS,
|
||||||
|
CONF_DC_PIN,
|
||||||
|
CONF_DIMENSIONS,
|
||||||
|
CONF_GREEN,
|
||||||
|
CONF_HEIGHT,
|
||||||
|
CONF_HSYNC_PIN,
|
||||||
|
CONF_ID,
|
||||||
|
CONF_IGNORE_STRAPPING_WARNING,
|
||||||
|
CONF_INVERT_COLORS,
|
||||||
|
CONF_LAMBDA,
|
||||||
|
CONF_MIRROR_X,
|
||||||
|
CONF_MIRROR_Y,
|
||||||
|
CONF_NUMBER,
|
||||||
|
CONF_OFFSET_HEIGHT,
|
||||||
|
CONF_OFFSET_WIDTH,
|
||||||
|
CONF_RED,
|
||||||
|
CONF_RESET_PIN,
|
||||||
|
CONF_TRANSFORM,
|
||||||
|
CONF_VSYNC_PIN,
|
||||||
|
CONF_WIDTH,
|
||||||
)
|
)
|
||||||
|
from esphome.core import TimePeriod
|
||||||
|
|
||||||
|
from .init_sequences import ST7701S_INITS, cmd
|
||||||
|
|
||||||
CONF_INIT_SEQUENCE = "init_sequence"
|
CONF_INIT_SEQUENCE = "init_sequence"
|
||||||
CONF_DE_PIN = "de_pin"
|
CONF_DE_PIN = "de_pin"
|
||||||
|
@ -59,6 +51,7 @@ DEPENDENCIES = ["spi", "esp32"]
|
||||||
st7701s_ns = cg.esphome_ns.namespace("st7701s")
|
st7701s_ns = cg.esphome_ns.namespace("st7701s")
|
||||||
ST7701S = st7701s_ns.class_("ST7701S", display.Display, cg.Component, spi.SPIDevice)
|
ST7701S = st7701s_ns.class_("ST7701S", display.Display, cg.Component, spi.SPIDevice)
|
||||||
ColorOrder = display.display_ns.enum("ColorMode")
|
ColorOrder = display.display_ns.enum("ColorMode")
|
||||||
|
ST7701S_DELAY_FLAG = 0xFF
|
||||||
|
|
||||||
COLOR_ORDERS = {
|
COLOR_ORDERS = {
|
||||||
"RGB": ColorOrder.COLOR_ORDER_RGB,
|
"RGB": ColorOrder.COLOR_ORDER_RGB,
|
||||||
|
@ -93,18 +86,23 @@ def map_sequence(value):
|
||||||
"""
|
"""
|
||||||
An initialisation sequence can be selected from one of the pre-defined sequences in init_sequences.py,
|
An initialisation sequence can be selected from one of the pre-defined sequences in init_sequences.py,
|
||||||
or can be a literal array of data bytes.
|
or can be a literal array of data bytes.
|
||||||
The format is a repeated sequence of [CMD, LEN, <data>] where <data> is LEN bytes.
|
The format is a repeated sequence of [CMD, <data>] where <data> is s a sequence of bytes. The length is inferred
|
||||||
|
from the length of the sequence and should not be explicit.
|
||||||
|
A delay can be inserted by specifying "- delay N" where N is in ms
|
||||||
"""
|
"""
|
||||||
|
if isinstance(value, str) and value.lower().startswith("delay "):
|
||||||
|
value = value.lower()[6:]
|
||||||
|
delay = cv.All(
|
||||||
|
cv.positive_time_period_milliseconds,
|
||||||
|
cv.Range(TimePeriod(milliseconds=1), TimePeriod(milliseconds=255)),
|
||||||
|
)(value)
|
||||||
|
return [delay, ST7701S_DELAY_FLAG]
|
||||||
if not isinstance(value, list):
|
if not isinstance(value, list):
|
||||||
value = cv.int_(value)
|
value = cv.int_(value)
|
||||||
value = cv.one_of(*ST7701S_INITS)(value)
|
value = cv.one_of(*ST7701S_INITS)(value)
|
||||||
return ST7701S_INITS[value]
|
return ST7701S_INITS[value]
|
||||||
# value = cv.ensure_list(cv.uint8_t)(value)
|
value = cv.Length(min=1, max=254)(value)
|
||||||
data_length = len(value)
|
return cmd(*value)
|
||||||
if data_length == 0:
|
|
||||||
raise cv.Invalid("Empty sequence")
|
|
||||||
value = cmd(*value)
|
|
||||||
return value
|
|
||||||
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.All(
|
CONFIG_SCHEMA = cv.All(
|
||||||
|
|
|
@ -138,12 +138,17 @@ void ST7701S::write_init_sequence_() {
|
||||||
for (size_t i = 0; i != this->init_sequence_.size();) {
|
for (size_t i = 0; i != this->init_sequence_.size();) {
|
||||||
uint8_t cmd = this->init_sequence_[i++];
|
uint8_t cmd = this->init_sequence_[i++];
|
||||||
size_t len = this->init_sequence_[i++];
|
size_t len = this->init_sequence_[i++];
|
||||||
|
if (len == ST7701S_DELAY_FLAG) {
|
||||||
|
ESP_LOGV(TAG, "Delay %dms", cmd);
|
||||||
|
delay(cmd);
|
||||||
|
} else {
|
||||||
this->write_sequence_(cmd, len, &this->init_sequence_[i]);
|
this->write_sequence_(cmd, len, &this->init_sequence_[i]);
|
||||||
i += len;
|
i += len;
|
||||||
esph_log_v(TAG, "Command %X, %d bytes", cmd, len);
|
ESP_LOGV(TAG, "Command %X, %d bytes", cmd, len);
|
||||||
if (cmd == SW_RESET_CMD)
|
if (cmd == SW_RESET_CMD)
|
||||||
delay(6);
|
delay(6);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// st7701 does not appear to support axis swapping
|
// st7701 does not appear to support axis swapping
|
||||||
this->write_sequence_(CMD2_BKSEL, sizeof(CMD2_BK0), CMD2_BK0);
|
this->write_sequence_(CMD2_BKSEL, sizeof(CMD2_BK0), CMD2_BK0);
|
||||||
this->write_command_(SDIR_CMD); // this is in the BK0 command set
|
this->write_command_(SDIR_CMD); // this is in the BK0 command set
|
||||||
|
@ -153,7 +158,7 @@ void ST7701S::write_init_sequence_() {
|
||||||
val |= 0x10;
|
val |= 0x10;
|
||||||
this->write_command_(MADCTL_CMD);
|
this->write_command_(MADCTL_CMD);
|
||||||
this->write_data_(val);
|
this->write_data_(val);
|
||||||
esph_log_d(TAG, "write MADCTL %X", val);
|
ESP_LOGD(TAG, "write MADCTL %X", val);
|
||||||
this->write_command_(this->invert_colors_ ? INVERT_ON : INVERT_OFF);
|
this->write_command_(this->invert_colors_ ? INVERT_ON : INVERT_OFF);
|
||||||
this->set_timeout(120, [this] {
|
this->set_timeout(120, [this] {
|
||||||
this->write_command_(SLEEP_OUT);
|
this->write_command_(SLEEP_OUT);
|
||||||
|
|
|
@ -25,6 +25,7 @@ const uint8_t INVERT_ON = 0x21;
|
||||||
const uint8_t DISPLAY_ON = 0x29;
|
const uint8_t DISPLAY_ON = 0x29;
|
||||||
const uint8_t CMD2_BKSEL = 0xFF;
|
const uint8_t CMD2_BKSEL = 0xFF;
|
||||||
const uint8_t CMD2_BK0[5] = {0x77, 0x01, 0x00, 0x00, 0x10};
|
const uint8_t CMD2_BK0[5] = {0x77, 0x01, 0x00, 0x00, 0x10};
|
||||||
|
const uint8_t ST7701S_DELAY_FLAG = 0xFF;
|
||||||
|
|
||||||
class ST7701S : public display::Display,
|
class ST7701S : public display::Display,
|
||||||
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_LEADING,
|
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_LEADING,
|
||||||
|
|
|
@ -9,6 +9,7 @@ from esphome.const import (
|
||||||
CONF_MULTIPLY,
|
CONF_MULTIPLY,
|
||||||
CONF_STEP,
|
CONF_STEP,
|
||||||
CONF_INITIAL_VALUE,
|
CONF_INITIAL_VALUE,
|
||||||
|
CONF_RESTORE_VALUE,
|
||||||
)
|
)
|
||||||
from .. import tuya_ns, CONF_TUYA_ID, Tuya, TuyaDatapointType
|
from .. import tuya_ns, CONF_TUYA_ID, Tuya, TuyaDatapointType
|
||||||
|
|
||||||
|
@ -58,6 +59,7 @@ CONFIG_SCHEMA = cv.All(
|
||||||
DATAPOINT_TYPES, lower=True
|
DATAPOINT_TYPES, lower=True
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_INITIAL_VALUE): cv.float_,
|
cv.Optional(CONF_INITIAL_VALUE): cv.float_,
|
||||||
|
cv.Optional(CONF_RESTORE_VALUE, default=False): cv.boolean,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
@ -90,3 +92,4 @@ async def to_code(config):
|
||||||
hidden_init_value := hidden_config.get(CONF_INITIAL_VALUE, None)
|
hidden_init_value := hidden_config.get(CONF_INITIAL_VALUE, None)
|
||||||
) is not None:
|
) is not None:
|
||||||
cg.add(var.set_datapoint_initial_value(hidden_init_value))
|
cg.add(var.set_datapoint_initial_value(hidden_init_value))
|
||||||
|
cg.add(var.set_restore_value(hidden_config[CONF_RESTORE_VALUE]))
|
||||||
|
|
|
@ -7,14 +7,28 @@ namespace tuya {
|
||||||
static const char *const TAG = "tuya.number";
|
static const char *const TAG = "tuya.number";
|
||||||
|
|
||||||
void TuyaNumber::setup() {
|
void TuyaNumber::setup() {
|
||||||
|
if (this->restore_value_) {
|
||||||
|
this->pref_ = global_preferences->make_preference<float>(this->get_object_id_hash());
|
||||||
|
}
|
||||||
|
|
||||||
this->parent_->register_listener(this->number_id_, [this](const TuyaDatapoint &datapoint) {
|
this->parent_->register_listener(this->number_id_, [this](const TuyaDatapoint &datapoint) {
|
||||||
if (datapoint.type == TuyaDatapointType::INTEGER) {
|
if (datapoint.type == TuyaDatapointType::INTEGER) {
|
||||||
ESP_LOGV(TAG, "MCU reported number %u is: %d", datapoint.id, datapoint.value_int);
|
ESP_LOGV(TAG, "MCU reported number %u is: %d", datapoint.id, datapoint.value_int);
|
||||||
this->publish_state(datapoint.value_int / multiply_by_);
|
float value = datapoint.value_int / multiply_by_;
|
||||||
|
this->publish_state(value);
|
||||||
|
if (this->restore_value_)
|
||||||
|
this->pref_.save(&value);
|
||||||
} else if (datapoint.type == TuyaDatapointType::ENUM) {
|
} else if (datapoint.type == TuyaDatapointType::ENUM) {
|
||||||
ESP_LOGV(TAG, "MCU reported number %u is: %u", datapoint.id, datapoint.value_enum);
|
ESP_LOGV(TAG, "MCU reported number %u is: %u", datapoint.id, datapoint.value_enum);
|
||||||
this->publish_state(datapoint.value_enum);
|
float value = datapoint.value_enum;
|
||||||
|
this->publish_state(value);
|
||||||
|
if (this->restore_value_)
|
||||||
|
this->pref_.save(&value);
|
||||||
|
} else {
|
||||||
|
ESP_LOGW(TAG, "Reported type (%d) is not a number!", static_cast<int>(datapoint.type));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((this->type_) && (this->type_ != datapoint.type)) {
|
if ((this->type_) && (this->type_ != datapoint.type)) {
|
||||||
ESP_LOGW(TAG, "Reported type (%d) different than previously set (%d)!", static_cast<int>(datapoint.type),
|
ESP_LOGW(TAG, "Reported type (%d) different than previously set (%d)!", static_cast<int>(datapoint.type),
|
||||||
static_cast<int>(*this->type_));
|
static_cast<int>(*this->type_));
|
||||||
|
@ -23,8 +37,26 @@ void TuyaNumber::setup() {
|
||||||
});
|
});
|
||||||
|
|
||||||
this->parent_->add_on_initialized_callback([this] {
|
this->parent_->add_on_initialized_callback([this] {
|
||||||
if ((this->initial_value_) && (this->type_)) {
|
if (this->type_) {
|
||||||
this->control(*this->initial_value_);
|
float value;
|
||||||
|
if (!this->restore_value_) {
|
||||||
|
if (this->initial_value_) {
|
||||||
|
value = *this->initial_value_;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!this->pref_.load(&value)) {
|
||||||
|
if (this->initial_value_) {
|
||||||
|
value = *this->initial_value_;
|
||||||
|
} else {
|
||||||
|
value = this->traits.get_min_value();
|
||||||
|
ESP_LOGW(TAG, "Failed to restore and there is no initial value defined. Setting min_value (%f)", value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this->control(value);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -38,6 +70,9 @@ void TuyaNumber::control(float value) {
|
||||||
this->parent_->set_enum_datapoint_value(this->number_id_, value);
|
this->parent_->set_enum_datapoint_value(this->number_id_, value);
|
||||||
}
|
}
|
||||||
this->publish_state(value);
|
this->publish_state(value);
|
||||||
|
|
||||||
|
if (this->restore_value_)
|
||||||
|
this->pref_.save(&value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TuyaNumber::dump_config() {
|
void TuyaNumber::dump_config() {
|
||||||
|
@ -52,6 +87,8 @@ void TuyaNumber::dump_config() {
|
||||||
if (this->initial_value_) {
|
if (this->initial_value_) {
|
||||||
ESP_LOGCONFIG(TAG, " Initial Value: %f", *this->initial_value_);
|
ESP_LOGCONFIG(TAG, " Initial Value: %f", *this->initial_value_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ESP_LOGCONFIG(TAG, " Restore Value: %s", YESNO(this->restore_value_));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace tuya
|
} // namespace tuya
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
|
||||||
#include "esphome/components/tuya/tuya.h"
|
|
||||||
#include "esphome/components/number/number.h"
|
#include "esphome/components/number/number.h"
|
||||||
|
#include "esphome/components/tuya/tuya.h"
|
||||||
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/core/optional.h"
|
#include "esphome/core/optional.h"
|
||||||
|
#include "esphome/core/preferences.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace tuya {
|
namespace tuya {
|
||||||
|
@ -16,6 +17,7 @@ class TuyaNumber : public number::Number, public Component {
|
||||||
void set_write_multiply(float factor) { multiply_by_ = factor; }
|
void set_write_multiply(float factor) { multiply_by_ = factor; }
|
||||||
void set_datapoint_type(TuyaDatapointType type) { type_ = type; }
|
void set_datapoint_type(TuyaDatapointType type) { type_ = type; }
|
||||||
void set_datapoint_initial_value(float value) { this->initial_value_ = value; }
|
void set_datapoint_initial_value(float value) { this->initial_value_ = value; }
|
||||||
|
void set_restore_value(bool restore_value) { this->restore_value_ = restore_value; }
|
||||||
|
|
||||||
void set_tuya_parent(Tuya *parent) { this->parent_ = parent; }
|
void set_tuya_parent(Tuya *parent) { this->parent_ = parent; }
|
||||||
|
|
||||||
|
@ -27,6 +29,9 @@ class TuyaNumber : public number::Number, public Component {
|
||||||
float multiply_by_{1.0};
|
float multiply_by_{1.0};
|
||||||
optional<TuyaDatapointType> type_{};
|
optional<TuyaDatapointType> type_{};
|
||||||
optional<float> initial_value_{};
|
optional<float> initial_value_{};
|
||||||
|
bool restore_value_{false};
|
||||||
|
|
||||||
|
ESPPreferenceObject pref_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace tuya
|
} // namespace tuya
|
||||||
|
|
158
esphome/components/udp/__init__.py
Normal file
158
esphome/components/udp/__init__.py
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
import hashlib
|
||||||
|
|
||||||
|
import esphome.codegen as cg
|
||||||
|
from esphome.components.api import CONF_ENCRYPTION
|
||||||
|
from esphome.components.binary_sensor import BinarySensor
|
||||||
|
from esphome.components.sensor import Sensor
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.const import (
|
||||||
|
CONF_BINARY_SENSORS,
|
||||||
|
CONF_ID,
|
||||||
|
CONF_INTERNAL,
|
||||||
|
CONF_KEY,
|
||||||
|
CONF_NAME,
|
||||||
|
CONF_PORT,
|
||||||
|
CONF_SENSORS,
|
||||||
|
)
|
||||||
|
from esphome.cpp_generator import MockObjClass
|
||||||
|
|
||||||
|
CODEOWNERS = ["@clydebarrow"]
|
||||||
|
DEPENDENCIES = ["network"]
|
||||||
|
AUTO_LOAD = ["socket"]
|
||||||
|
MULTI_CONF = True
|
||||||
|
|
||||||
|
udp_ns = cg.esphome_ns.namespace("udp")
|
||||||
|
UDPComponent = udp_ns.class_("UDPComponent", cg.PollingComponent)
|
||||||
|
|
||||||
|
CONF_BROADCAST = "broadcast"
|
||||||
|
CONF_BROADCAST_ID = "broadcast_id"
|
||||||
|
CONF_ADDRESSES = "addresses"
|
||||||
|
CONF_PROVIDER = "provider"
|
||||||
|
CONF_PROVIDERS = "providers"
|
||||||
|
CONF_REMOTE_ID = "remote_id"
|
||||||
|
CONF_UDP_ID = "udp_id"
|
||||||
|
CONF_PING_PONG_ENABLE = "ping_pong_enable"
|
||||||
|
CONF_PING_PONG_RECYCLE_TIME = "ping_pong_recycle_time"
|
||||||
|
CONF_ROLLING_CODE_ENABLE = "rolling_code_enable"
|
||||||
|
|
||||||
|
|
||||||
|
def sensor_validation(cls: MockObjClass):
|
||||||
|
return cv.maybe_simple_value(
|
||||||
|
cv.Schema(
|
||||||
|
{
|
||||||
|
cv.Required(CONF_ID): cv.use_id(cls),
|
||||||
|
cv.Optional(CONF_BROADCAST_ID): cv.validate_id_name,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
key=CONF_ID,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
ENCRYPTION_SCHEMA = {
|
||||||
|
cv.Optional(CONF_ENCRYPTION): cv.maybe_simple_value(
|
||||||
|
cv.Schema(
|
||||||
|
{
|
||||||
|
cv.Required(CONF_KEY): cv.string,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
key=CONF_KEY,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
PROVIDER_SCHEMA = cv.Schema(
|
||||||
|
{
|
||||||
|
cv.Required(CONF_NAME): cv.valid_name,
|
||||||
|
}
|
||||||
|
).extend(ENCRYPTION_SCHEMA)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_(config):
|
||||||
|
if CONF_ENCRYPTION in config:
|
||||||
|
if CONF_SENSORS not in config and CONF_BINARY_SENSORS not in config:
|
||||||
|
raise cv.Invalid("No sensors or binary sensors to encrypt")
|
||||||
|
elif config[CONF_ROLLING_CODE_ENABLE]:
|
||||||
|
raise cv.Invalid("Rolling code requires an encryption key")
|
||||||
|
if config[CONF_PING_PONG_ENABLE]:
|
||||||
|
if not any(CONF_ENCRYPTION in p for p in config.get(CONF_PROVIDERS) or ()):
|
||||||
|
raise cv.Invalid("Ping-pong requires at least one encrypted provider")
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.All(
|
||||||
|
cv.polling_component_schema("15s")
|
||||||
|
.extend(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(UDPComponent),
|
||||||
|
cv.Optional(CONF_PORT, default=18511): cv.port,
|
||||||
|
cv.Optional(CONF_ADDRESSES, default=["255.255.255.255"]): cv.ensure_list(
|
||||||
|
cv.ipv4
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_ROLLING_CODE_ENABLE, default=False): cv.boolean,
|
||||||
|
cv.Optional(CONF_PING_PONG_ENABLE, default=False): cv.boolean,
|
||||||
|
cv.Optional(
|
||||||
|
CONF_PING_PONG_RECYCLE_TIME, default="600s"
|
||||||
|
): cv.positive_time_period_seconds,
|
||||||
|
cv.Optional(CONF_SENSORS): cv.ensure_list(sensor_validation(Sensor)),
|
||||||
|
cv.Optional(CONF_BINARY_SENSORS): cv.ensure_list(
|
||||||
|
sensor_validation(BinarySensor)
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_PROVIDERS): cv.ensure_list(PROVIDER_SCHEMA),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.extend(ENCRYPTION_SCHEMA),
|
||||||
|
validate_,
|
||||||
|
)
|
||||||
|
|
||||||
|
SENSOR_SCHEMA = cv.Schema(
|
||||||
|
{
|
||||||
|
cv.Optional(CONF_REMOTE_ID): cv.string_strict,
|
||||||
|
cv.Required(CONF_PROVIDER): cv.valid_name,
|
||||||
|
cv.GenerateID(CONF_UDP_ID): cv.use_id(UDPComponent),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def require_internal_with_name(config):
|
||||||
|
if CONF_NAME in config and CONF_INTERNAL not in config:
|
||||||
|
raise cv.Invalid("Must provide internal: config when using name:")
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
def hash_encryption_key(config: dict):
|
||||||
|
return list(hashlib.sha256(config[CONF_KEY].encode()).digest())
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
cg.add_define("USE_UDP")
|
||||||
|
cg.add_global(udp_ns.using)
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
await cg.register_component(var, config)
|
||||||
|
cg.add(var.set_port(config[CONF_PORT]))
|
||||||
|
cg.add(var.set_rolling_code_enable(config[CONF_ROLLING_CODE_ENABLE]))
|
||||||
|
cg.add(var.set_ping_pong_enable(config[CONF_PING_PONG_ENABLE]))
|
||||||
|
cg.add(
|
||||||
|
var.set_ping_pong_recycle_time(
|
||||||
|
config[CONF_PING_PONG_RECYCLE_TIME].total_seconds
|
||||||
|
)
|
||||||
|
)
|
||||||
|
for sens_conf in config.get(CONF_SENSORS, ()):
|
||||||
|
sens_id = sens_conf[CONF_ID]
|
||||||
|
sensor = await cg.get_variable(sens_id)
|
||||||
|
bcst_id = sens_conf.get(CONF_BROADCAST_ID, sens_id.id)
|
||||||
|
cg.add(var.add_sensor(bcst_id, sensor))
|
||||||
|
for sens_conf in config.get(CONF_BINARY_SENSORS, ()):
|
||||||
|
sens_id = sens_conf[CONF_ID]
|
||||||
|
sensor = await cg.get_variable(sens_id)
|
||||||
|
bcst_id = sens_conf.get(CONF_BROADCAST_ID, sens_id.id)
|
||||||
|
cg.add(var.add_binary_sensor(bcst_id, sensor))
|
||||||
|
for address in config[CONF_ADDRESSES]:
|
||||||
|
cg.add(var.add_address(str(address)))
|
||||||
|
|
||||||
|
if encryption := config.get(CONF_ENCRYPTION):
|
||||||
|
cg.add(var.set_encryption_key(hash_encryption_key(encryption)))
|
||||||
|
|
||||||
|
for provider in config.get(CONF_PROVIDERS, ()):
|
||||||
|
name = provider[CONF_NAME]
|
||||||
|
cg.add(var.add_provider(name))
|
||||||
|
if encryption := provider.get(CONF_ENCRYPTION):
|
||||||
|
cg.add(var.set_provider_encryption(name, hash_encryption_key(encryption)))
|
27
esphome/components/udp/binary_sensor.py
Normal file
27
esphome/components/udp/binary_sensor.py
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
from esphome.components import binary_sensor
|
||||||
|
from esphome.config_validation import All, has_at_least_one_key
|
||||||
|
from esphome.const import CONF_ID
|
||||||
|
|
||||||
|
from . import (
|
||||||
|
CONF_PROVIDER,
|
||||||
|
CONF_REMOTE_ID,
|
||||||
|
CONF_UDP_ID,
|
||||||
|
SENSOR_SCHEMA,
|
||||||
|
require_internal_with_name,
|
||||||
|
)
|
||||||
|
|
||||||
|
DEPENDENCIES = ["udp"]
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = All(
|
||||||
|
binary_sensor.binary_sensor_schema().extend(SENSOR_SCHEMA),
|
||||||
|
has_at_least_one_key(CONF_ID, CONF_REMOTE_ID),
|
||||||
|
require_internal_with_name,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
var = await binary_sensor.new_binary_sensor(config)
|
||||||
|
comp = await cg.get_variable(config[CONF_UDP_ID])
|
||||||
|
remote_id = str(config.get(CONF_REMOTE_ID) or config.get(CONF_ID))
|
||||||
|
cg.add(comp.add_remote_binary_sensor(config[CONF_PROVIDER], remote_id, var))
|
27
esphome/components/udp/sensor.py
Normal file
27
esphome/components/udp/sensor.py
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
from esphome.components.sensor import new_sensor, sensor_schema
|
||||||
|
from esphome.config_validation import All, has_at_least_one_key
|
||||||
|
from esphome.const import CONF_ID
|
||||||
|
|
||||||
|
from . import (
|
||||||
|
CONF_PROVIDER,
|
||||||
|
CONF_REMOTE_ID,
|
||||||
|
CONF_UDP_ID,
|
||||||
|
SENSOR_SCHEMA,
|
||||||
|
require_internal_with_name,
|
||||||
|
)
|
||||||
|
|
||||||
|
DEPENDENCIES = ["udp"]
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = All(
|
||||||
|
sensor_schema().extend(SENSOR_SCHEMA),
|
||||||
|
has_at_least_one_key(CONF_ID, CONF_REMOTE_ID),
|
||||||
|
require_internal_with_name,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
var = await new_sensor(config)
|
||||||
|
comp = await cg.get_variable(config[CONF_UDP_ID])
|
||||||
|
remote_id = str(config.get(CONF_REMOTE_ID) or config.get(CONF_ID))
|
||||||
|
cg.add(comp.add_remote_sensor(config[CONF_PROVIDER], remote_id, var))
|
616
esphome/components/udp/udp_component.cpp
Normal file
616
esphome/components/udp/udp_component.cpp
Normal file
|
@ -0,0 +1,616 @@
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
#include "esphome/core/application.h"
|
||||||
|
#include "esphome/components/network/util.h"
|
||||||
|
#include "udp_component.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace udp {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Structure of a data packet; everything is little-endian
|
||||||
|
*
|
||||||
|
* --- In clear text ---
|
||||||
|
* MAGIC_NUMBER: 16 bits
|
||||||
|
* host name length: 1 byte
|
||||||
|
* host name: (length) bytes
|
||||||
|
* padding: 0 or more null bytes to a 4 byte boundary
|
||||||
|
*
|
||||||
|
* --- Encrypted (if key set) ----
|
||||||
|
* DATA_KEY: 1 byte: OR ROLLING_CODE_KEY:
|
||||||
|
* Rolling code (if enabled): 8 bytes
|
||||||
|
* Ping keys: if any
|
||||||
|
* repeat:
|
||||||
|
* PING_KEY: 1 byte
|
||||||
|
* ping code: 4 bytes
|
||||||
|
* Sensors:
|
||||||
|
* repeat:
|
||||||
|
* SENSOR_KEY: 1 byte
|
||||||
|
* float value: 4 bytes
|
||||||
|
* name length: 1 byte
|
||||||
|
* name
|
||||||
|
* Binary Sensors:
|
||||||
|
* repeat:
|
||||||
|
* BINARY_SENSOR_KEY: 1 byte
|
||||||
|
* bool value: 1 bytes
|
||||||
|
* name length: 1 byte
|
||||||
|
* name
|
||||||
|
*
|
||||||
|
* Padded to a 4 byte boundary with nulls
|
||||||
|
*
|
||||||
|
* Structure of a ping request packet:
|
||||||
|
* --- In clear text ---
|
||||||
|
* MAGIC_PING: 16 bits
|
||||||
|
* host name length: 1 byte
|
||||||
|
* host name: (length) bytes
|
||||||
|
* Ping key (4 bytes)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static const char *const TAG = "udp";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XXTEA implementation, using 256 bit key.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static const uint32_t DELTA = 0x9e3779b9;
|
||||||
|
#define MX ((((z >> 5) ^ (y << 2)) + ((y >> 3) ^ (z << 4))) ^ ((sum ^ y) + (k[(p ^ e) & 7] ^ z)))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypt a block of data in-place
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void xxtea_encrypt(uint32_t *v, size_t n, const uint32_t *k) {
|
||||||
|
uint32_t z, y, sum, e;
|
||||||
|
size_t p;
|
||||||
|
size_t q = 6 + 52 / n;
|
||||||
|
sum = 0;
|
||||||
|
z = v[n - 1];
|
||||||
|
while (q-- != 0) {
|
||||||
|
sum += DELTA;
|
||||||
|
e = (sum >> 2);
|
||||||
|
for (p = 0; p != n - 1; p++) {
|
||||||
|
y = v[p + 1];
|
||||||
|
z = v[p] += MX;
|
||||||
|
}
|
||||||
|
y = v[0];
|
||||||
|
z = v[n - 1] += MX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xxtea_decrypt(uint32_t *v, size_t n, const uint32_t *k) {
|
||||||
|
uint32_t z, y, sum, e;
|
||||||
|
size_t p;
|
||||||
|
size_t q = 6 + 52 / n;
|
||||||
|
sum = q * DELTA;
|
||||||
|
y = v[0];
|
||||||
|
while (q-- != 0) {
|
||||||
|
e = (sum >> 2);
|
||||||
|
for (p = n - 1; p != 0; p--) {
|
||||||
|
z = v[p - 1];
|
||||||
|
y = v[p] -= MX;
|
||||||
|
}
|
||||||
|
z = v[n - 1];
|
||||||
|
y = v[0] -= MX;
|
||||||
|
sum -= DELTA;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static size_t round4(size_t value) { return (value + 3) & ~3; }
|
||||||
|
|
||||||
|
union FuData {
|
||||||
|
uint32_t u32;
|
||||||
|
float f32;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const size_t MAX_PACKET_SIZE = 508;
|
||||||
|
static const uint16_t MAGIC_NUMBER = 0x4553;
|
||||||
|
static const uint16_t MAGIC_PING = 0x5048;
|
||||||
|
static const uint32_t PREF_HASH = 0x45535043;
|
||||||
|
enum DataKey {
|
||||||
|
ZERO_FILL_KEY,
|
||||||
|
DATA_KEY,
|
||||||
|
SENSOR_KEY,
|
||||||
|
BINARY_SENSOR_KEY,
|
||||||
|
PING_KEY,
|
||||||
|
ROLLING_CODE_KEY,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const size_t MAX_PING_KEYS = 4;
|
||||||
|
|
||||||
|
static inline void add(std::vector<uint8_t> &vec, uint32_t data) {
|
||||||
|
vec.push_back(data & 0xFF);
|
||||||
|
vec.push_back((data >> 8) & 0xFF);
|
||||||
|
vec.push_back((data >> 16) & 0xFF);
|
||||||
|
vec.push_back((data >> 24) & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t get_uint32(uint8_t *&buf) {
|
||||||
|
uint32_t data = *buf++;
|
||||||
|
data += *buf++ << 8;
|
||||||
|
data += *buf++ << 16;
|
||||||
|
data += *buf++ << 24;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint16_t get_uint16(uint8_t *&buf) {
|
||||||
|
uint16_t data = *buf++;
|
||||||
|
data += *buf++ << 8;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void add(std::vector<uint8_t> &vec, uint8_t data) { vec.push_back(data); }
|
||||||
|
static inline void add(std::vector<uint8_t> &vec, uint16_t data) {
|
||||||
|
vec.push_back((uint8_t) data);
|
||||||
|
vec.push_back((uint8_t) (data >> 8));
|
||||||
|
}
|
||||||
|
static inline void add(std::vector<uint8_t> &vec, DataKey data) { vec.push_back(data); }
|
||||||
|
static void add(std::vector<uint8_t> &vec, const char *str) {
|
||||||
|
auto len = strlen(str);
|
||||||
|
vec.push_back(len);
|
||||||
|
for (size_t i = 0; i != len; i++) {
|
||||||
|
vec.push_back(*str++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UDPComponent::setup() {
|
||||||
|
this->name_ = App.get_name().c_str();
|
||||||
|
if (strlen(this->name_) > 255) {
|
||||||
|
this->mark_failed();
|
||||||
|
this->status_set_error("Device name exceeds 255 chars");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->resend_ping_key_ = this->ping_pong_enable_;
|
||||||
|
// restore the upper 32 bits of the rolling code, increment and save.
|
||||||
|
this->pref_ = global_preferences->make_preference<uint32_t>(PREF_HASH, true);
|
||||||
|
this->pref_.load(&this->rolling_code_[1]);
|
||||||
|
this->rolling_code_[1]++;
|
||||||
|
this->pref_.save(&this->rolling_code_[1]);
|
||||||
|
this->ping_key_ = random_uint32();
|
||||||
|
ESP_LOGV(TAG, "Rolling code incremented, upper part now %u", (unsigned) this->rolling_code_[1]);
|
||||||
|
#ifdef USE_SENSOR
|
||||||
|
for (auto &sensor : this->sensors_) {
|
||||||
|
sensor.sensor->add_on_state_callback([this, &sensor](float x) {
|
||||||
|
this->updated_ = true;
|
||||||
|
sensor.updated = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef USE_BINARY_SENSOR
|
||||||
|
for (auto &sensor : this->binary_sensors_) {
|
||||||
|
sensor.sensor->add_on_state_callback([this, &sensor](bool value) {
|
||||||
|
this->updated_ = true;
|
||||||
|
sensor.updated = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
this->should_send_ = this->ping_pong_enable_;
|
||||||
|
#ifdef USE_SENSOR
|
||||||
|
this->should_send_ |= !this->sensors_.empty();
|
||||||
|
#endif
|
||||||
|
#ifdef USE_BINARY_SENSOR
|
||||||
|
this->should_send_ |= !this->binary_sensors_.empty();
|
||||||
|
#endif
|
||||||
|
this->should_listen_ = !this->providers_.empty() || this->is_encrypted_();
|
||||||
|
// initialise the header. This is invariant.
|
||||||
|
add(this->header_, MAGIC_NUMBER);
|
||||||
|
add(this->header_, this->name_);
|
||||||
|
// pad to a multiple of 4 bytes
|
||||||
|
while (this->header_.size() & 0x3)
|
||||||
|
this->header_.push_back(0);
|
||||||
|
#if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS)
|
||||||
|
for (const auto &address : this->addresses_) {
|
||||||
|
struct sockaddr saddr {};
|
||||||
|
socket::set_sockaddr(&saddr, sizeof(saddr), address, this->port_);
|
||||||
|
this->sockaddrs_.push_back(saddr);
|
||||||
|
}
|
||||||
|
// set up broadcast socket
|
||||||
|
if (this->should_send_) {
|
||||||
|
this->broadcast_socket_ = socket::socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
|
||||||
|
if (this->broadcast_socket_ == nullptr) {
|
||||||
|
this->mark_failed();
|
||||||
|
this->status_set_error("Could not create socket");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int enable = 1;
|
||||||
|
auto err = this->broadcast_socket_->setsockopt(SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
|
||||||
|
if (err != 0) {
|
||||||
|
this->status_set_warning("Socket unable to set reuseaddr");
|
||||||
|
// we can still continue
|
||||||
|
}
|
||||||
|
err = this->broadcast_socket_->setsockopt(SOL_SOCKET, SO_BROADCAST, &enable, sizeof(int));
|
||||||
|
if (err != 0) {
|
||||||
|
this->status_set_warning("Socket unable to set broadcast");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// create listening socket if we either want to subscribe to providers, or need to listen
|
||||||
|
// for ping key broadcasts.
|
||||||
|
if (this->should_listen_) {
|
||||||
|
this->listen_socket_ = socket::socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
|
||||||
|
if (this->listen_socket_ == nullptr) {
|
||||||
|
this->mark_failed();
|
||||||
|
this->status_set_error("Could not create socket");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto err = this->listen_socket_->setblocking(false);
|
||||||
|
if (err < 0) {
|
||||||
|
ESP_LOGE(TAG, "Unable to set nonblocking: errno %d", errno);
|
||||||
|
this->mark_failed();
|
||||||
|
this->status_set_error("Unable to set nonblocking");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int enable = 1;
|
||||||
|
err = this->listen_socket_->setsockopt(SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable));
|
||||||
|
if (err != 0) {
|
||||||
|
this->status_set_warning("Socket unable to set reuseaddr");
|
||||||
|
// we can still continue
|
||||||
|
}
|
||||||
|
struct sockaddr_in server {};
|
||||||
|
|
||||||
|
socklen_t sl = socket::set_sockaddr_any((struct sockaddr *) &server, sizeof(server), this->port_);
|
||||||
|
if (sl == 0) {
|
||||||
|
ESP_LOGE(TAG, "Socket unable to set sockaddr: errno %d", errno);
|
||||||
|
this->mark_failed();
|
||||||
|
this->status_set_error("Unable to set sockaddr");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = this->listen_socket_->bind((struct sockaddr *) &server, sizeof(server));
|
||||||
|
if (err != 0) {
|
||||||
|
ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
|
||||||
|
this->mark_failed();
|
||||||
|
this->status_set_error("Unable to bind socket");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// 8266 and RP2040 `Duino
|
||||||
|
for (const auto &address : this->addresses_) {
|
||||||
|
auto ipaddr = IPAddress();
|
||||||
|
ipaddr.fromString(address.c_str());
|
||||||
|
this->ipaddrs_.push_back(ipaddr);
|
||||||
|
}
|
||||||
|
if (this->should_listen_)
|
||||||
|
this->udp_client_.begin(this->port_);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void UDPComponent::init_data_() {
|
||||||
|
this->data_.clear();
|
||||||
|
if (this->rolling_code_enable_) {
|
||||||
|
add(this->data_, ROLLING_CODE_KEY);
|
||||||
|
add(this->data_, this->rolling_code_[0]);
|
||||||
|
add(this->data_, this->rolling_code_[1]);
|
||||||
|
this->increment_code_();
|
||||||
|
} else {
|
||||||
|
add(this->data_, DATA_KEY);
|
||||||
|
}
|
||||||
|
for (auto pkey : this->ping_keys_) {
|
||||||
|
add(this->data_, PING_KEY);
|
||||||
|
add(this->data_, pkey.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UDPComponent::flush_() {
|
||||||
|
if (!network::is_connected() || this->data_.empty())
|
||||||
|
return;
|
||||||
|
uint32_t buffer[MAX_PACKET_SIZE / 4];
|
||||||
|
memset(buffer, 0, sizeof buffer);
|
||||||
|
// len must be a multiple of 4
|
||||||
|
auto header_len = round4(this->header_.size()) / 4;
|
||||||
|
auto len = round4(data_.size()) / 4;
|
||||||
|
memcpy(buffer, this->header_.data(), this->header_.size());
|
||||||
|
memcpy(buffer + header_len, this->data_.data(), this->data_.size());
|
||||||
|
if (this->is_encrypted_()) {
|
||||||
|
xxtea_encrypt(buffer + header_len, len, (uint32_t *) this->encryption_key_.data());
|
||||||
|
}
|
||||||
|
auto total_len = (header_len + len) * 4;
|
||||||
|
this->send_packet_(buffer, total_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UDPComponent::add_binary_data_(uint8_t key, const char *id, bool data) {
|
||||||
|
auto len = 1 + 1 + 1 + strlen(id);
|
||||||
|
if (len + this->header_.size() + this->data_.size() > MAX_PACKET_SIZE) {
|
||||||
|
this->flush_();
|
||||||
|
}
|
||||||
|
add(this->data_, key);
|
||||||
|
add(this->data_, (uint8_t) data);
|
||||||
|
add(this->data_, id);
|
||||||
|
}
|
||||||
|
void UDPComponent::add_data_(uint8_t key, const char *id, float data) {
|
||||||
|
FuData udata{.f32 = data};
|
||||||
|
this->add_data_(key, id, udata.u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UDPComponent::add_data_(uint8_t key, const char *id, uint32_t data) {
|
||||||
|
auto len = 4 + 1 + 1 + strlen(id);
|
||||||
|
if (len + this->header_.size() + this->data_.size() > MAX_PACKET_SIZE) {
|
||||||
|
this->flush_();
|
||||||
|
}
|
||||||
|
add(this->data_, key);
|
||||||
|
add(this->data_, data);
|
||||||
|
add(this->data_, id);
|
||||||
|
}
|
||||||
|
void UDPComponent::send_data_(bool all) {
|
||||||
|
if (!this->should_send_ || !network::is_connected())
|
||||||
|
return;
|
||||||
|
this->init_data_();
|
||||||
|
#ifdef USE_SENSOR
|
||||||
|
for (auto &sensor : this->sensors_) {
|
||||||
|
if (all || sensor.updated) {
|
||||||
|
sensor.updated = false;
|
||||||
|
this->add_data_(SENSOR_KEY, sensor.id, sensor.sensor->get_state());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef USE_BINARY_SENSOR
|
||||||
|
for (auto &sensor : this->binary_sensors_) {
|
||||||
|
if (all || sensor.updated) {
|
||||||
|
sensor.updated = false;
|
||||||
|
this->add_binary_data_(BINARY_SENSOR_KEY, sensor.id, sensor.sensor->state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
this->flush_();
|
||||||
|
this->updated_ = false;
|
||||||
|
this->resend_data_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UDPComponent::update() {
|
||||||
|
this->updated_ = true;
|
||||||
|
this->resend_data_ = this->should_send_;
|
||||||
|
auto now = millis() / 1000;
|
||||||
|
if (this->last_key_time_ + this->ping_pong_recyle_time_ < now) {
|
||||||
|
this->resend_ping_key_ = this->ping_pong_enable_;
|
||||||
|
this->last_key_time_ = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UDPComponent::loop() {
|
||||||
|
uint8_t buf[MAX_PACKET_SIZE];
|
||||||
|
if (this->should_listen_) {
|
||||||
|
for (;;) {
|
||||||
|
#if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS)
|
||||||
|
auto len = this->listen_socket_->read(buf, sizeof(buf));
|
||||||
|
#else
|
||||||
|
auto len = this->udp_client_.parsePacket();
|
||||||
|
if (len > 0)
|
||||||
|
len = this->udp_client_.read(buf, sizeof(buf));
|
||||||
|
#endif
|
||||||
|
if (len > 0) {
|
||||||
|
this->process_(buf, len);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this->resend_ping_key_)
|
||||||
|
this->send_ping_pong_request_();
|
||||||
|
if (this->updated_) {
|
||||||
|
this->send_data_(this->resend_data_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UDPComponent::add_key_(const char *name, uint32_t key) {
|
||||||
|
if (!this->is_encrypted_())
|
||||||
|
return;
|
||||||
|
if (this->ping_keys_.count(name) == 0 && this->ping_keys_.size() == MAX_PING_KEYS) {
|
||||||
|
ESP_LOGW(TAG, "Ping key from %s discarded", name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->ping_keys_[name] = key;
|
||||||
|
this->resend_data_ = true;
|
||||||
|
ESP_LOGV(TAG, "Ping key from %s now %X", name, (unsigned) key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UDPComponent::process_ping_request_(const char *name, uint8_t *ptr, size_t len) {
|
||||||
|
if (len != 4) {
|
||||||
|
ESP_LOGW(TAG, "Bad ping request");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto key = get_uint32(ptr);
|
||||||
|
this->add_key_(name, key);
|
||||||
|
ESP_LOGV(TAG, "Updated ping key for %s to %08X", name, (unsigned) key);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool process_rolling_code(Provider &provider, uint8_t *&buf, const uint8_t *end) {
|
||||||
|
if (end - buf < 8)
|
||||||
|
return false;
|
||||||
|
auto code0 = get_uint32(buf);
|
||||||
|
auto code1 = get_uint32(buf);
|
||||||
|
if (code1 < provider.last_code[1] || (code1 == provider.last_code[1] && code0 <= provider.last_code[0])) {
|
||||||
|
ESP_LOGW(TAG, "Rolling code for %s %08lX:%08lX is old", provider.name, (unsigned long) code1,
|
||||||
|
(unsigned long) code0);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
provider.last_code[0] = code0;
|
||||||
|
provider.last_code[1] = code1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process a received packet
|
||||||
|
*/
|
||||||
|
void UDPComponent::process_(uint8_t *buf, const size_t len) {
|
||||||
|
auto ping_key_seen = !this->ping_pong_enable_;
|
||||||
|
if (len < 8) {
|
||||||
|
return ESP_LOGV(TAG, "Bad length %zu", len);
|
||||||
|
}
|
||||||
|
char namebuf[256]{};
|
||||||
|
uint8_t byte;
|
||||||
|
uint8_t *start_ptr = buf;
|
||||||
|
const uint8_t *end = buf + len;
|
||||||
|
FuData rdata{};
|
||||||
|
auto magic = get_uint16(buf);
|
||||||
|
if (magic != MAGIC_NUMBER && magic != MAGIC_PING)
|
||||||
|
return ESP_LOGV(TAG, "Bad magic %X", magic);
|
||||||
|
|
||||||
|
auto hlen = *buf++;
|
||||||
|
if (hlen > len - 3) {
|
||||||
|
return ESP_LOGV(TAG, "Bad hostname length %u > %zu", hlen, len - 3);
|
||||||
|
}
|
||||||
|
memcpy(namebuf, buf, hlen);
|
||||||
|
if (strcmp(this->name_, namebuf) == 0) {
|
||||||
|
return ESP_LOGV(TAG, "Ignoring our own data");
|
||||||
|
}
|
||||||
|
buf += hlen;
|
||||||
|
if (magic == MAGIC_PING)
|
||||||
|
return this->process_ping_request_(namebuf, buf, end - buf);
|
||||||
|
if (round4(len) != len) {
|
||||||
|
return ESP_LOGW(TAG, "Bad length %zu", len);
|
||||||
|
}
|
||||||
|
hlen = round4(hlen + 3);
|
||||||
|
buf = start_ptr + hlen;
|
||||||
|
if (buf == end) {
|
||||||
|
return ESP_LOGV(TAG, "No data after header");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->providers_.count(namebuf) == 0) {
|
||||||
|
return ESP_LOGVV(TAG, "Unknown hostname %s", namebuf);
|
||||||
|
}
|
||||||
|
auto &provider = this->providers_[namebuf];
|
||||||
|
// if encryption not used with this host, ping check is pointless since it would be easily spoofed.
|
||||||
|
if (provider.encryption_key.empty())
|
||||||
|
ping_key_seen = true;
|
||||||
|
|
||||||
|
ESP_LOGV(TAG, "Found hostname %s", namebuf);
|
||||||
|
#ifdef USE_SENSOR
|
||||||
|
auto &sensors = this->remote_sensors_[namebuf];
|
||||||
|
#endif
|
||||||
|
#ifdef USE_BINARY_SENSOR
|
||||||
|
auto &binary_sensors = this->remote_binary_sensors_[namebuf];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!provider.encryption_key.empty()) {
|
||||||
|
xxtea_decrypt((uint32_t *) buf, (end - buf) / 4, (uint32_t *) provider.encryption_key.data());
|
||||||
|
}
|
||||||
|
byte = *buf++;
|
||||||
|
if (byte == ROLLING_CODE_KEY) {
|
||||||
|
if (!process_rolling_code(provider, buf, end))
|
||||||
|
return;
|
||||||
|
} else if (byte != DATA_KEY) {
|
||||||
|
return ESP_LOGV(TAG, "Expected rolling_key or data_key, got %X", byte);
|
||||||
|
}
|
||||||
|
while (buf < end) {
|
||||||
|
byte = *buf++;
|
||||||
|
if (byte == ZERO_FILL_KEY)
|
||||||
|
continue;
|
||||||
|
if (byte == PING_KEY) {
|
||||||
|
if (end - buf < 4) {
|
||||||
|
return ESP_LOGV(TAG, "PING_KEY requires 4 more bytes");
|
||||||
|
}
|
||||||
|
auto key = get_uint32(buf);
|
||||||
|
if (key == this->ping_key_) {
|
||||||
|
ping_key_seen = true;
|
||||||
|
ESP_LOGV(TAG, "Found good ping key %X", (unsigned) key);
|
||||||
|
} else {
|
||||||
|
ESP_LOGV(TAG, "Unknown ping key %X", (unsigned) key);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!ping_key_seen) {
|
||||||
|
ESP_LOGW(TAG, "Ping key not seen");
|
||||||
|
this->resend_ping_key_ = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (byte == BINARY_SENSOR_KEY) {
|
||||||
|
if (end - buf < 3) {
|
||||||
|
return ESP_LOGV(TAG, "Binary sensor key requires at least 3 more bytes");
|
||||||
|
}
|
||||||
|
rdata.u32 = *buf++;
|
||||||
|
} else if (byte == SENSOR_KEY) {
|
||||||
|
if (end - buf < 6) {
|
||||||
|
return ESP_LOGV(TAG, "Sensor key requires at least 6 more bytes");
|
||||||
|
}
|
||||||
|
rdata.u32 = get_uint32(buf);
|
||||||
|
} else {
|
||||||
|
return ESP_LOGW(TAG, "Unknown key byte %X", byte);
|
||||||
|
}
|
||||||
|
|
||||||
|
hlen = *buf++;
|
||||||
|
if (end - buf < hlen) {
|
||||||
|
return ESP_LOGV(TAG, "Name length of %u not available", hlen);
|
||||||
|
}
|
||||||
|
memset(namebuf, 0, sizeof namebuf);
|
||||||
|
memcpy(namebuf, buf, hlen);
|
||||||
|
ESP_LOGV(TAG, "Found sensor key %d, id %s, data %lX", byte, namebuf, (unsigned long) rdata.u32);
|
||||||
|
buf += hlen;
|
||||||
|
#ifdef USE_SENSOR
|
||||||
|
if (byte == SENSOR_KEY && sensors.count(namebuf) != 0)
|
||||||
|
sensors[namebuf]->publish_state(rdata.f32);
|
||||||
|
#endif
|
||||||
|
#ifdef USE_BINARY_SENSOR
|
||||||
|
if (byte == BINARY_SENSOR_KEY && binary_sensors.count(namebuf) != 0)
|
||||||
|
binary_sensors[namebuf]->publish_state(rdata.u32 != 0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UDPComponent::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG, "UDP:");
|
||||||
|
ESP_LOGCONFIG(TAG, " Port: %u", this->port_);
|
||||||
|
ESP_LOGCONFIG(TAG, " Encrypted: %s", YESNO(this->is_encrypted_()));
|
||||||
|
ESP_LOGCONFIG(TAG, " Ping-pong: %s", YESNO(this->ping_pong_enable_));
|
||||||
|
for (const auto &address : this->addresses_)
|
||||||
|
ESP_LOGCONFIG(TAG, " Address: %s", address.c_str());
|
||||||
|
#ifdef USE_SENSOR
|
||||||
|
for (auto sensor : this->sensors_)
|
||||||
|
ESP_LOGCONFIG(TAG, " Sensor: %s", sensor.id);
|
||||||
|
#endif
|
||||||
|
#ifdef USE_BINARY_SENSOR
|
||||||
|
for (auto sensor : this->binary_sensors_)
|
||||||
|
ESP_LOGCONFIG(TAG, " Binary Sensor: %s", sensor.id);
|
||||||
|
#endif
|
||||||
|
for (const auto &host : this->providers_) {
|
||||||
|
ESP_LOGCONFIG(TAG, " Remote host: %s", host.first.c_str());
|
||||||
|
ESP_LOGCONFIG(TAG, " Encrypted: %s", YESNO(!host.second.encryption_key.empty()));
|
||||||
|
#ifdef USE_SENSOR
|
||||||
|
for (const auto &sensor : this->remote_sensors_[host.first.c_str()])
|
||||||
|
ESP_LOGCONFIG(TAG, " Sensor: %s", sensor.first.c_str());
|
||||||
|
#endif
|
||||||
|
#ifdef USE_BINARY_SENSOR
|
||||||
|
for (const auto &sensor : this->remote_binary_sensors_[host.first.c_str()])
|
||||||
|
ESP_LOGCONFIG(TAG, " Binary Sensor: %s", sensor.first.c_str());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void UDPComponent::increment_code_() {
|
||||||
|
if (this->rolling_code_enable_) {
|
||||||
|
if (++this->rolling_code_[0] == 0) {
|
||||||
|
this->rolling_code_[1]++;
|
||||||
|
this->pref_.save(&this->rolling_code_[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void UDPComponent::send_packet_(void *data, size_t len) {
|
||||||
|
#if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS)
|
||||||
|
for (const auto &saddr : this->sockaddrs_) {
|
||||||
|
auto result = this->broadcast_socket_->sendto(data, len, 0, &saddr, sizeof(saddr));
|
||||||
|
if (result < 0)
|
||||||
|
ESP_LOGW(TAG, "sendto() error %d", errno);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
auto iface = IPAddress(0, 0, 0, 0);
|
||||||
|
for (const auto &saddr : this->ipaddrs_) {
|
||||||
|
if (this->udp_client_.beginPacketMulticast(saddr, this->port_, iface, 128) != 0) {
|
||||||
|
this->udp_client_.write((const uint8_t *) data, len);
|
||||||
|
auto result = this->udp_client_.endPacket();
|
||||||
|
if (result == 0)
|
||||||
|
ESP_LOGW(TAG, "udp.write() error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void UDPComponent::send_ping_pong_request_() {
|
||||||
|
if (!this->ping_pong_enable_ || !network::is_connected())
|
||||||
|
return;
|
||||||
|
this->ping_key_ = random_uint32();
|
||||||
|
this->ping_header_.clear();
|
||||||
|
add(this->ping_header_, MAGIC_PING);
|
||||||
|
add(this->ping_header_, this->name_);
|
||||||
|
add(this->ping_header_, this->ping_key_);
|
||||||
|
this->send_packet_(this->ping_header_.data(), this->ping_header_.size());
|
||||||
|
this->resend_ping_key_ = false;
|
||||||
|
ESP_LOGV(TAG, "Sent new ping request %08X", (unsigned) this->ping_key_);
|
||||||
|
}
|
||||||
|
} // namespace udp
|
||||||
|
} // namespace esphome
|
158
esphome/components/udp/udp_component.h
Normal file
158
esphome/components/udp/udp_component.h
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#ifdef USE_SENSOR
|
||||||
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
#endif
|
||||||
|
#ifdef USE_BINARY_SENSOR
|
||||||
|
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||||
|
#endif
|
||||||
|
#if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS)
|
||||||
|
#include "esphome/components/socket/socket.h"
|
||||||
|
#else
|
||||||
|
#include <WiFiUdp.h>
|
||||||
|
#endif
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace udp {
|
||||||
|
|
||||||
|
struct Provider {
|
||||||
|
std::vector<uint8_t> encryption_key;
|
||||||
|
const char *name;
|
||||||
|
uint32_t last_code[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef USE_SENSOR
|
||||||
|
struct Sensor {
|
||||||
|
sensor::Sensor *sensor;
|
||||||
|
const char *id;
|
||||||
|
bool updated;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
#ifdef USE_BINARY_SENSOR
|
||||||
|
struct BinarySensor {
|
||||||
|
binary_sensor::BinarySensor *sensor;
|
||||||
|
const char *id;
|
||||||
|
bool updated;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class UDPComponent : public PollingComponent {
|
||||||
|
public:
|
||||||
|
void setup() override;
|
||||||
|
void loop() override;
|
||||||
|
void update() override;
|
||||||
|
void dump_config() override;
|
||||||
|
|
||||||
|
#ifdef USE_SENSOR
|
||||||
|
void add_sensor(const char *id, sensor::Sensor *sensor) {
|
||||||
|
Sensor st{sensor, id, true};
|
||||||
|
this->sensors_.push_back(st);
|
||||||
|
}
|
||||||
|
void add_remote_sensor(const char *hostname, const char *remote_id, sensor::Sensor *sensor) {
|
||||||
|
this->add_provider(hostname);
|
||||||
|
this->remote_sensors_[hostname][remote_id] = sensor;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef USE_BINARY_SENSOR
|
||||||
|
void add_binary_sensor(const char *id, binary_sensor::BinarySensor *sensor) {
|
||||||
|
BinarySensor st{sensor, id, true};
|
||||||
|
this->binary_sensors_.push_back(st);
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_remote_binary_sensor(const char *hostname, const char *remote_id, binary_sensor::BinarySensor *sensor) {
|
||||||
|
this->add_provider(hostname);
|
||||||
|
this->remote_binary_sensors_[hostname][remote_id] = sensor;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
void add_address(const char *addr) { this->addresses_.emplace_back(addr); }
|
||||||
|
void set_port(uint16_t port) { this->port_ = port; }
|
||||||
|
float get_setup_priority() const override { return setup_priority::AFTER_WIFI; }
|
||||||
|
|
||||||
|
void add_provider(const char *hostname) {
|
||||||
|
if (this->providers_.count(hostname) == 0) {
|
||||||
|
Provider provider;
|
||||||
|
provider.encryption_key = std::vector<uint8_t>{};
|
||||||
|
provider.last_code[0] = 0;
|
||||||
|
provider.last_code[1] = 0;
|
||||||
|
provider.name = hostname;
|
||||||
|
this->providers_[hostname] = provider;
|
||||||
|
#ifdef USE_SENSOR
|
||||||
|
this->remote_sensors_[hostname] = std::map<std::string, sensor::Sensor *>();
|
||||||
|
#endif
|
||||||
|
#ifdef USE_BINARY_SENSOR
|
||||||
|
this->remote_binary_sensors_[hostname] = std::map<std::string, binary_sensor::BinarySensor *>();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_encryption_key(std::vector<uint8_t> key) { this->encryption_key_ = std::move(key); }
|
||||||
|
void set_rolling_code_enable(bool enable) { this->rolling_code_enable_ = enable; }
|
||||||
|
void set_ping_pong_enable(bool enable) { this->ping_pong_enable_ = enable; }
|
||||||
|
void set_ping_pong_recycle_time(uint32_t recycle_time) { this->ping_pong_recyle_time_ = recycle_time; }
|
||||||
|
void set_provider_encryption(const char *name, std::vector<uint8_t> key) {
|
||||||
|
this->providers_[name].encryption_key = std::move(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void send_data_(bool all);
|
||||||
|
void process_(uint8_t *buf, size_t len);
|
||||||
|
void flush_();
|
||||||
|
void add_data_(uint8_t key, const char *id, float data);
|
||||||
|
void add_data_(uint8_t key, const char *id, uint32_t data);
|
||||||
|
void increment_code_();
|
||||||
|
void add_binary_data_(uint8_t key, const char *id, bool data);
|
||||||
|
void init_data_();
|
||||||
|
|
||||||
|
bool updated_{};
|
||||||
|
uint16_t port_{18511};
|
||||||
|
uint32_t ping_key_{};
|
||||||
|
uint32_t rolling_code_[2]{};
|
||||||
|
bool rolling_code_enable_{};
|
||||||
|
bool ping_pong_enable_{};
|
||||||
|
uint32_t ping_pong_recyle_time_{};
|
||||||
|
uint32_t last_key_time_{};
|
||||||
|
bool resend_ping_key_{};
|
||||||
|
bool resend_data_{};
|
||||||
|
bool should_send_{};
|
||||||
|
const char *name_{};
|
||||||
|
bool should_listen_{};
|
||||||
|
ESPPreferenceObject pref_;
|
||||||
|
|
||||||
|
#if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS)
|
||||||
|
std::unique_ptr<socket::Socket> broadcast_socket_ = nullptr;
|
||||||
|
std::unique_ptr<socket::Socket> listen_socket_ = nullptr;
|
||||||
|
std::vector<struct sockaddr> sockaddrs_{};
|
||||||
|
#else
|
||||||
|
std::vector<IPAddress> ipaddrs_{};
|
||||||
|
WiFiUDP udp_client_{};
|
||||||
|
#endif
|
||||||
|
std::vector<uint8_t> encryption_key_{};
|
||||||
|
std::vector<std::string> addresses_{};
|
||||||
|
|
||||||
|
#ifdef USE_SENSOR
|
||||||
|
std::vector<Sensor> sensors_{};
|
||||||
|
std::map<std::string, std::map<std::string, sensor::Sensor *>> remote_sensors_{};
|
||||||
|
#endif
|
||||||
|
#ifdef USE_BINARY_SENSOR
|
||||||
|
std::vector<BinarySensor> binary_sensors_{};
|
||||||
|
std::map<std::string, std::map<std::string, binary_sensor::BinarySensor *>> remote_binary_sensors_{};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::map<std::string, Provider> providers_{};
|
||||||
|
std::vector<uint8_t> ping_header_{};
|
||||||
|
std::vector<uint8_t> header_{};
|
||||||
|
std::vector<uint8_t> data_{};
|
||||||
|
std::map<const char *, uint32_t> ping_keys_{};
|
||||||
|
void add_key_(const char *name, uint32_t key);
|
||||||
|
void send_ping_pong_request_();
|
||||||
|
void send_packet_(void *data, size_t len);
|
||||||
|
void process_ping_request_(const char *name, uint8_t *ptr, size_t len);
|
||||||
|
|
||||||
|
inline bool is_encrypted_() { return !this->encryption_key_.empty(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace udp
|
||||||
|
} // namespace esphome
|
|
@ -1,35 +1,36 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import gzip
|
import gzip
|
||||||
|
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
|
||||||
import esphome.final_validate as fv
|
|
||||||
from esphome.components import web_server_base
|
from esphome.components import web_server_base
|
||||||
from esphome.components.web_server_base import CONF_WEB_SERVER_BASE_ID
|
from esphome.components.web_server_base import CONF_WEB_SERVER_BASE_ID
|
||||||
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
|
CONF_AUTH,
|
||||||
CONF_CSS_INCLUDE,
|
CONF_CSS_INCLUDE,
|
||||||
CONF_CSS_URL,
|
CONF_CSS_URL,
|
||||||
|
CONF_ENABLE_PRIVATE_NETWORK_ACCESS,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
|
CONF_INCLUDE_INTERNAL,
|
||||||
CONF_JS_INCLUDE,
|
CONF_JS_INCLUDE,
|
||||||
CONF_JS_URL,
|
CONF_JS_URL,
|
||||||
CONF_ENABLE_PRIVATE_NETWORK_ACCESS,
|
|
||||||
CONF_PORT,
|
|
||||||
CONF_AUTH,
|
|
||||||
CONF_USERNAME,
|
|
||||||
CONF_PASSWORD,
|
|
||||||
CONF_INCLUDE_INTERNAL,
|
|
||||||
CONF_OTA,
|
|
||||||
CONF_LOG,
|
|
||||||
CONF_VERSION,
|
|
||||||
CONF_LOCAL,
|
CONF_LOCAL,
|
||||||
|
CONF_LOG,
|
||||||
|
CONF_OTA,
|
||||||
|
CONF_PASSWORD,
|
||||||
|
CONF_PORT,
|
||||||
|
CONF_USERNAME,
|
||||||
|
CONF_VERSION,
|
||||||
CONF_WEB_SERVER_ID,
|
CONF_WEB_SERVER_ID,
|
||||||
CONF_WEB_SERVER_SORTING_WEIGHT,
|
CONF_WEB_SERVER_SORTING_WEIGHT,
|
||||||
|
PLATFORM_BK72XX,
|
||||||
PLATFORM_ESP32,
|
PLATFORM_ESP32,
|
||||||
PLATFORM_ESP8266,
|
PLATFORM_ESP8266,
|
||||||
PLATFORM_BK72XX,
|
|
||||||
PLATFORM_RTL87XX,
|
PLATFORM_RTL87XX,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE, coroutine_with_priority
|
from esphome.core import CORE, coroutine_with_priority
|
||||||
|
import esphome.final_validate as fv
|
||||||
|
|
||||||
AUTO_LOAD = ["json", "web_server_base"]
|
AUTO_LOAD = ["json", "web_server_base"]
|
||||||
|
|
||||||
|
@ -208,7 +209,6 @@ async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID], paren)
|
var = cg.new_Pvariable(config[CONF_ID], paren)
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
|
|
||||||
cg.add_define("USE_WEBSERVER")
|
|
||||||
version = config[CONF_VERSION]
|
version = config[CONF_VERSION]
|
||||||
|
|
||||||
cg.add(paren.set_port(config[CONF_PORT]))
|
cg.add(paren.set_port(config[CONF_PORT]))
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "list_entities.h"
|
#include "list_entities.h"
|
||||||
|
#ifdef USE_WEBSERVER
|
||||||
#include "esphome/core/application.h"
|
#include "esphome/core/application.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
#include "esphome/core/util.h"
|
#include "esphome/core/util.h"
|
||||||
|
@ -188,3 +189,4 @@ bool ListEntitiesIterator::on_update(update::UpdateEntity *update) {
|
||||||
|
|
||||||
} // namespace web_server
|
} // namespace web_server
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
#endif
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/defines.h"
|
||||||
|
#ifdef USE_WEBSERVER
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/core/component_iterator.h"
|
#include "esphome/core/component_iterator.h"
|
||||||
#include "esphome/core/defines.h"
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace web_server {
|
namespace web_server {
|
||||||
|
|
||||||
|
@ -78,3 +79,4 @@ class ListEntitiesIterator : public ComponentIterator {
|
||||||
|
|
||||||
} // namespace web_server
|
} // namespace web_server
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
#endif
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#include "web_server.h"
|
#include "web_server.h"
|
||||||
|
#ifdef USE_WEBSERVER
|
||||||
#include "esphome/components/json/json_util.h"
|
#include "esphome/components/json/json_util.h"
|
||||||
#include "esphome/components/network/util.h"
|
#include "esphome/components/network/util.h"
|
||||||
#include "esphome/core/application.h"
|
#include "esphome/core/application.h"
|
||||||
|
@ -1659,3 +1659,4 @@ void WebServer::schedule_(std::function<void()> &&f) {
|
||||||
|
|
||||||
} // namespace web_server
|
} // namespace web_server
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
#endif
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "list_entities.h"
|
#include "list_entities.h"
|
||||||
|
|
||||||
#include "esphome/components/web_server_base/web_server_base.h"
|
#include "esphome/components/web_server_base/web_server_base.h"
|
||||||
|
#ifdef USE_WEBSERVER
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/core/controller.h"
|
#include "esphome/core/controller.h"
|
||||||
#include "esphome/core/entity_base.h"
|
#include "esphome/core/entity_base.h"
|
||||||
|
@ -366,3 +367,4 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
||||||
|
|
||||||
} // namespace web_server
|
} // namespace web_server
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
#endif
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "web_server_base.h"
|
#include "web_server_base.h"
|
||||||
|
#ifdef USE_NETWORK
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
#include "esphome/core/application.h"
|
#include "esphome/core/application.h"
|
||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
|
@ -121,3 +122,4 @@ float WebServerBase::get_setup_priority() const {
|
||||||
|
|
||||||
} // namespace web_server_base
|
} // namespace web_server_base
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
#endif
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "esphome/core/defines.h"
|
||||||
|
#ifdef USE_NETWORK
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -145,3 +146,4 @@ class OTARequestHandler : public AsyncWebHandler {
|
||||||
|
|
||||||
} // namespace web_server_base
|
} // namespace web_server_base
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
#endif
|
||||||
|
|
|
@ -732,6 +732,7 @@ CONF_RW_PIN = "rw_pin"
|
||||||
CONF_RX_BUFFER_SIZE = "rx_buffer_size"
|
CONF_RX_BUFFER_SIZE = "rx_buffer_size"
|
||||||
CONF_RX_ONLY = "rx_only"
|
CONF_RX_ONLY = "rx_only"
|
||||||
CONF_RX_PIN = "rx_pin"
|
CONF_RX_PIN = "rx_pin"
|
||||||
|
CONF_RX_QUEUE_LEN = "rx_queue_len"
|
||||||
CONF_SAFE_MODE = "safe_mode"
|
CONF_SAFE_MODE = "safe_mode"
|
||||||
CONF_SAMPLE_RATE = "sample_rate"
|
CONF_SAMPLE_RATE = "sample_rate"
|
||||||
CONF_SAMSUNG = "samsung"
|
CONF_SAMSUNG = "samsung"
|
||||||
|
@ -883,6 +884,7 @@ CONF_TVOC = "tvoc"
|
||||||
CONF_TX_BUFFER_SIZE = "tx_buffer_size"
|
CONF_TX_BUFFER_SIZE = "tx_buffer_size"
|
||||||
CONF_TX_PIN = "tx_pin"
|
CONF_TX_PIN = "tx_pin"
|
||||||
CONF_TX_POWER = "tx_power"
|
CONF_TX_POWER = "tx_power"
|
||||||
|
CONF_TX_QUEUE_LEN = "tx_queue_len"
|
||||||
CONF_TYPE = "type"
|
CONF_TYPE = "type"
|
||||||
CONF_TYPE_ID = "type_id"
|
CONF_TYPE_ID = "type_id"
|
||||||
CONF_UART_ID = "uart_id"
|
CONF_UART_ID = "uart_id"
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
#include "bytebuffer.h"
|
#include "bytebuffer.h"
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstring>
|
#include "esphome/core/helpers.h"
|
||||||
|
|
||||||
|
#include <list>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
|
|
||||||
|
@ -110,18 +113,13 @@ uint32_t ByteBuffer::get_int24() {
|
||||||
}
|
}
|
||||||
float ByteBuffer::get_float() {
|
float ByteBuffer::get_float() {
|
||||||
assert(this->get_remaining() >= sizeof(float));
|
assert(this->get_remaining() >= sizeof(float));
|
||||||
auto ui_value = this->get_uint32();
|
return bit_cast<float>(this->get_uint32());
|
||||||
float value;
|
|
||||||
memcpy(&value, &ui_value, sizeof(float));
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
double ByteBuffer::get_double() {
|
double ByteBuffer::get_double() {
|
||||||
assert(this->get_remaining() >= sizeof(double));
|
assert(this->get_remaining() >= sizeof(double));
|
||||||
auto ui_value = this->get_uint64();
|
return bit_cast<double>(this->get_uint64());
|
||||||
double value;
|
|
||||||
memcpy(&value, &ui_value, sizeof(double));
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> ByteBuffer::get_vector(size_t length) {
|
std::vector<uint8_t> ByteBuffer::get_vector(size_t length) {
|
||||||
assert(this->get_remaining() >= length);
|
assert(this->get_remaining() >= length);
|
||||||
auto start = this->data_.begin() + this->position_;
|
auto start = this->data_.begin() + this->position_;
|
||||||
|
@ -154,16 +152,12 @@ void ByteBuffer::put_uint(uint64_t value, size_t length) {
|
||||||
void ByteBuffer::put_float(float value) {
|
void ByteBuffer::put_float(float value) {
|
||||||
static_assert(sizeof(float) == sizeof(uint32_t), "Float sizes other than 32 bit not supported");
|
static_assert(sizeof(float) == sizeof(uint32_t), "Float sizes other than 32 bit not supported");
|
||||||
assert(this->get_remaining() >= sizeof(float));
|
assert(this->get_remaining() >= sizeof(float));
|
||||||
uint32_t ui_value;
|
this->put_uint32(bit_cast<uint32_t>(value));
|
||||||
memcpy(&ui_value, &value, sizeof(float)); // this work-around required to silence compiler warnings
|
|
||||||
this->put_uint32(ui_value);
|
|
||||||
}
|
}
|
||||||
void ByteBuffer::put_double(double value) {
|
void ByteBuffer::put_double(double value) {
|
||||||
static_assert(sizeof(double) == sizeof(uint64_t), "Double sizes other than 64 bit not supported");
|
static_assert(sizeof(double) == sizeof(uint64_t), "Double sizes other than 64 bit not supported");
|
||||||
assert(this->get_remaining() >= sizeof(double));
|
assert(this->get_remaining() >= sizeof(double));
|
||||||
uint64_t ui_value;
|
this->put_uint64(bit_cast<uint64_t>(value));
|
||||||
memcpy(&ui_value, &value, sizeof(double));
|
|
||||||
this->put_uint64(ui_value);
|
|
||||||
}
|
}
|
||||||
void ByteBuffer::put_vector(const std::vector<uint8_t> &value) {
|
void ByteBuffer::put_vector(const std::vector<uint8_t> &value) {
|
||||||
assert(this->get_remaining() >= value.size());
|
assert(this->get_remaining() >= value.size());
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
#define USE_DATETIME_DATETIME
|
#define USE_DATETIME_DATETIME
|
||||||
#define USE_DATETIME_TIME
|
#define USE_DATETIME_TIME
|
||||||
#define USE_DEEP_SLEEP
|
#define USE_DEEP_SLEEP
|
||||||
|
#define USE_DISPLAY
|
||||||
#define USE_EVENT
|
#define USE_EVENT
|
||||||
#define USE_FAN
|
#define USE_FAN
|
||||||
#define USE_GRAPH
|
#define USE_GRAPH
|
||||||
|
|
|
@ -48,6 +48,8 @@ class StorageJSON:
|
||||||
firmware_bin_path: str,
|
firmware_bin_path: str,
|
||||||
loaded_integrations: set[str],
|
loaded_integrations: set[str],
|
||||||
no_mdns: bool,
|
no_mdns: bool,
|
||||||
|
framework: str | None = None,
|
||||||
|
core_platform: str | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
# Version of the storage JSON schema
|
# Version of the storage JSON schema
|
||||||
assert storage_version is None or isinstance(storage_version, int)
|
assert storage_version is None or isinstance(storage_version, int)
|
||||||
|
@ -78,6 +80,10 @@ class StorageJSON:
|
||||||
self.loaded_integrations = loaded_integrations
|
self.loaded_integrations = loaded_integrations
|
||||||
# Is mDNS disabled
|
# Is mDNS disabled
|
||||||
self.no_mdns = no_mdns
|
self.no_mdns = no_mdns
|
||||||
|
# The framework used to compile the firmware
|
||||||
|
self.framework = framework
|
||||||
|
# The core platform of this firmware. Like "esp32", "rp2040", "host" etc.
|
||||||
|
self.core_platform = core_platform
|
||||||
|
|
||||||
def as_dict(self):
|
def as_dict(self):
|
||||||
return {
|
return {
|
||||||
|
@ -94,6 +100,8 @@ class StorageJSON:
|
||||||
"firmware_bin_path": self.firmware_bin_path,
|
"firmware_bin_path": self.firmware_bin_path,
|
||||||
"loaded_integrations": sorted(self.loaded_integrations),
|
"loaded_integrations": sorted(self.loaded_integrations),
|
||||||
"no_mdns": self.no_mdns,
|
"no_mdns": self.no_mdns,
|
||||||
|
"framework": self.framework,
|
||||||
|
"core_platform": self.core_platform,
|
||||||
}
|
}
|
||||||
|
|
||||||
def to_json(self):
|
def to_json(self):
|
||||||
|
@ -127,6 +135,8 @@ class StorageJSON:
|
||||||
and CONF_DISABLED in esph.config[CONF_MDNS]
|
and CONF_DISABLED in esph.config[CONF_MDNS]
|
||||||
and esph.config[CONF_MDNS][CONF_DISABLED] is True
|
and esph.config[CONF_MDNS][CONF_DISABLED] is True
|
||||||
),
|
),
|
||||||
|
framework=esph.target_framework,
|
||||||
|
core_platform=esph.target_platform,
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -147,6 +157,8 @@ class StorageJSON:
|
||||||
firmware_bin_path=None,
|
firmware_bin_path=None,
|
||||||
loaded_integrations=set(),
|
loaded_integrations=set(),
|
||||||
no_mdns=False,
|
no_mdns=False,
|
||||||
|
framework=None,
|
||||||
|
core_platform=platform.lower(),
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -168,6 +180,8 @@ class StorageJSON:
|
||||||
firmware_bin_path = storage.get("firmware_bin_path")
|
firmware_bin_path = storage.get("firmware_bin_path")
|
||||||
loaded_integrations = set(storage.get("loaded_integrations", []))
|
loaded_integrations = set(storage.get("loaded_integrations", []))
|
||||||
no_mdns = storage.get("no_mdns", False)
|
no_mdns = storage.get("no_mdns", False)
|
||||||
|
framework = storage.get("framework")
|
||||||
|
core_platform = storage.get("core_platform")
|
||||||
return StorageJSON(
|
return StorageJSON(
|
||||||
storage_version,
|
storage_version,
|
||||||
name,
|
name,
|
||||||
|
@ -182,6 +196,8 @@ class StorageJSON:
|
||||||
firmware_bin_path,
|
firmware_bin_path,
|
||||||
loaded_integrations,
|
loaded_integrations,
|
||||||
no_mdns,
|
no_mdns,
|
||||||
|
framework,
|
||||||
|
core_platform,
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
|
@ -9,6 +9,7 @@ from esphome.config import iter_component_configs, iter_components
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
ENV_NOGITIGNORE,
|
ENV_NOGITIGNORE,
|
||||||
HEADER_FILE_EXTENSIONS,
|
HEADER_FILE_EXTENSIONS,
|
||||||
|
PLATFORM_ESP32,
|
||||||
SOURCE_FILE_EXTENSIONS,
|
SOURCE_FILE_EXTENSIONS,
|
||||||
__version__,
|
__version__,
|
||||||
)
|
)
|
||||||
|
@ -107,7 +108,10 @@ def storage_should_clean(old: StorageJSON, new: StorageJSON) -> bool:
|
||||||
if old.build_path != new.build_path:
|
if old.build_path != new.build_path:
|
||||||
return True
|
return True
|
||||||
if old.loaded_integrations != new.loaded_integrations:
|
if old.loaded_integrations != new.loaded_integrations:
|
||||||
return True
|
if new.core_platform == PLATFORM_ESP32:
|
||||||
|
from esphome.components.esp32 import FRAMEWORK_ESP_IDF
|
||||||
|
|
||||||
|
return new.framework == FRAMEWORK_ESP_IDF
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ display:
|
||||||
cs_pin: 12
|
cs_pin: 12
|
||||||
dc_pin: 13
|
dc_pin: 13
|
||||||
reset_pin: 21
|
reset_pin: 21
|
||||||
|
invert_colors: false
|
||||||
|
|
||||||
# Purposely test that `animation:` does auto-load `image:`
|
# Purposely test that `animation:` does auto-load `image:`
|
||||||
# Keep the `image:` undefined.
|
# Keep the `image:` undefined.
|
||||||
|
|
|
@ -11,6 +11,7 @@ display:
|
||||||
cs_pin: 8
|
cs_pin: 8
|
||||||
dc_pin: 9
|
dc_pin: 9
|
||||||
reset_pin: 10
|
reset_pin: 10
|
||||||
|
invert_colors: false
|
||||||
|
|
||||||
# Purposely test that `animation:` does auto-load `image:`
|
# Purposely test that `animation:` does auto-load `image:`
|
||||||
# Keep the `image:` undefined.
|
# Keep the `image:` undefined.
|
||||||
|
|
|
@ -11,6 +11,7 @@ display:
|
||||||
cs_pin: 8
|
cs_pin: 8
|
||||||
dc_pin: 9
|
dc_pin: 9
|
||||||
reset_pin: 10
|
reset_pin: 10
|
||||||
|
invert_colors: false
|
||||||
|
|
||||||
# Purposely test that `animation:` does auto-load `image:`
|
# Purposely test that `animation:` does auto-load `image:`
|
||||||
# Keep the `image:` undefined.
|
# Keep the `image:` undefined.
|
||||||
|
|
|
@ -11,6 +11,7 @@ display:
|
||||||
cs_pin: 12
|
cs_pin: 12
|
||||||
dc_pin: 13
|
dc_pin: 13
|
||||||
reset_pin: 21
|
reset_pin: 21
|
||||||
|
invert_colors: false
|
||||||
|
|
||||||
# Purposely test that `animation:` does auto-load `image:`
|
# Purposely test that `animation:` does auto-load `image:`
|
||||||
# Keep the `image:` undefined.
|
# Keep the `image:` undefined.
|
||||||
|
|
|
@ -11,6 +11,7 @@ display:
|
||||||
cs_pin: 5
|
cs_pin: 5
|
||||||
dc_pin: 15
|
dc_pin: 15
|
||||||
reset_pin: 16
|
reset_pin: 16
|
||||||
|
invert_colors: false
|
||||||
|
|
||||||
# Purposely test that `animation:` does auto-load `image:`
|
# Purposely test that `animation:` does auto-load `image:`
|
||||||
# Keep the `image:` undefined.
|
# Keep the `image:` undefined.
|
||||||
|
|
|
@ -11,6 +11,7 @@ display:
|
||||||
cs_pin: 20
|
cs_pin: 20
|
||||||
dc_pin: 21
|
dc_pin: 21
|
||||||
reset_pin: 22
|
reset_pin: 22
|
||||||
|
invert_colors: false
|
||||||
|
|
||||||
# Purposely test that `animation:` does auto-load `image:`
|
# Purposely test that `animation:` does auto-load `image:`
|
||||||
# Keep the `image:` undefined.
|
# Keep the `image:` undefined.
|
||||||
|
|
20
tests/components/ch422g/common.yaml
Normal file
20
tests/components/ch422g/common.yaml
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
ch422g:
|
||||||
|
- id: ch422g_hub
|
||||||
|
address: 0x24
|
||||||
|
|
||||||
|
binary_sensor:
|
||||||
|
- platform: gpio
|
||||||
|
id: ch422g_input
|
||||||
|
name: CH422G Binary Sensor
|
||||||
|
pin:
|
||||||
|
ch422g: ch422g_hub
|
||||||
|
number: 1
|
||||||
|
mode: INPUT
|
||||||
|
inverted: true
|
||||||
|
- platform: gpio
|
||||||
|
id: ch422g_output
|
||||||
|
pin:
|
||||||
|
ch422g: ch422g_hub
|
||||||
|
number: 0
|
||||||
|
mode: OUTPUT
|
||||||
|
inverted: false
|
6
tests/components/ch422g/test.esp32-ard.yaml
Normal file
6
tests/components/ch422g/test.esp32-ard.yaml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
i2c:
|
||||||
|
- id: i2c_ch422g
|
||||||
|
scl: 16
|
||||||
|
sda: 17
|
||||||
|
|
||||||
|
<<: !include common.yaml
|
6
tests/components/ch422g/test.esp32-c3-ard.yaml
Normal file
6
tests/components/ch422g/test.esp32-c3-ard.yaml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
i2c:
|
||||||
|
- id: i2c_ch422g
|
||||||
|
scl: 5
|
||||||
|
sda: 4
|
||||||
|
|
||||||
|
<<: !include common.yaml
|
6
tests/components/ch422g/test.esp32-c3-idf.yaml
Normal file
6
tests/components/ch422g/test.esp32-c3-idf.yaml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
i2c:
|
||||||
|
- id: i2c_ch422g
|
||||||
|
scl: 5
|
||||||
|
sda: 4
|
||||||
|
|
||||||
|
<<: !include common.yaml
|
6
tests/components/ch422g/test.esp32-idf.yaml
Normal file
6
tests/components/ch422g/test.esp32-idf.yaml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
i2c:
|
||||||
|
- id: i2c_ch422g
|
||||||
|
scl: 16
|
||||||
|
sda: 17
|
||||||
|
|
||||||
|
<<: !include common.yaml
|
6
tests/components/ch422g/test.esp8266-ard.yaml
Normal file
6
tests/components/ch422g/test.esp8266-ard.yaml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
i2c:
|
||||||
|
- id: i2c_ch422g
|
||||||
|
scl: 5
|
||||||
|
sda: 4
|
||||||
|
|
||||||
|
<<: !include common.yaml
|
6
tests/components/ch422g/test.rp2040-ard.yaml
Normal file
6
tests/components/ch422g/test.rp2040-ard.yaml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
i2c:
|
||||||
|
- id: i2c_ch422g
|
||||||
|
scl: 5
|
||||||
|
sda: 4
|
||||||
|
|
||||||
|
<<: !include common.yaml
|
|
@ -12,6 +12,7 @@ display:
|
||||||
dc_pin: GPIO4
|
dc_pin: GPIO4
|
||||||
reset_pin:
|
reset_pin:
|
||||||
number: GPIO21
|
number: GPIO21
|
||||||
|
invert_colors: false
|
||||||
|
|
||||||
i2c:
|
i2c:
|
||||||
scl: GPIO18
|
scl: GPIO18
|
||||||
|
|
|
@ -26,6 +26,7 @@ display:
|
||||||
mirror_x: true
|
mirror_x: true
|
||||||
mirror_y: true
|
mirror_y: true
|
||||||
auto_clear_enabled: false
|
auto_clear_enabled: false
|
||||||
|
invert_colors: false
|
||||||
|
|
||||||
spi:
|
spi:
|
||||||
clk_pin: 14
|
clk_pin: 14
|
||||||
|
|
|
@ -11,6 +11,7 @@ display:
|
||||||
cs_pin: 12
|
cs_pin: 12
|
||||||
dc_pin: 13
|
dc_pin: 13
|
||||||
reset_pin: 21
|
reset_pin: 21
|
||||||
|
invert_colors: false
|
||||||
lambda: |-
|
lambda: |-
|
||||||
// Draw an analog clock in the center of the screen
|
// Draw an analog clock in the center of the screen
|
||||||
int centerX = it.get_width() / 2;
|
int centerX = it.get_width() / 2;
|
||||||
|
|
23
tests/components/font/test.host.yaml
Normal file
23
tests/components/font/test.host.yaml
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
font:
|
||||||
|
- file: "gfonts://Roboto"
|
||||||
|
id: roboto
|
||||||
|
size: 20
|
||||||
|
glyphs: "0123456789."
|
||||||
|
extras:
|
||||||
|
- file: "gfonts://Roboto"
|
||||||
|
glyphs: ["\u00C4", "\u00C5", "\U000000C7"]
|
||||||
|
- file: "gfonts://Roboto"
|
||||||
|
id: roboto_web
|
||||||
|
size: 20
|
||||||
|
- file: "https://github.com/IdreesInc/Monocraft/releases/download/v3.0/Monocraft.ttf"
|
||||||
|
id: monocraft
|
||||||
|
size: 20
|
||||||
|
- file:
|
||||||
|
type: web
|
||||||
|
url: "https://github.com/IdreesInc/Monocraft/releases/download/v3.0/Monocraft.ttf"
|
||||||
|
id: monocraft2
|
||||||
|
size: 24
|
||||||
|
- file: $component_dir/Monocraft.ttf
|
||||||
|
id: monocraft3
|
||||||
|
size: 28
|
||||||
|
|
|
@ -19,6 +19,7 @@ display:
|
||||||
mirror_x: true
|
mirror_x: true
|
||||||
mirror_y: true
|
mirror_y: true
|
||||||
auto_clear_enabled: false
|
auto_clear_enabled: false
|
||||||
|
invert_colors: false
|
||||||
|
|
||||||
touchscreen:
|
touchscreen:
|
||||||
- platform: ft63x6
|
- platform: ft63x6
|
||||||
|
|
25
tests/components/gt911/common.yaml
Normal file
25
tests/components/gt911/common.yaml
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
i2c:
|
||||||
|
- id: i2c_gt911
|
||||||
|
scl: 5
|
||||||
|
sda: 4
|
||||||
|
|
||||||
|
display:
|
||||||
|
- platform: ssd1306_i2c
|
||||||
|
id: ssd1306_display
|
||||||
|
model: SSD1306_128X64
|
||||||
|
reset_pin: 10
|
||||||
|
pages:
|
||||||
|
- id: page1
|
||||||
|
lambda: |-
|
||||||
|
it.rectangle(0, 0, it.get_width(), it.get_height());
|
||||||
|
|
||||||
|
touchscreen:
|
||||||
|
- platform: gt911
|
||||||
|
display: ssd1306_display
|
||||||
|
interrupt_pin: 20
|
||||||
|
reset_pin: 21
|
||||||
|
|
||||||
|
binary_sensor:
|
||||||
|
- platform: gt911
|
||||||
|
id: touch_key_911
|
||||||
|
index: 0
|
|
@ -1,24 +1 @@
|
||||||
i2c:
|
<<: !include common.yaml
|
||||||
- id: i2c_gt911
|
|
||||||
scl: 16
|
|
||||||
sda: 17
|
|
||||||
|
|
||||||
display:
|
|
||||||
- platform: ssd1306_i2c
|
|
||||||
id: ssd1306_display
|
|
||||||
model: SSD1306_128X64
|
|
||||||
reset_pin: 13
|
|
||||||
pages:
|
|
||||||
- id: page1
|
|
||||||
lambda: |-
|
|
||||||
it.rectangle(0, 0, it.get_width(), it.get_height());
|
|
||||||
|
|
||||||
touchscreen:
|
|
||||||
- platform: gt911
|
|
||||||
display: ssd1306_display
|
|
||||||
interrupt_pin: 14
|
|
||||||
|
|
||||||
binary_sensor:
|
|
||||||
- platform: gt911
|
|
||||||
id: touch_key_911
|
|
||||||
index: 0
|
|
||||||
|
|
|
@ -1,24 +1 @@
|
||||||
i2c:
|
<<: !include common.yaml
|
||||||
- id: i2c_gt911
|
|
||||||
scl: 5
|
|
||||||
sda: 4
|
|
||||||
|
|
||||||
display:
|
|
||||||
- platform: ssd1306_i2c
|
|
||||||
id: ssd1306_display
|
|
||||||
model: SSD1306_128X64
|
|
||||||
reset_pin: 3
|
|
||||||
pages:
|
|
||||||
- id: page1
|
|
||||||
lambda: |-
|
|
||||||
it.rectangle(0, 0, it.get_width(), it.get_height());
|
|
||||||
|
|
||||||
touchscreen:
|
|
||||||
- platform: gt911
|
|
||||||
display: ssd1306_display
|
|
||||||
interrupt_pin: 6
|
|
||||||
|
|
||||||
binary_sensor:
|
|
||||||
- platform: gt911
|
|
||||||
id: touch_key_911
|
|
||||||
index: 0
|
|
||||||
|
|
|
@ -1,24 +1 @@
|
||||||
i2c:
|
<<: !include common.yaml
|
||||||
- id: i2c_gt911
|
|
||||||
scl: 5
|
|
||||||
sda: 4
|
|
||||||
|
|
||||||
display:
|
|
||||||
- platform: ssd1306_i2c
|
|
||||||
id: ssd1306_display
|
|
||||||
model: SSD1306_128X64
|
|
||||||
reset_pin: 3
|
|
||||||
pages:
|
|
||||||
- id: page1
|
|
||||||
lambda: |-
|
|
||||||
it.rectangle(0, 0, it.get_width(), it.get_height());
|
|
||||||
|
|
||||||
touchscreen:
|
|
||||||
- platform: gt911
|
|
||||||
display: ssd1306_display
|
|
||||||
interrupt_pin: 6
|
|
||||||
|
|
||||||
binary_sensor:
|
|
||||||
- platform: gt911
|
|
||||||
id: touch_key_911
|
|
||||||
index: 0
|
|
||||||
|
|
|
@ -1,24 +1 @@
|
||||||
i2c:
|
<<: !include common.yaml
|
||||||
- id: i2c_gt911
|
|
||||||
scl: 16
|
|
||||||
sda: 17
|
|
||||||
|
|
||||||
display:
|
|
||||||
- platform: ssd1306_i2c
|
|
||||||
id: ssd1306_display
|
|
||||||
model: SSD1306_128X64
|
|
||||||
reset_pin: 13
|
|
||||||
pages:
|
|
||||||
- id: page1
|
|
||||||
lambda: |-
|
|
||||||
it.rectangle(0, 0, it.get_width(), it.get_height());
|
|
||||||
|
|
||||||
touchscreen:
|
|
||||||
- platform: gt911
|
|
||||||
display: ssd1306_display
|
|
||||||
interrupt_pin: 14
|
|
||||||
|
|
||||||
binary_sensor:
|
|
||||||
- platform: gt911
|
|
||||||
id: touch_key_911
|
|
||||||
index: 0
|
|
||||||
|
|
|
@ -1,24 +1 @@
|
||||||
i2c:
|
<<: !include common.yaml
|
||||||
- id: i2c_gt911
|
|
||||||
scl: 5
|
|
||||||
sda: 4
|
|
||||||
|
|
||||||
display:
|
|
||||||
- platform: ssd1306_i2c
|
|
||||||
id: ssd1306_display
|
|
||||||
model: SSD1306_128X64
|
|
||||||
reset_pin: 3
|
|
||||||
pages:
|
|
||||||
- id: page1
|
|
||||||
lambda: |-
|
|
||||||
it.rectangle(0, 0, it.get_width(), it.get_height());
|
|
||||||
|
|
||||||
touchscreen:
|
|
||||||
- platform: gt911
|
|
||||||
display: ssd1306_display
|
|
||||||
interrupt_pin: 6
|
|
||||||
|
|
||||||
binary_sensor:
|
|
||||||
- platform: gt911
|
|
||||||
id: touch_key_911
|
|
||||||
index: 0
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ display:
|
||||||
cs_pin: 12
|
cs_pin: 12
|
||||||
dc_pin: 13
|
dc_pin: 13
|
||||||
reset_pin: 21
|
reset_pin: 21
|
||||||
|
invert_colors: true
|
||||||
|
|
||||||
image:
|
image:
|
||||||
- id: binary_image
|
- id: binary_image
|
||||||
|
|
|
@ -11,6 +11,7 @@ display:
|
||||||
cs_pin: 8
|
cs_pin: 8
|
||||||
dc_pin: 9
|
dc_pin: 9
|
||||||
reset_pin: 10
|
reset_pin: 10
|
||||||
|
invert_colors: true
|
||||||
|
|
||||||
image:
|
image:
|
||||||
- id: binary_image
|
- id: binary_image
|
||||||
|
|
|
@ -11,6 +11,7 @@ display:
|
||||||
cs_pin: 8
|
cs_pin: 8
|
||||||
dc_pin: 9
|
dc_pin: 9
|
||||||
reset_pin: 10
|
reset_pin: 10
|
||||||
|
invert_colors: true
|
||||||
|
|
||||||
image:
|
image:
|
||||||
- id: binary_image
|
- id: binary_image
|
||||||
|
|
|
@ -11,6 +11,7 @@ display:
|
||||||
cs_pin: 12
|
cs_pin: 12
|
||||||
dc_pin: 13
|
dc_pin: 13
|
||||||
reset_pin: 21
|
reset_pin: 21
|
||||||
|
invert_colors: true
|
||||||
|
|
||||||
image:
|
image:
|
||||||
- id: binary_image
|
- id: binary_image
|
||||||
|
|
|
@ -11,6 +11,7 @@ display:
|
||||||
cs_pin: 5
|
cs_pin: 5
|
||||||
dc_pin: 15
|
dc_pin: 15
|
||||||
reset_pin: 16
|
reset_pin: 16
|
||||||
|
invert_colors: true
|
||||||
|
|
||||||
image:
|
image:
|
||||||
- id: binary_image
|
- id: binary_image
|
||||||
|
|
|
@ -11,6 +11,7 @@ display:
|
||||||
cs_pin: 20
|
cs_pin: 20
|
||||||
dc_pin: 21
|
dc_pin: 21
|
||||||
reset_pin: 22
|
reset_pin: 22
|
||||||
|
invert_colors: true
|
||||||
|
|
||||||
image:
|
image:
|
||||||
- id: binary_image
|
- id: binary_image
|
||||||
|
|
|
@ -13,6 +13,7 @@ display:
|
||||||
cs_pin: 12
|
cs_pin: 12
|
||||||
dc_pin: 13
|
dc_pin: 13
|
||||||
reset_pin: 21
|
reset_pin: 21
|
||||||
|
invert_colors: true
|
||||||
lambda: |-
|
lambda: |-
|
||||||
it.fill(Color(0, 0, 0));
|
it.fill(Color(0, 0, 0));
|
||||||
it.image(0, 0, id(online_rgba_image));
|
it.image(0, 0, id(online_rgba_image));
|
||||||
|
|
|
@ -13,6 +13,7 @@ display:
|
||||||
cs_pin: 15
|
cs_pin: 15
|
||||||
dc_pin: 3
|
dc_pin: 3
|
||||||
reset_pin: 1
|
reset_pin: 1
|
||||||
|
invert_colors: true
|
||||||
lambda: |-
|
lambda: |-
|
||||||
it.fill(Color(0, 0, 0));
|
it.fill(Color(0, 0, 0));
|
||||||
it.image(0, 0, id(online_rgba_image));
|
it.image(0, 0, id(online_rgba_image));
|
||||||
|
|
|
@ -11,6 +11,7 @@ display:
|
||||||
cs_pin: 12
|
cs_pin: 12
|
||||||
dc_pin: 13
|
dc_pin: 13
|
||||||
reset_pin: 21
|
reset_pin: 21
|
||||||
|
invert_colors: false
|
||||||
lambda: |-
|
lambda: |-
|
||||||
// Draw a QR code in the center of the screen
|
// Draw a QR code in the center of the screen
|
||||||
auto scale = 2;
|
auto scale = 2;
|
||||||
|
|
|
@ -11,6 +11,7 @@ display:
|
||||||
cs_pin: 8
|
cs_pin: 8
|
||||||
dc_pin: 9
|
dc_pin: 9
|
||||||
reset_pin: 10
|
reset_pin: 10
|
||||||
|
invert_colors: false
|
||||||
lambda: |-
|
lambda: |-
|
||||||
// Draw a QR code in the center of the screen
|
// Draw a QR code in the center of the screen
|
||||||
auto scale = 2;
|
auto scale = 2;
|
||||||
|
|
|
@ -11,6 +11,7 @@ display:
|
||||||
cs_pin: 8
|
cs_pin: 8
|
||||||
dc_pin: 9
|
dc_pin: 9
|
||||||
reset_pin: 10
|
reset_pin: 10
|
||||||
|
invert_colors: false
|
||||||
lambda: |-
|
lambda: |-
|
||||||
// Draw a QR code in the center of the screen
|
// Draw a QR code in the center of the screen
|
||||||
auto scale = 2;
|
auto scale = 2;
|
||||||
|
|
|
@ -11,6 +11,7 @@ display:
|
||||||
cs_pin: 12
|
cs_pin: 12
|
||||||
dc_pin: 13
|
dc_pin: 13
|
||||||
reset_pin: 21
|
reset_pin: 21
|
||||||
|
invert_colors: false
|
||||||
lambda: |-
|
lambda: |-
|
||||||
// Draw a QR code in the center of the screen
|
// Draw a QR code in the center of the screen
|
||||||
auto scale = 2;
|
auto scale = 2;
|
||||||
|
|
|
@ -11,6 +11,7 @@ display:
|
||||||
cs_pin: 5
|
cs_pin: 5
|
||||||
dc_pin: 15
|
dc_pin: 15
|
||||||
reset_pin: 16
|
reset_pin: 16
|
||||||
|
invert_colors: false
|
||||||
lambda: |-
|
lambda: |-
|
||||||
// Draw a QR code in the center of the screen
|
// Draw a QR code in the center of the screen
|
||||||
auto scale = 2;
|
auto scale = 2;
|
||||||
|
|
|
@ -11,6 +11,7 @@ display:
|
||||||
cs_pin: 20
|
cs_pin: 20
|
||||||
dc_pin: 21
|
dc_pin: 21
|
||||||
reset_pin: 22
|
reset_pin: 22
|
||||||
|
invert_colors: false
|
||||||
lambda: |-
|
lambda: |-
|
||||||
// Draw a QR code in the center of the screen
|
// Draw a QR code in the center of the screen
|
||||||
auto scale = 2;
|
auto scale = 2;
|
||||||
|
|
|
@ -38,7 +38,12 @@ display:
|
||||||
hsync_pin: 16
|
hsync_pin: 16
|
||||||
vsync_pin: 17
|
vsync_pin: 17
|
||||||
pclk_pin: 21
|
pclk_pin: 21
|
||||||
init_sequence: 1
|
init_sequence:
|
||||||
|
- 1
|
||||||
|
- [0x23, 0xA, 0xB]
|
||||||
|
- delay 20ms
|
||||||
|
- [0x23, 0xA, 0xB]
|
||||||
|
- delay 0.2s
|
||||||
data_pins:
|
data_pins:
|
||||||
- number: 0
|
- number: 0
|
||||||
ignore_strapping_warning: true
|
ignore_strapping_warning: true
|
||||||
|
|
|
@ -18,6 +18,7 @@ display:
|
||||||
data_rate: 40MHz
|
data_rate: 40MHz
|
||||||
dimensions: 320x240
|
dimensions: 320x240
|
||||||
update_interval: never
|
update_interval: never
|
||||||
|
invert_colors: false
|
||||||
transform:
|
transform:
|
||||||
mirror_y: false
|
mirror_y: false
|
||||||
mirror_x: false
|
mirror_x: false
|
||||||
|
|
35
tests/components/udp/common.yaml
Normal file
35
tests/components/udp/common.yaml
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
wifi:
|
||||||
|
ssid: MySSID
|
||||||
|
password: password1
|
||||||
|
|
||||||
|
udp:
|
||||||
|
update_interval: 5s
|
||||||
|
encryption: "our key goes here"
|
||||||
|
rolling_code_enable: true
|
||||||
|
ping_pong_enable: true
|
||||||
|
binary_sensors:
|
||||||
|
- binary_sensor_id1
|
||||||
|
- id: binary_sensor_id1
|
||||||
|
broadcast_id: other_id
|
||||||
|
sensors:
|
||||||
|
- sensor_id1
|
||||||
|
- id: sensor_id1
|
||||||
|
broadcast_id: other_id
|
||||||
|
providers:
|
||||||
|
- name: some-device-name
|
||||||
|
encryption: "their key goes here"
|
||||||
|
|
||||||
|
sensor:
|
||||||
|
- platform: template
|
||||||
|
id: sensor_id1
|
||||||
|
- platform: udp
|
||||||
|
provider: some-device-name
|
||||||
|
id: our_id
|
||||||
|
remote_id: some_sensor_id
|
||||||
|
|
||||||
|
binary_sensor:
|
||||||
|
- platform: udp
|
||||||
|
provider: unencrypted-device
|
||||||
|
id: other_binary_sensor_id
|
||||||
|
- platform: template
|
||||||
|
id: binary_sensor_id1
|
1
tests/components/udp/test.bk72xx-ard.yaml
Normal file
1
tests/components/udp/test.bk72xx-ard.yaml
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<<: !include common.yaml
|
1
tests/components/udp/test.esp32-ard.yaml
Normal file
1
tests/components/udp/test.esp32-ard.yaml
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<<: !include common.yaml
|
1
tests/components/udp/test.esp32-c3-ard.yaml
Normal file
1
tests/components/udp/test.esp32-c3-ard.yaml
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<<: !include common.yaml
|
1
tests/components/udp/test.esp32-c3-idf.yaml
Normal file
1
tests/components/udp/test.esp32-c3-idf.yaml
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<<: !include common.yaml
|
1
tests/components/udp/test.esp32-idf.yaml
Normal file
1
tests/components/udp/test.esp32-idf.yaml
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<<: !include common.yaml
|
1
tests/components/udp/test.esp8266-ard.yaml
Normal file
1
tests/components/udp/test.esp8266-ard.yaml
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<<: !include common.yaml
|
4
tests/components/udp/test.host.yaml
Normal file
4
tests/components/udp/test.host.yaml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
packages:
|
||||||
|
common: !include common.yaml
|
||||||
|
|
||||||
|
wifi: !remove
|
1
tests/components/udp/test.rp2040-ard.yaml
Normal file
1
tests/components/udp/test.rp2040-ard.yaml
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<<: !include common.yaml
|
|
@ -12,6 +12,7 @@ display:
|
||||||
cs_pin: 13
|
cs_pin: 13
|
||||||
dc_pin: 14
|
dc_pin: 14
|
||||||
reset_pin: 21
|
reset_pin: 21
|
||||||
|
invert_colors: false
|
||||||
lambda: |-
|
lambda: |-
|
||||||
it.rectangle(0, 0, it.get_width(), it.get_height());
|
it.rectangle(0, 0, it.get_width(), it.get_height());
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue