Merge pull request #6547 from esphome/bump-2024.4.0

2024.4.0
This commit is contained in:
Jesse Hills
2024-04-17 14:13:43 +12:00
committed by GitHub
853 changed files with 22264 additions and 771 deletions
+2 -2
View File
@@ -36,7 +36,7 @@ runs:
- name: Build and push to ghcr by digest
id: build-ghcr
uses: docker/build-push-action@v5.2.0
uses: docker/build-push-action@v5.3.0
with:
context: .
file: ./docker/Dockerfile
@@ -67,7 +67,7 @@ runs:
- name: Build and push to dockerhub by digest
id: build-dockerhub
uses: docker/build-push-action@v5.2.0
uses: docker/build-push-action@v5.3.0
with:
context: .
file: ./docker/Dockerfile
+2 -2
View File
@@ -17,12 +17,12 @@ runs:
steps:
- name: Set up Python ${{ inputs.python-version }}
id: python
uses: actions/setup-python@v5.0.0
uses: actions/setup-python@v5.1.0
with:
python-version: ${{ inputs.python-version }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache/restore@v4.0.1
uses: actions/cache/restore@v4.0.2
with:
path: venv
# yamllint disable-line rule:line-length
+80
View File
@@ -0,0 +1,80 @@
name: API Proto CI
on:
pull_request:
paths:
- "esphome/components/api/api.proto"
- "esphome/components/api/api_pb2.cpp"
- "esphome/components/api/api_pb2.h"
- "esphome/components/api/api_pb2_service.cpp"
- "esphome/components/api/api_pb2_service.h"
- "script/api_protobuf/api_protobuf.py"
- ".github/workflows/ci-api-proto.yml"
permissions:
contents: read
pull-requests: write
jobs:
check:
name: Check generated files
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4.1.1
- name: Set up Python
uses: actions/setup-python@v5.1.0
with:
python-version: "3.11"
- name: Install apt dependencies
run: |
sudo apt update
sudo apt-cache show protobuf-compiler
sudo apt install -y protobuf-compiler
protoc --version
- name: Install python dependencies
run: pip install aioesphomeapi -c requirements.txt -r requirements_dev.txt
- name: Generate files
run: script/api_protobuf/api_protobuf.py
- name: Check for changes
run: |
if ! git diff --quiet; then
echo "## Job Failed" | tee -a $GITHUB_STEP_SUMMARY
echo "You have altered the generated proto files but they do not match what is expected." | tee -a $GITHUB_STEP_SUMMARY
echo "Please run 'script/api_protobuf/api_protobuf.py' and commit the changes." | tee -a $GITHUB_STEP_SUMMARY
exit 1
fi
- if: failure()
name: Review PR
uses: actions/github-script@v7.0.1
with:
script: |
await github.rest.pulls.createReview({
pull_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
event: 'REQUEST_CHANGES',
body: 'You have altered the generated proto files but they do not match what is expected.\nPlease run "script/api_protobuf/api_protobuf.py" and commit the changes.'
})
- if: success()
name: Dismiss review
uses: actions/github-script@v7.0.1
with:
script: |
let reviews = await github.rest.pulls.listReviews({
pull_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo
});
for (let review of reviews.data) {
if (review.user.login === 'github-actions[bot]' && review.state === 'CHANGES_REQUESTED') {
await github.rest.pulls.dismissReview({
pull_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
review_id: review.id,
message: 'Files now match the expected proto files.'
});
}
}
+3 -3
View File
@@ -2,7 +2,7 @@
name: CI for docker images
# Only run when docker paths change
# yamllint disable-line rule:truthy
on:
push:
branches: [dev, beta, release]
@@ -42,11 +42,11 @@ jobs:
steps:
- uses: actions/checkout@v4.1.1
- name: Set up Python
uses: actions/setup-python@v5.0.0
uses: actions/setup-python@v5.1.0
with:
python-version: "3.9"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3.1.0
uses: docker/setup-buildx-action@v3.3.0
- name: Set up QEMU
uses: docker/setup-qemu-action@v3.0.0
+3 -4
View File
@@ -1,7 +1,6 @@
---
name: CI
# yamllint disable-line rule:truthy
on:
push:
branches: [dev, beta, release]
@@ -42,12 +41,12 @@ jobs:
run: echo key="${{ hashFiles('requirements.txt', 'requirements_optional.txt', 'requirements_test.txt') }}" >> $GITHUB_OUTPUT
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
id: python
uses: actions/setup-python@v5.0.0
uses: actions/setup-python@v5.1.0
with:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@v4.0.1
uses: actions/cache@v4.0.2
with:
path: venv
# yamllint disable-line rule:line-length
@@ -367,7 +366,7 @@ jobs:
python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }}
- name: Cache platformio
uses: actions/cache@v4.0.1
uses: actions/cache@v4.0.2
with:
path: ~/.platformio
# yamllint disable-line rule:line-length
-1
View File
@@ -1,7 +1,6 @@
---
name: Lock
# yamllint disable-line rule:truthy
on:
schedule:
- cron: "30 0 * * *"
-1
View File
@@ -1,6 +1,5 @@
name: Needs Docs
# yamllint disable-line rule:truthy
on:
pull_request:
types: [labeled, unlabeled]
+8 -9
View File
@@ -1,7 +1,6 @@
---
name: Publish Release
# yamllint disable-line rule:truthy
on:
workflow_dispatch:
release:
@@ -45,7 +44,7 @@ jobs:
steps:
- uses: actions/checkout@v4.1.1
- name: Set up Python
uses: actions/setup-python@v5.0.0
uses: actions/setup-python@v5.1.0
with:
python-version: "3.x"
- name: Set up python environment
@@ -80,23 +79,23 @@ jobs:
steps:
- uses: actions/checkout@v4.1.1
- name: Set up Python
uses: actions/setup-python@v5.0.0
uses: actions/setup-python@v5.1.0
with:
python-version: "3.9"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3.1.0
uses: docker/setup-buildx-action@v3.3.0
- name: Set up QEMU
if: matrix.platform != 'linux/amd64'
uses: docker/setup-qemu-action@v3.0.0
- name: Log in to docker hub
uses: docker/login-action@v3.0.0
uses: docker/login-action@v3.1.0
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Log in to the GitHub container registry
uses: docker/login-action@v3.0.0
uses: docker/login-action@v3.1.0
with:
registry: ghcr.io
username: ${{ github.actor }}
@@ -163,17 +162,17 @@ jobs:
name: digests-${{ matrix.image.target }}-${{ matrix.registry }}
path: /tmp/digests
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3.1.0
uses: docker/setup-buildx-action@v3.3.0
- name: Log in to docker hub
if: matrix.registry == 'dockerhub'
uses: docker/login-action@v3.0.0
uses: docker/login-action@v3.1.0
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Log in to the GitHub container registry
if: matrix.registry == 'ghcr'
uses: docker/login-action@v3.0.0
uses: docker/login-action@v3.1.0
with:
registry: ghcr.io
username: ${{ github.actor }}
-1
View File
@@ -1,7 +1,6 @@
---
name: Stale
# yamllint disable-line rule:truthy
on:
schedule:
- cron: "30 0 * * *"
+2 -3
View File
@@ -1,7 +1,6 @@
---
name: Synchronise Device Classes from Home Assistant
# yamllint disable-line rule:truthy
on:
workflow_dispatch:
schedule:
@@ -23,7 +22,7 @@ jobs:
path: lib/home-assistant
- name: Setup Python
uses: actions/setup-python@v5.0.0
uses: actions/setup-python@v5.1.0
with:
python-version: 3.11
@@ -37,7 +36,7 @@ jobs:
python ./script/sync-device_class.py
- name: Commit changes
uses: peter-evans/create-pull-request@v6.0.1
uses: peter-evans/create-pull-request@v6.0.2
with:
commit-message: "Synchronise Device Classes from Home Assistant"
committer: esphomebot <esphome@nabucasa.com>
-1
View File
@@ -1,7 +1,6 @@
---
name: YAML lint
# yamllint disable-line rule:truthy
on:
push:
branches: [dev, beta, release]
+1
View File
@@ -16,3 +16,4 @@ rules:
indent-sequences: true
check-multi-line-strings: false
line-length: disable
truthy: disable
+9 -1
View File
@@ -42,6 +42,7 @@ esphome/components/as5600/* @ammmze
esphome/components/as5600/sensor/* @ammmze
esphome/components/as7341/* @mrgnr
esphome/components/async_tcp/* @OttoWinter
esphome/components/at581x/* @X-Ryl669
esphome/components/atc_mithermometer/* @ahpohl
esphome/components/atm90e26/* @danieltwagner
esphome/components/b_parasite/* @rbaron
@@ -86,10 +87,11 @@ esphome/components/cst816/* @clydebarrow
esphome/components/ct_clamp/* @jesserockz
esphome/components/current_based/* @djwmarcx
esphome/components/dac7678/* @NickB1
esphome/components/daikin_arc/* @MagicBear
esphome/components/daikin_brc/* @hagak
esphome/components/daly_bms/* @s1lvi0
esphome/components/dashboard_import/* @esphome/core
esphome/components/datetime/* @rfdarter
esphome/components/datetime/* @jesserockz @rfdarter
esphome/components/debug/* @OttoWinter
esphome/components/delonghi/* @grob6000
esphome/components/dfplayer/* @glmnet
@@ -113,6 +115,7 @@ esphome/components/esp32_ble_server/* @Rapsssito @clydebarrow @jesserockz
esphome/components/esp32_camera_web_server/* @ayufan
esphome/components/esp32_can/* @Sympatron
esphome/components/esp32_improv/* @jesserockz
esphome/components/esp32_rmt/* @jesserockz
esphome/components/esp32_rmt_led_strip/* @jesserockz
esphome/components/esp8266/* @esphome/core
esphome/components/ethernet_info/* @gtjadsonsantos
@@ -172,6 +175,7 @@ esphome/components/inkplate6/* @jesserockz
esphome/components/integration/* @OttoWinter
esphome/components/internal_temperature/* @Mat931
esphome/components/interval/* @esphome/core
esphome/components/jsn_sr04t/* @Mafus1
esphome/components/json/* @OttoWinter
esphome/components/kamstrup_kmp/* @cfeenstra1024
esphome/components/key_collector/* @ssieb
@@ -306,6 +310,7 @@ esphome/components/sfa30/* @ghsensdev
esphome/components/sgp40/* @SenexCrenshaw
esphome/components/sgp4x/* @SenexCrenshaw @martgras
esphome/components/shelly_dimmer/* @edge90 @rnauber
esphome/components/sht3xd/* @mrtoy-me
esphome/components/sht4x/* @sjtrny
esphome/components/shutdown/* @esphome/core @jsuanet
esphome/components/sigma_delta_output/* @Cat-Ion
@@ -345,6 +350,7 @@ esphome/components/st7789v/* @kbx81
esphome/components/st7920/* @marsjan155
esphome/components/substitutions/* @esphome/core
esphome/components/sun/* @OttoWinter
esphome/components/sun_gtil2/* @Mat931
esphome/components/switch/* @esphome/core
esphome/components/t6615/* @tylermenezes
esphome/components/tca9548a/* @andreashergert1984
@@ -358,6 +364,7 @@ esphome/components/text/* @mauritskorse
esphome/components/thermostat/* @kbx81
esphome/components/time/* @OttoWinter
esphome/components/tlc5947/* @rnauber
esphome/components/tlc5971/* @IJIJI
esphome/components/tm1621/* @Philippe12
esphome/components/tm1637/* @glmnet
esphome/components/tm1638/* @skykingjwc
@@ -399,6 +406,7 @@ esphome/components/wireguard/* @droscy @lhoracek @thomas0bernard
esphome/components/wl_134/* @hobbypunk90
esphome/components/x9c/* @EtienneMD
esphome/components/xgzp68xx/* @gcormier
esphome/components/xiaomi_hhccjcy10/* @fariouche
esphome/components/xiaomi_lywsd03mmc/* @ahpohl
esphome/components/xiaomi_mhoc303/* @drug123
esphome/components/xiaomi_mhoc401/* @vevsvevs
+2 -1
View File
@@ -4,13 +4,14 @@ from esphome.components import i2c
from esphome.const import CONF_ID
DEPENDENCIES = ["i2c"]
AUTO_LOAD = ["sensor", "voltage_sampler"]
MULTI_CONF = True
ads1115_ns = cg.esphome_ns.namespace("ads1115")
ADS1115Component = ads1115_ns.class_("ADS1115Component", cg.Component, i2c.I2CDevice)
CONF_CONTINUOUS_MODE = "continuous_mode"
CONF_ADS1115_ID = "ads1115_id"
CONFIG_SCHEMA = (
cv.Schema(
{
+8 -23
View File
@@ -1,6 +1,6 @@
#include "ads1115.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
#include "esphome/core/log.h"
namespace esphome {
namespace ads1115 {
@@ -75,25 +75,19 @@ void ADS1115Component::dump_config() {
if (this->is_failed()) {
ESP_LOGE(TAG, "Communication with ADS1115 failed!");
}
for (auto *sensor : this->sensors_) {
LOG_SENSOR(" ", "Sensor", sensor);
ESP_LOGCONFIG(TAG, " Multiplexer: %u", sensor->get_multiplexer());
ESP_LOGCONFIG(TAG, " Gain: %u", sensor->get_gain());
ESP_LOGCONFIG(TAG, " Resolution: %u", sensor->get_resolution());
}
}
float ADS1115Component::request_measurement(ADS1115Sensor *sensor) {
float ADS1115Component::request_measurement(ADS1115Multiplexer multiplexer, ADS1115Gain gain,
ADS1115Resolution resolution) {
uint16_t config = this->prev_config_;
// Multiplexer
// 0bxBBBxxxxxxxxxxxx
config &= 0b1000111111111111;
config |= (sensor->get_multiplexer() & 0b111) << 12;
config |= (multiplexer & 0b111) << 12;
// Gain
// 0bxxxxBBBxxxxxxxxx
config &= 0b1111000111111111;
config |= (sensor->get_gain() & 0b111) << 9;
config |= (gain & 0b111) << 9;
if (!this->continuous_mode_) {
// Start conversion
@@ -132,7 +126,7 @@ float ADS1115Component::request_measurement(ADS1115Sensor *sensor) {
return NAN;
}
if (sensor->get_resolution() == ADS1015_12_BITS) {
if (resolution == ADS1015_12_BITS) {
bool negative = (raw_conversion >> 15) == 1;
// shift raw_conversion as it's only 12-bits, left justified
@@ -151,8 +145,8 @@ float ADS1115Component::request_measurement(ADS1115Sensor *sensor) {
auto signed_conversion = static_cast<int16_t>(raw_conversion);
float millivolts;
float divider = (sensor->get_resolution() == ADS1115_16_BITS) ? 32768.0f : 2048.0f;
switch (sensor->get_gain()) {
float divider = (resolution == ADS1115_16_BITS) ? 32768.0f : 2048.0f;
switch (gain) {
case ADS1115_GAIN_6P144:
millivolts = (signed_conversion * 6144) / divider;
break;
@@ -179,14 +173,5 @@ float ADS1115Component::request_measurement(ADS1115Sensor *sensor) {
return millivolts / 1e3f;
}
float ADS1115Sensor::sample() { return this->parent_->request_measurement(this); }
void ADS1115Sensor::update() {
float v = this->parent_->request_measurement(this);
if (!std::isnan(v)) {
ESP_LOGD(TAG, "'%s': Got Voltage=%fV", this->get_name().c_str(), v);
this->publish_state(v);
}
}
} // namespace ads1115
} // namespace esphome
+2 -29
View File
@@ -1,9 +1,7 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/i2c/i2c.h"
#include "esphome/components/voltage_sampler/voltage_sampler.h"
#include "esphome/core/component.h"
#include <vector>
@@ -35,12 +33,8 @@ enum ADS1115Resolution {
ADS1015_12_BITS = 12,
};
class ADS1115Sensor;
class ADS1115Component : public Component, public i2c::I2CDevice {
public:
void register_sensor(ADS1115Sensor *obj) { this->sensors_.push_back(obj); }
/// Set up the internal sensor array.
void setup() override;
void dump_config() override;
/// HARDWARE_LATE setup priority
@@ -48,33 +42,12 @@ class ADS1115Component : public Component, public i2c::I2CDevice {
void set_continuous_mode(bool continuous_mode) { continuous_mode_ = continuous_mode; }
/// Helper method to request a measurement from a sensor.
float request_measurement(ADS1115Sensor *sensor);
float request_measurement(ADS1115Multiplexer multiplexer, ADS1115Gain gain, ADS1115Resolution resolution);
protected:
std::vector<ADS1115Sensor *> sensors_;
uint16_t prev_config_{0};
bool continuous_mode_;
};
/// Internal holder class that is in instance of Sensor so that the hub can create individual sensors.
class ADS1115Sensor : public sensor::Sensor, public PollingComponent, public voltage_sampler::VoltageSampler {
public:
ADS1115Sensor(ADS1115Component *parent) : parent_(parent) {}
void update() override;
void set_multiplexer(ADS1115Multiplexer multiplexer) { multiplexer_ = multiplexer; }
void set_gain(ADS1115Gain gain) { gain_ = gain; }
void set_resolution(ADS1115Resolution resolution) { resolution_ = resolution; }
float sample() override;
uint8_t get_multiplexer() const { return multiplexer_; }
uint8_t get_gain() const { return gain_; }
uint8_t get_resolution() const { return resolution_; }
protected:
ADS1115Component *parent_;
ADS1115Multiplexer multiplexer_;
ADS1115Gain gain_;
ADS1115Resolution resolution_;
};
} // namespace ads1115
} // namespace esphome
@@ -10,8 +10,9 @@ from esphome.const import (
UNIT_VOLT,
CONF_ID,
)
from . import ads1115_ns, ADS1115Component
from .. import ads1115_ns, ADS1115Component, CONF_ADS1115_ID
AUTO_LOAD = ["voltage_sampler"]
DEPENDENCIES = ["ads1115"]
ADS1115Multiplexer = ads1115_ns.enum("ADS1115Multiplexer")
@@ -43,20 +44,10 @@ RESOLUTION = {
}
def validate_gain(value):
if isinstance(value, float):
value = f"{value:0.03f}"
elif not isinstance(value, str):
raise cv.Invalid(f'invalid gain "{value}"')
return cv.enum(GAIN)(value)
ADS1115Sensor = ads1115_ns.class_(
"ADS1115Sensor", sensor.Sensor, cg.PollingComponent, voltage_sampler.VoltageSampler
)
CONF_ADS1115_ID = "ads1115_id"
CONFIG_SCHEMA = (
sensor.sensor_schema(
ADS1115Sensor,
@@ -69,7 +60,7 @@ CONFIG_SCHEMA = (
{
cv.GenerateID(CONF_ADS1115_ID): cv.use_id(ADS1115Component),
cv.Required(CONF_MULTIPLEXER): cv.enum(MUX, upper=True, space="_"),
cv.Required(CONF_GAIN): validate_gain,
cv.Required(CONF_GAIN): cv.enum(GAIN, string=True),
cv.Optional(CONF_RESOLUTION, default="16_BITS"): cv.enum(
RESOLUTION, upper=True, space="_"
),
@@ -80,13 +71,11 @@ CONFIG_SCHEMA = (
async def to_code(config):
paren = await cg.get_variable(config[CONF_ADS1115_ID])
var = cg.new_Pvariable(config[CONF_ID], paren)
var = cg.new_Pvariable(config[CONF_ID])
await sensor.register_sensor(var, config)
await cg.register_component(var, config)
await cg.register_parented(var, config[CONF_ADS1115_ID])
cg.add(var.set_multiplexer(config[CONF_MULTIPLEXER]))
cg.add(var.set_gain(config[CONF_GAIN]))
cg.add(var.set_resolution(config[CONF_RESOLUTION]))
cg.add(paren.register_sensor(var))
@@ -0,0 +1,30 @@
#include "ads1115_sensor.h"
#include "esphome/core/log.h"
namespace esphome {
namespace ads1115 {
static const char *const TAG = "ads1115.sensor";
float ADS1115Sensor::sample() {
return this->parent_->request_measurement(this->multiplexer_, this->gain_, this->resolution_);
}
void ADS1115Sensor::update() {
float v = this->sample();
if (!std::isnan(v)) {
ESP_LOGD(TAG, "'%s': Got Voltage=%fV", this->get_name().c_str(), v);
this->publish_state(v);
}
}
void ADS1115Sensor::dump_config() {
LOG_SENSOR(" ", "ADS1115 Sensor", this);
ESP_LOGCONFIG(TAG, " Multiplexer: %u", this->multiplexer_);
ESP_LOGCONFIG(TAG, " Gain: %u", this->gain_);
ESP_LOGCONFIG(TAG, " Resolution: %u", this->resolution_);
}
} // namespace ads1115
} // namespace esphome
@@ -0,0 +1,35 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/helpers.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/voltage_sampler/voltage_sampler.h"
#include "../ads1115.h"
namespace esphome {
namespace ads1115 {
/// Internal holder class that is in instance of Sensor so that the hub can create individual sensors.
class ADS1115Sensor : public sensor::Sensor,
public PollingComponent,
public voltage_sampler::VoltageSampler,
public Parented<ADS1115Component> {
public:
void update() override;
void set_multiplexer(ADS1115Multiplexer multiplexer) { this->multiplexer_ = multiplexer; }
void set_gain(ADS1115Gain gain) { this->gain_ = gain; }
void set_resolution(ADS1115Resolution resolution) { this->resolution_ = resolution; }
float sample() override;
void dump_config() override;
protected:
ADS1115Multiplexer multiplexer_;
ADS1115Gain gain_;
ADS1115Resolution resolution_;
};
} // namespace ads1115
} // namespace esphome
+10 -13
View File
@@ -15,7 +15,6 @@
#include "aht10.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
#include <cinttypes>
namespace esphome {
namespace aht10 {
@@ -27,7 +26,7 @@ static const uint8_t AHT10_MEASURE_CMD[] = {0xAC, 0x33, 0x00};
static const uint8_t AHT10_SOFTRESET_CMD[] = {0xBA};
static const uint8_t AHT10_DEFAULT_DELAY = 5; // ms, for initialization and temperature measurement
static const uint8_t AHT10_HUMIDITY_DELAY = 30; // ms
static const uint8_t AHT10_READ_DELAY = 80; // ms, time to wait for conversion result
static const uint8_t AHT10_SOFTRESET_DELAY = 30; // ms
static const uint8_t AHT10_ATTEMPTS = 3; // safety margin, normally 3 attempts are enough: 3*30=90ms
@@ -36,7 +35,6 @@ static const uint8_t AHT10_INIT_ATTEMPTS = 10;
static const uint8_t AHT10_STATUS_BUSY = 0x80;
void AHT10Component::setup() {
this->read_delay_ = this->humidity_sensor_ != nullptr ? AHT10_HUMIDITY_DELAY : AHT10_DEFAULT_DELAY;
if (this->write(AHT10_SOFTRESET_CMD, sizeof(AHT10_SOFTRESET_CMD)) != i2c::ERROR_OK) {
ESP_LOGE(TAG, "Reset AHT10 failed!");
}
@@ -86,19 +84,19 @@ void AHT10Component::setup() {
void AHT10Component::restart_read_() {
if (this->read_count_ == AHT10_ATTEMPTS) {
this->read_count_ = 0;
ESP_LOGE(TAG, "Measurements reading timed-out!");
this->status_set_error();
this->status_set_error("Measurements reading timed-out!");
return;
}
this->read_count_++;
this->set_timeout(this->read_delay_, [this]() { this->read_data_(); });
this->set_timeout(AHT10_READ_DELAY, [this]() { this->read_data_(); });
}
void AHT10Component::read_data_() {
uint8_t data[6];
ESP_LOGD(TAG, "Read attempt %d at %ums", this->read_count_, (unsigned) (millis() - this->start_time_));
if (this->read_count_ > 1)
ESP_LOGD(TAG, "Read attempt %d at %ums", this->read_count_, (unsigned) (millis() - this->start_time_));
if (this->read(data, 6) != i2c::ERROR_OK) {
ESP_LOGD(TAG, "Communication with AHT10 failed, waiting...");
this->status_set_warning("AHT10 read failed, retrying soon");
this->restart_read_();
return;
}
@@ -115,14 +113,14 @@ void AHT10Component::read_data_() {
} else {
ESP_LOGD(TAG, "ATH10 Unrealistic humidity (0x0), retrying...");
if (this->write(AHT10_MEASURE_CMD, sizeof(AHT10_MEASURE_CMD)) != i2c::ERROR_OK) {
ESP_LOGE(TAG, "Communication with AHT10 failed!");
this->status_set_warning();
this->status_set_warning("Communication with AHT10 failed!");
}
this->restart_read_();
return;
}
}
ESP_LOGD(TAG, "Success at %ums", (unsigned) (millis() - this->start_time_));
if (this->read_count_ > 1)
ESP_LOGD(TAG, "Success at %ums", (unsigned) (millis() - this->start_time_));
uint32_t raw_temperature = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5];
uint32_t raw_humidity = ((data[1] << 16) | (data[2] << 8) | data[3]) >> 4;
@@ -150,8 +148,7 @@ void AHT10Component::update() {
return;
this->start_time_ = millis();
if (this->write(AHT10_MEASURE_CMD, sizeof(AHT10_MEASURE_CMD)) != i2c::ERROR_OK) {
ESP_LOGE(TAG, "Communication with AHT10 failed!");
this->status_set_warning();
this->status_set_warning("Communication with AHT10 failed!");
return;
}
this->restart_read_();
-1
View File
@@ -27,7 +27,6 @@ class AHT10Component : public PollingComponent, public i2c::I2CDevice {
sensor::Sensor *humidity_sensor_{nullptr};
AHT10Variant variant_{};
unsigned read_count_{};
unsigned read_delay_{};
void read_data_();
void restart_read_();
uint32_t start_time_{};
+60 -1
View File
@@ -45,6 +45,7 @@ service APIConnection {
rpc lock_command (LockCommandRequest) returns (void) {}
rpc media_player_command (MediaPlayerCommandRequest) returns (void) {}
rpc date_command (DateCommandRequest) returns (void) {}
rpc time_command (TimeCommandRequest) returns (void) {}
rpc subscribe_bluetooth_le_advertisements(SubscribeBluetoothLEAdvertisementsRequest) returns (void) {}
rpc bluetooth_device_request(BluetoothDeviceRequest) returns (void) {}
@@ -217,7 +218,8 @@ message DeviceInfoResponse {
string friendly_name = 13;
uint32 voice_assistant_version = 14;
uint32 legacy_voice_assistant_version = 14;
uint32 voice_assistant_feature_flags = 17;
string suggested_area = 16;
}
@@ -1422,12 +1424,18 @@ message BluetoothDeviceClearCacheResponse {
}
// ==================== PUSH TO TALK ====================
enum VoiceAssistantSubscribeFlag {
VOICE_ASSISTANT_SUBSCRIBE_NONE = 0;
VOICE_ASSISTANT_SUBSCRIBE_API_AUDIO = 1;
}
message SubscribeVoiceAssistantRequest {
option (id) = 89;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_VOICE_ASSISTANT";
bool subscribe = 1;
uint32 flags = 2;
}
enum VoiceAssistantRequestFlag {
@@ -1495,6 +1503,16 @@ message VoiceAssistantEventResponse {
repeated VoiceAssistantEventData data = 2;
}
message VoiceAssistantAudio {
option (id) = 106;
option (source) = SOURCE_BOTH;
option (ifdef) = "USE_VOICE_ASSISTANT";
bytes data = 1;
bool end = 2;
}
// ==================== ALARM CONTROL PANEL ====================
enum AlarmControlPanelState {
ALARM_STATE_DISARMED = 0;
@@ -1641,3 +1659,44 @@ message DateCommandRequest {
uint32 month = 3;
uint32 day = 4;
}
// ==================== DATETIME TIME ====================
message ListEntitiesTimeResponse {
option (id) = 103;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_DATETIME_TIME";
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
string icon = 5;
bool disabled_by_default = 6;
EntityCategory entity_category = 7;
}
message TimeStateResponse {
option (id) = 104;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_DATETIME_TIME";
option (no_delay) = true;
fixed32 key = 1;
// If the time does not have a valid state yet.
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
bool missing_state = 2;
uint32 hour = 3;
uint32 minute = 4;
uint32 second = 5;
}
message TimeCommandRequest {
option (id) = 105;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_DATETIME_TIME";
option (no_delay) = true;
fixed32 key = 1;
uint32 hour = 2;
uint32 minute = 3;
uint32 second = 4;
}
+58 -6
View File
@@ -735,6 +735,43 @@ void APIConnection::date_command(const DateCommandRequest &msg) {
}
#endif
#ifdef USE_DATETIME_TIME
bool APIConnection::send_time_state(datetime::TimeEntity *time) {
if (!this->state_subscription_)
return false;
TimeStateResponse resp{};
resp.key = time->get_object_id_hash();
resp.missing_state = !time->has_state();
resp.hour = time->hour;
resp.minute = time->minute;
resp.second = time->second;
return this->send_time_state_response(resp);
}
bool APIConnection::send_time_info(datetime::TimeEntity *time) {
ListEntitiesTimeResponse msg;
msg.key = time->get_object_id_hash();
msg.object_id = time->get_object_id();
if (time->has_own_name())
msg.name = time->get_name();
msg.unique_id = get_default_unique_id("time", time);
msg.icon = time->get_icon();
msg.disabled_by_default = time->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(time->get_entity_category());
return this->send_list_entities_time_response(msg);
}
void APIConnection::time_command(const TimeCommandRequest &msg) {
datetime::TimeEntity *time = App.get_time_by_key(msg.key);
if (time == nullptr)
return;
auto call = time->make_call();
call.set_time(msg.hour, msg.minute, msg.second);
call.perform();
}
#endif
#ifdef USE_TEXT
bool APIConnection::send_text_state(text::Text *text, std::string state) {
if (!this->state_subscription_)
@@ -1040,10 +1077,15 @@ void APIConnection::on_voice_assistant_response(const VoiceAssistantResponse &ms
voice_assistant::global_voice_assistant->failed_to_start();
return;
}
struct sockaddr_storage storage;
socklen_t len = sizeof(storage);
this->helper_->getpeername((struct sockaddr *) &storage, &len);
voice_assistant::global_voice_assistant->start_streaming(&storage, msg.port);
if (msg.port == 0) {
// Use API Audio
voice_assistant::global_voice_assistant->start_streaming();
} else {
struct sockaddr_storage storage;
socklen_t len = sizeof(storage);
this->helper_->getpeername((struct sockaddr *) &storage, &len);
voice_assistant::global_voice_assistant->start_streaming(&storage, msg.port);
}
}
};
void APIConnection::on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) {
@@ -1055,6 +1097,15 @@ void APIConnection::on_voice_assistant_event_response(const VoiceAssistantEventR
voice_assistant::global_voice_assistant->on_event(msg);
}
}
void APIConnection::on_voice_assistant_audio(const VoiceAssistantAudio &msg) {
if (voice_assistant::global_voice_assistant != nullptr) {
if (voice_assistant::global_voice_assistant->get_api_connection() != this) {
return;
}
voice_assistant::global_voice_assistant->on_audio(msg);
}
};
#endif
@@ -1142,7 +1193,7 @@ HelloResponse APIConnection::hello(const HelloRequest &msg) {
HelloResponse resp;
resp.api_version_major = 1;
resp.api_version_minor = 9;
resp.api_version_minor = 10;
resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")";
resp.name = App.get_name();
@@ -1203,7 +1254,8 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
resp.bluetooth_proxy_feature_flags = bluetooth_proxy::global_bluetooth_proxy->get_feature_flags();
#endif
#ifdef USE_VOICE_ASSISTANT
resp.voice_assistant_version = voice_assistant::global_voice_assistant->get_version();
resp.legacy_voice_assistant_version = voice_assistant::global_voice_assistant->get_legacy_version();
resp.voice_assistant_feature_flags = voice_assistant::global_voice_assistant->get_feature_flags();
#endif
return resp;
}
+6
View File
@@ -77,6 +77,11 @@ class APIConnection : public APIServerConnection {
bool send_date_info(datetime::DateEntity *date);
void date_command(const DateCommandRequest &msg) override;
#endif
#ifdef USE_DATETIME_TIME
bool send_time_state(datetime::TimeEntity *time);
bool send_time_info(datetime::TimeEntity *time);
void time_command(const TimeCommandRequest &msg) override;
#endif
#ifdef USE_TEXT
bool send_text_state(text::Text *text, std::string state);
bool send_text_info(text::Text *text);
@@ -134,6 +139,7 @@ class APIConnection : public APIServerConnection {
void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) override;
void on_voice_assistant_response(const VoiceAssistantResponse &msg) override;
void on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) override;
void on_voice_assistant_audio(const VoiceAssistantAudio &msg) override;
#endif
#ifdef USE_ALARM_CONTROL_PANEL
+297 -5
View File
@@ -410,6 +410,19 @@ const char *proto_enum_to_string<enums::BluetoothDeviceRequestType>(enums::Bluet
}
#endif
#ifdef HAS_PROTO_MESSAGE_DUMP
template<>
const char *proto_enum_to_string<enums::VoiceAssistantSubscribeFlag>(enums::VoiceAssistantSubscribeFlag value) {
switch (value) {
case enums::VOICE_ASSISTANT_SUBSCRIBE_NONE:
return "VOICE_ASSISTANT_SUBSCRIBE_NONE";
case enums::VOICE_ASSISTANT_SUBSCRIBE_API_AUDIO:
return "VOICE_ASSISTANT_SUBSCRIBE_API_AUDIO";
default:
return "UNKNOWN";
}
}
#endif
#ifdef HAS_PROTO_MESSAGE_DUMP
template<> const char *proto_enum_to_string<enums::VoiceAssistantRequestFlag>(enums::VoiceAssistantRequestFlag value) {
switch (value) {
case enums::VOICE_ASSISTANT_REQUEST_NONE:
@@ -716,7 +729,11 @@ bool DeviceInfoResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
return true;
}
case 14: {
this->voice_assistant_version = value.as_uint32();
this->legacy_voice_assistant_version = value.as_uint32();
return true;
}
case 17: {
this->voice_assistant_feature_flags = value.as_uint32();
return true;
}
default:
@@ -784,7 +801,8 @@ void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_uint32(15, this->bluetooth_proxy_feature_flags);
buffer.encode_string(12, this->manufacturer);
buffer.encode_string(13, this->friendly_name);
buffer.encode_uint32(14, this->voice_assistant_version);
buffer.encode_uint32(14, this->legacy_voice_assistant_version);
buffer.encode_uint32(17, this->voice_assistant_feature_flags);
buffer.encode_string(16, this->suggested_area);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
@@ -850,8 +868,13 @@ void DeviceInfoResponse::dump_to(std::string &out) const {
out.append("'").append(this->friendly_name).append("'");
out.append("\n");
out.append(" voice_assistant_version: ");
sprintf(buffer, "%" PRIu32, this->voice_assistant_version);
out.append(" legacy_voice_assistant_version: ");
sprintf(buffer, "%" PRIu32, this->legacy_voice_assistant_version);
out.append(buffer);
out.append("\n");
out.append(" voice_assistant_feature_flags: ");
sprintf(buffer, "%" PRIu32, this->voice_assistant_feature_flags);
out.append(buffer);
out.append("\n");
@@ -6514,11 +6537,18 @@ bool SubscribeVoiceAssistantRequest::decode_varint(uint32_t field_id, ProtoVarIn
this->subscribe = value.as_bool();
return true;
}
case 2: {
this->flags = value.as_uint32();
return true;
}
default:
return false;
}
}
void SubscribeVoiceAssistantRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(1, this->subscribe); }
void SubscribeVoiceAssistantRequest::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(1, this->subscribe);
buffer.encode_uint32(2, this->flags);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void SubscribeVoiceAssistantRequest::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64];
@@ -6526,6 +6556,11 @@ void SubscribeVoiceAssistantRequest::dump_to(std::string &out) const {
out.append(" subscribe: ");
out.append(YESNO(this->subscribe));
out.append("\n");
out.append(" flags: ");
sprintf(buffer, "%" PRIu32, this->flags);
out.append(buffer);
out.append("\n");
out.append("}");
}
#endif
@@ -6752,6 +6787,44 @@ void VoiceAssistantEventResponse::dump_to(std::string &out) const {
out.append("}");
}
#endif
bool VoiceAssistantAudio::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 2: {
this->end = value.as_bool();
return true;
}
default:
return false;
}
}
bool VoiceAssistantAudio::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 1: {
this->data = value.as_string();
return true;
}
default:
return false;
}
}
void VoiceAssistantAudio::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(1, this->data);
buffer.encode_bool(2, this->end);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void VoiceAssistantAudio::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64];
out.append("VoiceAssistantAudio {\n");
out.append(" data: ");
out.append("'").append(this->data).append("'");
out.append("\n");
out.append(" end: ");
out.append(YESNO(this->end));
out.append("\n");
out.append("}");
}
#endif
bool ListEntitiesAlarmControlPanelResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 6: {
@@ -7403,6 +7476,225 @@ void DateCommandRequest::dump_to(std::string &out) const {
out.append("}");
}
#endif
bool ListEntitiesTimeResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 6: {
this->disabled_by_default = value.as_bool();
return true;
}
case 7: {
this->entity_category = value.as_enum<enums::EntityCategory>();
return true;
}
default:
return false;
}
}
bool ListEntitiesTimeResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 1: {
this->object_id = value.as_string();
return true;
}
case 3: {
this->name = value.as_string();
return true;
}
case 4: {
this->unique_id = value.as_string();
return true;
}
case 5: {
this->icon = value.as_string();
return true;
}
default:
return false;
}
}
bool ListEntitiesTimeResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
switch (field_id) {
case 2: {
this->key = value.as_fixed32();
return true;
}
default:
return false;
}
}
void ListEntitiesTimeResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(1, this->object_id);
buffer.encode_fixed32(2, this->key);
buffer.encode_string(3, this->name);
buffer.encode_string(4, this->unique_id);
buffer.encode_string(5, this->icon);
buffer.encode_bool(6, this->disabled_by_default);
buffer.encode_enum<enums::EntityCategory>(7, this->entity_category);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesTimeResponse::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64];
out.append("ListEntitiesTimeResponse {\n");
out.append(" object_id: ");
out.append("'").append(this->object_id).append("'");
out.append("\n");
out.append(" key: ");
sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer);
out.append("\n");
out.append(" name: ");
out.append("'").append(this->name).append("'");
out.append("\n");
out.append(" unique_id: ");
out.append("'").append(this->unique_id).append("'");
out.append("\n");
out.append(" icon: ");
out.append("'").append(this->icon).append("'");
out.append("\n");
out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default));
out.append("\n");
out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n");
out.append("}");
}
#endif
bool TimeStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 2: {
this->missing_state = value.as_bool();
return true;
}
case 3: {
this->hour = value.as_uint32();
return true;
}
case 4: {
this->minute = value.as_uint32();
return true;
}
case 5: {
this->second = value.as_uint32();
return true;
}
default:
return false;
}
}
bool TimeStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
switch (field_id) {
case 1: {
this->key = value.as_fixed32();
return true;
}
default:
return false;
}
}
void TimeStateResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_fixed32(1, this->key);
buffer.encode_bool(2, this->missing_state);
buffer.encode_uint32(3, this->hour);
buffer.encode_uint32(4, this->minute);
buffer.encode_uint32(5, this->second);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void TimeStateResponse::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64];
out.append("TimeStateResponse {\n");
out.append(" key: ");
sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer);
out.append("\n");
out.append(" missing_state: ");
out.append(YESNO(this->missing_state));
out.append("\n");
out.append(" hour: ");
sprintf(buffer, "%" PRIu32, this->hour);
out.append(buffer);
out.append("\n");
out.append(" minute: ");
sprintf(buffer, "%" PRIu32, this->minute);
out.append(buffer);
out.append("\n");
out.append(" second: ");
sprintf(buffer, "%" PRIu32, this->second);
out.append(buffer);
out.append("\n");
out.append("}");
}
#endif
bool TimeCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 2: {
this->hour = value.as_uint32();
return true;
}
case 3: {
this->minute = value.as_uint32();
return true;
}
case 4: {
this->second = value.as_uint32();
return true;
}
default:
return false;
}
}
bool TimeCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
switch (field_id) {
case 1: {
this->key = value.as_fixed32();
return true;
}
default:
return false;
}
}
void TimeCommandRequest::encode(ProtoWriteBuffer buffer) const {
buffer.encode_fixed32(1, this->key);
buffer.encode_uint32(2, this->hour);
buffer.encode_uint32(3, this->minute);
buffer.encode_uint32(4, this->second);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void TimeCommandRequest::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64];
out.append("TimeCommandRequest {\n");
out.append(" key: ");
sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer);
out.append("\n");
out.append(" hour: ");
sprintf(buffer, "%" PRIu32, this->hour);
out.append(buffer);
out.append("\n");
out.append(" minute: ");
sprintf(buffer, "%" PRIu32, this->minute);
out.append(buffer);
out.append("\n");
out.append(" second: ");
sprintf(buffer, "%" PRIu32, this->second);
out.append(buffer);
out.append("\n");
out.append("}");
}
#endif
} // namespace api
} // namespace esphome
+70 -1
View File
@@ -165,6 +165,10 @@ enum BluetoothDeviceRequestType : uint32_t {
BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE = 5,
BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE = 6,
};
enum VoiceAssistantSubscribeFlag : uint32_t {
VOICE_ASSISTANT_SUBSCRIBE_NONE = 0,
VOICE_ASSISTANT_SUBSCRIBE_API_AUDIO = 1,
};
enum VoiceAssistantRequestFlag : uint32_t {
VOICE_ASSISTANT_REQUEST_NONE = 0,
VOICE_ASSISTANT_REQUEST_USE_VAD = 1,
@@ -327,7 +331,8 @@ class DeviceInfoResponse : public ProtoMessage {
uint32_t bluetooth_proxy_feature_flags{0};
std::string manufacturer{};
std::string friendly_name{};
uint32_t voice_assistant_version{0};
uint32_t legacy_voice_assistant_version{0};
uint32_t voice_assistant_feature_flags{0};
std::string suggested_area{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
@@ -1674,6 +1679,7 @@ class BluetoothDeviceClearCacheResponse : public ProtoMessage {
class SubscribeVoiceAssistantRequest : public ProtoMessage {
public:
bool subscribe{false};
uint32_t flags{0};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
@@ -1749,6 +1755,19 @@ class VoiceAssistantEventResponse : public ProtoMessage {
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class VoiceAssistantAudio : public ProtoMessage {
public:
std::string data{};
bool end{false};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class ListEntitiesAlarmControlPanelResponse : public ProtoMessage {
public:
std::string object_id{};
@@ -1900,6 +1919,56 @@ class DateCommandRequest : public ProtoMessage {
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class ListEntitiesTimeResponse : public ProtoMessage {
public:
std::string object_id{};
uint32_t key{0};
std::string name{};
std::string unique_id{};
std::string icon{};
bool disabled_by_default{false};
enums::EntityCategory entity_category{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class TimeStateResponse : public ProtoMessage {
public:
uint32_t key{0};
bool missing_state{false};
uint32_t hour{0};
uint32_t minute{0};
uint32_t second{0};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class TimeCommandRequest : public ProtoMessage {
public:
uint32_t key{0};
uint32_t hour{0};
uint32_t minute{0};
uint32_t second{0};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
} // namespace api
} // namespace esphome
@@ -476,6 +476,14 @@ bool APIServerConnectionBase::send_voice_assistant_request(const VoiceAssistantR
#endif
#ifdef USE_VOICE_ASSISTANT
#endif
#ifdef USE_VOICE_ASSISTANT
bool APIServerConnectionBase::send_voice_assistant_audio(const VoiceAssistantAudio &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_voice_assistant_audio: %s", msg.dump().c_str());
#endif
return this->send_message_<VoiceAssistantAudio>(msg, 106);
}
#endif
#ifdef USE_ALARM_CONTROL_PANEL
bool APIServerConnectionBase::send_list_entities_alarm_control_panel_response(
const ListEntitiesAlarmControlPanelResponse &msg) {
@@ -531,6 +539,24 @@ bool APIServerConnectionBase::send_date_state_response(const DateStateResponse &
#endif
#ifdef USE_DATETIME_DATE
#endif
#ifdef USE_DATETIME_TIME
bool APIServerConnectionBase::send_list_entities_time_response(const ListEntitiesTimeResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_time_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesTimeResponse>(msg, 103);
}
#endif
#ifdef USE_DATETIME_TIME
bool APIServerConnectionBase::send_time_state_response(const TimeStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_time_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<TimeStateResponse>(msg, 104);
}
#endif
#ifdef USE_DATETIME_TIME
#endif
bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {
switch (msg_type) {
case 1: {
@@ -971,6 +997,28 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
ESP_LOGVV(TAG, "on_date_command_request: %s", msg.dump().c_str());
#endif
this->on_date_command_request(msg);
#endif
break;
}
case 105: {
#ifdef USE_DATETIME_TIME
TimeCommandRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_time_command_request: %s", msg.dump().c_str());
#endif
this->on_time_command_request(msg);
#endif
break;
}
case 106: {
#ifdef USE_VOICE_ASSISTANT
VoiceAssistantAudio msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_voice_assistant_audio: %s", msg.dump().c_str());
#endif
this->on_voice_assistant_audio(msg);
#endif
break;
}
@@ -1260,6 +1308,19 @@ void APIServerConnection::on_date_command_request(const DateCommandRequest &msg)
this->date_command(msg);
}
#endif
#ifdef USE_DATETIME_TIME
void APIServerConnection::on_time_command_request(const TimeCommandRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->time_command(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_subscribe_bluetooth_le_advertisements_request(
const SubscribeBluetoothLEAdvertisementsRequest &msg) {
+19
View File
@@ -240,6 +240,10 @@ class APIServerConnectionBase : public ProtoService {
#ifdef USE_VOICE_ASSISTANT
virtual void on_voice_assistant_event_response(const VoiceAssistantEventResponse &value){};
#endif
#ifdef USE_VOICE_ASSISTANT
bool send_voice_assistant_audio(const VoiceAssistantAudio &msg);
virtual void on_voice_assistant_audio(const VoiceAssistantAudio &value){};
#endif
#ifdef USE_ALARM_CONTROL_PANEL
bool send_list_entities_alarm_control_panel_response(const ListEntitiesAlarmControlPanelResponse &msg);
#endif
@@ -266,6 +270,15 @@ class APIServerConnectionBase : public ProtoService {
#endif
#ifdef USE_DATETIME_DATE
virtual void on_date_command_request(const DateCommandRequest &value){};
#endif
#ifdef USE_DATETIME_TIME
bool send_list_entities_time_response(const ListEntitiesTimeResponse &msg);
#endif
#ifdef USE_DATETIME_TIME
bool send_time_state_response(const TimeStateResponse &msg);
#endif
#ifdef USE_DATETIME_TIME
virtual void on_time_command_request(const TimeCommandRequest &value){};
#endif
protected:
bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;
@@ -324,6 +337,9 @@ class APIServerConnection : public APIServerConnectionBase {
#ifdef USE_DATETIME_DATE
virtual void date_command(const DateCommandRequest &msg) = 0;
#endif
#ifdef USE_DATETIME_TIME
virtual void time_command(const TimeCommandRequest &msg) = 0;
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) = 0;
#endif
@@ -413,6 +429,9 @@ class APIServerConnection : public APIServerConnectionBase {
#ifdef USE_DATETIME_DATE
void on_date_command_request(const DateCommandRequest &msg) override;
#endif
#ifdef USE_DATETIME_TIME
void on_time_command_request(const TimeCommandRequest &msg) override;
#endif
#ifdef USE_BLUETOOTH_PROXY
void on_subscribe_bluetooth_le_advertisements_request(const SubscribeBluetoothLEAdvertisementsRequest &msg) override;
#endif
+9
View File
@@ -264,6 +264,15 @@ void APIServer::on_date_update(datetime::DateEntity *obj) {
}
#endif
#ifdef USE_DATETIME_TIME
void APIServer::on_time_update(datetime::TimeEntity *obj) {
if (obj->is_internal())
return;
for (auto &c : this->clients_)
c->send_time_state(obj);
}
#endif
#ifdef USE_TEXT
void APIServer::on_text_update(text::Text *obj, const std::string &state) {
if (obj->is_internal())
+3
View File
@@ -69,6 +69,9 @@ class APIServer : public Component, public Controller {
#ifdef USE_DATETIME_DATE
void on_date_update(datetime::DateEntity *obj) override;
#endif
#ifdef USE_DATETIME_TIME
void on_time_update(datetime::TimeEntity *obj) override;
#endif
#ifdef USE_TEXT
void on_text_update(text::Text *obj, const std::string &state) override;
#endif
+4
View File
@@ -64,6 +64,10 @@ bool ListEntitiesIterator::on_number(number::Number *number) { return this->clie
bool ListEntitiesIterator::on_date(datetime::DateEntity *date) { return this->client_->send_date_info(date); }
#endif
#ifdef USE_DATETIME_TIME
bool ListEntitiesIterator::on_time(datetime::TimeEntity *time) { return this->client_->send_time_info(time); }
#endif
#ifdef USE_TEXT
bool ListEntitiesIterator::on_text(text::Text *text) { return this->client_->send_text_info(text); }
#endif
+3
View File
@@ -49,6 +49,9 @@ class ListEntitiesIterator : public ComponentIterator {
#ifdef USE_DATETIME_DATE
bool on_date(datetime::DateEntity *date) override;
#endif
#ifdef USE_DATETIME_TIME
bool on_time(datetime::TimeEntity *time) override;
#endif
#ifdef USE_TEXT
bool on_text(text::Text *text) override;
#endif
@@ -45,6 +45,9 @@ bool InitialStateIterator::on_number(number::Number *number) {
#ifdef USE_DATETIME_DATE
bool InitialStateIterator::on_date(datetime::DateEntity *date) { return this->client_->send_date_state(date); }
#endif
#ifdef USE_DATETIME_TIME
bool InitialStateIterator::on_time(datetime::TimeEntity *time) { return this->client_->send_time_state(time); }
#endif
#ifdef USE_TEXT
bool InitialStateIterator::on_text(text::Text *text) { return this->client_->send_text_state(text, text->state); }
#endif
+3
View File
@@ -46,6 +46,9 @@ class InitialStateIterator : public ComponentIterator {
#ifdef USE_DATETIME_DATE
bool on_date(datetime::DateEntity *date) override;
#endif
#ifdef USE_DATETIME_TIME
bool on_time(datetime::TimeEntity *time) override;
#endif
#ifdef USE_TEXT
bool on_text(text::Text *text) override;
#endif
+224
View File
@@ -0,0 +1,224 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation, core
from esphome.components import i2c
from esphome.automation import maybe_simple_id
from esphome.const import (
CONF_ID,
CONF_FREQUENCY,
)
CODEOWNERS = ["@X-Ryl669"]
DEPENDENCIES = ["i2c"]
MULTI_CONF = True
at581x_ns = cg.esphome_ns.namespace("at581x")
AT581XComponent = at581x_ns.class_("AT581XComponent", cg.Component, i2c.I2CDevice)
CONF_AT581X_ID = "at581x_id"
CONF_SENSING_DISTANCE = "sensing_distance"
CONF_SENSITIVITY = "sensitivity"
CONF_POWERON_SELFCHECK_TIME = "poweron_selfcheck_time"
CONF_PROTECT_TIME = "protect_time"
CONF_TRIGGER_BASE = "trigger_base"
CONF_TRIGGER_KEEP = "trigger_keep"
CONF_STAGE_GAIN = "stage_gain"
CONF_POWER_CONSUMPTION = "power_consumption"
CONF_HW_FRONTEND_RESET = "hw_frontend_reset"
RADAR_ALLOWED_FREQ = [
5696e6,
5715e6,
5730e6,
5748e6,
5765e6,
5784e6,
5800e6,
5819e6,
5836e6,
5851e6,
5869e6,
5888e6,
]
RADAR_ALLOWED_CUR_CONSUMPTION = [
48e-6,
56e-6,
63e-6,
70e-6,
77e-6,
91e-6,
105e-6,
115e-6,
40e-6,
44e-6,
47e-6,
51e-6,
54e-6,
61e-6,
68e-6,
78e-6,
]
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(AT581XComponent),
}
)
CONFIG_SCHEMA = cv.All(
CONFIG_SCHEMA.extend(i2c.i2c_device_schema(0x28)).extend(cv.COMPONENT_SCHEMA)
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
# Actions
AT581XResetAction = at581x_ns.class_("AT581XResetAction", automation.Action)
AT581XSettingsAction = at581x_ns.class_("AT581XSettingsAction", automation.Action)
@automation.register_action(
"at581x.reset",
AT581XResetAction,
maybe_simple_id(
{
cv.Required(CONF_ID): cv.use_id(AT581XComponent),
}
),
)
async def at581x_reset_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID])
return var
RADAR_SETTINGS_SCHEMA = cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(AT581XComponent),
cv.Optional(CONF_HW_FRONTEND_RESET): cv.templatable(cv.boolean),
cv.Optional(CONF_FREQUENCY, default="5800MHz"): cv.templatable(
cv.All(cv.frequency, cv.one_of(*RADAR_ALLOWED_FREQ))
),
cv.Optional(CONF_SENSING_DISTANCE, default=823): cv.templatable(
cv.int_range(min=0, max=1023)
),
cv.Optional(CONF_POWERON_SELFCHECK_TIME, default="2000ms"): cv.templatable(
cv.All(
cv.positive_time_period_milliseconds,
cv.Range(max=core.TimePeriod(milliseconds=65535)),
)
),
cv.Optional(CONF_POWER_CONSUMPTION, default="70uA"): cv.templatable(
cv.All(cv.current, cv.one_of(*RADAR_ALLOWED_CUR_CONSUMPTION))
),
cv.Optional(CONF_PROTECT_TIME, default="1000ms"): cv.templatable(
cv.All(
cv.positive_time_period_milliseconds,
cv.Range(
min=core.TimePeriod(milliseconds=1),
max=core.TimePeriod(milliseconds=65535),
),
)
),
cv.Optional(CONF_TRIGGER_BASE, default="500ms"): cv.templatable(
cv.All(
cv.positive_time_period_milliseconds,
cv.Range(
min=core.TimePeriod(milliseconds=1),
max=core.TimePeriod(milliseconds=65535),
),
)
),
cv.Optional(CONF_TRIGGER_KEEP, default="1500ms"): cv.templatable(
cv.All(
cv.positive_time_period_milliseconds,
cv.Range(
min=core.TimePeriod(milliseconds=1),
max=core.TimePeriod(milliseconds=65535),
),
)
),
cv.Optional(CONF_STAGE_GAIN, default=3): cv.templatable(
cv.int_range(min=0, max=12)
),
}
).add_extra(
cv.has_at_least_one_key(
CONF_HW_FRONTEND_RESET,
CONF_FREQUENCY,
CONF_SENSING_DISTANCE,
)
)
@automation.register_action(
"at581x.settings",
AT581XSettingsAction,
RADAR_SETTINGS_SCHEMA,
)
async def at581x_settings_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID])
# Radar configuration
if frontend_reset := config.get(CONF_HW_FRONTEND_RESET):
template_ = await cg.templatable(frontend_reset, args, int)
cg.add(var.set_hw_frontend_reset(template_))
if freq := config.get(CONF_FREQUENCY):
template_ = await cg.templatable(freq, args, float)
template_ = int(template_ / 1000000)
cg.add(var.set_frequency(template_))
if sens_dist := config.get(CONF_SENSING_DISTANCE):
template_ = await cg.templatable(sens_dist, args, int)
cg.add(var.set_sensing_distance(template_))
if selfcheck := config.get(CONF_POWERON_SELFCHECK_TIME):
template_ = await cg.templatable(selfcheck, args, float)
if isinstance(template_, cv.TimePeriod):
template_ = template_.total_milliseconds
template_ = int(template_)
cg.add(var.set_poweron_selfcheck_time(template_))
if protect := config.get(CONF_PROTECT_TIME):
template_ = await cg.templatable(protect, args, float)
if isinstance(template_, cv.TimePeriod):
template_ = template_.total_milliseconds
template_ = int(template_)
cg.add(var.set_protect_time(template_))
if trig_base := config.get(CONF_TRIGGER_BASE):
template_ = await cg.templatable(trig_base, args, float)
if isinstance(template_, cv.TimePeriod):
template_ = template_.total_milliseconds
template_ = int(template_)
cg.add(var.set_trigger_base(template_))
if trig_keep := config.get(CONF_TRIGGER_KEEP):
template_ = await cg.templatable(trig_keep, args, float)
if isinstance(template_, cv.TimePeriod):
template_ = template_.total_milliseconds
template_ = int(template_)
cg.add(var.set_trigger_keep(template_))
if stage_gain := config.get(CONF_STAGE_GAIN):
template_ = await cg.templatable(stage_gain, args, int)
cg.add(var.set_stage_gain(template_))
if power := config.get(CONF_POWER_CONSUMPTION):
template_ = await cg.templatable(power, args, float)
template_ = int(template_ * 1000000)
cg.add(var.set_power_consumption(template_))
return var
+195
View File
@@ -0,0 +1,195 @@
#include "at581x.h"
#include "esphome/core/log.h"
/* Select gain for AT581X (3dB per step for level1, 6dB per step for level 2), high value = small gain. (p12) */
const uint8_t GAIN_ADDR_TABLE[] = {0x5c, 0x63};
const uint8_t GAIN5C_TABLE[] = {0x08, 0x18, 0x28, 0x38, 0x48, 0x58, 0x68, 0x78, 0x88, 0x98, 0xa8, 0xb8, 0xc8};
const uint8_t GAIN63_TABLE[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06};
const uint8_t GAIN61_VALUE = 0xCA; // 0xC0 | 0x02 (freq present) | 0x08 (gain present)
/*!< Power consumption configuration table (p12). */
const uint8_t POWER_TABLE[] = {48, 56, 63, 70, 77, 91, 105, 115, 40, 44, 47, 51, 54, 61, 68, 78};
const uint8_t POWER67_TABLE[] = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7};
const uint8_t POWER68_TABLE[] = {0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8,
24, 24, 24, 24, 24, 24, 24, 24}; // See Page 12, shift by 3 bits
/*!< Frequency Configuration table (p14/15 of datasheet). */
const uint8_t FREQ_ADDR = 0x61;
const uint16_t FREQ_TABLE[] = {5696, 5715, 5730, 5748, 5765, 5784, 5800, 5819, 5836, 5851, 5869, 5888};
const uint8_t FREQ5F_TABLE[] = {0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x40, 0x41, 0x42, 0x43};
const uint8_t FREQ60_TABLE[] = {0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0x9e};
/*!< Value for RF and analog modules switch (p10). */
const uint8_t RF_OFF_TABLE[] = {0x46, 0xaa, 0x50};
const uint8_t RF_ON_TABLE[] = {0x45, 0x55, 0xA0};
const uint8_t RF_REG_ADDR[] = {0x5d, 0x62, 0x51};
/*!< Registers of Lighting delay time. Unit: ms, min 2s (p8) */
const uint8_t HIGH_LEVEL_DELAY_CONTROL_ADDR = 0x41; /*!< Time_flag_out_ctrl 0x01 */
const uint8_t HIGH_LEVEL_DELAY_VALUE_ADDR = 0x42; /*!< Time_flag_out_1 Bit<7:0> */
const uint8_t RESET_ADDR = 0x00;
/*!< Sensing distance address */
const uint8_t SIGNAL_DETECTION_THRESHOLD_ADDR_LO = 0x10;
const uint8_t SIGNAL_DETECTION_THRESHOLD_ADDR_HI = 0x11;
/*!< Bit field value for power registers */
const uint8_t POWER_THRESHOLD_ADDR_HI = 0x68;
const uint8_t POWER_THRESHOLD_ADDR_LO = 0x67;
const uint8_t PWR_WORK_TIME_EN = 8; // Reg 0x67
const uint8_t PWR_BURST_TIME_EN = 32; // Reg 0x68
const uint8_t PWR_THRESH_EN = 64; // Reg 0x68
const uint8_t PWR_THRESH_VAL_EN = 128; // Reg 0x67
/*!< Times */
const uint8_t TRIGGER_BASE_TIME_ADDR = 0x3D; // 4 bytes, so up to 0x40
const uint8_t PROTECT_TIME_ADDR = 0x4E; // 2 bytes, up to 0x4F
const uint8_t TRIGGER_KEEP_TIME_ADDR = 0x42; // 4 bytes, so up to 0x45
const uint8_t TIME41_VALUE = 1;
const uint8_t SELF_CHECK_TIME_ADDR = 0x38; // 2 bytes, up to 0x39
namespace esphome {
namespace at581x {
static const char *const TAG = "at581x";
bool AT581XComponent::i2c_write_reg(uint8_t addr, uint8_t data) {
return this->write_register(addr, &data, 1) == esphome::i2c::NO_ERROR;
}
bool AT581XComponent::i2c_write_reg(uint8_t addr, uint32_t data) {
return this->i2c_write_reg(addr + 0, uint8_t(data & 0xFF)) &&
this->i2c_write_reg(addr + 1, uint8_t((data >> 8) & 0xFF)) &&
this->i2c_write_reg(addr + 2, uint8_t((data >> 16) & 0xFF)) &&
this->i2c_write_reg(addr + 3, uint8_t((data >> 24) & 0xFF));
}
bool AT581XComponent::i2c_write_reg(uint8_t addr, uint16_t data) {
return this->i2c_write_reg(addr, uint8_t(data & 0xFF)) && this->i2c_write_reg(addr + 1, uint8_t((data >> 8) & 0xFF));
}
bool AT581XComponent::i2c_read_reg(uint8_t addr, uint8_t &data) {
return this->read_register(addr, &data, 1) == esphome::i2c::NO_ERROR;
}
void AT581XComponent::setup() { ESP_LOGCONFIG(TAG, "Setting up AT581X..."); }
void AT581XComponent::dump_config() { LOG_I2C_DEVICE(this); }
#define ARRAY_SIZE(X) (sizeof(X) / sizeof((X)[0]))
bool AT581XComponent::i2c_write_config() {
ESP_LOGCONFIG(TAG, "Writing new config for AT581X...");
ESP_LOGCONFIG(TAG, "Frequency: %dMHz", this->freq_);
ESP_LOGCONFIG(TAG, "Sensing distance: %d", this->delta_);
ESP_LOGCONFIG(TAG, "Power: %dµA", this->power_);
ESP_LOGCONFIG(TAG, "Gain: %d", this->gain_);
ESP_LOGCONFIG(TAG, "Trigger base time: %dms", this->trigger_base_time_ms_);
ESP_LOGCONFIG(TAG, "Trigger keep time: %dms", this->trigger_keep_time_ms_);
ESP_LOGCONFIG(TAG, "Protect time: %dms", this->protect_time_ms_);
ESP_LOGCONFIG(TAG, "Self check time: %dms", this->self_check_time_ms_);
// Set frequency point
if (!this->i2c_write_reg(FREQ_ADDR, GAIN61_VALUE)) {
ESP_LOGE(TAG, "Failed to write AT581X Freq mode");
return false;
}
// Find the current frequency from the table to know what value to write
for (size_t i = 0; i < ARRAY_SIZE(FREQ_TABLE) + 1; i++) {
if (i == ARRAY_SIZE(FREQ_TABLE)) {
ESP_LOGE(TAG, "Set frequency not found");
return false;
}
if (FREQ_TABLE[i] == this->freq_) {
if (!this->i2c_write_reg(0x5F, FREQ5F_TABLE[i]) || !this->i2c_write_reg(0x60, FREQ60_TABLE[i])) {
ESP_LOGE(TAG, "Failed to write AT581X Freq value");
return false;
}
break;
}
}
// Set distance
if (!this->i2c_write_reg(SIGNAL_DETECTION_THRESHOLD_ADDR_LO, (uint8_t) (this->delta_ & 0xFF)) ||
!this->i2c_write_reg(SIGNAL_DETECTION_THRESHOLD_ADDR_HI, (uint8_t) (this->delta_ >> 8))) {
ESP_LOGE(TAG, "Failed to write AT581X sensing distance low");
return false;
}
// Set power setting
uint8_t pwr67 = PWR_THRESH_VAL_EN | PWR_WORK_TIME_EN, pwr68 = PWR_BURST_TIME_EN | PWR_THRESH_EN;
for (size_t i = 0; i < ARRAY_SIZE(POWER_TABLE) + 1; i++) {
if (i == ARRAY_SIZE(POWER_TABLE)) {
ESP_LOGE(TAG, "Set power not found");
return false;
}
if (POWER_TABLE[i] == this->power_) {
pwr67 |= POWER67_TABLE[i];
pwr68 |= POWER68_TABLE[i]; // See Page 12
break;
}
}
if (!this->i2c_write_reg(POWER_THRESHOLD_ADDR_LO, pwr67) || !this->i2c_write_reg(POWER_THRESHOLD_ADDR_HI, pwr68)) {
ESP_LOGE(TAG, "Failed to write AT581X power registers");
return false;
}
// Set gain
if (!this->i2c_write_reg(GAIN_ADDR_TABLE[0], GAIN5C_TABLE[this->gain_]) ||
!this->i2c_write_reg(GAIN_ADDR_TABLE[1], GAIN63_TABLE[this->gain_ >> 1])) {
ESP_LOGE(TAG, "Failed to write AT581X gain registers");
return false;
}
// Set times
if (!this->i2c_write_reg(TRIGGER_BASE_TIME_ADDR, (uint32_t) this->trigger_base_time_ms_)) {
ESP_LOGE(TAG, "Failed to write AT581X trigger base time registers");
return false;
}
if (!this->i2c_write_reg(TRIGGER_KEEP_TIME_ADDR, (uint32_t) this->trigger_keep_time_ms_)) {
ESP_LOGE(TAG, "Failed to write AT581X trigger keep time registers");
return false;
}
if (!this->i2c_write_reg(PROTECT_TIME_ADDR, (uint16_t) this->protect_time_ms_)) {
ESP_LOGE(TAG, "Failed to write AT581X protect time registers");
return false;
}
if (!this->i2c_write_reg(SELF_CHECK_TIME_ADDR, (uint16_t) this->self_check_time_ms_)) {
ESP_LOGE(TAG, "Failed to write AT581X self check time registers");
return false;
}
if (!this->i2c_write_reg(0x41, TIME41_VALUE)) {
ESP_LOGE(TAG, "Failed to enable AT581X time registers");
return false;
}
// Don't know why it's required in other code, it's not in datasheet
if (!this->i2c_write_reg(0x55, (uint8_t) 0x04)) {
ESP_LOGE(TAG, "Failed to enable AT581X");
return false;
}
// Ok, config is written, let's reset the chip so it's using the new config
return this->reset_hardware_frontend();
}
// float AT581XComponent::get_setup_priority() const { return 0; }
bool AT581XComponent::reset_hardware_frontend() {
if (!this->i2c_write_reg(RESET_ADDR, (uint8_t) 0) || !this->i2c_write_reg(RESET_ADDR, (uint8_t) 1)) {
ESP_LOGE(TAG, "Failed to reset AT581X hardware frontend");
return false;
}
return true;
}
void AT581XComponent::set_rf_mode(bool enable) {
const uint8_t *p = enable ? &RF_ON_TABLE[0] : &RF_OFF_TABLE[0];
for (size_t i = 0; i < ARRAY_SIZE(RF_REG_ADDR); i++) {
if (!this->i2c_write_reg(RF_REG_ADDR[i], p[i])) {
ESP_LOGE(TAG, "Failed to write AT581X RF mode");
return;
}
}
}
} // namespace at581x
} // namespace esphome
+62
View File
@@ -0,0 +1,62 @@
#pragma once
#include <utility>
#include "esphome/core/component.h"
#include "esphome/core/hal.h"
#include "esphome/core/defines.h"
#ifdef USE_SWITCH
#include "esphome/components/switch/switch.h"
#endif
#include "esphome/components/i2c/i2c.h"
namespace esphome {
namespace at581x {
class AT581XComponent : public Component, public i2c::I2CDevice {
#ifdef USE_SWITCH
protected:
switch_::Switch *rf_power_switch_{nullptr};
public:
void set_rf_power_switch(switch_::Switch *s) {
this->rf_power_switch_ = s;
s->turn_on();
}
#endif
void setup() override;
void dump_config() override;
// float get_setup_priority() const override;
void set_sensing_distance(int distance) { this->delta_ = 1023 - distance; }
void set_rf_mode(bool enabled);
void set_frequency(int frequency) { this->freq_ = frequency; }
void set_poweron_selfcheck_time(int value) { this->self_check_time_ms_ = value; }
void set_protect_time(int value) { this->protect_time_ms_ = value; }
void set_trigger_base(int value) { this->trigger_base_time_ms_ = value; }
void set_trigger_keep(int value) { this->trigger_keep_time_ms_ = value; }
void set_stage_gain(int value) { this->gain_ = value; }
void set_power_consumption(int value) { this->power_ = value; }
bool i2c_write_config();
bool reset_hardware_frontend();
bool i2c_write_reg(uint8_t addr, uint8_t data);
bool i2c_write_reg(uint8_t addr, uint32_t data);
bool i2c_write_reg(uint8_t addr, uint16_t data);
bool i2c_read_reg(uint8_t addr, uint8_t &data);
protected:
int freq_;
int self_check_time_ms_; /*!< Power-on self-test time, range: 0 ~ 65536 ms */
int protect_time_ms_; /*!< Protection time, recommended 1000 ms */
int trigger_base_time_ms_; /*!< Default: 500 ms */
int trigger_keep_time_ms_; /*!< Total trig time = TRIGGER_BASE_TIME + DEF_TRIGGER_KEEP_TIME, minimum: 1 */
int delta_; /*!< Delta value: 0 ~ 1023, the larger the value, the shorter the distance */
int gain_; /*!< Default: 9dB */
int power_; /*!< In µA */
};
} // namespace at581x
} // namespace esphome
+71
View File
@@ -0,0 +1,71 @@
#pragma once
#include "esphome/core/automation.h"
#include "esphome/core/helpers.h"
#include "at581x.h"
namespace esphome {
namespace at581x {
template<typename... Ts> class AT581XResetAction : public Action<Ts...>, public Parented<AT581XComponent> {
public:
void play(Ts... x) { this->parent_->reset_hardware_frontend(); }
};
template<typename... Ts> class AT581XSettingsAction : public Action<Ts...>, public Parented<AT581XComponent> {
public:
TEMPLATABLE_VALUE(int8_t, hw_frontend_reset)
TEMPLATABLE_VALUE(int, frequency)
TEMPLATABLE_VALUE(int, sensing_distance)
TEMPLATABLE_VALUE(int, poweron_selfcheck_time)
TEMPLATABLE_VALUE(int, power_consumption)
TEMPLATABLE_VALUE(int, protect_time)
TEMPLATABLE_VALUE(int, trigger_base)
TEMPLATABLE_VALUE(int, trigger_keep)
TEMPLATABLE_VALUE(int, stage_gain)
void play(Ts... x) {
if (this->frequency_.has_value()) {
int v = this->frequency_.value(x...);
this->parent_->set_frequency(v);
}
if (this->sensing_distance_.has_value()) {
int v = this->sensing_distance_.value(x...);
this->parent_->set_sensing_distance(v);
}
if (this->poweron_selfcheck_time_.has_value()) {
int v = this->poweron_selfcheck_time_.value(x...);
this->parent_->set_poweron_selfcheck_time(v);
}
if (this->power_consumption_.has_value()) {
int v = this->power_consumption_.value(x...);
this->parent_->set_power_consumption(v);
}
if (this->protect_time_.has_value()) {
int v = this->protect_time_.value(x...);
this->parent_->set_protect_time(v);
}
if (this->trigger_base_.has_value()) {
int v = this->trigger_base_.value(x...);
this->parent_->set_trigger_base(v);
}
if (this->trigger_keep_.has_value()) {
int v = this->trigger_keep_.value(x...);
this->parent_->set_trigger_keep(v);
}
if (this->stage_gain_.has_value()) {
int v = this->stage_gain_.value(x...);
this->parent_->set_stage_gain(v);
}
// This actually perform all the modification on the system
this->parent_->i2c_write_config();
if (this->hw_frontend_reset_.has_value() && this->hw_frontend_reset_.value(x...) == true) {
this->parent_->reset_hardware_frontend();
}
}
};
} // namespace at581x
} // namespace esphome
@@ -0,0 +1,31 @@
import esphome.codegen as cg
from esphome.components import switch
import esphome.config_validation as cv
from esphome.const import (
DEVICE_CLASS_SWITCH,
ICON_WIFI,
)
from .. import CONF_AT581X_ID, AT581XComponent, at581x_ns
DEPENDENCIES = ["at581x"]
RFSwitch = at581x_ns.class_("RFSwitch", switch.Switch)
CONFIG_SCHEMA = switch.switch_schema(
RFSwitch,
device_class=DEVICE_CLASS_SWITCH,
icon=ICON_WIFI,
).extend(
cv.Schema(
{
cv.GenerateID(CONF_AT581X_ID): cv.use_id(AT581XComponent),
}
)
)
async def to_code(config):
at581x_component = await cg.get_variable(config[CONF_AT581X_ID])
s = await switch.new_switch(config)
await cg.register_parented(s, config[CONF_AT581X_ID])
cg.add(at581x_component.set_rf_power_switch(s))
@@ -0,0 +1,12 @@
#include "rf_switch.h"
namespace esphome {
namespace at581x {
void RFSwitch::write_state(bool state) {
this->publish_state(state);
this->parent_->set_rf_mode(state);
}
} // namespace at581x
} // namespace esphome
@@ -0,0 +1,15 @@
#pragma once
#include "esphome/components/switch/switch.h"
#include "../at581x.h"
namespace esphome {
namespace at581x {
class RFSwitch : public switch_::Switch, public Parented<AT581XComponent> {
protected:
void write_state(bool state) override;
};
} // namespace at581x
} // namespace esphome
@@ -73,8 +73,7 @@ class BLEPresenceDevice : public binary_sensor::BinarySensorInitiallyOff,
break;
case MATCH_BY_IRK:
if (resolve_irk_(device.address_uint64(), this->irk_)) {
this->publish_state(true);
this->found_ = true;
this->set_found_(true);
return true;
}
break;
@@ -6,100 +6,102 @@ namespace esphome {
namespace captive_portal {
const uint8_t INDEX_GZ[] PROGMEM = {
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xdd, 0x58, 0x5b, 0x8f, 0xdb, 0x36, 0x16, 0x7e, 0xef,
0xaf, 0xe0, 0x2a, 0x49, 0x2d, 0x37, 0x23, 0xea, 0x66, 0xf9, 0x2a, 0xa9, 0x48, 0xb2, 0x29, 0x5a, 0x20, 0x69, 0x03,
0xcc, 0xb4, 0xfb, 0x10, 0x04, 0x18, 0x5a, 0xa2, 0x2c, 0x66, 0x24, 0x4a, 0x15, 0xe9, 0x5b, 0x0c, 0xef, 0x6f, 0xdf,
0x43, 0x52, 0xf6, 0x38, 0xb3, 0x99, 0x05, 0x52, 0xec, 0x62, 0xd1, 0x4e, 0x26, 0x1c, 0x92, 0x3a, 0xd7, 0x4f, 0x3c,
0x17, 0x2a, 0xfe, 0x5b, 0xde, 0x64, 0x72, 0xdf, 0x52, 0x54, 0xca, 0xba, 0x4a, 0x63, 0x35, 0xa2, 0x8a, 0xf0, 0x55,
0x42, 0x39, 0xac, 0x28, 0xc9, 0xd3, 0xb8, 0xa6, 0x92, 0xa0, 0xac, 0x24, 0x9d, 0xa0, 0x32, 0xf9, 0xf5, 0xe6, 0x07,
0x67, 0x8a, 0xdc, 0x34, 0xae, 0x18, 0xbf, 0x43, 0x1d, 0xad, 0x12, 0x96, 0x35, 0x1c, 0x95, 0x1d, 0x2d, 0x92, 0x9c,
0x48, 0x32, 0x67, 0x35, 0x59, 0x51, 0x45, 0xa0, 0xd9, 0x38, 0xa9, 0x69, 0xb2, 0x61, 0x74, 0xdb, 0x36, 0x9d, 0x44,
0x40, 0x29, 0x29, 0x97, 0x89, 0xb5, 0x65, 0xb9, 0x2c, 0x93, 0x9c, 0x6e, 0x58, 0x46, 0x1d, 0xbd, 0xb8, 0x62, 0x9c,
0x49, 0x46, 0x2a, 0x47, 0x64, 0xa4, 0xa2, 0x89, 0x7f, 0xb5, 0x16, 0xb4, 0xd3, 0x0b, 0xb2, 0x84, 0x35, 0x6f, 0x2c,
0x10, 0x29, 0xb2, 0x8e, 0xb5, 0x12, 0x29, 0x7b, 0x93, 0xba, 0xc9, 0xd7, 0x15, 0x4d, 0x5d, 0x97, 0x08, 0xb0, 0x4b,
0xb8, 0x8c, 0xe7, 0x74, 0x87, 0xa7, 0xb3, 0x68, 0x32, 0x9e, 0xe6, 0x13, 0xfc, 0x51, 0x7c, 0x03, 0x9e, 0xad, 0x6b,
0x50, 0x87, 0xab, 0x26, 0x23, 0x92, 0x35, 0x1c, 0x0b, 0x4a, 0xba, 0xac, 0x4c, 0x92, 0xc4, 0xfa, 0x5e, 0x90, 0x0d,
0xb5, 0xbe, 0xfd, 0xd6, 0x3e, 0x13, 0xad, 0xa8, 0x7c, 0x5d, 0x51, 0x35, 0x15, 0x2f, 0xf7, 0x37, 0x64, 0xf5, 0x33,
0x58, 0x6e, 0x5b, 0x44, 0xb0, 0x9c, 0x5a, 0xc3, 0xf7, 0xde, 0x07, 0x2c, 0xe4, 0xbe, 0xa2, 0x38, 0x67, 0xa2, 0xad,
0xc8, 0x3e, 0xb1, 0x96, 0x20, 0xf5, 0xce, 0x1a, 0x2e, 0x8a, 0x35, 0xcf, 0x94, 0x70, 0x24, 0x6c, 0x3a, 0x3c, 0x54,
0x14, 0xcc, 0x4b, 0xde, 0x12, 0x59, 0xe2, 0x9a, 0xec, 0x6c, 0x33, 0x61, 0xdc, 0x0e, 0xbe, 0xb3, 0xe9, 0x73, 0xdf,
0xf3, 0x86, 0x57, 0x7a, 0xf0, 0x86, 0x2e, 0xfc, 0x5d, 0x74, 0x54, 0xae, 0x3b, 0x8e, 0x88, 0x7d, 0x1b, 0xb7, 0x40,
0x89, 0xf2, 0xc4, 0xaa, 0xfd, 0x00, 0x7b, 0xde, 0x14, 0xf9, 0x33, 0x1c, 0x44, 0x8e, 0xef, 0xe3, 0xd0, 0xf1, 0xa3,
0x6c, 0xe2, 0x44, 0xc8, 0x1f, 0xc1, 0x10, 0x04, 0x38, 0x42, 0xde, 0x27, 0x0b, 0x15, 0xac, 0xaa, 0x12, 0x8b, 0x37,
0x9c, 0x5a, 0x48, 0xc8, 0xae, 0xb9, 0xa3, 0x89, 0x95, 0xad, 0xbb, 0x0e, 0xec, 0x7f, 0xd5, 0x54, 0x4d, 0x07, 0x70,
0x7d, 0x83, 0x3e, 0xfb, 0xf9, 0x6a, 0x15, 0xb2, 0x23, 0x5c, 0x14, 0x4d, 0x57, 0x27, 0x96, 0x7e, 0x29, 0xf6, 0xd3,
0x83, 0x3c, 0x22, 0x35, 0x0c, 0x2f, 0x1e, 0x3a, 0x4d, 0xc7, 0x56, 0x8c, 0x27, 0x96, 0x1f, 0x20, 0x7f, 0x0a, 0x6a,
0x6f, 0x87, 0xc7, 0x33, 0x26, 0x44, 0x61, 0xd2, 0x7b, 0xd9, 0xd8, 0xef, 0x6f, 0x63, 0xb1, 0x59, 0xa1, 0x5d, 0x5d,
0x71, 0x91, 0x58, 0xa5, 0x94, 0xed, 0xdc, 0x75, 0xb7, 0xdb, 0x2d, 0xde, 0x86, 0xb8, 0xe9, 0x56, 0x6e, 0xe0, 0x79,
0x9e, 0x0b, 0x14, 0x16, 0x32, 0xe7, 0xc3, 0x0a, 0x46, 0x16, 0x2a, 0x29, 0x5b, 0x95, 0x52, 0xcf, 0xd3, 0xa7, 0x07,
0x7a, 0x8c, 0x15, 0x45, 0x7a, 0xfb, 0xe1, 0x42, 0x4b, 0x77, 0xa1, 0x85, 0x7e, 0x7f, 0x81, 0xe6, 0xe0, 0xad, 0x32,
0x6a, 0x42, 0x02, 0x14, 0x20, 0x4f, 0xff, 0x0b, 0x1c, 0x35, 0xef, 0x57, 0xce, 0x83, 0x15, 0xba, 0x58, 0xc1, 0x5f,
0xc0, 0x2f, 0xa8, 0xc7, 0xce, 0xec, 0xcc, 0xee, 0xab, 0xc7, 0x1b, 0xdf, 0xbb, 0xdf, 0x50, 0x3c, 0x3f, 0x8e, 0x2f,
0xd7, 0x4e, 0xf0, 0x9b, 0x22, 0x50, 0xd8, 0x9f, 0x99, 0x9c, 0xa0, 0xf4, 0x7f, 0x1b, 0x93, 0x08, 0x45, 0xfd, 0x4e,
0xe4, 0xa8, 0xf9, 0x79, 0xa5, 0x34, 0xa1, 0x68, 0x03, 0x54, 0xb5, 0x33, 0x76, 0x22, 0x12, 0xa2, 0xb0, 0x37, 0x09,
0x66, 0xb0, 0x3d, 0x06, 0xe6, 0x8b, 0x3d, 0x27, 0xfc, 0x34, 0x50, 0x30, 0xcf, 0x2d, 0xeb, 0x1e, 0x83, 0xe6, 0x12,
0x03, 0xfc, 0xb1, 0x81, 0x33, 0x67, 0x59, 0x80, 0x11, 0x95, 0x59, 0x69, 0x5b, 0x2e, 0x44, 0x5e, 0xc1, 0x56, 0x10,
0x15, 0x0d, 0xb7, 0x86, 0x58, 0x96, 0x94, 0xdb, 0x27, 0x56, 0xc5, 0x48, 0xf5, 0x13, 0xfb, 0xe1, 0x13, 0x39, 0x3c,
0x9c, 0xe3, 0x43, 0x32, 0x09, 0x71, 0x28, 0xb1, 0x8a, 0xe8, 0xab, 0xf3, 0xee, 0xb2, 0xc9, 0xf7, 0x8f, 0x84, 0x4e,
0xe9, 0x9b, 0xb8, 0x61, 0x9c, 0xd3, 0xee, 0x86, 0xee, 0xe0, 0x1d, 0xfe, 0x83, 0xfd, 0xc0, 0xd0, 0xcf, 0x54, 0x6e,
0x9b, 0xee, 0x4e, 0xcc, 0x91, 0xf5, 0xdc, 0x88, 0x5b, 0xa8, 0xa8, 0x61, 0x20, 0x9b, 0xb4, 0x02, 0x8b, 0x0a, 0x72,
0x82, 0xed, 0x0f, 0x21, 0x7e, 0xda, 0x7b, 0x4b, 0xf8, 0xc9, 0xb9, 0xdb, 0x38, 0x67, 0x1b, 0x94, 0x55, 0x10, 0xf5,
0x70, 0xfc, 0x8d, 0x28, 0x0b, 0xf5, 0x47, 0xbd, 0xe1, 0x19, 0x70, 0xdf, 0x25, 0xd6, 0x17, 0xa2, 0xfa, 0xe5, 0xfe,
0xa7, 0xdc, 0x1e, 0x08, 0x88, 0xe7, 0xc1, 0x10, 0x6f, 0x48, 0xb5, 0xa6, 0x28, 0x41, 0xb2, 0x64, 0xe2, 0xde, 0xc0,
0xc5, 0xa3, 0x6c, 0xad, 0xb8, 0x03, 0xae, 0x02, 0x1e, 0x0b, 0x7b, 0x68, 0x9d, 0x22, 0x2b, 0x26, 0x26, 0xef, 0x59,
0x4f, 0xac, 0x07, 0x16, 0x39, 0x15, 0x2d, 0xa4, 0x75, 0x1f, 0x81, 0x4f, 0x0f, 0xc2, 0xe6, 0xb8, 0x03, 0xed, 0xc3,
0xe3, 0x79, 0x33, 0x16, 0x2d, 0xe1, 0x0f, 0x19, 0x95, 0x81, 0xea, 0xa0, 0x43, 0xb2, 0x82, 0x99, 0x3a, 0xed, 0x40,
0x74, 0x56, 0xe8, 0x92, 0xd3, 0xf4, 0xe9, 0xa1, 0x03, 0x89, 0x2a, 0x07, 0x9d, 0x25, 0xc6, 0x2e, 0x40, 0x93, 0xde,
0x1e, 0x87, 0xf7, 0x7e, 0xfc, 0xbe, 0xa6, 0xdd, 0xfe, 0x9a, 0x56, 0x34, 0x93, 0x4d, 0x67, 0x5b, 0x4f, 0x40, 0x0b,
0xbc, 0x7e, 0xed, 0xf0, 0x8f, 0x37, 0x6f, 0xdf, 0x24, 0x8d, 0xcd, 0x86, 0x57, 0x8f, 0x51, 0xab, 0x0c, 0xff, 0x1e,
0x32, 0xfc, 0x3f, 0x93, 0x81, 0xca, 0xf1, 0x83, 0x0f, 0xc0, 0xaa, 0xfd, 0xbd, 0xbd, 0x4f, 0xf4, 0x2a, 0x18, 0x9f,
0x43, 0x40, 0x5f, 0x29, 0x0f, 0x9d, 0x71, 0x34, 0x3c, 0x82, 0x7e, 0xb0, 0x00, 0xec, 0xd6, 0xb9, 0x1a, 0x72, 0xb6,
0x4a, 0x9b, 0xe9, 0x77, 0x87, 0x65, 0xb3, 0x73, 0x04, 0xfb, 0xc4, 0xf8, 0x6a, 0xce, 0x78, 0x49, 0x3b, 0x26, 0x8f,
0x60, 0x2e, 0xa4, 0xfd, 0x76, 0x2d, 0x0f, 0x2d, 0xc9, 0x73, 0xf5, 0x24, 0x6a, 0x77, 0x8b, 0x02, 0x8a, 0x84, 0xa2,
0xa4, 0x73, 0x9f, 0xd6, 0x47, 0xf3, 0x5c, 0xe7, 0x83, 0xf9, 0x2c, 0x7a, 0x76, 0x54, 0x07, 0xee, 0x20, 0xe1, 0x65,
0x39, 0xa4, 0x62, 0x2b, 0x3e, 0xcf, 0xc0, 0x70, 0xda, 0x19, 0xa6, 0x82, 0xd4, 0xac, 0xda, 0xcf, 0x05, 0x64, 0x26,
0x07, 0xaa, 0x07, 0x2b, 0x8e, 0xcb, 0xb5, 0x94, 0x0d, 0x07, 0xdd, 0x5d, 0x4e, 0xbb, 0xb9, 0xb7, 0x30, 0x13, 0xa7,
0x23, 0x39, 0x5b, 0x8b, 0x39, 0x0e, 0x3b, 0x5a, 0x2f, 0x96, 0x24, 0xbb, 0x5b, 0x75, 0xcd, 0x9a, 0xe7, 0x4e, 0xa6,
0x32, 0xe7, 0xfc, 0x89, 0x5f, 0x90, 0x90, 0x66, 0x8b, 0x7e, 0x55, 0x14, 0xc5, 0x02, 0xa0, 0xa0, 0x8e, 0xc9, 0x44,
0xf3, 0x00, 0x8f, 0x14, 0xdb, 0x85, 0x99, 0x38, 0x50, 0x1b, 0xc6, 0x46, 0x48, 0xeb, 0xcf, 0x16, 0x27, 0x77, 0xbc,
0x05, 0xa4, 0x64, 0x01, 0x42, 0x5a, 0x88, 0x47, 0x30, 0xf3, 0x58, 0x13, 0xc6, 0x2f, 0xad, 0x57, 0xc7, 0x64, 0xd1,
0x97, 0x14, 0x80, 0x45, 0xab, 0xd1, 0x85, 0x65, 0x01, 0x45, 0xc3, 0x14, 0xc6, 0x79, 0x30, 0xf6, 0xda, 0xdd, 0x11,
0xf7, 0x07, 0xe4, 0x70, 0xa2, 0x2e, 0x2a, 0xba, 0x5b, 0x7c, 0x5c, 0x0b, 0xc9, 0x8a, 0xbd, 0xd3, 0x17, 0xd6, 0x39,
0x1c, 0x16, 0x28, 0xa8, 0x4b, 0x20, 0xa5, 0x94, 0x2f, 0xb4, 0x0e, 0x87, 0x49, 0x5a, 0x8b, 0x1e, 0xa7, 0xb3, 0x18,
0x7d, 0x40, 0x3f, 0x97, 0xf5, 0x9f, 0xa8, 0xd5, 0x59, 0x3c, 0xd4, 0xa4, 0x83, 0x44, 0xef, 0x2c, 0x1b, 0xc0, 0xb4,
0x9e, 0x3b, 0x13, 0x78, 0x57, 0xfd, 0x96, 0x12, 0x06, 0x9e, 0x83, 0x99, 0xba, 0x5e, 0x9e, 0xf0, 0xf6, 0xdb, 0x1d,
0x12, 0x4d, 0xc5, 0xf2, 0x9e, 0x4e, 0x93, 0x20, 0xef, 0x0c, 0x8f, 0x0f, 0xaf, 0x1b, 0xa9, 0xbd, 0x13, 0xd4, 0xa3,
0x62, 0x4a, 0x7c, 0xef, 0x0b, 0x6f, 0x24, 0x2f, 0x8a, 0x60, 0x59, 0x9c, 0x91, 0x52, 0x65, 0xef, 0xc8, 0xfa, 0x53,
0x11, 0x8c, 0x40, 0xc0, 0xe9, 0xdd, 0xc0, 0xfc, 0xc8, 0x74, 0x58, 0x1c, 0x2e, 0xa4, 0xe8, 0xa3, 0x3a, 0x5f, 0x77,
0x95, 0x6d, 0x7d, 0xe1, 0xe8, 0x3e, 0x0b, 0x5f, 0xdd, 0x97, 0xa5, 0xc1, 0xe3, 0x65, 0x69, 0x80, 0x54, 0x23, 0xf3,
0xb2, 0xd9, 0x25, 0x03, 0x5d, 0x20, 0x46, 0xf0, 0x3b, 0x78, 0x16, 0xbe, 0x06, 0xfe, 0xff, 0x4a, 0xbd, 0xf9, 0xc3,
0xc5, 0xe6, 0x2b, 0x2a, 0xcd, 0x57, 0x56, 0x19, 0xe3, 0x9d, 0x72, 0x1e, 0x66, 0x50, 0x4e, 0x18, 0x16, 0x6c, 0xe5,
0xff, 0x2f, 0xa0, 0xfd, 0x77, 0x1c, 0xc3, 0x17, 0xfe, 0x14, 0xcf, 0x90, 0x1e, 0x0c, 0x44, 0x38, 0x9c, 0xa2, 0xc9,
0xab, 0x11, 0x1e, 0xf9, 0x48, 0xb5, 0x30, 0x63, 0x34, 0x81, 0x7e, 0x0f, 0xf9, 0x63, 0x1c, 0x4e, 0x60, 0x03, 0x05,
0x3e, 0x8e, 0xde, 0x04, 0x21, 0x1e, 0x47, 0x40, 0x15, 0x78, 0x38, 0x0c, 0x90, 0xa1, 0x1d, 0xe3, 0x00, 0xc4, 0x29,
0x92, 0xb0, 0x06, 0xa0, 0xb3, 0x10, 0x7b, 0x13, 0x10, 0x37, 0xc6, 0xde, 0x0c, 0x4f, 0xc7, 0x68, 0x8a, 0x27, 0x00,
0x1d, 0x1e, 0x45, 0x95, 0x13, 0x61, 0x1f, 0xb6, 0xc3, 0x31, 0x99, 0xe2, 0x51, 0x88, 0xf4, 0x60, 0xe0, 0x98, 0x80,
0x08, 0x07, 0x7b, 0xfe, 0x9b, 0x10, 0x07, 0x13, 0xd0, 0x3b, 0x1a, 0xbd, 0x00, 0xb1, 0xb3, 0x11, 0x32, 0xa3, 0x81,
0x17, 0x14, 0x44, 0x8f, 0x81, 0x16, 0xfc, 0x75, 0x41, 0x03, 0x48, 0x7c, 0x14, 0xe2, 0x19, 0xc4, 0xae, 0xaf, 0xf8,
0xcd, 0x68, 0x70, 0xf3, 0x7d, 0xe4, 0xfd, 0x61, 0xcc, 0xc2, 0xbf, 0x2e, 0x66, 0xbe, 0x42, 0x00, 0xa6, 0xa0, 0x1b,
0xe4, 0x20, 0x3d, 0x18, 0xdd, 0xc0, 0x3c, 0x7d, 0x35, 0x43, 0x53, 0xe0, 0x1a, 0x4f, 0xd1, 0x0c, 0x45, 0x0a, 0x5d,
0x60, 0x1f, 0x19, 0x26, 0x07, 0x98, 0xbe, 0x12, 0xc6, 0xd1, 0x9f, 0x18, 0xc6, 0xc7, 0x7c, 0xfa, 0x13, 0xbb, 0xf4,
0xff, 0x48, 0x41, 0xd0, 0x8e, 0xe9, 0x36, 0x2c, 0x76, 0xcd, 0x95, 0x5e, 0x75, 0x51, 0x70, 0x43, 0x87, 0x6e, 0x04,
0x2e, 0xf9, 0x3e, 0x62, 0x79, 0x52, 0xfa, 0xe9, 0x67, 0xdd, 0x39, 0x50, 0xfa, 0x69, 0xac, 0xcb, 0x79, 0x7a, 0x53,
0x52, 0xf4, 0xfa, 0xfa, 0x1d, 0xdc, 0xca, 0xaa, 0x0a, 0xf1, 0x66, 0x0b, 0x97, 0xbf, 0x3d, 0x92, 0x8d, 0xba, 0xce,
0x73, 0xe8, 0x15, 0xd5, 0x14, 0xee, 0x0d, 0xa8, 0x6f, 0x16, 0x30, 0xc6, 0xf1, 0xb2, 0x4b, 0xdf, 0x55, 0x94, 0x08,
0x8a, 0x56, 0x6c, 0x43, 0x11, 0x93, 0xd0, 0x07, 0xd4, 0x14, 0x49, 0xa6, 0x86, 0x33, 0xa3, 0xa6, 0x83, 0x9e, 0x56,
0x2b, 0x31, 0xdd, 0x30, 0x58, 0x02, 0x62, 0xd2, 0xbe, 0xed, 0x8d, 0xcb, 0xd0, 0x58, 0x75, 0x4d, 0xa5, 0x84, 0x8e,
0x41, 0x59, 0x15, 0xa6, 0xb1, 0xba, 0x76, 0x22, 0xa2, 0x2f, 0x06, 0x89, 0xbb, 0x65, 0x05, 0x53, 0x97, 0xf9, 0x34,
0xd6, 0xad, 0xa2, 0x92, 0xa0, 0xba, 0x15, 0xf3, 0xe5, 0x41, 0xcf, 0x2a, 0xca, 0x57, 0x70, 0x9b, 0x84, 0x77, 0x01,
0xcd, 0x43, 0x46, 0xcb, 0xa6, 0x82, 0xe6, 0x24, 0xb9, 0xbe, 0xfe, 0xe9, 0xef, 0xea, 0x33, 0x85, 0x32, 0xe1, 0xcc,
0x09, 0x7d, 0xbe, 0x61, 0x54, 0x93, 0x9e, 0x6f, 0x3c, 0x32, 0x1f, 0x1c, 0x5a, 0xe8, 0xd3, 0xc1, 0xbf, 0xfc, 0x33,
0x29, 0xef, 0x4e, 0x9b, 0xbd, 0x24, 0xfd, 0x5f, 0x37, 0x9d, 0x86, 0x49, 0xac, 0x97, 0x35, 0x93, 0xe9, 0x35, 0x18,
0x18, 0xbb, 0xe6, 0x01, 0x38, 0xa7, 0x1c, 0x30, 0xb4, 0x65, 0xcf, 0x03, 0x60, 0xff, 0x72, 0xf3, 0x02, 0xfd, 0xda,
0xc2, 0x09, 0xa6, 0x06, 0x7b, 0xed, 0x65, 0x4d, 0x65, 0xd9, 0xe4, 0xc9, 0xbb, 0x5f, 0xae, 0x6f, 0xce, 0x1e, 0xaf,
0x35, 0x11, 0xa2, 0x3c, 0x33, 0x1f, 0x42, 0xd6, 0x95, 0x64, 0x2d, 0xe9, 0xa4, 0x16, 0xeb, 0xa8, 0x10, 0x38, 0x79,
0xa4, 0x9f, 0x17, 0xac, 0xa2, 0xc6, 0xa9, 0x9e, 0xd1, 0x4d, 0xd1, 0x97, 0x6c, 0x3c, 0xe9, 0x7e, 0x60, 0xa5, 0x6b,
0x4e, 0x89, 0x6b, 0x8e, 0x8c, 0xab, 0x3f, 0x13, 0xfd, 0x0b, 0x65, 0x37, 0xa3, 0x8e, 0x36, 0x12, 0x00, 0x00};
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xdd, 0x58, 0x69, 0x6f, 0xdc, 0x36, 0x1a, 0xfe, 0xde,
0x5f, 0xc1, 0x2a, 0x49, 0x47, 0xd3, 0x58, 0xd4, 0x35, 0x9a, 0x53, 0x9a, 0xc2, 0xf1, 0xa6, 0x68, 0x81, 0xa4, 0x0d,
0x60, 0xb7, 0xfd, 0x10, 0x04, 0x30, 0x47, 0xa2, 0x46, 0x8c, 0x25, 0x4a, 0x2b, 0x72, 0xae, 0x0c, 0x66, 0x7f, 0xfb,
0xbe, 0x24, 0x35, 0xe3, 0xb1, 0x37, 0x5e, 0x6c, 0x8a, 0x2d, 0x8a, 0xd6, 0x71, 0x68, 0x1e, 0xef, 0xf9, 0x88, 0xef,
0x21, 0xc5, 0x5f, 0x67, 0x75, 0x2a, 0x77, 0x0d, 0x45, 0x85, 0xac, 0xca, 0x79, 0xac, 0x46, 0x54, 0x12, 0xbe, 0x4c,
0x28, 0x87, 0x15, 0x25, 0xd9, 0x3c, 0xae, 0xa8, 0x24, 0x28, 0x2d, 0x48, 0x2b, 0xa8, 0x4c, 0x7e, 0xb9, 0xf9, 0xde,
0x19, 0x23, 0x77, 0x1e, 0x97, 0x8c, 0xdf, 0xa1, 0x96, 0x96, 0x09, 0x4b, 0x6b, 0x8e, 0x8a, 0x96, 0xe6, 0x49, 0x46,
0x24, 0x99, 0xb2, 0x8a, 0x2c, 0xa9, 0x22, 0xd0, 0x6c, 0x9c, 0x54, 0x34, 0x59, 0x33, 0xba, 0x69, 0xea, 0x56, 0x22,
0xa0, 0x94, 0x94, 0xcb, 0xc4, 0xda, 0xb0, 0x4c, 0x16, 0x49, 0x46, 0xd7, 0x2c, 0xa5, 0x8e, 0x5e, 0x5c, 0x30, 0xce,
0x24, 0x23, 0xa5, 0x23, 0x52, 0x52, 0xd2, 0xc4, 0xbf, 0x58, 0x09, 0xda, 0xea, 0x05, 0x59, 0xc0, 0x9a, 0xd7, 0x16,
0x88, 0x14, 0x69, 0xcb, 0x1a, 0x89, 0x94, 0xbd, 0x49, 0x55, 0x67, 0xab, 0x92, 0xce, 0x5d, 0x97, 0x08, 0xb0, 0x4b,
0xb8, 0x8c, 0x67, 0x74, 0x8b, 0x87, 0x61, 0x98, 0x06, 0x64, 0x94, 0xe3, 0x8f, 0xe2, 0x2b, 0xf0, 0x6c, 0x55, 0x81,
0x3a, 0x5c, 0xd6, 0x29, 0x91, 0xac, 0xe6, 0x58, 0x50, 0xd2, 0xa6, 0x45, 0x92, 0x24, 0xd6, 0x77, 0x82, 0xac, 0xa9,
0xf5, 0xcd, 0x37, 0xf6, 0x89, 0x68, 0x49, 0xe5, 0xeb, 0x92, 0xaa, 0xa9, 0x78, 0xb5, 0xbb, 0x21, 0xcb, 0x9f, 0xc0,
0x72, 0xdb, 0x22, 0x82, 0x65, 0xd4, 0xea, 0xbf, 0xf7, 0x3e, 0x60, 0x21, 0x77, 0x25, 0xc5, 0x19, 0x13, 0x4d, 0x49,
0x76, 0x89, 0xb5, 0x00, 0xa9, 0x77, 0x56, 0x7f, 0x96, 0xaf, 0x78, 0xaa, 0x84, 0x23, 0x61, 0xd3, 0xfe, 0xbe, 0xa4,
0x60, 0x5e, 0xf2, 0x96, 0xc8, 0x02, 0x57, 0x64, 0x6b, 0x9b, 0x09, 0xe3, 0x76, 0xf0, 0xad, 0x4d, 0x5f, 0xfa, 0x9e,
0xd7, 0xbf, 0xd0, 0x83, 0xd7, 0x77, 0xe1, 0xef, 0xac, 0xa5, 0x72, 0xd5, 0x72, 0x44, 0xec, 0xdb, 0xb8, 0x01, 0x4a,
0x94, 0x25, 0x56, 0xe5, 0x07, 0xd8, 0xf3, 0xc6, 0xc8, 0x9f, 0xe0, 0x20, 0x72, 0x7c, 0x1f, 0x87, 0x8e, 0x1f, 0xa5,
0x23, 0x27, 0x42, 0xfe, 0x00, 0x86, 0x20, 0xc0, 0x11, 0xf2, 0x3e, 0x59, 0x28, 0x67, 0x65, 0x99, 0x58, 0xbc, 0xe6,
0xd4, 0x42, 0x42, 0xb6, 0xf5, 0x1d, 0x4d, 0xac, 0x74, 0xd5, 0xb6, 0x60, 0xff, 0x55, 0x5d, 0xd6, 0x2d, 0xc0, 0xf5,
0x15, 0x7a, 0xf0, 0xf3, 0xc5, 0x2a, 0x64, 0x4b, 0xb8, 0xc8, 0xeb, 0xb6, 0x4a, 0x2c, 0xfd, 0x50, 0xec, 0xe7, 0x7b,
0x79, 0x40, 0x6a, 0xe8, 0x9f, 0x1d, 0x3a, 0x75, 0xcb, 0x96, 0x8c, 0x27, 0x96, 0x1f, 0x20, 0x7f, 0x0c, 0x6a, 0x6f,
0xfb, 0x87, 0x13, 0x26, 0x44, 0x61, 0xd2, 0x79, 0x59, 0xdb, 0xef, 0x6f, 0x63, 0xb1, 0x5e, 0xa2, 0x6d, 0x55, 0x72,
0x91, 0x58, 0x85, 0x94, 0xcd, 0xd4, 0x75, 0x37, 0x9b, 0x0d, 0xde, 0x84, 0xb8, 0x6e, 0x97, 0x6e, 0xe0, 0x79, 0x9e,
0x0b, 0x14, 0x16, 0x32, 0xf7, 0xc3, 0x0a, 0x06, 0x16, 0x2a, 0x28, 0x5b, 0x16, 0x52, 0xcf, 0xe7, 0xcf, 0xf7, 0xf4,
0x10, 0x2b, 0x8a, 0xf9, 0xed, 0x87, 0x33, 0x2d, 0xec, 0x4c, 0x0b, 0xfd, 0xee, 0x0c, 0xcd, 0xde, 0x5b, 0x65, 0xd4,
0x88, 0x04, 0x28, 0x40, 0x9e, 0xfe, 0x17, 0x38, 0x6a, 0xde, 0xad, 0x9c, 0x47, 0x2b, 0x74, 0xb6, 0x82, 0xbf, 0x80,
0x5f, 0x50, 0x0d, 0x9d, 0xc9, 0x89, 0xdd, 0x57, 0xc7, 0x6b, 0xdf, 0xbb, 0xdf, 0x50, 0x3c, 0x3f, 0x0c, 0xcf, 0xd7,
0x4e, 0xf0, 0xab, 0x22, 0x50, 0xd8, 0x9f, 0x98, 0x9c, 0xa0, 0xf0, 0x7f, 0x1d, 0x92, 0x08, 0x45, 0xdd, 0x4e, 0xe4,
0xa8, 0xf9, 0x69, 0xa5, 0x34, 0xa1, 0x68, 0x0d, 0x54, 0x95, 0x33, 0x74, 0x22, 0x12, 0xa2, 0xb0, 0x33, 0x09, 0x66,
0xb0, 0x3d, 0x04, 0xe6, 0xb3, 0x3d, 0x27, 0xfc, 0xd4, 0x53, 0x30, 0x4f, 0x2d, 0xeb, 0x1e, 0x83, 0xfa, 0x1c, 0x03,
0xfc, 0xb1, 0x86, 0x3b, 0x67, 0x59, 0x80, 0x11, 0x95, 0x69, 0x61, 0x5b, 0x2e, 0x44, 0x5e, 0xce, 0x96, 0x10, 0x15,
0x35, 0xb7, 0xfa, 0x58, 0x16, 0x94, 0xdb, 0x47, 0x56, 0xc5, 0x48, 0xf5, 0x89, 0xfd, 0xf8, 0x44, 0xf6, 0xf7, 0xa7,
0xf8, 0x90, 0x4c, 0x42, 0x1c, 0x4a, 0xac, 0x22, 0xfa, 0xe2, 0xb4, 0xbb, 0xa8, 0xb3, 0xdd, 0x13, 0xa1, 0x53, 0xf8,
0x26, 0x6e, 0x18, 0xe7, 0xb4, 0xbd, 0xa1, 0x5b, 0x78, 0x86, 0x6f, 0x2f, 0xaf, 0xd0, 0x65, 0x96, 0xb5, 0x54, 0x88,
0x29, 0xb2, 0x5e, 0x4a, 0x88, 0x91, 0xf4, 0x7f, 0x97, 0xe5, 0x3f, 0x90, 0xf5, 0x1b, 0xfb, 0x9e, 0xa1, 0x9f, 0xa8,
0xdc, 0xd4, 0xed, 0x5d, 0x27, 0x4d, 0x99, 0x36, 0x53, 0x11, 0xd8, 0x82, 0x9d, 0xa4, 0x11, 0x58, 0x94, 0x90, 0x5f,
0x6c, 0xbf, 0x0f, 0x7a, 0x9a, 0x7b, 0xaf, 0xf8, 0x11, 0xa8, 0xdb, 0x38, 0x63, 0x6b, 0x94, 0x96, 0x90, 0x41, 0x20,
0x94, 0x8c, 0x28, 0x0b, 0x75, 0x61, 0x53, 0xf3, 0x14, 0xb8, 0xef, 0x12, 0xeb, 0x33, 0x19, 0xe2, 0xd5, 0xee, 0xc7,
0xcc, 0xee, 0x09, 0xc8, 0x0d, 0xbd, 0x3e, 0x5e, 0x93, 0x72, 0x45, 0x51, 0x82, 0x64, 0xc1, 0xc4, 0xbd, 0x81, 0xb3,
0x27, 0xd9, 0x1a, 0x71, 0x07, 0x5c, 0x39, 0x1c, 0x0b, 0xbb, 0x6f, 0x1d, 0xa3, 0x34, 0x26, 0x26, 0x87, 0x5a, 0xcf,
0xac, 0x47, 0x16, 0x39, 0x25, 0xcd, 0xa5, 0x75, 0x1f, 0xcd, 0xcf, 0xf7, 0xc2, 0xe6, 0xb8, 0x05, 0xed, 0xfd, 0xc3,
0x69, 0x33, 0x16, 0x0d, 0xe1, 0x8f, 0x19, 0x95, 0x81, 0x2a, 0x68, 0x20, 0xf1, 0xc1, 0x4c, 0x45, 0x0e, 0x10, 0x9d,
0x14, 0xba, 0xe4, 0x38, 0x7d, 0xbe, 0x67, 0x20, 0x51, 0xe5, 0xb3, 0x93, 0xc4, 0xd8, 0x05, 0x68, 0xe6, 0xb7, 0x87,
0xfe, 0xbd, 0x1f, 0xff, 0x5c, 0xd1, 0x76, 0x77, 0x4d, 0x4b, 0x9a, 0xca, 0xba, 0xb5, 0xad, 0x67, 0xa0, 0x05, 0xae,
0x92, 0x76, 0xf8, 0x87, 0x9b, 0xb7, 0x6f, 0x92, 0xda, 0x6e, 0xfb, 0x17, 0x4f, 0x51, 0xab, 0x6a, 0xf1, 0x1e, 0xaa,
0xc5, 0xbf, 0x92, 0x9e, 0xaa, 0x17, 0xbd, 0x0f, 0xc0, 0xaa, 0xfd, 0xbd, 0xbd, 0x2f, 0x1a, 0x2a, 0xb0, 0x5f, 0x42,
0x72, 0xb8, 0x50, 0x1e, 0x3a, 0xc3, 0xa8, 0x7f, 0x00, 0xfd, 0x60, 0x01, 0xd8, 0xad, 0xf3, 0x3e, 0xe4, 0x7f, 0x95,
0x82, 0xe7, 0xdf, 0xee, 0x17, 0xf5, 0xd6, 0x11, 0xec, 0x13, 0xe3, 0xcb, 0x29, 0xe3, 0x05, 0x6d, 0x99, 0x3c, 0x80,
0xb9, 0x50, 0x42, 0x9a, 0x95, 0xdc, 0x37, 0x24, 0xcb, 0xd4, 0x49, 0xd4, 0x6c, 0x67, 0x39, 0x14, 0x1c, 0x45, 0x49,
0xa7, 0x3e, 0xad, 0x0e, 0xe6, 0x5c, 0xe7, 0x96, 0xe9, 0x24, 0x7a, 0x71, 0x50, 0x17, 0x6e, 0x2f, 0xe1, 0x61, 0x39,
0xa4, 0x64, 0x4b, 0x3e, 0x4d, 0xc1, 0x70, 0xda, 0x1a, 0xa6, 0x9c, 0x54, 0xac, 0xdc, 0x4d, 0x05, 0x64, 0x39, 0x07,
0x2a, 0x11, 0xcb, 0x0f, 0x8b, 0x95, 0x94, 0x35, 0x07, 0xdd, 0x6d, 0x46, 0xdb, 0xa9, 0x37, 0x33, 0x13, 0xa7, 0x25,
0x19, 0x5b, 0x89, 0x29, 0x0e, 0x5b, 0x5a, 0xcd, 0x16, 0x24, 0xbd, 0x5b, 0xb6, 0xf5, 0x8a, 0x67, 0x4e, 0xaa, 0xb2,
0xf0, 0xf4, 0x99, 0x9f, 0x93, 0x90, 0xa6, 0xb3, 0x6e, 0x95, 0xe7, 0xf9, 0x0c, 0xa0, 0xa0, 0x8e, 0xc9, 0x6a, 0xd3,
0x00, 0x0f, 0x14, 0xdb, 0x99, 0x99, 0x38, 0x50, 0x1b, 0xc6, 0x46, 0x28, 0x11, 0x2f, 0x66, 0x47, 0x77, 0xbc, 0x19,
0xa4, 0x77, 0x01, 0x42, 0x1a, 0x88, 0x6d, 0x30, 0xf3, 0x50, 0x11, 0xc6, 0xcf, 0xad, 0x57, 0xd7, 0x64, 0xd6, 0x95,
0x27, 0x80, 0x45, 0xab, 0xd1, 0x45, 0x6a, 0x06, 0x05, 0xc8, 0x14, 0xd9, 0x69, 0x30, 0xf4, 0x9a, 0xed, 0x01, 0x77,
0x17, 0x64, 0x7f, 0xa4, 0xce, 0x4b, 0xba, 0x9d, 0x7d, 0x5c, 0x09, 0xc9, 0xf2, 0x9d, 0xd3, 0x15, 0xe9, 0x29, 0x5c,
0x16, 0x28, 0xce, 0x0b, 0x20, 0xa5, 0x94, 0xcf, 0xb4, 0x0e, 0x87, 0x49, 0x5a, 0x89, 0x0e, 0xa7, 0x93, 0x18, 0x7d,
0x41, 0x1f, 0xca, 0xfa, 0x6f, 0xd4, 0xea, 0x2e, 0xee, 0x2b, 0xd2, 0x42, 0xd1, 0x70, 0x16, 0x35, 0x60, 0x5a, 0x4d,
0x9d, 0x11, 0x3c, 0xab, 0x6e, 0x4b, 0x09, 0x03, 0xcf, 0xc1, 0x4c, 0x5d, 0x7b, 0x8f, 0x78, 0xfb, 0xcd, 0x16, 0x89,
0xba, 0x64, 0x59, 0x47, 0xa7, 0x49, 0x90, 0x77, 0x82, 0xc7, 0x87, 0xc7, 0x8d, 0xd4, 0xde, 0x11, 0xea, 0x41, 0x3e,
0x26, 0xbe, 0xf7, 0x99, 0x27, 0x92, 0xe5, 0x79, 0xb0, 0xc8, 0x4f, 0x48, 0xa9, 0x12, 0x7a, 0x60, 0xdd, 0xad, 0x08,
0x06, 0x20, 0xe0, 0xf8, 0x6c, 0x60, 0x7e, 0x60, 0x3a, 0x2c, 0xf6, 0x67, 0x52, 0xf4, 0x55, 0x9d, 0xae, 0xda, 0xd2,
0xb6, 0x3e, 0x73, 0x75, 0x5f, 0x84, 0x57, 0xf7, 0x25, 0xae, 0xf7, 0x74, 0x89, 0xeb, 0x21, 0xd5, 0x14, 0xbd, 0xaa,
0xb7, 0x49, 0x4f, 0x17, 0x9b, 0x01, 0xfc, 0xf6, 0x5e, 0x84, 0xaf, 0x81, 0xff, 0xff, 0x52, 0xbb, 0x7e, 0x77, 0xe1,
0xfa, 0x82, 0xaa, 0xf5, 0x85, 0x15, 0xcb, 0x78, 0xa7, 0x9c, 0x87, 0x19, 0x94, 0x26, 0x86, 0x05, 0x5b, 0xfa, 0x7f,
0x04, 0xb4, 0xff, 0x89, 0x63, 0x78, 0xe9, 0x8f, 0xf1, 0x04, 0xe9, 0xc1, 0x40, 0x84, 0xc3, 0x31, 0x1a, 0x5d, 0x0d,
0xf0, 0xc0, 0x47, 0xaa, 0x1d, 0x1a, 0xa2, 0x11, 0x1e, 0x03, 0xc1, 0x10, 0x87, 0x23, 0xd8, 0x40, 0x81, 0x8f, 0xa3,
0x37, 0x41, 0x88, 0x87, 0x11, 0x50, 0x05, 0x1e, 0x0e, 0x03, 0x64, 0x68, 0x87, 0x38, 0x00, 0x71, 0x8a, 0x24, 0xac,
0x00, 0xe8, 0x34, 0xc4, 0xde, 0x08, 0xc4, 0x0d, 0xb1, 0x37, 0xc1, 0xe3, 0x21, 0x1a, 0xe3, 0x11, 0x40, 0x87, 0x07,
0x51, 0xe9, 0x44, 0xd8, 0x87, 0xed, 0x70, 0x48, 0xc6, 0x78, 0x10, 0x22, 0x3d, 0x18, 0x38, 0x46, 0x20, 0xc2, 0xc1,
0x9e, 0xff, 0x26, 0xc4, 0xc1, 0x08, 0xf4, 0x0e, 0x06, 0x97, 0x20, 0x76, 0x32, 0x40, 0x66, 0x34, 0xf0, 0x82, 0x82,
0xe8, 0x29, 0xd0, 0x82, 0xbf, 0x2f, 0x68, 0x00, 0x89, 0x8f, 0x42, 0x3c, 0x81, 0xd8, 0xf5, 0x15, 0xbf, 0x19, 0x0d,
0x6e, 0xbe, 0x8f, 0xbc, 0xdf, 0x8d, 0x59, 0xf8, 0xf7, 0xc5, 0xcc, 0x57, 0x08, 0xc0, 0x14, 0x74, 0x83, 0x1c, 0xa4,
0x07, 0xa3, 0x1b, 0x98, 0xc7, 0x57, 0x13, 0x34, 0x06, 0xae, 0xe1, 0x18, 0x4d, 0x50, 0xa4, 0xd0, 0x05, 0xf6, 0x81,
0x61, 0x72, 0x80, 0xe9, 0x0b, 0x61, 0x1c, 0xfc, 0x85, 0x61, 0x7c, 0xca, 0xa7, 0xbf, 0xb0, 0x4b, 0x7f, 0x46, 0x0a,
0x82, 0x76, 0x4c, 0xb7, 0x61, 0xb1, 0x6b, 0x3e, 0x0f, 0xa8, 0x2e, 0x0a, 0xde, 0xf6, 0xa1, 0x1b, 0x99, 0xc7, 0x85,
0x8f, 0x58, 0x96, 0x40, 0x57, 0x3f, 0x3f, 0x6b, 0xf5, 0x81, 0xd0, 0x3f, 0x1e, 0xc1, 0xec, 0x41, 0xe3, 0x6e, 0xce,
0x74, 0xa5, 0x9f, 0xdf, 0x14, 0x14, 0xbd, 0xbe, 0x7e, 0x07, 0x2f, 0x7f, 0x65, 0x89, 0x78, 0xbd, 0x81, 0x77, 0xcc,
0x1d, 0x92, 0xb5, 0xfa, 0x6a, 0xc0, 0xa1, 0x8d, 0x54, 0x53, 0x78, 0x3d, 0x41, 0x5d, 0x1f, 0x81, 0x31, 0x8e, 0x17,
0xed, 0xfc, 0x5d, 0x49, 0x89, 0xa0, 0x68, 0xc9, 0xd6, 0x14, 0x31, 0x09, 0x2d, 0x42, 0x45, 0x91, 0x64, 0x6a, 0x38,
0x31, 0x6a, 0x3a, 0x68, 0x77, 0xb5, 0x12, 0xd3, 0x28, 0x83, 0x25, 0x20, 0x66, 0xde, 0x75, 0xc4, 0x71, 0x11, 0x1a,
0xab, 0xae, 0xa9, 0x94, 0xd0, 0x4c, 0x28, 0xab, 0xc2, 0x79, 0xac, 0xde, 0x6e, 0x11, 0xd1, 0xef, 0x0c, 0x89, 0xbb,
0x61, 0x39, 0x53, 0xdf, 0x0c, 0xe6, 0xb1, 0xee, 0x22, 0x95, 0x04, 0xd5, 0xc8, 0x98, 0x0f, 0x1c, 0x7a, 0x56, 0x52,
0xbe, 0x84, 0x97, 0x56, 0x78, 0x4c, 0xd0, 0x57, 0xa4, 0xb4, 0xa8, 0x4b, 0xe8, 0x5b, 0x92, 0xeb, 0xeb, 0x1f, 0xff,
0xa1, 0xbe, 0x86, 0x28, 0x13, 0x4e, 0x9c, 0xf0, 0x0a, 0x60, 0x18, 0xd5, 0xa4, 0xe3, 0x1b, 0x0e, 0xcc, 0x77, 0x8d,
0x06, 0x5a, 0x78, 0xf0, 0x2f, 0x7b, 0x20, 0xe5, 0xdd, 0x71, 0xb3, 0x93, 0xa4, 0xff, 0xeb, 0x7e, 0xd4, 0x30, 0x89,
0xd5, 0xa2, 0x62, 0x72, 0x7e, 0x0d, 0x06, 0xc6, 0xae, 0x39, 0x00, 0xe7, 0x94, 0x03, 0x86, 0xb6, 0xe8, 0x78, 0x00,
0xec, 0x9f, 0x6f, 0x2e, 0xd1, 0x2f, 0x0d, 0x5c, 0x6e, 0x6a, 0xb0, 0xd7, 0x5e, 0x56, 0x54, 0x16, 0x75, 0x96, 0xbc,
0xfb, 0xf9, 0xfa, 0xe6, 0xe4, 0xf1, 0x4a, 0x13, 0x21, 0xca, 0x53, 0xf3, 0xbd, 0x65, 0x55, 0x4a, 0xd6, 0x90, 0x56,
0x6a, 0xb1, 0x8e, 0x8a, 0x8e, 0xa3, 0x47, 0xfa, 0x3c, 0x67, 0x25, 0x35, 0x4e, 0x75, 0x8c, 0xee, 0x1c, 0x7d, 0xce,
0xc6, 0xa3, 0xee, 0x47, 0x56, 0xba, 0xe6, 0x02, 0xb9, 0xe6, 0x36, 0xb9, 0xfa, 0x6b, 0xd4, 0xbf, 0x01, 0x14, 0xee,
0xbc, 0x64, 0x9d, 0x12, 0x00, 0x00};
} // namespace captive_portal
} // namespace esphome
@@ -12,7 +12,7 @@ static const char *const TAG = "captive_portal";
void CaptivePortal::handle_config(AsyncWebServerRequest *request) {
AsyncResponseStream *stream = request->beginResponseStream("application/json");
stream->addHeader("cache-control", "public, max-age=0, must-revalidate");
stream->printf(R"({"name":"%s","aps":[{})", App.get_name().c_str());
stream->printf(R"({"mac":"%s","name":"%s","aps":[{})", get_mac_address_pretty().c_str(), App.get_name().c_str());
for (auto &scan : wifi::global_wifi_component->get_scan_result()) {
if (scan.get_is_hidden())
@@ -0,0 +1 @@
CODEOWNERS = ["@MagicBear"]
+18
View File
@@ -0,0 +1,18 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import climate_ir
from esphome.const import CONF_ID
AUTO_LOAD = ["climate_ir"]
daikin_arc_ns = cg.esphome_ns.namespace("daikin_arc")
DaikinArcClimate = daikin_arc_ns.class_("DaikinArcClimate", climate_ir.ClimateIR)
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
{cv.GenerateID(): cv.declare_id(DaikinArcClimate)}
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await climate_ir.register_climate_ir(var, config)
@@ -0,0 +1,487 @@
#include "daikin_arc.h"
#include <cmath>
#include "esphome/components/remote_base/remote_base.h"
#include "esphome/core/log.h"
namespace esphome {
namespace daikin_arc {
static const char *const TAG = "daikin.climate";
void DaikinArcClimate::setup() {
climate_ir::ClimateIR::setup();
// Never send nan to HA
if (std::isnan(this->target_humidity))
this->target_humidity = 0;
if (std::isnan(this->current_temperature))
this->current_temperature = 0;
if (std::isnan(this->current_humidity))
this->current_humidity = 0;
}
void DaikinArcClimate::transmit_query_() {
uint8_t remote_header[8] = {0x11, 0xDA, 0x27, 0x00, 0x84, 0x87, 0x20, 0x00};
// Calculate checksum
for (int i = 0; i < sizeof(remote_header) - 1; i++) {
remote_header[sizeof(remote_header) - 1] += remote_header[i];
}
auto transmit = this->transmitter_->transmit();
auto *data = transmit.get_data();
data->set_carrier_frequency(DAIKIN_IR_FREQUENCY);
data->mark(DAIKIN_ARC_PRE_MARK);
data->space(DAIKIN_ARC_PRE_SPACE);
data->mark(DAIKIN_HEADER_MARK);
data->space(DAIKIN_HEADER_SPACE);
for (uint8_t i : remote_header) {
for (uint8_t mask = 1; mask > 0; mask <<= 1) { // iterate through bit mask
data->mark(DAIKIN_BIT_MARK);
bool bit = i & mask;
data->space(bit ? DAIKIN_ONE_SPACE : DAIKIN_ZERO_SPACE);
}
}
data->mark(DAIKIN_BIT_MARK);
data->space(0);
transmit.perform();
}
void DaikinArcClimate::transmit_state() {
// 0x11, 0xDA, 0x27, 0x00, 0xC5, 0x00, 0x00, 0xD7, 0x11, 0xDA, 0x27, 0x00,
// 0x42, 0x49, 0x05, 0xA2,
uint8_t remote_header[20] = {0x11, 0xDA, 0x27, 0x00, 0x02, 0xd0, 0x02, 0x03, 0x80, 0x03, 0x82, 0x30, 0x41, 0x1f, 0x82,
0xf4,
/* とつど */
/* 0x13 */
0x00, 0x24, 0x00, 0x00};
// 05 0 [1:3]MODE 1 [OFF TMR] [ON TMR] Power
// 06-07 TEMP
// 08 [0:3] SPEED [4:7] Swing
// 09 00
// 10 00
// 11, 12: timer
// 13 [0:6] 0000000 [7] POWERMODE
// 14 0a
// 15 c4
// 16 [0:3] 8 00 [6:7] SENSOR WIND = 11 / NORMAL = 00
// 17 24
uint8_t remote_state[19] = {
0x11, 0xDA, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x60, 0x00, 0x0a, 0xC4,
/* MODE TEMP HUMD FANH FANL
*/
/* ON
0x01 0x0a */
/* OF
0x00 0x02 */
0x80, 0x24, 0x00
/* センサー風 */
/* ON 0x83 */
/* OF 0x80 */
};
remote_state[5] = this->operation_mode_() | 0x08;
remote_state[6] = this->temperature_();
remote_state[7] = this->humidity_();
static uint8_t last_humidity = 0x66;
if (remote_state[7] != last_humidity && this->mode != climate::CLIMATE_MODE_OFF) {
ESP_LOGD(TAG, "Set Humditiy: %d, %d\n", (int) this->target_humidity, (int) remote_state[7]);
remote_header[9] |= 0x10;
last_humidity = remote_state[7];
}
uint16_t fan_speed = this->fan_speed_();
remote_state[8] = fan_speed >> 8;
remote_state[9] = fan_speed & 0xff;
// Calculate checksum
for (int i = 0; i < sizeof(remote_header) - 1; i++) {
remote_header[sizeof(remote_header) - 1] += remote_header[i];
}
// Calculate checksum
for (int i = 0; i < DAIKIN_STATE_FRAME_SIZE - 1; i++) {
remote_state[DAIKIN_STATE_FRAME_SIZE - 1] += remote_state[i];
}
auto transmit = this->transmitter_->transmit();
auto *data = transmit.get_data();
data->set_carrier_frequency(DAIKIN_IR_FREQUENCY);
data->mark(DAIKIN_ARC_PRE_MARK);
data->space(DAIKIN_ARC_PRE_SPACE);
data->mark(DAIKIN_HEADER_MARK);
data->space(DAIKIN_HEADER_SPACE);
for (uint8_t i : remote_header) {
for (uint8_t mask = 1; mask > 0; mask <<= 1) { // iterate through bit mask
data->mark(DAIKIN_BIT_MARK);
bool bit = i & mask;
data->space(bit ? DAIKIN_ONE_SPACE : DAIKIN_ZERO_SPACE);
}
}
data->mark(DAIKIN_BIT_MARK);
data->space(DAIKIN_MESSAGE_SPACE);
data->mark(DAIKIN_HEADER_MARK);
data->space(DAIKIN_HEADER_SPACE);
for (uint8_t i : remote_state) {
for (uint8_t mask = 1; mask > 0; mask <<= 1) { // iterate through bit mask
data->mark(DAIKIN_BIT_MARK);
bool bit = i & mask;
data->space(bit ? DAIKIN_ONE_SPACE : DAIKIN_ZERO_SPACE);
}
}
data->mark(DAIKIN_BIT_MARK);
data->space(0);
transmit.perform();
}
uint8_t DaikinArcClimate::operation_mode_() {
uint8_t operating_mode = DAIKIN_MODE_ON;
switch (this->mode) {
case climate::CLIMATE_MODE_COOL:
operating_mode |= DAIKIN_MODE_COOL;
break;
case climate::CLIMATE_MODE_DRY:
operating_mode |= DAIKIN_MODE_DRY;
break;
case climate::CLIMATE_MODE_HEAT:
operating_mode |= DAIKIN_MODE_HEAT;
break;
case climate::CLIMATE_MODE_HEAT_COOL:
operating_mode |= DAIKIN_MODE_AUTO;
break;
case climate::CLIMATE_MODE_FAN_ONLY:
operating_mode |= DAIKIN_MODE_FAN;
break;
case climate::CLIMATE_MODE_OFF:
default:
operating_mode = DAIKIN_MODE_OFF;
break;
}
return operating_mode;
}
uint16_t DaikinArcClimate::fan_speed_() {
uint16_t fan_speed;
switch (this->fan_mode.value()) {
case climate::CLIMATE_FAN_LOW:
fan_speed = DAIKIN_FAN_1 << 8;
break;
case climate::CLIMATE_FAN_MEDIUM:
fan_speed = DAIKIN_FAN_3 << 8;
break;
case climate::CLIMATE_FAN_HIGH:
fan_speed = DAIKIN_FAN_5 << 8;
break;
case climate::CLIMATE_FAN_AUTO:
default:
fan_speed = DAIKIN_FAN_AUTO << 8;
}
// If swing is enabled switch first 4 bits to 1111
switch (this->swing_mode) {
case climate::CLIMATE_SWING_VERTICAL:
fan_speed |= 0x0F00;
break;
case climate::CLIMATE_SWING_HORIZONTAL:
fan_speed |= 0x000F;
break;
case climate::CLIMATE_SWING_BOTH:
fan_speed |= 0x0F0F;
break;
default:
break;
}
return fan_speed;
}
uint8_t DaikinArcClimate::temperature_() {
// Force special temperatures depending on the mode
switch (this->mode) {
case climate::CLIMATE_MODE_FAN_ONLY:
return 0x32;
case climate::CLIMATE_MODE_HEAT_COOL:
case climate::CLIMATE_MODE_DRY:
return 0xc0;
default:
float new_temp = clamp<float>(this->target_temperature, DAIKIN_TEMP_MIN, DAIKIN_TEMP_MAX);
uint8_t temperature = (uint8_t) floor(new_temp);
return temperature << 1 | (new_temp - temperature > 0 ? 0x01 : 0);
}
}
uint8_t DaikinArcClimate::humidity_() {
if (this->target_humidity == 39) {
return 0;
} else if (this->target_humidity <= 40 || this->target_humidity == 44) {
return 40;
} else if (this->target_humidity <= 45 || this->target_humidity == 49) // 41 - 45
{
return 45;
} else if (this->target_humidity <= 50 || this->target_humidity == 52) // 45 - 50
{
return 50;
} else {
return 0xff;
}
}
climate::ClimateTraits DaikinArcClimate::traits() {
climate::ClimateTraits traits = climate_ir::ClimateIR::traits();
traits.set_supports_current_temperature(true);
traits.set_supports_current_humidity(false);
traits.set_supports_target_humidity(true);
traits.set_visual_min_humidity(38);
traits.set_visual_max_humidity(52);
return traits;
}
bool DaikinArcClimate::parse_state_frame_(const uint8_t frame[]) {
uint8_t checksum = 0;
for (int i = 0; i < (DAIKIN_STATE_FRAME_SIZE - 1); i++) {
checksum += frame[i];
}
if (frame[DAIKIN_STATE_FRAME_SIZE - 1] != checksum) {
ESP_LOGI(TAG, "checksum error");
return false;
}
char buf[DAIKIN_STATE_FRAME_SIZE * 3 + 1] = {0};
for (size_t i = 0; i < DAIKIN_STATE_FRAME_SIZE; i++) {
sprintf(buf, "%s%02x ", buf, frame[i]);
}
ESP_LOGD(TAG, "FRAME %s", buf);
uint8_t mode = frame[5];
if (mode & DAIKIN_MODE_ON) {
switch (mode & 0xF0) {
case DAIKIN_MODE_COOL:
this->mode = climate::CLIMATE_MODE_COOL;
break;
case DAIKIN_MODE_DRY:
this->mode = climate::CLIMATE_MODE_DRY;
break;
case DAIKIN_MODE_HEAT:
this->mode = climate::CLIMATE_MODE_HEAT;
break;
case DAIKIN_MODE_AUTO:
this->mode = climate::CLIMATE_MODE_HEAT_COOL;
break;
case DAIKIN_MODE_FAN:
this->mode = climate::CLIMATE_MODE_FAN_ONLY;
break;
}
} else {
this->mode = climate::CLIMATE_MODE_OFF;
}
uint8_t temperature = frame[6];
if (!(temperature & 0xC0)) {
this->target_temperature = temperature >> 1;
this->target_temperature += (temperature & 0x1) ? 0.5 : 0;
}
this->target_humidity = frame[7]; // 0, 40, 45, 50, 0xff
uint8_t fan_mode = frame[8];
uint8_t swing_mode = frame[9];
if (fan_mode & 0xF && swing_mode & 0xF) {
this->swing_mode = climate::CLIMATE_SWING_BOTH;
} else if (fan_mode & 0xF) {
this->swing_mode = climate::CLIMATE_SWING_VERTICAL;
} else if (swing_mode & 0xF) {
this->swing_mode = climate::CLIMATE_SWING_HORIZONTAL;
} else {
this->swing_mode = climate::CLIMATE_SWING_OFF;
}
switch (fan_mode & 0xF0) {
case DAIKIN_FAN_1:
case DAIKIN_FAN_2:
case DAIKIN_FAN_SILENT:
this->fan_mode = climate::CLIMATE_FAN_LOW;
break;
case DAIKIN_FAN_3:
this->fan_mode = climate::CLIMATE_FAN_MEDIUM;
break;
case DAIKIN_FAN_4:
case DAIKIN_FAN_5:
this->fan_mode = climate::CLIMATE_FAN_HIGH;
break;
case DAIKIN_FAN_AUTO:
this->fan_mode = climate::CLIMATE_FAN_AUTO;
break;
}
/*
05 0 [1:3]MODE 1 [OFF TMR] [ON TMR] Power
06-07 TEMP
08 [0:3] SPEED [4:7] Swing
09 00
10 00
11, 12: timer
13 [0:6] 0000000 [7] POWERMODE
14 0a
15 c4
16 [0:3] 8 00 [6:7] SENSOR WIND = 11 / NORMAL = 00
17 24
05 06 07 08 09 10 11 12 13 14 15 16 17 18
None FRAME 11 da 27 00 00 49 2e 00 b0 00 00 06 60 00 0a c4 80 24 11
1H FRAME 11 da 27 00 00 4d 2e 00 b0 00 00 c6 30 00 2a c4 80 24 c5
1H30 FRAME 11 da 27 00 00 4d 2e 00 b0 00 00 a6 32 00 2a c4 80 24 a7
2H FRAME 11 da 27 00 00 4d 2e 00 b0 00 00 86 34 00 2a c4 80 24 89
*/
this->publish_state();
return true;
}
bool DaikinArcClimate::on_receive(remote_base::RemoteReceiveData data) {
uint8_t state_frame[DAIKIN_STATE_FRAME_SIZE] = {};
bool valid_daikin_frame = false;
if (data.expect_item(DAIKIN_HEADER_MARK, DAIKIN_HEADER_SPACE)) {
valid_daikin_frame = true;
int bytes_count = data.size() / 2 / 8;
std::unique_ptr<char[]> buf(new char[bytes_count * 3 + 1]);
buf[0] = '\0';
for (size_t i = 0; i < bytes_count; i++) {
uint8_t byte = 0;
for (int8_t bit = 0; bit < 8; bit++) {
if (data.expect_item(DAIKIN_BIT_MARK, DAIKIN_ONE_SPACE)) {
byte |= 1 << bit;
} else if (!data.expect_item(DAIKIN_BIT_MARK, DAIKIN_ZERO_SPACE)) {
valid_daikin_frame = false;
break;
}
}
sprintf(buf.get(), "%s%02x ", buf.get(), byte);
}
ESP_LOGD(TAG, "WHOLE FRAME %s size: %d", buf.get(), data.size());
}
if (!valid_daikin_frame) {
char sbuf[16 * 10 + 1];
sbuf[0] = '\0';
for (size_t j = 0; j < data.size(); j++) {
if ((j - 2) % 16 == 0) {
if (j > 0) {
ESP_LOGD(TAG, "DATA %04x: %s", (j - 16 > 0xffff ? 0 : j - 16), sbuf);
}
sbuf[0] = '\0';
}
char type_ch = ' ';
// debug_tolerance = 25%
if (DAIKIN_DBG_LOWER(DAIKIN_ARC_PRE_MARK) <= data[j] && data[j] <= DAIKIN_DBG_UPPER(DAIKIN_ARC_PRE_MARK))
type_ch = 'P';
if (DAIKIN_DBG_LOWER(DAIKIN_ARC_PRE_SPACE) <= -data[j] && -data[j] <= DAIKIN_DBG_UPPER(DAIKIN_ARC_PRE_SPACE))
type_ch = 'a';
if (DAIKIN_DBG_LOWER(DAIKIN_HEADER_MARK) <= data[j] && data[j] <= DAIKIN_DBG_UPPER(DAIKIN_HEADER_MARK))
type_ch = 'H';
if (DAIKIN_DBG_LOWER(DAIKIN_HEADER_SPACE) <= -data[j] && -data[j] <= DAIKIN_DBG_UPPER(DAIKIN_HEADER_SPACE))
type_ch = 'h';
if (DAIKIN_DBG_LOWER(DAIKIN_BIT_MARK) <= data[j] && data[j] <= DAIKIN_DBG_UPPER(DAIKIN_BIT_MARK))
type_ch = 'B';
if (DAIKIN_DBG_LOWER(DAIKIN_ONE_SPACE) <= -data[j] && -data[j] <= DAIKIN_DBG_UPPER(DAIKIN_ONE_SPACE))
type_ch = '1';
if (DAIKIN_DBG_LOWER(DAIKIN_ZERO_SPACE) <= -data[j] && -data[j] <= DAIKIN_DBG_UPPER(DAIKIN_ZERO_SPACE))
type_ch = '0';
if (abs(data[j]) > 100000) {
sprintf(sbuf, "%s%-5d[%c] ", sbuf, data[j] > 0 ? 99999 : -99999, type_ch);
} else {
sprintf(sbuf, "%s%-5d[%c] ", sbuf, (int) (round(data[j] / 10.) * 10), type_ch);
}
if (j == data.size() - 1) {
ESP_LOGD(TAG, "DATA %04x: %s", (j - 8 > 0xffff ? 0 : j - 8), sbuf);
}
}
}
data.reset();
if (!data.expect_item(DAIKIN_HEADER_MARK, DAIKIN_HEADER_SPACE)) {
ESP_LOGI(TAG, "non daikin_arc expect item");
return false;
}
for (uint8_t pos = 0; pos < DAIKIN_STATE_FRAME_SIZE; pos++) {
uint8_t byte = 0;
for (int8_t bit = 0; bit < 8; bit++) {
if (data.expect_item(DAIKIN_BIT_MARK, DAIKIN_ONE_SPACE)) {
byte |= 1 << bit;
} else if (!data.expect_item(DAIKIN_BIT_MARK, DAIKIN_ZERO_SPACE)) {
ESP_LOGI(TAG, "non daikin_arc expect item pos: %d", pos);
return false;
}
}
state_frame[pos] = byte;
if (pos == 0) {
// frame header
if (byte != 0x11) {
ESP_LOGI(TAG, "non daikin_arc expect pos: %d header: %02x", pos, byte);
return false;
}
} else if (pos == 1) {
// frame header
if (byte != 0xDA) {
ESP_LOGI(TAG, "non daikin_arc expect pos: %d header: %02x", pos, byte);
return false;
}
} else if (pos == 2) {
// frame header
if (byte != 0x27) {
ESP_LOGI(TAG, "non daikin_arc expect pos: %d header: %02x", pos, byte);
return false;
}
} else if (pos == 3) { // NOLINT(bugprone-branch-clone)
// frame header
if (byte != 0x00) {
ESP_LOGI(TAG, "non daikin_arc expect pos: %d header: %02x", pos, byte);
return false;
}
} else if (pos == 4) {
// frame type
if (byte != 0x00) {
ESP_LOGI(TAG, "non daikin_arc expect pos: %d header: %02x", pos, byte);
return false;
}
} else if (pos == 5) {
if (data.size() == 385) {
/*
11 da 27 00 00 1a 0c 04 2c 21 61 07 00 07 0c 00 18 00 0e 3c 00 6c 1b 61
Inside Temp
Outside Temp
Humdidity
*/
this->current_temperature = state_frame[5]; // Inside temperature
// this->current_temperature = state_frame[6]; // Outside temperature
this->publish_state();
return true;
} else if ((byte & 0x40) != 0x40) {
ESP_LOGI(TAG, "non daikin_arc expect pos: %d header: %02x", pos, byte);
return false;
}
}
}
return this->parse_state_frame_(state_frame);
}
void DaikinArcClimate::control(const climate::ClimateCall &call) {
if (call.get_target_humidity().has_value()) {
this->target_humidity = *call.get_target_humidity();
}
climate_ir::ClimateIR::control(call);
}
} // namespace daikin_arc
} // namespace esphome
@@ -0,0 +1,76 @@
#pragma once
#include "esphome/components/climate_ir/climate_ir.h"
namespace esphome {
namespace daikin_arc {
// Values for Daikin ARC43XXX IR Controllers
// Temperature
const uint8_t DAIKIN_TEMP_MIN = 10; // Celsius
const uint8_t DAIKIN_TEMP_MAX = 30; // Celsius
// Modes
const uint8_t DAIKIN_MODE_AUTO = 0x00;
const uint8_t DAIKIN_MODE_COOL = 0x30;
const uint8_t DAIKIN_MODE_HEAT = 0x40;
const uint8_t DAIKIN_MODE_DRY = 0x20;
const uint8_t DAIKIN_MODE_FAN = 0x60;
const uint8_t DAIKIN_MODE_OFF = 0x00;
const uint8_t DAIKIN_MODE_ON = 0x01;
// Fan Speed
const uint8_t DAIKIN_FAN_AUTO = 0xA0;
const uint8_t DAIKIN_FAN_SILENT = 0xB0;
const uint8_t DAIKIN_FAN_1 = 0x30;
const uint8_t DAIKIN_FAN_2 = 0x40;
const uint8_t DAIKIN_FAN_3 = 0x50;
const uint8_t DAIKIN_FAN_4 = 0x60;
const uint8_t DAIKIN_FAN_5 = 0x70;
// IR Transmission
const uint32_t DAIKIN_IR_FREQUENCY = 38000;
const uint32_t DAIKIN_ARC_PRE_MARK = 9950;
const uint32_t DAIKIN_ARC_PRE_SPACE = 25100;
const uint32_t DAIKIN_HEADER_MARK = 3450;
const uint32_t DAIKIN_HEADER_SPACE = 1760;
const uint32_t DAIKIN_BIT_MARK = 400;
const uint32_t DAIKIN_ONE_SPACE = 1300;
const uint32_t DAIKIN_ZERO_SPACE = 480;
const uint32_t DAIKIN_MESSAGE_SPACE = 35000;
const uint8_t DAIKIN_DBG_TOLERANCE = 25;
#define DAIKIN_DBG_LOWER(x) ((100 - DAIKIN_DBG_TOLERANCE) * (x) / 100U)
#define DAIKIN_DBG_UPPER(x) ((100 + DAIKIN_DBG_TOLERANCE) * (x) / 100U)
// State Frame size
const uint8_t DAIKIN_STATE_FRAME_SIZE = 19;
class DaikinArcClimate : public climate_ir::ClimateIR {
public:
DaikinArcClimate()
: climate_ir::ClimateIR(DAIKIN_TEMP_MIN, DAIKIN_TEMP_MAX, 0.5f, true, true,
{climate::CLIMATE_FAN_AUTO, climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM,
climate::CLIMATE_FAN_HIGH},
{climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_VERTICAL,
climate::CLIMATE_SWING_HORIZONTAL, climate::CLIMATE_SWING_BOTH}) {}
void setup() override;
protected:
void control(const climate::ClimateCall &call) override;
// Transmit via IR the state of this climate controller.
void transmit_query_();
void transmit_state() override;
climate::ClimateTraits traits() override;
uint8_t operation_mode_();
uint16_t fan_speed_();
uint8_t temperature_();
uint8_t humidity_();
// Handle received IR Buffer
bool on_receive(remote_base::RemoteReceiveData data) override;
bool parse_state_frame_(const uint8_t frame[]);
};
} // namespace daikin_arc
} // namespace esphome
+15 -3
View File
@@ -60,7 +60,7 @@ void DallasComponent::setup() {
for (auto *sensor : this->sensors_) {
if (sensor->get_index().has_value()) {
if (*sensor->get_index() >= this->found_sensors_.size()) {
this->status_set_error();
this->status_set_error("Sensor configured by index but not found");
continue;
}
sensor->set_address(this->found_sensors_[*sensor->get_index()]);
@@ -109,8 +109,12 @@ void DallasComponent::update() {
result = this->one_wire_->reset();
}
if (!result) {
ESP_LOGE(TAG, "Requesting conversion failed");
this->status_set_warning();
if (!this->found_sensors_.empty()) {
// Only log error if at the start sensors were found (and thus are disconnected during uptime)
ESP_LOGE(TAG, "Requesting conversion failed");
this->status_set_warning();
}
for (auto *sensor : this->sensors_) {
sensor->publish_state(NAN);
}
@@ -124,6 +128,12 @@ void DallasComponent::update() {
}
for (auto *sensor : this->sensors_) {
if (sensor->get_address() == 0) {
ESP_LOGV(TAG, "'%s' - Indexed sensor not found at startup, skipping update", sensor->get_name().c_str());
sensor->publish_state(NAN);
continue;
}
this->set_timeout(sensor->get_address_name(), sensor->millis_to_wait_for_conversion(), [this, sensor] {
bool res = sensor->read_scratch_pad();
@@ -152,6 +162,8 @@ void DallasTemperatureSensor::set_resolution(uint8_t resolution) { this->resolut
optional<uint8_t> DallasTemperatureSensor::get_index() const { return this->index_; }
void DallasTemperatureSensor::set_index(uint8_t index) { this->index_ = index; }
uint8_t *DallasTemperatureSensor::get_address8() { return reinterpret_cast<uint8_t *>(&this->address_); }
uint64_t DallasTemperatureSensor::get_address() { return this->address_; }
const std::string &DallasTemperatureSensor::get_address_name() {
if (this->address_name_.empty()) {
this->address_name_ = std::string("0x") + format_hex(this->address_);
@@ -37,6 +37,7 @@ class DallasTemperatureSensor : public sensor::Sensor {
void set_parent(DallasComponent *parent) { parent_ = parent; }
/// Helper to get a pointer to the address as uint8_t.
uint8_t *get_address8();
uint64_t get_address();
/// Helper to create (and cache) the name for this sensor. For example "0xfe0000031f1eaf29".
const std::string &get_address_name();

Some files were not shown because too many files have changed in this diff Show More