mirror of
https://github.com/esphome/esphome.git
synced 2025-07-29 06:36:45 +00:00
commit
97fb8c2cdf
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
@ -56,16 +56,14 @@ jobs:
|
|||||||
uses: actions/setup-python@v5.6.0
|
uses: actions/setup-python@v5.6.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
- name: Set up python environment
|
|
||||||
env:
|
|
||||||
ESPHOME_NO_VENV: 1
|
|
||||||
run: script/setup
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |-
|
run: |-
|
||||||
pip3 install build
|
pip3 install build
|
||||||
python3 -m build
|
python3 -m build
|
||||||
- name: Publish
|
- name: Publish
|
||||||
uses: pypa/gh-action-pypi-publish@v1.12.4
|
uses: pypa/gh-action-pypi-publish@v1.12.4
|
||||||
|
with:
|
||||||
|
skip-existing: true
|
||||||
|
|
||||||
deploy-docker:
|
deploy-docker:
|
||||||
name: Build ESPHome ${{ matrix.platform.arch }}
|
name: Build ESPHome ${{ matrix.platform.arch }}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import ble_client, climate
|
from esphome.components import ble_client, climate
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ID, CONF_UNIT_OF_MEASUREMENT
|
from esphome.const import CONF_UNIT_OF_MEASUREMENT
|
||||||
|
|
||||||
UNITS = {
|
UNITS = {
|
||||||
"f": "f",
|
"f": "f",
|
||||||
@ -17,9 +17,9 @@ Anova = anova_ns.class_(
|
|||||||
)
|
)
|
||||||
|
|
||||||
CONFIG_SCHEMA = (
|
CONFIG_SCHEMA = (
|
||||||
climate.CLIMATE_SCHEMA.extend(
|
climate.climate_schema(Anova)
|
||||||
|
.extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(Anova),
|
|
||||||
cv.Required(CONF_UNIT_OF_MEASUREMENT): cv.enum(UNITS),
|
cv.Required(CONF_UNIT_OF_MEASUREMENT): cv.enum(UNITS),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -29,8 +29,7 @@ CONFIG_SCHEMA = (
|
|||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = await climate.new_climate(config)
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await climate.register_climate(var, config)
|
|
||||||
await ble_client.register_ble_node(var, config)
|
await ble_client.register_ble_node(var, config)
|
||||||
cg.add(var.set_unit_of_measurement(config[CONF_UNIT_OF_MEASUREMENT]))
|
cg.add(var.set_unit_of_measurement(config[CONF_UNIT_OF_MEASUREMENT]))
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import climate_ir
|
from esphome.components import climate_ir
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.const import CONF_ID
|
|
||||||
|
|
||||||
AUTO_LOAD = ["climate_ir"]
|
AUTO_LOAD = ["climate_ir"]
|
||||||
CODEOWNERS = ["@bazuchan"]
|
CODEOWNERS = ["@bazuchan"]
|
||||||
@ -9,13 +7,8 @@ CODEOWNERS = ["@bazuchan"]
|
|||||||
ballu_ns = cg.esphome_ns.namespace("ballu")
|
ballu_ns = cg.esphome_ns.namespace("ballu")
|
||||||
BalluClimate = ballu_ns.class_("BalluClimate", climate_ir.ClimateIR)
|
BalluClimate = ballu_ns.class_("BalluClimate", climate_ir.ClimateIR)
|
||||||
|
|
||||||
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
|
CONFIG_SCHEMA = climate_ir.climare_ir_with_receiver_schema(BalluClimate)
|
||||||
{
|
|
||||||
cv.GenerateID(): cv.declare_id(BalluClimate),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
await climate_ir.new_climate_ir(config)
|
||||||
await climate_ir.register_climate_ir(var, config)
|
|
||||||
|
@ -9,7 +9,6 @@ from esphome.const import (
|
|||||||
CONF_DEFAULT_TARGET_TEMPERATURE_LOW,
|
CONF_DEFAULT_TARGET_TEMPERATURE_LOW,
|
||||||
CONF_HEAT_ACTION,
|
CONF_HEAT_ACTION,
|
||||||
CONF_HUMIDITY_SENSOR,
|
CONF_HUMIDITY_SENSOR,
|
||||||
CONF_ID,
|
|
||||||
CONF_IDLE_ACTION,
|
CONF_IDLE_ACTION,
|
||||||
CONF_SENSOR,
|
CONF_SENSOR,
|
||||||
)
|
)
|
||||||
@ -19,9 +18,9 @@ BangBangClimate = bang_bang_ns.class_("BangBangClimate", climate.Climate, cg.Com
|
|||||||
BangBangClimateTargetTempConfig = bang_bang_ns.struct("BangBangClimateTargetTempConfig")
|
BangBangClimateTargetTempConfig = bang_bang_ns.struct("BangBangClimateTargetTempConfig")
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.All(
|
CONFIG_SCHEMA = cv.All(
|
||||||
climate.CLIMATE_SCHEMA.extend(
|
climate.climate_schema(BangBangClimate)
|
||||||
|
.extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(BangBangClimate),
|
|
||||||
cv.Required(CONF_SENSOR): cv.use_id(sensor.Sensor),
|
cv.Required(CONF_SENSOR): cv.use_id(sensor.Sensor),
|
||||||
cv.Optional(CONF_HUMIDITY_SENSOR): cv.use_id(sensor.Sensor),
|
cv.Optional(CONF_HUMIDITY_SENSOR): cv.use_id(sensor.Sensor),
|
||||||
cv.Required(CONF_DEFAULT_TARGET_TEMPERATURE_LOW): cv.temperature,
|
cv.Required(CONF_DEFAULT_TARGET_TEMPERATURE_LOW): cv.temperature,
|
||||||
@ -36,15 +35,15 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
}
|
}
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
).extend(cv.COMPONENT_SCHEMA),
|
)
|
||||||
|
.extend(cv.COMPONENT_SCHEMA),
|
||||||
cv.has_at_least_one_key(CONF_COOL_ACTION, CONF_HEAT_ACTION),
|
cv.has_at_least_one_key(CONF_COOL_ACTION, CONF_HEAT_ACTION),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = await climate.new_climate(config)
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await climate.register_climate(var, config)
|
|
||||||
|
|
||||||
sens = await cg.get_variable(config[CONF_SENSOR])
|
sens = await cg.get_variable(config[CONF_SENSOR])
|
||||||
cg.add(var.set_sensor(sens))
|
cg.add(var.set_sensor(sens))
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
import logging
|
|
||||||
|
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import ble_client, climate
|
from esphome.components import ble_client, climate
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_HEAT_MODE,
|
CONF_HEAT_MODE,
|
||||||
CONF_ID,
|
|
||||||
CONF_RECEIVE_TIMEOUT,
|
CONF_RECEIVE_TIMEOUT,
|
||||||
CONF_TEMPERATURE_SOURCE,
|
CONF_TEMPERATURE_SOURCE,
|
||||||
CONF_TIME_ID,
|
CONF_TIME_ID,
|
||||||
@ -13,7 +10,6 @@ from esphome.const import (
|
|||||||
|
|
||||||
from .. import BEDJET_CLIENT_SCHEMA, bedjet_ns, register_bedjet_child
|
from .. import BEDJET_CLIENT_SCHEMA, bedjet_ns, register_bedjet_child
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
CODEOWNERS = ["@jhansche"]
|
CODEOWNERS = ["@jhansche"]
|
||||||
DEPENDENCIES = ["bedjet"]
|
DEPENDENCIES = ["bedjet"]
|
||||||
|
|
||||||
@ -30,9 +26,9 @@ BEDJET_TEMPERATURE_SOURCES = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
CONFIG_SCHEMA = (
|
CONFIG_SCHEMA = (
|
||||||
climate.CLIMATE_SCHEMA.extend(
|
climate.climate_schema(BedJetClimate)
|
||||||
|
.extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(BedJetClimate),
|
|
||||||
cv.Optional(CONF_HEAT_MODE, default="heat"): cv.enum(
|
cv.Optional(CONF_HEAT_MODE, default="heat"): cv.enum(
|
||||||
BEDJET_HEAT_MODES, lower=True
|
BEDJET_HEAT_MODES, lower=True
|
||||||
),
|
),
|
||||||
@ -63,9 +59,8 @@ CONFIG_SCHEMA = (
|
|||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = await climate.new_climate(config)
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await climate.register_climate(var, config)
|
|
||||||
await register_bedjet_child(var, config)
|
await register_bedjet_child(var, config)
|
||||||
|
|
||||||
cg.add(var.set_heating_mode(config[CONF_HEAT_MODE]))
|
cg.add(var.set_heating_mode(config[CONF_HEAT_MODE]))
|
||||||
|
@ -1,31 +1,22 @@
|
|||||||
import logging
|
|
||||||
|
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import fan
|
from esphome.components import fan
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ID
|
|
||||||
|
|
||||||
from .. import BEDJET_CLIENT_SCHEMA, bedjet_ns, register_bedjet_child
|
from .. import BEDJET_CLIENT_SCHEMA, bedjet_ns, register_bedjet_child
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
CODEOWNERS = ["@jhansche"]
|
CODEOWNERS = ["@jhansche"]
|
||||||
DEPENDENCIES = ["bedjet"]
|
DEPENDENCIES = ["bedjet"]
|
||||||
|
|
||||||
BedJetFan = bedjet_ns.class_("BedJetFan", fan.Fan, cg.PollingComponent)
|
BedJetFan = bedjet_ns.class_("BedJetFan", fan.Fan, cg.PollingComponent)
|
||||||
|
|
||||||
CONFIG_SCHEMA = (
|
CONFIG_SCHEMA = (
|
||||||
fan.FAN_SCHEMA.extend(
|
fan.fan_schema(BedJetFan)
|
||||||
{
|
|
||||||
cv.GenerateID(): cv.declare_id(BedJetFan),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.extend(cv.polling_component_schema("60s"))
|
.extend(cv.polling_component_schema("60s"))
|
||||||
.extend(BEDJET_CLIENT_SCHEMA)
|
.extend(BEDJET_CLIENT_SCHEMA)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = await fan.new_fan(config)
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await fan.register_fan(var, config)
|
|
||||||
await register_bedjet_child(var, config)
|
await register_bedjet_child(var, config)
|
||||||
|
@ -1,31 +1,28 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import fan, output
|
from esphome.components import fan, output
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import CONF_DIRECTION_OUTPUT, CONF_OSCILLATION_OUTPUT, CONF_OUTPUT
|
||||||
CONF_DIRECTION_OUTPUT,
|
|
||||||
CONF_OSCILLATION_OUTPUT,
|
|
||||||
CONF_OUTPUT,
|
|
||||||
CONF_OUTPUT_ID,
|
|
||||||
)
|
|
||||||
|
|
||||||
from .. import binary_ns
|
from .. import binary_ns
|
||||||
|
|
||||||
BinaryFan = binary_ns.class_("BinaryFan", fan.Fan, cg.Component)
|
BinaryFan = binary_ns.class_("BinaryFan", fan.Fan, cg.Component)
|
||||||
|
|
||||||
CONFIG_SCHEMA = fan.FAN_SCHEMA.extend(
|
CONFIG_SCHEMA = (
|
||||||
{
|
fan.fan_schema(BinaryFan)
|
||||||
cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(BinaryFan),
|
.extend(
|
||||||
cv.Required(CONF_OUTPUT): cv.use_id(output.BinaryOutput),
|
{
|
||||||
cv.Optional(CONF_DIRECTION_OUTPUT): cv.use_id(output.BinaryOutput),
|
cv.Required(CONF_OUTPUT): cv.use_id(output.BinaryOutput),
|
||||||
cv.Optional(CONF_OSCILLATION_OUTPUT): cv.use_id(output.BinaryOutput),
|
cv.Optional(CONF_DIRECTION_OUTPUT): cv.use_id(output.BinaryOutput),
|
||||||
}
|
cv.Optional(CONF_OSCILLATION_OUTPUT): cv.use_id(output.BinaryOutput),
|
||||||
).extend(cv.COMPONENT_SCHEMA)
|
}
|
||||||
|
)
|
||||||
|
.extend(cv.COMPONENT_SCHEMA)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_OUTPUT_ID])
|
var = await fan.new_fan(config)
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await fan.register_fan(var, config)
|
|
||||||
|
|
||||||
output_ = await cg.get_variable(config[CONF_OUTPUT])
|
output_ = await cg.get_variable(config[CONF_OUTPUT])
|
||||||
cg.add(var.set_output(output_))
|
cg.add(var.set_output(output_))
|
||||||
|
@ -51,35 +51,60 @@ bool BluetoothProxy::parse_device(const esp32_ble_tracker::ESPBTDevice &device)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static constexpr size_t FLUSH_BATCH_SIZE = 8;
|
||||||
|
static std::vector<api::BluetoothLERawAdvertisement> &get_batch_buffer() {
|
||||||
|
static std::vector<api::BluetoothLERawAdvertisement> batch_buffer;
|
||||||
|
return batch_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
bool BluetoothProxy::parse_devices(esp_ble_gap_cb_param_t::ble_scan_result_evt_param *advertisements, size_t count) {
|
bool BluetoothProxy::parse_devices(esp_ble_gap_cb_param_t::ble_scan_result_evt_param *advertisements, size_t count) {
|
||||||
if (!api::global_api_server->is_connected() || this->api_connection_ == nullptr || !this->raw_advertisements_)
|
if (!api::global_api_server->is_connected() || this->api_connection_ == nullptr || !this->raw_advertisements_)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
api::BluetoothLERawAdvertisementsResponse resp;
|
// Get the batch buffer reference
|
||||||
// Pre-allocate the advertisements vector to avoid reallocations
|
auto &batch_buffer = get_batch_buffer();
|
||||||
resp.advertisements.reserve(count);
|
|
||||||
|
|
||||||
|
// Reserve additional capacity if needed
|
||||||
|
size_t new_size = batch_buffer.size() + count;
|
||||||
|
if (batch_buffer.capacity() < new_size) {
|
||||||
|
batch_buffer.reserve(new_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add new advertisements to the batch buffer
|
||||||
for (size_t i = 0; i < count; i++) {
|
for (size_t i = 0; i < count; i++) {
|
||||||
auto &result = advertisements[i];
|
auto &result = advertisements[i];
|
||||||
api::BluetoothLERawAdvertisement adv;
|
uint8_t length = result.adv_data_len + result.scan_rsp_len;
|
||||||
|
|
||||||
|
batch_buffer.emplace_back();
|
||||||
|
auto &adv = batch_buffer.back();
|
||||||
adv.address = esp32_ble::ble_addr_to_uint64(result.bda);
|
adv.address = esp32_ble::ble_addr_to_uint64(result.bda);
|
||||||
adv.rssi = result.rssi;
|
adv.rssi = result.rssi;
|
||||||
adv.address_type = result.ble_addr_type;
|
adv.address_type = result.ble_addr_type;
|
||||||
|
adv.data.assign(&result.ble_adv[0], &result.ble_adv[length]);
|
||||||
|
|
||||||
uint8_t length = result.adv_data_len + result.scan_rsp_len;
|
ESP_LOGV(TAG, "Queuing raw packet from %02X:%02X:%02X:%02X:%02X:%02X, length %d. RSSI: %d dB", result.bda[0],
|
||||||
adv.data.reserve(length);
|
|
||||||
// Use a bulk insert instead of individual push_backs
|
|
||||||
adv.data.insert(adv.data.end(), &result.ble_adv[0], &result.ble_adv[length]);
|
|
||||||
|
|
||||||
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);
|
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);
|
|
||||||
this->api_connection_->send_bluetooth_le_raw_advertisements_response(resp);
|
// Only send if we've accumulated a good batch size to maximize batching efficiency
|
||||||
|
// https://github.com/esphome/backlog/issues/21
|
||||||
|
if (batch_buffer.size() >= FLUSH_BATCH_SIZE) {
|
||||||
|
this->flush_pending_advertisements();
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BluetoothProxy::flush_pending_advertisements() {
|
||||||
|
auto &batch_buffer = get_batch_buffer();
|
||||||
|
if (batch_buffer.empty() || !api::global_api_server->is_connected() || this->api_connection_ == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
api::BluetoothLERawAdvertisementsResponse resp;
|
||||||
|
resp.advertisements.swap(batch_buffer);
|
||||||
|
this->api_connection_->send_bluetooth_le_raw_advertisements_response(resp);
|
||||||
|
}
|
||||||
|
|
||||||
void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device) {
|
void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device) {
|
||||||
api::BluetoothLEAdvertisementResponse resp;
|
api::BluetoothLEAdvertisementResponse resp;
|
||||||
resp.address = device.address_uint64();
|
resp.address = device.address_uint64();
|
||||||
@ -91,28 +116,28 @@ void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &devi
|
|||||||
// Pre-allocate vectors based on known sizes
|
// Pre-allocate vectors based on known sizes
|
||||||
auto service_uuids = device.get_service_uuids();
|
auto service_uuids = device.get_service_uuids();
|
||||||
resp.service_uuids.reserve(service_uuids.size());
|
resp.service_uuids.reserve(service_uuids.size());
|
||||||
for (auto uuid : service_uuids) {
|
for (auto &uuid : service_uuids) {
|
||||||
resp.service_uuids.push_back(uuid.to_string());
|
resp.service_uuids.emplace_back(uuid.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pre-allocate service data vector
|
// Pre-allocate service data vector
|
||||||
auto service_datas = device.get_service_datas();
|
auto service_datas = device.get_service_datas();
|
||||||
resp.service_data.reserve(service_datas.size());
|
resp.service_data.reserve(service_datas.size());
|
||||||
for (auto &data : service_datas) {
|
for (auto &data : service_datas) {
|
||||||
api::BluetoothServiceData service_data;
|
resp.service_data.emplace_back();
|
||||||
|
auto &service_data = resp.service_data.back();
|
||||||
service_data.uuid = data.uuid.to_string();
|
service_data.uuid = data.uuid.to_string();
|
||||||
service_data.data.assign(data.data.begin(), data.data.end());
|
service_data.data.assign(data.data.begin(), data.data.end());
|
||||||
resp.service_data.push_back(std::move(service_data));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pre-allocate manufacturer data vector
|
// Pre-allocate manufacturer data vector
|
||||||
auto manufacturer_datas = device.get_manufacturer_datas();
|
auto manufacturer_datas = device.get_manufacturer_datas();
|
||||||
resp.manufacturer_data.reserve(manufacturer_datas.size());
|
resp.manufacturer_data.reserve(manufacturer_datas.size());
|
||||||
for (auto &data : manufacturer_datas) {
|
for (auto &data : manufacturer_datas) {
|
||||||
api::BluetoothServiceData manufacturer_data;
|
resp.manufacturer_data.emplace_back();
|
||||||
|
auto &manufacturer_data = resp.manufacturer_data.back();
|
||||||
manufacturer_data.uuid = data.uuid.to_string();
|
manufacturer_data.uuid = data.uuid.to_string();
|
||||||
manufacturer_data.data.assign(data.data.begin(), data.data.end());
|
manufacturer_data.data.assign(data.data.begin(), data.data.end());
|
||||||
resp.manufacturer_data.push_back(std::move(manufacturer_data));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this->api_connection_->send_bluetooth_le_advertisement(resp);
|
this->api_connection_->send_bluetooth_le_advertisement(resp);
|
||||||
@ -148,6 +173,18 @@ void BluetoothProxy::loop() {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Flush any pending BLE advertisements that have been accumulated but not yet sent
|
||||||
|
if (this->raw_advertisements_) {
|
||||||
|
static uint32_t last_flush_time = 0;
|
||||||
|
uint32_t now = millis();
|
||||||
|
|
||||||
|
// Flush accumulated advertisements every 100ms
|
||||||
|
if (now - last_flush_time >= 100) {
|
||||||
|
this->flush_pending_advertisements();
|
||||||
|
last_flush_time = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
for (auto *connection : this->connections_) {
|
for (auto *connection : this->connections_) {
|
||||||
if (connection->send_service_ == connection->service_count_) {
|
if (connection->send_service_ == connection->service_count_) {
|
||||||
connection->send_service_ = DONE_SENDING_SERVICES;
|
connection->send_service_ = DONE_SENDING_SERVICES;
|
||||||
|
@ -56,6 +56,7 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com
|
|||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void loop() override;
|
void loop() override;
|
||||||
|
void flush_pending_advertisements();
|
||||||
esp32_ble_tracker::AdvertisementParserType get_advertisement_parser_type() override;
|
esp32_ble_tracker::AdvertisementParserType get_advertisement_parser_type() override;
|
||||||
|
|
||||||
void register_connection(BluetoothConnection *connection) {
|
void register_connection(BluetoothConnection *connection) {
|
||||||
|
@ -1,7 +1,13 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
from esphome import core
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import climate, remote_base, sensor
|
from esphome.components import climate, remote_base, sensor
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_SENSOR, CONF_SUPPORTS_COOL, CONF_SUPPORTS_HEAT
|
from esphome.const import CONF_ID, CONF_SENSOR, CONF_SUPPORTS_COOL, CONF_SUPPORTS_HEAT
|
||||||
|
from esphome.cpp_generator import MockObjClass
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
DEPENDENCIES = ["remote_transmitter"]
|
DEPENDENCIES = ["remote_transmitter"]
|
||||||
AUTO_LOAD = ["sensor", "remote_base"]
|
AUTO_LOAD = ["sensor", "remote_base"]
|
||||||
@ -16,30 +22,58 @@ ClimateIR = climate_ir_ns.class_(
|
|||||||
remote_base.RemoteTransmittable,
|
remote_base.RemoteTransmittable,
|
||||||
)
|
)
|
||||||
|
|
||||||
CLIMATE_IR_SCHEMA = (
|
|
||||||
climate.CLIMATE_SCHEMA.extend(
|
def climate_ir_schema(
|
||||||
|
class_: MockObjClass,
|
||||||
|
) -> cv.Schema:
|
||||||
|
return (
|
||||||
|
climate.climate_schema(class_)
|
||||||
|
.extend(
|
||||||
|
{
|
||||||
|
cv.Optional(CONF_SUPPORTS_COOL, default=True): cv.boolean,
|
||||||
|
cv.Optional(CONF_SUPPORTS_HEAT, default=True): cv.boolean,
|
||||||
|
cv.Optional(CONF_SENSOR): cv.use_id(sensor.Sensor),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(cv.COMPONENT_SCHEMA)
|
||||||
|
.extend(remote_base.REMOTE_TRANSMITTABLE_SCHEMA)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def climare_ir_with_receiver_schema(
|
||||||
|
class_: MockObjClass,
|
||||||
|
) -> cv.Schema:
|
||||||
|
return climate_ir_schema(class_).extend(
|
||||||
{
|
{
|
||||||
cv.Optional(CONF_SUPPORTS_COOL, default=True): cv.boolean,
|
cv.Optional(remote_base.CONF_RECEIVER_ID): cv.use_id(
|
||||||
cv.Optional(CONF_SUPPORTS_HEAT, default=True): cv.boolean,
|
remote_base.RemoteReceiverBase
|
||||||
cv.Optional(CONF_SENSOR): cv.use_id(sensor.Sensor),
|
),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.extend(cv.COMPONENT_SCHEMA)
|
|
||||||
.extend(remote_base.REMOTE_TRANSMITTABLE_SCHEMA)
|
|
||||||
)
|
|
||||||
|
|
||||||
CLIMATE_IR_WITH_RECEIVER_SCHEMA = CLIMATE_IR_SCHEMA.extend(
|
|
||||||
{
|
# Remove before 2025.11.0
|
||||||
cv.Optional(remote_base.CONF_RECEIVER_ID): cv.use_id(
|
def deprecated_schema_constant(config):
|
||||||
remote_base.RemoteReceiverBase
|
type: str = "unknown"
|
||||||
),
|
if (id := config.get(CONF_ID)) is not None and isinstance(id, core.ID):
|
||||||
}
|
type = str(id.type).split("::", maxsplit=1)[0]
|
||||||
)
|
_LOGGER.warning(
|
||||||
|
"Using `climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA` is deprecated and will be removed in ESPHome 2025.11.0. "
|
||||||
|
"Please use `climate_ir.climare_ir_with_receiver_schema(...)` instead. "
|
||||||
|
"If you are seeing this, report an issue to the external_component author and ask them to update it. "
|
||||||
|
"https://developers.esphome.io/blog/2025/05/14/_schema-deprecations/. "
|
||||||
|
"Component using this schema: %s",
|
||||||
|
type,
|
||||||
|
)
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
CLIMATE_IR_WITH_RECEIVER_SCHEMA = climare_ir_with_receiver_schema(ClimateIR)
|
||||||
|
CLIMATE_IR_WITH_RECEIVER_SCHEMA.add_extra(deprecated_schema_constant)
|
||||||
|
|
||||||
|
|
||||||
async def register_climate_ir(var, config):
|
async def register_climate_ir(var, config):
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await climate.register_climate(var, config)
|
|
||||||
await remote_base.register_transmittable(var, config)
|
await remote_base.register_transmittable(var, config)
|
||||||
cg.add(var.set_supports_cool(config[CONF_SUPPORTS_COOL]))
|
cg.add(var.set_supports_cool(config[CONF_SUPPORTS_COOL]))
|
||||||
cg.add(var.set_supports_heat(config[CONF_SUPPORTS_HEAT]))
|
cg.add(var.set_supports_heat(config[CONF_SUPPORTS_HEAT]))
|
||||||
@ -48,3 +82,9 @@ async def register_climate_ir(var, config):
|
|||||||
if sensor_id := config.get(CONF_SENSOR):
|
if sensor_id := config.get(CONF_SENSOR):
|
||||||
sens = await cg.get_variable(sensor_id)
|
sens = await cg.get_variable(sensor_id)
|
||||||
cg.add(var.set_sensor(sens))
|
cg.add(var.set_sensor(sens))
|
||||||
|
|
||||||
|
|
||||||
|
async def new_climate_ir(config, *args):
|
||||||
|
var = await climate.new_climate(config, *args)
|
||||||
|
await register_climate_ir(var, config)
|
||||||
|
return var
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import climate_ir
|
from esphome.components import climate_ir
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ID
|
|
||||||
|
|
||||||
AUTO_LOAD = ["climate_ir"]
|
AUTO_LOAD = ["climate_ir"]
|
||||||
|
|
||||||
@ -14,9 +13,8 @@ CONF_BIT_HIGH = "bit_high"
|
|||||||
CONF_BIT_ONE_LOW = "bit_one_low"
|
CONF_BIT_ONE_LOW = "bit_one_low"
|
||||||
CONF_BIT_ZERO_LOW = "bit_zero_low"
|
CONF_BIT_ZERO_LOW = "bit_zero_low"
|
||||||
|
|
||||||
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
|
CONFIG_SCHEMA = climate_ir.climare_ir_with_receiver_schema(LgIrClimate).extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(LgIrClimate),
|
|
||||||
cv.Optional(
|
cv.Optional(
|
||||||
CONF_HEADER_HIGH, default="8000us"
|
CONF_HEADER_HIGH, default="8000us"
|
||||||
): cv.positive_time_period_microseconds,
|
): cv.positive_time_period_microseconds,
|
||||||
@ -37,8 +35,7 @@ CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
|
|||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = await climate_ir.new_climate_ir(config)
|
||||||
await climate_ir.register_climate_ir(var, config)
|
|
||||||
|
|
||||||
cg.add(var.set_header_high(config[CONF_HEADER_HIGH]))
|
cg.add(var.set_header_high(config[CONF_HEADER_HIGH]))
|
||||||
cg.add(var.set_header_low(config[CONF_HEADER_LOW]))
|
cg.add(var.set_header_low(config[CONF_HEADER_LOW]))
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import climate_ir
|
from esphome.components import climate_ir
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.const import CONF_ID
|
|
||||||
|
|
||||||
AUTO_LOAD = ["climate_ir"]
|
AUTO_LOAD = ["climate_ir"]
|
||||||
CODEOWNERS = ["@glmnet"]
|
CODEOWNERS = ["@glmnet"]
|
||||||
@ -9,13 +7,8 @@ CODEOWNERS = ["@glmnet"]
|
|||||||
coolix_ns = cg.esphome_ns.namespace("coolix")
|
coolix_ns = cg.esphome_ns.namespace("coolix")
|
||||||
CoolixClimate = coolix_ns.class_("CoolixClimate", climate_ir.ClimateIR)
|
CoolixClimate = coolix_ns.class_("CoolixClimate", climate_ir.ClimateIR)
|
||||||
|
|
||||||
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
|
CONFIG_SCHEMA = climate_ir.climare_ir_with_receiver_schema(CoolixClimate)
|
||||||
{
|
|
||||||
cv.GenerateID(): cv.declare_id(CoolixClimate),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
await climate_ir.new_climate_ir(config)
|
||||||
await climate_ir.register_climate_ir(var, config)
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import fan
|
from esphome.components import fan
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ENTITY_CATEGORY, CONF_ICON, CONF_ID, CONF_SOURCE_ID
|
from esphome.const import CONF_ENTITY_CATEGORY, CONF_ICON, CONF_SOURCE_ID
|
||||||
from esphome.core.entity_helpers import inherit_property_from
|
from esphome.core.entity_helpers import inherit_property_from
|
||||||
|
|
||||||
from .. import copy_ns
|
from .. import copy_ns
|
||||||
@ -9,12 +9,15 @@ from .. import copy_ns
|
|||||||
CopyFan = copy_ns.class_("CopyFan", fan.Fan, cg.Component)
|
CopyFan = copy_ns.class_("CopyFan", fan.Fan, cg.Component)
|
||||||
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = fan.FAN_SCHEMA.extend(
|
CONFIG_SCHEMA = (
|
||||||
{
|
fan.fan_schema(CopyFan)
|
||||||
cv.GenerateID(): cv.declare_id(CopyFan),
|
.extend(
|
||||||
cv.Required(CONF_SOURCE_ID): cv.use_id(fan.Fan),
|
{
|
||||||
}
|
cv.Required(CONF_SOURCE_ID): cv.use_id(fan.Fan),
|
||||||
).extend(cv.COMPONENT_SCHEMA)
|
}
|
||||||
|
)
|
||||||
|
.extend(cv.COMPONENT_SCHEMA)
|
||||||
|
)
|
||||||
|
|
||||||
FINAL_VALIDATE_SCHEMA = cv.All(
|
FINAL_VALIDATE_SCHEMA = cv.All(
|
||||||
inherit_property_from(CONF_ICON, CONF_SOURCE_ID),
|
inherit_property_from(CONF_ICON, CONF_SOURCE_ID),
|
||||||
@ -23,8 +26,7 @@ FINAL_VALIDATE_SCHEMA = cv.All(
|
|||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = await fan.new_fan(config)
|
||||||
await fan.register_fan(var, config)
|
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
|
|
||||||
source = await cg.get_variable(config[CONF_SOURCE_ID])
|
source = await cg.get_variable(config[CONF_SOURCE_ID])
|
||||||
|
@ -1,20 +1,13 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import climate_ir
|
from esphome.components import climate_ir
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.const import CONF_ID
|
|
||||||
|
|
||||||
AUTO_LOAD = ["climate_ir"]
|
AUTO_LOAD = ["climate_ir"]
|
||||||
|
|
||||||
daikin_ns = cg.esphome_ns.namespace("daikin")
|
daikin_ns = cg.esphome_ns.namespace("daikin")
|
||||||
DaikinClimate = daikin_ns.class_("DaikinClimate", climate_ir.ClimateIR)
|
DaikinClimate = daikin_ns.class_("DaikinClimate", climate_ir.ClimateIR)
|
||||||
|
|
||||||
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
|
CONFIG_SCHEMA = climate_ir.climare_ir_with_receiver_schema(DaikinClimate)
|
||||||
{
|
|
||||||
cv.GenerateID(): cv.declare_id(DaikinClimate),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
await climate_ir.new_climate_ir(config)
|
||||||
await climate_ir.register_climate_ir(var, config)
|
|
||||||
|
@ -1,18 +1,13 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import climate_ir
|
from esphome.components import climate_ir
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.const import CONF_ID
|
|
||||||
|
|
||||||
AUTO_LOAD = ["climate_ir"]
|
AUTO_LOAD = ["climate_ir"]
|
||||||
|
|
||||||
daikin_arc_ns = cg.esphome_ns.namespace("daikin_arc")
|
daikin_arc_ns = cg.esphome_ns.namespace("daikin_arc")
|
||||||
DaikinArcClimate = daikin_arc_ns.class_("DaikinArcClimate", climate_ir.ClimateIR)
|
DaikinArcClimate = daikin_arc_ns.class_("DaikinArcClimate", climate_ir.ClimateIR)
|
||||||
|
|
||||||
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
|
CONFIG_SCHEMA = climate_ir.climare_ir_with_receiver_schema(DaikinArcClimate)
|
||||||
{cv.GenerateID(): cv.declare_id(DaikinArcClimate)}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
await climate_ir.new_climate_ir(config)
|
||||||
await climate_ir.register_climate_ir(var, config)
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import climate_ir
|
from esphome.components import climate_ir
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ID, CONF_USE_FAHRENHEIT
|
from esphome.const import CONF_USE_FAHRENHEIT
|
||||||
|
|
||||||
AUTO_LOAD = ["climate_ir"]
|
AUTO_LOAD = ["climate_ir"]
|
||||||
|
|
||||||
@ -9,15 +9,13 @@ daikin_brc_ns = cg.esphome_ns.namespace("daikin_brc")
|
|||||||
DaikinBrcClimate = daikin_brc_ns.class_("DaikinBrcClimate", climate_ir.ClimateIR)
|
DaikinBrcClimate = daikin_brc_ns.class_("DaikinBrcClimate", climate_ir.ClimateIR)
|
||||||
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
|
CONFIG_SCHEMA = climate_ir.climare_ir_with_receiver_schema(DaikinBrcClimate).extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(DaikinBrcClimate),
|
|
||||||
cv.Optional(CONF_USE_FAHRENHEIT, default=False): cv.boolean,
|
cv.Optional(CONF_USE_FAHRENHEIT, default=False): cv.boolean,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = await climate_ir.new_climate_ir(config)
|
||||||
await climate_ir.register_climate_ir(var, config)
|
|
||||||
cg.add(var.set_fahrenheit(config[CONF_USE_FAHRENHEIT]))
|
cg.add(var.set_fahrenheit(config[CONF_USE_FAHRENHEIT]))
|
||||||
|
@ -1,20 +1,13 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import climate_ir
|
from esphome.components import climate_ir
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.const import CONF_ID
|
|
||||||
|
|
||||||
AUTO_LOAD = ["climate_ir"]
|
AUTO_LOAD = ["climate_ir"]
|
||||||
|
|
||||||
delonghi_ns = cg.esphome_ns.namespace("delonghi")
|
delonghi_ns = cg.esphome_ns.namespace("delonghi")
|
||||||
DelonghiClimate = delonghi_ns.class_("DelonghiClimate", climate_ir.ClimateIR)
|
DelonghiClimate = delonghi_ns.class_("DelonghiClimate", climate_ir.ClimateIR)
|
||||||
|
|
||||||
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
|
CONFIG_SCHEMA = climate_ir.climare_ir_with_receiver_schema(DelonghiClimate)
|
||||||
{
|
|
||||||
cv.GenerateID(): cv.declare_id(DelonghiClimate),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
await climate_ir.new_climate_ir(config)
|
||||||
await climate_ir.register_climate_ir(var, config)
|
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import climate_ir
|
from esphome.components import climate_ir
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.const import CONF_ID
|
|
||||||
|
|
||||||
CODEOWNERS = ["@E440QF"]
|
CODEOWNERS = ["@E440QF"]
|
||||||
AUTO_LOAD = ["climate_ir"]
|
AUTO_LOAD = ["climate_ir"]
|
||||||
@ -9,13 +7,8 @@ AUTO_LOAD = ["climate_ir"]
|
|||||||
emmeti_ns = cg.esphome_ns.namespace("emmeti")
|
emmeti_ns = cg.esphome_ns.namespace("emmeti")
|
||||||
EmmetiClimate = emmeti_ns.class_("EmmetiClimate", climate_ir.ClimateIR)
|
EmmetiClimate = emmeti_ns.class_("EmmetiClimate", climate_ir.ClimateIR)
|
||||||
|
|
||||||
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
|
CONFIG_SCHEMA = climate_ir.climare_ir_with_receiver_schema(EmmetiClimate)
|
||||||
{
|
|
||||||
cv.GenerateID(): cv.declare_id(EmmetiClimate),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
await climate_ir.new_climate_ir(config)
|
||||||
await climate_ir.register_climate_ir(var, config)
|
|
||||||
|
@ -122,7 +122,7 @@ void ESP32BLETracker::loop() {
|
|||||||
|
|
||||||
if (this->scanner_state_ == ScannerState::RUNNING &&
|
if (this->scanner_state_ == ScannerState::RUNNING &&
|
||||||
this->scan_result_index_ && // if it looks like we have a scan result we will take the lock
|
this->scan_result_index_ && // if it looks like we have a scan result we will take the lock
|
||||||
xSemaphoreTake(this->scan_result_lock_, 5L / portTICK_PERIOD_MS)) {
|
xSemaphoreTake(this->scan_result_lock_, 0)) {
|
||||||
uint32_t index = this->scan_result_index_;
|
uint32_t index = this->scan_result_index_;
|
||||||
if (index >= ESP32BLETracker::SCAN_RESULT_BUFFER_SIZE) {
|
if (index >= ESP32BLETracker::SCAN_RESULT_BUFFER_SIZE) {
|
||||||
ESP_LOGW(TAG, "Too many BLE events to process. Some devices may not show up.");
|
ESP_LOGW(TAG, "Too many BLE events to process. Some devices may not show up.");
|
||||||
@ -447,7 +447,7 @@ void ESP32BLETracker::gap_scan_stop_complete_(const esp_ble_gap_cb_param_t::ble_
|
|||||||
void ESP32BLETracker::gap_scan_result_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m) {
|
void ESP32BLETracker::gap_scan_result_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m) {
|
||||||
ESP_LOGV(TAG, "gap_scan_result - event %d", param.search_evt);
|
ESP_LOGV(TAG, "gap_scan_result - event %d", param.search_evt);
|
||||||
if (param.search_evt == ESP_GAP_SEARCH_INQ_RES_EVT) {
|
if (param.search_evt == ESP_GAP_SEARCH_INQ_RES_EVT) {
|
||||||
if (xSemaphoreTake(this->scan_result_lock_, 0L)) {
|
if (xSemaphoreTake(this->scan_result_lock_, 0)) {
|
||||||
if (this->scan_result_index_ < ESP32BLETracker::SCAN_RESULT_BUFFER_SIZE) {
|
if (this->scan_result_index_ < ESP32BLETracker::SCAN_RESULT_BUFFER_SIZE) {
|
||||||
this->scan_result_buffer_[this->scan_result_index_++] = param;
|
this->scan_result_buffer_[this->scan_result_index_++] = param;
|
||||||
}
|
}
|
||||||
|
@ -290,7 +290,7 @@ class ESP32BLETracker : public Component,
|
|||||||
#ifdef USE_PSRAM
|
#ifdef USE_PSRAM
|
||||||
const static u_int8_t SCAN_RESULT_BUFFER_SIZE = 32;
|
const static u_int8_t SCAN_RESULT_BUFFER_SIZE = 32;
|
||||||
#else
|
#else
|
||||||
const static u_int8_t SCAN_RESULT_BUFFER_SIZE = 16;
|
const static u_int8_t SCAN_RESULT_BUFFER_SIZE = 20;
|
||||||
#endif // USE_PSRAM
|
#endif // USE_PSRAM
|
||||||
esp_ble_gap_cb_param_t::ble_scan_result_evt_param *scan_result_buffer_;
|
esp_ble_gap_cb_param_t::ble_scan_result_evt_param *scan_result_buffer_;
|
||||||
esp_bt_status_t scan_start_failed_{ESP_BT_STATUS_SUCCESS};
|
esp_bt_status_t scan_start_failed_{ESP_BT_STATUS_SUCCESS};
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import climate_ir
|
from esphome.components import climate_ir
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.const import CONF_ID
|
|
||||||
|
|
||||||
AUTO_LOAD = ["climate_ir"]
|
AUTO_LOAD = ["climate_ir"]
|
||||||
|
|
||||||
@ -10,13 +8,8 @@ FujitsuGeneralClimate = fujitsu_general_ns.class_(
|
|||||||
"FujitsuGeneralClimate", climate_ir.ClimateIR
|
"FujitsuGeneralClimate", climate_ir.ClimateIR
|
||||||
)
|
)
|
||||||
|
|
||||||
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
|
CONFIG_SCHEMA = climate_ir.climare_ir_with_receiver_schema(FujitsuGeneralClimate)
|
||||||
{
|
|
||||||
cv.GenerateID(): cv.declare_id(FujitsuGeneralClimate),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
await climate_ir.new_climate_ir(config)
|
||||||
await climate_ir.register_climate_ir(var, config)
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import climate_ir
|
from esphome.components import climate_ir
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ID, CONF_MODEL
|
from esphome.const import CONF_MODEL
|
||||||
|
|
||||||
CODEOWNERS = ["@orestismers"]
|
CODEOWNERS = ["@orestismers"]
|
||||||
|
|
||||||
@ -21,16 +21,13 @@ MODELS = {
|
|||||||
"yag": Model.GREE_YAG,
|
"yag": Model.GREE_YAG,
|
||||||
}
|
}
|
||||||
|
|
||||||
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
|
CONFIG_SCHEMA = climate_ir.climare_ir_with_receiver_schema(GreeClimate).extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(GreeClimate),
|
|
||||||
cv.Required(CONF_MODEL): cv.enum(MODELS),
|
cv.Required(CONF_MODEL): cv.enum(MODELS),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = await climate_ir.new_climate_ir(config)
|
||||||
cg.add(var.set_model(config[CONF_MODEL]))
|
cg.add(var.set_model(config[CONF_MODEL]))
|
||||||
|
|
||||||
await climate_ir.register_climate_ir(var, config)
|
|
||||||
|
@ -30,6 +30,7 @@ from esphome.const import (
|
|||||||
CONF_VISUAL,
|
CONF_VISUAL,
|
||||||
CONF_WIFI,
|
CONF_WIFI,
|
||||||
)
|
)
|
||||||
|
from esphome.cpp_generator import MockObjClass
|
||||||
import esphome.final_validate as fv
|
import esphome.final_validate as fv
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@ -185,42 +186,46 @@ def validate_visual(config):
|
|||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
||||||
BASE_CONFIG_SCHEMA = (
|
def _base_config_schema(class_: MockObjClass) -> cv.Schema:
|
||||||
climate.CLIMATE_SCHEMA.extend(
|
return (
|
||||||
{
|
climate.climate_schema(class_)
|
||||||
cv.Optional(CONF_SUPPORTED_MODES): cv.ensure_list(
|
.extend(
|
||||||
cv.enum(SUPPORTED_CLIMATE_MODES_OPTIONS, upper=True)
|
{
|
||||||
),
|
cv.Optional(CONF_SUPPORTED_MODES): cv.ensure_list(
|
||||||
cv.Optional(
|
cv.enum(SUPPORTED_CLIMATE_MODES_OPTIONS, upper=True)
|
||||||
CONF_SUPPORTED_SWING_MODES,
|
),
|
||||||
default=[
|
cv.Optional(
|
||||||
"VERTICAL",
|
CONF_SUPPORTED_SWING_MODES,
|
||||||
"HORIZONTAL",
|
default=[
|
||||||
"BOTH",
|
"VERTICAL",
|
||||||
],
|
"HORIZONTAL",
|
||||||
): cv.ensure_list(cv.enum(SUPPORTED_SWING_MODES_OPTIONS, upper=True)),
|
"BOTH",
|
||||||
cv.Optional(CONF_WIFI_SIGNAL, default=False): cv.boolean,
|
],
|
||||||
cv.Optional(CONF_DISPLAY): cv.boolean,
|
): cv.ensure_list(cv.enum(SUPPORTED_SWING_MODES_OPTIONS, upper=True)),
|
||||||
cv.Optional(
|
cv.Optional(CONF_WIFI_SIGNAL, default=False): cv.boolean,
|
||||||
CONF_ANSWER_TIMEOUT,
|
cv.Optional(CONF_DISPLAY): cv.boolean,
|
||||||
): cv.positive_time_period_milliseconds,
|
cv.Optional(
|
||||||
cv.Optional(CONF_ON_STATUS_MESSAGE): automation.validate_automation(
|
CONF_ANSWER_TIMEOUT,
|
||||||
{
|
): cv.positive_time_period_milliseconds,
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StatusMessageTrigger),
|
cv.Optional(CONF_ON_STATUS_MESSAGE): automation.validate_automation(
|
||||||
}
|
{
|
||||||
),
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||||
}
|
StatusMessageTrigger
|
||||||
|
),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(uart.UART_DEVICE_SCHEMA)
|
||||||
|
.extend(cv.COMPONENT_SCHEMA)
|
||||||
)
|
)
|
||||||
.extend(uart.UART_DEVICE_SCHEMA)
|
|
||||||
.extend(cv.COMPONENT_SCHEMA)
|
|
||||||
)
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.All(
|
CONFIG_SCHEMA = cv.All(
|
||||||
cv.typed_schema(
|
cv.typed_schema(
|
||||||
{
|
{
|
||||||
PROTOCOL_SMARTAIR2: BASE_CONFIG_SCHEMA.extend(
|
PROTOCOL_SMARTAIR2: _base_config_schema(Smartair2Climate).extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(Smartair2Climate),
|
|
||||||
cv.Optional(
|
cv.Optional(
|
||||||
CONF_ALTERNATIVE_SWING_CONTROL, default=False
|
CONF_ALTERNATIVE_SWING_CONTROL, default=False
|
||||||
): cv.boolean,
|
): cv.boolean,
|
||||||
@ -232,9 +237,8 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
PROTOCOL_HON: BASE_CONFIG_SCHEMA.extend(
|
PROTOCOL_HON: _base_config_schema(HonClimate).extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(HonClimate),
|
|
||||||
cv.Optional(
|
cv.Optional(
|
||||||
CONF_CONTROL_METHOD, default="SET_GROUP_PARAMETERS"
|
CONF_CONTROL_METHOD, default="SET_GROUP_PARAMETERS"
|
||||||
): cv.ensure_list(
|
): cv.ensure_list(
|
||||||
@ -464,10 +468,9 @@ FINAL_VALIDATE_SCHEMA = _final_validate
|
|||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
cg.add(haier_ns.init_haier_protocol_logging())
|
cg.add(haier_ns.init_haier_protocol_logging())
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = await climate.new_climate(config)
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await uart.register_uart_device(var, config)
|
await uart.register_uart_device(var, config)
|
||||||
await climate.register_climate(var, config)
|
|
||||||
|
|
||||||
cg.add(var.set_send_wifi(config[CONF_WIFI_SIGNAL]))
|
cg.add(var.set_send_wifi(config[CONF_WIFI_SIGNAL]))
|
||||||
if CONF_CONTROL_METHOD in config:
|
if CONF_CONTROL_METHOD in config:
|
||||||
|
@ -30,25 +30,28 @@ DECAY_MODE_OPTIONS = {
|
|||||||
# Actions
|
# Actions
|
||||||
BrakeAction = hbridge_ns.class_("BrakeAction", automation.Action)
|
BrakeAction = hbridge_ns.class_("BrakeAction", automation.Action)
|
||||||
|
|
||||||
CONFIG_SCHEMA = fan.FAN_SCHEMA.extend(
|
CONFIG_SCHEMA = (
|
||||||
{
|
fan.fan_schema(HBridgeFan)
|
||||||
cv.GenerateID(CONF_ID): cv.declare_id(HBridgeFan),
|
.extend(
|
||||||
cv.Required(CONF_PIN_A): cv.use_id(output.FloatOutput),
|
{
|
||||||
cv.Required(CONF_PIN_B): cv.use_id(output.FloatOutput),
|
cv.Required(CONF_PIN_A): cv.use_id(output.FloatOutput),
|
||||||
cv.Optional(CONF_DECAY_MODE, default="SLOW"): cv.enum(
|
cv.Required(CONF_PIN_B): cv.use_id(output.FloatOutput),
|
||||||
DECAY_MODE_OPTIONS, upper=True
|
cv.Optional(CONF_DECAY_MODE, default="SLOW"): cv.enum(
|
||||||
),
|
DECAY_MODE_OPTIONS, upper=True
|
||||||
cv.Optional(CONF_SPEED_COUNT, default=100): cv.int_range(min=1),
|
),
|
||||||
cv.Optional(CONF_ENABLE_PIN): cv.use_id(output.FloatOutput),
|
cv.Optional(CONF_SPEED_COUNT, default=100): cv.int_range(min=1),
|
||||||
cv.Optional(CONF_PRESET_MODES): validate_preset_modes,
|
cv.Optional(CONF_ENABLE_PIN): cv.use_id(output.FloatOutput),
|
||||||
}
|
cv.Optional(CONF_PRESET_MODES): validate_preset_modes,
|
||||||
).extend(cv.COMPONENT_SCHEMA)
|
}
|
||||||
|
)
|
||||||
|
.extend(cv.COMPONENT_SCHEMA)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@automation.register_action(
|
@automation.register_action(
|
||||||
"fan.hbridge.brake",
|
"fan.hbridge.brake",
|
||||||
BrakeAction,
|
BrakeAction,
|
||||||
maybe_simple_id({cv.Required(CONF_ID): cv.use_id(HBridgeFan)}),
|
maybe_simple_id({cv.GenerateID(): cv.use_id(HBridgeFan)}),
|
||||||
)
|
)
|
||||||
async def fan_hbridge_brake_to_code(config, action_id, template_arg, args):
|
async def fan_hbridge_brake_to_code(config, action_id, template_arg, args):
|
||||||
paren = await cg.get_variable(config[CONF_ID])
|
paren = await cg.get_variable(config[CONF_ID])
|
||||||
@ -56,13 +59,12 @@ async def fan_hbridge_brake_to_code(config, action_id, template_arg, args):
|
|||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(
|
var = await fan.new_fan(
|
||||||
config[CONF_ID],
|
config,
|
||||||
config[CONF_SPEED_COUNT],
|
config[CONF_SPEED_COUNT],
|
||||||
config[CONF_DECAY_MODE],
|
config[CONF_DECAY_MODE],
|
||||||
)
|
)
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await fan.register_fan(var, config)
|
|
||||||
pin_a_ = await cg.get_variable(config[CONF_PIN_A])
|
pin_a_ = await cg.get_variable(config[CONF_PIN_A])
|
||||||
cg.add(var.set_pin_a(pin_a_))
|
cg.add(var.set_pin_a(pin_a_))
|
||||||
pin_b_ = await cg.get_variable(config[CONF_PIN_B])
|
pin_b_ = await cg.get_variable(config[CONF_PIN_B])
|
||||||
|
@ -2,7 +2,6 @@ import esphome.codegen as cg
|
|||||||
from esphome.components import climate_ir
|
from esphome.components import climate_ir
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_ID,
|
|
||||||
CONF_MAX_TEMPERATURE,
|
CONF_MAX_TEMPERATURE,
|
||||||
CONF_MIN_TEMPERATURE,
|
CONF_MIN_TEMPERATURE,
|
||||||
CONF_PROTOCOL,
|
CONF_PROTOCOL,
|
||||||
@ -98,9 +97,8 @@ VERTICAL_DIRECTIONS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.All(
|
CONFIG_SCHEMA = cv.All(
|
||||||
climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
|
climate_ir.climare_ir_with_receiver_schema(HeatpumpIRClimate).extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(HeatpumpIRClimate),
|
|
||||||
cv.Required(CONF_PROTOCOL): cv.enum(PROTOCOLS),
|
cv.Required(CONF_PROTOCOL): cv.enum(PROTOCOLS),
|
||||||
cv.Required(CONF_HORIZONTAL_DEFAULT): cv.enum(HORIZONTAL_DIRECTIONS),
|
cv.Required(CONF_HORIZONTAL_DEFAULT): cv.enum(HORIZONTAL_DIRECTIONS),
|
||||||
cv.Required(CONF_VERTICAL_DEFAULT): cv.enum(VERTICAL_DIRECTIONS),
|
cv.Required(CONF_VERTICAL_DEFAULT): cv.enum(VERTICAL_DIRECTIONS),
|
||||||
@ -112,8 +110,8 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = await climate_ir.new_climate_ir(config)
|
||||||
if CONF_VISUAL not in config:
|
if CONF_VISUAL not in config:
|
||||||
config[CONF_VISUAL] = {}
|
config[CONF_VISUAL] = {}
|
||||||
visual = config[CONF_VISUAL]
|
visual = config[CONF_VISUAL]
|
||||||
@ -121,7 +119,6 @@ def to_code(config):
|
|||||||
visual[CONF_MAX_TEMPERATURE] = config[CONF_MAX_TEMPERATURE]
|
visual[CONF_MAX_TEMPERATURE] = config[CONF_MAX_TEMPERATURE]
|
||||||
if CONF_MIN_TEMPERATURE not in visual:
|
if CONF_MIN_TEMPERATURE not in visual:
|
||||||
visual[CONF_MIN_TEMPERATURE] = config[CONF_MIN_TEMPERATURE]
|
visual[CONF_MIN_TEMPERATURE] = config[CONF_MIN_TEMPERATURE]
|
||||||
yield climate_ir.register_climate_ir(var, config)
|
|
||||||
cg.add(var.set_protocol(config[CONF_PROTOCOL]))
|
cg.add(var.set_protocol(config[CONF_PROTOCOL]))
|
||||||
cg.add(var.set_horizontal_default(config[CONF_HORIZONTAL_DEFAULT]))
|
cg.add(var.set_horizontal_default(config[CONF_HORIZONTAL_DEFAULT]))
|
||||||
cg.add(var.set_vertical_default(config[CONF_VERTICAL_DEFAULT]))
|
cg.add(var.set_vertical_default(config[CONF_VERTICAL_DEFAULT]))
|
||||||
|
@ -1,20 +1,13 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import climate_ir
|
from esphome.components import climate_ir
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.const import CONF_ID
|
|
||||||
|
|
||||||
AUTO_LOAD = ["climate_ir"]
|
AUTO_LOAD = ["climate_ir"]
|
||||||
|
|
||||||
hitachi_ac344_ns = cg.esphome_ns.namespace("hitachi_ac344")
|
hitachi_ac344_ns = cg.esphome_ns.namespace("hitachi_ac344")
|
||||||
HitachiClimate = hitachi_ac344_ns.class_("HitachiClimate", climate_ir.ClimateIR)
|
HitachiClimate = hitachi_ac344_ns.class_("HitachiClimate", climate_ir.ClimateIR)
|
||||||
|
|
||||||
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
|
CONFIG_SCHEMA = climate_ir.climare_ir_with_receiver_schema(HitachiClimate)
|
||||||
{
|
|
||||||
cv.GenerateID(): cv.declare_id(HitachiClimate),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
await climate_ir.new_climate_ir(config)
|
||||||
await climate_ir.register_climate_ir(var, config)
|
|
||||||
|
@ -1,20 +1,13 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import climate_ir
|
from esphome.components import climate_ir
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.const import CONF_ID
|
|
||||||
|
|
||||||
AUTO_LOAD = ["climate_ir"]
|
AUTO_LOAD = ["climate_ir"]
|
||||||
|
|
||||||
hitachi_ac424_ns = cg.esphome_ns.namespace("hitachi_ac424")
|
hitachi_ac424_ns = cg.esphome_ns.namespace("hitachi_ac424")
|
||||||
HitachiClimate = hitachi_ac424_ns.class_("HitachiClimate", climate_ir.ClimateIR)
|
HitachiClimate = hitachi_ac424_ns.class_("HitachiClimate", climate_ir.ClimateIR)
|
||||||
|
|
||||||
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
|
CONFIG_SCHEMA = climate_ir.climare_ir_with_receiver_schema(HitachiClimate)
|
||||||
{
|
|
||||||
cv.GenerateID(): cv.declare_id(HitachiClimate),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
await climate_ir.new_climate_ir(config)
|
||||||
await climate_ir.register_climate_ir(var, config)
|
|
||||||
|
@ -2,7 +2,7 @@ from esphome import pins
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import esp32, media_player
|
from esphome.components import esp32, media_player
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ID, CONF_MODE
|
from esphome.const import CONF_MODE
|
||||||
|
|
||||||
from .. import (
|
from .. import (
|
||||||
CONF_I2S_AUDIO_ID,
|
CONF_I2S_AUDIO_ID,
|
||||||
@ -57,16 +57,17 @@ def validate_esp32_variant(config):
|
|||||||
CONFIG_SCHEMA = cv.All(
|
CONFIG_SCHEMA = cv.All(
|
||||||
cv.typed_schema(
|
cv.typed_schema(
|
||||||
{
|
{
|
||||||
"internal": media_player.MEDIA_PLAYER_SCHEMA.extend(
|
"internal": media_player.media_player_schema(I2SAudioMediaPlayer)
|
||||||
|
.extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(I2SAudioMediaPlayer),
|
|
||||||
cv.GenerateID(CONF_I2S_AUDIO_ID): cv.use_id(I2SAudioComponent),
|
cv.GenerateID(CONF_I2S_AUDIO_ID): cv.use_id(I2SAudioComponent),
|
||||||
cv.Required(CONF_MODE): cv.enum(INTERNAL_DAC_OPTIONS, lower=True),
|
cv.Required(CONF_MODE): cv.enum(INTERNAL_DAC_OPTIONS, lower=True),
|
||||||
}
|
}
|
||||||
).extend(cv.COMPONENT_SCHEMA),
|
)
|
||||||
"external": media_player.MEDIA_PLAYER_SCHEMA.extend(
|
.extend(cv.COMPONENT_SCHEMA),
|
||||||
|
"external": media_player.media_player_schema(I2SAudioMediaPlayer)
|
||||||
|
.extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(I2SAudioMediaPlayer),
|
|
||||||
cv.GenerateID(CONF_I2S_AUDIO_ID): cv.use_id(I2SAudioComponent),
|
cv.GenerateID(CONF_I2S_AUDIO_ID): cv.use_id(I2SAudioComponent),
|
||||||
cv.Required(
|
cv.Required(
|
||||||
CONF_I2S_DOUT_PIN
|
CONF_I2S_DOUT_PIN
|
||||||
@ -79,7 +80,8 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
*I2C_COMM_FMT_OPTIONS, lower=True
|
*I2C_COMM_FMT_OPTIONS, lower=True
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
).extend(cv.COMPONENT_SCHEMA),
|
)
|
||||||
|
.extend(cv.COMPONENT_SCHEMA),
|
||||||
},
|
},
|
||||||
key=CONF_DAC_TYPE,
|
key=CONF_DAC_TYPE,
|
||||||
),
|
),
|
||||||
@ -97,9 +99,8 @@ FINAL_VALIDATE_SCHEMA = _final_validate
|
|||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = await media_player.new_media_player(config)
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await media_player.register_media_player(var, config)
|
|
||||||
|
|
||||||
await cg.register_parented(var, config[CONF_I2S_AUDIO_ID])
|
await cg.register_parented(var, config[CONF_I2S_AUDIO_ID])
|
||||||
|
|
||||||
|
@ -2,6 +2,8 @@ from esphome import automation
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
|
CONF_ENTITY_CATEGORY,
|
||||||
|
CONF_ICON,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
CONF_ON_IDLE,
|
CONF_ON_IDLE,
|
||||||
CONF_ON_STATE,
|
CONF_ON_STATE,
|
||||||
@ -10,6 +12,7 @@ from esphome.const import (
|
|||||||
)
|
)
|
||||||
from esphome.core import CORE
|
from esphome.core import CORE
|
||||||
from esphome.coroutine import coroutine_with_priority
|
from esphome.coroutine import coroutine_with_priority
|
||||||
|
from esphome.cpp_generator import MockObjClass
|
||||||
from esphome.cpp_helpers import setup_entity
|
from esphome.cpp_helpers import setup_entity
|
||||||
|
|
||||||
CODEOWNERS = ["@jesserockz"]
|
CODEOWNERS = ["@jesserockz"]
|
||||||
@ -103,7 +106,13 @@ async def register_media_player(var, config):
|
|||||||
await setup_media_player_core_(var, config)
|
await setup_media_player_core_(var, config)
|
||||||
|
|
||||||
|
|
||||||
MEDIA_PLAYER_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(
|
async def new_media_player(config, *args):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID], *args)
|
||||||
|
await register_media_player(var, config)
|
||||||
|
return var
|
||||||
|
|
||||||
|
|
||||||
|
_MEDIA_PLAYER_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(
|
||||||
{
|
{
|
||||||
cv.Optional(CONF_ON_STATE): automation.validate_automation(
|
cv.Optional(CONF_ON_STATE): automation.validate_automation(
|
||||||
{
|
{
|
||||||
@ -134,6 +143,29 @@ MEDIA_PLAYER_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def media_player_schema(
|
||||||
|
class_: MockObjClass,
|
||||||
|
*,
|
||||||
|
entity_category: str = cv.UNDEFINED,
|
||||||
|
icon: str = cv.UNDEFINED,
|
||||||
|
) -> cv.Schema:
|
||||||
|
schema = {cv.GenerateID(CONF_ID): cv.declare_id(class_)}
|
||||||
|
|
||||||
|
for key, default, validator in [
|
||||||
|
(CONF_ENTITY_CATEGORY, entity_category, cv.entity_category),
|
||||||
|
(CONF_ICON, icon, cv.icon),
|
||||||
|
]:
|
||||||
|
if default is not cv.UNDEFINED:
|
||||||
|
schema[cv.Optional(key, default=default)] = validator
|
||||||
|
|
||||||
|
return _MEDIA_PLAYER_SCHEMA.extend(schema)
|
||||||
|
|
||||||
|
|
||||||
|
# Remove before 2025.11.0
|
||||||
|
MEDIA_PLAYER_SCHEMA = media_player_schema(MediaPlayer)
|
||||||
|
MEDIA_PLAYER_SCHEMA.add_extra(cv.deprecated_schema_constant("media_player"))
|
||||||
|
|
||||||
|
|
||||||
MEDIA_PLAYER_ACTION_SCHEMA = automation.maybe_simple_id(
|
MEDIA_PLAYER_ACTION_SCHEMA = automation.maybe_simple_id(
|
||||||
cv.Schema(
|
cv.Schema(
|
||||||
{
|
{
|
||||||
|
@ -104,9 +104,9 @@ validate_custom_fan_modes = cv.enum(CUSTOM_FAN_MODES, upper=True)
|
|||||||
validate_custom_presets = cv.enum(CUSTOM_PRESETS, upper=True)
|
validate_custom_presets = cv.enum(CUSTOM_PRESETS, upper=True)
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.All(
|
CONFIG_SCHEMA = cv.All(
|
||||||
climate.CLIMATE_SCHEMA.extend(
|
climate.climate_schema(AirConditioner)
|
||||||
|
.extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(AirConditioner),
|
|
||||||
cv.Optional(CONF_PERIOD, default="1s"): cv.time_period,
|
cv.Optional(CONF_PERIOD, default="1s"): cv.time_period,
|
||||||
cv.Optional(CONF_TIMEOUT, default="2s"): cv.time_period,
|
cv.Optional(CONF_TIMEOUT, default="2s"): cv.time_period,
|
||||||
cv.Optional(CONF_NUM_ATTEMPTS, default=3): cv.int_range(min=1, max=5),
|
cv.Optional(CONF_NUM_ATTEMPTS, default=3): cv.int_range(min=1, max=5),
|
||||||
@ -259,10 +259,9 @@ async def power_inv_to_code(var, config, args):
|
|||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = await climate.new_climate(config)
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await uart.register_uart_device(var, config)
|
await uart.register_uart_device(var, config)
|
||||||
await climate.register_climate(var, config)
|
|
||||||
cg.add(var.set_period(config[CONF_PERIOD].total_milliseconds))
|
cg.add(var.set_period(config[CONF_PERIOD].total_milliseconds))
|
||||||
cg.add(var.set_response_timeout(config[CONF_TIMEOUT].total_milliseconds))
|
cg.add(var.set_response_timeout(config[CONF_TIMEOUT].total_milliseconds))
|
||||||
cg.add(var.set_request_attempts(config[CONF_NUM_ATTEMPTS]))
|
cg.add(var.set_request_attempts(config[CONF_NUM_ATTEMPTS]))
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import climate_ir
|
from esphome.components import climate_ir
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ID, CONF_USE_FAHRENHEIT
|
from esphome.const import CONF_USE_FAHRENHEIT
|
||||||
|
|
||||||
AUTO_LOAD = ["climate_ir", "coolix"]
|
AUTO_LOAD = ["climate_ir", "coolix"]
|
||||||
CODEOWNERS = ["@dudanov"]
|
CODEOWNERS = ["@dudanov"]
|
||||||
@ -10,15 +10,13 @@ midea_ir_ns = cg.esphome_ns.namespace("midea_ir")
|
|||||||
MideaIR = midea_ir_ns.class_("MideaIR", climate_ir.ClimateIR)
|
MideaIR = midea_ir_ns.class_("MideaIR", climate_ir.ClimateIR)
|
||||||
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
|
CONFIG_SCHEMA = climate_ir.climare_ir_with_receiver_schema(MideaIR).extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(MideaIR),
|
|
||||||
cv.Optional(CONF_USE_FAHRENHEIT, default=False): cv.boolean,
|
cv.Optional(CONF_USE_FAHRENHEIT, default=False): cv.boolean,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = await climate_ir.new_climate_ir(config)
|
||||||
await climate_ir.register_climate_ir(var, config)
|
|
||||||
cg.add(var.set_fahrenheit(config[CONF_USE_FAHRENHEIT]))
|
cg.add(var.set_fahrenheit(config[CONF_USE_FAHRENHEIT]))
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import climate_ir
|
from esphome.components import climate_ir
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ID
|
|
||||||
|
|
||||||
CODEOWNERS = ["@RubyBailey"]
|
CODEOWNERS = ["@RubyBailey"]
|
||||||
AUTO_LOAD = ["climate_ir"]
|
AUTO_LOAD = ["climate_ir"]
|
||||||
@ -44,9 +43,8 @@ VERTICAL_DIRECTIONS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
|
CONFIG_SCHEMA = climate_ir.climare_ir_with_receiver_schema(MitsubishiClimate).extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(MitsubishiClimate),
|
|
||||||
cv.Optional(CONF_SET_FAN_MODE, default="3levels"): cv.enum(SETFANMODE),
|
cv.Optional(CONF_SET_FAN_MODE, default="3levels"): cv.enum(SETFANMODE),
|
||||||
cv.Optional(CONF_SUPPORTS_DRY, default=False): cv.boolean,
|
cv.Optional(CONF_SUPPORTS_DRY, default=False): cv.boolean,
|
||||||
cv.Optional(CONF_SUPPORTS_FAN_ONLY, default=False): cv.boolean,
|
cv.Optional(CONF_SUPPORTS_FAN_ONLY, default=False): cv.boolean,
|
||||||
@ -61,8 +59,7 @@ CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
|
|||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = await climate_ir.new_climate_ir(config)
|
||||||
await climate_ir.register_climate_ir(var, config)
|
|
||||||
|
|
||||||
cg.add(var.set_fan_mode(config[CONF_SET_FAN_MODE]))
|
cg.add(var.set_fan_mode(config[CONF_SET_FAN_MODE]))
|
||||||
cg.add(var.set_supports_dry(config[CONF_SUPPORTS_DRY]))
|
cg.add(var.set_supports_dry(config[CONF_SUPPORTS_DRY]))
|
||||||
|
@ -1,20 +1,13 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import climate_ir
|
from esphome.components import climate_ir
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.const import CONF_ID
|
|
||||||
|
|
||||||
AUTO_LOAD = ["climate_ir"]
|
AUTO_LOAD = ["climate_ir"]
|
||||||
|
|
||||||
noblex_ns = cg.esphome_ns.namespace("noblex")
|
noblex_ns = cg.esphome_ns.namespace("noblex")
|
||||||
NoblexClimate = noblex_ns.class_("NoblexClimate", climate_ir.ClimateIR)
|
NoblexClimate = noblex_ns.class_("NoblexClimate", climate_ir.ClimateIR)
|
||||||
|
|
||||||
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
|
CONFIG_SCHEMA = climate_ir.climare_ir_with_receiver_schema(NoblexClimate)
|
||||||
{
|
|
||||||
cv.GenerateID(): cv.declare_id(NoblexClimate),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
await climate_ir.new_climate_ir(config)
|
||||||
await climate_ir.register_climate_ir(var, config)
|
|
||||||
|
@ -41,9 +41,8 @@ CONF_KI_MULTIPLIER = "ki_multiplier"
|
|||||||
CONF_KD_MULTIPLIER = "kd_multiplier"
|
CONF_KD_MULTIPLIER = "kd_multiplier"
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.All(
|
CONFIG_SCHEMA = cv.All(
|
||||||
climate.CLIMATE_SCHEMA.extend(
|
climate.climate_schema(PIDClimate).extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(PIDClimate),
|
|
||||||
cv.Required(CONF_SENSOR): cv.use_id(sensor.Sensor),
|
cv.Required(CONF_SENSOR): cv.use_id(sensor.Sensor),
|
||||||
cv.Optional(CONF_HUMIDITY_SENSOR): cv.use_id(sensor.Sensor),
|
cv.Optional(CONF_HUMIDITY_SENSOR): cv.use_id(sensor.Sensor),
|
||||||
cv.Required(CONF_DEFAULT_TARGET_TEMPERATURE): cv.temperature,
|
cv.Required(CONF_DEFAULT_TARGET_TEMPERATURE): cv.temperature,
|
||||||
@ -80,9 +79,8 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = await climate.new_climate(config)
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await climate.register_climate(var, config)
|
|
||||||
|
|
||||||
sens = await cg.get_variable(config[CONF_SENSOR])
|
sens = await cg.get_variable(config[CONF_SENSOR])
|
||||||
cg.add(var.set_sensor(sens))
|
cg.add(var.set_sensor(sens))
|
||||||
|
@ -271,9 +271,8 @@ PIPELINE_SCHEMA = cv.Schema(
|
|||||||
)
|
)
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.All(
|
CONFIG_SCHEMA = cv.All(
|
||||||
media_player.MEDIA_PLAYER_SCHEMA.extend(
|
media_player.media_player_schema(SpeakerMediaPlayer).extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(SpeakerMediaPlayer),
|
|
||||||
cv.Required(CONF_ANNOUNCEMENT_PIPELINE): PIPELINE_SCHEMA,
|
cv.Required(CONF_ANNOUNCEMENT_PIPELINE): PIPELINE_SCHEMA,
|
||||||
cv.Optional(CONF_MEDIA_PIPELINE): PIPELINE_SCHEMA,
|
cv.Optional(CONF_MEDIA_PIPELINE): PIPELINE_SCHEMA,
|
||||||
cv.Optional(CONF_BUFFER_SIZE, default=1000000): cv.int_range(
|
cv.Optional(CONF_BUFFER_SIZE, default=1000000): cv.int_range(
|
||||||
@ -343,9 +342,8 @@ async def to_code(config):
|
|||||||
# Allocate wifi buffers in PSRAM
|
# Allocate wifi buffers in PSRAM
|
||||||
esp32.add_idf_sdkconfig_option("CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP", True)
|
esp32.add_idf_sdkconfig_option("CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP", True)
|
||||||
|
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = await media_player.new_media_player(config)
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await media_player.register_media_player(var, config)
|
|
||||||
|
|
||||||
cg.add_define("USE_OTA_STATE_CALLBACK")
|
cg.add_define("USE_OTA_STATE_CALLBACK")
|
||||||
|
|
||||||
|
@ -6,7 +6,6 @@ from esphome.const import (
|
|||||||
CONF_DIRECTION_OUTPUT,
|
CONF_DIRECTION_OUTPUT,
|
||||||
CONF_OSCILLATION_OUTPUT,
|
CONF_OSCILLATION_OUTPUT,
|
||||||
CONF_OUTPUT,
|
CONF_OUTPUT,
|
||||||
CONF_OUTPUT_ID,
|
|
||||||
CONF_PRESET_MODES,
|
CONF_PRESET_MODES,
|
||||||
CONF_SPEED,
|
CONF_SPEED,
|
||||||
CONF_SPEED_COUNT,
|
CONF_SPEED_COUNT,
|
||||||
@ -16,25 +15,27 @@ from .. import speed_ns
|
|||||||
|
|
||||||
SpeedFan = speed_ns.class_("SpeedFan", cg.Component, fan.Fan)
|
SpeedFan = speed_ns.class_("SpeedFan", cg.Component, fan.Fan)
|
||||||
|
|
||||||
CONFIG_SCHEMA = fan.FAN_SCHEMA.extend(
|
CONFIG_SCHEMA = (
|
||||||
{
|
fan.fan_schema(SpeedFan)
|
||||||
cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(SpeedFan),
|
.extend(
|
||||||
cv.Required(CONF_OUTPUT): cv.use_id(output.FloatOutput),
|
{
|
||||||
cv.Optional(CONF_OSCILLATION_OUTPUT): cv.use_id(output.BinaryOutput),
|
cv.Required(CONF_OUTPUT): cv.use_id(output.FloatOutput),
|
||||||
cv.Optional(CONF_DIRECTION_OUTPUT): cv.use_id(output.BinaryOutput),
|
cv.Optional(CONF_OSCILLATION_OUTPUT): cv.use_id(output.BinaryOutput),
|
||||||
cv.Optional(CONF_SPEED): cv.invalid(
|
cv.Optional(CONF_DIRECTION_OUTPUT): cv.use_id(output.BinaryOutput),
|
||||||
"Configuring individual speeds is deprecated."
|
cv.Optional(CONF_SPEED): cv.invalid(
|
||||||
),
|
"Configuring individual speeds is deprecated."
|
||||||
cv.Optional(CONF_SPEED_COUNT, default=100): cv.int_range(min=1),
|
),
|
||||||
cv.Optional(CONF_PRESET_MODES): validate_preset_modes,
|
cv.Optional(CONF_SPEED_COUNT, default=100): cv.int_range(min=1),
|
||||||
}
|
cv.Optional(CONF_PRESET_MODES): validate_preset_modes,
|
||||||
).extend(cv.COMPONENT_SCHEMA)
|
}
|
||||||
|
)
|
||||||
|
.extend(cv.COMPONENT_SCHEMA)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_OUTPUT_ID], config[CONF_SPEED_COUNT])
|
var = await fan.new_fan(config, config[CONF_SPEED_COUNT])
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await fan.register_fan(var, config)
|
|
||||||
|
|
||||||
output_ = await cg.get_variable(config[CONF_OUTPUT])
|
output_ = await cg.get_variable(config[CONF_OUTPUT])
|
||||||
cg.add(var.set_output(output_))
|
cg.add(var.set_output(output_))
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import climate_ir
|
from esphome.components import climate_ir
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.const import CONF_ID
|
|
||||||
|
|
||||||
AUTO_LOAD = ["climate_ir"]
|
AUTO_LOAD = ["climate_ir"]
|
||||||
CODEOWNERS = ["@glmnet"]
|
CODEOWNERS = ["@glmnet"]
|
||||||
@ -9,13 +7,8 @@ CODEOWNERS = ["@glmnet"]
|
|||||||
tcl112_ns = cg.esphome_ns.namespace("tcl112")
|
tcl112_ns = cg.esphome_ns.namespace("tcl112")
|
||||||
Tcl112Climate = tcl112_ns.class_("Tcl112Climate", climate_ir.ClimateIR)
|
Tcl112Climate = tcl112_ns.class_("Tcl112Climate", climate_ir.ClimateIR)
|
||||||
|
|
||||||
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
|
CONFIG_SCHEMA = climate_ir.climare_ir_with_receiver_schema(Tcl112Climate)
|
||||||
{
|
|
||||||
cv.GenerateID(): cv.declare_id(Tcl112Climate),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
await climate_ir.new_climate_ir(config)
|
||||||
await climate_ir.register_climate_ir(var, config)
|
|
||||||
|
@ -2,7 +2,7 @@ import esphome.codegen as cg
|
|||||||
from esphome.components import fan
|
from esphome.components import fan
|
||||||
from esphome.components.fan import validate_preset_modes
|
from esphome.components.fan import validate_preset_modes
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_OUTPUT_ID, CONF_PRESET_MODES, CONF_SPEED_COUNT
|
from esphome.const import CONF_PRESET_MODES, CONF_SPEED_COUNT
|
||||||
|
|
||||||
from .. import template_ns
|
from .. import template_ns
|
||||||
|
|
||||||
@ -13,21 +13,23 @@ TemplateFan = template_ns.class_("TemplateFan", cg.Component, fan.Fan)
|
|||||||
CONF_HAS_DIRECTION = "has_direction"
|
CONF_HAS_DIRECTION = "has_direction"
|
||||||
CONF_HAS_OSCILLATING = "has_oscillating"
|
CONF_HAS_OSCILLATING = "has_oscillating"
|
||||||
|
|
||||||
CONFIG_SCHEMA = fan.FAN_SCHEMA.extend(
|
CONFIG_SCHEMA = (
|
||||||
{
|
fan.fan_schema(TemplateFan)
|
||||||
cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(TemplateFan),
|
.extend(
|
||||||
cv.Optional(CONF_HAS_DIRECTION, default=False): cv.boolean,
|
{
|
||||||
cv.Optional(CONF_HAS_OSCILLATING, default=False): cv.boolean,
|
cv.Optional(CONF_HAS_DIRECTION, default=False): cv.boolean,
|
||||||
cv.Optional(CONF_SPEED_COUNT): cv.int_range(min=1),
|
cv.Optional(CONF_HAS_OSCILLATING, default=False): cv.boolean,
|
||||||
cv.Optional(CONF_PRESET_MODES): validate_preset_modes,
|
cv.Optional(CONF_SPEED_COUNT): cv.int_range(min=1),
|
||||||
}
|
cv.Optional(CONF_PRESET_MODES): validate_preset_modes,
|
||||||
).extend(cv.COMPONENT_SCHEMA)
|
}
|
||||||
|
)
|
||||||
|
.extend(cv.COMPONENT_SCHEMA)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_OUTPUT_ID])
|
var = await fan.new_fan(config)
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await fan.register_fan(var, config)
|
|
||||||
|
|
||||||
cg.add(var.set_has_direction(config[CONF_HAS_DIRECTION]))
|
cg.add(var.set_has_direction(config[CONF_HAS_DIRECTION]))
|
||||||
cg.add(var.set_has_oscillating(config[CONF_HAS_OSCILLATING]))
|
cg.add(var.set_has_oscillating(config[CONF_HAS_OSCILLATING]))
|
||||||
|
@ -516,9 +516,9 @@ def validate_thermostat(config):
|
|||||||
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.All(
|
CONFIG_SCHEMA = cv.All(
|
||||||
climate.CLIMATE_SCHEMA.extend(
|
climate.climate_schema(ThermostatClimate)
|
||||||
|
.extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(ThermostatClimate),
|
|
||||||
cv.Required(CONF_SENSOR): cv.use_id(sensor.Sensor),
|
cv.Required(CONF_SENSOR): cv.use_id(sensor.Sensor),
|
||||||
cv.Optional(CONF_HUMIDITY_SENSOR): cv.use_id(sensor.Sensor),
|
cv.Optional(CONF_HUMIDITY_SENSOR): cv.use_id(sensor.Sensor),
|
||||||
cv.Required(CONF_IDLE_ACTION): automation.validate_automation(single=True),
|
cv.Required(CONF_IDLE_ACTION): automation.validate_automation(single=True),
|
||||||
@ -631,7 +631,8 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
single=True
|
single=True
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
).extend(cv.COMPONENT_SCHEMA),
|
)
|
||||||
|
.extend(cv.COMPONENT_SCHEMA),
|
||||||
cv.has_at_least_one_key(
|
cv.has_at_least_one_key(
|
||||||
CONF_COOL_ACTION, CONF_DRY_ACTION, CONF_FAN_ONLY_ACTION, CONF_HEAT_ACTION
|
CONF_COOL_ACTION, CONF_DRY_ACTION, CONF_FAN_ONLY_ACTION, CONF_HEAT_ACTION
|
||||||
),
|
),
|
||||||
@ -640,9 +641,8 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = await climate.new_climate(config)
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await climate.register_climate(var, config)
|
|
||||||
|
|
||||||
heat_cool_mode_available = CONF_HEAT_ACTION in config and CONF_COOL_ACTION in config
|
heat_cool_mode_available = CONF_HEAT_ACTION in config and CONF_COOL_ACTION in config
|
||||||
two_points_available = CONF_HEAT_ACTION in config and (
|
two_points_available = CONF_HEAT_ACTION in config and (
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import climate_ir
|
from esphome.components import climate_ir
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ID, CONF_MODEL
|
from esphome.const import CONF_MODEL
|
||||||
|
|
||||||
AUTO_LOAD = ["climate_ir"]
|
AUTO_LOAD = ["climate_ir"]
|
||||||
CODEOWNERS = ["@kbx81"]
|
CODEOWNERS = ["@kbx81"]
|
||||||
@ -16,15 +16,13 @@ MODELS = {
|
|||||||
"RAC-PT1411HWRU-F": Model.MODEL_RAC_PT1411HWRU_F,
|
"RAC-PT1411HWRU-F": Model.MODEL_RAC_PT1411HWRU_F,
|
||||||
}
|
}
|
||||||
|
|
||||||
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
|
CONFIG_SCHEMA = climate_ir.climare_ir_with_receiver_schema(ToshibaClimate).extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(ToshibaClimate),
|
|
||||||
cv.Optional(CONF_MODEL, default="generic"): cv.enum(MODELS, upper=True),
|
cv.Optional(CONF_MODEL, default="generic"): cv.enum(MODELS, upper=True),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = await climate_ir.new_climate_ir(config)
|
||||||
await climate_ir.register_climate_ir(var, config)
|
|
||||||
cg.add(var.set_model(config[CONF_MODEL]))
|
cg.add(var.set_model(config[CONF_MODEL]))
|
||||||
|
@ -4,7 +4,6 @@ from esphome.components import climate
|
|||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_FAN_MODE,
|
CONF_FAN_MODE,
|
||||||
CONF_ID,
|
|
||||||
CONF_PRESET,
|
CONF_PRESET,
|
||||||
CONF_SUPPORTS_COOL,
|
CONF_SUPPORTS_COOL,
|
||||||
CONF_SUPPORTS_HEAT,
|
CONF_SUPPORTS_HEAT,
|
||||||
@ -151,9 +150,9 @@ SWING_MODES = cv.Schema(
|
|||||||
)
|
)
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.All(
|
CONFIG_SCHEMA = cv.All(
|
||||||
climate.CLIMATE_SCHEMA.extend(
|
climate.climate_schema(TuyaClimate)
|
||||||
|
.extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(TuyaClimate),
|
|
||||||
cv.GenerateID(CONF_TUYA_ID): cv.use_id(Tuya),
|
cv.GenerateID(CONF_TUYA_ID): cv.use_id(Tuya),
|
||||||
cv.Optional(CONF_SUPPORTS_HEAT, default=True): cv.boolean,
|
cv.Optional(CONF_SUPPORTS_HEAT, default=True): cv.boolean,
|
||||||
cv.Optional(CONF_SUPPORTS_COOL, default=False): cv.boolean,
|
cv.Optional(CONF_SUPPORTS_COOL, default=False): cv.boolean,
|
||||||
@ -186,7 +185,8 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
"'eco_temperature' has been moved inside of the 'eco' config block under 'preset' as 'temperature'"
|
"'eco_temperature' has been moved inside of the 'eco' config block under 'preset' as 'temperature'"
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
).extend(cv.COMPONENT_SCHEMA),
|
)
|
||||||
|
.extend(cv.COMPONENT_SCHEMA),
|
||||||
cv.has_at_least_one_key(CONF_TARGET_TEMPERATURE_DATAPOINT, CONF_SWITCH_DATAPOINT),
|
cv.has_at_least_one_key(CONF_TARGET_TEMPERATURE_DATAPOINT, CONF_SWITCH_DATAPOINT),
|
||||||
validate_temperature_multipliers,
|
validate_temperature_multipliers,
|
||||||
validate_cooling_values,
|
validate_cooling_values,
|
||||||
@ -194,9 +194,8 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = await climate.new_climate(config)
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await climate.register_climate(var, config)
|
|
||||||
|
|
||||||
paren = await cg.get_variable(config[CONF_TUYA_ID])
|
paren = await cg.get_variable(config[CONF_TUYA_ID])
|
||||||
cg.add(var.set_tuya_parent(paren))
|
cg.add(var.set_tuya_parent(paren))
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import climate
|
from esphome.components import climate
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.const import CONF_ID
|
|
||||||
|
|
||||||
from .. import (
|
from .. import (
|
||||||
UPONOR_SMATRIX_DEVICE_SCHEMA,
|
UPONOR_SMATRIX_DEVICE_SCHEMA,
|
||||||
@ -19,15 +17,12 @@ UponorSmatrixClimate = uponor_smatrix_ns.class_(
|
|||||||
UponorSmatrixDevice,
|
UponorSmatrixDevice,
|
||||||
)
|
)
|
||||||
|
|
||||||
CONFIG_SCHEMA = climate.CLIMATE_SCHEMA.extend(
|
CONFIG_SCHEMA = climate.climate_schema(UponorSmatrixClimate).extend(
|
||||||
{
|
UPONOR_SMATRIX_DEVICE_SCHEMA
|
||||||
cv.GenerateID(): cv.declare_id(UponorSmatrixClimate),
|
)
|
||||||
}
|
|
||||||
).extend(UPONOR_SMATRIX_DEVICE_SCHEMA)
|
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = await climate.new_climate(config)
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await climate.register_climate(var, config)
|
|
||||||
await register_uponor_smatrix_device(var, config)
|
await register_uponor_smatrix_device(var, config)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import climate_ir
|
from esphome.components import climate_ir
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ID, CONF_MODEL
|
from esphome.const import CONF_MODEL
|
||||||
|
|
||||||
AUTO_LOAD = ["climate_ir"]
|
AUTO_LOAD = ["climate_ir"]
|
||||||
CODEOWNERS = ["@glmnet"]
|
CODEOWNERS = ["@glmnet"]
|
||||||
@ -15,15 +15,13 @@ MODELS = {
|
|||||||
"DG11J1-91": Model.MODEL_DG11J1_91,
|
"DG11J1-91": Model.MODEL_DG11J1_91,
|
||||||
}
|
}
|
||||||
|
|
||||||
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
|
CONFIG_SCHEMA = climate_ir.climare_ir_with_receiver_schema(WhirlpoolClimate).extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(WhirlpoolClimate),
|
|
||||||
cv.Optional(CONF_MODEL, default="DG11J1-3A"): cv.enum(MODELS, upper=True),
|
cv.Optional(CONF_MODEL, default="DG11J1-3A"): cv.enum(MODELS, upper=True),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = await climate_ir.new_climate_ir(config)
|
||||||
await climate_ir.register_climate_ir(var, config)
|
|
||||||
cg.add(var.set_model(config[CONF_MODEL]))
|
cg.add(var.set_model(config[CONF_MODEL]))
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import climate_ir
|
from esphome.components import climate_ir
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ID, CONF_USE_FAHRENHEIT
|
from esphome.const import CONF_USE_FAHRENHEIT
|
||||||
|
|
||||||
AUTO_LOAD = ["climate_ir"]
|
AUTO_LOAD = ["climate_ir"]
|
||||||
|
|
||||||
@ -9,15 +9,13 @@ whynter_ns = cg.esphome_ns.namespace("whynter")
|
|||||||
Whynter = whynter_ns.class_("Whynter", climate_ir.ClimateIR)
|
Whynter = whynter_ns.class_("Whynter", climate_ir.ClimateIR)
|
||||||
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
|
CONFIG_SCHEMA = climate_ir.climare_ir_with_receiver_schema(Whynter).extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(Whynter),
|
|
||||||
cv.Optional(CONF_USE_FAHRENHEIT, default=False): cv.boolean,
|
cv.Optional(CONF_USE_FAHRENHEIT, default=False): cv.boolean,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = await climate_ir.new_climate_ir(config)
|
||||||
await climate_ir.register_climate_ir(var, config)
|
|
||||||
cg.add(var.set_fahrenheit(config[CONF_USE_FAHRENHEIT]))
|
cg.add(var.set_fahrenheit(config[CONF_USE_FAHRENHEIT]))
|
||||||
|
@ -2,7 +2,7 @@ import esphome.codegen as cg
|
|||||||
from esphome.components import climate, remote_transmitter, sensor
|
from esphome.components import climate, remote_transmitter, sensor
|
||||||
from esphome.components.remote_base import CONF_TRANSMITTER_ID
|
from esphome.components.remote_base import CONF_TRANSMITTER_ID
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ID, CONF_SENSOR, CONF_SUPPORTS_COOL, CONF_SUPPORTS_HEAT
|
from esphome.const import CONF_SENSOR, CONF_SUPPORTS_COOL, CONF_SUPPORTS_HEAT
|
||||||
|
|
||||||
AUTO_LOAD = ["sensor"]
|
AUTO_LOAD = ["sensor"]
|
||||||
|
|
||||||
@ -10,9 +10,9 @@ yashima_ns = cg.esphome_ns.namespace("yashima")
|
|||||||
YashimaClimate = yashima_ns.class_("YashimaClimate", climate.Climate, cg.Component)
|
YashimaClimate = yashima_ns.class_("YashimaClimate", climate.Climate, cg.Component)
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.All(
|
CONFIG_SCHEMA = cv.All(
|
||||||
climate.CLIMATE_SCHEMA.extend(
|
climate.climate_schema(YashimaClimate)
|
||||||
|
.extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(YashimaClimate),
|
|
||||||
cv.GenerateID(CONF_TRANSMITTER_ID): cv.use_id(
|
cv.GenerateID(CONF_TRANSMITTER_ID): cv.use_id(
|
||||||
remote_transmitter.RemoteTransmitterComponent
|
remote_transmitter.RemoteTransmitterComponent
|
||||||
),
|
),
|
||||||
@ -20,14 +20,14 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
cv.Optional(CONF_SUPPORTS_HEAT, default=True): cv.boolean,
|
cv.Optional(CONF_SUPPORTS_HEAT, default=True): cv.boolean,
|
||||||
cv.Optional(CONF_SENSOR): cv.use_id(sensor.Sensor),
|
cv.Optional(CONF_SENSOR): cv.use_id(sensor.Sensor),
|
||||||
}
|
}
|
||||||
).extend(cv.COMPONENT_SCHEMA)
|
)
|
||||||
|
.extend(cv.COMPONENT_SCHEMA)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = await climate.new_climate(config)
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await climate.register_climate(var, config)
|
|
||||||
|
|
||||||
cg.add(var.set_supports_cool(config[CONF_SUPPORTS_COOL]))
|
cg.add(var.set_supports_cool(config[CONF_SUPPORTS_COOL]))
|
||||||
cg.add(var.set_supports_heat(config[CONF_SUPPORTS_HEAT]))
|
cg.add(var.set_supports_heat(config[CONF_SUPPORTS_HEAT]))
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import climate_ir
|
from esphome.components import climate_ir
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.const import CONF_ID
|
|
||||||
|
|
||||||
AUTO_LOAD = ["climate_ir"]
|
AUTO_LOAD = ["climate_ir"]
|
||||||
CODEOWNERS = ["@cfeenstra1024"]
|
CODEOWNERS = ["@cfeenstra1024"]
|
||||||
@ -9,11 +7,8 @@ CODEOWNERS = ["@cfeenstra1024"]
|
|||||||
zhlt01_ns = cg.esphome_ns.namespace("zhlt01")
|
zhlt01_ns = cg.esphome_ns.namespace("zhlt01")
|
||||||
ZHLT01Climate = zhlt01_ns.class_("ZHLT01Climate", climate_ir.ClimateIR)
|
ZHLT01Climate = zhlt01_ns.class_("ZHLT01Climate", climate_ir.ClimateIR)
|
||||||
|
|
||||||
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
|
CONFIG_SCHEMA = climate_ir.climare_ir_with_receiver_schema(ZHLT01Climate)
|
||||||
{cv.GenerateID(): cv.declare_id(ZHLT01Climate)}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
await climate_ir.new_climate_ir(config)
|
||||||
await climate_ir.register_climate_ir(var, config)
|
|
||||||
|
@ -2077,14 +2077,20 @@ def rename_key(old_key, new_key):
|
|||||||
# Remove before 2025.11.0
|
# Remove before 2025.11.0
|
||||||
def deprecated_schema_constant(entity_type: str):
|
def deprecated_schema_constant(entity_type: str):
|
||||||
def validator(config):
|
def validator(config):
|
||||||
|
type: str = "unknown"
|
||||||
|
if (id := config.get(CONF_ID)) is not None and isinstance(id, core.ID):
|
||||||
|
type = str(id.type).split("::", maxsplit=1)[0]
|
||||||
_LOGGER.warning(
|
_LOGGER.warning(
|
||||||
"Using `%s.%s_SCHEMA` is deprecated and will be removed in ESPHome 2025.11.0. "
|
"Using `%s.%s_SCHEMA` is deprecated and will be removed in ESPHome 2025.11.0. "
|
||||||
"Please use `%s.%s_schema(...)` instead. "
|
"Please use `%s.%s_schema(...)` instead. "
|
||||||
"If you are seeing this, report an issue to the external_component author and ask them to update it.",
|
"If you are seeing this, report an issue to the external_component author and ask them to update it. "
|
||||||
|
"https://developers.esphome.io/blog/2025/05/14/_schema-deprecations/. "
|
||||||
|
"Component using this schema: %s",
|
||||||
entity_type,
|
entity_type,
|
||||||
entity_type.upper(),
|
entity_type.upper(),
|
||||||
entity_type,
|
entity_type,
|
||||||
entity_type,
|
entity_type,
|
||||||
|
type,
|
||||||
)
|
)
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
"""Constants used by esphome."""
|
"""Constants used by esphome."""
|
||||||
|
|
||||||
__version__ = "2025.5.0b1"
|
__version__ = "2025.5.0b2"
|
||||||
|
|
||||||
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
|
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
|
||||||
VALID_SUBSTITUTIONS_CHARACTERS = (
|
VALID_SUBSTITUTIONS_CHARACTERS = (
|
||||||
|
@ -12,7 +12,7 @@ pyserial==3.5
|
|||||||
platformio==6.1.18 # When updating platformio, also update /docker/Dockerfile
|
platformio==6.1.18 # When updating platformio, also update /docker/Dockerfile
|
||||||
esptool==4.8.1
|
esptool==4.8.1
|
||||||
click==8.1.7
|
click==8.1.7
|
||||||
esphome-dashboard==20250415.0
|
esphome-dashboard==20250514.0
|
||||||
aioesphomeapi==30.2.0
|
aioesphomeapi==30.2.0
|
||||||
zeroconf==0.147.0
|
zeroconf==0.147.0
|
||||||
puremagic==1.29
|
puremagic==1.29
|
||||||
|
Loading…
x
Reference in New Issue
Block a user