mirror of
https://github.com/esphome/esphome.git
synced 2026-03-25 12:46:56 +01:00
commit
9fbbcd6d8a
@ -4,53 +4,60 @@
|
||||
"postCreateCommand": [
|
||||
"script/devcontainer-post-create"
|
||||
],
|
||||
"containerEnv": {
|
||||
"DEVCONTAINER": "1"
|
||||
},
|
||||
"runArgs": [
|
||||
"--privileged",
|
||||
"-e",
|
||||
"ESPHOME_DASHBOARD_USE_PING=1"
|
||||
],
|
||||
"appPort": 6052,
|
||||
"extensions": [
|
||||
// python
|
||||
"ms-python.python",
|
||||
"visualstudioexptteam.vscodeintellicode",
|
||||
// yaml
|
||||
"redhat.vscode-yaml",
|
||||
// cpp
|
||||
"ms-vscode.cpptools",
|
||||
// editorconfig
|
||||
"editorconfig.editorconfig",
|
||||
],
|
||||
"settings": {
|
||||
"python.languageServer": "Pylance",
|
||||
"python.pythonPath": "/usr/bin/python3",
|
||||
"python.linting.pylintEnabled": true,
|
||||
"python.linting.enabled": true,
|
||||
"python.formatting.provider": "black",
|
||||
"editor.formatOnPaste": false,
|
||||
"editor.formatOnSave": true,
|
||||
"editor.formatOnType": true,
|
||||
"files.trimTrailingWhitespace": true,
|
||||
"terminal.integrated.defaultProfile.linux": "bash",
|
||||
"yaml.customTags": [
|
||||
"!secret scalar",
|
||||
"!lambda scalar",
|
||||
"!include_dir_named scalar",
|
||||
"!include_dir_list scalar",
|
||||
"!include_dir_merge_list scalar",
|
||||
"!include_dir_merge_named scalar"
|
||||
],
|
||||
"files.exclude": {
|
||||
"**/.git": true,
|
||||
"**/.DS_Store": true,
|
||||
"**/*.pyc": {
|
||||
"when": "$(basename).py"
|
||||
},
|
||||
"**/__pycache__": true
|
||||
},
|
||||
"files.associations": {
|
||||
"**/.vscode/*.json": "jsonc"
|
||||
},
|
||||
"C_Cpp.clang_format_path": "/usr/bin/clang-format-11",
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": [
|
||||
// python
|
||||
"ms-python.python",
|
||||
"visualstudioexptteam.vscodeintellicode",
|
||||
// yaml
|
||||
"redhat.vscode-yaml",
|
||||
// cpp
|
||||
"ms-vscode.cpptools",
|
||||
// editorconfig
|
||||
"editorconfig.editorconfig",
|
||||
],
|
||||
"settings": {
|
||||
"python.languageServer": "Pylance",
|
||||
"python.pythonPath": "/usr/bin/python3",
|
||||
"python.linting.pylintEnabled": true,
|
||||
"python.linting.enabled": true,
|
||||
"python.formatting.provider": "black",
|
||||
"editor.formatOnPaste": false,
|
||||
"editor.formatOnSave": true,
|
||||
"editor.formatOnType": true,
|
||||
"files.trimTrailingWhitespace": true,
|
||||
"terminal.integrated.defaultProfile.linux": "bash",
|
||||
"yaml.customTags": [
|
||||
"!secret scalar",
|
||||
"!lambda scalar",
|
||||
"!include_dir_named scalar",
|
||||
"!include_dir_list scalar",
|
||||
"!include_dir_merge_list scalar",
|
||||
"!include_dir_merge_named scalar"
|
||||
],
|
||||
"files.exclude": {
|
||||
"**/.git": true,
|
||||
"**/.DS_Store": true,
|
||||
"**/*.pyc": {
|
||||
"when": "$(basename).py"
|
||||
},
|
||||
"**/__pycache__": true
|
||||
},
|
||||
"files.associations": {
|
||||
"**/.vscode/*.json": "jsonc"
|
||||
},
|
||||
"C_Cpp.clang_format_path": "/usr/bin/clang-format-13"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
@ -41,6 +41,10 @@ jobs:
|
||||
file: tests/test3.yaml
|
||||
name: Test tests/test3.yaml
|
||||
pio_cache_key: test3
|
||||
- id: test
|
||||
file: tests/test3.1.yaml
|
||||
name: Test tests/test3.1.yaml
|
||||
pio_cache_key: test3.1
|
||||
- id: test
|
||||
file: tests/test4.yaml
|
||||
name: Test tests/test4.yaml
|
||||
@ -129,7 +133,7 @@ jobs:
|
||||
- name: Install clang tools
|
||||
run: |
|
||||
sudo apt-get install \
|
||||
clang-format-11 \
|
||||
clang-format-13 \
|
||||
clang-tidy-11
|
||||
if: matrix.id == 'clang-tidy' || matrix.id == 'clang-format'
|
||||
|
||||
|
||||
4
.github/workflows/stale.yml
vendored
4
.github/workflows/stale.yml
vendored
@ -18,7 +18,7 @@ jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v7
|
||||
- uses: actions/stale@v8
|
||||
with:
|
||||
days-before-pr-stale: 90
|
||||
days-before-pr-close: 7
|
||||
@ -38,7 +38,7 @@ jobs:
|
||||
close-issues:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v7
|
||||
- uses: actions/stale@v8
|
||||
with:
|
||||
days-before-pr-stale: -1
|
||||
days-before-pr-close: -1
|
||||
|
||||
60
.github/workflows/sync-device-classes.yml
vendored
Normal file
60
.github/workflows/sync-device-classes.yml
vendored
Normal file
@ -0,0 +1,60 @@
|
||||
---
|
||||
name: Synchronise Device Classes from Home Assistant
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '45 6 * * *'
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
name: Sync Device Classes
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Checkout Home Assistant
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: home-assistant/core
|
||||
path: lib/home-assistant
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.11
|
||||
|
||||
- name: Install Home Assistant
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -e lib/home-assistant
|
||||
|
||||
- name: Sync
|
||||
run: |
|
||||
python ./script/sync-device_class.py
|
||||
|
||||
- name: Get PR template
|
||||
id: pr-template-body
|
||||
run: |
|
||||
body=$(cat .github/PULL_REQUEST_TEMPLATE.md)
|
||||
delimiter="$(openssl rand -hex 8)"
|
||||
echo "body<<$delimiter" >> $GITHUB_OUTPUT
|
||||
echo "$body" >> $GITHUB_OUTPUT
|
||||
echo "$delimiter" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Commit changes
|
||||
uses: peter-evans/create-pull-request@v4
|
||||
with:
|
||||
commit-message: "Synchronise Device Classes from Home Assistant"
|
||||
committer: esphomebot <esphome@nabucasa.com>
|
||||
author: esphomebot <esphome@nabucasa.com>
|
||||
branch: sync/device-classes/
|
||||
branch-suffix: timestamp
|
||||
delete-branch: true
|
||||
title: "Synchronise Device Classes from Home Assistant"
|
||||
body: ${{ steps.pr-template-body.outputs.body }}
|
||||
@ -2,8 +2,8 @@
|
||||
# See https://pre-commit.com for more information
|
||||
# See https://pre-commit.com/hooks.html for more hooks
|
||||
repos:
|
||||
- repo: https://github.com/ambv/black
|
||||
rev: 23.1.0
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 23.3.0
|
||||
hooks:
|
||||
- id: black
|
||||
args:
|
||||
|
||||
15
.vscode/tasks.json
vendored
15
.vscode/tasks.json
vendored
@ -2,15 +2,24 @@
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "run",
|
||||
"label": "Run Dashboard",
|
||||
"type": "shell",
|
||||
"command": "python3 -m esphome dashboard config/",
|
||||
"command": "${command:python.interpreterPath}",
|
||||
"args": [
|
||||
"-m",
|
||||
"esphome",
|
||||
"dashboard",
|
||||
"config/"
|
||||
],
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "clang-tidy",
|
||||
"type": "shell",
|
||||
"command": "./script/clang-tidy",
|
||||
"command": "${command:python.interpreterPath}",
|
||||
"args": [
|
||||
"./script/clang-tidy"
|
||||
],
|
||||
"problemMatcher": [
|
||||
{
|
||||
"owner": "clang-tidy",
|
||||
|
||||
@ -111,6 +111,8 @@ esphome/components/hte501/* @Stock-M
|
||||
esphome/components/hydreon_rgxx/* @functionpointer
|
||||
esphome/components/i2c/* @esphome/core
|
||||
esphome/components/i2s_audio/* @jesserockz
|
||||
esphome/components/i2s_audio/media_player/* @jesserockz
|
||||
esphome/components/i2s_audio/microphone/* @jesserockz
|
||||
esphome/components/ili9xxx/* @nielsnl68
|
||||
esphome/components/improv_base/* @esphome/core
|
||||
esphome/components/improv_serial/* @esphome/core
|
||||
@ -154,11 +156,13 @@ esphome/components/mcp9808/* @k7hpn
|
||||
esphome/components/md5/* @esphome/core
|
||||
esphome/components/mdns/* @esphome/core
|
||||
esphome/components/media_player/* @jesserockz
|
||||
esphome/components/microphone/* @jesserockz
|
||||
esphome/components/mics_4514/* @jesserockz
|
||||
esphome/components/midea/* @dudanov
|
||||
esphome/components/midea_ir/* @dudanov
|
||||
esphome/components/mitsubishi/* @RubyBailey
|
||||
esphome/components/mlx90393/* @functionpointer
|
||||
esphome/components/mmc5603/* @benhoff
|
||||
esphome/components/modbus_controller/* @martgras
|
||||
esphome/components/modbus_controller/binary_sensor/* @martgras
|
||||
esphome/components/modbus_controller/number/* @martgras
|
||||
@ -286,6 +290,7 @@ esphome/components/ufire_ise/* @pvizeli
|
||||
esphome/components/ultrasonic/* @OttoWinter
|
||||
esphome/components/vbus/* @ssieb
|
||||
esphome/components/version/* @esphome/core
|
||||
esphome/components/voice_assistant/* @jesserockz
|
||||
esphome/components/wake_on_lan/* @willwill2will54
|
||||
esphome/components/web_server_base/* @OttoWinter
|
||||
esphome/components/whirlpool/* @glmnet
|
||||
|
||||
@ -135,7 +135,7 @@ RUN \
|
||||
apt-get update \
|
||||
# Use pinned versions so that we get updates with build caching
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
clang-format-11=1:11.0.1-2 \
|
||||
clang-format-13=1:13.0.1-6~deb11u1 \
|
||||
clang-tidy-11=1:11.0.1-2 \
|
||||
patch=2.7.6-7 \
|
||||
software-properties-common=0.96.20.2-2.1 \
|
||||
|
||||
@ -152,6 +152,8 @@ def run_miniterm(config, port):
|
||||
_LOGGER.error("Could not connect to serial port %s", port)
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
def wrap_to_code(name, comp):
|
||||
coro = coroutine(comp.to_code)
|
||||
|
||||
@ -65,7 +65,7 @@ void Am43Component::control(const CoverCall &call) {
|
||||
|
||||
if (this->invert_position_)
|
||||
pos = 1 - pos;
|
||||
auto *packet = this->encoder_->get_set_position_request(100 - (uint8_t)(pos * 100));
|
||||
auto *packet = this->encoder_->get_set_position_request(100 - (uint8_t) (pos * 100));
|
||||
auto status =
|
||||
esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_,
|
||||
packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
|
||||
|
||||
@ -76,8 +76,6 @@ async def to_code(config):
|
||||
pos = 0
|
||||
for frameIndex in range(frames):
|
||||
image.seek(frameIndex)
|
||||
if CONF_RESIZE in config:
|
||||
image.thumbnail(config[CONF_RESIZE])
|
||||
frame = image.convert("RGB")
|
||||
if CONF_RESIZE in config:
|
||||
frame = frame.resize([width, height])
|
||||
|
||||
@ -53,6 +53,9 @@ service APIConnection {
|
||||
rpc bluetooth_gatt_write_descriptor(BluetoothGATTWriteDescriptorRequest) returns (void) {}
|
||||
rpc bluetooth_gatt_notify(BluetoothGATTNotifyRequest) returns (void) {}
|
||||
rpc subscribe_bluetooth_connections_free(SubscribeBluetoothConnectionsFreeRequest) returns (BluetoothConnectionsFreeResponse) {}
|
||||
rpc unsubscribe_bluetooth_le_advertisements(UnsubscribeBluetoothLEAdvertisementsRequest) returns (void) {}
|
||||
|
||||
rpc subscribe_voice_assistant(SubscribeVoiceAssistantRequest) returns (void) {}
|
||||
}
|
||||
|
||||
|
||||
@ -208,6 +211,8 @@ message DeviceInfoResponse {
|
||||
string manufacturer = 12;
|
||||
|
||||
string friendly_name = 13;
|
||||
|
||||
uint32 voice_assistant_version = 14;
|
||||
}
|
||||
|
||||
message ListEntitiesRequest {
|
||||
@ -1125,6 +1130,7 @@ message MediaPlayerCommandRequest {
|
||||
message SubscribeBluetoothLEAdvertisementsRequest {
|
||||
option (id) = 66;
|
||||
option (source) = SOURCE_CLIENT;
|
||||
option (ifdef) = "USE_BLUETOOTH_PROXY";
|
||||
}
|
||||
|
||||
message BluetoothServiceData {
|
||||
@ -1156,6 +1162,7 @@ enum BluetoothDeviceRequestType {
|
||||
BLUETOOTH_DEVICE_REQUEST_TYPE_UNPAIR = 3;
|
||||
BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITH_CACHE = 4;
|
||||
BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE = 5;
|
||||
BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE = 6;
|
||||
}
|
||||
|
||||
message BluetoothDeviceRequest {
|
||||
@ -1359,3 +1366,71 @@ message BluetoothDeviceUnpairingResponse {
|
||||
bool success = 2;
|
||||
int32 error = 3;
|
||||
}
|
||||
|
||||
message UnsubscribeBluetoothLEAdvertisementsRequest {
|
||||
option (id) = 87;
|
||||
option (source) = SOURCE_CLIENT;
|
||||
option (ifdef) = "USE_BLUETOOTH_PROXY";
|
||||
}
|
||||
|
||||
message BluetoothDeviceClearCacheResponse {
|
||||
option (id) = 88;
|
||||
option (source) = SOURCE_SERVER;
|
||||
option (ifdef) = "USE_BLUETOOTH_PROXY";
|
||||
|
||||
uint64 address = 1;
|
||||
bool success = 2;
|
||||
int32 error = 3;
|
||||
}
|
||||
|
||||
// ==================== PUSH TO TALK ====================
|
||||
message SubscribeVoiceAssistantRequest {
|
||||
option (id) = 89;
|
||||
option (source) = SOURCE_CLIENT;
|
||||
option (ifdef) = "USE_VOICE_ASSISTANT";
|
||||
|
||||
bool subscribe = 1;
|
||||
}
|
||||
|
||||
message VoiceAssistantRequest {
|
||||
option (id) = 90;
|
||||
option (source) = SOURCE_SERVER;
|
||||
option (ifdef) = "USE_VOICE_ASSISTANT";
|
||||
|
||||
bool start = 1;
|
||||
}
|
||||
|
||||
message VoiceAssistantResponse {
|
||||
option (id) = 91;
|
||||
option (source) = SOURCE_CLIENT;
|
||||
option (ifdef) = "USE_VOICE_ASSISTANT";
|
||||
|
||||
uint32 port = 1;
|
||||
bool error = 2;
|
||||
}
|
||||
|
||||
enum VoiceAssistantEvent {
|
||||
VOICE_ASSISTANT_ERROR = 0;
|
||||
VOICE_ASSISTANT_RUN_START = 1;
|
||||
VOICE_ASSISTANT_RUN_END = 2;
|
||||
VOICE_ASSISTANT_STT_START = 3;
|
||||
VOICE_ASSISTANT_STT_END = 4;
|
||||
VOICE_ASSISTANT_INTENT_START = 5;
|
||||
VOICE_ASSISTANT_INTENT_END = 6;
|
||||
VOICE_ASSISTANT_TTS_START = 7;
|
||||
VOICE_ASSISTANT_TTS_END = 8;
|
||||
}
|
||||
|
||||
message VoiceAssistantEventData {
|
||||
string name = 1;
|
||||
string value = 2;
|
||||
}
|
||||
|
||||
message VoiceAssistantEventResponse {
|
||||
option (id) = 92;
|
||||
option (source) = SOURCE_CLIENT;
|
||||
option (ifdef) = "USE_VOICE_ASSISTANT";
|
||||
|
||||
VoiceAssistantEvent event_type = 1;
|
||||
repeated VoiceAssistantEventData data = 2;
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
#include "api_connection.h"
|
||||
#include <cerrno>
|
||||
#include <cinttypes>
|
||||
#include "esphome/components/network/util.h"
|
||||
#include "esphome/core/entity_base.h"
|
||||
#include "esphome/core/hal.h"
|
||||
@ -15,6 +16,9 @@
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
#include "esphome/components/bluetooth_proxy/bluetooth_proxy.h"
|
||||
#endif
|
||||
#ifdef USE_VOICE_ASSISTANT
|
||||
#include "esphome/components/voice_assistant/voice_assistant.h"
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace api {
|
||||
@ -180,7 +184,8 @@ bool APIConnection::send_binary_sensor_info(binary_sensor::BinarySensor *binary_
|
||||
ListEntitiesBinarySensorResponse msg;
|
||||
msg.object_id = binary_sensor->get_object_id();
|
||||
msg.key = binary_sensor->get_object_id_hash();
|
||||
msg.name = binary_sensor->get_name();
|
||||
if (binary_sensor->has_own_name())
|
||||
msg.name = binary_sensor->get_name();
|
||||
msg.unique_id = get_default_unique_id("binary_sensor", binary_sensor);
|
||||
msg.device_class = binary_sensor->get_device_class();
|
||||
msg.is_status_binary_sensor = binary_sensor->is_status_binary_sensor();
|
||||
@ -212,7 +217,8 @@ bool APIConnection::send_cover_info(cover::Cover *cover) {
|
||||
ListEntitiesCoverResponse msg;
|
||||
msg.key = cover->get_object_id_hash();
|
||||
msg.object_id = cover->get_object_id();
|
||||
msg.name = cover->get_name();
|
||||
if (cover->has_own_name())
|
||||
msg.name = cover->get_name();
|
||||
msg.unique_id = get_default_unique_id("cover", cover);
|
||||
msg.assumed_state = traits.get_is_assumed_state();
|
||||
msg.supports_position = traits.get_supports_position();
|
||||
@ -275,7 +281,8 @@ bool APIConnection::send_fan_info(fan::Fan *fan) {
|
||||
ListEntitiesFanResponse msg;
|
||||
msg.key = fan->get_object_id_hash();
|
||||
msg.object_id = fan->get_object_id();
|
||||
msg.name = fan->get_name();
|
||||
if (fan->has_own_name())
|
||||
msg.name = fan->get_name();
|
||||
msg.unique_id = get_default_unique_id("fan", fan);
|
||||
msg.supports_oscillation = traits.supports_oscillation();
|
||||
msg.supports_speed = traits.supports_speed();
|
||||
@ -337,7 +344,8 @@ bool APIConnection::send_light_info(light::LightState *light) {
|
||||
ListEntitiesLightResponse msg;
|
||||
msg.key = light->get_object_id_hash();
|
||||
msg.object_id = light->get_object_id();
|
||||
msg.name = light->get_name();
|
||||
if (light->has_own_name())
|
||||
msg.name = light->get_name();
|
||||
msg.unique_id = get_default_unique_id("light", light);
|
||||
|
||||
msg.disabled_by_default = light->is_disabled_by_default();
|
||||
@ -418,7 +426,8 @@ bool APIConnection::send_sensor_info(sensor::Sensor *sensor) {
|
||||
ListEntitiesSensorResponse msg;
|
||||
msg.key = sensor->get_object_id_hash();
|
||||
msg.object_id = sensor->get_object_id();
|
||||
msg.name = sensor->get_name();
|
||||
if (sensor->has_own_name())
|
||||
msg.name = sensor->get_name();
|
||||
msg.unique_id = sensor->unique_id();
|
||||
if (msg.unique_id.empty())
|
||||
msg.unique_id = get_default_unique_id("sensor", sensor);
|
||||
@ -448,7 +457,8 @@ bool APIConnection::send_switch_info(switch_::Switch *a_switch) {
|
||||
ListEntitiesSwitchResponse msg;
|
||||
msg.key = a_switch->get_object_id_hash();
|
||||
msg.object_id = a_switch->get_object_id();
|
||||
msg.name = a_switch->get_name();
|
||||
if (a_switch->has_own_name())
|
||||
msg.name = a_switch->get_name();
|
||||
msg.unique_id = get_default_unique_id("switch", a_switch);
|
||||
msg.icon = a_switch->get_icon();
|
||||
msg.assumed_state = a_switch->assumed_state();
|
||||
@ -533,7 +543,8 @@ bool APIConnection::send_climate_info(climate::Climate *climate) {
|
||||
ListEntitiesClimateResponse msg;
|
||||
msg.key = climate->get_object_id_hash();
|
||||
msg.object_id = climate->get_object_id();
|
||||
msg.name = climate->get_name();
|
||||
if (climate->has_own_name())
|
||||
msg.name = climate->get_name();
|
||||
msg.unique_id = get_default_unique_id("climate", climate);
|
||||
|
||||
msg.disabled_by_default = climate->is_disabled_by_default();
|
||||
@ -611,7 +622,8 @@ bool APIConnection::send_number_info(number::Number *number) {
|
||||
ListEntitiesNumberResponse msg;
|
||||
msg.key = number->get_object_id_hash();
|
||||
msg.object_id = number->get_object_id();
|
||||
msg.name = number->get_name();
|
||||
if (number->has_own_name())
|
||||
msg.name = number->get_name();
|
||||
msg.unique_id = get_default_unique_id("number", number);
|
||||
msg.icon = number->get_icon();
|
||||
msg.disabled_by_default = number->is_disabled_by_default();
|
||||
@ -652,7 +664,8 @@ bool APIConnection::send_select_info(select::Select *select) {
|
||||
ListEntitiesSelectResponse msg;
|
||||
msg.key = select->get_object_id_hash();
|
||||
msg.object_id = select->get_object_id();
|
||||
msg.name = select->get_name();
|
||||
if (select->has_own_name())
|
||||
msg.name = select->get_name();
|
||||
msg.unique_id = get_default_unique_id("select", select);
|
||||
msg.icon = select->get_icon();
|
||||
msg.disabled_by_default = select->is_disabled_by_default();
|
||||
@ -679,7 +692,8 @@ bool APIConnection::send_button_info(button::Button *button) {
|
||||
ListEntitiesButtonResponse msg;
|
||||
msg.key = button->get_object_id_hash();
|
||||
msg.object_id = button->get_object_id();
|
||||
msg.name = button->get_name();
|
||||
if (button->has_own_name())
|
||||
msg.name = button->get_name();
|
||||
msg.unique_id = get_default_unique_id("button", button);
|
||||
msg.icon = button->get_icon();
|
||||
msg.disabled_by_default = button->is_disabled_by_default();
|
||||
@ -710,7 +724,8 @@ bool APIConnection::send_lock_info(lock::Lock *a_lock) {
|
||||
ListEntitiesLockResponse msg;
|
||||
msg.key = a_lock->get_object_id_hash();
|
||||
msg.object_id = a_lock->get_object_id();
|
||||
msg.name = a_lock->get_name();
|
||||
if (a_lock->has_own_name())
|
||||
msg.name = a_lock->get_name();
|
||||
msg.unique_id = get_default_unique_id("lock", a_lock);
|
||||
msg.icon = a_lock->get_icon();
|
||||
msg.assumed_state = a_lock->traits.get_assumed_state();
|
||||
@ -755,7 +770,8 @@ bool APIConnection::send_media_player_info(media_player::MediaPlayer *media_play
|
||||
ListEntitiesMediaPlayerResponse msg;
|
||||
msg.key = media_player->get_object_id_hash();
|
||||
msg.object_id = media_player->get_object_id();
|
||||
msg.name = media_player->get_name();
|
||||
if (media_player->has_own_name())
|
||||
msg.name = media_player->get_name();
|
||||
msg.unique_id = get_default_unique_id("media_player", media_player);
|
||||
msg.icon = media_player->get_icon();
|
||||
msg.disabled_by_default = media_player->is_disabled_by_default();
|
||||
@ -799,7 +815,8 @@ bool APIConnection::send_camera_info(esp32_camera::ESP32Camera *camera) {
|
||||
ListEntitiesCameraResponse msg;
|
||||
msg.key = camera->get_object_id_hash();
|
||||
msg.object_id = camera->get_object_id();
|
||||
msg.name = camera->get_name();
|
||||
if (camera->has_own_name())
|
||||
msg.name = camera->get_name();
|
||||
msg.unique_id = get_default_unique_id("camera", camera);
|
||||
msg.disabled_by_default = camera->is_disabled_by_default();
|
||||
msg.icon = camera->get_icon();
|
||||
@ -879,6 +896,30 @@ BluetoothConnectionsFreeResponse APIConnection::subscribe_bluetooth_connections_
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_VOICE_ASSISTANT
|
||||
bool APIConnection::request_voice_assistant(bool start) {
|
||||
if (!this->voice_assistant_subscription_)
|
||||
return false;
|
||||
VoiceAssistantRequest msg;
|
||||
msg.start = start;
|
||||
return this->send_voice_assistant_request(msg);
|
||||
}
|
||||
void APIConnection::on_voice_assistant_response(const VoiceAssistantResponse &msg) {
|
||||
if (voice_assistant::global_voice_assistant != nullptr) {
|
||||
struct sockaddr_storage storage;
|
||||
socklen_t len = sizeof(storage);
|
||||
this->helper_->getpeername((struct sockaddr *) &storage, &len);
|
||||
voice_assistant::global_voice_assistant->start(&storage, msg.port);
|
||||
}
|
||||
};
|
||||
void APIConnection::on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) {
|
||||
if (voice_assistant::global_voice_assistant != nullptr) {
|
||||
voice_assistant::global_voice_assistant->on_event(msg);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool APIConnection::send_log_message(int level, const char *tag, const char *line) {
|
||||
if (this->log_subscription_ < level)
|
||||
return false;
|
||||
@ -898,7 +939,7 @@ HelloResponse APIConnection::hello(const HelloRequest &msg) {
|
||||
this->helper_->set_log_info(client_info_);
|
||||
this->client_api_version_major_ = msg.api_version_major;
|
||||
this->client_api_version_minor_ = msg.api_version_minor;
|
||||
ESP_LOGV(TAG, "Hello from client: '%s' | API Version %d.%d", this->client_info_.c_str(),
|
||||
ESP_LOGV(TAG, "Hello from client: '%s' | API Version %" PRIu32 ".%" PRIu32, this->client_info_.c_str(),
|
||||
this->client_api_version_major_, this->client_api_version_minor_);
|
||||
|
||||
HelloResponse resp;
|
||||
@ -953,7 +994,12 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
|
||||
resp.webserver_port = USE_WEBSERVER_PORT;
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
resp.bluetooth_proxy_version = bluetooth_proxy::global_bluetooth_proxy->has_active() ? 4 : 1;
|
||||
resp.bluetooth_proxy_version = bluetooth_proxy::global_bluetooth_proxy->has_active()
|
||||
? bluetooth_proxy::ACTIVE_CONNECTIONS_VERSION
|
||||
: bluetooth_proxy::PASSIVE_ONLY_VERSION;
|
||||
#endif
|
||||
#ifdef USE_VOICE_ASSISTANT
|
||||
resp.voice_assistant_version = 1;
|
||||
#endif
|
||||
return resp;
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
#include "api_server.h"
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/defines.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
@ -97,6 +98,12 @@ class APIConnection : public APIServerConnection {
|
||||
this->send_homeassistant_service_response(call);
|
||||
}
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) override {
|
||||
this->bluetooth_le_advertisement_subscription_ = true;
|
||||
}
|
||||
void unsubscribe_bluetooth_le_advertisements(const UnsubscribeBluetoothLEAdvertisementsRequest &msg) override {
|
||||
this->bluetooth_le_advertisement_subscription_ = false;
|
||||
}
|
||||
bool send_bluetooth_le_advertisement(const BluetoothLEAdvertisementResponse &msg);
|
||||
|
||||
void bluetooth_device_request(const BluetoothDeviceRequest &msg) override;
|
||||
@ -117,6 +124,15 @@ class APIConnection : public APIServerConnection {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_VOICE_ASSISTANT
|
||||
void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) override {
|
||||
this->voice_assistant_subscription_ = msg.subscribe;
|
||||
}
|
||||
bool request_voice_assistant(bool start);
|
||||
void on_voice_assistant_response(const VoiceAssistantResponse &msg) override;
|
||||
void on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) override;
|
||||
#endif
|
||||
|
||||
void on_disconnect_response(const DisconnectResponse &value) override;
|
||||
void on_ping_response(const PingResponse &value) override {
|
||||
// we initiated ping
|
||||
@ -150,9 +166,7 @@ class APIConnection : public APIServerConnection {
|
||||
return {};
|
||||
}
|
||||
void execute_service(const ExecuteServiceRequest &msg) override;
|
||||
void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) override {
|
||||
this->bluetooth_le_advertisement_subscription_ = true;
|
||||
}
|
||||
|
||||
bool is_authenticated() override { return this->connection_state_ == ConnectionState::AUTHENTICATED; }
|
||||
bool is_connection_setup() override {
|
||||
return this->connection_state_ == ConnectionState ::CONNECTED || this->is_authenticated();
|
||||
@ -197,7 +211,12 @@ class APIConnection : public APIServerConnection {
|
||||
uint32_t last_traffic_;
|
||||
bool sent_ping_{false};
|
||||
bool service_call_subscription_{false};
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
bool bluetooth_le_advertisement_subscription_{false};
|
||||
#endif
|
||||
#ifdef USE_VOICE_ASSISTANT
|
||||
bool voice_assistant_subscription_{false};
|
||||
#endif
|
||||
bool next_close_ = false;
|
||||
APIServer *parent_;
|
||||
InitialStateIterator initial_state_iterator_;
|
||||
|
||||
@ -295,7 +295,7 @@ APIError APINoiseFrameHelper::state_action_() {
|
||||
if (aerr != APIError::OK)
|
||||
return aerr;
|
||||
// ignore contents, may be used in future for flags
|
||||
prologue_.push_back((uint8_t)(frame.msg.size() >> 8));
|
||||
prologue_.push_back((uint8_t) (frame.msg.size() >> 8));
|
||||
prologue_.push_back((uint8_t) frame.msg.size());
|
||||
prologue_.insert(prologue_.end(), frame.msg.begin(), frame.msg.end());
|
||||
|
||||
@ -492,9 +492,9 @@ APIError APINoiseFrameHelper::write_packet(uint16_t type, const uint8_t *payload
|
||||
// tmpbuf[1], tmpbuf[2] to be set later
|
||||
const uint8_t msg_offset = 3;
|
||||
const uint8_t payload_offset = msg_offset + 4;
|
||||
tmpbuf[msg_offset + 0] = (uint8_t)(type >> 8); // type
|
||||
tmpbuf[msg_offset + 0] = (uint8_t) (type >> 8); // type
|
||||
tmpbuf[msg_offset + 1] = (uint8_t) type;
|
||||
tmpbuf[msg_offset + 2] = (uint8_t)(payload_len >> 8); // data_len
|
||||
tmpbuf[msg_offset + 2] = (uint8_t) (payload_len >> 8); // data_len
|
||||
tmpbuf[msg_offset + 3] = (uint8_t) payload_len;
|
||||
// copy data
|
||||
std::copy(payload, payload + payload_len, &tmpbuf[payload_offset]);
|
||||
@ -512,7 +512,7 @@ APIError APINoiseFrameHelper::write_packet(uint16_t type, const uint8_t *payload
|
||||
}
|
||||
|
||||
size_t total_len = 3 + mbuf.size;
|
||||
tmpbuf[1] = (uint8_t)(mbuf.size >> 8);
|
||||
tmpbuf[1] = (uint8_t) (mbuf.size >> 8);
|
||||
tmpbuf[2] = (uint8_t) mbuf.size;
|
||||
|
||||
struct iovec iov;
|
||||
@ -610,7 +610,7 @@ APIError APINoiseFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
|
||||
APIError APINoiseFrameHelper::write_frame_(const uint8_t *data, size_t len) {
|
||||
uint8_t header[3];
|
||||
header[0] = 0x01; // indicator
|
||||
header[1] = (uint8_t)(len >> 8);
|
||||
header[1] = (uint8_t) (len >> 8);
|
||||
header[2] = (uint8_t) len;
|
||||
|
||||
struct iovec iov[2];
|
||||
|
||||
@ -10,8 +10,8 @@
|
||||
#include "noise/protocol.h"
|
||||
#endif
|
||||
|
||||
#include "esphome/components/socket/socket.h"
|
||||
#include "api_noise_context.h"
|
||||
#include "esphome/components/socket/socket.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace api {
|
||||
@ -67,6 +67,7 @@ class APIFrameHelper {
|
||||
virtual bool can_write_without_blocking() = 0;
|
||||
virtual APIError write_packet(uint16_t type, const uint8_t *data, size_t len) = 0;
|
||||
virtual std::string getpeername() = 0;
|
||||
virtual int getpeername(struct sockaddr *addr, socklen_t *addrlen) = 0;
|
||||
virtual APIError close() = 0;
|
||||
virtual APIError shutdown(int how) = 0;
|
||||
// Give this helper a name for logging
|
||||
@ -84,7 +85,10 @@ class APINoiseFrameHelper : public APIFrameHelper {
|
||||
APIError read_packet(ReadPacketBuffer *buffer) override;
|
||||
bool can_write_without_blocking() override;
|
||||
APIError write_packet(uint16_t type, const uint8_t *payload, size_t len) override;
|
||||
std::string getpeername() override { return socket_->getpeername(); }
|
||||
std::string getpeername() override { return this->socket_->getpeername(); }
|
||||
int getpeername(struct sockaddr *addr, socklen_t *addrlen) override {
|
||||
return this->socket_->getpeername(addr, addrlen);
|
||||
}
|
||||
APIError close() override;
|
||||
APIError shutdown(int how) override;
|
||||
// Give this helper a name for logging
|
||||
@ -144,7 +148,10 @@ class APIPlaintextFrameHelper : public APIFrameHelper {
|
||||
APIError read_packet(ReadPacketBuffer *buffer) override;
|
||||
bool can_write_without_blocking() override;
|
||||
APIError write_packet(uint16_t type, const uint8_t *payload, size_t len) override;
|
||||
std::string getpeername() override { return socket_->getpeername(); }
|
||||
std::string getpeername() override { return this->socket_->getpeername(); }
|
||||
int getpeername(struct sockaddr *addr, socklen_t *addrlen) override {
|
||||
return this->socket_->getpeername(addr, addrlen);
|
||||
}
|
||||
APIError close() override;
|
||||
APIError shutdown(int how) override;
|
||||
// Give this helper a name for logging
|
||||
|
||||
@ -400,6 +400,34 @@ const char *proto_enum_to_string<enums::BluetoothDeviceRequestType>(enums::Bluet
|
||||
return "BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITH_CACHE";
|
||||
case enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE:
|
||||
return "BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE";
|
||||
case enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE:
|
||||
return "BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
template<> const char *proto_enum_to_string<enums::VoiceAssistantEvent>(enums::VoiceAssistantEvent value) {
|
||||
switch (value) {
|
||||
case enums::VOICE_ASSISTANT_ERROR:
|
||||
return "VOICE_ASSISTANT_ERROR";
|
||||
case enums::VOICE_ASSISTANT_RUN_START:
|
||||
return "VOICE_ASSISTANT_RUN_START";
|
||||
case enums::VOICE_ASSISTANT_RUN_END:
|
||||
return "VOICE_ASSISTANT_RUN_END";
|
||||
case enums::VOICE_ASSISTANT_STT_START:
|
||||
return "VOICE_ASSISTANT_STT_START";
|
||||
case enums::VOICE_ASSISTANT_STT_END:
|
||||
return "VOICE_ASSISTANT_STT_END";
|
||||
case enums::VOICE_ASSISTANT_INTENT_START:
|
||||
return "VOICE_ASSISTANT_INTENT_START";
|
||||
case enums::VOICE_ASSISTANT_INTENT_END:
|
||||
return "VOICE_ASSISTANT_INTENT_END";
|
||||
case enums::VOICE_ASSISTANT_TTS_START:
|
||||
return "VOICE_ASSISTANT_TTS_START";
|
||||
case enums::VOICE_ASSISTANT_TTS_END:
|
||||
return "VOICE_ASSISTANT_TTS_END";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
@ -592,6 +620,10 @@ bool DeviceInfoResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
this->bluetooth_proxy_version = value.as_uint32();
|
||||
return true;
|
||||
}
|
||||
case 14: {
|
||||
this->voice_assistant_version = value.as_uint32();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@ -652,6 +684,7 @@ void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_uint32(11, this->bluetooth_proxy_version);
|
||||
buffer.encode_string(12, this->manufacturer);
|
||||
buffer.encode_string(13, this->friendly_name);
|
||||
buffer.encode_uint32(14, this->voice_assistant_version);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void DeviceInfoResponse::dump_to(std::string &out) const {
|
||||
@ -710,6 +743,11 @@ void DeviceInfoResponse::dump_to(std::string &out) const {
|
||||
out.append(" friendly_name: ");
|
||||
out.append("'").append(this->friendly_name).append("'");
|
||||
out.append("\n");
|
||||
|
||||
out.append(" voice_assistant_version: ");
|
||||
sprintf(buffer, "%u", this->voice_assistant_version);
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
@ -6060,6 +6098,204 @@ void BluetoothDeviceUnpairingResponse::dump_to(std::string &out) const {
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
void UnsubscribeBluetoothLEAdvertisementsRequest::encode(ProtoWriteBuffer buffer) const {}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void UnsubscribeBluetoothLEAdvertisementsRequest::dump_to(std::string &out) const {
|
||||
out.append("UnsubscribeBluetoothLEAdvertisementsRequest {}");
|
||||
}
|
||||
#endif
|
||||
bool BluetoothDeviceClearCacheResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
this->address = value.as_uint64();
|
||||
return true;
|
||||
}
|
||||
case 2: {
|
||||
this->success = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
case 3: {
|
||||
this->error = value.as_int32();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
void BluetoothDeviceClearCacheResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_uint64(1, this->address);
|
||||
buffer.encode_bool(2, this->success);
|
||||
buffer.encode_int32(3, this->error);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void BluetoothDeviceClearCacheResponse::dump_to(std::string &out) const {
|
||||
__attribute__((unused)) char buffer[64];
|
||||
out.append("BluetoothDeviceClearCacheResponse {\n");
|
||||
out.append(" address: ");
|
||||
sprintf(buffer, "%llu", this->address);
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
|
||||
out.append(" success: ");
|
||||
out.append(YESNO(this->success));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" error: ");
|
||||
sprintf(buffer, "%d", this->error);
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool SubscribeVoiceAssistantRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
this->subscribe = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
void SubscribeVoiceAssistantRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(1, this->subscribe); }
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void SubscribeVoiceAssistantRequest::dump_to(std::string &out) const {
|
||||
__attribute__((unused)) char buffer[64];
|
||||
out.append("SubscribeVoiceAssistantRequest {\n");
|
||||
out.append(" subscribe: ");
|
||||
out.append(YESNO(this->subscribe));
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool VoiceAssistantRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
this->start = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
void VoiceAssistantRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(1, this->start); }
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void VoiceAssistantRequest::dump_to(std::string &out) const {
|
||||
__attribute__((unused)) char buffer[64];
|
||||
out.append("VoiceAssistantRequest {\n");
|
||||
out.append(" start: ");
|
||||
out.append(YESNO(this->start));
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool VoiceAssistantResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
this->port = value.as_uint32();
|
||||
return true;
|
||||
}
|
||||
case 2: {
|
||||
this->error = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
void VoiceAssistantResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_uint32(1, this->port);
|
||||
buffer.encode_bool(2, this->error);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void VoiceAssistantResponse::dump_to(std::string &out) const {
|
||||
__attribute__((unused)) char buffer[64];
|
||||
out.append("VoiceAssistantResponse {\n");
|
||||
out.append(" port: ");
|
||||
sprintf(buffer, "%u", this->port);
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
|
||||
out.append(" error: ");
|
||||
out.append(YESNO(this->error));
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool VoiceAssistantEventData::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
this->name = value.as_string();
|
||||
return true;
|
||||
}
|
||||
case 2: {
|
||||
this->value = value.as_string();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
void VoiceAssistantEventData::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_string(1, this->name);
|
||||
buffer.encode_string(2, this->value);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void VoiceAssistantEventData::dump_to(std::string &out) const {
|
||||
__attribute__((unused)) char buffer[64];
|
||||
out.append("VoiceAssistantEventData {\n");
|
||||
out.append(" name: ");
|
||||
out.append("'").append(this->name).append("'");
|
||||
out.append("\n");
|
||||
|
||||
out.append(" value: ");
|
||||
out.append("'").append(this->value).append("'");
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool VoiceAssistantEventResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
this->event_type = value.as_enum<enums::VoiceAssistantEvent>();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool VoiceAssistantEventResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 2: {
|
||||
this->data.push_back(value.as_message<VoiceAssistantEventData>());
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
void VoiceAssistantEventResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_enum<enums::VoiceAssistantEvent>(1, this->event_type);
|
||||
for (auto &it : this->data) {
|
||||
buffer.encode_message<VoiceAssistantEventData>(2, it, true);
|
||||
}
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void VoiceAssistantEventResponse::dump_to(std::string &out) const {
|
||||
__attribute__((unused)) char buffer[64];
|
||||
out.append("VoiceAssistantEventResponse {\n");
|
||||
out.append(" event_type: ");
|
||||
out.append(proto_enum_to_string<enums::VoiceAssistantEvent>(this->event_type));
|
||||
out.append("\n");
|
||||
|
||||
for (const auto &it : this->data) {
|
||||
out.append(" data: ");
|
||||
it.dump_to(out);
|
||||
out.append("\n");
|
||||
}
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace api
|
||||
} // namespace esphome
|
||||
|
||||
@ -163,6 +163,18 @@ enum BluetoothDeviceRequestType : uint32_t {
|
||||
BLUETOOTH_DEVICE_REQUEST_TYPE_UNPAIR = 3,
|
||||
BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITH_CACHE = 4,
|
||||
BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE = 5,
|
||||
BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE = 6,
|
||||
};
|
||||
enum VoiceAssistantEvent : uint32_t {
|
||||
VOICE_ASSISTANT_ERROR = 0,
|
||||
VOICE_ASSISTANT_RUN_START = 1,
|
||||
VOICE_ASSISTANT_RUN_END = 2,
|
||||
VOICE_ASSISTANT_STT_START = 3,
|
||||
VOICE_ASSISTANT_STT_END = 4,
|
||||
VOICE_ASSISTANT_INTENT_START = 5,
|
||||
VOICE_ASSISTANT_INTENT_END = 6,
|
||||
VOICE_ASSISTANT_TTS_START = 7,
|
||||
VOICE_ASSISTANT_TTS_END = 8,
|
||||
};
|
||||
|
||||
} // namespace enums
|
||||
@ -278,6 +290,7 @@ class DeviceInfoResponse : public ProtoMessage {
|
||||
uint32_t bluetooth_proxy_version{0};
|
||||
std::string manufacturer{};
|
||||
std::string friendly_name{};
|
||||
uint32_t voice_assistant_version{0};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
@ -1554,6 +1567,87 @@ class BluetoothDeviceUnpairingResponse : public ProtoMessage {
|
||||
protected:
|
||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||
};
|
||||
class UnsubscribeBluetoothLEAdvertisementsRequest : public ProtoMessage {
|
||||
public:
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
};
|
||||
class BluetoothDeviceClearCacheResponse : public ProtoMessage {
|
||||
public:
|
||||
uint64_t address{0};
|
||||
bool success{false};
|
||||
int32_t error{0};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||
};
|
||||
class SubscribeVoiceAssistantRequest : public ProtoMessage {
|
||||
public:
|
||||
bool subscribe{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||
};
|
||||
class VoiceAssistantRequest : public ProtoMessage {
|
||||
public:
|
||||
bool start{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||
};
|
||||
class VoiceAssistantResponse : public ProtoMessage {
|
||||
public:
|
||||
uint32_t port{0};
|
||||
bool error{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||
};
|
||||
class VoiceAssistantEventData : public ProtoMessage {
|
||||
public:
|
||||
std::string name{};
|
||||
std::string value{};
|
||||
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;
|
||||
};
|
||||
class VoiceAssistantEventResponse : public ProtoMessage {
|
||||
public:
|
||||
enums::VoiceAssistantEvent event_type{};
|
||||
std::vector<VoiceAssistantEventData> data{};
|
||||
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;
|
||||
};
|
||||
|
||||
} // namespace api
|
||||
} // namespace esphome
|
||||
|
||||
@ -329,6 +329,8 @@ bool APIServerConnectionBase::send_media_player_state_response(const MediaPlayer
|
||||
#ifdef USE_MEDIA_PLAYER
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
bool APIServerConnectionBase::send_bluetooth_le_advertisement_response(const BluetoothLEAdvertisementResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_bluetooth_le_advertisement_response: %s", msg.dump().c_str());
|
||||
@ -441,6 +443,30 @@ bool APIServerConnectionBase::send_bluetooth_device_unpairing_response(const Blu
|
||||
return this->send_message_<BluetoothDeviceUnpairingResponse>(msg, 86);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
bool APIServerConnectionBase::send_bluetooth_device_clear_cache_response(const BluetoothDeviceClearCacheResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_bluetooth_device_clear_cache_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<BluetoothDeviceClearCacheResponse>(msg, 88);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_VOICE_ASSISTANT
|
||||
#endif
|
||||
#ifdef USE_VOICE_ASSISTANT
|
||||
bool APIServerConnectionBase::send_voice_assistant_request(const VoiceAssistantRequest &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_voice_assistant_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<VoiceAssistantRequest>(msg, 90);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_VOICE_ASSISTANT
|
||||
#endif
|
||||
#ifdef USE_VOICE_ASSISTANT
|
||||
#endif
|
||||
bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {
|
||||
switch (msg_type) {
|
||||
case 1: {
|
||||
@ -709,12 +735,14 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
||||
break;
|
||||
}
|
||||
case 66: {
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
SubscribeBluetoothLEAdvertisementsRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_subscribe_bluetooth_le_advertisements_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_subscribe_bluetooth_le_advertisements_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 68: {
|
||||
@ -802,6 +830,50 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
||||
ESP_LOGVV(TAG, "on_subscribe_bluetooth_connections_free_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_subscribe_bluetooth_connections_free_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 87: {
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
UnsubscribeBluetoothLEAdvertisementsRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_unsubscribe_bluetooth_le_advertisements_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_unsubscribe_bluetooth_le_advertisements_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 89: {
|
||||
#ifdef USE_VOICE_ASSISTANT
|
||||
SubscribeVoiceAssistantRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_subscribe_voice_assistant_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_subscribe_voice_assistant_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 91: {
|
||||
#ifdef USE_VOICE_ASSISTANT
|
||||
VoiceAssistantResponse msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_voice_assistant_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_voice_assistant_response(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 92: {
|
||||
#ifdef USE_VOICE_ASSISTANT
|
||||
VoiceAssistantEventResponse msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_voice_assistant_event_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_voice_assistant_event_response(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
@ -1065,6 +1137,7 @@ void APIServerConnection::on_media_player_command_request(const MediaPlayerComma
|
||||
this->media_player_command(msg);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
void APIServerConnection::on_subscribe_bluetooth_le_advertisements_request(
|
||||
const SubscribeBluetoothLEAdvertisementsRequest &msg) {
|
||||
if (!this->is_connection_setup()) {
|
||||
@ -1077,6 +1150,7 @@ void APIServerConnection::on_subscribe_bluetooth_le_advertisements_request(
|
||||
}
|
||||
this->subscribe_bluetooth_le_advertisements(msg);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
void APIServerConnection::on_bluetooth_device_request(const BluetoothDeviceRequest &msg) {
|
||||
if (!this->is_connection_setup()) {
|
||||
@ -1185,6 +1259,33 @@ void APIServerConnection::on_subscribe_bluetooth_connections_free_request(
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
void APIServerConnection::on_unsubscribe_bluetooth_le_advertisements_request(
|
||||
const UnsubscribeBluetoothLEAdvertisementsRequest &msg) {
|
||||
if (!this->is_connection_setup()) {
|
||||
this->on_no_setup_connection();
|
||||
return;
|
||||
}
|
||||
if (!this->is_authenticated()) {
|
||||
this->on_unauthenticated_access();
|
||||
return;
|
||||
}
|
||||
this->unsubscribe_bluetooth_le_advertisements(msg);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_VOICE_ASSISTANT
|
||||
void APIServerConnection::on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &msg) {
|
||||
if (!this->is_connection_setup()) {
|
||||
this->on_no_setup_connection();
|
||||
return;
|
||||
}
|
||||
if (!this->is_authenticated()) {
|
||||
this->on_unauthenticated_access();
|
||||
return;
|
||||
}
|
||||
this->subscribe_voice_assistant(msg);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace api
|
||||
} // namespace esphome
|
||||
|
||||
@ -154,8 +154,10 @@ class APIServerConnectionBase : public ProtoService {
|
||||
#ifdef USE_MEDIA_PLAYER
|
||||
virtual void on_media_player_command_request(const MediaPlayerCommandRequest &value){};
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
virtual void on_subscribe_bluetooth_le_advertisements_request(
|
||||
const SubscribeBluetoothLEAdvertisementsRequest &value){};
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
bool send_bluetooth_le_advertisement_response(const BluetoothLEAdvertisementResponse &msg);
|
||||
#endif
|
||||
@ -215,6 +217,25 @@ class APIServerConnectionBase : public ProtoService {
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
bool send_bluetooth_device_unpairing_response(const BluetoothDeviceUnpairingResponse &msg);
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
virtual void on_unsubscribe_bluetooth_le_advertisements_request(
|
||||
const UnsubscribeBluetoothLEAdvertisementsRequest &value){};
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
bool send_bluetooth_device_clear_cache_response(const BluetoothDeviceClearCacheResponse &msg);
|
||||
#endif
|
||||
#ifdef USE_VOICE_ASSISTANT
|
||||
virtual void on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &value){};
|
||||
#endif
|
||||
#ifdef USE_VOICE_ASSISTANT
|
||||
bool send_voice_assistant_request(const VoiceAssistantRequest &msg);
|
||||
#endif
|
||||
#ifdef USE_VOICE_ASSISTANT
|
||||
virtual void on_voice_assistant_response(const VoiceAssistantResponse &value){};
|
||||
#endif
|
||||
#ifdef USE_VOICE_ASSISTANT
|
||||
virtual void on_voice_assistant_event_response(const VoiceAssistantEventResponse &value){};
|
||||
#endif
|
||||
protected:
|
||||
bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;
|
||||
@ -267,7 +288,9 @@ class APIServerConnection : public APIServerConnectionBase {
|
||||
#ifdef USE_MEDIA_PLAYER
|
||||
virtual void media_player_command(const MediaPlayerCommandRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
virtual void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
virtual void bluetooth_device_request(const BluetoothDeviceRequest &msg) = 0;
|
||||
#endif
|
||||
@ -292,6 +315,12 @@ class APIServerConnection : public APIServerConnectionBase {
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
virtual BluetoothConnectionsFreeResponse subscribe_bluetooth_connections_free(
|
||||
const SubscribeBluetoothConnectionsFreeRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
virtual void unsubscribe_bluetooth_le_advertisements(const UnsubscribeBluetoothLEAdvertisementsRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_VOICE_ASSISTANT
|
||||
virtual void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) = 0;
|
||||
#endif
|
||||
protected:
|
||||
void on_hello_request(const HelloRequest &msg) override;
|
||||
@ -339,7 +368,9 @@ class APIServerConnection : public APIServerConnectionBase {
|
||||
#ifdef USE_MEDIA_PLAYER
|
||||
void on_media_player_command_request(const MediaPlayerCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
void on_subscribe_bluetooth_le_advertisements_request(const SubscribeBluetoothLEAdvertisementsRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
void on_bluetooth_device_request(const BluetoothDeviceRequest &msg) override;
|
||||
#endif
|
||||
@ -364,6 +395,13 @@ class APIServerConnection : public APIServerConnectionBase {
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
void on_subscribe_bluetooth_connections_free_request(const SubscribeBluetoothConnectionsFreeRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
void on_unsubscribe_bluetooth_le_advertisements_request(
|
||||
const UnsubscribeBluetoothLEAdvertisementsRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_VOICE_ASSISTANT
|
||||
void on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &msg) override;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace api
|
||||
|
||||
@ -45,7 +45,7 @@ void APIServer::setup() {
|
||||
|
||||
struct sockaddr_storage server;
|
||||
|
||||
socklen_t sl = socket::set_sockaddr_any((struct sockaddr *) &server, sizeof(server), htons(this->port_));
|
||||
socklen_t sl = socket::set_sockaddr_any((struct sockaddr *) &server, sizeof(server), this->port_);
|
||||
if (sl == 0) {
|
||||
ESP_LOGW(TAG, "Socket unable to set sockaddr: errno %d", errno);
|
||||
this->mark_failed();
|
||||
@ -331,6 +331,17 @@ void APIServer::send_bluetooth_device_unpairing(uint64_t address, bool success,
|
||||
}
|
||||
}
|
||||
|
||||
void APIServer::send_bluetooth_device_clear_cache(uint64_t address, bool success, esp_err_t error) {
|
||||
BluetoothDeviceClearCacheResponse call;
|
||||
call.address = address;
|
||||
call.success = success;
|
||||
call.error = error;
|
||||
|
||||
for (auto &client : this->clients_) {
|
||||
client->send_bluetooth_device_clear_cache_response(call);
|
||||
}
|
||||
}
|
||||
|
||||
void APIServer::send_bluetooth_connections_free(uint8_t free, uint8_t limit) {
|
||||
BluetoothConnectionsFreeResponse call;
|
||||
call.free = free;
|
||||
@ -416,5 +427,18 @@ void APIServer::on_shutdown() {
|
||||
delay(10);
|
||||
}
|
||||
|
||||
#ifdef USE_VOICE_ASSISTANT
|
||||
void APIServer::start_voice_assistant() {
|
||||
for (auto &c : this->clients_) {
|
||||
c->request_voice_assistant(true);
|
||||
}
|
||||
}
|
||||
void APIServer::stop_voice_assistant() {
|
||||
for (auto &c : this->clients_) {
|
||||
c->request_voice_assistant(false);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace api
|
||||
} // namespace esphome
|
||||
|
||||
@ -80,6 +80,7 @@ class APIServer : public Component, public Controller {
|
||||
void send_bluetooth_device_connection(uint64_t address, bool connected, uint16_t mtu = 0, esp_err_t error = ESP_OK);
|
||||
void send_bluetooth_device_pairing(uint64_t address, bool paired, esp_err_t error = ESP_OK);
|
||||
void send_bluetooth_device_unpairing(uint64_t address, bool success, esp_err_t error = ESP_OK);
|
||||
void send_bluetooth_device_clear_cache(uint64_t address, bool success, esp_err_t error = ESP_OK);
|
||||
void send_bluetooth_connections_free(uint8_t free, uint8_t limit);
|
||||
void send_bluetooth_gatt_read_response(const BluetoothGATTReadResponse &call);
|
||||
void send_bluetooth_gatt_write_response(const BluetoothGATTWriteResponse &call);
|
||||
@ -94,6 +95,11 @@ class APIServer : public Component, public Controller {
|
||||
void request_time();
|
||||
#endif
|
||||
|
||||
#ifdef USE_VOICE_ASSISTANT
|
||||
void start_voice_assistant();
|
||||
void stop_voice_assistant();
|
||||
#endif
|
||||
|
||||
bool is_connected() const;
|
||||
|
||||
struct HomeAssistantStateSubscription {
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
#include "proto.h"
|
||||
#include <cinttypes>
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
@ -13,7 +14,7 @@ void ProtoMessage::decode(const uint8_t *buffer, size_t length) {
|
||||
uint32_t consumed;
|
||||
auto res = ProtoVarInt::parse(&buffer[i], length - i, &consumed);
|
||||
if (!res.has_value()) {
|
||||
ESP_LOGV(TAG, "Invalid field start at %u", i);
|
||||
ESP_LOGV(TAG, "Invalid field start at %" PRIu32, i);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -25,12 +26,12 @@ void ProtoMessage::decode(const uint8_t *buffer, size_t length) {
|
||||
case 0: { // VarInt
|
||||
res = ProtoVarInt::parse(&buffer[i], length - i, &consumed);
|
||||
if (!res.has_value()) {
|
||||
ESP_LOGV(TAG, "Invalid VarInt at %u", i);
|
||||
ESP_LOGV(TAG, "Invalid VarInt at %" PRIu32, i);
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
if (!this->decode_varint(field_id, *res)) {
|
||||
ESP_LOGV(TAG, "Cannot decode VarInt field %u with value %u!", field_id, res->as_uint32());
|
||||
ESP_LOGV(TAG, "Cannot decode VarInt field %" PRIu32 " with value %" PRIu32 "!", field_id, res->as_uint32());
|
||||
}
|
||||
i += consumed;
|
||||
break;
|
||||
@ -38,38 +39,38 @@ void ProtoMessage::decode(const uint8_t *buffer, size_t length) {
|
||||
case 2: { // Length-delimited
|
||||
res = ProtoVarInt::parse(&buffer[i], length - i, &consumed);
|
||||
if (!res.has_value()) {
|
||||
ESP_LOGV(TAG, "Invalid Length Delimited at %u", i);
|
||||
ESP_LOGV(TAG, "Invalid Length Delimited at %" PRIu32, i);
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
uint32_t field_length = res->as_uint32();
|
||||
i += consumed;
|
||||
if (field_length > length - i) {
|
||||
ESP_LOGV(TAG, "Out-of-bounds Length Delimited at %u", i);
|
||||
ESP_LOGV(TAG, "Out-of-bounds Length Delimited at %" PRIu32, i);
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
if (!this->decode_length(field_id, ProtoLengthDelimited(&buffer[i], field_length))) {
|
||||
ESP_LOGV(TAG, "Cannot decode Length Delimited field %u!", field_id);
|
||||
ESP_LOGV(TAG, "Cannot decode Length Delimited field %" PRIu32 "!", field_id);
|
||||
}
|
||||
i += field_length;
|
||||
break;
|
||||
}
|
||||
case 5: { // 32-bit
|
||||
if (length - i < 4) {
|
||||
ESP_LOGV(TAG, "Out-of-bounds Fixed32-bit at %u", i);
|
||||
ESP_LOGV(TAG, "Out-of-bounds Fixed32-bit at %" PRIu32, i);
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
uint32_t val = encode_uint32(buffer[i + 3], buffer[i + 2], buffer[i + 1], buffer[i]);
|
||||
if (!this->decode_32bit(field_id, Proto32Bit(val))) {
|
||||
ESP_LOGV(TAG, "Cannot decode 32-bit field %u with value %u!", field_id, val);
|
||||
ESP_LOGV(TAG, "Cannot decode 32-bit field %" PRIu32 " with value %" PRIu32 "!", field_id, val);
|
||||
}
|
||||
i += 4;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ESP_LOGV(TAG, "Invalid field type at %u", i);
|
||||
ESP_LOGV(TAG, "Invalid field type at %" PRIu32, i);
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -12,7 +12,6 @@ from esphome.const import (
|
||||
CONF_CAPACITANCE,
|
||||
)
|
||||
|
||||
AUTO_LOAD = ["sensor", "binary_sensor"]
|
||||
MULTI_CONF = True
|
||||
|
||||
CONF_AS3935_ID = "as3935_id"
|
||||
|
||||
@ -26,9 +26,13 @@ void AS3935Component::setup() {
|
||||
void AS3935Component::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "AS3935:");
|
||||
LOG_PIN(" Interrupt Pin: ", this->irq_pin_);
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
LOG_BINARY_SENSOR(" ", "Thunder alert", this->thunder_alert_binary_sensor_);
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
LOG_SENSOR(" ", "Distance", this->distance_sensor_);
|
||||
LOG_SENSOR(" ", "Lightning energy", this->energy_sensor_);
|
||||
#endif
|
||||
}
|
||||
|
||||
float AS3935Component::get_setup_priority() const { return setup_priority::DATA; }
|
||||
@ -44,16 +48,22 @@ void AS3935Component::loop() {
|
||||
ESP_LOGI(TAG, "Disturber was detected - try increasing the spike rejection value!");
|
||||
} else if (int_value == LIGHTNING_INT) {
|
||||
ESP_LOGI(TAG, "Lightning has been detected!");
|
||||
if (this->thunder_alert_binary_sensor_ != nullptr)
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
if (this->thunder_alert_binary_sensor_ != nullptr) {
|
||||
this->thunder_alert_binary_sensor_->publish_state(true);
|
||||
this->set_timeout(10, [this]() { this->thunder_alert_binary_sensor_->publish_state(false); });
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
uint8_t distance = this->get_distance_to_storm_();
|
||||
if (this->distance_sensor_ != nullptr)
|
||||
this->distance_sensor_->publish_state(distance);
|
||||
|
||||
uint32_t energy = this->get_lightning_energy_();
|
||||
if (this->energy_sensor_ != nullptr)
|
||||
this->energy_sensor_->publish_state(energy);
|
||||
#endif
|
||||
}
|
||||
this->thunder_alert_binary_sensor_->publish_state(false);
|
||||
}
|
||||
|
||||
void AS3935Component::write_indoor(bool indoor) {
|
||||
|
||||
@ -1,9 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#ifdef USE_SENSOR
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#endif
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace as3935 {
|
||||
@ -52,6 +57,15 @@ enum AS3935Values {
|
||||
};
|
||||
|
||||
class AS3935Component : public Component {
|
||||
#ifdef USE_SENSOR
|
||||
SUB_SENSOR(distance)
|
||||
SUB_SENSOR(energy)
|
||||
#endif
|
||||
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
SUB_BINARY_SENSOR(thunder_alert)
|
||||
#endif
|
||||
|
||||
public:
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
@ -59,11 +73,7 @@ class AS3935Component : public Component {
|
||||
void loop() override;
|
||||
|
||||
void set_irq_pin(GPIOPin *irq_pin) { irq_pin_ = irq_pin; }
|
||||
void set_distance_sensor(sensor::Sensor *distance_sensor) { distance_sensor_ = distance_sensor; }
|
||||
void set_energy_sensor(sensor::Sensor *energy_sensor) { energy_sensor_ = energy_sensor; }
|
||||
void set_thunder_alert_binary_sensor(binary_sensor::BinarySensor *thunder_alert_binary_sensor) {
|
||||
thunder_alert_binary_sensor_ = thunder_alert_binary_sensor;
|
||||
}
|
||||
|
||||
void set_indoor(bool indoor) { indoor_ = indoor; }
|
||||
void write_indoor(bool indoor);
|
||||
void set_noise_level(uint8_t noise_level) { noise_level_ = noise_level; }
|
||||
@ -92,9 +102,6 @@ class AS3935Component : public Component {
|
||||
|
||||
virtual void write_register(uint8_t reg, uint8_t mask, uint8_t bits, uint8_t start_position) = 0;
|
||||
|
||||
sensor::Sensor *distance_sensor_{nullptr};
|
||||
sensor::Sensor *energy_sensor_{nullptr};
|
||||
binary_sensor::BinarySensor *thunder_alert_binary_sensor_{nullptr};
|
||||
GPIOPin *irq_pin_;
|
||||
|
||||
bool indoor_;
|
||||
|
||||
@ -116,7 +116,7 @@ class BedJetHub : public esphome::ble_client::BLEClientNode, public PollingCompo
|
||||
void update() override;
|
||||
void dump_config() override;
|
||||
void setup() override { this->codec_ = make_unique<BedjetCodec>(); }
|
||||
float get_setup_priority() const override { return setup_priority::AFTER_WIFI; }
|
||||
float get_setup_priority() const override { return setup_priority::BLUETOOTH; }
|
||||
|
||||
/** @return The BedJet's configured name, or the MAC address if not discovered yet. */
|
||||
std::string get_name() {
|
||||
|
||||
@ -41,16 +41,13 @@ void BinarySensor::send_state_internal(bool state, bool is_initial) {
|
||||
this->state_callback_.call(state);
|
||||
}
|
||||
}
|
||||
std::string BinarySensor::device_class() { return ""; }
|
||||
|
||||
BinarySensor::BinarySensor() : state(false) {}
|
||||
void BinarySensor::set_device_class(const std::string &device_class) { this->device_class_ = device_class; }
|
||||
std::string BinarySensor::get_device_class() {
|
||||
if (this->device_class_.has_value())
|
||||
return *this->device_class_;
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
return this->device_class();
|
||||
#pragma GCC diagnostic pop
|
||||
return "";
|
||||
}
|
||||
void BinarySensor::add_filter(Filter *filter) {
|
||||
filter->parent_ = this;
|
||||
|
||||
@ -80,14 +80,6 @@ class BinarySensor : public EntityBase {
|
||||
|
||||
virtual bool is_status_binary_sensor() const;
|
||||
|
||||
// ========== OVERRIDE METHODS ==========
|
||||
// (You'll only need this when creating your own custom binary sensor)
|
||||
/** Override this to set the default device class.
|
||||
*
|
||||
* @deprecated This method is deprecated, set the property during config validation instead. (2022.1)
|
||||
*/
|
||||
virtual std::string device_class();
|
||||
|
||||
protected:
|
||||
CallbackManager<void(bool)> state_callback_{};
|
||||
optional<std::string> device_class_{}; ///< Stores the override of the device class
|
||||
|
||||
@ -30,7 +30,7 @@ void BinarySensorMap::process_group_() {
|
||||
if (bs.binary_sensor->state) {
|
||||
num_active_sensors++;
|
||||
total_current_value += bs.sensor_value;
|
||||
mask |= 1 << i;
|
||||
mask |= 1ULL << i;
|
||||
}
|
||||
}
|
||||
// check if the sensor map was touched
|
||||
@ -38,12 +38,11 @@ void BinarySensorMap::process_group_() {
|
||||
// did the bit_mask change or is it a new sensor touch
|
||||
if (this->last_mask_ != mask) {
|
||||
float publish_value = total_current_value / num_active_sensors;
|
||||
ESP_LOGD(TAG, "'%s' - Publishing %.2f", this->name_.c_str(), publish_value);
|
||||
this->publish_state(publish_value);
|
||||
}
|
||||
} else if (this->last_mask_ != 0ULL) {
|
||||
// is this a new sensor release
|
||||
ESP_LOGD(TAG, "'%s' - No binary sensor active, publishing NAN", this->name_.c_str());
|
||||
ESP_LOGV(TAG, "'%s' - No binary sensor active, publishing NAN", this->name_.c_str());
|
||||
this->publish_state(NAN);
|
||||
}
|
||||
this->last_mask_ = mask;
|
||||
@ -52,28 +51,22 @@ void BinarySensorMap::process_group_() {
|
||||
void BinarySensorMap::process_sum_() {
|
||||
float total_current_value = 0.0;
|
||||
uint64_t mask = 0x00;
|
||||
// check all binary_sensors for its state. when active add its value to total_current_value.
|
||||
// create a bitmask for the binary_sensor status on all channels
|
||||
// - check all binary_sensor states
|
||||
// - if active, add its value to total_current_value
|
||||
// - creates a bitmask for the binary_sensor status on all channels
|
||||
for (size_t i = 0; i < this->channels_.size(); i++) {
|
||||
auto bs = this->channels_[i];
|
||||
if (bs.binary_sensor->state) {
|
||||
total_current_value += bs.sensor_value;
|
||||
mask |= 1 << i;
|
||||
mask |= 1ULL << i;
|
||||
}
|
||||
}
|
||||
// check if the sensor map was touched
|
||||
if (mask != 0ULL) {
|
||||
// did the bit_mask change or is it a new sensor touch
|
||||
if (this->last_mask_ != mask) {
|
||||
float publish_value = total_current_value;
|
||||
ESP_LOGD(TAG, "'%s' - Publishing %.2f", this->name_.c_str(), publish_value);
|
||||
this->publish_state(publish_value);
|
||||
}
|
||||
} else if (this->last_mask_ != 0ULL) {
|
||||
// is this a new sensor release
|
||||
ESP_LOGD(TAG, "'%s' - No binary sensor active, publishing 0", this->name_.c_str());
|
||||
this->publish_state(0.0);
|
||||
|
||||
// update state only if the binary sensor states have changed or if no state has ever been sent on boot
|
||||
if ((this->last_mask_ != mask) || (!this->has_state())) {
|
||||
this->publish_state(total_current_value);
|
||||
}
|
||||
|
||||
this->last_mask_ = mask;
|
||||
}
|
||||
|
||||
|
||||
@ -39,7 +39,7 @@ CONFIG_SCHEMA = cv.typed_schema(
|
||||
).extend(
|
||||
{
|
||||
cv.Required(CONF_CHANNELS): cv.All(
|
||||
cv.ensure_list(entry), cv.Length(min=1)
|
||||
cv.ensure_list(entry), cv.Length(min=1, max=64)
|
||||
),
|
||||
}
|
||||
),
|
||||
@ -50,7 +50,7 @@ CONFIG_SCHEMA = cv.typed_schema(
|
||||
).extend(
|
||||
{
|
||||
cv.Required(CONF_CHANNELS): cv.All(
|
||||
cv.ensure_list(entry), cv.Length(min=1)
|
||||
cv.ensure_list(entry), cv.Length(min=1, max=64)
|
||||
),
|
||||
}
|
||||
),
|
||||
|
||||
@ -306,6 +306,13 @@ void BluetoothProxy::bluetooth_device_request(const api::BluetoothDeviceRequest
|
||||
api::global_api_server->send_bluetooth_device_unpairing(msg.address, ret == ESP_OK, ret);
|
||||
break;
|
||||
}
|
||||
case api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE: {
|
||||
esp_bd_addr_t address;
|
||||
uint64_to_bd_addr(msg.address, address);
|
||||
esp_err_t ret = esp_ble_gattc_cache_clean(address);
|
||||
api::global_api_server->send_bluetooth_device_clear_cache(msg.address, ret == ESP_OK, ret);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -68,6 +68,14 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com
|
||||
|
||||
extern BluetoothProxy *global_bluetooth_proxy; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
||||
// Version 1: Initial version without active connections
|
||||
// Version 2: Support for active connections
|
||||
// Version 3: New connection API
|
||||
// Version 4: Pairing support
|
||||
// Version 5: Cache clear support
|
||||
static const uint32_t ACTIVE_CONNECTIONS_VERSION = 5;
|
||||
static const uint32_t PASSIVE_ONLY_VERSION = 1;
|
||||
|
||||
} // namespace bluetooth_proxy
|
||||
} // namespace esphome
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
#include "bme680.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace bme680 {
|
||||
@ -275,8 +275,8 @@ uint8_t BME680Component::calc_heater_resistance_(uint16_t temperature) {
|
||||
var3 = var1 + (var2 / 2);
|
||||
var4 = (var3 / (res_heat_range + 4));
|
||||
var5 = (131 * res_heat_val) + 65536;
|
||||
heatr_res_x100 = (int32_t)(((var4 / var5) - 250) * 34);
|
||||
heatr_res = (uint8_t)((heatr_res_x100 + 50) / 100);
|
||||
heatr_res_x100 = (int32_t) (((var4 / var5) - 250) * 34);
|
||||
heatr_res = (uint8_t) ((heatr_res_x100 + 50) / 100);
|
||||
|
||||
return heatr_res;
|
||||
}
|
||||
@ -316,7 +316,7 @@ void BME680Component::read_data_() {
|
||||
uint32_t raw_temperature = (uint32_t(data[5]) << 12) | (uint32_t(data[6]) << 4) | (uint32_t(data[7]) >> 4);
|
||||
uint32_t raw_pressure = (uint32_t(data[2]) << 12) | (uint32_t(data[3]) << 4) | (uint32_t(data[4]) >> 4);
|
||||
uint32_t raw_humidity = (uint32_t(data[8]) << 8) | uint32_t(data[9]);
|
||||
uint16_t raw_gas = (uint16_t)((uint32_t) data[13] * 4 | (((uint32_t) data[14]) / 64));
|
||||
uint16_t raw_gas = (uint16_t) ((uint32_t) data[13] * 4 | (((uint32_t) data[14]) / 64));
|
||||
uint8_t gas_range = data[14] & 0x0F;
|
||||
|
||||
float temperature = this->calc_temperature_(raw_temperature);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import i2c
|
||||
from esphome.components import i2c, esp32
|
||||
from esphome.const import CONF_ID
|
||||
|
||||
CODEOWNERS = ["@trvrnrth"]
|
||||
@ -32,22 +32,31 @@ BME680BSECComponent = bme680_bsec_ns.class_(
|
||||
"BME680BSECComponent", cg.Component, i2c.I2CDevice
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(BME680BSECComponent),
|
||||
cv.Optional(CONF_TEMPERATURE_OFFSET, default=0): cv.temperature,
|
||||
cv.Optional(CONF_IAQ_MODE, default="STATIC"): cv.enum(
|
||||
IAQ_MODE_OPTIONS, upper=True
|
||||
),
|
||||
cv.Optional(CONF_SAMPLE_RATE, default="LP"): cv.enum(
|
||||
SAMPLE_RATE_OPTIONS, upper=True
|
||||
),
|
||||
cv.Optional(
|
||||
CONF_STATE_SAVE_INTERVAL, default="6hours"
|
||||
): cv.positive_time_period_minutes,
|
||||
},
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(BME680BSECComponent),
|
||||
cv.Optional(CONF_TEMPERATURE_OFFSET, default=0): cv.temperature,
|
||||
cv.Optional(CONF_IAQ_MODE, default="STATIC"): cv.enum(
|
||||
IAQ_MODE_OPTIONS, upper=True
|
||||
),
|
||||
cv.Optional(CONF_SAMPLE_RATE, default="LP"): cv.enum(
|
||||
SAMPLE_RATE_OPTIONS, upper=True
|
||||
),
|
||||
cv.Optional(
|
||||
CONF_STATE_SAVE_INTERVAL, default="6hours"
|
||||
): cv.positive_time_period_minutes,
|
||||
}
|
||||
).extend(i2c.i2c_device_schema(0x76)),
|
||||
cv.only_with_arduino,
|
||||
).extend(i2c.i2c_device_schema(0x76))
|
||||
cv.Any(
|
||||
cv.only_on_esp8266,
|
||||
cv.All(
|
||||
cv.only_on_esp32,
|
||||
esp32.only_on_variant(supported=[esp32.const.VARIANT_ESP32]),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
|
||||
@ -6,9 +6,6 @@ namespace button {
|
||||
|
||||
static const char *const TAG = "button";
|
||||
|
||||
Button::Button(const std::string &name) : EntityBase(name) {}
|
||||
Button::Button() : Button("") {}
|
||||
|
||||
void Button::press() {
|
||||
ESP_LOGD(TAG, "'%s' Pressed.", this->get_name().c_str());
|
||||
this->press_action();
|
||||
|
||||
@ -28,9 +28,6 @@ namespace button {
|
||||
*/
|
||||
class Button : public EntityBase {
|
||||
public:
|
||||
explicit Button();
|
||||
explicit Button(const std::string &name);
|
||||
|
||||
/** Press this button. This is called by the front-end.
|
||||
*
|
||||
* For implementing buttons, please override press_action.
|
||||
|
||||
@ -145,8 +145,8 @@ void CCS811Component::send_env_data_() {
|
||||
// https://github.com/adafruit/Adafruit_CCS811/blob/0990f5c620354d8bc087c4706bec091d8e6e5dfd/Adafruit_CCS811.cpp#L135-L142
|
||||
uint16_t hum_conv = static_cast<uint16_t>(lroundf(humidity * 512.0f + 0.5f));
|
||||
uint16_t temp_conv = static_cast<uint16_t>(lroundf(temperature * 512.0f + 0.5f));
|
||||
this->write_bytes(0x05, {(uint8_t)((hum_conv >> 8) & 0xff), (uint8_t)((hum_conv & 0xff)),
|
||||
(uint8_t)((temp_conv >> 8) & 0xff), (uint8_t)((temp_conv & 0xff))});
|
||||
this->write_bytes(0x05, {(uint8_t) ((hum_conv >> 8) & 0xff), (uint8_t) ((hum_conv & 0xff)),
|
||||
(uint8_t) ((temp_conv >> 8) & 0xff), (uint8_t) ((temp_conv & 0xff))});
|
||||
}
|
||||
void CCS811Component::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "CCS811");
|
||||
|
||||
@ -324,6 +324,10 @@ async def setup_climate_core_(var, config):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
|
||||
for conf in config.get(CONF_ON_CONTROL, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
|
||||
|
||||
async def register_climate(var, config):
|
||||
if not CORE.has_id(config[CONF_ID]):
|
||||
|
||||
@ -453,12 +453,7 @@ void Climate::set_visual_temperature_step_override(float target, float current)
|
||||
this->visual_target_temperature_step_override_ = target;
|
||||
this->visual_current_temperature_step_override_ = current;
|
||||
}
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
Climate::Climate(const std::string &name) : EntityBase(name) {}
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
Climate::Climate() : Climate("") {}
|
||||
ClimateCall Climate::make_call() { return ClimateCall(this); }
|
||||
|
||||
ClimateCall ClimateDeviceRestoreState::to_call(Climate *climate) {
|
||||
|
||||
@ -166,11 +166,6 @@ struct ClimateDeviceRestoreState {
|
||||
*/
|
||||
class Climate : public EntityBase {
|
||||
public:
|
||||
/// Construct a climate device with empty name (will be set later).
|
||||
Climate();
|
||||
/// Construct a climate device with a name.
|
||||
Climate(const std::string &name);
|
||||
|
||||
/// The active mode of the climate device.
|
||||
ClimateMode mode{CLIMATE_MODE_OFF};
|
||||
/// The active state of the climate device.
|
||||
|
||||
@ -14,12 +14,15 @@ from .. import copy_ns
|
||||
CopySelect = copy_ns.class_("CopySelect", select.Select, cg.Component)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = select.SELECT_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(CopySelect),
|
||||
cv.Required(CONF_SOURCE_ID): cv.use_id(select.Select),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
CONFIG_SCHEMA = (
|
||||
select.select_schema(CopySelect)
|
||||
.extend(
|
||||
{
|
||||
cv.Required(CONF_SOURCE_ID): cv.use_id(select.Select),
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = cv.All(
|
||||
inherit_property_from(CONF_ICON, CONF_SOURCE_ID),
|
||||
|
||||
@ -31,7 +31,7 @@ const char *cover_operation_to_str(CoverOperation op) {
|
||||
}
|
||||
}
|
||||
|
||||
Cover::Cover(const std::string &name) : EntityBase(name), position{COVER_OPEN} {}
|
||||
Cover::Cover() : position{COVER_OPEN} {}
|
||||
|
||||
CoverCall::CoverCall(Cover *parent) : parent_(parent) {}
|
||||
CoverCall &CoverCall::set_command(const char *command) {
|
||||
@ -204,18 +204,13 @@ optional<CoverRestoreState> Cover::restore_state_() {
|
||||
return {};
|
||||
return recovered;
|
||||
}
|
||||
Cover::Cover() : Cover("") {}
|
||||
std::string Cover::get_device_class() {
|
||||
if (this->device_class_override_.has_value())
|
||||
return *this->device_class_override_;
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
return this->device_class();
|
||||
#pragma GCC diagnostic pop
|
||||
return "";
|
||||
}
|
||||
bool Cover::is_fully_open() const { return this->position == COVER_OPEN; }
|
||||
bool Cover::is_fully_closed() const { return this->position == COVER_CLOSED; }
|
||||
std::string Cover::device_class() { return ""; }
|
||||
|
||||
CoverCall CoverRestoreState::to_call(Cover *cover) {
|
||||
auto call = cover->make_call();
|
||||
|
||||
@ -111,7 +111,6 @@ const char *cover_operation_to_str(CoverOperation op);
|
||||
class Cover : public EntityBase {
|
||||
public:
|
||||
explicit Cover();
|
||||
explicit Cover(const std::string &name);
|
||||
|
||||
/// The current operation of the cover (idle, opening, closing).
|
||||
CoverOperation current_operation{COVER_OPERATION_IDLE};
|
||||
@ -170,12 +169,6 @@ class Cover : public EntityBase {
|
||||
|
||||
virtual void control(const CoverCall &call) = 0;
|
||||
|
||||
/** Override this to set the default device class.
|
||||
*
|
||||
* @deprecated This method is deprecated, set the property during config validation instead. (2022.1)
|
||||
*/
|
||||
virtual std::string device_class();
|
||||
|
||||
optional<CoverRestoreState> restore_state_();
|
||||
|
||||
CallbackManager<void()> state_callback_{};
|
||||
|
||||
@ -305,7 +305,7 @@ bool CS5460AComponent::check_status_() {
|
||||
voltage_sensor_->publish_state(raw_voltage * voltage_multiplier_);
|
||||
|
||||
if (power_sensor_ != nullptr && raw_energy != prev_raw_energy_) {
|
||||
int32_t raw = (int32_t)(raw_energy << 8) >> 8; /* Sign-extend */
|
||||
int32_t raw = (int32_t) (raw_energy << 8) >> 8; /* Sign-extend */
|
||||
power_sensor_->publish_state(raw * power_multiplier_);
|
||||
prev_raw_energy_ = raw_energy;
|
||||
}
|
||||
|
||||
@ -33,7 +33,10 @@ void CTClampSensor::update() {
|
||||
|
||||
const float rms_ac_dc_squared = this->sample_squared_sum_ / this->num_samples_;
|
||||
const float rms_dc = this->sample_sum_ / this->num_samples_;
|
||||
const float rms_ac = std::sqrt(rms_ac_dc_squared - rms_dc * rms_dc);
|
||||
const float rms_ac_squared = rms_ac_dc_squared - rms_dc * rms_dc;
|
||||
float rms_ac = 0;
|
||||
if (rms_ac_squared > 0)
|
||||
rms_ac = std::sqrt(rms_ac_squared);
|
||||
ESP_LOGD(TAG, "'%s' - Raw AC Value: %.3fA after %d different samples (%d SPS)", this->name_.c_str(), rms_ac,
|
||||
this->num_samples_, 1000 * this->num_samples_ / this->sample_duration_);
|
||||
this->publish_state(rms_ac);
|
||||
|
||||
@ -61,8 +61,8 @@ void DalyBmsComponent::request_data_(uint8_t data_id) {
|
||||
request_message[9] = 0x00; // |
|
||||
request_message[10] = 0x00; // |
|
||||
request_message[11] = 0x00; // Empty Data
|
||||
request_message[12] = (uint8_t)(request_message[0] + request_message[1] + request_message[2] +
|
||||
request_message[3]); // Checksum (Lower byte of the other bytes sum)
|
||||
request_message[12] = (uint8_t) (request_message[0] + request_message[1] + request_message[2] +
|
||||
request_message[3]); // Checksum (Lower byte of the other bytes sum)
|
||||
|
||||
this->write_array(request_message, sizeof(request_message));
|
||||
this->flush();
|
||||
|
||||
@ -19,7 +19,7 @@ void DFPlayer::play_folder(uint16_t folder, uint16_t file) {
|
||||
}
|
||||
|
||||
void DFPlayer::send_cmd_(uint8_t cmd, uint16_t argument) {
|
||||
uint8_t buffer[10]{0x7e, 0xff, 0x06, cmd, 0x01, (uint8_t)(argument >> 8), (uint8_t) argument, 0x00, 0x00, 0xef};
|
||||
uint8_t buffer[10]{0x7e, 0xff, 0x06, cmd, 0x01, (uint8_t) (argument >> 8), (uint8_t) argument, 0x00, 0x00, 0xef};
|
||||
uint16_t checksum = 0;
|
||||
for (uint8_t i = 1; i < 7; i++)
|
||||
checksum += buffer[i];
|
||||
|
||||
@ -664,7 +664,7 @@ bool Animation::get_pixel(int x, int y) const {
|
||||
return false;
|
||||
const uint32_t width_8 = ((this->width_ + 7u) / 8u) * 8u;
|
||||
const uint32_t frame_index = this->height_ * width_8 * this->current_frame_;
|
||||
if (frame_index >= (uint32_t)(this->width_ * this->height_ * this->animation_frame_count_))
|
||||
if (frame_index >= (uint32_t) (this->width_ * this->height_ * this->animation_frame_count_))
|
||||
return false;
|
||||
const uint32_t pos = x + y * width_8 + frame_index;
|
||||
return progmem_read_byte(this->data_start_ + (pos / 8u)) & (0x80 >> (pos % 8u));
|
||||
@ -673,7 +673,7 @@ Color Animation::get_color_pixel(int x, int y) const {
|
||||
if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_)
|
||||
return Color::BLACK;
|
||||
const uint32_t frame_index = this->width_ * this->height_ * this->current_frame_;
|
||||
if (frame_index >= (uint32_t)(this->width_ * this->height_ * this->animation_frame_count_))
|
||||
if (frame_index >= (uint32_t) (this->width_ * this->height_ * this->animation_frame_count_))
|
||||
return Color::BLACK;
|
||||
const uint32_t pos = (x + y * this->width_ + frame_index) * 3;
|
||||
const uint32_t color32 = (progmem_read_byte(this->data_start_ + pos + 2) << 0) |
|
||||
@ -685,7 +685,7 @@ Color Animation::get_rgb565_pixel(int x, int y) const {
|
||||
if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_)
|
||||
return Color::BLACK;
|
||||
const uint32_t frame_index = this->width_ * this->height_ * this->current_frame_;
|
||||
if (frame_index >= (uint32_t)(this->width_ * this->height_ * this->animation_frame_count_))
|
||||
if (frame_index >= (uint32_t) (this->width_ * this->height_ * this->animation_frame_count_))
|
||||
return Color::BLACK;
|
||||
const uint32_t pos = (x + y * this->width_ + frame_index) * 2;
|
||||
uint16_t rgb565 =
|
||||
@ -699,7 +699,7 @@ Color Animation::get_grayscale_pixel(int x, int y) const {
|
||||
if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_)
|
||||
return Color::BLACK;
|
||||
const uint32_t frame_index = this->width_ * this->height_ * this->current_frame_;
|
||||
if (frame_index >= (uint32_t)(this->width_ * this->height_ * this->animation_frame_count_))
|
||||
if (frame_index >= (uint32_t) (this->width_ * this->height_ * this->animation_frame_count_))
|
||||
return Color::BLACK;
|
||||
const uint32_t pos = (x + y * this->width_ + frame_index);
|
||||
const uint8_t gray = progmem_read_byte(this->data_start_ + pos);
|
||||
|
||||
@ -168,7 +168,7 @@ void ENS210Component::update() {
|
||||
return;
|
||||
}
|
||||
// Pack bytes for humidity
|
||||
h_val_data = (uint32_t)((uint32_t) data[5] << 16 | (uint32_t) data[4] << 8 | (uint32_t) data[3]);
|
||||
h_val_data = (uint32_t) ((uint32_t) data[5] << 16 | (uint32_t) data[4] << 8 | (uint32_t) data[3]);
|
||||
// Extract humidity data and update the status
|
||||
extract_measurement_(h_val_data, &humidity_data, &humidity_status);
|
||||
|
||||
@ -183,7 +183,7 @@ void ENS210Component::update() {
|
||||
return;
|
||||
}
|
||||
// Pack bytes for temperature
|
||||
t_val_data = (uint32_t)((uint32_t) data[2] << 16 | (uint32_t) data[1] << 8 | (uint32_t) data[0]);
|
||||
t_val_data = (uint32_t) ((uint32_t) data[2] << 16 | (uint32_t) data[1] << 8 | (uint32_t) data[0]);
|
||||
// Extract temperature data and update the status
|
||||
extract_measurement_(t_val_data, &temperature_data, &temperature_status);
|
||||
|
||||
|
||||
@ -23,7 +23,7 @@ void loop();
|
||||
namespace esphome {
|
||||
|
||||
void IRAM_ATTR HOT yield() { vPortYield(); }
|
||||
uint32_t IRAM_ATTR HOT millis() { return (uint32_t)(esp_timer_get_time() / 1000ULL); }
|
||||
uint32_t IRAM_ATTR HOT millis() { return (uint32_t) (esp_timer_get_time() / 1000ULL); }
|
||||
void IRAM_ATTR HOT delay(uint32_t ms) { vTaskDelay(ms / portTICK_PERIOD_MS); }
|
||||
uint32_t IRAM_ATTR HOT micros() { return (uint32_t) esp_timer_get_time(); }
|
||||
void IRAM_ATTR HOT delayMicroseconds(uint32_t us) { delay_microseconds_safe(us); }
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
#include "gpio.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include <cinttypes>
|
||||
|
||||
namespace esphome {
|
||||
namespace esp32 {
|
||||
@ -74,7 +75,7 @@ void ESP32InternalGPIOPin::attach_interrupt(void (*func)(void *), void *arg, gpi
|
||||
|
||||
std::string ESP32InternalGPIOPin::dump_summary() const {
|
||||
char buffer[32];
|
||||
snprintf(buffer, sizeof(buffer), "GPIO%u", static_cast<uint32_t>(pin_));
|
||||
snprintf(buffer, sizeof(buffer), "GPIO%" PRIu32, static_cast<uint32_t>(pin_));
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
#include "esphome/core/log.h"
|
||||
#include <nvs_flash.h>
|
||||
#include <cstring>
|
||||
#include <cinttypes>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
@ -101,7 +102,7 @@ class ESP32Preferences : public ESPPreferences {
|
||||
pref->nvs_handle = nvs_handle;
|
||||
|
||||
uint32_t keyval = type;
|
||||
pref->key = str_sprintf("%u", keyval);
|
||||
pref->key = str_sprintf("%" PRIu32, keyval);
|
||||
|
||||
return ESPPreferenceObject(pref);
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
|
||||
#include <esp_bt.h>
|
||||
#include <esp_bt_main.h>
|
||||
#include <esp_bt_device.h>
|
||||
#include <esp_gap_ble_api.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/FreeRTOSConfig.h>
|
||||
@ -211,7 +212,16 @@ void ESP32BLE::real_gattc_event_handler_(esp_gattc_cb_event_t event, esp_gatt_if
|
||||
|
||||
float ESP32BLE::get_setup_priority() const { return setup_priority::BLUETOOTH; }
|
||||
|
||||
void ESP32BLE::dump_config() { ESP_LOGCONFIG(TAG, "ESP32 BLE:"); }
|
||||
void ESP32BLE::dump_config() {
|
||||
const uint8_t *mac_address = esp_bt_dev_get_address();
|
||||
if (mac_address) {
|
||||
ESP_LOGCONFIG(TAG, "ESP32 BLE:");
|
||||
ESP_LOGCONFIG(TAG, " MAC address: %02X:%02X:%02X:%02X:%02X:%02X", mac_address[0], mac_address[1], mac_address[2],
|
||||
mac_address[3], mac_address[4], mac_address[5]);
|
||||
} else {
|
||||
ESP_LOGCONFIG(TAG, "ESP32 BLE: bluetooth stack is not enabled");
|
||||
}
|
||||
}
|
||||
|
||||
ESP32BLE *global_ble = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
||||
|
||||
@ -316,18 +316,18 @@ float BLEClientBase::parse_char_value(uint8_t *value, uint16_t length) {
|
||||
case 0xD: // int12.
|
||||
case 0xE: // int16.
|
||||
if (length > 2) {
|
||||
return (float) ((int16_t)(value[1] << 8) + (int16_t) value[2]);
|
||||
return (float) ((int16_t) (value[1] << 8) + (int16_t) value[2]);
|
||||
}
|
||||
// fall through
|
||||
case 0xF: // int24.
|
||||
if (length > 3) {
|
||||
return (float) ((int32_t)(value[1] << 16) + (int32_t)(value[2] << 8) + (int32_t)(value[3]));
|
||||
return (float) ((int32_t) (value[1] << 16) + (int32_t) (value[2] << 8) + (int32_t) (value[3]));
|
||||
}
|
||||
// fall through
|
||||
case 0x10: // int32.
|
||||
if (length > 4) {
|
||||
return (float) ((int32_t)(value[1] << 24) + (int32_t)(value[2] << 16) + (int32_t)(value[3] << 8) +
|
||||
(int32_t)(value[4]));
|
||||
return (float) ((int32_t) (value[1] << 24) + (int32_t) (value[2] << 16) + (int32_t) (value[3] << 8) +
|
||||
(int32_t) (value[4]));
|
||||
}
|
||||
}
|
||||
ESP_LOGW(TAG, "[%d] [%s] Cannot parse characteristic value of type 0x%x length %d", this->connection_index_,
|
||||
|
||||
@ -45,10 +45,11 @@ class BLEClientBase : public espbt::ESPBTClient, public Component {
|
||||
memset(this->remote_bda_, 0, sizeof(this->remote_bda_));
|
||||
this->address_str_ = "";
|
||||
} else {
|
||||
this->address_str_ = str_snprintf("%02X:%02X:%02X:%02X:%02X:%02X", 17, (uint8_t)(this->address_ >> 40) & 0xff,
|
||||
(uint8_t)(this->address_ >> 32) & 0xff, (uint8_t)(this->address_ >> 24) & 0xff,
|
||||
(uint8_t)(this->address_ >> 16) & 0xff, (uint8_t)(this->address_ >> 8) & 0xff,
|
||||
(uint8_t)(this->address_ >> 0) & 0xff);
|
||||
this->address_str_ =
|
||||
str_snprintf("%02X:%02X:%02X:%02X:%02X:%02X", 17, (uint8_t) (this->address_ >> 40) & 0xff,
|
||||
(uint8_t) (this->address_ >> 32) & 0xff, (uint8_t) (this->address_ >> 24) & 0xff,
|
||||
(uint8_t) (this->address_ >> 16) & 0xff, (uint8_t) (this->address_ >> 8) & 0xff,
|
||||
(uint8_t) (this->address_ >> 0) & 0xff);
|
||||
}
|
||||
}
|
||||
std::string address_str() const { return this->address_str_; }
|
||||
|
||||
@ -148,44 +148,44 @@ bool BLECharacteristic::is_failed() {
|
||||
|
||||
void BLECharacteristic::set_broadcast_property(bool value) {
|
||||
if (value) {
|
||||
this->properties_ = (esp_gatt_char_prop_t)(this->properties_ | ESP_GATT_CHAR_PROP_BIT_BROADCAST);
|
||||
this->properties_ = (esp_gatt_char_prop_t) (this->properties_ | ESP_GATT_CHAR_PROP_BIT_BROADCAST);
|
||||
} else {
|
||||
this->properties_ = (esp_gatt_char_prop_t)(this->properties_ & ~ESP_GATT_CHAR_PROP_BIT_BROADCAST);
|
||||
this->properties_ = (esp_gatt_char_prop_t) (this->properties_ & ~ESP_GATT_CHAR_PROP_BIT_BROADCAST);
|
||||
}
|
||||
}
|
||||
void BLECharacteristic::set_indicate_property(bool value) {
|
||||
if (value) {
|
||||
this->properties_ = (esp_gatt_char_prop_t)(this->properties_ | ESP_GATT_CHAR_PROP_BIT_INDICATE);
|
||||
this->properties_ = (esp_gatt_char_prop_t) (this->properties_ | ESP_GATT_CHAR_PROP_BIT_INDICATE);
|
||||
} else {
|
||||
this->properties_ = (esp_gatt_char_prop_t)(this->properties_ & ~ESP_GATT_CHAR_PROP_BIT_INDICATE);
|
||||
this->properties_ = (esp_gatt_char_prop_t) (this->properties_ & ~ESP_GATT_CHAR_PROP_BIT_INDICATE);
|
||||
}
|
||||
}
|
||||
void BLECharacteristic::set_notify_property(bool value) {
|
||||
if (value) {
|
||||
this->properties_ = (esp_gatt_char_prop_t)(this->properties_ | ESP_GATT_CHAR_PROP_BIT_NOTIFY);
|
||||
this->properties_ = (esp_gatt_char_prop_t) (this->properties_ | ESP_GATT_CHAR_PROP_BIT_NOTIFY);
|
||||
} else {
|
||||
this->properties_ = (esp_gatt_char_prop_t)(this->properties_ & ~ESP_GATT_CHAR_PROP_BIT_NOTIFY);
|
||||
this->properties_ = (esp_gatt_char_prop_t) (this->properties_ & ~ESP_GATT_CHAR_PROP_BIT_NOTIFY);
|
||||
}
|
||||
}
|
||||
void BLECharacteristic::set_read_property(bool value) {
|
||||
if (value) {
|
||||
this->properties_ = (esp_gatt_char_prop_t)(this->properties_ | ESP_GATT_CHAR_PROP_BIT_READ);
|
||||
this->properties_ = (esp_gatt_char_prop_t) (this->properties_ | ESP_GATT_CHAR_PROP_BIT_READ);
|
||||
} else {
|
||||
this->properties_ = (esp_gatt_char_prop_t)(this->properties_ & ~ESP_GATT_CHAR_PROP_BIT_READ);
|
||||
this->properties_ = (esp_gatt_char_prop_t) (this->properties_ & ~ESP_GATT_CHAR_PROP_BIT_READ);
|
||||
}
|
||||
}
|
||||
void BLECharacteristic::set_write_property(bool value) {
|
||||
if (value) {
|
||||
this->properties_ = (esp_gatt_char_prop_t)(this->properties_ | ESP_GATT_CHAR_PROP_BIT_WRITE);
|
||||
this->properties_ = (esp_gatt_char_prop_t) (this->properties_ | ESP_GATT_CHAR_PROP_BIT_WRITE);
|
||||
} else {
|
||||
this->properties_ = (esp_gatt_char_prop_t)(this->properties_ & ~ESP_GATT_CHAR_PROP_BIT_WRITE);
|
||||
this->properties_ = (esp_gatt_char_prop_t) (this->properties_ & ~ESP_GATT_CHAR_PROP_BIT_WRITE);
|
||||
}
|
||||
}
|
||||
void BLECharacteristic::set_write_no_response_property(bool value) {
|
||||
if (value) {
|
||||
this->properties_ = (esp_gatt_char_prop_t)(this->properties_ | ESP_GATT_CHAR_PROP_BIT_WRITE_NR);
|
||||
this->properties_ = (esp_gatt_char_prop_t) (this->properties_ | ESP_GATT_CHAR_PROP_BIT_WRITE_NR);
|
||||
} else {
|
||||
this->properties_ = (esp_gatt_char_prop_t)(this->properties_ & ~ESP_GATT_CHAR_PROP_BIT_WRITE_NR);
|
||||
this->properties_ = (esp_gatt_char_prop_t) (this->properties_ & ~ESP_GATT_CHAR_PROP_BIT_WRITE_NR);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -55,6 +55,22 @@ FRAME_SIZES = {
|
||||
"SXGA": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_1280X1024,
|
||||
"1600X1200": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_1600X1200,
|
||||
"UXGA": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_1600X1200,
|
||||
"1920X1080": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_1920X1080,
|
||||
"FHD": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_1920X1080,
|
||||
"720X1280": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_720X1280,
|
||||
"PHD": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_720X1280,
|
||||
"864X1536": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_864X1536,
|
||||
"P3MP": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_864X1536,
|
||||
"2048X1536": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_2048X1536,
|
||||
"QXGA": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_2048X1536,
|
||||
"2560X1440": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_2560X1440,
|
||||
"QHD": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_2560X1440,
|
||||
"2560X1600": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_2560X1600,
|
||||
"WQXGA": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_2560X1600,
|
||||
"1080X1920": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_1080X1920,
|
||||
"PFHD": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_1080X1920,
|
||||
"2560X1920": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_2560X1920,
|
||||
"QSXGA": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_2560X1920,
|
||||
}
|
||||
ESP32GainControlMode = esp32_camera_ns.enum("ESP32GainControlMode")
|
||||
ENUM_GAIN_CONTROL_MODE = {
|
||||
@ -140,7 +156,7 @@ CONFIG_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(
|
||||
{
|
||||
cv.Required(CONF_PIN): pins.internal_gpio_input_pin_number,
|
||||
cv.Optional(CONF_FREQUENCY, default="20MHz"): cv.All(
|
||||
cv.frequency, cv.one_of(20e6, 10e6)
|
||||
cv.frequency, cv.Range(min=8e6, max=20e6)
|
||||
),
|
||||
}
|
||||
),
|
||||
|
||||
@ -91,6 +91,30 @@ void ESP32Camera::dump_config() {
|
||||
case FRAMESIZE_UXGA:
|
||||
ESP_LOGCONFIG(TAG, " Resolution: 1600x1200 (UXGA)");
|
||||
break;
|
||||
case FRAMESIZE_FHD:
|
||||
ESP_LOGCONFIG(TAG, " Resolution: 1920x1080 (FHD)");
|
||||
break;
|
||||
case FRAMESIZE_P_HD:
|
||||
ESP_LOGCONFIG(TAG, " Resolution: 720x1280 (P_HD)");
|
||||
break;
|
||||
case FRAMESIZE_P_3MP:
|
||||
ESP_LOGCONFIG(TAG, " Resolution: 864x1536 (P_3MP)");
|
||||
break;
|
||||
case FRAMESIZE_QXGA:
|
||||
ESP_LOGCONFIG(TAG, " Resolution: 2048x1536 (QXGA)");
|
||||
break;
|
||||
case FRAMESIZE_QHD:
|
||||
ESP_LOGCONFIG(TAG, " Resolution: 2560x1440 (QHD)");
|
||||
break;
|
||||
case FRAMESIZE_WQXGA:
|
||||
ESP_LOGCONFIG(TAG, " Resolution: 2560x1600 (WQXGA)");
|
||||
break;
|
||||
case FRAMESIZE_P_FHD:
|
||||
ESP_LOGCONFIG(TAG, " Resolution: 1080x1920 (P_FHD)");
|
||||
break;
|
||||
case FRAMESIZE_QSXGA:
|
||||
ESP_LOGCONFIG(TAG, " Resolution: 2560x1920 (QSXGA)");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -178,7 +202,7 @@ void ESP32Camera::loop() {
|
||||
float ESP32Camera::get_setup_priority() const { return setup_priority::DATA; }
|
||||
|
||||
/* ---------------- constructors ---------------- */
|
||||
ESP32Camera::ESP32Camera(const std::string &name) : EntityBase(name) {
|
||||
ESP32Camera::ESP32Camera() {
|
||||
this->config_.pin_pwdn = -1;
|
||||
this->config_.pin_reset = -1;
|
||||
this->config_.pin_xclk = -1;
|
||||
@ -191,7 +215,6 @@ ESP32Camera::ESP32Camera(const std::string &name) : EntityBase(name) {
|
||||
|
||||
global_esp32_camera = this;
|
||||
}
|
||||
ESP32Camera::ESP32Camera() : ESP32Camera("") {}
|
||||
|
||||
/* ---------------- setters ---------------- */
|
||||
/* set pin assignment */
|
||||
@ -257,6 +280,30 @@ void ESP32Camera::set_frame_size(ESP32CameraFrameSize size) {
|
||||
case ESP32_CAMERA_SIZE_1600X1200:
|
||||
this->config_.frame_size = FRAMESIZE_UXGA;
|
||||
break;
|
||||
case ESP32_CAMERA_SIZE_1920X1080:
|
||||
this->config_.frame_size = FRAMESIZE_FHD;
|
||||
break;
|
||||
case ESP32_CAMERA_SIZE_720X1280:
|
||||
this->config_.frame_size = FRAMESIZE_P_HD;
|
||||
break;
|
||||
case ESP32_CAMERA_SIZE_864X1536:
|
||||
this->config_.frame_size = FRAMESIZE_P_3MP;
|
||||
break;
|
||||
case ESP32_CAMERA_SIZE_2048X1536:
|
||||
this->config_.frame_size = FRAMESIZE_QXGA;
|
||||
break;
|
||||
case ESP32_CAMERA_SIZE_2560X1440:
|
||||
this->config_.frame_size = FRAMESIZE_QHD;
|
||||
break;
|
||||
case ESP32_CAMERA_SIZE_2560X1600:
|
||||
this->config_.frame_size = FRAMESIZE_WQXGA;
|
||||
break;
|
||||
case ESP32_CAMERA_SIZE_1080X1920:
|
||||
this->config_.frame_size = FRAMESIZE_P_FHD;
|
||||
break;
|
||||
case ESP32_CAMERA_SIZE_2560X1920:
|
||||
this->config_.frame_size = FRAMESIZE_QSXGA;
|
||||
break;
|
||||
}
|
||||
}
|
||||
void ESP32Camera::set_jpeg_quality(uint8_t quality) { this->config_.jpeg_quality = quality; }
|
||||
|
||||
@ -29,6 +29,14 @@ enum ESP32CameraFrameSize {
|
||||
ESP32_CAMERA_SIZE_1024X768, // XGA
|
||||
ESP32_CAMERA_SIZE_1280X1024, // SXGA
|
||||
ESP32_CAMERA_SIZE_1600X1200, // UXGA
|
||||
ESP32_CAMERA_SIZE_1920X1080, // FHD
|
||||
ESP32_CAMERA_SIZE_720X1280, // PHD
|
||||
ESP32_CAMERA_SIZE_864X1536, // P3MP
|
||||
ESP32_CAMERA_SIZE_2048X1536, // QXGA
|
||||
ESP32_CAMERA_SIZE_2560X1440, // QHD
|
||||
ESP32_CAMERA_SIZE_2560X1600, // WQXGA
|
||||
ESP32_CAMERA_SIZE_1080X1920, // PFHD
|
||||
ESP32_CAMERA_SIZE_2560X1920, // QSXGA
|
||||
};
|
||||
|
||||
enum ESP32AgcGainCeiling {
|
||||
@ -95,7 +103,6 @@ class CameraImageReader {
|
||||
/* ---------------- ESP32Camera class ---------------- */
|
||||
class ESP32Camera : public Component, public EntityBase {
|
||||
public:
|
||||
ESP32Camera(const std::string &name);
|
||||
ESP32Camera();
|
||||
|
||||
/* setters */
|
||||
|
||||
@ -22,20 +22,12 @@ ESP32ImprovComponent = esp32_improv_ns.class_(
|
||||
)
|
||||
|
||||
|
||||
def validate_none_(value):
|
||||
if value in ("none", "None"):
|
||||
return None
|
||||
if cv.boolean(value) is False:
|
||||
return None
|
||||
raise cv.Invalid("Must be none")
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(ESP32ImprovComponent),
|
||||
cv.GenerateID(CONF_BLE_SERVER_ID): cv.use_id(esp32_ble_server.BLEServer),
|
||||
cv.Required(CONF_AUTHORIZER): cv.Any(
|
||||
validate_none_, cv.use_id(binary_sensor.BinarySensor)
|
||||
cv.none, cv.use_id(binary_sensor.BinarySensor)
|
||||
),
|
||||
cv.Optional(CONF_STATUS_INDICATOR): cv.use_id(output.BinaryOutput),
|
||||
cv.Optional(
|
||||
|
||||
@ -34,6 +34,7 @@ ETHERNET_TYPES = {
|
||||
"DP83848": EthernetType.ETHERNET_TYPE_DP83848,
|
||||
"IP101": EthernetType.ETHERNET_TYPE_IP101,
|
||||
"JL1101": EthernetType.ETHERNET_TYPE_JL1101,
|
||||
"KSZ8081": EthernetType.ETHERNET_TYPE_KSZ8081,
|
||||
}
|
||||
|
||||
emac_rmii_clock_mode_t = cg.global_ns.enum("emac_rmii_clock_mode_t")
|
||||
|
||||
@ -74,6 +74,10 @@ void EthernetComponent::setup() {
|
||||
phy = esp_eth_phy_new_jl1101(&phy_config);
|
||||
break;
|
||||
}
|
||||
case ETHERNET_TYPE_KSZ8081: {
|
||||
phy = esp_eth_phy_new_ksz8081(&phy_config);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
this->mark_failed();
|
||||
return;
|
||||
@ -140,7 +144,7 @@ void EthernetComponent::loop() {
|
||||
}
|
||||
|
||||
void EthernetComponent::dump_config() {
|
||||
std::string eth_type;
|
||||
const char *eth_type;
|
||||
switch (this->type_) {
|
||||
case ETHERNET_TYPE_LAN8720:
|
||||
eth_type = "LAN8720";
|
||||
@ -158,6 +162,14 @@ void EthernetComponent::dump_config() {
|
||||
eth_type = "IP101";
|
||||
break;
|
||||
|
||||
case ETHERNET_TYPE_JL1101:
|
||||
eth_type = "JL1101";
|
||||
break;
|
||||
|
||||
case ETHERNET_TYPE_KSZ8081:
|
||||
eth_type = "KSZ8081";
|
||||
break;
|
||||
|
||||
default:
|
||||
eth_type = "Unknown";
|
||||
break;
|
||||
@ -170,7 +182,8 @@ void EthernetComponent::dump_config() {
|
||||
}
|
||||
ESP_LOGCONFIG(TAG, " MDC Pin: %u", this->mdc_pin_);
|
||||
ESP_LOGCONFIG(TAG, " MDIO Pin: %u", this->mdio_pin_);
|
||||
ESP_LOGCONFIG(TAG, " Type: %s", eth_type.c_str());
|
||||
ESP_LOGCONFIG(TAG, " Type: %s", eth_type);
|
||||
ESP_LOGCONFIG(TAG, " PHY addr: %u", this->phy_addr_);
|
||||
}
|
||||
|
||||
float EthernetComponent::get_setup_priority() const { return setup_priority::WIFI; }
|
||||
@ -255,14 +268,22 @@ void EthernetComponent::start_connect_() {
|
||||
if (this->manual_ip_.has_value()) {
|
||||
if (uint32_t(this->manual_ip_->dns1) != 0) {
|
||||
ip_addr_t d;
|
||||
#if LWIP_IPV6
|
||||
d.type = IPADDR_TYPE_V4;
|
||||
d.u_addr.ip4.addr = static_cast<uint32_t>(this->manual_ip_->dns1);
|
||||
#else
|
||||
d.addr = static_cast<uint32_t>(this->manual_ip_->dns1);
|
||||
#endif
|
||||
dns_setserver(0, &d);
|
||||
}
|
||||
if (uint32_t(this->manual_ip_->dns1) != 0) {
|
||||
ip_addr_t d;
|
||||
#if LWIP_IPV6
|
||||
d.type = IPADDR_TYPE_V4;
|
||||
d.u_addr.ip4.addr = static_cast<uint32_t>(this->manual_ip_->dns2);
|
||||
#else
|
||||
d.addr = static_cast<uint32_t>(this->manual_ip_->dns2);
|
||||
#endif
|
||||
dns_setserver(1, &d);
|
||||
}
|
||||
} else {
|
||||
@ -289,8 +310,13 @@ void EthernetComponent::dump_connect_params_() {
|
||||
const ip_addr_t *dns_ip1 = dns_getserver(0);
|
||||
const ip_addr_t *dns_ip2 = dns_getserver(1);
|
||||
|
||||
#if LWIP_IPV6
|
||||
ESP_LOGCONFIG(TAG, " DNS1: %s", network::IPAddress(dns_ip1->u_addr.ip4.addr).str().c_str());
|
||||
ESP_LOGCONFIG(TAG, " DNS2: %s", network::IPAddress(dns_ip2->u_addr.ip4.addr).str().c_str());
|
||||
#else
|
||||
ESP_LOGCONFIG(TAG, " DNS1: %s", network::IPAddress(dns_ip1->addr).str().c_str());
|
||||
ESP_LOGCONFIG(TAG, " DNS2: %s", network::IPAddress(dns_ip2->addr).str().c_str());
|
||||
#endif
|
||||
|
||||
esp_err_t err;
|
||||
|
||||
|
||||
@ -14,11 +14,13 @@ namespace esphome {
|
||||
namespace ethernet {
|
||||
|
||||
enum EthernetType {
|
||||
ETHERNET_TYPE_LAN8720 = 0,
|
||||
ETHERNET_TYPE_UNKNOWN = 0,
|
||||
ETHERNET_TYPE_LAN8720,
|
||||
ETHERNET_TYPE_RTL8201,
|
||||
ETHERNET_TYPE_DP83848,
|
||||
ETHERNET_TYPE_IP101,
|
||||
ETHERNET_TYPE_JL1101,
|
||||
ETHERNET_TYPE_KSZ8081,
|
||||
};
|
||||
|
||||
struct ManualIP {
|
||||
@ -69,7 +71,7 @@ class EthernetComponent : public Component {
|
||||
int power_pin_{-1};
|
||||
uint8_t mdc_pin_{23};
|
||||
uint8_t mdio_pin_{18};
|
||||
EthernetType type_{ETHERNET_TYPE_LAN8720};
|
||||
EthernetType type_{ETHERNET_TYPE_UNKNOWN};
|
||||
emac_rmii_clock_mode_t clk_mode_{EMAC_CLK_EXT_IN};
|
||||
emac_rmii_clock_gpio_t clk_gpio_{EMAC_CLK_IN_GPIO};
|
||||
optional<ManualIP> manual_ip_{};
|
||||
|
||||
@ -12,14 +12,14 @@ static const char *const TAG = "ezo.sensor";
|
||||
|
||||
enum EzoCommandType : uint8_t {
|
||||
EZO_READ = 0,
|
||||
EZO_LED = 1,
|
||||
EZO_DEVICE_INFORMATION = 2,
|
||||
EZO_SLOPE = 3,
|
||||
EZO_LED,
|
||||
EZO_DEVICE_INFORMATION,
|
||||
EZO_SLOPE,
|
||||
EZO_CALIBRATION,
|
||||
EZO_SLEEP = 4,
|
||||
EZO_I2C = 5,
|
||||
EZO_T = 6,
|
||||
EZO_CUSTOM = 7
|
||||
EZO_SLEEP,
|
||||
EZO_I2C,
|
||||
EZO_T,
|
||||
EZO_CUSTOM
|
||||
};
|
||||
|
||||
enum EzoCalibrationType : uint8_t { EZO_CAL_LOW = 0, EZO_CAL_MID = 1, EZO_CAL_HIGH = 2 };
|
||||
|
||||
@ -63,7 +63,7 @@ FanIsOffCondition = fan_ns.class_("FanIsOffCondition", automation.Condition.temp
|
||||
FAN_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(Fan),
|
||||
cv.Optional(CONF_RESTORE_MODE, default="RESTORE_DEFAULT_OFF"): cv.enum(
|
||||
cv.Optional(CONF_RESTORE_MODE, default="ALWAYS_OFF"): cv.enum(
|
||||
RESTORE_MODES, upper=True, space="_"
|
||||
),
|
||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTFanComponent),
|
||||
|
||||
@ -80,9 +80,6 @@ void FanRestoreState::apply(Fan &fan) {
|
||||
fan.publish_state();
|
||||
}
|
||||
|
||||
Fan::Fan() : EntityBase("") {}
|
||||
Fan::Fan(const std::string &name) : EntityBase(name) {}
|
||||
|
||||
FanCall Fan::turn_on() { return this->make_call().set_state(true); }
|
||||
FanCall Fan::turn_off() { return this->make_call().set_state(false); }
|
||||
FanCall Fan::toggle() { return this->make_call().set_state(!this->state); }
|
||||
|
||||
@ -99,10 +99,6 @@ struct FanRestoreState {
|
||||
|
||||
class Fan : public EntityBase {
|
||||
public:
|
||||
Fan();
|
||||
/// Construct the fan with name.
|
||||
explicit Fan(const std::string &name);
|
||||
|
||||
/// The current on/off state of the fan.
|
||||
bool state{false};
|
||||
/// The current oscillation state of the fan.
|
||||
|
||||
@ -15,7 +15,6 @@ enum ESPDEPRECATED("LegacyFanDirection members are deprecated, use FanDirection
|
||||
class ESPDEPRECATED("FanState is deprecated, use Fan instead.", "2022.2") FanState : public Fan, public Component {
|
||||
public:
|
||||
FanState() = default;
|
||||
explicit FanState(const std::string &name) : Fan(name) {}
|
||||
|
||||
/// Get the traits of this fan.
|
||||
FanTraits get_traits() override { return this->traits_; }
|
||||
|
||||
@ -95,7 +95,7 @@ void FingerprintGrowComponent::scan_and_match_() {
|
||||
}
|
||||
if (this->scan_image_(1) == OK) {
|
||||
this->waiting_removal_ = true;
|
||||
this->data_ = {SEARCH, 0x01, 0x00, 0x00, (uint8_t)(this->capacity_ >> 8), (uint8_t)(this->capacity_ & 0xFF)};
|
||||
this->data_ = {SEARCH, 0x01, 0x00, 0x00, (uint8_t) (this->capacity_ >> 8), (uint8_t) (this->capacity_ & 0xFF)};
|
||||
switch (this->send_command_()) {
|
||||
case OK: {
|
||||
ESP_LOGD(TAG, "Fingerprint matched");
|
||||
@ -171,7 +171,7 @@ uint8_t FingerprintGrowComponent::save_fingerprint_() {
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Storing model");
|
||||
this->data_ = {STORE, 0x01, (uint8_t)(this->enrollment_slot_ >> 8), (uint8_t)(this->enrollment_slot_ & 0xFF)};
|
||||
this->data_ = {STORE, 0x01, (uint8_t) (this->enrollment_slot_ >> 8), (uint8_t) (this->enrollment_slot_ & 0xFF)};
|
||||
switch (this->send_command_()) {
|
||||
case OK:
|
||||
ESP_LOGI(TAG, "Stored model");
|
||||
@ -188,8 +188,8 @@ uint8_t FingerprintGrowComponent::save_fingerprint_() {
|
||||
|
||||
bool FingerprintGrowComponent::check_password_() {
|
||||
ESP_LOGD(TAG, "Checking password");
|
||||
this->data_ = {VERIFY_PASSWORD, (uint8_t)(this->password_ >> 24), (uint8_t)(this->password_ >> 16),
|
||||
(uint8_t)(this->password_ >> 8), (uint8_t)(this->password_ & 0xFF)};
|
||||
this->data_ = {VERIFY_PASSWORD, (uint8_t) (this->password_ >> 24), (uint8_t) (this->password_ >> 16),
|
||||
(uint8_t) (this->password_ >> 8), (uint8_t) (this->password_ & 0xFF)};
|
||||
switch (this->send_command_()) {
|
||||
case OK:
|
||||
ESP_LOGD(TAG, "Password verified");
|
||||
@ -203,8 +203,8 @@ bool FingerprintGrowComponent::check_password_() {
|
||||
|
||||
bool FingerprintGrowComponent::set_password_() {
|
||||
ESP_LOGI(TAG, "Setting new password: %d", this->new_password_);
|
||||
this->data_ = {SET_PASSWORD, (uint8_t)(this->new_password_ >> 24), (uint8_t)(this->new_password_ >> 16),
|
||||
(uint8_t)(this->new_password_ >> 8), (uint8_t)(this->new_password_ & 0xFF)};
|
||||
this->data_ = {SET_PASSWORD, (uint8_t) (this->new_password_ >> 24), (uint8_t) (this->new_password_ >> 16),
|
||||
(uint8_t) (this->new_password_ >> 8), (uint8_t) (this->new_password_ & 0xFF)};
|
||||
if (this->send_command_() == OK) {
|
||||
ESP_LOGI(TAG, "New password successfully set");
|
||||
ESP_LOGI(TAG, "Define the new password in your configuration and reflash now");
|
||||
@ -250,7 +250,7 @@ void FingerprintGrowComponent::get_fingerprint_count_() {
|
||||
|
||||
void FingerprintGrowComponent::delete_fingerprint(uint16_t finger_id) {
|
||||
ESP_LOGI(TAG, "Deleting fingerprint in slot %d", finger_id);
|
||||
this->data_ = {DELETE, (uint8_t)(finger_id >> 8), (uint8_t)(finger_id & 0xFF), 0x00, 0x01};
|
||||
this->data_ = {DELETE, (uint8_t) (finger_id >> 8), (uint8_t) (finger_id & 0xFF), 0x00, 0x01};
|
||||
switch (this->send_command_()) {
|
||||
case OK:
|
||||
ESP_LOGI(TAG, "Deleted fingerprint");
|
||||
@ -320,8 +320,8 @@ void FingerprintGrowComponent::aura_led_control(uint8_t state, uint8_t speed, ui
|
||||
}
|
||||
|
||||
uint8_t FingerprintGrowComponent::send_command_() {
|
||||
this->write((uint8_t)(START_CODE >> 8));
|
||||
this->write((uint8_t)(START_CODE & 0xFF));
|
||||
this->write((uint8_t) (START_CODE >> 8));
|
||||
this->write((uint8_t) (START_CODE & 0xFF));
|
||||
this->write(this->address_[0]);
|
||||
this->write(this->address_[1]);
|
||||
this->write(this->address_[2]);
|
||||
@ -329,8 +329,8 @@ uint8_t FingerprintGrowComponent::send_command_() {
|
||||
this->write(COMMAND);
|
||||
|
||||
uint16_t wire_length = this->data_.size() + 2;
|
||||
this->write((uint8_t)(wire_length >> 8));
|
||||
this->write((uint8_t)(wire_length & 0xFF));
|
||||
this->write((uint8_t) (wire_length >> 8));
|
||||
this->write((uint8_t) (wire_length & 0xFF));
|
||||
|
||||
uint16_t sum = ((wire_length) >> 8) + ((wire_length) &0xFF) + COMMAND;
|
||||
for (auto data : this->data_) {
|
||||
@ -338,8 +338,8 @@ uint8_t FingerprintGrowComponent::send_command_() {
|
||||
sum += data;
|
||||
}
|
||||
|
||||
this->write((uint8_t)(sum >> 8));
|
||||
this->write((uint8_t)(sum & 0xFF));
|
||||
this->write((uint8_t) (sum >> 8));
|
||||
this->write((uint8_t) (sum & 0xFF));
|
||||
|
||||
this->data_.clear();
|
||||
|
||||
@ -354,11 +354,11 @@ uint8_t FingerprintGrowComponent::send_command_() {
|
||||
byte = this->read();
|
||||
switch (idx) {
|
||||
case 0:
|
||||
if (byte != (uint8_t)(START_CODE >> 8))
|
||||
if (byte != (uint8_t) (START_CODE >> 8))
|
||||
continue;
|
||||
break;
|
||||
case 1:
|
||||
if (byte != (uint8_t)(START_CODE & 0xFF)) {
|
||||
if (byte != (uint8_t) (START_CODE & 0xFF)) {
|
||||
idx = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -91,10 +91,10 @@ class FingerprintGrowComponent : public PollingComponent, public uart::UARTDevic
|
||||
void dump_config() override;
|
||||
|
||||
void set_address(uint32_t address) {
|
||||
this->address_[0] = (uint8_t)(address >> 24);
|
||||
this->address_[1] = (uint8_t)(address >> 16);
|
||||
this->address_[2] = (uint8_t)(address >> 8);
|
||||
this->address_[3] = (uint8_t)(address & 0xFF);
|
||||
this->address_[0] = (uint8_t) (address >> 24);
|
||||
this->address_[1] = (uint8_t) (address >> 16);
|
||||
this->address_[2] = (uint8_t) (address >> 8);
|
||||
this->address_[3] = (uint8_t) (address & 0xFF);
|
||||
}
|
||||
void set_sensing_pin(GPIOPin *sensing_pin) { this->sensing_pin_ = sensing_pin; }
|
||||
void set_password(uint32_t password) { this->password_ = password; }
|
||||
|
||||
@ -122,11 +122,18 @@ void Graph::draw(DisplayBuffer *buff, uint16_t x_offset, uint16_t y_offset, Colo
|
||||
}
|
||||
|
||||
// Adjust limits to nice y_per_div boundaries
|
||||
int yn = int(ymin / y_per_div);
|
||||
int ym = int(ymax / y_per_div) + int(1 * (fmodf(ymax, y_per_div) != 0));
|
||||
ymin = yn * y_per_div;
|
||||
ymax = ym * y_per_div;
|
||||
yrange = ymax - ymin;
|
||||
int yn = 0;
|
||||
int ym = 1;
|
||||
if (!std::isnan(ymin) && !std::isnan(ymax)) {
|
||||
yn = (int) floorf(ymin / y_per_div);
|
||||
ym = (int) ceilf(ymax / y_per_div);
|
||||
if (yn == ym) {
|
||||
ym++;
|
||||
}
|
||||
ymin = yn * y_per_div;
|
||||
ymax = ym * y_per_div;
|
||||
yrange = ymax - ymin;
|
||||
}
|
||||
|
||||
/// Draw grid
|
||||
if (!std::isnan(this->gridspacing_y_)) {
|
||||
|
||||
@ -12,7 +12,7 @@ void set_bits(uint8_t *const dst, const uint8_t offset, const uint8_t nbits, con
|
||||
uint8_t mask = UINT8_MAX >> (8 - ((nbits > 8) ? 8 : nbits));
|
||||
// Calculate the mask & clear the space for the data.
|
||||
// Clear the destination bits.
|
||||
*dst &= ~(uint8_t)(mask << offset);
|
||||
*dst &= ~(uint8_t) (mask << offset);
|
||||
// Merge in the data.
|
||||
*dst |= ((data & mask) << offset);
|
||||
}
|
||||
|
||||
@ -12,7 +12,7 @@ void set_bits(uint8_t *const dst, const uint8_t offset, const uint8_t nbits, con
|
||||
uint8_t mask = UINT8_MAX >> (8 - ((nbits > 8) ? 8 : nbits));
|
||||
// Calculate the mask & clear the space for the data.
|
||||
// Clear the destination bits.
|
||||
*dst &= ~(uint8_t)(mask << offset);
|
||||
*dst &= ~(uint8_t) (mask << offset);
|
||||
// Merge in the data.
|
||||
*dst |= ((data & mask) << offset);
|
||||
}
|
||||
|
||||
@ -35,9 +35,9 @@ uint8_t HONEYWELLABPSensor::readsensor_() {
|
||||
// if device is normal and there is new data, bitmask and save the raw data
|
||||
if (status_ == 0) {
|
||||
// 14 - bit pressure is the last 6 bits of byte 0 (high bits) & all of byte 1 (lowest 8 bits)
|
||||
pressure_count_ = ((uint16_t)(buf_[0]) << 8 & 0x3F00) | ((uint16_t)(buf_[1]) & 0xFF);
|
||||
pressure_count_ = ((uint16_t) (buf_[0]) << 8 & 0x3F00) | ((uint16_t) (buf_[1]) & 0xFF);
|
||||
// 11 - bit temperature is all of byte 2 (lowest 8 bits) and the first three bits of byte 3
|
||||
temperature_count_ = (((uint16_t)(buf_[2]) << 3) & 0x7F8) | (((uint16_t)(buf_[3]) >> 5) & 0x7);
|
||||
temperature_count_ = (((uint16_t) (buf_[2]) << 3) & 0x7F8) | (((uint16_t) (buf_[3]) >> 5) & 0x7);
|
||||
ESP_LOGV(TAG, "Sensor pressure_count_ %d", pressure_count_);
|
||||
ESP_LOGV(TAG, "Sensor temperature_count_ %d", temperature_count_);
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include <cstring>
|
||||
#include <cinttypes>
|
||||
|
||||
namespace esphome {
|
||||
namespace i2c {
|
||||
@ -47,7 +48,7 @@ void IDFI2CBus::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "I2C Bus:");
|
||||
ESP_LOGCONFIG(TAG, " SDA Pin: GPIO%u", this->sda_pin_);
|
||||
ESP_LOGCONFIG(TAG, " SCL Pin: GPIO%u", this->scl_pin_);
|
||||
ESP_LOGCONFIG(TAG, " Frequency: %u Hz", this->frequency_);
|
||||
ESP_LOGCONFIG(TAG, " Frequency: %" PRIu32 " Hz", this->frequency_);
|
||||
switch (this->recovery_result_) {
|
||||
case RECOVERY_COMPLETED:
|
||||
ESP_LOGCONFIG(TAG, " Recovery: bus successfully recovered");
|
||||
|
||||
@ -0,0 +1,70 @@
|
||||
import esphome.config_validation as cv
|
||||
import esphome.final_validate as fv
|
||||
import esphome.codegen as cg
|
||||
|
||||
from esphome import pins
|
||||
from esphome.const import CONF_ID
|
||||
from esphome.components.esp32 import get_esp32_variant
|
||||
from esphome.components.esp32.const import (
|
||||
VARIANT_ESP32,
|
||||
VARIANT_ESP32S2,
|
||||
VARIANT_ESP32S3,
|
||||
VARIANT_ESP32C3,
|
||||
)
|
||||
|
||||
CODEOWNERS = ["@jesserockz"]
|
||||
DEPENDENCIES = ["esp32"]
|
||||
MULTI_CONF = True
|
||||
|
||||
CONF_I2S_DOUT_PIN = "i2s_dout_pin"
|
||||
CONF_I2S_DIN_PIN = "i2s_din_pin"
|
||||
CONF_I2S_BCLK_PIN = "i2s_bclk_pin"
|
||||
CONF_I2S_LRCLK_PIN = "i2s_lrclk_pin"
|
||||
|
||||
CONF_I2S_AUDIO = "i2s_audio"
|
||||
CONF_I2S_AUDIO_ID = "i2s_audio_id"
|
||||
|
||||
i2s_audio_ns = cg.esphome_ns.namespace("i2s_audio")
|
||||
I2SAudioComponent = i2s_audio_ns.class_("I2SAudioComponent", cg.Component)
|
||||
I2SAudioIn = i2s_audio_ns.class_("I2SAudioIn", cg.Parented.template(I2SAudioComponent))
|
||||
I2SAudioOut = i2s_audio_ns.class_(
|
||||
"I2SAudioOut", cg.Parented.template(I2SAudioComponent)
|
||||
)
|
||||
|
||||
# https://github.com/espressif/esp-idf/blob/master/components/soc/{variant}/include/soc/soc_caps.h
|
||||
I2S_PORTS = {
|
||||
VARIANT_ESP32: 2,
|
||||
VARIANT_ESP32S2: 1,
|
||||
VARIANT_ESP32S3: 2,
|
||||
VARIANT_ESP32C3: 1,
|
||||
}
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(I2SAudioComponent),
|
||||
cv.Required(CONF_I2S_BCLK_PIN): pins.internal_gpio_output_pin_number,
|
||||
cv.Required(CONF_I2S_LRCLK_PIN): pins.internal_gpio_output_pin_number,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def _final_validate(_):
|
||||
i2s_audio_configs = fv.full_config.get()[CONF_I2S_AUDIO]
|
||||
variant = get_esp32_variant()
|
||||
if variant not in I2S_PORTS:
|
||||
raise cv.Invalid(f"Unsupported variant {variant}")
|
||||
if len(i2s_audio_configs) > I2S_PORTS[variant]:
|
||||
raise cv.Invalid(
|
||||
f"Only {I2S_PORTS[variant]} I2S audio ports are supported on {variant}"
|
||||
)
|
||||
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = _final_validate
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
|
||||
cg.add(var.set_bclk_pin(config[CONF_I2S_BCLK_PIN]))
|
||||
cg.add(var.set_lrclk_pin(config[CONF_I2S_LRCLK_PIN]))
|
||||
30
esphome/components/i2s_audio/i2s_audio.cpp
Normal file
30
esphome/components/i2s_audio/i2s_audio.cpp
Normal file
@ -0,0 +1,30 @@
|
||||
#include "i2s_audio.h"
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace i2s_audio {
|
||||
|
||||
static const char *const TAG = "i2s_audio";
|
||||
|
||||
void I2SAudioComponent::setup() {
|
||||
static i2s_port_t next_port_num = I2S_NUM_0;
|
||||
|
||||
if (next_port_num >= I2S_NUM_MAX) {
|
||||
ESP_LOGE(TAG, "Too many I2S Audio components!");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
this->port_ = next_port_num;
|
||||
next_port_num = (i2s_port_t) (next_port_num + 1);
|
||||
|
||||
ESP_LOGCONFIG(TAG, "Setting up I2S Audio...");
|
||||
}
|
||||
|
||||
} // namespace i2s_audio
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_ESP32
|
||||
64
esphome/components/i2s_audio/i2s_audio.h
Normal file
64
esphome/components/i2s_audio/i2s_audio.h
Normal file
@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
#include <driver/i2s.h>
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace i2s_audio {
|
||||
|
||||
class I2SAudioComponent;
|
||||
|
||||
class I2SAudioIn : public Parented<I2SAudioComponent> {};
|
||||
|
||||
class I2SAudioOut : public Parented<I2SAudioComponent> {};
|
||||
|
||||
class I2SAudioComponent : public Component {
|
||||
public:
|
||||
void setup() override;
|
||||
|
||||
void register_audio_in(I2SAudioIn *in) {
|
||||
this->audio_in_ = in;
|
||||
in->set_parent(this);
|
||||
}
|
||||
void register_audio_out(I2SAudioOut *out) {
|
||||
this->audio_out_ = out;
|
||||
out->set_parent(this);
|
||||
}
|
||||
|
||||
i2s_pin_config_t get_pin_config() const {
|
||||
return {
|
||||
.mck_io_num = I2S_PIN_NO_CHANGE,
|
||||
.bck_io_num = this->bclk_pin_,
|
||||
.ws_io_num = this->lrclk_pin_,
|
||||
.data_out_num = I2S_PIN_NO_CHANGE,
|
||||
.data_in_num = I2S_PIN_NO_CHANGE,
|
||||
};
|
||||
}
|
||||
|
||||
void set_bclk_pin(uint8_t pin) { this->bclk_pin_ = pin; }
|
||||
void set_lrclk_pin(uint8_t pin) { this->lrclk_pin_ = pin; }
|
||||
|
||||
void lock() { this->lock_.lock(); }
|
||||
bool try_lock() { return this->lock_.try_lock(); }
|
||||
void unlock() { this->lock_.unlock(); }
|
||||
|
||||
i2s_port_t get_port() const { return this->port_; }
|
||||
|
||||
protected:
|
||||
Mutex lock_;
|
||||
|
||||
I2SAudioIn *audio_in_{nullptr};
|
||||
I2SAudioOut *audio_out_{nullptr};
|
||||
|
||||
uint8_t bclk_pin_;
|
||||
uint8_t lrclk_pin_;
|
||||
i2s_port_t port_{};
|
||||
};
|
||||
|
||||
} // namespace i2s_audio
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_ESP32
|
||||
@ -5,22 +5,25 @@ import esphome.config_validation as cv
|
||||
from esphome import pins
|
||||
|
||||
from esphome.const import CONF_ID, CONF_MODE
|
||||
from esphome.core import CORE
|
||||
|
||||
from .. import (
|
||||
i2s_audio_ns,
|
||||
I2SAudioComponent,
|
||||
I2SAudioOut,
|
||||
CONF_I2S_AUDIO_ID,
|
||||
CONF_I2S_DOUT_PIN,
|
||||
)
|
||||
|
||||
CODEOWNERS = ["@jesserockz"]
|
||||
DEPENDENCIES = ["esp32"]
|
||||
|
||||
i2s_audio_ns = cg.esphome_ns.namespace("i2s_audio")
|
||||
DEPENDENCIES = ["i2s_audio"]
|
||||
|
||||
I2SAudioMediaPlayer = i2s_audio_ns.class_(
|
||||
"I2SAudioMediaPlayer", cg.Component, media_player.MediaPlayer
|
||||
"I2SAudioMediaPlayer", cg.Component, media_player.MediaPlayer, I2SAudioOut
|
||||
)
|
||||
|
||||
i2s_dac_mode_t = cg.global_ns.enum("i2s_dac_mode_t")
|
||||
|
||||
CONF_I2S_DOUT_PIN = "i2s_dout_pin"
|
||||
CONF_I2S_BCLK_PIN = "i2s_bclk_pin"
|
||||
CONF_I2S_LRCLK_PIN = "i2s_lrclk_pin"
|
||||
|
||||
CONF_MUTE_PIN = "mute_pin"
|
||||
CONF_AUDIO_ID = "audio_id"
|
||||
CONF_DAC_TYPE = "dac_type"
|
||||
@ -48,34 +51,26 @@ def validate_esp32_variant(config):
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.typed_schema(
|
||||
{
|
||||
"internal": cv.Schema(
|
||||
"internal": media_player.MEDIA_PLAYER_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(I2SAudioMediaPlayer),
|
||||
cv.GenerateID(CONF_I2S_AUDIO_ID): cv.use_id(I2SAudioComponent),
|
||||
cv.Required(CONF_MODE): cv.enum(INTERNAL_DAC_OPTIONS, lower=True),
|
||||
}
|
||||
)
|
||||
.extend(media_player.MEDIA_PLAYER_SCHEMA)
|
||||
.extend(cv.COMPONENT_SCHEMA),
|
||||
"external": cv.Schema(
|
||||
).extend(cv.COMPONENT_SCHEMA),
|
||||
"external": media_player.MEDIA_PLAYER_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(I2SAudioMediaPlayer),
|
||||
cv.GenerateID(CONF_I2S_AUDIO_ID): cv.use_id(I2SAudioComponent),
|
||||
cv.Required(
|
||||
CONF_I2S_DOUT_PIN
|
||||
): pins.internal_gpio_output_pin_number,
|
||||
cv.Required(
|
||||
CONF_I2S_BCLK_PIN
|
||||
): pins.internal_gpio_output_pin_number,
|
||||
cv.Required(
|
||||
CONF_I2S_LRCLK_PIN
|
||||
): pins.internal_gpio_output_pin_number,
|
||||
cv.Optional(CONF_MUTE_PIN): pins.gpio_output_pin_schema,
|
||||
cv.Optional(CONF_MODE, default="mono"): cv.one_of(
|
||||
*EXTERNAL_DAC_OPTIONS, lower=True
|
||||
),
|
||||
}
|
||||
)
|
||||
.extend(media_player.MEDIA_PLAYER_SCHEMA)
|
||||
.extend(cv.COMPONENT_SCHEMA),
|
||||
).extend(cv.COMPONENT_SCHEMA),
|
||||
},
|
||||
key=CONF_DAC_TYPE,
|
||||
),
|
||||
@ -89,19 +84,19 @@ async def to_code(config):
|
||||
await cg.register_component(var, config)
|
||||
await media_player.register_media_player(var, config)
|
||||
|
||||
parent = await cg.get_variable(config[CONF_I2S_AUDIO_ID])
|
||||
cg.add(parent.register_audio_out(var))
|
||||
|
||||
if config[CONF_DAC_TYPE] == "internal":
|
||||
cg.add(var.set_internal_dac_mode(config[CONF_MODE]))
|
||||
else:
|
||||
cg.add(var.set_dout_pin(config[CONF_I2S_DOUT_PIN]))
|
||||
cg.add(var.set_bclk_pin(config[CONF_I2S_BCLK_PIN]))
|
||||
cg.add(var.set_lrclk_pin(config[CONF_I2S_LRCLK_PIN]))
|
||||
if CONF_MUTE_PIN in config:
|
||||
pin = await cg.gpio_pin_expression(config[CONF_MUTE_PIN])
|
||||
cg.add(var.set_mute_pin(pin))
|
||||
cg.add(var.set_external_dac_channels(2 if config[CONF_MODE] == "stereo" else 1))
|
||||
|
||||
if CORE.is_esp32:
|
||||
cg.add_library("WiFiClientSecure", None)
|
||||
cg.add_library("HTTPClient", None)
|
||||
cg.add_library("esphome/ESP32-audioI2S", "2.0.6")
|
||||
cg.add_build_flag("-DAUDIO_NO_SD_FS")
|
||||
cg.add_library("WiFiClientSecure", None)
|
||||
cg.add_library("HTTPClient", None)
|
||||
cg.add_library("esphome/ESP32-audioI2S", "2.0.6")
|
||||
cg.add_build_flag("-DAUDIO_NO_SD_FS")
|
||||
@ -11,11 +11,19 @@ static const char *const TAG = "audio";
|
||||
|
||||
void I2SAudioMediaPlayer::control(const media_player::MediaPlayerCall &call) {
|
||||
if (call.get_media_url().has_value()) {
|
||||
if (this->audio_->isRunning())
|
||||
this->audio_->stopSong();
|
||||
this->high_freq_.start();
|
||||
this->audio_->connecttohost(call.get_media_url().value().c_str());
|
||||
this->state = media_player::MEDIA_PLAYER_STATE_PLAYING;
|
||||
this->current_url_ = call.get_media_url();
|
||||
|
||||
if (this->state == media_player::MEDIA_PLAYER_STATE_PLAYING && this->audio_ != nullptr) {
|
||||
if (this->audio_->isRunning()) {
|
||||
this->audio_->stopSong();
|
||||
}
|
||||
this->audio_->connecttohost(this->current_url_.value().c_str());
|
||||
} else {
|
||||
this->start();
|
||||
}
|
||||
}
|
||||
if (this->i2s_state_ != I2S_STATE_RUNNING) {
|
||||
return;
|
||||
}
|
||||
if (call.get_volume().has_value()) {
|
||||
this->volume = call.get_volume().value();
|
||||
@ -35,7 +43,7 @@ void I2SAudioMediaPlayer::control(const media_player::MediaPlayerCall &call) {
|
||||
this->state = media_player::MEDIA_PLAYER_STATE_PAUSED;
|
||||
break;
|
||||
case media_player::MEDIA_PLAYER_COMMAND_STOP:
|
||||
this->stop_();
|
||||
this->stop();
|
||||
break;
|
||||
case media_player::MEDIA_PLAYER_COMMAND_MUTE:
|
||||
this->mute_();
|
||||
@ -94,22 +102,51 @@ void I2SAudioMediaPlayer::set_volume_(float volume, bool publish) {
|
||||
this->volume = volume;
|
||||
}
|
||||
|
||||
void I2SAudioMediaPlayer::stop_() {
|
||||
if (this->audio_->isRunning())
|
||||
this->audio_->stopSong();
|
||||
this->high_freq_.stop();
|
||||
void I2SAudioMediaPlayer::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up Audio...");
|
||||
this->state = media_player::MEDIA_PLAYER_STATE_IDLE;
|
||||
}
|
||||
|
||||
void I2SAudioMediaPlayer::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up Audio...");
|
||||
void I2SAudioMediaPlayer::loop() {
|
||||
switch (this->i2s_state_) {
|
||||
case I2S_STATE_STARTING:
|
||||
this->start_();
|
||||
break;
|
||||
case I2S_STATE_RUNNING:
|
||||
this->play_();
|
||||
break;
|
||||
case I2S_STATE_STOPPING:
|
||||
this->stop_();
|
||||
break;
|
||||
case I2S_STATE_STOPPED:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void I2SAudioMediaPlayer::play_() {
|
||||
this->audio_->loop();
|
||||
if (this->state == media_player::MEDIA_PLAYER_STATE_PLAYING && !this->audio_->isRunning()) {
|
||||
this->stop();
|
||||
}
|
||||
}
|
||||
|
||||
void I2SAudioMediaPlayer::start() { this->i2s_state_ = I2S_STATE_STARTING; }
|
||||
void I2SAudioMediaPlayer::start_() {
|
||||
if (this->parent_->try_lock()) {
|
||||
return; // Waiting for another i2s to return lock
|
||||
}
|
||||
|
||||
#if SOC_I2S_SUPPORTS_DAC
|
||||
if (this->internal_dac_mode_ != I2S_DAC_CHANNEL_DISABLE) {
|
||||
this->audio_ = make_unique<Audio>(true, this->internal_dac_mode_);
|
||||
this->audio_ = make_unique<Audio>(true, this->internal_dac_mode_, this->parent_->get_port());
|
||||
} else {
|
||||
#endif
|
||||
this->audio_ = make_unique<Audio>(false);
|
||||
this->audio_->setPinout(this->bclk_pin_, this->lrclk_pin_, this->dout_pin_);
|
||||
this->audio_ = make_unique<Audio>(false, I2S_DAC_CHANNEL_BOTH_EN, this->parent_->get_port());
|
||||
|
||||
i2s_pin_config_t pin_config = this->parent_->get_pin_config();
|
||||
pin_config.data_out_num = this->dout_pin_;
|
||||
i2s_set_pin(this->parent_->get_port(), &pin_config);
|
||||
|
||||
this->audio_->forceMono(this->external_dac_channels_ == 1);
|
||||
if (this->mute_pin_ != nullptr) {
|
||||
this->mute_pin_->setup();
|
||||
@ -118,16 +155,30 @@ void I2SAudioMediaPlayer::setup() {
|
||||
#if SOC_I2S_SUPPORTS_DAC
|
||||
}
|
||||
#endif
|
||||
this->state = media_player::MEDIA_PLAYER_STATE_IDLE;
|
||||
}
|
||||
|
||||
void I2SAudioMediaPlayer::loop() {
|
||||
this->audio_->loop();
|
||||
if (this->state == media_player::MEDIA_PLAYER_STATE_PLAYING && !this->audio_->isRunning()) {
|
||||
this->stop_();
|
||||
this->i2s_state_ = I2S_STATE_RUNNING;
|
||||
this->high_freq_.start();
|
||||
if (this->current_url_.has_value()) {
|
||||
this->audio_->connecttohost(this->current_url_.value().c_str());
|
||||
this->state = media_player::MEDIA_PLAYER_STATE_PLAYING;
|
||||
this->publish_state();
|
||||
}
|
||||
}
|
||||
void I2SAudioMediaPlayer::stop() { this->i2s_state_ = I2S_STATE_STOPPING; }
|
||||
void I2SAudioMediaPlayer::stop_() {
|
||||
if (this->audio_->isRunning()) {
|
||||
this->audio_->stopSong();
|
||||
return;
|
||||
}
|
||||
|
||||
this->audio_ = nullptr;
|
||||
this->current_url_ = {};
|
||||
this->parent_->unlock();
|
||||
this->i2s_state_ = I2S_STATE_STOPPED;
|
||||
|
||||
this->high_freq_.stop();
|
||||
this->state = media_player::MEDIA_PLAYER_STATE_IDLE;
|
||||
this->publish_state();
|
||||
}
|
||||
|
||||
media_player::MediaPlayerTraits I2SAudioMediaPlayer::get_traits() {
|
||||
auto traits = media_player::MediaPlayerTraits();
|
||||
@ -2,6 +2,10 @@
|
||||
|
||||
#ifdef USE_ESP32_FRAMEWORK_ARDUINO
|
||||
|
||||
#include "../i2s_audio.h"
|
||||
|
||||
#include <driver/i2s.h>
|
||||
|
||||
#include "esphome/components/media_player/media_player.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/gpio.h"
|
||||
@ -12,7 +16,14 @@
|
||||
namespace esphome {
|
||||
namespace i2s_audio {
|
||||
|
||||
class I2SAudioMediaPlayer : public Component, public media_player::MediaPlayer {
|
||||
enum I2SState : uint8_t {
|
||||
I2S_STATE_STOPPED = 0,
|
||||
I2S_STATE_STARTING,
|
||||
I2S_STATE_RUNNING,
|
||||
I2S_STATE_STOPPING,
|
||||
};
|
||||
|
||||
class I2SAudioMediaPlayer : public Component, public media_player::MediaPlayer, public I2SAudioOut {
|
||||
public:
|
||||
void setup() override;
|
||||
float get_setup_priority() const override { return esphome::setup_priority::LATE; }
|
||||
@ -22,8 +33,6 @@ class I2SAudioMediaPlayer : public Component, public media_player::MediaPlayer {
|
||||
void dump_config() override;
|
||||
|
||||
void set_dout_pin(uint8_t pin) { this->dout_pin_ = pin; }
|
||||
void set_bclk_pin(uint8_t pin) { this->bclk_pin_ = pin; }
|
||||
void set_lrclk_pin(uint8_t pin) { this->lrclk_pin_ = pin; }
|
||||
void set_mute_pin(GPIOPin *mute_pin) { this->mute_pin_ = mute_pin; }
|
||||
#if SOC_I2S_SUPPORTS_DAC
|
||||
void set_internal_dac_mode(i2s_dac_mode_t mode) { this->internal_dac_mode_ = mode; }
|
||||
@ -34,20 +43,24 @@ class I2SAudioMediaPlayer : public Component, public media_player::MediaPlayer {
|
||||
|
||||
bool is_muted() const override { return this->muted_; }
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
protected:
|
||||
void control(const media_player::MediaPlayerCall &call) override;
|
||||
|
||||
void mute_();
|
||||
void unmute_();
|
||||
void set_volume_(float volume, bool publish = true);
|
||||
void stop_();
|
||||
|
||||
void start_();
|
||||
void stop_();
|
||||
void play_();
|
||||
|
||||
I2SState i2s_state_{I2S_STATE_STOPPED};
|
||||
std::unique_ptr<Audio> audio_;
|
||||
|
||||
uint8_t dout_pin_{0};
|
||||
uint8_t din_pin_{0};
|
||||
uint8_t bclk_pin_;
|
||||
uint8_t lrclk_pin_;
|
||||
|
||||
GPIOPin *mute_pin_{nullptr};
|
||||
bool muted_{false};
|
||||
@ -59,6 +72,8 @@ class I2SAudioMediaPlayer : public Component, public media_player::MediaPlayer {
|
||||
uint8_t external_dac_channels_;
|
||||
|
||||
HighFrequencyLoopRequester high_freq_;
|
||||
|
||||
optional<std::string> current_url_{};
|
||||
};
|
||||
|
||||
} // namespace i2s_audio
|
||||
41
esphome/components/i2s_audio/microphone/__init__.py
Normal file
41
esphome/components/i2s_audio/microphone/__init__.py
Normal file
@ -0,0 +1,41 @@
|
||||
import esphome.config_validation as cv
|
||||
import esphome.codegen as cg
|
||||
|
||||
from esphome import pins
|
||||
from esphome.const import CONF_ID
|
||||
from esphome.components import microphone
|
||||
|
||||
from .. import (
|
||||
i2s_audio_ns,
|
||||
I2SAudioComponent,
|
||||
I2SAudioIn,
|
||||
CONF_I2S_AUDIO_ID,
|
||||
CONF_I2S_DIN_PIN,
|
||||
)
|
||||
|
||||
CODEOWNERS = ["@jesserockz"]
|
||||
DEPENDENCIES = ["i2s_audio"]
|
||||
|
||||
I2SAudioMicrophone = i2s_audio_ns.class_(
|
||||
"I2SAudioMicrophone", I2SAudioIn, microphone.Microphone, cg.Component
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = microphone.MICROPHONE_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(I2SAudioMicrophone),
|
||||
cv.GenerateID(CONF_I2S_AUDIO_ID): cv.use_id(I2SAudioComponent),
|
||||
cv.Required(CONF_I2S_DIN_PIN): pins.internal_gpio_input_pin_number,
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
|
||||
parent = await cg.get_variable(config[CONF_I2S_AUDIO_ID])
|
||||
cg.add(parent.register_audio_in(var))
|
||||
|
||||
cg.add(var.set_din_pin(config[CONF_I2S_DIN_PIN]))
|
||||
|
||||
await microphone.register_microphone(var, config)
|
||||
101
esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp
Normal file
101
esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp
Normal file
@ -0,0 +1,101 @@
|
||||
#include "i2s_audio_microphone.h"
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
#include <driver/i2s.h>
|
||||
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace i2s_audio {
|
||||
|
||||
static const size_t BUFFER_SIZE = 512;
|
||||
|
||||
static const char *const TAG = "i2s_audio.microphone";
|
||||
|
||||
void I2SAudioMicrophone::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up I2S Audio Microphone...");
|
||||
this->buffer_.resize(BUFFER_SIZE);
|
||||
}
|
||||
|
||||
void I2SAudioMicrophone::start() { this->state_ = microphone::STATE_STARTING; }
|
||||
void I2SAudioMicrophone::start_() {
|
||||
if (!this->parent_->try_lock()) {
|
||||
return; // Waiting for another i2s to return lock
|
||||
}
|
||||
i2s_driver_config_t config = {
|
||||
.mode = (i2s_mode_t) (I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM),
|
||||
.sample_rate = 16000,
|
||||
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
|
||||
.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT,
|
||||
.communication_format = I2S_COMM_FORMAT_STAND_I2S,
|
||||
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
|
||||
.dma_buf_count = 4,
|
||||
.dma_buf_len = 256,
|
||||
.use_apll = false,
|
||||
.tx_desc_auto_clear = false,
|
||||
.fixed_mclk = 0,
|
||||
.mclk_multiple = I2S_MCLK_MULTIPLE_DEFAULT,
|
||||
.bits_per_chan = I2S_BITS_PER_CHAN_DEFAULT,
|
||||
};
|
||||
|
||||
i2s_driver_install(this->parent_->get_port(), &config, 0, nullptr);
|
||||
|
||||
i2s_pin_config_t pin_config = this->parent_->get_pin_config();
|
||||
pin_config.data_in_num = this->din_pin_;
|
||||
|
||||
i2s_set_pin(this->parent_->get_port(), &pin_config);
|
||||
this->state_ = microphone::STATE_RUNNING;
|
||||
this->high_freq_.start();
|
||||
}
|
||||
|
||||
void I2SAudioMicrophone::stop() {
|
||||
if (this->state_ == microphone::STATE_STOPPED)
|
||||
return;
|
||||
this->state_ = microphone::STATE_STOPPING;
|
||||
}
|
||||
|
||||
void I2SAudioMicrophone::stop_() {
|
||||
i2s_stop(this->parent_->get_port());
|
||||
i2s_driver_uninstall(this->parent_->get_port());
|
||||
this->parent_->unlock();
|
||||
this->state_ = microphone::STATE_STOPPED;
|
||||
this->high_freq_.stop();
|
||||
}
|
||||
|
||||
void I2SAudioMicrophone::read_() {
|
||||
size_t bytes_read = 0;
|
||||
esp_err_t err =
|
||||
i2s_read(this->parent_->get_port(), this->buffer_.data(), BUFFER_SIZE, &bytes_read, (100 / portTICK_PERIOD_MS));
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "Error reading from I2S microphone: %s", esp_err_to_name(err));
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
|
||||
this->status_clear_warning();
|
||||
|
||||
this->data_callbacks_.call(this->buffer_);
|
||||
}
|
||||
|
||||
void I2SAudioMicrophone::loop() {
|
||||
switch (this->state_) {
|
||||
case microphone::STATE_STOPPED:
|
||||
break;
|
||||
case microphone::STATE_STARTING:
|
||||
this->start_();
|
||||
break;
|
||||
case microphone::STATE_RUNNING:
|
||||
this->read_();
|
||||
break;
|
||||
case microphone::STATE_STOPPING:
|
||||
this->stop_();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace i2s_audio
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_ESP32
|
||||
@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
#include "../i2s_audio.h"
|
||||
|
||||
#include "esphome/components/microphone/microphone.h"
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace i2s_audio {
|
||||
|
||||
class I2SAudioMicrophone : public I2SAudioIn, public microphone::Microphone, public Component {
|
||||
public:
|
||||
void setup() override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
|
||||
void loop() override;
|
||||
|
||||
void set_din_pin(uint8_t pin) { this->din_pin_ = pin; }
|
||||
|
||||
protected:
|
||||
void start_();
|
||||
void stop_();
|
||||
void read_();
|
||||
|
||||
uint8_t din_pin_{0};
|
||||
std::vector<uint8_t> buffer_;
|
||||
|
||||
HighFrequencyLoopRequester high_freq_;
|
||||
};
|
||||
|
||||
} // namespace i2s_audio
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_ESP32
|
||||
@ -1,8 +1,8 @@
|
||||
#include "ili9xxx_display.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ili9xxx {
|
||||
@ -85,7 +85,7 @@ void ILI9XXXDisplay::fill(Color color) {
|
||||
case BITS_16:
|
||||
new_color = display::ColorUtil::color_to_565(color);
|
||||
for (uint32_t i = 0; i < this->get_buffer_length_() * 2; i = i + 2) {
|
||||
this->buffer_[i] = (uint8_t)(new_color >> 8);
|
||||
this->buffer_[i] = (uint8_t) (new_color >> 8);
|
||||
this->buffer_[i + 1] = (uint8_t) new_color;
|
||||
}
|
||||
return;
|
||||
@ -111,8 +111,8 @@ void HOT ILI9XXXDisplay::draw_absolute_pixel_internal(int x, int y, Color color)
|
||||
case BITS_16:
|
||||
pos = pos * 2;
|
||||
new_color = display::ColorUtil::color_to_565(color, display::ColorOrder::COLOR_ORDER_RGB);
|
||||
if (this->buffer_[pos] != (uint8_t)(new_color >> 8)) {
|
||||
this->buffer_[pos] = (uint8_t)(new_color >> 8);
|
||||
if (this->buffer_[pos] != (uint8_t) (new_color >> 8)) {
|
||||
this->buffer_[pos] = (uint8_t) (new_color >> 8);
|
||||
updated = true;
|
||||
}
|
||||
pos = pos + 1;
|
||||
@ -192,9 +192,9 @@ void ILI9XXXDisplay::display_() {
|
||||
|
||||
uint8_t pass_buff[3];
|
||||
|
||||
pass_buff[2] = (uint8_t)((red / 32.0) * 64) << 2;
|
||||
pass_buff[2] = (uint8_t) ((red / 32.0) * 64) << 2;
|
||||
pass_buff[1] = (uint8_t) green << 2;
|
||||
pass_buff[0] = (uint8_t)((blue / 32.0) * 64) << 2;
|
||||
pass_buff[0] = (uint8_t) ((blue / 32.0) * 64) << 2;
|
||||
|
||||
this->write_array(pass_buff, sizeof(pass_buff));
|
||||
}
|
||||
|
||||
@ -21,7 +21,7 @@ std::string ImprovBase::get_formatted_next_url_() {
|
||||
// Ip address
|
||||
pos = this->next_url_.find("{{ip_address}}");
|
||||
if (pos != std::string::npos) {
|
||||
std::string ip = network::IPAddress(network::get_ip_address()).str();
|
||||
std::string ip = network::get_ip_address().str();
|
||||
copy.replace(pos, 14, ip);
|
||||
}
|
||||
|
||||
|
||||
@ -73,7 +73,7 @@ void parse_json(const std::string &data, const json_parse_t &f) {
|
||||
const size_t free_heap = rp2040.getFreeHeap();
|
||||
#endif
|
||||
bool pass = false;
|
||||
size_t request_size = std::min(free_heap, (size_t)(data.size() * 1.5));
|
||||
size_t request_size = std::min(free_heap, (size_t) (data.size() * 1.5));
|
||||
do {
|
||||
DynamicJsonDocument json_document(request_size);
|
||||
if (json_document.capacity() == 0) {
|
||||
|
||||
@ -17,7 +17,7 @@ void GPIOLCDDisplay::setup() {
|
||||
this->enable_pin_->setup(); // OUTPUT
|
||||
this->enable_pin_->digital_write(false);
|
||||
|
||||
for (uint8_t i = 0; i < (uint8_t)(this->is_four_bit_mode() ? 4u : 8u); i++) {
|
||||
for (uint8_t i = 0; i < (uint8_t) (this->is_four_bit_mode() ? 4u : 8u); i++) {
|
||||
this->data_pins_[i]->setup(); // OUTPUT
|
||||
this->data_pins_[i]->digital_write(false);
|
||||
}
|
||||
|
||||
@ -118,7 +118,7 @@ class LD2410Component : public Component, public uart::UARTDevice {
|
||||
#endif
|
||||
|
||||
std::vector<uint8_t> rx_buffer_;
|
||||
int two_byte_to_int_(char firstbyte, char secondbyte) { return (int16_t)(secondbyte << 8) + firstbyte; }
|
||||
int two_byte_to_int_(char firstbyte, char secondbyte) { return (int16_t) (secondbyte << 8) + firstbyte; }
|
||||
void send_command_(uint8_t command_str, uint8_t *command_value, int command_value_len);
|
||||
|
||||
void set_max_distances_timeout_(uint8_t max_moving_distance_range, uint8_t max_still_distance_range,
|
||||
|
||||
@ -60,7 +60,7 @@ LIGHT_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).ex
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(LightState),
|
||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTJSONLightComponent),
|
||||
cv.Optional(CONF_RESTORE_MODE, default="restore_default_off"): cv.enum(
|
||||
cv.Optional(CONF_RESTORE_MODE, default="ALWAYS_OFF"): cv.enum(
|
||||
RESTORE_MODES, upper=True, space="_"
|
||||
),
|
||||
cv.Optional(CONF_ON_TURN_ON): auto.validate_automation(
|
||||
|
||||
@ -52,25 +52,26 @@ enum class ColorMode : uint8_t {
|
||||
/// Only on/off control.
|
||||
ON_OFF = (uint8_t) ColorCapability::ON_OFF,
|
||||
/// Dimmable light.
|
||||
BRIGHTNESS = (uint8_t)(ColorCapability::ON_OFF | ColorCapability::BRIGHTNESS),
|
||||
BRIGHTNESS = (uint8_t) (ColorCapability::ON_OFF | ColorCapability::BRIGHTNESS),
|
||||
/// White output only (use only if the light also has another color mode such as RGB).
|
||||
WHITE = (uint8_t)(ColorCapability::ON_OFF | ColorCapability::BRIGHTNESS | ColorCapability::WHITE),
|
||||
WHITE = (uint8_t) (ColorCapability::ON_OFF | ColorCapability::BRIGHTNESS | ColorCapability::WHITE),
|
||||
/// Controllable color temperature output.
|
||||
COLOR_TEMPERATURE =
|
||||
(uint8_t)(ColorCapability::ON_OFF | ColorCapability::BRIGHTNESS | ColorCapability::COLOR_TEMPERATURE),
|
||||
(uint8_t) (ColorCapability::ON_OFF | ColorCapability::BRIGHTNESS | ColorCapability::COLOR_TEMPERATURE),
|
||||
/// Cold and warm white output with individually controllable brightness.
|
||||
COLD_WARM_WHITE = (uint8_t)(ColorCapability::ON_OFF | ColorCapability::BRIGHTNESS | ColorCapability::COLD_WARM_WHITE),
|
||||
COLD_WARM_WHITE =
|
||||
(uint8_t) (ColorCapability::ON_OFF | ColorCapability::BRIGHTNESS | ColorCapability::COLD_WARM_WHITE),
|
||||
/// RGB color output.
|
||||
RGB = (uint8_t)(ColorCapability::ON_OFF | ColorCapability::BRIGHTNESS | ColorCapability::RGB),
|
||||
RGB = (uint8_t) (ColorCapability::ON_OFF | ColorCapability::BRIGHTNESS | ColorCapability::RGB),
|
||||
/// RGB color output and a separate white output.
|
||||
RGB_WHITE =
|
||||
(uint8_t)(ColorCapability::ON_OFF | ColorCapability::BRIGHTNESS | ColorCapability::RGB | ColorCapability::WHITE),
|
||||
(uint8_t) (ColorCapability::ON_OFF | ColorCapability::BRIGHTNESS | ColorCapability::RGB | ColorCapability::WHITE),
|
||||
/// RGB color output and a separate white output with controllable color temperature.
|
||||
RGB_COLOR_TEMPERATURE = (uint8_t)(ColorCapability::ON_OFF | ColorCapability::BRIGHTNESS | ColorCapability::RGB |
|
||||
ColorCapability::WHITE | ColorCapability::COLOR_TEMPERATURE),
|
||||
RGB_COLOR_TEMPERATURE = (uint8_t) (ColorCapability::ON_OFF | ColorCapability::BRIGHTNESS | ColorCapability::RGB |
|
||||
ColorCapability::WHITE | ColorCapability::COLOR_TEMPERATURE),
|
||||
/// RGB color output, and separate cold and warm white outputs.
|
||||
RGB_COLD_WARM_WHITE = (uint8_t)(ColorCapability::ON_OFF | ColorCapability::BRIGHTNESS | ColorCapability::RGB |
|
||||
ColorCapability::COLD_WARM_WHITE),
|
||||
RGB_COLD_WARM_WHITE = (uint8_t) (ColorCapability::ON_OFF | ColorCapability::BRIGHTNESS | ColorCapability::RGB |
|
||||
ColorCapability::COLD_WARM_WHITE),
|
||||
};
|
||||
|
||||
/// Helper class to allow bitwise operations on ColorMode with ColorCapability
|
||||
|
||||
@ -8,7 +8,6 @@ namespace light {
|
||||
|
||||
static const char *const TAG = "light";
|
||||
|
||||
LightState::LightState(const std::string &name, LightOutput *output) : EntityBase(name), output_(output) {}
|
||||
LightState::LightState(LightOutput *output) : output_(output) {}
|
||||
|
||||
LightTraits LightState::get_traits() { return this->output_->get_traits(); }
|
||||
|
||||
@ -33,9 +33,6 @@ enum LightRestoreMode {
|
||||
*/
|
||||
class LightState : public EntityBase, public Component {
|
||||
public:
|
||||
/// Construct this LightState using the provided traits and name.
|
||||
LightState(const std::string &name, LightOutput *output);
|
||||
|
||||
LightState(LightOutput *output);
|
||||
|
||||
LightTraits get_traits();
|
||||
|
||||
@ -113,8 +113,8 @@ void LilygoT547Touchscreen::loop() {
|
||||
if (tp.state == 0x06)
|
||||
tp.state = 0x07;
|
||||
|
||||
uint16_t y = (uint16_t)((buffer[i * 5 + 1 + offset] << 4) | ((buffer[i * 5 + 3 + offset] >> 4) & 0x0F));
|
||||
uint16_t x = (uint16_t)((buffer[i * 5 + 2 + offset] << 4) | (buffer[i * 5 + 3 + offset] & 0x0F));
|
||||
uint16_t y = (uint16_t) ((buffer[i * 5 + 1 + offset] << 4) | ((buffer[i * 5 + 3 + offset] >> 4) & 0x0F));
|
||||
uint16_t x = (uint16_t) ((buffer[i * 5 + 2 + offset] << 4) | (buffer[i * 5 + 3 + offset] & 0x0F));
|
||||
|
||||
switch (this->rotation_) {
|
||||
case ROTATE_0_DEGREES:
|
||||
@ -142,8 +142,8 @@ void LilygoT547Touchscreen::loop() {
|
||||
tp.id = (buffer[0] >> 4) & 0x0F;
|
||||
tp.state = 0x06;
|
||||
|
||||
uint16_t y = (uint16_t)((buffer[0 * 5 + 1] << 4) | ((buffer[0 * 5 + 3] >> 4) & 0x0F));
|
||||
uint16_t x = (uint16_t)((buffer[0 * 5 + 2] << 4) | (buffer[0 * 5 + 3] & 0x0F));
|
||||
uint16_t y = (uint16_t) ((buffer[0 * 5 + 1] << 4) | ((buffer[0 * 5 + 3] >> 4) & 0x0F));
|
||||
uint16_t x = (uint16_t) ((buffer[0 * 5 + 2] << 4) | (buffer[0 * 5 + 3] & 0x0F));
|
||||
|
||||
switch (this->rotation_) {
|
||||
case ROTATE_0_DEGREES:
|
||||
|
||||
@ -24,8 +24,7 @@ const char *lock_state_to_string(LockState state) {
|
||||
}
|
||||
}
|
||||
|
||||
Lock::Lock(const std::string &name) : EntityBase(name), state(LOCK_STATE_NONE) {}
|
||||
Lock::Lock() : Lock("") {}
|
||||
Lock::Lock() : state(LOCK_STATE_NONE) {}
|
||||
LockCall Lock::make_call() { return LockCall(this); }
|
||||
|
||||
void Lock::lock() {
|
||||
|
||||
@ -103,7 +103,6 @@ class LockCall {
|
||||
class Lock : public EntityBase {
|
||||
public:
|
||||
explicit Lock();
|
||||
explicit Lock(const std::string &name);
|
||||
|
||||
/** Make a lock device control call, this is used to control the lock device, see the LockCall description
|
||||
* for more info.
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
#include "logger.h"
|
||||
#include <cinttypes>
|
||||
|
||||
#ifdef USE_ESP_IDF
|
||||
#include <driver/uart.h>
|
||||
@ -292,7 +293,7 @@ const char *const UART_SELECTIONS[] = {"UART0", "UART1", "USB_CDC"};
|
||||
void Logger::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "Logger:");
|
||||
ESP_LOGCONFIG(TAG, " Level: %s", LOG_LEVELS[ESPHOME_LOG_LEVEL]);
|
||||
ESP_LOGCONFIG(TAG, " Log Baud Rate: %u", this->baud_rate_);
|
||||
ESP_LOGCONFIG(TAG, " Log Baud Rate: %" PRIu32, this->baud_rate_);
|
||||
ESP_LOGCONFIG(TAG, " Hardware UART: %s", UART_SELECTIONS[this->uart_]);
|
||||
for (auto &it : this->log_levels_) {
|
||||
ESP_LOGCONFIG(TAG, " Level for '%s': %s", it.tag.c_str(), LOG_LEVELS[it.level]);
|
||||
|
||||
@ -7,6 +7,7 @@ CODEOWNERS = ["@rspaargaren"]
|
||||
DEPENDENCIES = ["spi"]
|
||||
|
||||
CONF_ROTATE_CHIP = "rotate_chip"
|
||||
CONF_FLIP_X = "flip_x"
|
||||
CONF_SCROLL_SPEED = "scroll_speed"
|
||||
CONF_SCROLL_DWELL = "scroll_dwell"
|
||||
CONF_SCROLL_DELAY = "scroll_delay"
|
||||
@ -67,6 +68,7 @@ CONFIG_SCHEMA = (
|
||||
CONF_SCROLL_DWELL, default="1000ms"
|
||||
): cv.positive_time_period_milliseconds,
|
||||
cv.Optional(CONF_REVERSE_ENABLE, default=False): cv.boolean,
|
||||
cv.Optional(CONF_FLIP_X, default=False): cv.boolean,
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("500ms"))
|
||||
@ -91,6 +93,7 @@ async def to_code(config):
|
||||
cg.add(var.set_scroll(config[CONF_SCROLL_ENABLE]))
|
||||
cg.add(var.set_scroll_mode(config[CONF_SCROLL_MODE]))
|
||||
cg.add(var.set_reverse(config[CONF_REVERSE_ENABLE]))
|
||||
cg.add(var.set_flip_x([CONF_FLIP_X]))
|
||||
|
||||
if CONF_LAMBDA in config:
|
||||
lambda_ = await cg.process_lambda(
|
||||
|
||||
@ -261,13 +261,21 @@ void MAX7219Component::send64pixels(uint8_t chip, const uint8_t pixels[8]) {
|
||||
if (this->orientation_ == 0) {
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
// run this loop 8 times for all the pixels[8] received
|
||||
b |= ((pixels[i] >> col) & 1) << (7 - i); // change the column bits into row bits
|
||||
if (this->flip_x_) {
|
||||
b |= ((pixels[i] >> col) & 1) << i; // change the column bits into row bits
|
||||
} else {
|
||||
b |= ((pixels[i] >> col) & 1) << (7 - i); // change the column bits into row bits
|
||||
}
|
||||
}
|
||||
} else if (this->orientation_ == 1) {
|
||||
b = pixels[col];
|
||||
} else if (this->orientation_ == 2) {
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
b |= ((pixels[i] >> (7 - col)) & 1) << i;
|
||||
if (this->flip_x_) {
|
||||
b |= ((pixels[i] >> (7 - col)) & 1) << (7 - i);
|
||||
} else {
|
||||
b |= ((pixels[i] >> (7 - col)) & 1) << i;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
b = pixels[7 - col];
|
||||
|
||||
@ -67,6 +67,7 @@ class MAX7219Component : public PollingComponent,
|
||||
void set_scroll(bool on_off) { this->scroll_ = on_off; };
|
||||
void set_scroll_mode(ScrollMode mode) { this->scroll_mode_ = mode; };
|
||||
void set_reverse(bool on_off) { this->reverse_ = on_off; };
|
||||
void set_flip_x(bool flip_x) { this->flip_x_ = flip_x; };
|
||||
|
||||
void send_char(uint8_t chip, uint8_t data);
|
||||
void send64pixels(uint8_t chip, const uint8_t pixels[8]);
|
||||
@ -108,6 +109,7 @@ class MAX7219Component : public PollingComponent,
|
||||
ChipLinesStyle chip_lines_style_;
|
||||
bool scroll_;
|
||||
bool reverse_;
|
||||
bool flip_x_;
|
||||
bool update_{false};
|
||||
uint16_t scroll_speed_;
|
||||
uint16_t scroll_delay_;
|
||||
|
||||
@ -148,19 +148,19 @@ canbus::Error MCP2515::set_clk_out_(const CanClkOut divisor) {
|
||||
}
|
||||
|
||||
void MCP2515::prepare_id_(uint8_t *buffer, const bool extended, const uint32_t id) {
|
||||
uint16_t canid = (uint16_t)(id & 0x0FFFF);
|
||||
uint16_t canid = (uint16_t) (id & 0x0FFFF);
|
||||
|
||||
if (extended) {
|
||||
buffer[MCP_EID0] = (uint8_t)(canid & 0xFF);
|
||||
buffer[MCP_EID8] = (uint8_t)(canid >> 8);
|
||||
canid = (uint16_t)(id >> 16);
|
||||
buffer[MCP_SIDL] = (uint8_t)(canid & 0x03);
|
||||
buffer[MCP_SIDL] += (uint8_t)((canid & 0x1C) << 3);
|
||||
buffer[MCP_EID0] = (uint8_t) (canid & 0xFF);
|
||||
buffer[MCP_EID8] = (uint8_t) (canid >> 8);
|
||||
canid = (uint16_t) (id >> 16);
|
||||
buffer[MCP_SIDL] = (uint8_t) (canid & 0x03);
|
||||
buffer[MCP_SIDL] += (uint8_t) ((canid & 0x1C) << 3);
|
||||
buffer[MCP_SIDL] |= TXB_EXIDE_MASK;
|
||||
buffer[MCP_SIDH] = (uint8_t)(canid >> 5);
|
||||
buffer[MCP_SIDH] = (uint8_t) (canid >> 5);
|
||||
} else {
|
||||
buffer[MCP_SIDH] = (uint8_t)(canid >> 3);
|
||||
buffer[MCP_SIDL] = (uint8_t)((canid & 0x07) << 5);
|
||||
buffer[MCP_SIDH] = (uint8_t) (canid >> 3);
|
||||
buffer[MCP_SIDL] = (uint8_t) ((canid & 0x07) << 5);
|
||||
buffer[MCP_EID0] = 0;
|
||||
buffer[MCP_EID8] = 0;
|
||||
}
|
||||
|
||||
@ -32,7 +32,7 @@ void MCP9600Component::setup() {
|
||||
|
||||
uint16_t dev_id = 0;
|
||||
this->read_byte_16(MCP9600_REGISTER_DEVICE_ID, &dev_id);
|
||||
this->device_id_ = (uint8_t)(dev_id >> 8);
|
||||
this->device_id_ = (uint8_t) (dev_id >> 8);
|
||||
|
||||
// Allows both MCP9600's and MCP9601's to be connected.
|
||||
if (this->device_id_ != (uint8_t) 0x40 && this->device_id_ != (uint8_t) 0x41) {
|
||||
|
||||
@ -54,16 +54,16 @@ void MCP9808Sensor::update() {
|
||||
}
|
||||
|
||||
float temp = NAN;
|
||||
uint8_t msb = (uint8_t)((raw_temp & 0xff00) >> 8);
|
||||
uint8_t msb = (uint8_t) ((raw_temp & 0xff00) >> 8);
|
||||
uint8_t lsb = raw_temp & 0x00ff;
|
||||
|
||||
msb = msb & MCP9808_AMBIENT_CLEAR_FLAGS;
|
||||
|
||||
if ((msb & MCP9808_AMBIENT_TEMP_NEGATIVE) == MCP9808_AMBIENT_TEMP_NEGATIVE) {
|
||||
msb = msb & MCP9808_AMBIENT_CLEAR_SIGN;
|
||||
temp = (256 - ((uint16_t)(msb) *16 + lsb / 16.0f)) * -1;
|
||||
temp = (256 - ((uint16_t) (msb) *16 + lsb / 16.0f)) * -1;
|
||||
} else {
|
||||
temp = (uint16_t)(msb) *16 + lsb / 16.0f;
|
||||
temp = (uint16_t) (msb) *16 + lsb / 16.0f;
|
||||
}
|
||||
|
||||
if (std::isnan(temp)) {
|
||||
|
||||
91
esphome/components/microphone/__init__.py
Normal file
91
esphome/components/microphone/__init__.py
Normal file
@ -0,0 +1,91 @@
|
||||
from esphome import automation
|
||||
import esphome.config_validation as cv
|
||||
import esphome.codegen as cg
|
||||
|
||||
from esphome.automation import maybe_simple_id
|
||||
from esphome.const import CONF_ID, CONF_TRIGGER_ID
|
||||
from esphome.core import CORE
|
||||
from esphome.coroutine import coroutine_with_priority
|
||||
|
||||
|
||||
CODEOWNERS = ["@jesserockz"]
|
||||
|
||||
IS_PLATFORM_COMPONENT = True
|
||||
|
||||
CONF_ON_DATA = "on_data"
|
||||
|
||||
microphone_ns = cg.esphome_ns.namespace("microphone")
|
||||
|
||||
Microphone = microphone_ns.class_("Microphone")
|
||||
|
||||
CaptureAction = microphone_ns.class_(
|
||||
"CaptureAction", automation.Action, cg.Parented.template(Microphone)
|
||||
)
|
||||
StopCaptureAction = microphone_ns.class_(
|
||||
"StopCaptureAction", automation.Action, cg.Parented.template(Microphone)
|
||||
)
|
||||
|
||||
|
||||
DataTrigger = microphone_ns.class_(
|
||||
"DataTrigger",
|
||||
automation.Trigger.template(cg.std_vector.template(cg.int16).operator("ref")),
|
||||
)
|
||||
|
||||
IsCapturingCondition = microphone_ns.class_(
|
||||
"IsCapturingCondition", automation.Condition
|
||||
)
|
||||
|
||||
|
||||
async def setup_microphone_core_(var, config):
|
||||
for conf in config.get(CONF_ON_DATA, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(
|
||||
trigger,
|
||||
[(cg.std_vector.template(cg.uint8).operator("ref").operator("const"), "x")],
|
||||
conf,
|
||||
)
|
||||
|
||||
|
||||
async def register_microphone(var, config):
|
||||
if not CORE.has_id(config[CONF_ID]):
|
||||
var = cg.Pvariable(config[CONF_ID], var)
|
||||
await setup_microphone_core_(var, config)
|
||||
|
||||
|
||||
MICROPHONE_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.Optional(CONF_ON_DATA): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DataTrigger),
|
||||
}
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
MICROPHONE_ACTION_SCHEMA = maybe_simple_id({cv.GenerateID(): cv.use_id(Microphone)})
|
||||
|
||||
|
||||
async def media_player_action(config, action_id, template_arg, args):
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
return var
|
||||
|
||||
|
||||
automation.register_action(
|
||||
"microphone.capture", CaptureAction, MICROPHONE_ACTION_SCHEMA
|
||||
)(media_player_action)
|
||||
|
||||
automation.register_action(
|
||||
"microphone.stop_capture", StopCaptureAction, MICROPHONE_ACTION_SCHEMA
|
||||
)(media_player_action)
|
||||
|
||||
automation.register_condition(
|
||||
"microphone.is_capturing", IsCapturingCondition, MICROPHONE_ACTION_SCHEMA
|
||||
)(media_player_action)
|
||||
|
||||
|
||||
@coroutine_with_priority(100.0)
|
||||
async def to_code(config):
|
||||
cg.add_global(microphone_ns.using)
|
||||
cg.add_define("USE_MICROPHONE")
|
||||
32
esphome/components/microphone/automation.h
Normal file
32
esphome/components/microphone/automation.h
Normal file
@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/automation.h"
|
||||
#include "microphone.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace esphome {
|
||||
namespace microphone {
|
||||
|
||||
template<typename... Ts> class CaptureAction : public Action<Ts...>, public Parented<Microphone> {
|
||||
void play(Ts... x) override { this->parent_->start(); }
|
||||
};
|
||||
|
||||
template<typename... Ts> class StopCaptureAction : public Action<Ts...>, public Parented<Microphone> {
|
||||
void play(Ts... x) override { this->parent_->stop(); }
|
||||
};
|
||||
|
||||
class DataTrigger : public Trigger<const std::vector<uint8_t> &> {
|
||||
public:
|
||||
explicit DataTrigger(Microphone *mic) {
|
||||
mic->add_data_callback([this](const std::vector<uint8_t> &data) { this->trigger(data); });
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... Ts> class IsCapturingActon : public Condition<Ts...>, public Parented<Microphone> {
|
||||
public:
|
||||
bool check(Ts... x) override { return this->parent_->is_running(); }
|
||||
};
|
||||
|
||||
} // namespace microphone
|
||||
} // namespace esphome
|
||||
33
esphome/components/microphone/microphone.h
Normal file
33
esphome/components/microphone/microphone.h
Normal file
@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/entity_base.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace microphone {
|
||||
|
||||
enum State : uint8_t {
|
||||
STATE_STOPPED = 0,
|
||||
STATE_STARTING,
|
||||
STATE_RUNNING,
|
||||
STATE_STOPPING,
|
||||
};
|
||||
|
||||
class Microphone {
|
||||
public:
|
||||
virtual void start() = 0;
|
||||
virtual void stop() = 0;
|
||||
void add_data_callback(std::function<void(const std::vector<uint8_t> &)> &&data_callback) {
|
||||
this->data_callbacks_.add(std::move(data_callback));
|
||||
}
|
||||
|
||||
bool is_running() const { return this->state_ == STATE_RUNNING; }
|
||||
|
||||
protected:
|
||||
State state_{STATE_STOPPED};
|
||||
|
||||
CallbackManager<void(const std::vector<uint8_t> &)> data_callbacks_{};
|
||||
};
|
||||
|
||||
} // namespace microphone
|
||||
} // namespace esphome
|
||||
1
esphome/components/mmc5603/__init__.py
Normal file
1
esphome/components/mmc5603/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
CODEOWNERS = ["@benhoff"]
|
||||
162
esphome/components/mmc5603/mmc5603.cpp
Normal file
162
esphome/components/mmc5603/mmc5603.cpp
Normal file
@ -0,0 +1,162 @@
|
||||
#include "mmc5603.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace mmc5603 {
|
||||
|
||||
static const char *const TAG = "mmc5603";
|
||||
static const uint8_t MMC5603_ADDRESS = 0x30;
|
||||
static const uint8_t MMC56X3_PRODUCT_ID = 0x39;
|
||||
|
||||
static const uint8_t MMC56X3_DEFAULT_ADDRESS = 0x30;
|
||||
static const uint8_t MMC56X3_CHIP_ID = 0x10;
|
||||
|
||||
static const uint8_t MMC56X3_ADDR_XOUT0 = 0x00;
|
||||
static const uint8_t MMC56X3_ADDR_XOUT1 = 0x01;
|
||||
static const uint8_t MMC56X3_ADDR_XOUT2 = 0x06;
|
||||
|
||||
static const uint8_t MMC56X3_ADDR_YOUT0 = 0x02;
|
||||
static const uint8_t MMC56X3_ADDR_YOUT1 = 0x03;
|
||||
static const uint8_t MMC56X3_ADDR_YOUT2 = 0x07;
|
||||
|
||||
static const uint8_t MMC56X3_ADDR_ZOUT0 = 0x04;
|
||||
static const uint8_t MMC56X3_ADDR_ZOUT1 = 0x05;
|
||||
static const uint8_t MMC56X3_ADDR_ZOUT2 = 0x08;
|
||||
|
||||
static const uint8_t MMC56X3_OUT_TEMP = 0x09;
|
||||
static const uint8_t MMC56X3_STATUS_REG = 0x18;
|
||||
static const uint8_t MMC56X3_CTRL0_REG = 0x1B;
|
||||
static const uint8_t MMC56X3_CTRL1_REG = 0x1C;
|
||||
static const uint8_t MMC56X3_CTRL2_REG = 0x1D;
|
||||
static const uint8_t MMC5603_ODR_REG = 0x1A;
|
||||
|
||||
void MMC5603Component::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up MMC5603...");
|
||||
uint8_t id = 0;
|
||||
if (!this->read_byte(MMC56X3_PRODUCT_ID, &id)) {
|
||||
this->error_code_ = COMMUNICATION_FAILED;
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
if (id != MMC56X3_CHIP_ID) {
|
||||
ESP_LOGCONFIG(TAG, "Chip Wrong");
|
||||
this->error_code_ = ID_REGISTERS;
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this->write_byte(MMC56X3_CTRL1_REG, 0x80)) { // turn on set bit
|
||||
ESP_LOGCONFIG(TAG, "Control 1 Failed for set bit");
|
||||
this->error_code_ = COMMUNICATION_FAILED;
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this->write_byte(MMC56X3_CTRL0_REG, 0x08)) { // turn on set bit
|
||||
ESP_LOGCONFIG(TAG, "Control 0 Failed for set bit");
|
||||
this->error_code_ = COMMUNICATION_FAILED;
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this->write_byte(MMC56X3_CTRL0_REG, 0x10)) {
|
||||
this->error_code_ = COMMUNICATION_FAILED;
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t ctrl_2 = 0;
|
||||
|
||||
ctrl_2 &= ~0x10; // turn off cmm_en bit
|
||||
if (!this->write_byte(MMC56X3_CTRL2_REG, ctrl_2)) {
|
||||
this->error_code_ = COMMUNICATION_FAILED;
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
}
|
||||
void MMC5603Component::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "MMC5603:");
|
||||
LOG_I2C_DEVICE(this);
|
||||
if (this->error_code_ == COMMUNICATION_FAILED) {
|
||||
ESP_LOGE(TAG, "Communication with MMC5603 failed!");
|
||||
} else if (this->error_code_ == ID_REGISTERS) {
|
||||
ESP_LOGE(TAG, "The ID registers don't match - Is this really an MMC5603?");
|
||||
}
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
|
||||
LOG_SENSOR(" ", "X Axis", this->x_sensor_);
|
||||
LOG_SENSOR(" ", "Y Axis", this->y_sensor_);
|
||||
LOG_SENSOR(" ", "Z Axis", this->z_sensor_);
|
||||
LOG_SENSOR(" ", "Heading", this->heading_sensor_);
|
||||
}
|
||||
|
||||
float MMC5603Component::get_setup_priority() const { return setup_priority::DATA; }
|
||||
|
||||
void MMC5603Component::update() {
|
||||
if (!this->write_byte(MMC56X3_CTRL0_REG, 0x01)) {
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
uint8_t status = 0;
|
||||
if (!this->read_byte(MMC56X3_STATUS_REG, &status)) {
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t buffer[9] = {0};
|
||||
|
||||
if (!this->read_byte(MMC56X3_ADDR_XOUT0, &buffer[0]) || !this->read_byte(MMC56X3_ADDR_XOUT1, &buffer[1]) ||
|
||||
!this->read_byte(MMC56X3_ADDR_XOUT2, &buffer[2])) {
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this->read_byte(MMC56X3_ADDR_YOUT0, &buffer[3]) || !this->read_byte(MMC56X3_ADDR_YOUT1, &buffer[4]) ||
|
||||
!this->read_byte(MMC56X3_ADDR_YOUT2, &buffer[5])) {
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this->read_byte(MMC56X3_ADDR_ZOUT0, &buffer[6]) || !this->read_byte(MMC56X3_ADDR_ZOUT1, &buffer[7]) ||
|
||||
!this->read_byte(MMC56X3_ADDR_ZOUT2, &buffer[8])) {
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t raw_x = 0;
|
||||
raw_x |= buffer[0] << 12;
|
||||
raw_x |= buffer[1] << 4;
|
||||
raw_x |= buffer[2] << 0;
|
||||
|
||||
const float x = 0.0625 * (raw_x - 524288);
|
||||
|
||||
int32_t raw_y = 0;
|
||||
raw_y |= buffer[3] << 12;
|
||||
raw_y |= buffer[4] << 4;
|
||||
raw_y |= buffer[5] << 0;
|
||||
|
||||
const float y = 0.0625 * (raw_y - 524288);
|
||||
|
||||
int32_t raw_z = 0;
|
||||
raw_z |= buffer[6] << 12;
|
||||
raw_z |= buffer[7] << 4;
|
||||
raw_z |= buffer[8] << 0;
|
||||
|
||||
const float z = 0.0625 * (raw_z - 524288);
|
||||
|
||||
const float heading = atan2f(0.0f - x, y) * 180.0f / M_PI;
|
||||
ESP_LOGD(TAG, "Got x=%0.02fµT y=%0.02fµT z=%0.02fµT heading=%0.01f°", x, y, z, heading);
|
||||
|
||||
if (this->x_sensor_ != nullptr)
|
||||
this->x_sensor_->publish_state(x);
|
||||
if (this->y_sensor_ != nullptr)
|
||||
this->y_sensor_->publish_state(y);
|
||||
if (this->z_sensor_ != nullptr)
|
||||
this->z_sensor_->publish_state(z);
|
||||
if (this->heading_sensor_ != nullptr)
|
||||
this->heading_sensor_->publish_state(heading);
|
||||
}
|
||||
|
||||
} // namespace mmc5603
|
||||
} // namespace esphome
|
||||
43
esphome/components/mmc5603/mmc5603.h
Normal file
43
esphome/components/mmc5603/mmc5603.h
Normal file
@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace mmc5603 {
|
||||
|
||||
enum MMC5603Datarate {
|
||||
MMC5603_DATARATE_75_0_HZ,
|
||||
MMC5603_DATARATE_150_0_HZ,
|
||||
MMC5603_DATARATE_255_0_HZ,
|
||||
};
|
||||
|
||||
class MMC5603Component : public PollingComponent, public i2c::I2CDevice {
|
||||
public:
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override;
|
||||
void update() override;
|
||||
|
||||
void set_datarate(MMC5603Datarate datarate) { datarate_ = datarate; }
|
||||
void set_x_sensor(sensor::Sensor *x_sensor) { x_sensor_ = x_sensor; }
|
||||
void set_y_sensor(sensor::Sensor *y_sensor) { y_sensor_ = y_sensor; }
|
||||
void set_z_sensor(sensor::Sensor *z_sensor) { z_sensor_ = z_sensor; }
|
||||
void set_heading_sensor(sensor::Sensor *heading_sensor) { heading_sensor_ = heading_sensor; }
|
||||
|
||||
protected:
|
||||
MMC5603Datarate datarate_;
|
||||
sensor::Sensor *x_sensor_{nullptr};
|
||||
sensor::Sensor *y_sensor_{nullptr};
|
||||
sensor::Sensor *z_sensor_{nullptr};
|
||||
sensor::Sensor *heading_sensor_{nullptr};
|
||||
enum ErrorCode {
|
||||
NONE = 0,
|
||||
COMMUNICATION_FAILED,
|
||||
ID_REGISTERS,
|
||||
} error_code_;
|
||||
};
|
||||
|
||||
} // namespace mmc5603
|
||||
} // namespace esphome
|
||||
91
esphome/components/mmc5603/sensor.py
Normal file
91
esphome/components/mmc5603/sensor.py
Normal file
@ -0,0 +1,91 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import i2c, sensor
|
||||
from esphome.const import (
|
||||
CONF_ADDRESS,
|
||||
CONF_ID,
|
||||
ICON_MAGNET,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_MICROTESLA,
|
||||
UNIT_DEGREES,
|
||||
ICON_SCREEN_ROTATION,
|
||||
CONF_UPDATE_INTERVAL,
|
||||
)
|
||||
|
||||
DEPENDENCIES = ["i2c"]
|
||||
|
||||
mmc5603_ns = cg.esphome_ns.namespace("mmc5603")
|
||||
|
||||
CONF_FIELD_STRENGTH_X = "field_strength_x"
|
||||
CONF_FIELD_STRENGTH_Y = "field_strength_y"
|
||||
CONF_FIELD_STRENGTH_Z = "field_strength_z"
|
||||
CONF_HEADING = "heading"
|
||||
|
||||
MMC5603Component = mmc5603_ns.class_(
|
||||
"MMC5603Component", cg.PollingComponent, i2c.I2CDevice
|
||||
)
|
||||
|
||||
|
||||
MMC5603Datarate = mmc5603_ns.enum("MMC5603Datarate")
|
||||
MMC5603Datarates = {
|
||||
75: MMC5603Datarate.MMC5603_DATARATE_75_0_HZ,
|
||||
150: MMC5603Datarate.MMC5603_DATARATE_150_0_HZ,
|
||||
255: MMC5603Datarate.MMC5603_DATARATE_255_0_HZ,
|
||||
}
|
||||
|
||||
|
||||
field_strength_schema = sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_MICROTESLA,
|
||||
icon=ICON_MAGNET,
|
||||
accuracy_decimals=1,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
heading_schema = sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_DEGREES,
|
||||
icon=ICON_SCREEN_ROTATION,
|
||||
accuracy_decimals=1,
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(MMC5603Component),
|
||||
cv.Optional(CONF_ADDRESS): cv.i2c_address,
|
||||
cv.Optional(CONF_FIELD_STRENGTH_X): field_strength_schema,
|
||||
cv.Optional(CONF_FIELD_STRENGTH_Y): field_strength_schema,
|
||||
cv.Optional(CONF_FIELD_STRENGTH_Z): field_strength_schema,
|
||||
cv.Optional(CONF_HEADING): heading_schema,
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("60s"))
|
||||
.extend(i2c.i2c_device_schema(0x1E))
|
||||
)
|
||||
|
||||
|
||||
def auto_data_rate(config):
|
||||
interval_msec = config[CONF_UPDATE_INTERVAL].total_milliseconds
|
||||
interval_hz = 1000.0 / interval_msec
|
||||
for datarate in sorted(MMC5603Datarates.keys()):
|
||||
if float(datarate) >= interval_hz:
|
||||
return MMC5603Datarates[datarate]
|
||||
return MMC5603Datarates[75]
|
||||
|
||||
|
||||
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)
|
||||
|
||||
cg.add(var.set_datarate(auto_data_rate(config)))
|
||||
if CONF_FIELD_STRENGTH_X in config:
|
||||
sens = await sensor.new_sensor(config[CONF_FIELD_STRENGTH_X])
|
||||
cg.add(var.set_x_sensor(sens))
|
||||
if CONF_FIELD_STRENGTH_Y in config:
|
||||
sens = await sensor.new_sensor(config[CONF_FIELD_STRENGTH_Y])
|
||||
cg.add(var.set_y_sensor(sens))
|
||||
if CONF_FIELD_STRENGTH_Z in config:
|
||||
sens = await sensor.new_sensor(config[CONF_FIELD_STRENGTH_Z])
|
||||
cg.add(var.set_z_sensor(sens))
|
||||
if CONF_HEADING in config:
|
||||
sens = await sensor.new_sensor(config[CONF_HEADING])
|
||||
cg.add(var.set_heading_sensor(sens))
|
||||
@ -64,9 +64,10 @@ INTEGER_SENSOR_VALUE_TYPE = {
|
||||
}
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
select.SELECT_SCHEMA.extend(cv.COMPONENT_SCHEMA).extend(
|
||||
select.select_schema(ModbusSelect)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(ModbusSelect),
|
||||
cv.GenerateID(CONF_MODBUS_CONTROLLER_ID): cv.use_id(ModbusController),
|
||||
cv.Required(CONF_ADDRESS): cv.positive_int,
|
||||
cv.Optional(CONF_VALUE_TYPE, default="U_WORD"): cv.enum(
|
||||
|
||||
@ -123,7 +123,8 @@ uint32_t MopekaProCheck::parse_distance_(const std::vector<uint8_t> &message) {
|
||||
double raw_level = raw & 0x3FFF;
|
||||
double raw_t = (message[2] & 0x7F);
|
||||
|
||||
return (uint32_t)(raw_level * (MOPEKA_LPG_COEF[0] + MOPEKA_LPG_COEF[1] * raw_t + MOPEKA_LPG_COEF[2] * raw_t * raw_t));
|
||||
return (uint32_t) (raw_level *
|
||||
(MOPEKA_LPG_COEF[0] + MOPEKA_LPG_COEF[1] * raw_t + MOPEKA_LPG_COEF[2] * raw_t * raw_t));
|
||||
}
|
||||
|
||||
uint8_t MopekaProCheck::parse_temperature_(const std::vector<uint8_t> &message) { return (message[2] & 0x7F) - 40; }
|
||||
|
||||
@ -216,7 +216,7 @@ uint8_t MopekaStdCheck::parse_temperature_(const mopeka_std_package *message) {
|
||||
if (tmp == 0x0) {
|
||||
return -40;
|
||||
} else {
|
||||
return (uint8_t)((tmp - 25.0f) * 1.776964f);
|
||||
return (uint8_t) ((tmp - 25.0f) * 1.776964f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2,16 +2,16 @@
|
||||
|
||||
#ifdef USE_MQTT
|
||||
|
||||
#include <utility>
|
||||
#include "esphome/components/network/util.h"
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/components/network/util.h"
|
||||
#include <utility>
|
||||
#ifdef USE_LOGGER
|
||||
#include "esphome/components/logger/logger.h"
|
||||
#endif
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/dns.h"
|
||||
#include "lwip/err.h"
|
||||
#include "mqtt_component.h"
|
||||
|
||||
namespace esphome {
|
||||
@ -104,7 +104,11 @@ void MQTTClientComponent::start_dnslookup_() {
|
||||
// Got IP immediately
|
||||
this->dns_resolved_ = true;
|
||||
#ifdef USE_ESP32
|
||||
#if LWIP_IPV6
|
||||
this->ip_ = addr.u_addr.ip4.addr;
|
||||
#else
|
||||
this->ip_ = addr.addr;
|
||||
#endif
|
||||
#endif
|
||||
#ifdef USE_ESP8266
|
||||
this->ip_ = addr.addr;
|
||||
@ -160,8 +164,12 @@ void MQTTClientComponent::dns_found_callback(const char *name, const ip_addr_t *
|
||||
a_this->dns_resolve_error_ = true;
|
||||
} else {
|
||||
#ifdef USE_ESP32
|
||||
#if LWIP_IPV6
|
||||
a_this->ip_ = ipaddr->u_addr.ip4.addr;
|
||||
#else
|
||||
a_this->ip_ = ipaddr->addr;
|
||||
#endif
|
||||
#endif // USE_ESP32
|
||||
#ifdef USE_ESP8266
|
||||
a_this->ip_ = ipaddr->addr;
|
||||
#endif
|
||||
|
||||
@ -22,6 +22,8 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
if CONF_ENABLE_IPV6 in config and config[CONF_ENABLE_IPV6]:
|
||||
add_idf_sdkconfig_option("CONFIG_LWIP_IPV6", True)
|
||||
add_idf_sdkconfig_option("CONFIG_LWIP_IPV6_AUTOCONFIG", True)
|
||||
if CONF_ENABLE_IPV6 in config:
|
||||
add_idf_sdkconfig_option("CONFIG_LWIP_IPV6", config[CONF_ENABLE_IPV6])
|
||||
add_idf_sdkconfig_option(
|
||||
"CONFIG_LWIP_IPV6_AUTOCONFIG", config[CONF_ENABLE_IPV6]
|
||||
)
|
||||
|
||||
@ -12,10 +12,10 @@ struct IPAddress {
|
||||
IPAddress() : addr_({0, 0, 0, 0}) {}
|
||||
IPAddress(uint8_t first, uint8_t second, uint8_t third, uint8_t fourth) : addr_({first, second, third, fourth}) {}
|
||||
IPAddress(uint32_t raw) {
|
||||
addr_[0] = (uint8_t)(raw >> 0);
|
||||
addr_[1] = (uint8_t)(raw >> 8);
|
||||
addr_[2] = (uint8_t)(raw >> 16);
|
||||
addr_[3] = (uint8_t)(raw >> 24);
|
||||
addr_[0] = (uint8_t) (raw >> 0);
|
||||
addr_[1] = (uint8_t) (raw >> 8);
|
||||
addr_[2] = (uint8_t) (raw >> 16);
|
||||
addr_[3] = (uint8_t) (raw >> 24);
|
||||
}
|
||||
operator uint32_t() const {
|
||||
uint32_t res = 0;
|
||||
|
||||
@ -44,6 +44,11 @@ NdefMessage::NdefMessage(std::vector<uint8_t> &data) {
|
||||
index += id_length;
|
||||
}
|
||||
|
||||
if ((data.begin() + index > data.end()) || (data.begin() + index + payload_length > data.end())) {
|
||||
ESP_LOGE(TAG, "Corrupt record encountered; NdefMessage constructor aborting");
|
||||
break;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> payload_data(data.begin() + index, data.begin() + index + payload_length);
|
||||
|
||||
std::unique_ptr<NdefRecord> record;
|
||||
|
||||
@ -42,8 +42,8 @@ class NdefRecord {
|
||||
virtual const std::string &get_payload() const { return this->payload_; };
|
||||
|
||||
virtual std::vector<uint8_t> get_encoded_payload() {
|
||||
std::vector<uint8_t> empty_payload;
|
||||
return empty_payload;
|
||||
std::vector<uint8_t> payload(this->payload_.begin(), this->payload_.end());
|
||||
return payload;
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
@ -89,18 +89,18 @@ uint32_t get_mifare_classic_buffer_size(uint32_t message_length) {
|
||||
}
|
||||
|
||||
bool mifare_classic_is_first_block(uint8_t block_num) {
|
||||
if (block_num < 128) {
|
||||
return (block_num % 4 == 0);
|
||||
if (block_num < MIFARE_CLASSIC_BLOCKS_PER_SECT_LOW * MIFARE_CLASSIC_16BLOCK_SECT_START) {
|
||||
return (block_num % MIFARE_CLASSIC_BLOCKS_PER_SECT_LOW == 0);
|
||||
} else {
|
||||
return (block_num % 16 == 0);
|
||||
return (block_num % MIFARE_CLASSIC_BLOCKS_PER_SECT_HIGH == 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool mifare_classic_is_trailer_block(uint8_t block_num) {
|
||||
if (block_num < 128) {
|
||||
return ((block_num + 1) % 4 == 0);
|
||||
if (block_num < MIFARE_CLASSIC_BLOCKS_PER_SECT_LOW * MIFARE_CLASSIC_16BLOCK_SECT_START) {
|
||||
return ((block_num + 1) % MIFARE_CLASSIC_BLOCKS_PER_SECT_LOW == 0);
|
||||
} else {
|
||||
return ((block_num + 1) % 16 == 0);
|
||||
return ((block_num + 1) % MIFARE_CLASSIC_BLOCKS_PER_SECT_HIGH == 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -14,6 +14,9 @@ namespace nfc {
|
||||
static const uint8_t MIFARE_CLASSIC_BLOCK_SIZE = 16;
|
||||
static const uint8_t MIFARE_CLASSIC_LONG_TLV_SIZE = 4;
|
||||
static const uint8_t MIFARE_CLASSIC_SHORT_TLV_SIZE = 2;
|
||||
static const uint8_t MIFARE_CLASSIC_BLOCKS_PER_SECT_LOW = 4;
|
||||
static const uint8_t MIFARE_CLASSIC_BLOCKS_PER_SECT_HIGH = 16;
|
||||
static const uint8_t MIFARE_CLASSIC_16BLOCK_SECT_START = 32;
|
||||
|
||||
static const uint8_t MIFARE_ULTRALIGHT_PAGE_SIZE = 4;
|
||||
static const uint8_t MIFARE_ULTRALIGHT_READ_SIZE = 4;
|
||||
@ -30,10 +33,18 @@ static const uint8_t TAG_TYPE_UNKNOWN = 99;
|
||||
// Mifare Commands
|
||||
static const uint8_t MIFARE_CMD_AUTH_A = 0x60;
|
||||
static const uint8_t MIFARE_CMD_AUTH_B = 0x61;
|
||||
static const uint8_t MIFARE_CMD_HALT = 0x50;
|
||||
static const uint8_t MIFARE_CMD_READ = 0x30;
|
||||
static const uint8_t MIFARE_CMD_WRITE = 0xA0;
|
||||
static const uint8_t MIFARE_CMD_WRITE_ULTRALIGHT = 0xA2;
|
||||
|
||||
// Mifare Ack/Nak
|
||||
static const uint8_t MIFARE_CMD_ACK = 0x0A;
|
||||
static const uint8_t MIFARE_CMD_NAK_INVALID_XFER_BUFF_VALID = 0x00;
|
||||
static const uint8_t MIFARE_CMD_NAK_CRC_ERROR_XFER_BUFF_VALID = 0x01;
|
||||
static const uint8_t MIFARE_CMD_NAK_INVALID_XFER_BUFF_INVALID = 0x04;
|
||||
static const uint8_t MIFARE_CMD_NAK_CRC_ERROR_XFER_BUFF_INVALID = 0x05;
|
||||
|
||||
static const char *const MIFARE_CLASSIC = "Mifare Classic";
|
||||
static const char *const NFC_FORUM_TYPE_2 = "NFC Forum Type 2";
|
||||
static const char *const ERROR = "Error";
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
from typing import Optional
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome import automation
|
||||
@ -31,6 +30,7 @@ from esphome.const import (
|
||||
DEVICE_CLASS_DISTANCE,
|
||||
DEVICE_CLASS_EMPTY,
|
||||
DEVICE_CLASS_ENERGY,
|
||||
DEVICE_CLASS_ENERGY_STORAGE,
|
||||
DEVICE_CLASS_FREQUENCY,
|
||||
DEVICE_CLASS_GAS,
|
||||
DEVICE_CLASS_HUMIDITY,
|
||||
@ -59,6 +59,7 @@ from esphome.const import (
|
||||
DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS,
|
||||
DEVICE_CLASS_VOLTAGE,
|
||||
DEVICE_CLASS_VOLUME,
|
||||
DEVICE_CLASS_VOLUME_STORAGE,
|
||||
DEVICE_CLASS_WATER,
|
||||
DEVICE_CLASS_WEIGHT,
|
||||
DEVICE_CLASS_WIND_SPEED,
|
||||
@ -81,6 +82,7 @@ DEVICE_CLASSES = [
|
||||
DEVICE_CLASS_DISTANCE,
|
||||
DEVICE_CLASS_EMPTY,
|
||||
DEVICE_CLASS_ENERGY,
|
||||
DEVICE_CLASS_ENERGY_STORAGE,
|
||||
DEVICE_CLASS_FREQUENCY,
|
||||
DEVICE_CLASS_GAS,
|
||||
DEVICE_CLASS_HUMIDITY,
|
||||
@ -109,6 +111,7 @@ DEVICE_CLASSES = [
|
||||
DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS,
|
||||
DEVICE_CLASS_VOLTAGE,
|
||||
DEVICE_CLASS_VOLUME,
|
||||
DEVICE_CLASS_VOLUME_STORAGE,
|
||||
DEVICE_CLASS_WATER,
|
||||
DEVICE_CLASS_WEIGHT,
|
||||
DEVICE_CLASS_WIND_SPEED,
|
||||
@ -204,14 +207,13 @@ def number_schema(
|
||||
|
||||
|
||||
async def setup_number_core_(
|
||||
var, config, *, min_value: float, max_value: float, step: Optional[float]
|
||||
var, config, *, min_value: float, max_value: float, step: float
|
||||
):
|
||||
await setup_entity(var, config)
|
||||
|
||||
cg.add(var.traits.set_min_value(min_value))
|
||||
cg.add(var.traits.set_max_value(max_value))
|
||||
if step is not None:
|
||||
cg.add(var.traits.set_step(step))
|
||||
cg.add(var.traits.set_step(step))
|
||||
|
||||
cg.add(var.traits.set_mode(config[CONF_MODE]))
|
||||
|
||||
@ -239,7 +241,7 @@ async def setup_number_core_(
|
||||
|
||||
|
||||
async def register_number(
|
||||
var, config, *, min_value: float, max_value: float, step: Optional[float] = None
|
||||
var, config, *, min_value: float, max_value: float, step: float
|
||||
):
|
||||
if not CORE.has_id(config[CONF_ID]):
|
||||
var = cg.Pvariable(config[CONF_ID], var)
|
||||
@ -249,9 +251,7 @@ async def register_number(
|
||||
)
|
||||
|
||||
|
||||
async def new_number(
|
||||
config, *, min_value: float, max_value: float, step: Optional[float] = None
|
||||
):
|
||||
async def new_number(config, *, min_value: float, max_value: float, step: float):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await register_number(
|
||||
var, config, min_value=min_value, max_value=max_value, step=step
|
||||
|
||||
@ -65,7 +65,7 @@ void OTAComponent::setup() {
|
||||
|
||||
struct sockaddr_storage server;
|
||||
|
||||
socklen_t sl = socket::set_sockaddr_any((struct sockaddr *) &server, sizeof(server), htons(this->port_));
|
||||
socklen_t sl = socket::set_sockaddr_any((struct sockaddr *) &server, sizeof(server), this->port_);
|
||||
if (sl == 0) {
|
||||
ESP_LOGW(TAG, "Socket unable to set sockaddr: errno %d", errno);
|
||||
this->mark_failed();
|
||||
|
||||
@ -102,7 +102,7 @@ async def to_code(config):
|
||||
conf[CONF_ADDRESSABLE_LIGHT_ID],
|
||||
await cg.get_variable(conf[CONF_SINGLE_LIGHT_ID]),
|
||||
)
|
||||
light_state = cg.new_Pvariable(conf[CONF_LIGHT_ID], "", wrapper)
|
||||
light_state = cg.new_Pvariable(conf[CONF_LIGHT_ID], wrapper)
|
||||
await cg.register_component(light_state, conf)
|
||||
segments.append(AddressableSegment(light_state, 0, 1, False))
|
||||
|
||||
|
||||
@ -770,15 +770,15 @@ uint8_t Pipsolar::check_incoming_crc_() {
|
||||
uint16_t crc16;
|
||||
crc16 = cal_crc_half_(read_buffer_, read_pos_ - 3);
|
||||
ESP_LOGD(TAG, "checking crc on incoming message");
|
||||
if (((uint8_t)((crc16) >> 8)) == read_buffer_[read_pos_ - 3] &&
|
||||
((uint8_t)((crc16) &0xff)) == read_buffer_[read_pos_ - 2]) {
|
||||
if (((uint8_t) ((crc16) >> 8)) == read_buffer_[read_pos_ - 3] &&
|
||||
((uint8_t) ((crc16) &0xff)) == read_buffer_[read_pos_ - 2]) {
|
||||
ESP_LOGD(TAG, "CRC OK");
|
||||
read_buffer_[read_pos_ - 1] = 0;
|
||||
read_buffer_[read_pos_ - 2] = 0;
|
||||
read_buffer_[read_pos_ - 3] = 0;
|
||||
return 1;
|
||||
}
|
||||
ESP_LOGD(TAG, "CRC NOK expected: %X %X but got: %X %X", ((uint8_t)((crc16) >> 8)), ((uint8_t)((crc16) &0xff)),
|
||||
ESP_LOGD(TAG, "CRC NOK expected: %X %X but got: %X %X", ((uint8_t) ((crc16) >> 8)), ((uint8_t) ((crc16) &0xff)),
|
||||
read_buffer_[read_pos_ - 3], read_buffer_[read_pos_ - 2]);
|
||||
return 0;
|
||||
}
|
||||
@ -800,8 +800,8 @@ uint8_t Pipsolar::send_next_command_() {
|
||||
crc16 = cal_crc_half_(byte_command, length);
|
||||
this->write_str(command);
|
||||
// checksum
|
||||
this->write(((uint8_t)((crc16) >> 8))); // highbyte
|
||||
this->write(((uint8_t)((crc16) &0xff))); // lowbyte
|
||||
this->write(((uint8_t) ((crc16) >> 8))); // highbyte
|
||||
this->write(((uint8_t) ((crc16) &0xff))); // lowbyte
|
||||
// end Byte
|
||||
this->write(0x0D);
|
||||
ESP_LOGD(TAG, "Sending command from queue: %s with length %d", command, length);
|
||||
@ -829,8 +829,8 @@ void Pipsolar::send_next_poll_() {
|
||||
this->write_array(this->used_polling_commands_[this->last_polling_command_].command,
|
||||
this->used_polling_commands_[this->last_polling_command_].length);
|
||||
// checksum
|
||||
this->write(((uint8_t)((crc16) >> 8))); // highbyte
|
||||
this->write(((uint8_t)((crc16) &0xff))); // lowbyte
|
||||
this->write(((uint8_t) ((crc16) >> 8))); // highbyte
|
||||
this->write(((uint8_t) ((crc16) &0xff))); // lowbyte
|
||||
// end Byte
|
||||
this->write(0x0D);
|
||||
ESP_LOGD(TAG, "Sending polling command : %s with length %d",
|
||||
@ -882,7 +882,7 @@ void Pipsolar::add_polling_command_(const char *command, ENUMPollingCommand poll
|
||||
used_polling_command.command = new uint8_t[length]; // NOLINT(cppcoreguidelines-owning-memory)
|
||||
size_t i = 0;
|
||||
for (; beg != end; ++beg, ++i) {
|
||||
used_polling_command.command[i] = (uint8_t)(*beg);
|
||||
used_polling_command.command[i] = (uint8_t) (*beg);
|
||||
}
|
||||
used_polling_command.errors = 0;
|
||||
used_polling_command.identifier = polling_command;
|
||||
@ -907,17 +907,17 @@ uint16_t Pipsolar::cal_crc_half_(uint8_t *msg, uint8_t len) {
|
||||
crc = 0;
|
||||
|
||||
while (len-- != 0) {
|
||||
da = ((uint8_t)(crc >> 8)) >> 4;
|
||||
da = ((uint8_t) (crc >> 8)) >> 4;
|
||||
crc <<= 4;
|
||||
crc ^= crc_ta[da ^ (*ptr >> 4)];
|
||||
da = ((uint8_t)(crc >> 8)) >> 4;
|
||||
da = ((uint8_t) (crc >> 8)) >> 4;
|
||||
crc <<= 4;
|
||||
crc ^= crc_ta[da ^ (*ptr & 0x0f)];
|
||||
ptr++;
|
||||
}
|
||||
|
||||
b_crc_low = crc;
|
||||
b_crc_hign = (uint8_t)(crc >> 8);
|
||||
b_crc_hign = (uint8_t) (crc >> 8);
|
||||
|
||||
if (b_crc_low == 0x28 || b_crc_low == 0x0d || b_crc_low == 0x0a)
|
||||
b_crc_low++;
|
||||
|
||||
@ -264,13 +264,52 @@ void PMSX003Component::parse_data_() {
|
||||
break;
|
||||
}
|
||||
case PMSX003_TYPE_5003T: {
|
||||
uint16_t pm_1_0_std_concentration = this->get_16_bit_uint_(4);
|
||||
uint16_t pm_2_5_std_concentration = this->get_16_bit_uint_(6);
|
||||
uint16_t pm_10_0_std_concentration = this->get_16_bit_uint_(8);
|
||||
|
||||
uint16_t pm_1_0_concentration = this->get_16_bit_uint_(10);
|
||||
uint16_t pm_2_5_concentration = this->get_16_bit_uint_(12);
|
||||
uint16_t pm_10_0_concentration = this->get_16_bit_uint_(14);
|
||||
|
||||
uint16_t pm_particles_03um = this->get_16_bit_uint_(16);
|
||||
uint16_t pm_particles_05um = this->get_16_bit_uint_(18);
|
||||
uint16_t pm_particles_10um = this->get_16_bit_uint_(20);
|
||||
uint16_t pm_particles_25um = this->get_16_bit_uint_(22);
|
||||
// Note the pm particles 50um & 100um are not returned,
|
||||
// as PMS5003T uses those data values for temperature and humidity.
|
||||
|
||||
float temperature = this->get_16_bit_uint_(24) / 10.0f;
|
||||
float humidity = this->get_16_bit_uint_(26) / 10.0f;
|
||||
ESP_LOGD(TAG, "Got PM2.5 Concentration: %u µg/m^3, Temperature: %.1f°C, Humidity: %.1f%%", pm_2_5_concentration,
|
||||
temperature, humidity);
|
||||
|
||||
ESP_LOGD(TAG,
|
||||
"Got PM1.0 Concentration: %u µg/m^3, PM2.5 Concentration %u µg/m^3, PM10.0 Concentration: %u µg/m^3, "
|
||||
"Temperature: %.1f°C, Humidity: %.1f%%",
|
||||
pm_1_0_concentration, pm_2_5_concentration, pm_10_0_concentration, temperature, humidity);
|
||||
|
||||
if (this->pm_1_0_std_sensor_ != nullptr)
|
||||
this->pm_1_0_std_sensor_->publish_state(pm_1_0_std_concentration);
|
||||
if (this->pm_2_5_std_sensor_ != nullptr)
|
||||
this->pm_2_5_std_sensor_->publish_state(pm_2_5_std_concentration);
|
||||
if (this->pm_10_0_std_sensor_ != nullptr)
|
||||
this->pm_10_0_std_sensor_->publish_state(pm_10_0_std_concentration);
|
||||
|
||||
if (this->pm_1_0_sensor_ != nullptr)
|
||||
this->pm_1_0_sensor_->publish_state(pm_1_0_concentration);
|
||||
if (this->pm_2_5_sensor_ != nullptr)
|
||||
this->pm_2_5_sensor_->publish_state(pm_2_5_concentration);
|
||||
if (this->pm_10_0_sensor_ != nullptr)
|
||||
this->pm_10_0_sensor_->publish_state(pm_10_0_concentration);
|
||||
|
||||
if (this->pm_particles_03um_sensor_ != nullptr)
|
||||
this->pm_particles_03um_sensor_->publish_state(pm_particles_03um);
|
||||
if (this->pm_particles_05um_sensor_ != nullptr)
|
||||
this->pm_particles_05um_sensor_->publish_state(pm_particles_05um);
|
||||
if (this->pm_particles_10um_sensor_ != nullptr)
|
||||
this->pm_particles_10um_sensor_->publish_state(pm_particles_10um);
|
||||
if (this->pm_particles_25um_sensor_ != nullptr)
|
||||
this->pm_particles_25um_sensor_->publish_state(pm_particles_25um);
|
||||
|
||||
if (this->temperature_sensor_ != nullptr)
|
||||
this->temperature_sensor_->publish_state(temperature);
|
||||
if (this->humidity_sensor_ != nullptr)
|
||||
|
||||
@ -55,9 +55,9 @@ PMSX003_TYPES = {
|
||||
}
|
||||
|
||||
SENSORS_TO_TYPE = {
|
||||
CONF_PM_1_0: [TYPE_PMSX003, TYPE_PMS5003ST, TYPE_PMS5003S],
|
||||
CONF_PM_1_0: [TYPE_PMSX003, TYPE_PMS5003T, TYPE_PMS5003ST, TYPE_PMS5003S],
|
||||
CONF_PM_2_5: [TYPE_PMSX003, TYPE_PMS5003T, TYPE_PMS5003ST, TYPE_PMS5003S],
|
||||
CONF_PM_10_0: [TYPE_PMSX003, TYPE_PMS5003ST, TYPE_PMS5003S],
|
||||
CONF_PM_10_0: [TYPE_PMSX003, TYPE_PMS5003T, TYPE_PMS5003ST, TYPE_PMS5003S],
|
||||
CONF_TEMPERATURE: [TYPE_PMS5003T, TYPE_PMS5003ST],
|
||||
CONF_HUMIDITY: [TYPE_PMS5003T, TYPE_PMS5003ST],
|
||||
CONF_FORMALDEHYDE: [TYPE_PMS5003ST, TYPE_PMS5003S],
|
||||
|
||||
@ -19,9 +19,12 @@ void PN532::setup() {
|
||||
|
||||
// Get version data
|
||||
if (!this->write_command_({PN532_COMMAND_VERSION_DATA})) {
|
||||
ESP_LOGE(TAG, "Error sending version command");
|
||||
this->mark_failed();
|
||||
return;
|
||||
ESP_LOGW(TAG, "Error sending version command, trying again...");
|
||||
if (!this->write_command_({PN532_COMMAND_VERSION_DATA})) {
|
||||
ESP_LOGE(TAG, "Error sending version command");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint8_t> version_data;
|
||||
|
||||
@ -52,7 +52,13 @@ std::unique_ptr<nfc::NfcTag> PN532::read_mifare_classic_tag_(std::vector<uint8_t
|
||||
current_block++;
|
||||
}
|
||||
}
|
||||
buffer.erase(buffer.begin(), buffer.begin() + message_start_index);
|
||||
|
||||
if (buffer.begin() + message_start_index < buffer.end()) {
|
||||
buffer.erase(buffer.begin(), buffer.begin() + message_start_index);
|
||||
} else {
|
||||
return make_unique<nfc::NfcTag>(uid, nfc::MIFARE_CLASSIC);
|
||||
}
|
||||
|
||||
return make_unique<nfc::NfcTag>(uid, nfc::MIFARE_CLASSIC, buffer);
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/modbus/modbus.h"
|
||||
|
||||
@ -121,38 +121,38 @@ bool QMP6988Component::get_calibration_data_() {
|
||||
}
|
||||
|
||||
qmp6988_data_.qmp6988_cali.COE_a0 =
|
||||
(QMP6988_S32_t)(((a_data_uint8_tr[18] << SHIFT_LEFT_12_POSITION) |
|
||||
(a_data_uint8_tr[19] << SHIFT_LEFT_4_POSITION) | (a_data_uint8_tr[24] & 0x0f))
|
||||
<< 12);
|
||||
(QMP6988_S32_t) (((a_data_uint8_tr[18] << SHIFT_LEFT_12_POSITION) |
|
||||
(a_data_uint8_tr[19] << SHIFT_LEFT_4_POSITION) | (a_data_uint8_tr[24] & 0x0f))
|
||||
<< 12);
|
||||
qmp6988_data_.qmp6988_cali.COE_a0 = qmp6988_data_.qmp6988_cali.COE_a0 >> 12;
|
||||
|
||||
qmp6988_data_.qmp6988_cali.COE_a1 =
|
||||
(QMP6988_S16_t)(((a_data_uint8_tr[20]) << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[21]);
|
||||
(QMP6988_S16_t) (((a_data_uint8_tr[20]) << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[21]);
|
||||
qmp6988_data_.qmp6988_cali.COE_a2 =
|
||||
(QMP6988_S16_t)(((a_data_uint8_tr[22]) << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[23]);
|
||||
(QMP6988_S16_t) (((a_data_uint8_tr[22]) << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[23]);
|
||||
|
||||
qmp6988_data_.qmp6988_cali.COE_b00 =
|
||||
(QMP6988_S32_t)(((a_data_uint8_tr[0] << SHIFT_LEFT_12_POSITION) | (a_data_uint8_tr[1] << SHIFT_LEFT_4_POSITION) |
|
||||
((a_data_uint8_tr[24] & 0xf0) >> SHIFT_RIGHT_4_POSITION))
|
||||
<< 12);
|
||||
(QMP6988_S32_t) (((a_data_uint8_tr[0] << SHIFT_LEFT_12_POSITION) | (a_data_uint8_tr[1] << SHIFT_LEFT_4_POSITION) |
|
||||
((a_data_uint8_tr[24] & 0xf0) >> SHIFT_RIGHT_4_POSITION))
|
||||
<< 12);
|
||||
qmp6988_data_.qmp6988_cali.COE_b00 = qmp6988_data_.qmp6988_cali.COE_b00 >> 12;
|
||||
|
||||
qmp6988_data_.qmp6988_cali.COE_bt1 =
|
||||
(QMP6988_S16_t)(((a_data_uint8_tr[2]) << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[3]);
|
||||
(QMP6988_S16_t) (((a_data_uint8_tr[2]) << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[3]);
|
||||
qmp6988_data_.qmp6988_cali.COE_bt2 =
|
||||
(QMP6988_S16_t)(((a_data_uint8_tr[4]) << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[5]);
|
||||
(QMP6988_S16_t) (((a_data_uint8_tr[4]) << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[5]);
|
||||
qmp6988_data_.qmp6988_cali.COE_bp1 =
|
||||
(QMP6988_S16_t)(((a_data_uint8_tr[6]) << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[7]);
|
||||
(QMP6988_S16_t) (((a_data_uint8_tr[6]) << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[7]);
|
||||
qmp6988_data_.qmp6988_cali.COE_b11 =
|
||||
(QMP6988_S16_t)(((a_data_uint8_tr[8]) << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[9]);
|
||||
(QMP6988_S16_t) (((a_data_uint8_tr[8]) << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[9]);
|
||||
qmp6988_data_.qmp6988_cali.COE_bp2 =
|
||||
(QMP6988_S16_t)(((a_data_uint8_tr[10]) << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[11]);
|
||||
(QMP6988_S16_t) (((a_data_uint8_tr[10]) << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[11]);
|
||||
qmp6988_data_.qmp6988_cali.COE_b12 =
|
||||
(QMP6988_S16_t)(((a_data_uint8_tr[12]) << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[13]);
|
||||
(QMP6988_S16_t) (((a_data_uint8_tr[12]) << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[13]);
|
||||
qmp6988_data_.qmp6988_cali.COE_b21 =
|
||||
(QMP6988_S16_t)(((a_data_uint8_tr[14]) << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[15]);
|
||||
(QMP6988_S16_t) (((a_data_uint8_tr[14]) << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[15]);
|
||||
qmp6988_data_.qmp6988_cali.COE_bp3 =
|
||||
(QMP6988_S16_t)(((a_data_uint8_tr[16]) << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[17]);
|
||||
(QMP6988_S16_t) (((a_data_uint8_tr[16]) << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[17]);
|
||||
|
||||
ESP_LOGV(TAG, "<-----------calibration data-------------->\r\n");
|
||||
ESP_LOGV(TAG, "COE_a0[%d] COE_a1[%d] COE_a2[%d] COE_b00[%d]\r\n", qmp6988_data_.qmp6988_cali.COE_a0,
|
||||
@ -197,7 +197,7 @@ QMP6988_S16_t QMP6988Component::get_compensated_temperature_(qmp6988_ik_data_t *
|
||||
wk2 = ((QMP6988_S64_t) ik->a2 * (QMP6988_S64_t) dt) >> 14; // 30Q47+24-1=53 (39Q33)
|
||||
wk2 = (wk2 * (QMP6988_S64_t) dt) >> 10; // 39Q33+24-1=62 (52Q23)
|
||||
wk2 = ((wk1 + wk2) / 32767) >> 19; // 54,52->55Q23 (20Q04)
|
||||
ret = (QMP6988_S16_t)((ik->a0 + wk2) >> 4); // 21Q4 -> 17Q0
|
||||
ret = (QMP6988_S16_t) ((ik->a0 + wk2) >> 4); // 21Q4 -> 17Q0
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -332,13 +332,13 @@ void QMP6988Component::calculate_pressure_() {
|
||||
ESP_LOGE(TAG, "Error reading raw pressure/temp values");
|
||||
return;
|
||||
}
|
||||
p_read = (QMP6988_U32_t)((((QMP6988_U32_t)(a_data_uint8_tr[0])) << SHIFT_LEFT_16_POSITION) |
|
||||
(((QMP6988_U16_t)(a_data_uint8_tr[1])) << SHIFT_LEFT_8_POSITION) | (a_data_uint8_tr[2]));
|
||||
p_raw = (QMP6988_S32_t)(p_read - SUBTRACTOR);
|
||||
p_read = (QMP6988_U32_t) ((((QMP6988_U32_t) (a_data_uint8_tr[0])) << SHIFT_LEFT_16_POSITION) |
|
||||
(((QMP6988_U16_t) (a_data_uint8_tr[1])) << SHIFT_LEFT_8_POSITION) | (a_data_uint8_tr[2]));
|
||||
p_raw = (QMP6988_S32_t) (p_read - SUBTRACTOR);
|
||||
|
||||
t_read = (QMP6988_U32_t)((((QMP6988_U32_t)(a_data_uint8_tr[3])) << SHIFT_LEFT_16_POSITION) |
|
||||
(((QMP6988_U16_t)(a_data_uint8_tr[4])) << SHIFT_LEFT_8_POSITION) | (a_data_uint8_tr[5]));
|
||||
t_raw = (QMP6988_S32_t)(t_read - SUBTRACTOR);
|
||||
t_read = (QMP6988_U32_t) ((((QMP6988_U32_t) (a_data_uint8_tr[3])) << SHIFT_LEFT_16_POSITION) |
|
||||
(((QMP6988_U16_t) (a_data_uint8_tr[4])) << SHIFT_LEFT_8_POSITION) | (a_data_uint8_tr[5]));
|
||||
t_raw = (QMP6988_S32_t) (t_read - SUBTRACTOR);
|
||||
|
||||
t_int = this->get_compensated_temperature_(&(qmp6988_data_.ik), t_raw);
|
||||
p_int = this->get_compensated_pressure_(&(qmp6988_data_.ik), p_raw, t_int);
|
||||
|
||||
@ -78,7 +78,7 @@ optional<RC5Data> RC5Protocol::decode(RemoteReceiveData src) {
|
||||
out_data |= 1;
|
||||
}
|
||||
|
||||
out.command = (uint8_t)(out_data & 0x3F) + (1 - field_bit) * 64u;
|
||||
out.command = (uint8_t) (out_data & 0x3F) + (1 - field_bit) * 64u;
|
||||
out.address = (out_data >> 6) & 0x1F;
|
||||
return out;
|
||||
}
|
||||
|
||||
@ -12,7 +12,7 @@ from esphome.const import (
|
||||
CONF_TOLERANCE,
|
||||
CONF_MEMORY_BLOCKS,
|
||||
)
|
||||
from esphome.core import CORE
|
||||
from esphome.core import CORE, TimePeriod
|
||||
|
||||
AUTO_LOAD = ["remote_base"]
|
||||
remote_receiver_ns = cg.esphome_ns.namespace("remote_receiver")
|
||||
@ -33,9 +33,10 @@ CONFIG_SCHEMA = remote_base.validate_triggers(
|
||||
cv.SplitDefault(
|
||||
CONF_BUFFER_SIZE, esp32="10000b", esp8266="1000b"
|
||||
): cv.validate_bytes,
|
||||
cv.Optional(
|
||||
CONF_FILTER, default="50us"
|
||||
): cv.positive_time_period_microseconds,
|
||||
cv.Optional(CONF_FILTER, default="50us"): cv.All(
|
||||
cv.positive_time_period_microseconds,
|
||||
cv.Range(max=TimePeriod(microseconds=255)),
|
||||
),
|
||||
cv.Optional(
|
||||
CONF_IDLE, default="10ms"
|
||||
): cv.positive_time_period_microseconds,
|
||||
|
||||
@ -43,7 +43,7 @@ void SCD30Component::setup() {
|
||||
uint16_t(raw_firmware_version[0] & 0xFF));
|
||||
|
||||
if (this->temperature_offset_ != 0) {
|
||||
if (!this->write_command(SCD30_CMD_TEMPERATURE_OFFSET, (uint16_t)(temperature_offset_ * 100.0))) {
|
||||
if (!this->write_command(SCD30_CMD_TEMPERATURE_OFFSET, (uint16_t) (temperature_offset_ * 100.0))) {
|
||||
ESP_LOGE(TAG, "Sensor SCD30 error setting temperature offset.");
|
||||
this->error_code_ = MEASUREMENT_INIT_FAILED;
|
||||
this->mark_failed();
|
||||
|
||||
@ -16,7 +16,7 @@ class SCD30Component : public Component, public sensirion_common::SensirionI2CDe
|
||||
void set_automatic_self_calibration(bool asc) { enable_asc_ = asc; }
|
||||
void set_altitude_compensation(uint16_t altitude) { altitude_compensation_ = altitude; }
|
||||
void set_ambient_pressure_compensation(float pressure) {
|
||||
ambient_pressure_compensation_ = (uint16_t)(pressure * 1000);
|
||||
ambient_pressure_compensation_ = (uint16_t) (pressure * 1000);
|
||||
}
|
||||
void set_temperature_offset(float offset) { temperature_offset_ = offset; }
|
||||
void set_update_interval(uint16_t interval) { update_interval_ = interval; }
|
||||
|
||||
@ -50,7 +50,7 @@ void SCD4XComponent::setup() {
|
||||
uint16_t(raw_serial_number[0] & 0xFF), (uint16_t(raw_serial_number[1]) >> 8));
|
||||
|
||||
if (!this->write_command(SCD4X_CMD_TEMPERATURE_OFFSET,
|
||||
(uint16_t)(temperature_offset_ * SCD4X_TEMPERATURE_OFFSET_MULTIPLIER))) {
|
||||
(uint16_t) (temperature_offset_ * SCD4X_TEMPERATURE_OFFSET_MULTIPLIER))) {
|
||||
ESP_LOGE(TAG, "Error setting temperature offset.");
|
||||
this->error_code_ = MEASUREMENT_INIT_FAILED;
|
||||
this->mark_failed();
|
||||
|
||||
@ -3,6 +3,8 @@ import esphome.config_validation as cv
|
||||
from esphome import automation
|
||||
from esphome.components import mqtt
|
||||
from esphome.const import (
|
||||
CONF_ENTITY_CATEGORY,
|
||||
CONF_ICON,
|
||||
CONF_ID,
|
||||
CONF_ON_VALUE,
|
||||
CONF_OPTION,
|
||||
@ -14,6 +16,7 @@ from esphome.const import (
|
||||
CONF_INDEX,
|
||||
)
|
||||
from esphome.core import CORE, coroutine_with_priority
|
||||
from esphome.cpp_generator import MockObjClass
|
||||
from esphome.cpp_helpers import setup_entity
|
||||
|
||||
CODEOWNERS = ["@esphome/core"]
|
||||
@ -43,8 +46,6 @@ SELECT_OPERATION_OPTIONS = {
|
||||
"LAST": SelectOperation.SELECT_OP_LAST,
|
||||
}
|
||||
|
||||
icon = cv.icon
|
||||
|
||||
|
||||
SELECT_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
|
||||
{
|
||||
@ -58,6 +59,30 @@ SELECT_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).e
|
||||
}
|
||||
)
|
||||
|
||||
_UNDEF = object()
|
||||
|
||||
|
||||
def select_schema(
|
||||
class_: MockObjClass = _UNDEF,
|
||||
*,
|
||||
entity_category: str = _UNDEF,
|
||||
icon: str = _UNDEF,
|
||||
):
|
||||
schema = cv.Schema({})
|
||||
if class_ is not _UNDEF:
|
||||
schema = schema.extend({cv.GenerateID(): cv.declare_id(class_)})
|
||||
if entity_category is not _UNDEF:
|
||||
schema = schema.extend(
|
||||
{
|
||||
cv.Optional(
|
||||
CONF_ENTITY_CATEGORY, default=entity_category
|
||||
): cv.entity_category
|
||||
}
|
||||
)
|
||||
if icon is not _UNDEF:
|
||||
schema = schema.extend({cv.Optional(CONF_ICON, default=icon): cv.icon})
|
||||
return SELECT_SCHEMA.extend(schema)
|
||||
|
||||
|
||||
async def setup_select_core_(var, config, *, options: list[str]):
|
||||
await setup_entity(var, config)
|
||||
|
||||
@ -86,7 +86,7 @@ void SenseAirComponent::background_calibration_result() {
|
||||
}
|
||||
|
||||
// Check if 5th bit (register CI6) is set
|
||||
ESP_LOGD(TAG, "SenseAir Result=%s (%02x%02x%02x %02x%02x %02x%02x)", (response[4] & 0b100000) != 0 ? "OK" : "NOT_OK",
|
||||
ESP_LOGI(TAG, "SenseAir Result=%s (%02x%02x%02x %02x%02x %02x%02x)", (response[4] & 0b100000) != 0 ? "OK" : "NOT_OK",
|
||||
response[0], response[1], response[2], response[3], response[4], response[5], response[6]);
|
||||
}
|
||||
|
||||
|
||||
@ -43,6 +43,7 @@ from esphome.const import (
|
||||
DEVICE_CLASS_DURATION,
|
||||
DEVICE_CLASS_EMPTY,
|
||||
DEVICE_CLASS_ENERGY,
|
||||
DEVICE_CLASS_ENERGY_STORAGE,
|
||||
DEVICE_CLASS_FREQUENCY,
|
||||
DEVICE_CLASS_GAS,
|
||||
DEVICE_CLASS_HUMIDITY,
|
||||
@ -72,6 +73,7 @@ from esphome.const import (
|
||||
DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS,
|
||||
DEVICE_CLASS_VOLTAGE,
|
||||
DEVICE_CLASS_VOLUME,
|
||||
DEVICE_CLASS_VOLUME_STORAGE,
|
||||
DEVICE_CLASS_WATER,
|
||||
DEVICE_CLASS_WEIGHT,
|
||||
DEVICE_CLASS_WIND_SPEED,
|
||||
@ -97,6 +99,7 @@ DEVICE_CLASSES = [
|
||||
DEVICE_CLASS_DURATION,
|
||||
DEVICE_CLASS_EMPTY,
|
||||
DEVICE_CLASS_ENERGY,
|
||||
DEVICE_CLASS_ENERGY_STORAGE,
|
||||
DEVICE_CLASS_FREQUENCY,
|
||||
DEVICE_CLASS_GAS,
|
||||
DEVICE_CLASS_HUMIDITY,
|
||||
@ -126,6 +129,7 @@ DEVICE_CLASSES = [
|
||||
DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS,
|
||||
DEVICE_CLASS_VOLTAGE,
|
||||
DEVICE_CLASS_VOLUME,
|
||||
DEVICE_CLASS_VOLUME_STORAGE,
|
||||
DEVICE_CLASS_WATER,
|
||||
DEVICE_CLASS_WEIGHT,
|
||||
DEVICE_CLASS_WIND_SPEED,
|
||||
@ -194,6 +198,7 @@ SensorPublishAction = sensor_ns.class_("SensorPublishAction", automation.Action)
|
||||
Filter = sensor_ns.class_("Filter")
|
||||
QuantileFilter = sensor_ns.class_("QuantileFilter", Filter)
|
||||
MedianFilter = sensor_ns.class_("MedianFilter", Filter)
|
||||
SkipInitialFilter = sensor_ns.class_("SkipInitialFilter", Filter)
|
||||
MinFilter = sensor_ns.class_("MinFilter", Filter)
|
||||
MaxFilter = sensor_ns.class_("MaxFilter", Filter)
|
||||
SlidingWindowMovingAverageFilter = sensor_ns.class_(
|
||||
@ -365,6 +370,11 @@ MIN_SCHEMA = cv.All(
|
||||
)
|
||||
|
||||
|
||||
@FILTER_REGISTRY.register("skip_initial", SkipInitialFilter, cv.positive_not_null_int)
|
||||
async def skip_initial_filter_to_code(config, filter_id):
|
||||
return cg.new_Pvariable(filter_id, config)
|
||||
|
||||
|
||||
@FILTER_REGISTRY.register("min", MinFilter, MIN_SCHEMA)
|
||||
async def min_filter_to_code(config, filter_id):
|
||||
return cg.new_Pvariable(
|
||||
@ -466,9 +476,21 @@ async def lambda_filter_to_code(config, filter_id):
|
||||
return cg.new_Pvariable(filter_id, lambda_)
|
||||
|
||||
|
||||
@FILTER_REGISTRY.register("delta", DeltaFilter, cv.float_)
|
||||
def validate_delta(config):
|
||||
try:
|
||||
return (cv.positive_float(config), False)
|
||||
except cv.Invalid:
|
||||
pass
|
||||
try:
|
||||
return (cv.percentage(config), True)
|
||||
except cv.Invalid:
|
||||
pass
|
||||
raise cv.Invalid("Delta filter requires a positive number or percentage value.")
|
||||
|
||||
|
||||
@FILTER_REGISTRY.register("delta", DeltaFilter, validate_delta)
|
||||
async def delta_filter_to_code(config, filter_id):
|
||||
return cg.new_Pvariable(filter_id, config)
|
||||
return cg.new_Pvariable(filter_id, *config)
|
||||
|
||||
|
||||
@FILTER_REGISTRY.register("or", OrFilter, validate_filters)
|
||||
|
||||
@ -74,6 +74,19 @@ optional<float> MedianFilter::new_value(float value) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// SkipInitialFilter
|
||||
SkipInitialFilter::SkipInitialFilter(size_t num_to_ignore) : num_to_ignore_(num_to_ignore) {}
|
||||
optional<float> SkipInitialFilter::new_value(float value) {
|
||||
if (num_to_ignore_ > 0) {
|
||||
num_to_ignore_--;
|
||||
ESP_LOGV(TAG, "SkipInitialFilter(%p)::new_value(%f) SKIPPING, %u left", this, value, num_to_ignore_);
|
||||
return {};
|
||||
}
|
||||
|
||||
ESP_LOGV(TAG, "SkipInitialFilter(%p)::new_value(%f) SENDING", this, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
// QuantileFilter
|
||||
QuantileFilter::QuantileFilter(size_t window_size, size_t send_every, size_t send_first_at, float quantile)
|
||||
: send_every_(send_every), send_at_(send_every - send_first_at), window_size_(window_size), quantile_(quantile) {}
|
||||
@ -315,19 +328,23 @@ optional<float> ThrottleFilter::new_value(float value) {
|
||||
}
|
||||
|
||||
// DeltaFilter
|
||||
DeltaFilter::DeltaFilter(float min_delta) : min_delta_(min_delta), last_value_(NAN) {}
|
||||
DeltaFilter::DeltaFilter(float delta, bool percentage_mode)
|
||||
: delta_(delta), current_delta_(delta), percentage_mode_(percentage_mode), last_value_(NAN) {}
|
||||
optional<float> DeltaFilter::new_value(float value) {
|
||||
if (std::isnan(value)) {
|
||||
if (std::isnan(this->last_value_)) {
|
||||
return {};
|
||||
} else {
|
||||
if (this->percentage_mode_) {
|
||||
this->current_delta_ = fabsf(value * this->delta_);
|
||||
}
|
||||
return this->last_value_ = value;
|
||||
}
|
||||
}
|
||||
if (std::isnan(this->last_value_)) {
|
||||
return this->last_value_ = value;
|
||||
}
|
||||
if (fabsf(value - this->last_value_) >= this->min_delta_) {
|
||||
if (std::isnan(this->last_value_) || fabsf(value - this->last_value_) >= this->current_delta_) {
|
||||
if (this->percentage_mode_) {
|
||||
this->current_delta_ = fabsf(value * this->delta_);
|
||||
}
|
||||
return this->last_value_ = value;
|
||||
}
|
||||
return {};
|
||||
|
||||
@ -102,6 +102,24 @@ class MedianFilter : public Filter {
|
||||
size_t window_size_;
|
||||
};
|
||||
|
||||
/** Simple skip filter.
|
||||
*
|
||||
* Skips the first N values, then passes everything else.
|
||||
*/
|
||||
class SkipInitialFilter : public Filter {
|
||||
public:
|
||||
/** Construct a SkipInitialFilter.
|
||||
*
|
||||
* @param num_to_ignore How many values to ignore before the filter becomes a no-op.
|
||||
*/
|
||||
explicit SkipInitialFilter(size_t num_to_ignore);
|
||||
|
||||
optional<float> new_value(float value) override;
|
||||
|
||||
protected:
|
||||
size_t num_to_ignore_;
|
||||
};
|
||||
|
||||
/** Simple min filter.
|
||||
*
|
||||
* Takes the min of the last <send_every> values and pushes it out every <send_every>.
|
||||
@ -325,12 +343,14 @@ class HeartbeatFilter : public Filter, public Component {
|
||||
|
||||
class DeltaFilter : public Filter {
|
||||
public:
|
||||
explicit DeltaFilter(float min_delta);
|
||||
explicit DeltaFilter(float delta, bool percentage_mode);
|
||||
|
||||
optional<float> new_value(float value) override;
|
||||
|
||||
protected:
|
||||
float min_delta_;
|
||||
float delta_;
|
||||
float current_delta_;
|
||||
bool percentage_mode_;
|
||||
float last_value_{NAN};
|
||||
};
|
||||
|
||||
|
||||
@ -20,54 +20,37 @@ std::string state_class_to_string(StateClass state_class) {
|
||||
}
|
||||
}
|
||||
|
||||
Sensor::Sensor(const std::string &name) : EntityBase(name), state(NAN), raw_state(NAN) {}
|
||||
Sensor::Sensor() : Sensor("") {}
|
||||
Sensor::Sensor() : state(NAN), raw_state(NAN) {}
|
||||
|
||||
std::string Sensor::get_unit_of_measurement() {
|
||||
if (this->unit_of_measurement_.has_value())
|
||||
return *this->unit_of_measurement_;
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
return this->unit_of_measurement();
|
||||
#pragma GCC diagnostic pop
|
||||
return "";
|
||||
}
|
||||
void Sensor::set_unit_of_measurement(const std::string &unit_of_measurement) {
|
||||
this->unit_of_measurement_ = unit_of_measurement;
|
||||
}
|
||||
std::string Sensor::unit_of_measurement() { return ""; }
|
||||
|
||||
int8_t Sensor::get_accuracy_decimals() {
|
||||
if (this->accuracy_decimals_.has_value())
|
||||
return *this->accuracy_decimals_;
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
return this->accuracy_decimals();
|
||||
#pragma GCC diagnostic pop
|
||||
return 0;
|
||||
}
|
||||
void Sensor::set_accuracy_decimals(int8_t accuracy_decimals) { this->accuracy_decimals_ = accuracy_decimals; }
|
||||
int8_t Sensor::accuracy_decimals() { return 0; }
|
||||
|
||||
std::string Sensor::get_device_class() {
|
||||
if (this->device_class_.has_value())
|
||||
return *this->device_class_;
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
return this->device_class();
|
||||
#pragma GCC diagnostic pop
|
||||
return "";
|
||||
}
|
||||
void Sensor::set_device_class(const std::string &device_class) { this->device_class_ = device_class; }
|
||||
std::string Sensor::device_class() { return ""; }
|
||||
|
||||
void Sensor::set_state_class(StateClass state_class) { this->state_class_ = state_class; }
|
||||
StateClass Sensor::get_state_class() {
|
||||
if (this->state_class_.has_value())
|
||||
return *this->state_class_;
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
return this->state_class();
|
||||
#pragma GCC diagnostic pop
|
||||
return StateClass::STATE_CLASS_NONE;
|
||||
}
|
||||
StateClass Sensor::state_class() { return StateClass::STATE_CLASS_NONE; }
|
||||
|
||||
void Sensor::publish_state(float state) {
|
||||
this->raw_state = state;
|
||||
|
||||
@ -57,7 +57,6 @@ std::string state_class_to_string(StateClass state_class);
|
||||
class Sensor : public EntityBase {
|
||||
public:
|
||||
explicit Sensor();
|
||||
explicit Sensor(const std::string &name);
|
||||
|
||||
/// Get the unit of measurement, using the manual override if set.
|
||||
std::string get_unit_of_measurement();
|
||||
@ -150,44 +149,18 @@ class Sensor : public EntityBase {
|
||||
/// Return whether this sensor has gotten a full state (that passed through all filters) yet.
|
||||
bool has_state() const;
|
||||
|
||||
/** A unique ID for this sensor, empty for no unique id. See unique ID requirements:
|
||||
* https://developers.home-assistant.io/docs/en/entity_registry_index.html#unique-id-requirements
|
||||
/** Override this method to set the unique ID of this sensor.
|
||||
*
|
||||
* @return The unique id as a string.
|
||||
* @deprecated Do not use for new sensors, a suitable unique ID is automatically generated (2023.4).
|
||||
*/
|
||||
virtual std::string unique_id();
|
||||
|
||||
void internal_send_state_to_frontend(float state);
|
||||
|
||||
protected:
|
||||
/** Override this to set the default unit of measurement.
|
||||
*
|
||||
* @deprecated This method is deprecated, set the property during config validation instead. (2022.1)
|
||||
*/
|
||||
virtual std::string unit_of_measurement(); // NOLINT
|
||||
|
||||
/** Override this to set the default accuracy in decimals.
|
||||
*
|
||||
* @deprecated This method is deprecated, set the property during config validation instead. (2022.1)
|
||||
*/
|
||||
virtual int8_t accuracy_decimals(); // NOLINT
|
||||
|
||||
/** Override this to set the default device class.
|
||||
*
|
||||
* @deprecated This method is deprecated, set the property during config validation instead. (2022.1)
|
||||
*/
|
||||
virtual std::string device_class(); // NOLINT
|
||||
|
||||
/** Override this to set the default state class.
|
||||
*
|
||||
* @deprecated This method is deprecated, set the property during config validation instead. (2022.1)
|
||||
*/
|
||||
virtual StateClass state_class(); // NOLINT
|
||||
|
||||
CallbackManager<void(float)> raw_callback_; ///< Storage for raw state callbacks.
|
||||
CallbackManager<void(float)> callback_; ///< Storage for filtered state callbacks.
|
||||
|
||||
bool has_state_{false};
|
||||
Filter *filter_list_{nullptr}; ///< Store all active filters.
|
||||
|
||||
optional<std::string> unit_of_measurement_; ///< Unit of measurement override
|
||||
@ -195,6 +168,7 @@ class Sensor : public EntityBase {
|
||||
optional<std::string> device_class_; ///< Device class override
|
||||
optional<StateClass> state_class_{STATE_CLASS_NONE}; ///< State class override
|
||||
bool force_update_{false}; ///< Force update mode
|
||||
bool has_state_{false};
|
||||
};
|
||||
|
||||
} // namespace sensor
|
||||
|
||||
@ -234,8 +234,8 @@ bool SGP4xComponent::measure_raw_(uint16_t &voc_raw, uint16_t &nox_raw) {
|
||||
response_words = 2;
|
||||
}
|
||||
}
|
||||
uint16_t rhticks = llround((uint16_t)((humidity * 65535) / 100));
|
||||
uint16_t tempticks = (uint16_t)(((temperature + 45) * 65535) / 175);
|
||||
uint16_t rhticks = llround((uint16_t) ((humidity * 65535) / 100));
|
||||
uint16_t tempticks = (uint16_t) (((temperature + 45) * 65535) / 175);
|
||||
// first parameter are the relative humidity ticks
|
||||
data[0] = rhticks;
|
||||
// secomd parameter are the temperature ticks
|
||||
|
||||
@ -19,7 +19,7 @@ void SHT4XComponent::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up sht4x...");
|
||||
|
||||
if (this->duty_cycle_ > 0.0) {
|
||||
uint32_t heater_interval = (uint32_t)(this->heater_time_ / this->duty_cycle_);
|
||||
uint32_t heater_interval = (uint32_t) (this->heater_time_ / this->duty_cycle_);
|
||||
ESP_LOGD(TAG, "Heater interval: %i", heater_interval);
|
||||
|
||||
if (this->heater_power_ == SHT4X_HEATERPOWER_HIGH) {
|
||||
|
||||
@ -20,7 +20,9 @@ std::string format_sockaddr(const struct sockaddr_storage &storage) {
|
||||
char buf[INET_ADDRSTRLEN];
|
||||
if (inet_ntop(AF_INET, &addr->sin_addr, buf, sizeof(buf)) != nullptr)
|
||||
return std::string{buf};
|
||||
} else if (storage.ss_family == AF_INET6) {
|
||||
}
|
||||
#if LWIP_IPV6
|
||||
else if (storage.ss_family == AF_INET6) {
|
||||
const struct sockaddr_in6 *addr = reinterpret_cast<const struct sockaddr_in6 *>(&storage);
|
||||
char buf[INET6_ADDRSTRLEN];
|
||||
// Format IPv4-mapped IPv6 addresses as regular IPv4 addresses
|
||||
@ -32,6 +34,7 @@ std::string format_sockaddr(const struct sockaddr_storage &storage) {
|
||||
if (inet_ntop(AF_INET6, &addr->sin6_addr, buf, sizeof(buf)) != nullptr)
|
||||
return std::string{buf};
|
||||
}
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -136,6 +139,11 @@ class BSDSocketImpl : public Socket {
|
||||
return ::writev(fd_, iov, iovcnt);
|
||||
#endif
|
||||
}
|
||||
|
||||
ssize_t sendto(const void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen) override {
|
||||
return ::sendto(fd_, buf, len, flags, to, tolen);
|
||||
}
|
||||
|
||||
int setblocking(bool blocking) override {
|
||||
int fl = ::fcntl(fd_, F_GETFL, 0);
|
||||
if (blocking) {
|
||||
|
||||
@ -15,19 +15,28 @@
|
||||
/* Address families. */
|
||||
#define AF_UNSPEC 0
|
||||
#define AF_INET 2
|
||||
#define AF_INET6 10
|
||||
#define PF_INET AF_INET
|
||||
#define PF_INET6 AF_INET6
|
||||
#define PF_UNSPEC AF_UNSPEC
|
||||
|
||||
#define IPPROTO_IP 0
|
||||
#define IPPROTO_TCP 6
|
||||
|
||||
#if LWIP_IPV6
|
||||
#define AF_INET6 10
|
||||
#define PF_INET6 AF_INET6
|
||||
|
||||
#define IPPROTO_IPV6 41
|
||||
#define IPPROTO_ICMPV6 58
|
||||
#endif
|
||||
|
||||
#define TCP_NODELAY 0x01
|
||||
|
||||
#define F_GETFL 3
|
||||
#define F_SETFL 4
|
||||
|
||||
#ifdef O_NONBLOCK
|
||||
#undef O_NONBLOCK
|
||||
#endif
|
||||
#define O_NONBLOCK 1
|
||||
|
||||
#define SHUT_RD 0
|
||||
@ -58,6 +67,7 @@ struct sockaddr_in {
|
||||
char sin_zero[SIN_ZERO_LEN];
|
||||
};
|
||||
|
||||
#if LWIP_IPV6
|
||||
// NOLINTNEXTLINE(readability-identifier-naming)
|
||||
struct sockaddr_in6 {
|
||||
uint8_t sin6_len; /* length of this structure */
|
||||
@ -67,6 +77,7 @@ struct sockaddr_in6 {
|
||||
struct in6_addr sin6_addr; /* IPv6 address */
|
||||
uint32_t sin6_scope_id; /* Set of interfaces for scope */
|
||||
};
|
||||
#endif
|
||||
|
||||
// NOLINTNEXTLINE(readability-identifier-naming)
|
||||
struct sockaddr {
|
||||
|
||||
@ -467,6 +467,10 @@ class LWIPRawImpl : public Socket {
|
||||
}
|
||||
return written;
|
||||
}
|
||||
ssize_t sendto(const void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen) override {
|
||||
// return ::sendto(fd_, buf, len, flags, to, tolen);
|
||||
return 0;
|
||||
}
|
||||
int setblocking(bool blocking) override {
|
||||
if (pcb_ == nullptr) {
|
||||
errno = ECONNRESET;
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
#include "socket.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include <cstring>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace socket {
|
||||
@ -14,6 +15,39 @@ std::unique_ptr<Socket> socket_ip(int type, int protocol) {
|
||||
#endif
|
||||
}
|
||||
|
||||
socklen_t set_sockaddr(struct sockaddr *addr, socklen_t addrlen, const std::string &ip_address, uint16_t port) {
|
||||
#if LWIP_IPV6
|
||||
if (addrlen < sizeof(sockaddr_in6)) {
|
||||
errno = EINVAL;
|
||||
return 0;
|
||||
}
|
||||
auto *server = reinterpret_cast<sockaddr_in6 *>(addr);
|
||||
memset(server, 0, sizeof(sockaddr_in6));
|
||||
server->sin6_family = AF_INET6;
|
||||
server->sin6_port = htons(port);
|
||||
|
||||
if (ip_address.find('.') != std::string::npos) {
|
||||
server->sin6_addr.un.u32_addr[3] = inet_addr(ip_address.c_str());
|
||||
} else {
|
||||
ip6_addr_t ip6;
|
||||
inet6_aton(ip_address.c_str(), &ip6);
|
||||
memcpy(server->sin6_addr.un.u32_addr, ip6.addr, sizeof(ip6.addr));
|
||||
}
|
||||
return sizeof(sockaddr_in6);
|
||||
#else
|
||||
if (addrlen < sizeof(sockaddr_in)) {
|
||||
errno = EINVAL;
|
||||
return 0;
|
||||
}
|
||||
auto *server = reinterpret_cast<sockaddr_in *>(addr);
|
||||
memset(server, 0, sizeof(sockaddr_in));
|
||||
server->sin_family = AF_INET;
|
||||
server->sin_addr.s_addr = inet_addr(ip_address.c_str());
|
||||
server->sin_port = htons(port);
|
||||
return sizeof(sockaddr_in);
|
||||
#endif
|
||||
}
|
||||
|
||||
socklen_t set_sockaddr_any(struct sockaddr *addr, socklen_t addrlen, uint16_t port) {
|
||||
#if LWIP_IPV6
|
||||
if (addrlen < sizeof(sockaddr_in6)) {
|
||||
@ -23,7 +57,7 @@ socklen_t set_sockaddr_any(struct sockaddr *addr, socklen_t addrlen, uint16_t po
|
||||
auto *server = reinterpret_cast<sockaddr_in6 *>(addr);
|
||||
memset(server, 0, sizeof(sockaddr_in6));
|
||||
server->sin6_family = AF_INET6;
|
||||
server->sin6_port = port;
|
||||
server->sin6_port = htons(port);
|
||||
server->sin6_addr = in6addr_any;
|
||||
return sizeof(sockaddr_in6);
|
||||
#else
|
||||
@ -35,7 +69,7 @@ socklen_t set_sockaddr_any(struct sockaddr *addr, socklen_t addrlen, uint16_t po
|
||||
memset(server, 0, sizeof(sockaddr_in));
|
||||
server->sin_family = AF_INET;
|
||||
server->sin_addr.s_addr = ESPHOME_INADDR_ANY;
|
||||
server->sin_port = port;
|
||||
server->sin_port = htons(port);
|
||||
return sizeof(sockaddr_in);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "headers.h"
|
||||
#include "esphome/core/optional.h"
|
||||
#include "headers.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace socket {
|
||||
@ -34,6 +34,8 @@ class Socket {
|
||||
virtual ssize_t readv(const struct iovec *iov, int iovcnt) = 0;
|
||||
virtual ssize_t write(const void *buf, size_t len) = 0;
|
||||
virtual ssize_t writev(const struct iovec *iov, int iovcnt) = 0;
|
||||
virtual ssize_t sendto(const void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen);
|
||||
|
||||
virtual int setblocking(bool blocking) = 0;
|
||||
virtual int loop() { return 0; };
|
||||
};
|
||||
@ -44,7 +46,10 @@ std::unique_ptr<Socket> socket(int domain, int type, int protocol);
|
||||
/// Create a socket in the newest available IP domain (IPv6 or IPv4) of the given type and protocol.
|
||||
std::unique_ptr<Socket> socket_ip(int type, int protocol);
|
||||
|
||||
/// Set a sockaddr to the any address for the IP version used by socket_ip().
|
||||
/// Set a sockaddr to the specified address and port for the IP version used by socket_ip().
|
||||
socklen_t set_sockaddr(struct sockaddr *addr, socklen_t addrlen, const std::string &ip_address, uint16_t port);
|
||||
|
||||
/// Set a sockaddr to the any address and specified port for the IP version used by socket_ip().
|
||||
socklen_t set_sockaddr_any(struct sockaddr *addr, socklen_t addrlen, uint16_t port);
|
||||
|
||||
} // namespace socket
|
||||
|
||||
@ -17,6 +17,7 @@ spi_ns = cg.esphome_ns.namespace("spi")
|
||||
SPIComponent = spi_ns.class_("SPIComponent", cg.Component)
|
||||
SPIDevice = spi_ns.class_("SPIDevice")
|
||||
MULTI_CONF = True
|
||||
CONF_FORCE_SW = "force_sw"
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
@ -25,6 +26,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.Required(CONF_CLK_PIN): pins.gpio_output_pin_schema,
|
||||
cv.Optional(CONF_MISO_PIN): pins.gpio_input_pin_schema,
|
||||
cv.Optional(CONF_MOSI_PIN): pins.gpio_output_pin_schema,
|
||||
cv.Optional(CONF_FORCE_SW, default=False): cv.boolean,
|
||||
}
|
||||
),
|
||||
cv.has_at_least_one_key(CONF_MISO_PIN, CONF_MOSI_PIN),
|
||||
@ -39,6 +41,7 @@ async def to_code(config):
|
||||
|
||||
clk = await cg.gpio_pin_expression(config[CONF_CLK_PIN])
|
||||
cg.add(var.set_clk(clk))
|
||||
cg.add(var.set_force_sw(config[CONF_FORCE_SW]))
|
||||
if CONF_MISO_PIN in config:
|
||||
miso = await cg.gpio_pin_expression(config[CONF_MISO_PIN])
|
||||
cg.add(var.set_miso(miso))
|
||||
|
||||
@ -25,7 +25,7 @@ void SPIComponent::setup() {
|
||||
this->clk_->digital_write(true);
|
||||
|
||||
#ifdef USE_SPI_ARDUINO_BACKEND
|
||||
bool use_hw_spi = true;
|
||||
bool use_hw_spi = !this->force_sw_;
|
||||
const bool has_miso = this->miso_ != nullptr;
|
||||
const bool has_mosi = this->mosi_ != nullptr;
|
||||
int8_t clk_pin = -1, miso_pin = -1, mosi_pin = -1;
|
||||
@ -79,7 +79,7 @@ void SPIComponent::setup() {
|
||||
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
|
||||
this->hw_spi_ = new SPIClass(FSPI); // NOLINT(cppcoreguidelines-owning-memory)
|
||||
#else
|
||||
this->hw_spi_ = new SPIClass(VSPI); // NOLINT(cppcoreguidelines-owning-memory)
|
||||
this->hw_spi_ = new SPIClass(HSPI); // NOLINT(cppcoreguidelines-owning-memory)
|
||||
#endif // USE_ESP32_VARIANT
|
||||
}
|
||||
spi_bus_num++;
|
||||
|
||||
@ -74,6 +74,7 @@ class SPIComponent : public Component {
|
||||
void set_clk(GPIOPin *clk) { clk_ = clk; }
|
||||
void set_miso(GPIOPin *miso) { miso_ = miso; }
|
||||
void set_mosi(GPIOPin *mosi) { mosi_ = mosi; }
|
||||
void set_force_sw(bool force_sw) { force_sw_ = force_sw; }
|
||||
|
||||
void setup() override;
|
||||
|
||||
@ -260,6 +261,7 @@ class SPIComponent : public Component {
|
||||
GPIOPin *miso_{nullptr};
|
||||
GPIOPin *mosi_{nullptr};
|
||||
GPIOPin *active_cs_{nullptr};
|
||||
bool force_sw_{false};
|
||||
#ifdef USE_SPI_ARDUINO_BACKEND
|
||||
SPIClass *hw_spi_{nullptr};
|
||||
#endif // USE_SPI_ARDUINO_BACKEND
|
||||
|
||||
@ -330,6 +330,7 @@ SPRINKLER_VALVE_SCHEMA = cv.Schema(
|
||||
SPRINKLER_CONTROLLER_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(Sprinkler),
|
||||
cv.Optional(CONF_NAME): cv.string,
|
||||
cv.Optional(CONF_AUTO_ADVANCE_SWITCH): cv.maybe_simple_value(
|
||||
switch.switch_schema(
|
||||
SprinklerControllerSwitch, entity_category=ENTITY_CATEGORY_CONFIG
|
||||
@ -424,7 +425,8 @@ SPRINKLER_CONTROLLER_SCHEMA = cv.Schema(
|
||||
): cv.positive_time_period_seconds,
|
||||
cv.Required(CONF_VALVES): cv.ensure_list(SPRINKLER_VALVE_SCHEMA),
|
||||
}
|
||||
).extend(cv.ENTITY_BASE_SCHEMA)
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.ensure_list(SPRINKLER_CONTROLLER_SCHEMA),
|
||||
@ -559,16 +561,19 @@ async def sprinkler_simple_action_to_code(config, action_id, template_arg, args)
|
||||
|
||||
async def to_code(config):
|
||||
for sprinkler_controller in config:
|
||||
if len(sprinkler_controller[CONF_VALVES]) > 1:
|
||||
var = cg.new_Pvariable(
|
||||
sprinkler_controller[CONF_ID],
|
||||
sprinkler_controller[CONF_MAIN_SWITCH][CONF_NAME],
|
||||
)
|
||||
var = cg.new_Pvariable(sprinkler_controller[CONF_ID])
|
||||
|
||||
if CONF_NAME in sprinkler_controller:
|
||||
cg.add(var.set_name(sprinkler_controller[CONF_NAME]))
|
||||
else:
|
||||
var = cg.new_Pvariable(
|
||||
sprinkler_controller[CONF_ID],
|
||||
sprinkler_controller[CONF_VALVES][0][CONF_VALVE_SWITCH][CONF_NAME],
|
||||
)
|
||||
if len(sprinkler_controller[CONF_VALVES]) > 1:
|
||||
name = sprinkler_controller[CONF_MAIN_SWITCH][CONF_NAME]
|
||||
else:
|
||||
name = sprinkler_controller[CONF_VALVES][0][CONF_VALVE_SWITCH][
|
||||
CONF_NAME
|
||||
]
|
||||
cg.add(var.set_name(name))
|
||||
|
||||
await cg.register_component(var, sprinkler_controller)
|
||||
|
||||
if len(sprinkler_controller[CONF_VALVES]) > 1:
|
||||
|
||||
@ -138,15 +138,7 @@ float SprinklerControllerSwitch::get_setup_priority() const { return setup_prior
|
||||
Trigger<> *SprinklerControllerSwitch::get_turn_on_trigger() const { return this->turn_on_trigger_; }
|
||||
Trigger<> *SprinklerControllerSwitch::get_turn_off_trigger() const { return this->turn_off_trigger_; }
|
||||
|
||||
void SprinklerControllerSwitch::setup() {
|
||||
this->state = this->get_initial_state_with_restore_mode().value_or(false);
|
||||
|
||||
if (this->state) {
|
||||
this->turn_on();
|
||||
} else {
|
||||
this->turn_off();
|
||||
}
|
||||
}
|
||||
void SprinklerControllerSwitch::setup() { this->state = this->get_initial_state_with_restore_mode().value_or(false); }
|
||||
|
||||
void SprinklerControllerSwitch::dump_config() { LOG_SWITCH("", "Sprinkler Switch", this); }
|
||||
|
||||
@ -378,10 +370,11 @@ SprinklerValveOperator *SprinklerValveRunRequest::valve_operator() { return this
|
||||
|
||||
SprinklerValveRunRequestOrigin SprinklerValveRunRequest::request_is_from() { return this->origin_; }
|
||||
|
||||
Sprinkler::Sprinkler() {}
|
||||
Sprinkler::Sprinkler(const std::string &name) : EntityBase(name) {}
|
||||
|
||||
void Sprinkler::setup() { this->all_valves_off_(true); }
|
||||
void Sprinkler::setup() {
|
||||
this->timer_.push_back({this->name_ + "sm", false, 0, 0, std::bind(&Sprinkler::sm_timer_callback_, this)});
|
||||
this->timer_.push_back({this->name_ + "vs", false, 0, 0, std::bind(&Sprinkler::valve_selection_callback_, this)});
|
||||
this->all_valves_off_(true);
|
||||
}
|
||||
|
||||
void Sprinkler::loop() {
|
||||
for (auto &p : this->pump_) {
|
||||
@ -1218,8 +1211,6 @@ SprinklerSwitch *Sprinkler::valve_pump_switch_by_pump_index(size_t pump_index) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t Sprinkler::hash_base() { return 3129891955UL; }
|
||||
|
||||
bool Sprinkler::valve_is_enabled_(const size_t valve_number) {
|
||||
if (this->is_a_valid_valve(valve_number)) {
|
||||
if (this->valve_[valve_number].enable_switch != nullptr) {
|
||||
|
||||
@ -201,15 +201,14 @@ class SprinklerValveRunRequest {
|
||||
SprinklerValveRunRequestOrigin origin_{USER};
|
||||
};
|
||||
|
||||
class Sprinkler : public Component, public EntityBase {
|
||||
class Sprinkler : public Component {
|
||||
public:
|
||||
Sprinkler();
|
||||
Sprinkler(const std::string &name);
|
||||
|
||||
void setup() override;
|
||||
void loop() override;
|
||||
void dump_config() override;
|
||||
|
||||
void set_name(const std::string &name) { this->name_ = name; }
|
||||
|
||||
/// add a valve to the controller
|
||||
void add_valve(SprinklerControllerSwitch *valve_sw, SprinklerControllerSwitch *enable_sw = nullptr);
|
||||
|
||||
@ -423,8 +422,6 @@ class Sprinkler : public Component, public EntityBase {
|
||||
SprinklerSwitch *valve_pump_switch_by_pump_index(size_t pump_index);
|
||||
|
||||
protected:
|
||||
uint32_t hash_base() override;
|
||||
|
||||
/// returns true if valve number is enabled
|
||||
bool valve_is_enabled_(size_t valve_number);
|
||||
|
||||
@ -528,6 +525,8 @@ class Sprinkler : public Component, public EntityBase {
|
||||
uint32_t start_delay_{0};
|
||||
uint32_t stop_delay_{0};
|
||||
|
||||
std::string name_;
|
||||
|
||||
/// Sprinkler controller state
|
||||
SprinklerState state_{IDLE};
|
||||
|
||||
@ -574,9 +573,7 @@ class Sprinkler : public Component, public EntityBase {
|
||||
std::vector<SprinklerValveOperator> valve_op_{2};
|
||||
|
||||
/// Valve control timers
|
||||
std::vector<SprinklerTimer> timer_{
|
||||
{this->name_ + "sm", false, 0, 0, std::bind(&Sprinkler::sm_timer_callback_, this)},
|
||||
{this->name_ + "vs", false, 0, 0, std::bind(&Sprinkler::valve_selection_callback_, this)}};
|
||||
std::vector<SprinklerTimer> timer_{};
|
||||
|
||||
/// Other Sprinkler instances we should be aware of (used to check if pumps are in use)
|
||||
std::vector<Sprinkler *> other_controllers_;
|
||||
|
||||
@ -27,6 +27,7 @@ MODELS = {
|
||||
"SSD1306_96X16": SSD1306Model.SSD1306_MODEL_96_16,
|
||||
"SSD1306_64X48": SSD1306Model.SSD1306_MODEL_64_48,
|
||||
"SSD1306_64X32": SSD1306Model.SSD1306_MODEL_64_32,
|
||||
"SSD1306_72X40": SSD1306Model.SSD1306_MODEL_72_40,
|
||||
"SH1106_128X32": SSD1306Model.SH1106_MODEL_128_32,
|
||||
"SH1106_128X64": SSD1306Model.SH1106_MODEL_128_64,
|
||||
"SH1106_96X16": SSD1306Model.SH1106_MODEL_96_16,
|
||||
|
||||
@ -59,6 +59,7 @@ void SSD1306::setup() {
|
||||
// Set Y offset (0xD3)
|
||||
this->command(SSD1306_COMMAND_SET_DISPLAY_OFFSET_Y);
|
||||
this->command(0x00 + this->offset_y_);
|
||||
|
||||
// Set start line at line 0 (0x40)
|
||||
this->command(SSD1306_COMMAND_SET_START_LINE | 0x00);
|
||||
|
||||
@ -100,6 +101,7 @@ void SSD1306::setup() {
|
||||
case SH1107_MODEL_128_64:
|
||||
case SSD1305_MODEL_128_32:
|
||||
case SSD1305_MODEL_128_64:
|
||||
case SSD1306_MODEL_72_40:
|
||||
this->command(0x12);
|
||||
break;
|
||||
}
|
||||
@ -118,6 +120,9 @@ void SSD1306::setup() {
|
||||
case SH1107_MODEL_128_64:
|
||||
this->command(0x35);
|
||||
break;
|
||||
case SSD1306_MODEL_72_40:
|
||||
this->command(0x20);
|
||||
break;
|
||||
default:
|
||||
this->command(0x00);
|
||||
break;
|
||||
@ -156,6 +161,10 @@ void SSD1306::display() {
|
||||
this->command(0x20 + this->offset_x_);
|
||||
this->command(0x20 + this->offset_x_ + this->get_width_internal() - 1);
|
||||
break;
|
||||
case SSD1306_MODEL_72_40:
|
||||
this->command(0x1C + this->offset_x_);
|
||||
this->command(0x1C + this->offset_x_ + this->get_width_internal() - 1);
|
||||
break;
|
||||
default:
|
||||
this->command(0 + this->offset_x_); // Page start address, 0
|
||||
this->command(this->get_width_internal() + this->offset_x_ - 1);
|
||||
@ -225,6 +234,8 @@ int SSD1306::get_height_internal() {
|
||||
case SSD1306_MODEL_64_48:
|
||||
case SH1106_MODEL_64_48:
|
||||
return 48;
|
||||
case SSD1306_MODEL_72_40:
|
||||
return 40;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
@ -246,6 +257,8 @@ int SSD1306::get_width_internal() {
|
||||
case SH1106_MODEL_64_48:
|
||||
case SH1107_MODEL_128_64:
|
||||
return 64;
|
||||
case SSD1306_MODEL_72_40:
|
||||
return 72;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
@ -294,6 +307,8 @@ const char *SSD1306::model_str_() {
|
||||
return "SSD1306 96x16";
|
||||
case SSD1306_MODEL_64_48:
|
||||
return "SSD1306 64x48";
|
||||
case SSD1306_MODEL_72_40:
|
||||
return "SSD1306 72x40";
|
||||
case SH1106_MODEL_128_32:
|
||||
return "SH1106 128x32";
|
||||
case SH1106_MODEL_128_64:
|
||||
|
||||
@ -13,6 +13,7 @@ enum SSD1306Model {
|
||||
SSD1306_MODEL_96_16,
|
||||
SSD1306_MODEL_64_48,
|
||||
SSD1306_MODEL_64_32,
|
||||
SSD1306_MODEL_72_40,
|
||||
SH1106_MODEL_128_32,
|
||||
SH1106_MODEL_128_64,
|
||||
SH1106_MODEL_96_16,
|
||||
|
||||
@ -53,8 +53,14 @@ void HOT I2CSSD1306::write_display_data() {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
size_t block_size = 16;
|
||||
if ((this->get_buffer_length_() & 8) == 8) {
|
||||
// use smaller block size for e.g. 72x40 displays where buffer size is multiple of 8, not 16
|
||||
block_size = 8;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < this->get_buffer_length_();) {
|
||||
uint8_t data[16];
|
||||
uint8_t data[block_size];
|
||||
for (uint8_t &j : data)
|
||||
j = this->buffer_[i++];
|
||||
this->write_bytes(0x40, data, sizeof(data));
|
||||
|
||||
@ -74,7 +74,7 @@ void ST7920::goto_xy_(uint16_t x, uint16_t y) {
|
||||
|
||||
void HOT ST7920::write_display_data() {
|
||||
uint8_t i, j, b;
|
||||
for (j = 0; j < (uint8_t)(this->get_height_internal() / 2); j++) {
|
||||
for (j = 0; j < (uint8_t) (this->get_height_internal() / 2); j++) {
|
||||
this->goto_xy_(0, j);
|
||||
this->enable();
|
||||
for (i = 0; i < 16; i++) { // 16 bytes from line #0+
|
||||
|
||||
@ -1,26 +1,35 @@
|
||||
from esphome import pins
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import light
|
||||
from esphome.const import CONF_OUTPUT_ID, CONF_PIN
|
||||
from esphome.components import light, output
|
||||
from esphome.const import CONF_OUTPUT, CONF_OUTPUT_ID, CONF_PIN
|
||||
from .. import status_led_ns
|
||||
|
||||
AUTO_LOAD = ["output"]
|
||||
|
||||
StatusLEDLightOutput = status_led_ns.class_(
|
||||
"StatusLEDLightOutput", light.LightOutput, cg.Component
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = light.BINARY_LIGHT_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(StatusLEDLightOutput),
|
||||
cv.Required(CONF_PIN): pins.gpio_output_pin_schema,
|
||||
}
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
light.BINARY_LIGHT_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(StatusLEDLightOutput),
|
||||
cv.Optional(CONF_PIN): pins.gpio_output_pin_schema,
|
||||
cv.Optional(CONF_OUTPUT): cv.use_id(output.BinaryOutput),
|
||||
}
|
||||
),
|
||||
cv.has_at_least_one_key(CONF_PIN, CONF_OUTPUT),
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_OUTPUT_ID])
|
||||
pin = await cg.gpio_pin_expression(config[CONF_PIN])
|
||||
cg.add(var.set_pin(pin))
|
||||
if CONF_PIN in config:
|
||||
pin = await cg.gpio_pin_expression(config[CONF_PIN])
|
||||
cg.add(var.set_pin(pin))
|
||||
if CONF_OUTPUT in config:
|
||||
out = await cg.get_variable(config[CONF_OUTPUT])
|
||||
cg.add(var.set_output(out))
|
||||
await cg.register_component(var, config)
|
||||
# cg.add(cg.App.register_component(var))
|
||||
await light.register_light(var, config)
|
||||
|
||||
@ -15,10 +15,10 @@ void StatusLEDLightOutput::loop() {
|
||||
}
|
||||
|
||||
if ((new_state & STATUS_LED_ERROR) != 0u) {
|
||||
this->pin_->digital_write(millis() % 250u < 150u);
|
||||
this->output_state_(millis() % 250u < 150u);
|
||||
this->last_app_state_ = new_state;
|
||||
} else if ((new_state & STATUS_LED_WARNING) != 0u) {
|
||||
this->pin_->digital_write(millis() % 1500u < 250u);
|
||||
this->output_state_(millis() % 1500u < 250u);
|
||||
this->last_app_state_ = new_state;
|
||||
} else if (new_state != this->last_app_state_) {
|
||||
// if no error/warning -> restore light state or turn off
|
||||
@ -26,17 +26,16 @@ void StatusLEDLightOutput::loop() {
|
||||
|
||||
if (lightstate_)
|
||||
lightstate_->current_values_as_binary(&state);
|
||||
|
||||
this->pin_->digital_write(state);
|
||||
this->last_app_state_ = new_state;
|
||||
|
||||
ESP_LOGD(TAG, "Restoring light state %s", ONOFF(state));
|
||||
|
||||
this->output_state_(state);
|
||||
this->last_app_state_ = new_state;
|
||||
}
|
||||
}
|
||||
|
||||
void StatusLEDLightOutput::setup_state(light::LightState *state) {
|
||||
lightstate_ = state;
|
||||
ESP_LOGD(TAG, "'%s': Setting initital state", state->get_name().c_str());
|
||||
ESP_LOGD(TAG, "'%s': Setting initial state", state->get_name().c_str());
|
||||
this->write_state(state);
|
||||
}
|
||||
|
||||
@ -47,16 +46,18 @@ void StatusLEDLightOutput::write_state(light::LightState *state) {
|
||||
// if in warning/error, don't overwrite the status_led
|
||||
// once it is back to OK, the loop will restore the state
|
||||
if ((App.get_app_state() & (STATUS_LED_ERROR | STATUS_LED_WARNING)) == 0u) {
|
||||
this->pin_->digital_write(binary);
|
||||
ESP_LOGD(TAG, "'%s': Setting state %s", state->get_name().c_str(), ONOFF(binary));
|
||||
this->output_state_(binary);
|
||||
}
|
||||
}
|
||||
|
||||
void StatusLEDLightOutput::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up Status LED...");
|
||||
|
||||
this->pin_->setup();
|
||||
this->pin_->digital_write(false);
|
||||
if (this->pin_ != nullptr) {
|
||||
this->pin_->setup();
|
||||
this->pin_->digital_write(false);
|
||||
}
|
||||
}
|
||||
|
||||
void StatusLEDLightOutput::dump_config() {
|
||||
@ -64,5 +65,12 @@ void StatusLEDLightOutput::dump_config() {
|
||||
LOG_PIN(" Pin: ", this->pin_);
|
||||
}
|
||||
|
||||
void StatusLEDLightOutput::output_state_(bool state) {
|
||||
if (this->pin_ != nullptr)
|
||||
this->pin_->digital_write(state);
|
||||
if (this->output_ != nullptr)
|
||||
this->output_->set_state(state);
|
||||
}
|
||||
|
||||
} // namespace status_led
|
||||
} // namespace esphome
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/components/light/light_output.h"
|
||||
#include "esphome/components/output/binary_output.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace status_led {
|
||||
@ -10,6 +11,7 @@ namespace status_led {
|
||||
class StatusLEDLightOutput : public light::LightOutput, public Component {
|
||||
public:
|
||||
void set_pin(GPIOPin *pin) { pin_ = pin; }
|
||||
void set_output(output::BinaryOutput *output) { output_ = output; }
|
||||
|
||||
light::LightTraits get_traits() override {
|
||||
auto traits = light::LightTraits();
|
||||
@ -31,9 +33,11 @@ class StatusLEDLightOutput : public light::LightOutput, public Component {
|
||||
float get_loop_priority() const override { return 50.0f; }
|
||||
|
||||
protected:
|
||||
GPIOPin *pin_;
|
||||
GPIOPin *pin_{nullptr};
|
||||
output::BinaryOutput *output_{nullptr};
|
||||
light::LightState *lightstate_{};
|
||||
uint32_t last_app_state_{0xFFFF};
|
||||
void output_state_(bool state);
|
||||
};
|
||||
|
||||
} // namespace status_led
|
||||
|
||||
@ -269,7 +269,7 @@ struct SunAtLocation {
|
||||
num_t jd = julian_day(date) + added_d;
|
||||
|
||||
num_t eot = SunAtTime(jd).equation_of_time() * 240;
|
||||
time_t new_timestamp = (time_t)(date.timestamp + added_d * 86400 - eot);
|
||||
time_t new_timestamp = (time_t) (date.timestamp + added_d * 86400 - eot);
|
||||
return time::ESPTime::from_epoch_utc(new_timestamp);
|
||||
}
|
||||
};
|
||||
|
||||
@ -92,7 +92,7 @@ def switch_schema(
|
||||
device_class: str = _UNDEF,
|
||||
icon: str = _UNDEF,
|
||||
block_inverted: bool = False,
|
||||
default_restore_mode: str = "RESTORE_DEFAULT_OFF",
|
||||
default_restore_mode: str = "ALWAYS_OFF",
|
||||
):
|
||||
schema = _SWITCH_SCHEMA.extend(
|
||||
{
|
||||
|
||||
@ -6,8 +6,7 @@ namespace switch_ {
|
||||
|
||||
static const char *const TAG = "switch";
|
||||
|
||||
Switch::Switch(const std::string &name) : EntityBase(name), state(false) {}
|
||||
Switch::Switch() : Switch("") {}
|
||||
Switch::Switch() : state(false) {}
|
||||
|
||||
void Switch::turn_on() {
|
||||
ESP_LOGD(TAG, "'%s' Turning ON.", this->get_name().c_str());
|
||||
|
||||
@ -32,7 +32,6 @@ enum SwitchRestoreMode {
|
||||
class Switch : public EntityBase {
|
||||
public:
|
||||
explicit Switch();
|
||||
explicit Switch(const std::string &name);
|
||||
|
||||
/** Publish a state to the front-end from the back-end.
|
||||
*
|
||||
|
||||
@ -42,6 +42,9 @@ void SX1509Component::dump_config() {
|
||||
|
||||
void SX1509Component::loop() {
|
||||
if (this->has_keypad_) {
|
||||
if (millis() - this->last_loop_timestamp_ < min_loop_period_)
|
||||
return;
|
||||
this->last_loop_timestamp_ = millis();
|
||||
uint16_t key_data = this->read_key_data();
|
||||
for (auto *binary_sensor : this->keypad_binary_sensors_)
|
||||
binary_sensor->process(key_data);
|
||||
|
||||
@ -69,6 +69,9 @@ class SX1509Component : public Component, public i2c::I2CDevice {
|
||||
uint8_t debounce_time_ = 1;
|
||||
std::vector<SX1509Processor *> keypad_binary_sensors_;
|
||||
|
||||
uint32_t last_loop_timestamp_ = 0;
|
||||
const uint32_t min_loop_period_ = 15; // ms
|
||||
|
||||
void setup_keypad_();
|
||||
void set_debounce_config_(uint8_t config_value);
|
||||
void set_debounce_time_(uint8_t time);
|
||||
|
||||
@ -13,7 +13,7 @@ bool SX1509GPIOPin::digital_read() { return this->parent_->digital_read(this->pi
|
||||
void SX1509GPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); }
|
||||
std::string SX1509GPIOPin::dump_summary() const {
|
||||
char buffer[32];
|
||||
snprintf(buffer, sizeof(buffer), "%u via MCP23016", pin_);
|
||||
snprintf(buffer, sizeof(buffer), "%u via sx1509", pin_);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
@ -287,7 +287,7 @@ void TCS34725Component::update() {
|
||||
}
|
||||
|
||||
// calculate register value from timing
|
||||
uint8_t regval_atime = (uint8_t)(256.f - integration_time_next / 2.4f);
|
||||
uint8_t regval_atime = (uint8_t) (256.f - integration_time_next / 2.4f);
|
||||
ESP_LOGD(TAG, "Integration time: %.1fms, ideal: %.1fms regval_new %d Gain: %.f Clear channel raw: %d gain reg: %d",
|
||||
this->integration_time_, integration_time_next, regval_atime, this->gain_, raw_c, this->gain_reg_);
|
||||
|
||||
|
||||
@ -43,9 +43,9 @@ def validate(config):
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
select.SELECT_SCHEMA.extend(
|
||||
select.select_schema(TemplateSelect)
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(TemplateSelect),
|
||||
cv.Required(CONF_OPTIONS): cv.All(
|
||||
cv.ensure_list(cv.string_strict), cv.Length(min=1)
|
||||
),
|
||||
@ -55,7 +55,8 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.Optional(CONF_INITIAL_OPTION): cv.string_strict,
|
||||
cv.Optional(CONF_RESTORE_VALUE): cv.boolean,
|
||||
}
|
||||
).extend(cv.polling_component_schema("60s")),
|
||||
)
|
||||
.extend(cv.polling_component_schema("60s")),
|
||||
validate,
|
||||
)
|
||||
|
||||
|
||||
@ -6,9 +6,6 @@ namespace text_sensor {
|
||||
|
||||
static const char *const TAG = "text_sensor";
|
||||
|
||||
TextSensor::TextSensor() : TextSensor("") {}
|
||||
TextSensor::TextSensor(const std::string &name) : EntityBase(name) {}
|
||||
|
||||
void TextSensor::publish_state(const std::string &state) {
|
||||
this->raw_state = state;
|
||||
this->raw_callback_.call(state);
|
||||
|
||||
@ -30,9 +30,6 @@ namespace text_sensor {
|
||||
|
||||
class TextSensor : public EntityBase {
|
||||
public:
|
||||
explicit TextSensor();
|
||||
explicit TextSensor(const std::string &name);
|
||||
|
||||
/// Getter-syntax for .state.
|
||||
std::string get_state() const;
|
||||
/// Getter-syntax for .raw_state
|
||||
@ -61,6 +58,10 @@ class TextSensor : public EntityBase {
|
||||
|
||||
// ========== INTERNAL METHODS ==========
|
||||
// (In most use cases you won't need these)
|
||||
/** Override this method to set the unique ID of this sensor.
|
||||
*
|
||||
* @deprecated Do not use for new sensors, a suitable unique ID is automatically generated (2023.4).
|
||||
*/
|
||||
virtual std::string unique_id();
|
||||
|
||||
bool has_state();
|
||||
|
||||
@ -16,6 +16,7 @@ time_based_ns = cg.esphome_ns.namespace("time_based")
|
||||
TimeBasedCover = time_based_ns.class_("TimeBasedCover", cover.Cover, cg.Component)
|
||||
|
||||
CONF_HAS_BUILT_IN_ENDSTOP = "has_built_in_endstop"
|
||||
CONF_MANUAL_CONTROL = "manual_control"
|
||||
|
||||
CONFIG_SCHEMA = cover.COVER_SCHEMA.extend(
|
||||
{
|
||||
@ -26,6 +27,7 @@ CONFIG_SCHEMA = cover.COVER_SCHEMA.extend(
|
||||
cv.Required(CONF_CLOSE_ACTION): automation.validate_automation(single=True),
|
||||
cv.Required(CONF_CLOSE_DURATION): cv.positive_time_period_milliseconds,
|
||||
cv.Optional(CONF_HAS_BUILT_IN_ENDSTOP, default=False): cv.boolean,
|
||||
cv.Optional(CONF_MANUAL_CONTROL, default=False): cv.boolean,
|
||||
cv.Optional(CONF_ASSUMED_STATE, default=True): cv.boolean,
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
@ -51,4 +53,5 @@ async def to_code(config):
|
||||
)
|
||||
|
||||
cg.add(var.set_has_built_in_endstop(config[CONF_HAS_BUILT_IN_ENDSTOP]))
|
||||
cg.add(var.set_manual_control(config[CONF_MANUAL_CONTROL]))
|
||||
cg.add(var.set_assumed_state(config[CONF_ASSUMED_STATE]))
|
||||
|
||||
@ -79,6 +79,14 @@ void TimeBasedCover::control(const CoverCall &call) {
|
||||
auto pos = *call.get_position();
|
||||
if (pos == this->position) {
|
||||
// already at target
|
||||
if (this->manual_control_ && (pos == COVER_OPEN || pos == COVER_CLOSED)) {
|
||||
// for covers with manual control switch, we can't rely on the computed position, so if
|
||||
// the command triggered again, we'll assume it's in the opposite direction anyway.
|
||||
auto op = pos == COVER_CLOSED ? COVER_OPERATION_CLOSING : COVER_OPERATION_OPENING;
|
||||
this->position = pos == COVER_CLOSED ? COVER_OPEN : COVER_CLOSED;
|
||||
this->target_position_ = pos;
|
||||
this->start_direction_(op);
|
||||
}
|
||||
// for covers with built in end stop, we should send the command again
|
||||
if (this->has_built_in_endstop_ && (pos == COVER_OPEN || pos == COVER_CLOSED)) {
|
||||
auto op = pos == COVER_CLOSED ? COVER_OPERATION_CLOSING : COVER_OPERATION_OPENING;
|
||||
|
||||
@ -21,6 +21,7 @@ class TimeBasedCover : public cover::Cover, public Component {
|
||||
void set_close_duration(uint32_t close_duration) { this->close_duration_ = close_duration; }
|
||||
cover::CoverTraits get_traits() override;
|
||||
void set_has_built_in_endstop(bool value) { this->has_built_in_endstop_ = value; }
|
||||
void set_manual_control(bool value) { this->manual_control_ = value; }
|
||||
void set_assumed_state(bool value) { this->assumed_state_ = value; }
|
||||
|
||||
protected:
|
||||
@ -44,6 +45,7 @@ class TimeBasedCover : public cover::Cover, public Component {
|
||||
uint32_t last_publish_time_{0};
|
||||
float target_position_{0};
|
||||
bool has_built_in_endstop_{false};
|
||||
bool manual_control_{false};
|
||||
bool assumed_state_{false};
|
||||
cover::CoverOperation last_operation_{cover::COVER_OPERATION_OPENING};
|
||||
};
|
||||
|
||||
@ -168,7 +168,7 @@ uint8_t TM1637Display::get_keys() {
|
||||
// Bit | 7 6 5 4 3 2 1 0
|
||||
// ------+------------------------
|
||||
// To | 0 0 0 0 K2 S2 S1 S0
|
||||
key_code = (uint8_t)((key_code & 0x80) >> 7 | (key_code & 0x40) >> 5 | (key_code & 0x20) >> 3 | (key_code & 0x08));
|
||||
key_code = (uint8_t) ((key_code & 0x80) >> 7 | (key_code & 0x40) >> 5 | (key_code & 0x20) >> 3 | (key_code & 0x08));
|
||||
}
|
||||
return key_code;
|
||||
}
|
||||
|
||||
@ -140,7 +140,7 @@ void TM1638Component::set_intensity(uint8_t brightness_level) {
|
||||
this->send_command_(TM1638_REGISTER_FIXEDADDRESS);
|
||||
|
||||
if (brightness_level > 0) {
|
||||
this->send_command_((uint8_t)(TM1638_REGISTER_DISPLAYON | intensity_));
|
||||
this->send_command_((uint8_t) (TM1638_REGISTER_DISPLAYON | intensity_));
|
||||
} else {
|
||||
this->send_command_(TM1638_REGISTER_DISPLAYOFF);
|
||||
}
|
||||
|
||||
@ -25,15 +25,18 @@ def ensure_option_map(value):
|
||||
return value
|
||||
|
||||
|
||||
CONFIG_SCHEMA = select.SELECT_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(TuyaSelect),
|
||||
cv.GenerateID(CONF_TUYA_ID): cv.use_id(Tuya),
|
||||
cv.Required(CONF_ENUM_DATAPOINT): cv.uint8_t,
|
||||
cv.Required(CONF_OPTIONS): ensure_option_map,
|
||||
cv.Optional(CONF_OPTIMISTIC, default=False): cv.boolean,
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
CONFIG_SCHEMA = (
|
||||
select.select_schema(TuyaSelect)
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(CONF_TUYA_ID): cv.use_id(Tuya),
|
||||
cv.Required(CONF_ENUM_DATAPOINT): cv.uint8_t,
|
||||
cv.Required(CONF_OPTIONS): ensure_option_map,
|
||||
cv.Optional(CONF_OPTIMISTIC, default=False): cv.boolean,
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
|
||||
@ -381,8 +381,8 @@ void Tuya::handle_datapoints_(const uint8_t *buffer, size_t len) {
|
||||
}
|
||||
|
||||
void Tuya::send_raw_command_(TuyaCommand command) {
|
||||
uint8_t len_hi = (uint8_t)(command.payload.size() >> 8);
|
||||
uint8_t len_lo = (uint8_t)(command.payload.size() & 0xFF);
|
||||
uint8_t len_hi = (uint8_t) (command.payload.size() >> 8);
|
||||
uint8_t len_lo = (uint8_t) (command.payload.size() & 0xFF);
|
||||
uint8_t version = 0;
|
||||
|
||||
this->last_command_timestamp_ = millis();
|
||||
|
||||
@ -100,8 +100,8 @@ void ESP32ArduinoUARTComponent::setup() {
|
||||
invert = true;
|
||||
if (rx_pin_ != nullptr && rx_pin_->is_inverted())
|
||||
invert = true;
|
||||
this->hw_serial_->begin(this->baud_rate_, get_config(), rx, tx, invert);
|
||||
this->hw_serial_->setRxBufferSize(this->rx_buffer_size_);
|
||||
this->hw_serial_->begin(this->baud_rate_, get_config(), rx, tx, invert);
|
||||
}
|
||||
|
||||
void ESP32ArduinoUARTComponent::dump_config() {
|
||||
|
||||
@ -28,6 +28,9 @@ class ESP32ArduinoUARTComponent : public UARTComponent, public Component {
|
||||
|
||||
uint32_t get_config();
|
||||
|
||||
HardwareSerial *get_hw_serial() { return this->hw_serial_; }
|
||||
uint8_t get_hw_serial_number() { return this->number_; }
|
||||
|
||||
protected:
|
||||
void check_logger_conflict() override;
|
||||
|
||||
|
||||
@ -79,7 +79,12 @@ void IDFUARTComponent::setup() {
|
||||
return;
|
||||
}
|
||||
|
||||
err = uart_driver_install(this->uart_num_, this->rx_buffer_size_, 0, 0, nullptr, 0);
|
||||
err = uart_driver_install(this->uart_num_, /* UART RX ring buffer size. */ this->rx_buffer_size_,
|
||||
/* UART TX ring buffer size. If set to zero, driver will not use TX buffer, TX function will
|
||||
block task until all data have been sent out.*/
|
||||
0,
|
||||
/* UART event queue size/depth. */ 20, &(this->uart_event_queue_),
|
||||
/* Flags used to allocate the interrupt. */ 0);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "uart_driver_install failed: %s", esp_err_to_name(err));
|
||||
this->mark_failed();
|
||||
|
||||
@ -23,9 +23,13 @@ class IDFUARTComponent : public UARTComponent, public Component {
|
||||
int available() override;
|
||||
void flush() override;
|
||||
|
||||
uint8_t get_hw_serial_number() { return this->uart_num_; }
|
||||
QueueHandle_t *get_uart_event_queue() { return &this->uart_event_queue_; }
|
||||
|
||||
protected:
|
||||
void check_logger_conflict() override;
|
||||
uart_port_t uart_num_;
|
||||
QueueHandle_t uart_event_queue_;
|
||||
uart_config_t get_config_();
|
||||
SemaphoreHandle_t lock_;
|
||||
|
||||
|
||||
@ -30,6 +30,9 @@ class RP2040UartComponent : public UARTComponent, public Component {
|
||||
|
||||
uint16_t get_config();
|
||||
|
||||
bool is_hw_serial() { return this->hw_serial_; }
|
||||
HardwareSerial *get_hw_serial() { return this->serial_; }
|
||||
|
||||
protected:
|
||||
void check_logger_conflict() override {}
|
||||
bool hw_serial_{false};
|
||||
|
||||
@ -12,7 +12,7 @@ static inline uint16_t get_u16(std::vector<uint8_t> &message, int start) {
|
||||
}
|
||||
|
||||
static inline int16_t get_i16(std::vector<uint8_t> &message, int start) {
|
||||
return (int16_t)((message[start + 1] << 8) + message[start]);
|
||||
return (int16_t) ((message[start + 1] << 8) + message[start]);
|
||||
}
|
||||
|
||||
void DeltaSolBSPlusSensor::dump_config() {
|
||||
|
||||
@ -88,7 +88,7 @@ void VL53L0XSensor::setup() {
|
||||
|
||||
this->timeout_start_us_ = micros();
|
||||
while (reg(0x83).get() == 0x00) {
|
||||
if (this->timeout_us_ > 0 && ((uint16_t)(micros() - this->timeout_start_us_) > this->timeout_us_)) {
|
||||
if (this->timeout_us_ > 0 && ((uint16_t) (micros() - this->timeout_start_us_) > this->timeout_us_)) {
|
||||
ESP_LOGE(TAG, "'%s' - setup timeout", this->name_.c_str());
|
||||
this->mark_failed();
|
||||
return;
|
||||
|
||||
57
esphome/components/voice_assistant/__init__.py
Normal file
57
esphome/components/voice_assistant/__init__.py
Normal file
@ -0,0 +1,57 @@
|
||||
import esphome.config_validation as cv
|
||||
import esphome.codegen as cg
|
||||
|
||||
from esphome.const import CONF_ID, CONF_MICROPHONE
|
||||
from esphome import automation
|
||||
from esphome.automation import register_action
|
||||
from esphome.components import microphone
|
||||
|
||||
AUTO_LOAD = ["socket"]
|
||||
DEPENDENCIES = ["api", "microphone"]
|
||||
|
||||
CODEOWNERS = ["@jesserockz"]
|
||||
|
||||
voice_assistant_ns = cg.esphome_ns.namespace("voice_assistant")
|
||||
VoiceAssistant = voice_assistant_ns.class_("VoiceAssistant", cg.Component)
|
||||
|
||||
StartAction = voice_assistant_ns.class_(
|
||||
"StartAction", automation.Action, cg.Parented.template(VoiceAssistant)
|
||||
)
|
||||
StopAction = voice_assistant_ns.class_(
|
||||
"StopAction", automation.Action, cg.Parented.template(VoiceAssistant)
|
||||
)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(VoiceAssistant),
|
||||
cv.GenerateID(CONF_MICROPHONE): cv.use_id(microphone.Microphone),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
|
||||
mic = await cg.get_variable(config[CONF_MICROPHONE])
|
||||
cg.add(var.set_microphone(mic))
|
||||
|
||||
cg.add_define("USE_VOICE_ASSISTANT")
|
||||
|
||||
|
||||
VOICE_ASSISTANT_ACTION_SCHEMA = cv.Schema({cv.GenerateID(): cv.use_id(VoiceAssistant)})
|
||||
|
||||
|
||||
@register_action("voice_assistant.start", StartAction, VOICE_ASSISTANT_ACTION_SCHEMA)
|
||||
async def voice_assistant_listen_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
|
||||
|
||||
|
||||
@register_action("voice_assistant.stop", StopAction, VOICE_ASSISTANT_ACTION_SCHEMA)
|
||||
async def voice_assistant_stop_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
|
||||
148
esphome/components/voice_assistant/voice_assistant.cpp
Normal file
148
esphome/components/voice_assistant/voice_assistant.cpp
Normal file
@ -0,0 +1,148 @@
|
||||
#include "voice_assistant.h"
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace voice_assistant {
|
||||
|
||||
static const char *const TAG = "voice_assistant";
|
||||
|
||||
float VoiceAssistant::get_setup_priority() const { return setup_priority::AFTER_CONNECTION; }
|
||||
|
||||
void VoiceAssistant::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up Voice Assistant...");
|
||||
|
||||
global_voice_assistant = this;
|
||||
|
||||
this->socket_ = socket::socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
|
||||
if (socket_ == nullptr) {
|
||||
ESP_LOGW(TAG, "Could not create socket.");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
int enable = 1;
|
||||
int err = socket_->setsockopt(SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
|
||||
if (err != 0) {
|
||||
ESP_LOGW(TAG, "Socket unable to set reuseaddr: errno %d", err);
|
||||
// we can still continue
|
||||
}
|
||||
err = socket_->setblocking(false);
|
||||
if (err != 0) {
|
||||
ESP_LOGW(TAG, "Socket unable to set nonblocking mode: errno %d", err);
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
this->mic_->add_data_callback([this](const std::vector<uint8_t> &data) {
|
||||
if (!this->running_) {
|
||||
return;
|
||||
}
|
||||
this->socket_->sendto(data.data(), data.size(), 0, (struct sockaddr *) &this->dest_addr_, sizeof(this->dest_addr_));
|
||||
});
|
||||
}
|
||||
|
||||
void VoiceAssistant::start(struct sockaddr_storage *addr, uint16_t port) {
|
||||
ESP_LOGD(TAG, "Starting...");
|
||||
|
||||
memcpy(&this->dest_addr_, addr, sizeof(this->dest_addr_));
|
||||
if (this->dest_addr_.ss_family == AF_INET) {
|
||||
((struct sockaddr_in *) &this->dest_addr_)->sin_port = htons(port);
|
||||
}
|
||||
#if LWIP_IPV6
|
||||
else if (this->dest_addr_.ss_family == AF_INET6) {
|
||||
((struct sockaddr_in6 *) &this->dest_addr_)->sin6_port = htons(port);
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
ESP_LOGW(TAG, "Unknown address family: %d", this->dest_addr_.ss_family);
|
||||
return;
|
||||
}
|
||||
this->running_ = true;
|
||||
this->mic_->start();
|
||||
}
|
||||
|
||||
void VoiceAssistant::request_start() {
|
||||
ESP_LOGD(TAG, "Requesting start...");
|
||||
api::global_api_server->start_voice_assistant();
|
||||
}
|
||||
|
||||
void VoiceAssistant::signal_stop() {
|
||||
ESP_LOGD(TAG, "Signaling stop...");
|
||||
this->mic_->stop();
|
||||
this->running_ = false;
|
||||
api::global_api_server->stop_voice_assistant();
|
||||
memset(&this->dest_addr_, 0, sizeof(this->dest_addr_));
|
||||
}
|
||||
|
||||
void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) {
|
||||
switch (msg.event_type) {
|
||||
case api::enums::VOICE_ASSISTANT_RUN_END:
|
||||
ESP_LOGD(TAG, "Voice Assistant ended.");
|
||||
break;
|
||||
case api::enums::VOICE_ASSISTANT_STT_END: {
|
||||
std::string text;
|
||||
for (auto arg : msg.data) {
|
||||
if (arg.name == "text") {
|
||||
text = std::move(arg.value);
|
||||
}
|
||||
}
|
||||
if (text.empty()) {
|
||||
ESP_LOGW(TAG, "No text in STT_END event.");
|
||||
return;
|
||||
}
|
||||
ESP_LOGD(TAG, "Speech recognised as: \"%s\"", text.c_str());
|
||||
// TODO `on_stt_end` trigger
|
||||
break;
|
||||
}
|
||||
case api::enums::VOICE_ASSISTANT_TTS_START: {
|
||||
std::string text;
|
||||
for (auto arg : msg.data) {
|
||||
if (arg.name == "text") {
|
||||
text = std::move(arg.value);
|
||||
}
|
||||
}
|
||||
if (text.empty()) {
|
||||
ESP_LOGW(TAG, "No text in TTS_START event.");
|
||||
return;
|
||||
}
|
||||
ESP_LOGD(TAG, "Response: \"%s\"", text.c_str());
|
||||
// TODO `on_tts_start` trigger
|
||||
break;
|
||||
}
|
||||
case api::enums::VOICE_ASSISTANT_TTS_END: {
|
||||
std::string url;
|
||||
for (auto arg : msg.data) {
|
||||
if (arg.name == "url") {
|
||||
url = std::move(arg.value);
|
||||
}
|
||||
}
|
||||
if (url.empty()) {
|
||||
ESP_LOGW(TAG, "No url in TTS_END event.");
|
||||
return;
|
||||
}
|
||||
ESP_LOGD(TAG, "Response URL: \"%s\"", url.c_str());
|
||||
// TODO `on_tts_end` trigger
|
||||
break;
|
||||
}
|
||||
case api::enums::VOICE_ASSISTANT_ERROR: {
|
||||
std::string code = "";
|
||||
std::string message = "";
|
||||
for (auto arg : msg.data) {
|
||||
if (arg.name == "code") {
|
||||
code = std::move(arg.value);
|
||||
} else if (arg.name == "message") {
|
||||
message = std::move(arg.value);
|
||||
}
|
||||
}
|
||||
ESP_LOGE(TAG, "Error: %s - %s", code.c_str(), message.c_str());
|
||||
// TODO `on_error` trigger
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
VoiceAssistant *global_voice_assistant = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
||||
} // namespace voice_assistant
|
||||
} // namespace esphome
|
||||
50
esphome/components/voice_assistant/voice_assistant.h
Normal file
50
esphome/components/voice_assistant/voice_assistant.h
Normal file
@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
#include "esphome/components/api/api_pb2.h"
|
||||
#include "esphome/components/api/api_server.h"
|
||||
#include "esphome/components/microphone/microphone.h"
|
||||
#include "esphome/components/socket/socket.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace voice_assistant {
|
||||
|
||||
class VoiceAssistant : public Component {
|
||||
public:
|
||||
void setup() override;
|
||||
float get_setup_priority() const override;
|
||||
void start(struct sockaddr_storage *addr, uint16_t port);
|
||||
|
||||
void set_microphone(microphone::Microphone *mic) { this->mic_ = mic; }
|
||||
|
||||
void request_start();
|
||||
void signal_stop();
|
||||
|
||||
void on_event(const api::VoiceAssistantEventResponse &msg);
|
||||
|
||||
protected:
|
||||
std::unique_ptr<socket::Socket> socket_ = nullptr;
|
||||
struct sockaddr_storage dest_addr_;
|
||||
|
||||
microphone::Microphone *mic_{nullptr};
|
||||
|
||||
bool running_{false};
|
||||
};
|
||||
|
||||
template<typename... Ts> class StartAction : public Action<Ts...>, public Parented<VoiceAssistant> {
|
||||
public:
|
||||
void play(Ts... x) override { this->parent_->request_start(); }
|
||||
};
|
||||
|
||||
template<typename... Ts> class StopAction : public Action<Ts...>, public Parented<VoiceAssistant> {
|
||||
public:
|
||||
void play(Ts... x) override { this->parent_->signal_stop(); }
|
||||
};
|
||||
|
||||
extern VoiceAssistant *global_voice_assistant; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
||||
} // namespace voice_assistant
|
||||
} // namespace esphome
|
||||
@ -29,6 +29,7 @@ WaveshareEPaper2P7In = waveshare_epaper_ns.class_(
|
||||
WaveshareEPaper2P9InB = waveshare_epaper_ns.class_(
|
||||
"WaveshareEPaper2P9InB", WaveshareEPaper
|
||||
)
|
||||
GDEY029T94 = waveshare_epaper_ns.class_("GDEY029T94", WaveshareEPaper)
|
||||
WaveshareEPaper4P2In = waveshare_epaper_ns.class_(
|
||||
"WaveshareEPaper4P2In", WaveshareEPaper
|
||||
)
|
||||
@ -73,6 +74,7 @@ MODELS = {
|
||||
"2.13in-ttgo-b74": ("a", WaveshareEPaperTypeAModel.TTGO_EPAPER_2_13_IN_B74),
|
||||
"2.90in": ("a", WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_9_IN),
|
||||
"2.90inv2": ("a", WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_9_IN_V2),
|
||||
"gdey029t94": ("c", GDEY029T94),
|
||||
"2.70in": ("b", WaveshareEPaper2P7In),
|
||||
"2.90in-b": ("b", WaveshareEPaper2P9InB),
|
||||
"4.20in": ("b", WaveshareEPaper4P2In),
|
||||
|
||||
@ -137,7 +137,7 @@ void HOT WaveshareEPaper::draw_absolute_pixel_internal(int x, int y, Color color
|
||||
if (x >= this->get_width_internal() || y >= this->get_height_internal() || x < 0 || y < 0)
|
||||
return;
|
||||
|
||||
const uint32_t pos = (x + y * this->get_width_internal()) / 8u;
|
||||
const uint32_t pos = (x + y * this->get_width_controller()) / 8u;
|
||||
const uint8_t subpos = x & 0x07;
|
||||
// flip logic
|
||||
if (!color.is_on()) {
|
||||
@ -146,7 +146,9 @@ void HOT WaveshareEPaper::draw_absolute_pixel_internal(int x, int y, Color color
|
||||
this->buffer_[pos] &= ~(0x80 >> subpos);
|
||||
}
|
||||
}
|
||||
uint32_t WaveshareEPaper::get_buffer_length_() { return this->get_width_internal() * this->get_height_internal() / 8u; }
|
||||
uint32_t WaveshareEPaper::get_buffer_length_() {
|
||||
return this->get_width_controller() * this->get_height_internal() / 8u;
|
||||
}
|
||||
void WaveshareEPaper::start_command_() {
|
||||
this->dc_pin_->digital_write(false);
|
||||
this->enable();
|
||||
@ -291,7 +293,7 @@ void HOT WaveshareEPaperTypeA::display() {
|
||||
// COMMAND SET RAM X ADDRESS START END POSITION
|
||||
this->command(0x44);
|
||||
this->data(0x00);
|
||||
this->data((this->get_width_internal() - 1) >> 3);
|
||||
this->data((this->get_width_controller() - 1) >> 3);
|
||||
// COMMAND SET RAM Y ADDRESS START END POSITION
|
||||
this->command(0x45);
|
||||
this->data(this->get_height_internal() - 1);
|
||||
@ -392,12 +394,26 @@ int WaveshareEPaperTypeA::get_width_internal() {
|
||||
case TTGO_EPAPER_2_13_IN_B73:
|
||||
case TTGO_EPAPER_2_13_IN_B74:
|
||||
case TTGO_EPAPER_2_13_IN_B1:
|
||||
return 122;
|
||||
case WAVESHARE_EPAPER_2_9_IN:
|
||||
case WAVESHARE_EPAPER_2_9_IN_V2:
|
||||
return 128;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
// The controller of the 2.13" displays has a buffer larger than screen size
|
||||
int WaveshareEPaperTypeA::get_width_controller() {
|
||||
switch (this->model_) {
|
||||
case WAVESHARE_EPAPER_2_13_IN:
|
||||
case TTGO_EPAPER_2_13_IN:
|
||||
case TTGO_EPAPER_2_13_IN_B73:
|
||||
case TTGO_EPAPER_2_13_IN_B74:
|
||||
case TTGO_EPAPER_2_13_IN_B1:
|
||||
return 128;
|
||||
default:
|
||||
return this->get_width_internal();
|
||||
}
|
||||
}
|
||||
int WaveshareEPaperTypeA::get_height_internal() {
|
||||
switch (this->model_) {
|
||||
case WAVESHARE_EPAPER_1_54_IN:
|
||||
@ -663,6 +679,90 @@ void WaveshareEPaper2P9InB::dump_config() {
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
}
|
||||
|
||||
// ========================================================
|
||||
// Good Display 2.9in black/white/grey
|
||||
// Datasheet:
|
||||
// - https://v4.cecdn.yun300.cn/100001_1909185148/SSD1680.pdf
|
||||
// - https://github.com/adafruit/Adafruit_EPD/blob/master/src/panels/ThinkInk_290_Grayscale4_T5.h
|
||||
// ========================================================
|
||||
|
||||
void GDEY029T94::initialize() {
|
||||
// from https://www.waveshare.com/w/upload/b/bb/2.9inch-e-paper-b-specification.pdf, page 37
|
||||
// EPD hardware init start
|
||||
this->reset_();
|
||||
|
||||
// COMMAND POWER SETTINGS
|
||||
this->command(0x00);
|
||||
this->data(0x03);
|
||||
this->data(0x00);
|
||||
this->data(0x2b);
|
||||
this->data(0x2b);
|
||||
this->data(0x03); /* for b/w */
|
||||
|
||||
// COMMAND BOOSTER SOFT START
|
||||
this->command(0x06);
|
||||
this->data(0x17);
|
||||
this->data(0x17);
|
||||
this->data(0x17);
|
||||
|
||||
// COMMAND POWER ON
|
||||
this->command(0x04);
|
||||
this->wait_until_idle_();
|
||||
|
||||
// Not sure what this does but it's in the Adafruit EPD library
|
||||
this->command(0xFF);
|
||||
this->wait_until_idle_();
|
||||
|
||||
// COMMAND PANEL SETTING
|
||||
this->command(0x00);
|
||||
// 128x296 resolution: 10
|
||||
// LUT from OTP: 0
|
||||
// B/W mode (doesn't work): 1
|
||||
// scan-up: 1
|
||||
// shift-right: 1
|
||||
// booster ON: 1
|
||||
// no soft reset: 1
|
||||
this->data(0b10011111);
|
||||
|
||||
// COMMAND RESOLUTION SETTING
|
||||
// set to 128x296 by COMMAND PANEL SETTING
|
||||
|
||||
// COMMAND VCOM AND DATA INTERVAL SETTING
|
||||
// use defaults for white border and ESPHome image polarity
|
||||
|
||||
// EPD hardware init end
|
||||
}
|
||||
void HOT GDEY029T94::display() {
|
||||
// COMMAND DATA START TRANSMISSION 2 (B/W only)
|
||||
this->command(0x13);
|
||||
delay(2);
|
||||
this->start_data_();
|
||||
for (size_t i = 0; i < this->get_buffer_length_(); i++) {
|
||||
this->write_byte(this->buffer_[i]);
|
||||
}
|
||||
this->end_data_();
|
||||
delay(2);
|
||||
|
||||
// COMMAND DISPLAY REFRESH
|
||||
this->command(0x12);
|
||||
delay(2);
|
||||
this->wait_until_idle_();
|
||||
|
||||
// COMMAND POWER OFF
|
||||
// NOTE: power off < deep sleep
|
||||
this->command(0x02);
|
||||
}
|
||||
int GDEY029T94::get_width_internal() { return 128; }
|
||||
int GDEY029T94::get_height_internal() { return 296; }
|
||||
void GDEY029T94::dump_config() {
|
||||
LOG_DISPLAY("", "Waveshare E-Paper (Good Display)", this);
|
||||
ESP_LOGCONFIG(TAG, " Model: 2.9in Greyscale GDEY029T94");
|
||||
LOG_PIN(" Reset Pin: ", this->reset_pin_);
|
||||
LOG_PIN(" DC Pin: ", this->dc_pin_);
|
||||
LOG_PIN(" Busy Pin: ", this->busy_pin_);
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
}
|
||||
|
||||
static const uint8_t LUT_VCOM_DC_4_2[] = {
|
||||
0x00, 0x17, 0x00, 0x00, 0x00, 0x02, 0x00, 0x17, 0x17, 0x00, 0x00, 0x02, 0x00, 0x0A, 0x01,
|
||||
0x00, 0x00, 0x01, 0x00, 0x0E, 0x0E, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
@ -54,6 +54,8 @@ class WaveshareEPaper : public PollingComponent,
|
||||
}
|
||||
}
|
||||
|
||||
virtual int get_width_controller() { return this->get_width_internal(); };
|
||||
|
||||
uint32_t get_buffer_length_();
|
||||
uint32_t reset_duration_{200};
|
||||
|
||||
@ -111,6 +113,8 @@ class WaveshareEPaperTypeA : public WaveshareEPaper {
|
||||
|
||||
int get_height_internal() override;
|
||||
|
||||
int get_width_controller() override;
|
||||
|
||||
uint32_t full_update_every_{30};
|
||||
uint32_t at_update_{0};
|
||||
WaveshareEPaperTypeAModel model_;
|
||||
@ -146,6 +150,26 @@ class WaveshareEPaper2P7In : public WaveshareEPaper {
|
||||
int get_height_internal() override;
|
||||
};
|
||||
|
||||
class GDEY029T94 : public WaveshareEPaper {
|
||||
public:
|
||||
void initialize() override;
|
||||
|
||||
void display() override;
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
void deep_sleep() override {
|
||||
// COMMAND DEEP SLEEP
|
||||
this->command(0x07);
|
||||
this->data(0xA5); // check byte
|
||||
}
|
||||
|
||||
protected:
|
||||
int get_width_internal() override;
|
||||
|
||||
int get_height_internal() override;
|
||||
};
|
||||
|
||||
class WaveshareEPaper2P9InB : public WaveshareEPaper {
|
||||
public:
|
||||
void initialize() override;
|
||||
|
||||
@ -867,7 +867,7 @@ void WebServer::handle_climate_request(AsyncWebServerRequest *request, const Url
|
||||
}
|
||||
|
||||
// Longest: HORIZONTAL
|
||||
#define PSTR_LOCAL(mode_s) strncpy_P(__buf, (PGM_P)((mode_s)), 15)
|
||||
#define PSTR_LOCAL(mode_s) strncpy_P(__buf, (PGM_P) ((mode_s)), 15)
|
||||
|
||||
std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_config) {
|
||||
return json::build_json([obj, start_config](JsonObject root) {
|
||||
|
||||
@ -78,7 +78,7 @@ void WhirlpoolClimate::transmit_state() {
|
||||
|
||||
// Temperature
|
||||
auto temp = (uint8_t) roundf(clamp(this->target_temperature, this->temperature_min_(), this->temperature_max_()));
|
||||
remote_state[3] |= (uint8_t)(temp - this->temperature_min_()) << 4;
|
||||
remote_state[3] |= (uint8_t) (temp - this->temperature_min_()) << 4;
|
||||
|
||||
// Fan speed
|
||||
switch (this->fan_mode.value()) {
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
#include "wifi_component.h"
|
||||
#include <cinttypes>
|
||||
|
||||
#if defined(USE_ESP32) || defined(USE_ESP_IDF)
|
||||
#include <esp_wifi.h>
|
||||
@ -370,7 +371,7 @@ void WiFiComponent::print_connect_params_() {
|
||||
if (this->selected_ap_.get_bssid().has_value()) {
|
||||
ESP_LOGV(TAG, " Priority: %.1f", this->get_sta_priority(*this->selected_ap_.get_bssid()));
|
||||
}
|
||||
ESP_LOGCONFIG(TAG, " Channel: %d", wifi_channel_());
|
||||
ESP_LOGCONFIG(TAG, " Channel: %" PRId32, wifi_channel_());
|
||||
ESP_LOGCONFIG(TAG, " Subnet: %s", wifi_subnet_mask_().str().c_str());
|
||||
ESP_LOGCONFIG(TAG, " Gateway: %s", wifi_gateway_ip_().str().c_str());
|
||||
ESP_LOGCONFIG(TAG, " DNS1: %s", wifi_dns_ip_(0).str().c_str());
|
||||
|
||||
@ -4,19 +4,19 @@
|
||||
|
||||
#include <esp_wifi.h>
|
||||
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#ifdef USE_WIFI_WPA2_EAP
|
||||
#include <esp_wpa2.h>
|
||||
#endif
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/dns.h"
|
||||
#include "lwip/apps/sntp.h"
|
||||
#include "lwip/dns.h"
|
||||
#include "lwip/err.h"
|
||||
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/util.h"
|
||||
|
||||
namespace esphome {
|
||||
@ -128,13 +128,23 @@ bool WiFiComponent::wifi_sta_ip_config_(optional<ManualIP> manual_ip) {
|
||||
}
|
||||
|
||||
ip_addr_t dns;
|
||||
#if LWIP_IPV6
|
||||
dns.type = IPADDR_TYPE_V4;
|
||||
#endif
|
||||
if (uint32_t(manual_ip->dns1) != 0) {
|
||||
#if LWIP_IPV6
|
||||
dns.u_addr.ip4.addr = static_cast<uint32_t>(manual_ip->dns1);
|
||||
#else
|
||||
dns.addr = static_cast<uint32_t>(manual_ip->dns1);
|
||||
#endif
|
||||
dns_setserver(0, &dns);
|
||||
}
|
||||
if (uint32_t(manual_ip->dns2) != 0) {
|
||||
#if LWIP_IPV6
|
||||
dns.u_addr.ip4.addr = static_cast<uint32_t>(manual_ip->dns2);
|
||||
#else
|
||||
dns.addr = static_cast<uint32_t>(manual_ip->dns2);
|
||||
#endif
|
||||
dns_setserver(1, &dns);
|
||||
}
|
||||
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
#include <esp_event.h>
|
||||
#include <esp_netif.h>
|
||||
|
||||
#include <cinttypes>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
#ifdef USE_WIFI_WPA2_EAP
|
||||
@ -451,13 +452,23 @@ bool WiFiComponent::wifi_sta_ip_config_(optional<ManualIP> manual_ip) {
|
||||
}
|
||||
|
||||
ip_addr_t dns;
|
||||
#if LWIP_IPV6
|
||||
dns.type = IPADDR_TYPE_V4;
|
||||
#endif
|
||||
if (uint32_t(manual_ip->dns1) != 0) {
|
||||
#if LWIP_IPV6
|
||||
dns.u_addr.ip4.addr = static_cast<uint32_t>(manual_ip->dns1);
|
||||
#else
|
||||
dns.addr = static_cast<uint32_t>(manual_ip->dns1);
|
||||
#endif
|
||||
dns_setserver(0, &dns);
|
||||
}
|
||||
if (uint32_t(manual_ip->dns2) != 0) {
|
||||
#if LWIP_IPV6
|
||||
dns.u_addr.ip4.addr = static_cast<uint32_t>(manual_ip->dns2);
|
||||
#else
|
||||
dns.addr = static_cast<uint32_t>(manual_ip->dns2);
|
||||
#endif
|
||||
dns_setserver(1, &dns);
|
||||
}
|
||||
|
||||
@ -639,7 +650,7 @@ void WiFiComponent::wifi_process_event_(IDFWiFiEvent *data) {
|
||||
|
||||
} else if (data->event_base == IP_EVENT && data->event_id == IP_EVENT_STA_GOT_IP) {
|
||||
const auto &it = data->data.ip_got_ip;
|
||||
#ifdef LWIP_IPV6_AUTOCONFIG
|
||||
#if LWIP_IPV6_AUTOCONFIG
|
||||
tcpip_adapter_create_ip6_linklocal(TCPIP_ADAPTER_IF_STA);
|
||||
#endif
|
||||
ESP_LOGV(TAG, "Event: Got IP static_ip=%s gateway=%s", format_ip4_addr(it.ip_info.ip).c_str(),
|
||||
@ -656,7 +667,7 @@ void WiFiComponent::wifi_process_event_(IDFWiFiEvent *data) {
|
||||
|
||||
} else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_SCAN_DONE) {
|
||||
const auto &it = data->data.sta_scan_done;
|
||||
ESP_LOGV(TAG, "Event: WiFi Scan Done status=%u number=%u scan_id=%u", it.status, it.number, it.scan_id);
|
||||
ESP_LOGV(TAG, "Event: WiFi Scan Done status=%" PRIu32 " number=%u scan_id=%u", it.status, it.number, it.scan_id);
|
||||
|
||||
scan_result_.clear();
|
||||
this->scan_done_ = true;
|
||||
@ -912,7 +923,11 @@ network::IPAddress WiFiComponent::wifi_gateway_ip_() {
|
||||
}
|
||||
network::IPAddress WiFiComponent::wifi_dns_ip_(int num) {
|
||||
const ip_addr_t *dns_ip = dns_getserver(num);
|
||||
#if LWIP_IPV6
|
||||
return {dns_ip->u_addr.ip4.addr};
|
||||
#else
|
||||
return {dns_ip->addr};
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace wifi
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user