mirror of
https://github.com/esphome/esphome.git
synced 2026-05-30 15:28:34 +08:00
+5
-1
@@ -14,7 +14,12 @@ Checks: >-
|
|||||||
-cert-str34-c,
|
-cert-str34-c,
|
||||||
-clang-analyzer-optin.cplusplus.UninitializedObject,
|
-clang-analyzer-optin.cplusplus.UninitializedObject,
|
||||||
-clang-analyzer-osx.*,
|
-clang-analyzer-osx.*,
|
||||||
|
-clang-diagnostic-delete-abstract-non-virtual-dtor,
|
||||||
|
-clang-diagnostic-delete-non-abstract-non-virtual-dtor,
|
||||||
-clang-diagnostic-shadow-field,
|
-clang-diagnostic-shadow-field,
|
||||||
|
-clang-diagnostic-sign-compare,
|
||||||
|
-clang-diagnostic-unused-variable,
|
||||||
|
-clang-diagnostic-unused-const-variable,
|
||||||
-cppcoreguidelines-avoid-c-arrays,
|
-cppcoreguidelines-avoid-c-arrays,
|
||||||
-cppcoreguidelines-avoid-goto,
|
-cppcoreguidelines-avoid-goto,
|
||||||
-cppcoreguidelines-avoid-magic-numbers,
|
-cppcoreguidelines-avoid-magic-numbers,
|
||||||
@@ -80,7 +85,6 @@ Checks: >-
|
|||||||
-readability-use-anyofallof,
|
-readability-use-anyofallof,
|
||||||
-warnings-as-errors
|
-warnings-as-errors
|
||||||
WarningsAsErrors: '*'
|
WarningsAsErrors: '*'
|
||||||
HeaderFilterRegex: '^.*/src/esphome/.*'
|
|
||||||
AnalyzeTemporaryDtors: false
|
AnalyzeTemporaryDtors: false
|
||||||
FormatStyle: google
|
FormatStyle: google
|
||||||
CheckOptions:
|
CheckOptions:
|
||||||
|
|||||||
@@ -36,26 +36,27 @@ jobs:
|
|||||||
container: ghcr.io/esphome/esphome-lint:1.1
|
container: ghcr.io/esphome/esphome-lint:1.1
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
# Set up the pio project so that the cpp checks know how files are compiled
|
|
||||||
# (build flags, libraries etc)
|
|
||||||
- name: Set up platformio environment
|
|
||||||
run: pio init --ide atom
|
|
||||||
|
|
||||||
- name: Register problem matchers
|
- name: Register problem matchers
|
||||||
run: |
|
run: |
|
||||||
echo "::add-matcher::.github/workflows/matchers/clang-tidy.json"
|
echo "::add-matcher::.github/workflows/matchers/clang-tidy.json"
|
||||||
echo "::add-matcher::.github/workflows/matchers/gcc.json"
|
echo "::add-matcher::.github/workflows/matchers/gcc.json"
|
||||||
|
|
||||||
|
# Also run git-diff-index so that the step is marked as failed on formatting errors,
|
||||||
|
# since clang-format doesn't do anything but change files if -i is passed.
|
||||||
- name: Run clang-format
|
- name: Run clang-format
|
||||||
run: script/clang-format -i
|
run: |
|
||||||
|
script/clang-format -i
|
||||||
|
git diff-index --quiet HEAD --
|
||||||
if: ${{ matrix.id == 'clang-format' }}
|
if: ${{ matrix.id == 'clang-format' }}
|
||||||
|
|
||||||
- name: Run clang-tidy
|
- name: Run clang-tidy
|
||||||
run: script/clang-tidy --all-headers --fix --split-num 4 --split-at ${{ matrix.split }}
|
run: script/clang-tidy --all-headers --fix --split-num 4 --split-at ${{ matrix.split }}
|
||||||
if: ${{ matrix.id == 'clang-tidy' }}
|
if: ${{ matrix.id == 'clang-tidy' }}
|
||||||
|
|
||||||
- name: Suggest changes
|
- name: Suggested changes
|
||||||
run: script/ci-suggest-changes
|
run: script/ci-suggest-changes
|
||||||
|
if: always()
|
||||||
|
|
||||||
ci:
|
ci:
|
||||||
# Don't use the esphome-lint docker image because it may contain outdated requirements.
|
# Don't use the esphome-lint docker image because it may contain outdated requirements.
|
||||||
@@ -82,6 +83,9 @@ jobs:
|
|||||||
- id: test
|
- id: test
|
||||||
file: tests/test4.yaml
|
file: tests/test4.yaml
|
||||||
name: Test tests/test4.yaml
|
name: Test tests/test4.yaml
|
||||||
|
- id: test
|
||||||
|
file: tests/test5.yaml
|
||||||
|
name: Test tests/test5.yaml
|
||||||
- id: pytest
|
- id: pytest
|
||||||
name: Run pytest
|
name: Run pytest
|
||||||
|
|
||||||
|
|||||||
@@ -125,4 +125,5 @@ config/
|
|||||||
tests/build/
|
tests/build/
|
||||||
tests/.esphome/
|
tests/.esphome/
|
||||||
/.temp-clang-tidy.cpp
|
/.temp-clang-tidy.cpp
|
||||||
|
/.temp/
|
||||||
.pio/
|
.pio/
|
||||||
|
|||||||
Vendored
+1
-1
@@ -10,7 +10,7 @@
|
|||||||
{
|
{
|
||||||
"label": "clang-tidy",
|
"label": "clang-tidy",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "test -f .gcc-flags.json || pio init --silent --ide atom; ./script/clang-tidy",
|
"command": "./script/clang-tidy",
|
||||||
"problemMatcher": [
|
"problemMatcher": [
|
||||||
{
|
{
|
||||||
"owner": "clang-tidy",
|
"owner": "clang-tidy",
|
||||||
|
|||||||
+18
@@ -14,6 +14,8 @@ esphome/core/* @esphome/core
|
|||||||
esphome/components/ac_dimmer/* @glmnet
|
esphome/components/ac_dimmer/* @glmnet
|
||||||
esphome/components/adc/* @esphome/core
|
esphome/components/adc/* @esphome/core
|
||||||
esphome/components/addressable_light/* @justfalter
|
esphome/components/addressable_light/* @justfalter
|
||||||
|
esphome/components/am43/* @buxtronix
|
||||||
|
esphome/components/am43/cover/* @buxtronix
|
||||||
esphome/components/animation/* @syndlex
|
esphome/components/animation/* @syndlex
|
||||||
esphome/components/anova/* @buxtronix
|
esphome/components/anova/* @buxtronix
|
||||||
esphome/components/api/* @OttoWinter
|
esphome/components/api/* @OttoWinter
|
||||||
@@ -29,6 +31,7 @@ esphome/components/canbus/* @danielschramm @mvturnho
|
|||||||
esphome/components/captive_portal/* @OttoWinter
|
esphome/components/captive_portal/* @OttoWinter
|
||||||
esphome/components/climate/* @esphome/core
|
esphome/components/climate/* @esphome/core
|
||||||
esphome/components/climate_ir/* @glmnet
|
esphome/components/climate_ir/* @glmnet
|
||||||
|
esphome/components/color_temperature/* @jesserockz
|
||||||
esphome/components/coolix/* @glmnet
|
esphome/components/coolix/* @glmnet
|
||||||
esphome/components/cover/* @esphome/core
|
esphome/components/cover/* @esphome/core
|
||||||
esphome/components/cs5460a/* @balrog-kun
|
esphome/components/cs5460a/* @balrog-kun
|
||||||
@@ -37,6 +40,7 @@ esphome/components/debug/* @OttoWinter
|
|||||||
esphome/components/dfplayer/* @glmnet
|
esphome/components/dfplayer/* @glmnet
|
||||||
esphome/components/dht/* @OttoWinter
|
esphome/components/dht/* @OttoWinter
|
||||||
esphome/components/ds1307/* @badbadc0ffee
|
esphome/components/ds1307/* @badbadc0ffee
|
||||||
|
esphome/components/dsmr/* @glmnet @zuidwijk
|
||||||
esphome/components/esp32_ble/* @jesserockz
|
esphome/components/esp32_ble/* @jesserockz
|
||||||
esphome/components/esp32_ble_server/* @jesserockz
|
esphome/components/esp32_ble_server/* @jesserockz
|
||||||
esphome/components/esp32_improv/* @jesserockz
|
esphome/components/esp32_improv/* @jesserockz
|
||||||
@@ -48,7 +52,9 @@ esphome/components/globals/* @esphome/core
|
|||||||
esphome/components/gpio/* @esphome/core
|
esphome/components/gpio/* @esphome/core
|
||||||
esphome/components/gps/* @coogle
|
esphome/components/gps/* @coogle
|
||||||
esphome/components/havells_solar/* @sourabhjaiswal
|
esphome/components/havells_solar/* @sourabhjaiswal
|
||||||
|
esphome/components/hitachi_ac424/* @sourabhjaiswal
|
||||||
esphome/components/homeassistant/* @OttoWinter
|
esphome/components/homeassistant/* @OttoWinter
|
||||||
|
esphome/components/hrxl_maxsonar_wr/* @netmikey
|
||||||
esphome/components/i2c/* @esphome/core
|
esphome/components/i2c/* @esphome/core
|
||||||
esphome/components/improv/* @jesserockz
|
esphome/components/improv/* @jesserockz
|
||||||
esphome/components/inkbird_ibsth1_mini/* @fkirill
|
esphome/components/inkbird_ibsth1_mini/* @fkirill
|
||||||
@@ -83,19 +89,26 @@ esphome/components/number/* @esphome/core
|
|||||||
esphome/components/ota/* @esphome/core
|
esphome/components/ota/* @esphome/core
|
||||||
esphome/components/output/* @esphome/core
|
esphome/components/output/* @esphome/core
|
||||||
esphome/components/pid/* @OttoWinter
|
esphome/components/pid/* @OttoWinter
|
||||||
|
esphome/components/pipsolar/* @andreashergert1984
|
||||||
|
esphome/components/pmsa003i/* @sjtrny
|
||||||
esphome/components/pn532/* @OttoWinter @jesserockz
|
esphome/components/pn532/* @OttoWinter @jesserockz
|
||||||
esphome/components/pn532_i2c/* @OttoWinter @jesserockz
|
esphome/components/pn532_i2c/* @OttoWinter @jesserockz
|
||||||
esphome/components/pn532_spi/* @OttoWinter @jesserockz
|
esphome/components/pn532_spi/* @OttoWinter @jesserockz
|
||||||
esphome/components/power_supply/* @esphome/core
|
esphome/components/power_supply/* @esphome/core
|
||||||
esphome/components/pulse_meter/* @stevebaxter
|
esphome/components/pulse_meter/* @stevebaxter
|
||||||
|
esphome/components/pvvx_mithermometer/* @pasiz
|
||||||
esphome/components/rc522/* @glmnet
|
esphome/components/rc522/* @glmnet
|
||||||
esphome/components/rc522_i2c/* @glmnet
|
esphome/components/rc522_i2c/* @glmnet
|
||||||
esphome/components/rc522_spi/* @glmnet
|
esphome/components/rc522_spi/* @glmnet
|
||||||
esphome/components/restart/* @esphome/core
|
esphome/components/restart/* @esphome/core
|
||||||
esphome/components/rf_bridge/* @jesserockz
|
esphome/components/rf_bridge/* @jesserockz
|
||||||
|
esphome/components/rgbct/* @jesserockz
|
||||||
esphome/components/rtttl/* @glmnet
|
esphome/components/rtttl/* @glmnet
|
||||||
esphome/components/script/* @esphome/core
|
esphome/components/script/* @esphome/core
|
||||||
esphome/components/sdm_meter/* @jesserockz @polyfaces
|
esphome/components/sdm_meter/* @jesserockz @polyfaces
|
||||||
|
esphome/components/sdp3x/* @Azimath
|
||||||
|
esphome/components/selec_meter/* @sourabhjaiswal
|
||||||
|
esphome/components/select/* @esphome/core
|
||||||
esphome/components/sensor/* @esphome/core
|
esphome/components/sensor/* @esphome/core
|
||||||
esphome/components/sgp40/* @SenexCrenshaw
|
esphome/components/sgp40/* @SenexCrenshaw
|
||||||
esphome/components/sht4x/* @sjtrny
|
esphome/components/sht4x/* @sjtrny
|
||||||
@@ -119,14 +132,19 @@ esphome/components/st7789v/* @kbx81
|
|||||||
esphome/components/substitutions/* @esphome/core
|
esphome/components/substitutions/* @esphome/core
|
||||||
esphome/components/sun/* @OttoWinter
|
esphome/components/sun/* @OttoWinter
|
||||||
esphome/components/switch/* @esphome/core
|
esphome/components/switch/* @esphome/core
|
||||||
|
esphome/components/t6615/* @tylermenezes
|
||||||
esphome/components/tca9548a/* @andreashergert1984
|
esphome/components/tca9548a/* @andreashergert1984
|
||||||
esphome/components/tcl112/* @glmnet
|
esphome/components/tcl112/* @glmnet
|
||||||
esphome/components/teleinfo/* @0hax
|
esphome/components/teleinfo/* @0hax
|
||||||
esphome/components/thermostat/* @kbx81
|
esphome/components/thermostat/* @kbx81
|
||||||
esphome/components/time/* @OttoWinter
|
esphome/components/time/* @OttoWinter
|
||||||
|
esphome/components/tlc5947/* @rnauber
|
||||||
esphome/components/tm1637/* @glmnet
|
esphome/components/tm1637/* @glmnet
|
||||||
esphome/components/tmp102/* @timsavage
|
esphome/components/tmp102/* @timsavage
|
||||||
|
esphome/components/tmp117/* @Azimath
|
||||||
esphome/components/tof10120/* @wstrzalka
|
esphome/components/tof10120/* @wstrzalka
|
||||||
|
esphome/components/toshiba/* @kbx81
|
||||||
|
esphome/components/tsl2591/* @wjcarpenter
|
||||||
esphome/components/tuya/binary_sensor/* @jesserockz
|
esphome/components/tuya/binary_sensor/* @jesserockz
|
||||||
esphome/components/tuya/climate/* @jesserockz
|
esphome/components/tuya/climate/* @jesserockz
|
||||||
esphome/components/tuya/sensor/* @jesserockz
|
esphome/components/tuya/sensor/* @jesserockz
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
#!/usr/bin/with-contenv bashio
|
|
||||||
# ==============================================================================
|
|
||||||
# Community Hass.io Add-ons: ESPHome
|
|
||||||
# This files installs the user ESPHome version if specified
|
|
||||||
# ==============================================================================
|
|
||||||
|
|
||||||
declare esphome_version
|
|
||||||
|
|
||||||
if bashio::config.has_value 'esphome_version'; then
|
|
||||||
esphome_version=$(bashio::config 'esphome_version')
|
|
||||||
if [[ $esphome_version == *":"* ]]; then
|
|
||||||
IFS=':' read -r -a array <<< "$esphome_version"
|
|
||||||
username=${array[0]}
|
|
||||||
ref=${array[1]}
|
|
||||||
else
|
|
||||||
username="esphome"
|
|
||||||
ref=$esphome_version
|
|
||||||
fi
|
|
||||||
full_url="https://github.com/${username}/esphome/archive/${ref}.zip"
|
|
||||||
bashio::log.info "Installing esphome version '${esphome_version}' (${full_url})..."
|
|
||||||
pip3 install -U --no-cache-dir "${full_url}" \
|
|
||||||
|| bashio::exit.nok "Failed installing esphome pinned version."
|
|
||||||
fi
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
#!/usr/bin/with-contenv bashio
|
|
||||||
# ==============================================================================
|
|
||||||
# Community Hass.io Add-ons: ESPHome
|
|
||||||
# This files migrates the esphome config directory from the old path
|
|
||||||
# ==============================================================================
|
|
||||||
|
|
||||||
if [[ ! -d /config/esphome && -d /config/esphomeyaml ]]; then
|
|
||||||
echo "Moving config directory from /config/esphomeyaml to /config/esphome"
|
|
||||||
mv /config/esphomeyaml /config/esphome
|
|
||||||
mv /config/esphome/.esphomeyaml /config/esphome/.esphome
|
|
||||||
fi
|
|
||||||
+18
-8
@@ -11,6 +11,7 @@ from esphome.config import iter_components, read_config, strip_default_ids
|
|||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_BAUD_RATE,
|
CONF_BAUD_RATE,
|
||||||
CONF_BROKER,
|
CONF_BROKER,
|
||||||
|
CONF_DEASSERT_RTS_DTR,
|
||||||
CONF_LOGGER,
|
CONF_LOGGER,
|
||||||
CONF_OTA,
|
CONF_OTA,
|
||||||
CONF_PASSWORD,
|
CONF_PASSWORD,
|
||||||
@@ -99,10 +100,21 @@ def run_miniterm(config, port):
|
|||||||
baud_rate = config["logger"][CONF_BAUD_RATE]
|
baud_rate = config["logger"][CONF_BAUD_RATE]
|
||||||
if baud_rate == 0:
|
if baud_rate == 0:
|
||||||
_LOGGER.info("UART logging is disabled (baud_rate=0). Not starting UART logs.")
|
_LOGGER.info("UART logging is disabled (baud_rate=0). Not starting UART logs.")
|
||||||
|
return
|
||||||
_LOGGER.info("Starting log output from %s with baud rate %s", port, baud_rate)
|
_LOGGER.info("Starting log output from %s with baud rate %s", port, baud_rate)
|
||||||
|
|
||||||
backtrace_state = False
|
backtrace_state = False
|
||||||
with serial.Serial(port, baudrate=baud_rate) as ser:
|
ser = serial.Serial()
|
||||||
|
ser.baudrate = baud_rate
|
||||||
|
ser.port = port
|
||||||
|
|
||||||
|
# We can't set to False by default since it leads to toggling and hence
|
||||||
|
# ESP32 resets on some platforms.
|
||||||
|
if config["logger"][CONF_DEASSERT_RTS_DTR]:
|
||||||
|
ser.dtr = False
|
||||||
|
ser.rts = False
|
||||||
|
|
||||||
|
with ser:
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
raw = ser.readline()
|
raw = ser.readline()
|
||||||
@@ -284,7 +296,6 @@ def command_vscode(args):
|
|||||||
|
|
||||||
logging.disable(logging.INFO)
|
logging.disable(logging.INFO)
|
||||||
logging.disable(logging.WARNING)
|
logging.disable(logging.WARNING)
|
||||||
CORE.config_path = args.configuration
|
|
||||||
vscode.read_config(args)
|
vscode.read_config(args)
|
||||||
|
|
||||||
|
|
||||||
@@ -394,7 +405,7 @@ def command_update_all(args):
|
|||||||
import click
|
import click
|
||||||
|
|
||||||
success = {}
|
success = {}
|
||||||
files = list_yaml_files(args.configuration[0])
|
files = list_yaml_files(args.configuration)
|
||||||
twidth = 60
|
twidth = 60
|
||||||
|
|
||||||
def print_bar(middle_text):
|
def print_bar(middle_text):
|
||||||
@@ -408,7 +419,7 @@ def command_update_all(args):
|
|||||||
print("-" * twidth)
|
print("-" * twidth)
|
||||||
print()
|
print()
|
||||||
rc = run_external_process(
|
rc = run_external_process(
|
||||||
"esphome", "--dashboard", "run", "--no-logs", "--device", "OTA", f
|
"esphome", "--dashboard", "run", f, "--no-logs", "--device", "OTA"
|
||||||
)
|
)
|
||||||
if rc == 0:
|
if rc == 0:
|
||||||
print_bar("[{}] {}".format(color(Fore.BOLD_GREEN, "SUCCESS"), f))
|
print_bar("[{}] {}".format(color(Fore.BOLD_GREEN, "SUCCESS"), f))
|
||||||
@@ -505,6 +516,7 @@ def parse_args(argv):
|
|||||||
"clean",
|
"clean",
|
||||||
"dashboard",
|
"dashboard",
|
||||||
"vscode",
|
"vscode",
|
||||||
|
"update-all",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -681,14 +693,12 @@ def parse_args(argv):
|
|||||||
)
|
)
|
||||||
|
|
||||||
parser_vscode = subparsers.add_parser("vscode")
|
parser_vscode = subparsers.add_parser("vscode")
|
||||||
parser_vscode.add_argument(
|
parser_vscode.add_argument("configuration", help="Your YAML configuration file.")
|
||||||
"configuration", help="Your YAML configuration file.", nargs=1
|
|
||||||
)
|
|
||||||
parser_vscode.add_argument("--ace", action="store_true")
|
parser_vscode.add_argument("--ace", action="store_true")
|
||||||
|
|
||||||
parser_update = subparsers.add_parser("update-all")
|
parser_update = subparsers.add_parser("update-all")
|
||||||
parser_update.add_argument(
|
parser_update.add_argument(
|
||||||
"configuration", help="Your YAML configuration file directory.", nargs=1
|
"configuration", help="Your YAML configuration file directories.", nargs="+"
|
||||||
)
|
)
|
||||||
|
|
||||||
return parser.parse_args(argv[1:])
|
return parser.parse_args(argv[1:])
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -125,7 +125,7 @@ void ICACHE_RAM_ATTR HOT AcDimmerDataStore::gpio_intr() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ICACHE_RAM_ATTR HOT AcDimmerDataStore::s_gpio_intr(AcDimmerDataStore *store) {
|
void ICACHE_RAM_ATTR HOT AcDimmerDataStore::s_gpio_intr(AcDimmerDataStore *store) {
|
||||||
// Attaching pin interrupts on the same pin will override the previous interupt
|
// Attaching pin interrupts on the same pin will override the previous interrupt
|
||||||
// However, the user expects that multiple dimmers sharing the same ZC pin will work.
|
// However, the user expects that multiple dimmers sharing the same ZC pin will work.
|
||||||
// We solve this in a bit of a hacky way: On each pin interrupt, we check all dimmers
|
// We solve this in a bit of a hacky way: On each pin interrupt, we check all dimmers
|
||||||
// if any of them are using the same ZC pin, and also trigger the interrupt for *them*.
|
// if any of them are using the same ZC pin, and also trigger the interrupt for *them*.
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ void AdalightLightEffect::reset_frame_(light::AddressableLight &it) {
|
|||||||
|
|
||||||
void AdalightLightEffect::blank_all_leds_(light::AddressableLight &it) {
|
void AdalightLightEffect::blank_all_leds_(light::AddressableLight &it) {
|
||||||
for (int led = it.size(); led-- > 0;) {
|
for (int led = it.size(); led-- > 0;) {
|
||||||
it[led].set(COLOR_BLACK);
|
it[led].set(Color::BLACK);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ static const char *const TAG = "adc";
|
|||||||
void ADCSensor::set_attenuation(adc_atten_t attenuation) { this->attenuation_ = attenuation; }
|
void ADCSensor::set_attenuation(adc_atten_t attenuation) { this->attenuation_ = attenuation; }
|
||||||
|
|
||||||
inline adc1_channel_t gpio_to_adc1(uint8_t pin) {
|
inline adc1_channel_t gpio_to_adc1(uint8_t pin) {
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
switch (pin) {
|
switch (pin) {
|
||||||
case 36:
|
case 36:
|
||||||
return ADC1_CHANNEL_0;
|
return ADC1_CHANNEL_0;
|
||||||
@@ -34,6 +35,22 @@ inline adc1_channel_t gpio_to_adc1(uint8_t pin) {
|
|||||||
default:
|
default:
|
||||||
return ADC1_CHANNEL_MAX;
|
return ADC1_CHANNEL_MAX;
|
||||||
}
|
}
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32H2
|
||||||
|
switch (pin) {
|
||||||
|
case 0:
|
||||||
|
return ADC1_CHANNEL_0;
|
||||||
|
case 1:
|
||||||
|
return ADC1_CHANNEL_1;
|
||||||
|
case 2:
|
||||||
|
return ADC1_CHANNEL_2;
|
||||||
|
case 3:
|
||||||
|
return ADC1_CHANNEL_3;
|
||||||
|
case 4:
|
||||||
|
return ADC1_CHANNEL_4;
|
||||||
|
default:
|
||||||
|
return ADC1_CHANNEL_MAX;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -46,8 +63,10 @@ void ADCSensor::setup() {
|
|||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
adc1_config_channel_atten(gpio_to_adc1(pin_), attenuation_);
|
adc1_config_channel_atten(gpio_to_adc1(pin_), attenuation_);
|
||||||
adc1_config_width(ADC_WIDTH_BIT_12);
|
adc1_config_width(ADC_WIDTH_BIT_12);
|
||||||
|
#if !CONFIG_IDF_TARGET_ESP32C3 && !CONFIG_IDF_TARGET_ESP32H2
|
||||||
adc_gpio_init(ADC_UNIT_1, (adc_channel_t) gpio_to_adc1(pin_));
|
adc_gpio_init(ADC_UNIT_1, (adc_channel_t) gpio_to_adc1(pin_));
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
void ADCSensor::dump_config() {
|
void ADCSensor::dump_config() {
|
||||||
LOG_SENSOR("", "ADC Sensor", this);
|
LOG_SENSOR("", "ADC Sensor", this);
|
||||||
@@ -89,6 +108,7 @@ float ADCSensor::sample() {
|
|||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
int raw = adc1_get_raw(gpio_to_adc1(pin_));
|
int raw = adc1_get_raw(gpio_to_adc1(pin_));
|
||||||
float value_v = raw / 4095.0f;
|
float value_v = raw / 4095.0f;
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
switch (this->attenuation_) {
|
switch (this->attenuation_) {
|
||||||
case ADC_ATTEN_DB_0:
|
case ADC_ATTEN_DB_0:
|
||||||
value_v *= 1.1;
|
value_v *= 1.1;
|
||||||
@@ -105,6 +125,24 @@ float ADCSensor::sample() {
|
|||||||
default: // This is to satisfy the unused ADC_ATTEN_MAX
|
default: // This is to satisfy the unused ADC_ATTEN_MAX
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32H2
|
||||||
|
switch (this->attenuation_) {
|
||||||
|
case ADC_ATTEN_DB_0:
|
||||||
|
value_v *= 0.84;
|
||||||
|
break;
|
||||||
|
case ADC_ATTEN_DB_2_5:
|
||||||
|
value_v *= 1.13;
|
||||||
|
break;
|
||||||
|
case ADC_ATTEN_DB_6:
|
||||||
|
value_v *= 1.56;
|
||||||
|
break;
|
||||||
|
case ADC_ATTEN_DB_11:
|
||||||
|
value_v *= 3.0;
|
||||||
|
break;
|
||||||
|
default: // This is to satisfy the unused ADC_ATTEN_MAX
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
return value_v;
|
return value_v;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ from esphome.const import (
|
|||||||
CONF_ID,
|
CONF_ID,
|
||||||
CONF_PIN,
|
CONF_PIN,
|
||||||
DEVICE_CLASS_VOLTAGE,
|
DEVICE_CLASS_VOLTAGE,
|
||||||
ICON_EMPTY,
|
|
||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_MEASUREMENT,
|
||||||
UNIT_VOLT,
|
UNIT_VOLT,
|
||||||
)
|
)
|
||||||
@@ -37,7 +36,10 @@ ADCSensor = adc_ns.class_(
|
|||||||
|
|
||||||
CONFIG_SCHEMA = (
|
CONFIG_SCHEMA = (
|
||||||
sensor.sensor_schema(
|
sensor.sensor_schema(
|
||||||
UNIT_VOLT, ICON_EMPTY, 2, DEVICE_CLASS_VOLTAGE, STATE_CLASS_MEASUREMENT
|
unit_of_measurement=UNIT_VOLT,
|
||||||
|
accuracy_decimals=2,
|
||||||
|
device_class=DEVICE_CLASS_VOLTAGE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
)
|
)
|
||||||
.extend(
|
.extend(
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ from esphome.const import (
|
|||||||
DEVICE_CLASS_CURRENT,
|
DEVICE_CLASS_CURRENT,
|
||||||
DEVICE_CLASS_POWER,
|
DEVICE_CLASS_POWER,
|
||||||
DEVICE_CLASS_VOLTAGE,
|
DEVICE_CLASS_VOLTAGE,
|
||||||
ICON_EMPTY,
|
|
||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_MEASUREMENT,
|
||||||
UNIT_VOLT,
|
UNIT_VOLT,
|
||||||
UNIT_AMPERE,
|
UNIT_AMPERE,
|
||||||
@@ -32,27 +31,34 @@ CONFIG_SCHEMA = (
|
|||||||
cv.GenerateID(): cv.declare_id(ADE7953),
|
cv.GenerateID(): cv.declare_id(ADE7953),
|
||||||
cv.Optional(CONF_IRQ_PIN): pins.input_pin,
|
cv.Optional(CONF_IRQ_PIN): pins.input_pin,
|
||||||
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
|
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
|
||||||
UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE, STATE_CLASS_MEASUREMENT
|
unit_of_measurement=UNIT_VOLT,
|
||||||
|
accuracy_decimals=1,
|
||||||
|
device_class=DEVICE_CLASS_VOLTAGE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_CURRENT_A): sensor.sensor_schema(
|
cv.Optional(CONF_CURRENT_A): sensor.sensor_schema(
|
||||||
UNIT_AMPERE,
|
unit_of_measurement=UNIT_AMPERE,
|
||||||
ICON_EMPTY,
|
accuracy_decimals=2,
|
||||||
2,
|
device_class=DEVICE_CLASS_CURRENT,
|
||||||
DEVICE_CLASS_CURRENT,
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
STATE_CLASS_MEASUREMENT,
|
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_CURRENT_B): sensor.sensor_schema(
|
cv.Optional(CONF_CURRENT_B): sensor.sensor_schema(
|
||||||
UNIT_AMPERE,
|
unit_of_measurement=UNIT_AMPERE,
|
||||||
ICON_EMPTY,
|
accuracy_decimals=2,
|
||||||
2,
|
device_class=DEVICE_CLASS_CURRENT,
|
||||||
DEVICE_CLASS_CURRENT,
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
STATE_CLASS_MEASUREMENT,
|
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_ACTIVE_POWER_A): sensor.sensor_schema(
|
cv.Optional(CONF_ACTIVE_POWER_A): sensor.sensor_schema(
|
||||||
UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
|
unit_of_measurement=UNIT_WATT,
|
||||||
|
accuracy_decimals=1,
|
||||||
|
device_class=DEVICE_CLASS_POWER,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_ACTIVE_POWER_B): sensor.sensor_schema(
|
cv.Optional(CONF_ACTIVE_POWER_B): sensor.sensor_schema(
|
||||||
UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
|
unit_of_measurement=UNIT_WATT,
|
||||||
|
accuracy_decimals=1,
|
||||||
|
device_class=DEVICE_CLASS_POWER,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ from esphome.const import (
|
|||||||
CONF_GAIN,
|
CONF_GAIN,
|
||||||
CONF_MULTIPLEXER,
|
CONF_MULTIPLEXER,
|
||||||
DEVICE_CLASS_VOLTAGE,
|
DEVICE_CLASS_VOLTAGE,
|
||||||
ICON_EMPTY,
|
|
||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_MEASUREMENT,
|
||||||
UNIT_VOLT,
|
UNIT_VOLT,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
@@ -53,7 +52,10 @@ ADS1115Sensor = ads1115_ns.class_(
|
|||||||
CONF_ADS1115_ID = "ads1115_id"
|
CONF_ADS1115_ID = "ads1115_id"
|
||||||
CONFIG_SCHEMA = (
|
CONFIG_SCHEMA = (
|
||||||
sensor.sensor_schema(
|
sensor.sensor_schema(
|
||||||
UNIT_VOLT, ICON_EMPTY, 3, DEVICE_CLASS_VOLTAGE, STATE_CLASS_MEASUREMENT
|
unit_of_measurement=UNIT_VOLT,
|
||||||
|
accuracy_decimals=3,
|
||||||
|
device_class=DEVICE_CLASS_VOLTAGE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
)
|
)
|
||||||
.extend(
|
.extend(
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
//
|
//
|
||||||
// According to the datasheet, the component is supposed to respond in more than 75ms. In fact, it can answer almost
|
// According to the datasheet, the component is supposed to respond in more than 75ms. In fact, it can answer almost
|
||||||
// immediately for temperature. But for humidity, it takes >90ms to get a valid data. From experience, we have best
|
// immediately for temperature. But for humidity, it takes >90ms to get a valid data. From experience, we have best
|
||||||
// results making successive requests; the current implementation make 3 attemps with a delay of 30ms each time.
|
// results making successive requests; the current implementation makes 3 attempts with a delay of 30ms each time.
|
||||||
|
|
||||||
#include "aht10.h"
|
#include "aht10.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
@@ -23,7 +23,7 @@ static const uint8_t AHT10_CALIBRATE_CMD[] = {0xE1};
|
|||||||
static const uint8_t AHT10_MEASURE_CMD[] = {0xAC, 0x33, 0x00};
|
static const uint8_t AHT10_MEASURE_CMD[] = {0xAC, 0x33, 0x00};
|
||||||
static const uint8_t AHT10_DEFAULT_DELAY = 5; // ms, for calibration and temperature measurement
|
static const uint8_t AHT10_DEFAULT_DELAY = 5; // ms, for calibration and temperature measurement
|
||||||
static const uint8_t AHT10_HUMIDITY_DELAY = 30; // ms
|
static const uint8_t AHT10_HUMIDITY_DELAY = 30; // ms
|
||||||
static const uint8_t AHT10_ATTEMPS = 3; // safety margin, normally 3 attemps are enough: 3*30=90ms
|
static const uint8_t AHT10_ATTEMPTS = 3; // safety margin, normally 3 attempts are enough: 3*30=90ms
|
||||||
|
|
||||||
void AHT10Component::setup() {
|
void AHT10Component::setup() {
|
||||||
ESP_LOGCONFIG(TAG, "Setting up AHT10...");
|
ESP_LOGCONFIG(TAG, "Setting up AHT10...");
|
||||||
@@ -58,8 +58,8 @@ void AHT10Component::update() {
|
|||||||
uint8_t delay = AHT10_DEFAULT_DELAY;
|
uint8_t delay = AHT10_DEFAULT_DELAY;
|
||||||
if (this->humidity_sensor_ != nullptr)
|
if (this->humidity_sensor_ != nullptr)
|
||||||
delay = AHT10_HUMIDITY_DELAY;
|
delay = AHT10_HUMIDITY_DELAY;
|
||||||
for (int i = 0; i < AHT10_ATTEMPS; ++i) {
|
for (int i = 0; i < AHT10_ATTEMPTS; ++i) {
|
||||||
ESP_LOGVV(TAG, "Attemps %u at %6ld", i, millis());
|
ESP_LOGVV(TAG, "Attempt %u at %6ld", i, millis());
|
||||||
delay_microseconds_accurate(4);
|
delay_microseconds_accurate(4);
|
||||||
if (!this->read_bytes(0, data, 6, delay)) {
|
if (!this->read_bytes(0, data, 6, delay)) {
|
||||||
ESP_LOGD(TAG, "Communication with AHT10 failed, waiting...");
|
ESP_LOGD(TAG, "Communication with AHT10 failed, waiting...");
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ from esphome.const import (
|
|||||||
CONF_TEMPERATURE,
|
CONF_TEMPERATURE,
|
||||||
DEVICE_CLASS_HUMIDITY,
|
DEVICE_CLASS_HUMIDITY,
|
||||||
DEVICE_CLASS_TEMPERATURE,
|
DEVICE_CLASS_TEMPERATURE,
|
||||||
ICON_EMPTY,
|
|
||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_MEASUREMENT,
|
||||||
UNIT_CELSIUS,
|
UNIT_CELSIUS,
|
||||||
UNIT_PERCENT,
|
UNIT_PERCENT,
|
||||||
@@ -23,18 +22,16 @@ CONFIG_SCHEMA = (
|
|||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(AHT10Component),
|
cv.GenerateID(): cv.declare_id(AHT10Component),
|
||||||
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
||||||
UNIT_CELSIUS,
|
unit_of_measurement=UNIT_CELSIUS,
|
||||||
ICON_EMPTY,
|
accuracy_decimals=2,
|
||||||
2,
|
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||||
DEVICE_CLASS_TEMPERATURE,
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
STATE_CLASS_MEASUREMENT,
|
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
|
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
|
||||||
UNIT_PERCENT,
|
unit_of_measurement=UNIT_PERCENT,
|
||||||
ICON_EMPTY,
|
accuracy_decimals=2,
|
||||||
2,
|
device_class=DEVICE_CLASS_HUMIDITY,
|
||||||
DEVICE_CLASS_HUMIDITY,
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
STATE_CLASS_MEASUREMENT,
|
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ from esphome.const import (
|
|||||||
DEVICE_CLASS_TEMPERATURE,
|
DEVICE_CLASS_TEMPERATURE,
|
||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_MEASUREMENT,
|
||||||
UNIT_CELSIUS,
|
UNIT_CELSIUS,
|
||||||
ICON_EMPTY,
|
|
||||||
UNIT_PERCENT,
|
UNIT_PERCENT,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -25,18 +24,16 @@ CONFIG_SCHEMA = (
|
|||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(AM2320Component),
|
cv.GenerateID(): cv.declare_id(AM2320Component),
|
||||||
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
||||||
UNIT_CELSIUS,
|
unit_of_measurement=UNIT_CELSIUS,
|
||||||
ICON_EMPTY,
|
accuracy_decimals=1,
|
||||||
1,
|
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||||
DEVICE_CLASS_TEMPERATURE,
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
STATE_CLASS_MEASUREMENT,
|
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
|
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
|
||||||
UNIT_PERCENT,
|
unit_of_measurement=UNIT_PERCENT,
|
||||||
ICON_EMPTY,
|
accuracy_decimals=1,
|
||||||
1,
|
device_class=DEVICE_CLASS_HUMIDITY,
|
||||||
DEVICE_CLASS_HUMIDITY,
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
STATE_CLASS_MEASUREMENT,
|
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
#include "am43.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace am43 {
|
||||||
|
|
||||||
|
static const char *TAG = "am43";
|
||||||
|
|
||||||
|
void Am43::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG, "AM43");
|
||||||
|
LOG_SENSOR(" ", "Battery", this->battery_);
|
||||||
|
LOG_SENSOR(" ", "Illuminance", this->illuminance_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Am43::setup() {
|
||||||
|
this->encoder_ = new Am43Encoder();
|
||||||
|
this->decoder_ = new Am43Decoder();
|
||||||
|
this->logged_in_ = false;
|
||||||
|
this->last_battery_update_ = 0;
|
||||||
|
this->current_sensor_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Am43::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) {
|
||||||
|
switch (event) {
|
||||||
|
case ESP_GATTC_OPEN_EVT: {
|
||||||
|
this->logged_in_ = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ESP_GATTC_DISCONNECT_EVT: {
|
||||||
|
this->logged_in_ = false;
|
||||||
|
this->node_state = espbt::ClientState::Idle;
|
||||||
|
if (this->battery_ != nullptr)
|
||||||
|
this->battery_->publish_state(NAN);
|
||||||
|
if (this->illuminance_ != nullptr)
|
||||||
|
this->illuminance_->publish_state(NAN);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ESP_GATTC_SEARCH_CMPL_EVT: {
|
||||||
|
auto chr = this->parent_->get_characteristic(AM43_SERVICE_UUID, AM43_CHARACTERISTIC_UUID);
|
||||||
|
if (chr == nullptr) {
|
||||||
|
if (this->parent_->get_characteristic(AM43_TUYA_SERVICE_UUID, AM43_TUYA_CHARACTERISTIC_UUID) != nullptr) {
|
||||||
|
ESP_LOGE(TAG, "[%s] Detected a Tuya AM43 which is not supported, sorry.",
|
||||||
|
this->parent_->address_str().c_str());
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "[%s] No control service found at device, not an AM43..?",
|
||||||
|
this->parent_->address_str().c_str());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this->char_handle_ = chr->handle;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
|
||||||
|
this->node_state = espbt::ClientState::Established;
|
||||||
|
this->update();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ESP_GATTC_NOTIFY_EVT: {
|
||||||
|
if (param->notify.handle != this->char_handle_)
|
||||||
|
break;
|
||||||
|
this->decoder_->decode(param->notify.value, param->notify.value_len);
|
||||||
|
|
||||||
|
if (this->battery_ != nullptr && this->decoder_->has_battery_level() &&
|
||||||
|
millis() - this->last_battery_update_ > 10000) {
|
||||||
|
this->battery_->publish_state(this->decoder_->battery_level_);
|
||||||
|
this->last_battery_update_ = millis();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->illuminance_ != nullptr && this->decoder_->has_light_level()) {
|
||||||
|
this->illuminance_->publish_state(this->decoder_->light_level_);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->current_sensor_ > 0) {
|
||||||
|
if (this->illuminance_ != nullptr) {
|
||||||
|
auto packet = this->encoder_->get_light_level_request();
|
||||||
|
auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_,
|
||||||
|
packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP,
|
||||||
|
ESP_GATT_AUTH_REQ_NONE);
|
||||||
|
if (status)
|
||||||
|
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(),
|
||||||
|
status);
|
||||||
|
}
|
||||||
|
this->current_sensor_ = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Am43::update() {
|
||||||
|
if (this->node_state != espbt::ClientState::Established) {
|
||||||
|
ESP_LOGW(TAG, "[%s] Cannot poll, not connected", this->parent_->address_str().c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this->current_sensor_ == 0) {
|
||||||
|
if (this->battery_ != nullptr) {
|
||||||
|
auto packet = this->encoder_->get_battery_level_request();
|
||||||
|
auto status =
|
||||||
|
esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length,
|
||||||
|
packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
|
||||||
|
if (status)
|
||||||
|
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status);
|
||||||
|
}
|
||||||
|
this->current_sensor_++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace am43
|
||||||
|
} // namespace esphome
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/components/ble_client/ble_client.h"
|
||||||
|
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||||
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
#include "esphome/components/am43/am43_base.h"
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
|
||||||
|
#include <esp_gattc_api.h>
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace am43 {
|
||||||
|
|
||||||
|
namespace espbt = esphome::esp32_ble_tracker;
|
||||||
|
|
||||||
|
class Am43 : public esphome::ble_client::BLEClientNode, public PollingComponent {
|
||||||
|
public:
|
||||||
|
void setup() override;
|
||||||
|
void update() override;
|
||||||
|
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||||
|
esp_ble_gattc_cb_param_t *param) override;
|
||||||
|
void dump_config() override;
|
||||||
|
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||||
|
void set_battery(sensor::Sensor *battery) { battery_ = battery; }
|
||||||
|
void set_illuminance(sensor::Sensor *illuminance) { illuminance_ = illuminance; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
uint16_t char_handle_;
|
||||||
|
Am43Encoder *encoder_;
|
||||||
|
Am43Decoder *decoder_;
|
||||||
|
bool logged_in_;
|
||||||
|
sensor::Sensor *battery_{nullptr};
|
||||||
|
sensor::Sensor *illuminance_{nullptr};
|
||||||
|
uint8_t current_sensor_;
|
||||||
|
// The AM43 often gets into a state where it spams loads of battery update
|
||||||
|
// notifications. Here we will limit to no more than every 10s.
|
||||||
|
uint8_t last_battery_update_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace am43
|
||||||
|
} // namespace esphome
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
#include "am43_base.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace am43 {
|
||||||
|
|
||||||
|
const uint8_t START_PACKET[5] = {0x00, 0xff, 0x00, 0x00, 0x9a};
|
||||||
|
|
||||||
|
std::string pkt_to_hex(const uint8_t *data, uint16_t len) {
|
||||||
|
char buf[64];
|
||||||
|
memset(buf, 0, 64);
|
||||||
|
for (int i = 0; i < len; i++)
|
||||||
|
sprintf(&buf[i * 2], "%02x", data[i]);
|
||||||
|
std::string ret = buf;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Am43Packet *Am43Encoder::get_battery_level_request() {
|
||||||
|
uint8_t data = 0x1;
|
||||||
|
return this->encode_(0xA2, &data, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Am43Packet *Am43Encoder::get_light_level_request() {
|
||||||
|
uint8_t data = 0x1;
|
||||||
|
return this->encode_(0xAA, &data, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Am43Packet *Am43Encoder::get_position_request() {
|
||||||
|
uint8_t data = 0x1;
|
||||||
|
return this->encode_(CMD_GET_POSITION, &data, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Am43Packet *Am43Encoder::get_send_pin_request(uint16_t pin) {
|
||||||
|
uint8_t data[2];
|
||||||
|
data[0] = (pin & 0xFF00) >> 8;
|
||||||
|
data[1] = pin & 0xFF;
|
||||||
|
return this->encode_(CMD_SEND_PIN, data, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
Am43Packet *Am43Encoder::get_open_request() {
|
||||||
|
uint8_t data = 0xDD;
|
||||||
|
return this->encode_(CMD_SET_STATE, &data, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Am43Packet *Am43Encoder::get_close_request() {
|
||||||
|
uint8_t data = 0xEE;
|
||||||
|
return this->encode_(CMD_SET_STATE, &data, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Am43Packet *Am43Encoder::get_stop_request() {
|
||||||
|
uint8_t data = 0xCC;
|
||||||
|
return this->encode_(CMD_SET_STATE, &data, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Am43Packet *Am43Encoder::get_set_position_request(uint8_t position) {
|
||||||
|
return this->encode_(CMD_SET_POSITION, &position, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Am43Encoder::checksum_() {
|
||||||
|
uint8_t checksum = 0;
|
||||||
|
int i = 0;
|
||||||
|
for (i = 0; i < this->packet_.length; i++)
|
||||||
|
checksum = checksum ^ this->packet_.data[i];
|
||||||
|
this->packet_.data[i] = checksum ^ 0xff;
|
||||||
|
this->packet_.length++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Am43Packet *Am43Encoder::encode_(uint8_t command, uint8_t *data, uint8_t length) {
|
||||||
|
memcpy(this->packet_.data, START_PACKET, 5);
|
||||||
|
this->packet_.data[5] = command;
|
||||||
|
this->packet_.data[6] = length;
|
||||||
|
memcpy(&this->packet_.data[7], data, length);
|
||||||
|
this->packet_.length = length + 7;
|
||||||
|
this->checksum_();
|
||||||
|
ESP_LOGV("am43", "ENC(%d): 0x%s", packet_.length, pkt_to_hex(packet_.data, packet_.length).c_str());
|
||||||
|
return &this->packet_;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define VERIFY_MIN_LENGTH(x) \
|
||||||
|
if (length < (x)) \
|
||||||
|
return;
|
||||||
|
|
||||||
|
void Am43Decoder::decode(const uint8_t *data, uint16_t length) {
|
||||||
|
this->has_battery_level_ = false;
|
||||||
|
this->has_light_level_ = false;
|
||||||
|
this->has_set_position_response_ = false;
|
||||||
|
this->has_set_state_response_ = false;
|
||||||
|
this->has_position_ = false;
|
||||||
|
this->has_pin_response_ = false;
|
||||||
|
ESP_LOGV("am43", "DEC(%d): 0x%s", length, pkt_to_hex(data, length).c_str());
|
||||||
|
|
||||||
|
if (length < 2 || data[0] != 0x9a)
|
||||||
|
return;
|
||||||
|
switch (data[1]) {
|
||||||
|
case CMD_GET_BATTERY_LEVEL: {
|
||||||
|
VERIFY_MIN_LENGTH(8);
|
||||||
|
this->battery_level_ = data[7];
|
||||||
|
this->has_battery_level_ = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CMD_GET_LIGHT_LEVEL: {
|
||||||
|
VERIFY_MIN_LENGTH(5);
|
||||||
|
this->light_level_ = 100 * ((float) data[4] / 9);
|
||||||
|
this->has_light_level_ = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CMD_GET_POSITION: {
|
||||||
|
VERIFY_MIN_LENGTH(6);
|
||||||
|
this->position_ = data[5];
|
||||||
|
this->has_position_ = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CMD_NOTIFY_POSITION: {
|
||||||
|
VERIFY_MIN_LENGTH(5);
|
||||||
|
this->position_ = data[4];
|
||||||
|
this->has_position_ = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CMD_SEND_PIN: {
|
||||||
|
VERIFY_MIN_LENGTH(4);
|
||||||
|
this->pin_ok_ = data[3] == RESPONSE_ACK;
|
||||||
|
this->has_pin_response_ = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CMD_SET_POSITION: {
|
||||||
|
VERIFY_MIN_LENGTH(4);
|
||||||
|
this->set_position_ok_ = data[3] == RESPONSE_ACK;
|
||||||
|
this->has_set_position_response_ = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CMD_SET_STATE: {
|
||||||
|
VERIFY_MIN_LENGTH(4);
|
||||||
|
this->set_state_ok_ = data[3] == RESPONSE_ACK;
|
||||||
|
this->has_set_state_response_ = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace am43
|
||||||
|
} // namespace esphome
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace am43 {
|
||||||
|
|
||||||
|
static const uint16_t AM43_SERVICE_UUID = 0xFE50;
|
||||||
|
static const uint16_t AM43_CHARACTERISTIC_UUID = 0xFE51;
|
||||||
|
//
|
||||||
|
// Tuya identifiers, only to detect and warn users as they are incompatible.
|
||||||
|
static const uint16_t AM43_TUYA_SERVICE_UUID = 0x1910;
|
||||||
|
static const uint16_t AM43_TUYA_CHARACTERISTIC_UUID = 0x2b11;
|
||||||
|
|
||||||
|
struct Am43Packet {
|
||||||
|
uint8_t length;
|
||||||
|
uint8_t data[24];
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint8_t CMD_GET_BATTERY_LEVEL = 0xA2;
|
||||||
|
static const uint8_t CMD_GET_LIGHT_LEVEL = 0xAA;
|
||||||
|
static const uint8_t CMD_GET_POSITION = 0xA7;
|
||||||
|
static const uint8_t CMD_SEND_PIN = 0x17;
|
||||||
|
static const uint8_t CMD_SET_STATE = 0x0A;
|
||||||
|
static const uint8_t CMD_SET_POSITION = 0x0D;
|
||||||
|
static const uint8_t CMD_NOTIFY_POSITION = 0xA1;
|
||||||
|
|
||||||
|
static const uint8_t RESPONSE_ACK = 0x5A;
|
||||||
|
static const uint8_t RESPONSE_NACK = 0xA5;
|
||||||
|
|
||||||
|
class Am43Encoder {
|
||||||
|
public:
|
||||||
|
Am43Packet *get_battery_level_request();
|
||||||
|
Am43Packet *get_light_level_request();
|
||||||
|
Am43Packet *get_position_request();
|
||||||
|
Am43Packet *get_send_pin_request(uint16_t pin);
|
||||||
|
Am43Packet *get_open_request();
|
||||||
|
Am43Packet *get_close_request();
|
||||||
|
Am43Packet *get_stop_request();
|
||||||
|
Am43Packet *get_set_position_request(uint8_t position);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void checksum_();
|
||||||
|
Am43Packet *encode_(uint8_t command, uint8_t *data, uint8_t length);
|
||||||
|
Am43Packet packet_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Am43Decoder {
|
||||||
|
public:
|
||||||
|
void decode(const uint8_t *data, uint16_t length);
|
||||||
|
bool has_battery_level() { return this->has_battery_level_; }
|
||||||
|
bool has_light_level() { return this->has_light_level_; }
|
||||||
|
bool has_set_position_response() { return this->has_set_position_response_; }
|
||||||
|
bool has_set_state_response() { return this->has_set_state_response_; }
|
||||||
|
bool has_position() { return this->has_position_; }
|
||||||
|
bool has_pin_response() { return this->has_pin_response_; }
|
||||||
|
|
||||||
|
union {
|
||||||
|
uint8_t position_;
|
||||||
|
uint8_t battery_level_;
|
||||||
|
float light_level_;
|
||||||
|
uint8_t set_position_ok_;
|
||||||
|
uint8_t set_state_ok_;
|
||||||
|
uint8_t pin_ok_;
|
||||||
|
};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool has_battery_level_;
|
||||||
|
bool has_light_level_;
|
||||||
|
bool has_set_position_response_;
|
||||||
|
bool has_set_state_response_;
|
||||||
|
bool has_position_;
|
||||||
|
bool has_pin_response_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace am43
|
||||||
|
} // namespace esphome
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import cover, ble_client
|
||||||
|
from esphome.const import CONF_ID, CONF_PIN
|
||||||
|
|
||||||
|
CODEOWNERS = ["@buxtronix"]
|
||||||
|
DEPENDENCIES = ["ble_client"]
|
||||||
|
AUTO_LOAD = ["am43"]
|
||||||
|
|
||||||
|
CONF_INVERT_POSITION = "invert_position"
|
||||||
|
|
||||||
|
am43_ns = cg.esphome_ns.namespace("am43")
|
||||||
|
Am43Component = am43_ns.class_(
|
||||||
|
"Am43Component", cover.Cover, ble_client.BLEClientNode, cg.Component
|
||||||
|
)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = (
|
||||||
|
cover.COVER_SCHEMA.extend(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(Am43Component),
|
||||||
|
cv.Optional(CONF_PIN, default=8888): cv.int_range(min=0, max=0xFFFF),
|
||||||
|
cv.Optional(CONF_INVERT_POSITION, default=False): cv.boolean,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(ble_client.BLE_CLIENT_SCHEMA)
|
||||||
|
.extend(cv.COMPONENT_SCHEMA)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
cg.add(var.set_pin(config[CONF_PIN]))
|
||||||
|
cg.add(var.set_invert_position(config[CONF_INVERT_POSITION]))
|
||||||
|
yield cg.register_component(var, config)
|
||||||
|
yield cover.register_cover(var, config)
|
||||||
|
yield ble_client.register_ble_node(var, config)
|
||||||
@@ -0,0 +1,149 @@
|
|||||||
|
#include "am43_cover.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace am43 {
|
||||||
|
|
||||||
|
static const char *TAG = "am43_cover";
|
||||||
|
|
||||||
|
using namespace esphome::cover;
|
||||||
|
|
||||||
|
void Am43Component::dump_config() {
|
||||||
|
LOG_COVER("", "AM43 Cover", this);
|
||||||
|
ESP_LOGCONFIG(TAG, " Device Pin: %d", this->pin_);
|
||||||
|
ESP_LOGCONFIG(TAG, " Invert Position: %d", (int) this->invert_position_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Am43Component::setup() {
|
||||||
|
this->position = COVER_OPEN;
|
||||||
|
this->encoder_ = new Am43Encoder();
|
||||||
|
this->decoder_ = new Am43Decoder();
|
||||||
|
this->logged_in_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Am43Component::loop() {
|
||||||
|
if (this->node_state == espbt::ClientState::Established && !this->logged_in_) {
|
||||||
|
auto packet = this->encoder_->get_send_pin_request(this->pin_);
|
||||||
|
auto status =
|
||||||
|
esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length,
|
||||||
|
packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
|
||||||
|
ESP_LOGI(TAG, "[%s] Logging into AM43", this->get_name().c_str());
|
||||||
|
if (status)
|
||||||
|
ESP_LOGW(TAG, "[%s] Error writing set_pin to device, error = %d", this->get_name().c_str(), status);
|
||||||
|
else
|
||||||
|
this->logged_in_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CoverTraits Am43Component::get_traits() {
|
||||||
|
auto traits = CoverTraits();
|
||||||
|
traits.set_supports_position(true);
|
||||||
|
traits.set_supports_tilt(false);
|
||||||
|
traits.set_is_assumed_state(false);
|
||||||
|
return traits;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Am43Component::control(const CoverCall &call) {
|
||||||
|
if (this->node_state != espbt::ClientState::Established) {
|
||||||
|
ESP_LOGW(TAG, "[%s] Cannot send cover control, not connected", this->get_name().c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (call.get_stop()) {
|
||||||
|
auto packet = this->encoder_->get_stop_request();
|
||||||
|
auto status =
|
||||||
|
esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length,
|
||||||
|
packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
|
||||||
|
if (status)
|
||||||
|
ESP_LOGW(TAG, "[%s] Error writing stop command to device, error = %d", this->get_name().c_str(), status);
|
||||||
|
}
|
||||||
|
if (call.get_position().has_value()) {
|
||||||
|
auto pos = *call.get_position();
|
||||||
|
|
||||||
|
if (this->invert_position_)
|
||||||
|
pos = 1 - pos;
|
||||||
|
auto packet = this->encoder_->get_set_position_request(100 - (uint8_t)(pos * 100));
|
||||||
|
auto status =
|
||||||
|
esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length,
|
||||||
|
packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
|
||||||
|
if (status)
|
||||||
|
ESP_LOGW(TAG, "[%s] Error writing set_position command to device, error = %d", this->get_name().c_str(), status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Am43Component::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||||
|
esp_ble_gattc_cb_param_t *param) {
|
||||||
|
switch (event) {
|
||||||
|
case ESP_GATTC_DISCONNECT_EVT: {
|
||||||
|
this->logged_in_ = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ESP_GATTC_SEARCH_CMPL_EVT: {
|
||||||
|
auto chr = this->parent_->get_characteristic(AM43_SERVICE_UUID, AM43_CHARACTERISTIC_UUID);
|
||||||
|
if (chr == nullptr) {
|
||||||
|
if (this->parent_->get_characteristic(AM43_TUYA_SERVICE_UUID, AM43_TUYA_CHARACTERISTIC_UUID) != nullptr) {
|
||||||
|
ESP_LOGE(TAG, "[%s] Detected a Tuya AM43 which is not supported, sorry.", this->get_name().c_str());
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "[%s] No control service found at device, not an AM43..?", this->get_name().c_str());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this->char_handle_ = chr->handle;
|
||||||
|
|
||||||
|
auto status = esp_ble_gattc_register_for_notify(this->parent_->gattc_if, this->parent_->remote_bda, chr->handle);
|
||||||
|
if (status) {
|
||||||
|
ESP_LOGW(TAG, "[%s] esp_ble_gattc_register_for_notify failed, status=%d", this->get_name().c_str(), status);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
|
||||||
|
this->node_state = espbt::ClientState::Established;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ESP_GATTC_NOTIFY_EVT: {
|
||||||
|
if (param->notify.handle != this->char_handle_)
|
||||||
|
break;
|
||||||
|
this->decoder_->decode(param->notify.value, param->notify.value_len);
|
||||||
|
|
||||||
|
if (this->decoder_->has_position()) {
|
||||||
|
this->position = ((float) this->decoder_->position_ / 100.0);
|
||||||
|
if (!this->invert_position_)
|
||||||
|
this->position = 1 - this->position;
|
||||||
|
if (this->position > 0.97)
|
||||||
|
this->position = 1.0;
|
||||||
|
if (this->position < 0.02)
|
||||||
|
this->position = 0.0;
|
||||||
|
this->publish_state();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->decoder_->has_pin_response()) {
|
||||||
|
if (this->decoder_->pin_ok_) {
|
||||||
|
ESP_LOGI(TAG, "[%s] AM43 pin accepted.", this->get_name().c_str());
|
||||||
|
auto packet = this->encoder_->get_position_request();
|
||||||
|
auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_,
|
||||||
|
packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP,
|
||||||
|
ESP_GATT_AUTH_REQ_NONE);
|
||||||
|
if (status)
|
||||||
|
ESP_LOGW(TAG, "[%s] Error writing set_position to device, error = %d", this->get_name().c_str(), status);
|
||||||
|
} else {
|
||||||
|
ESP_LOGW(TAG, "[%s] AM43 pin rejected!", this->get_name().c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->decoder_->has_set_position_response() && !this->decoder_->set_position_ok_)
|
||||||
|
ESP_LOGW(TAG, "[%s] Got nack after sending set_position. Bad pin?", this->get_name().c_str());
|
||||||
|
|
||||||
|
if (this->decoder_->has_set_state_response() && !this->decoder_->set_state_ok_)
|
||||||
|
ESP_LOGW(TAG, "[%s] Got nack after sending set_state. Bad pin?", this->get_name().c_str());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace am43
|
||||||
|
} // namespace esphome
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/components/ble_client/ble_client.h"
|
||||||
|
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||||
|
#include "esphome/components/cover/cover.h"
|
||||||
|
#include "esphome/components/am43/am43_base.h"
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
|
||||||
|
#include <esp_gattc_api.h>
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace am43 {
|
||||||
|
|
||||||
|
namespace espbt = esphome::esp32_ble_tracker;
|
||||||
|
|
||||||
|
class Am43Component : public cover::Cover, public esphome::ble_client::BLEClientNode, public Component {
|
||||||
|
public:
|
||||||
|
void setup() override;
|
||||||
|
void loop() override;
|
||||||
|
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||||
|
esp_ble_gattc_cb_param_t *param) override;
|
||||||
|
void dump_config() override;
|
||||||
|
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||||
|
cover::CoverTraits get_traits() override;
|
||||||
|
void set_pin(uint16_t pin) { this->pin_ = pin; }
|
||||||
|
void set_invert_position(bool invert_position) { this->invert_position_ = invert_position; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void control(const cover::CoverCall &call) override;
|
||||||
|
uint16_t char_handle_;
|
||||||
|
uint16_t pin_;
|
||||||
|
bool invert_position_;
|
||||||
|
Am43Encoder *encoder_;
|
||||||
|
Am43Decoder *decoder_;
|
||||||
|
bool logged_in_;
|
||||||
|
|
||||||
|
float position_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace am43
|
||||||
|
} // namespace esphome
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import sensor, ble_client
|
||||||
|
from esphome.const import (
|
||||||
|
CONF_ID,
|
||||||
|
CONF_BATTERY_LEVEL,
|
||||||
|
ICON_BATTERY,
|
||||||
|
CONF_ILLUMINANCE,
|
||||||
|
ICON_BRIGHTNESS_5,
|
||||||
|
UNIT_PERCENT,
|
||||||
|
)
|
||||||
|
|
||||||
|
CODEOWNERS = ["@buxtronix"]
|
||||||
|
|
||||||
|
am43_ns = cg.esphome_ns.namespace("am43")
|
||||||
|
Am43 = am43_ns.class_("Am43", ble_client.BLEClientNode, cg.PollingComponent)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = (
|
||||||
|
cv.Schema(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(Am43),
|
||||||
|
cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(
|
||||||
|
UNIT_PERCENT, ICON_BATTERY, 0
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_ILLUMINANCE): sensor.sensor_schema(
|
||||||
|
UNIT_PERCENT, ICON_BRIGHTNESS_5, 0
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(ble_client.BLE_CLIENT_SCHEMA)
|
||||||
|
.extend(cv.polling_component_schema("120s"))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
yield cg.register_component(var, config)
|
||||||
|
yield ble_client.register_ble_node(var, config)
|
||||||
|
|
||||||
|
if CONF_BATTERY_LEVEL in config:
|
||||||
|
sens = yield sensor.new_sensor(config[CONF_BATTERY_LEVEL])
|
||||||
|
cg.add(var.set_battery(sens))
|
||||||
|
|
||||||
|
if CONF_ILLUMINANCE in config:
|
||||||
|
sens = yield sensor.new_sensor(config[CONF_ILLUMINANCE])
|
||||||
|
cg.add(var.set_illuminance(sens))
|
||||||
@@ -90,19 +90,24 @@ void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_
|
|||||||
if (this->codec_->has_running()) {
|
if (this->codec_->has_running()) {
|
||||||
this->mode = this->codec_->running_ ? climate::CLIMATE_MODE_HEAT : climate::CLIMATE_MODE_OFF;
|
this->mode = this->codec_->running_ ? climate::CLIMATE_MODE_HEAT : climate::CLIMATE_MODE_OFF;
|
||||||
}
|
}
|
||||||
|
if (this->codec_->has_unit()) {
|
||||||
|
this->fahrenheit_ = (this->codec_->unit_ == 'f');
|
||||||
|
ESP_LOGD(TAG, "Anova units is %s", this->fahrenheit_ ? "fahrenheit" : "celcius");
|
||||||
|
this->current_request_++;
|
||||||
|
}
|
||||||
this->publish_state();
|
this->publish_state();
|
||||||
|
|
||||||
if (this->current_request_ > 0) {
|
if (this->current_request_ > 1) {
|
||||||
AnovaPacket *pkt = nullptr;
|
AnovaPacket *pkt = nullptr;
|
||||||
switch (this->current_request_++) {
|
switch (this->current_request_++) {
|
||||||
case 1:
|
case 2:
|
||||||
pkt = this->codec_->get_read_target_temp_request();
|
pkt = this->codec_->get_read_target_temp_request();
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 3:
|
||||||
pkt = this->codec_->get_read_current_temp_request();
|
pkt = this->codec_->get_read_current_temp_request();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
this->current_request_ = 0;
|
this->current_request_ = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (pkt != nullptr) {
|
if (pkt != nullptr) {
|
||||||
@@ -121,12 +126,16 @@ void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Anova::set_unit_of_measurement(const char *unit) { this->fahrenheit_ = !strncmp(unit, "f", 1); }
|
||||||
|
|
||||||
void Anova::update() {
|
void Anova::update() {
|
||||||
if (this->node_state != espbt::ClientState::Established)
|
if (this->node_state != espbt::ClientState::Established)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (this->current_request_ == 0) {
|
if (this->current_request_ < 2) {
|
||||||
auto pkt = this->codec_->get_read_device_status_request();
|
auto pkt = this->codec_->get_read_device_status_request();
|
||||||
|
if (this->current_request_ == 0)
|
||||||
|
auto pkt = this->codec_->get_set_unit_request(this->fahrenheit_ ? 'f' : 'c');
|
||||||
auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_,
|
auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_,
|
||||||
pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
|
pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
|
||||||
if (status)
|
if (status)
|
||||||
|
|||||||
@@ -36,12 +36,14 @@ class Anova : public climate::Climate, public esphome::ble_client::BLEClientNode
|
|||||||
traits.set_visual_temperature_step(0.1);
|
traits.set_visual_temperature_step(0.1);
|
||||||
return traits;
|
return traits;
|
||||||
}
|
}
|
||||||
|
void set_unit_of_measurement(const char *);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
AnovaCodec *codec_;
|
AnovaCodec *codec_;
|
||||||
void control(const climate::ClimateCall &call) override;
|
void control(const climate::ClimateCall &call) override;
|
||||||
uint16_t char_handle_;
|
uint16_t char_handle_;
|
||||||
uint8_t current_request_;
|
uint8_t current_request_;
|
||||||
|
bool fahrenheit_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace anova
|
} // namespace anova
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace anova {
|
namespace anova {
|
||||||
|
|
||||||
|
float ftoc(float f) { return (f - 32.0) * (5.0f / 9.0f); }
|
||||||
|
|
||||||
|
float ctof(float c) { return (c * 9.0f / 5.0f) + 32.0; }
|
||||||
|
|
||||||
AnovaPacket *AnovaCodec::clean_packet_() {
|
AnovaPacket *AnovaCodec::clean_packet_() {
|
||||||
this->packet_.length = strlen((char *) this->packet_.data);
|
this->packet_.length = strlen((char *) this->packet_.data);
|
||||||
this->packet_.data[this->packet_.length] = '\0';
|
this->packet_.data[this->packet_.length] = '\0';
|
||||||
@@ -42,6 +46,8 @@ AnovaPacket *AnovaCodec::get_read_data_request() {
|
|||||||
|
|
||||||
AnovaPacket *AnovaCodec::get_set_target_temp_request(float temperature) {
|
AnovaPacket *AnovaCodec::get_set_target_temp_request(float temperature) {
|
||||||
this->current_query_ = SET_TARGET_TEMPERATURE;
|
this->current_query_ = SET_TARGET_TEMPERATURE;
|
||||||
|
if (this->fahrenheit_)
|
||||||
|
temperature = ctof(temperature);
|
||||||
sprintf((char *) this->packet_.data, CMD_SET_TARGET_TEMP, temperature);
|
sprintf((char *) this->packet_.data, CMD_SET_TARGET_TEMP, temperature);
|
||||||
return this->clean_packet_();
|
return this->clean_packet_();
|
||||||
}
|
}
|
||||||
@@ -67,7 +73,6 @@ AnovaPacket *AnovaCodec::get_stop_request() {
|
|||||||
void AnovaCodec::decode(const uint8_t *data, uint16_t length) {
|
void AnovaCodec::decode(const uint8_t *data, uint16_t length) {
|
||||||
memset(this->buf_, 0, 32);
|
memset(this->buf_, 0, 32);
|
||||||
strncpy(this->buf_, (char *) data, length);
|
strncpy(this->buf_, (char *) data, length);
|
||||||
ESP_LOGV("anova", "Received: %s\n", this->buf_);
|
|
||||||
this->has_target_temp_ = this->has_current_temp_ = this->has_unit_ = this->has_running_ = false;
|
this->has_target_temp_ = this->has_current_temp_ = this->has_unit_ = this->has_running_ = false;
|
||||||
switch (this->current_query_) {
|
switch (this->current_query_) {
|
||||||
case READ_DEVICE_STATUS: {
|
case READ_DEVICE_STATUS: {
|
||||||
@@ -97,19 +102,32 @@ void AnovaCodec::decode(const uint8_t *data, uint16_t length) {
|
|||||||
}
|
}
|
||||||
case READ_TARGET_TEMPERATURE: {
|
case READ_TARGET_TEMPERATURE: {
|
||||||
this->target_temp_ = strtof(this->buf_, nullptr);
|
this->target_temp_ = strtof(this->buf_, nullptr);
|
||||||
|
if (this->fahrenheit_)
|
||||||
|
this->target_temp_ = ftoc(this->target_temp_);
|
||||||
this->has_target_temp_ = true;
|
this->has_target_temp_ = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SET_TARGET_TEMPERATURE: {
|
case SET_TARGET_TEMPERATURE: {
|
||||||
this->target_temp_ = strtof(this->buf_, nullptr);
|
this->target_temp_ = strtof(this->buf_, nullptr);
|
||||||
|
if (this->fahrenheit_)
|
||||||
|
this->target_temp_ = ftoc(this->target_temp_);
|
||||||
this->has_target_temp_ = true;
|
this->has_target_temp_ = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case READ_CURRENT_TEMPERATURE: {
|
case READ_CURRENT_TEMPERATURE: {
|
||||||
this->current_temp_ = strtof(this->buf_, nullptr);
|
this->current_temp_ = strtof(this->buf_, nullptr);
|
||||||
|
if (this->fahrenheit_)
|
||||||
|
this->current_temp_ = ftoc(this->current_temp_);
|
||||||
this->has_current_temp_ = true;
|
this->has_current_temp_ = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case SET_UNIT:
|
||||||
|
case READ_UNIT: {
|
||||||
|
this->unit_ = this->buf_[0];
|
||||||
|
this->fahrenheit_ = this->buf_[0] == 'f';
|
||||||
|
this->has_unit_ = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ class AnovaCodec {
|
|||||||
bool has_unit_;
|
bool has_unit_;
|
||||||
bool has_running_;
|
bool has_running_;
|
||||||
char buf_[32];
|
char buf_[32];
|
||||||
|
bool fahrenheit_;
|
||||||
|
|
||||||
CurrentQuery current_query_;
|
CurrentQuery current_query_;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.components import climate, ble_client
|
from esphome.components import climate, ble_client
|
||||||
from esphome.const import CONF_ID
|
from esphome.const import CONF_ID, CONF_UNIT_OF_MEASUREMENT
|
||||||
|
|
||||||
|
UNITS = {
|
||||||
|
"f": "f",
|
||||||
|
"c": "c",
|
||||||
|
}
|
||||||
|
|
||||||
CODEOWNERS = ["@buxtronix"]
|
CODEOWNERS = ["@buxtronix"]
|
||||||
DEPENDENCIES = ["ble_client"]
|
DEPENDENCIES = ["ble_client"]
|
||||||
@@ -12,14 +17,20 @@ Anova = anova_ns.class_(
|
|||||||
)
|
)
|
||||||
|
|
||||||
CONFIG_SCHEMA = (
|
CONFIG_SCHEMA = (
|
||||||
climate.CLIMATE_SCHEMA.extend({cv.GenerateID(): cv.declare_id(Anova)})
|
climate.CLIMATE_SCHEMA.extend(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(Anova),
|
||||||
|
cv.Required(CONF_UNIT_OF_MEASUREMENT): cv.enum(UNITS),
|
||||||
|
}
|
||||||
|
)
|
||||||
.extend(ble_client.BLE_CLIENT_SCHEMA)
|
.extend(ble_client.BLE_CLIENT_SCHEMA)
|
||||||
.extend(cv.polling_component_schema("60s"))
|
.extend(cv.polling_component_schema("60s"))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
yield cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
yield climate.register_climate(var, config)
|
await climate.register_climate(var, config)
|
||||||
yield ble_client.register_ble_node(var, config)
|
await ble_client.register_ble_node(var, config)
|
||||||
|
cg.add(var.set_unit_of_measurement(config[CONF_UNIT_OF_MEASUREMENT]))
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import esphome.config_validation as cv
|
|||||||
from esphome.components import sensor
|
from esphome.components import sensor
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_TYPE,
|
CONF_TYPE,
|
||||||
DEVICE_CLASS_EMPTY,
|
|
||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_MEASUREMENT,
|
||||||
UNIT_PERCENT,
|
UNIT_PERCENT,
|
||||||
ICON_LIGHTBULB,
|
ICON_LIGHTBULB,
|
||||||
@@ -21,7 +20,10 @@ TYPES = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
CONFIG_SCHEMA = sensor.sensor_schema(
|
CONFIG_SCHEMA = sensor.sensor_schema(
|
||||||
UNIT_PERCENT, ICON_LIGHTBULB, 1, DEVICE_CLASS_EMPTY, STATE_CLASS_MEASUREMENT
|
unit_of_measurement=UNIT_PERCENT,
|
||||||
|
icon=ICON_LIGHTBULB,
|
||||||
|
accuracy_decimals=1,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
).extend(
|
).extend(
|
||||||
{
|
{
|
||||||
cv.Required(CONF_TYPE): cv.one_of(*TYPES, upper=True),
|
cv.Required(CONF_TYPE): cv.one_of(*TYPES, upper=True),
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ service APIConnection {
|
|||||||
rpc camera_image (CameraImageRequest) returns (void) {}
|
rpc camera_image (CameraImageRequest) returns (void) {}
|
||||||
rpc climate_command (ClimateCommandRequest) returns (void) {}
|
rpc climate_command (ClimateCommandRequest) returns (void) {}
|
||||||
rpc number_command (NumberCommandRequest) returns (void) {}
|
rpc number_command (NumberCommandRequest) returns (void) {}
|
||||||
|
rpc select_command (SelectCommandRequest) returns (void) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -213,6 +214,7 @@ message ListEntitiesBinarySensorResponse {
|
|||||||
|
|
||||||
string device_class = 5;
|
string device_class = 5;
|
||||||
bool is_status_binary_sensor = 6;
|
bool is_status_binary_sensor = 6;
|
||||||
|
bool disabled_by_default = 7;
|
||||||
}
|
}
|
||||||
message BinarySensorStateResponse {
|
message BinarySensorStateResponse {
|
||||||
option (id) = 21;
|
option (id) = 21;
|
||||||
@@ -242,6 +244,7 @@ message ListEntitiesCoverResponse {
|
|||||||
bool supports_position = 6;
|
bool supports_position = 6;
|
||||||
bool supports_tilt = 7;
|
bool supports_tilt = 7;
|
||||||
string device_class = 8;
|
string device_class = 8;
|
||||||
|
bool disabled_by_default = 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum LegacyCoverState {
|
enum LegacyCoverState {
|
||||||
@@ -309,6 +312,7 @@ message ListEntitiesFanResponse {
|
|||||||
bool supports_speed = 6;
|
bool supports_speed = 6;
|
||||||
bool supports_direction = 7;
|
bool supports_direction = 7;
|
||||||
int32 supported_speed_count = 8;
|
int32 supported_speed_count = 8;
|
||||||
|
bool disabled_by_default = 9;
|
||||||
}
|
}
|
||||||
enum FanSpeed {
|
enum FanSpeed {
|
||||||
FAN_SPEED_LOW = 0;
|
FAN_SPEED_LOW = 0;
|
||||||
@@ -352,6 +356,18 @@ message FanCommandRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ==================== LIGHT ====================
|
// ==================== LIGHT ====================
|
||||||
|
enum ColorMode {
|
||||||
|
COLOR_MODE_UNKNOWN = 0;
|
||||||
|
COLOR_MODE_ON_OFF = 1;
|
||||||
|
COLOR_MODE_BRIGHTNESS = 2;
|
||||||
|
COLOR_MODE_WHITE = 7;
|
||||||
|
COLOR_MODE_COLOR_TEMPERATURE = 11;
|
||||||
|
COLOR_MODE_COLD_WARM_WHITE = 19;
|
||||||
|
COLOR_MODE_RGB = 35;
|
||||||
|
COLOR_MODE_RGB_WHITE = 39;
|
||||||
|
COLOR_MODE_RGB_COLOR_TEMPERATURE = 47;
|
||||||
|
COLOR_MODE_RGB_COLD_WARM_WHITE = 51;
|
||||||
|
}
|
||||||
message ListEntitiesLightResponse {
|
message ListEntitiesLightResponse {
|
||||||
option (id) = 15;
|
option (id) = 15;
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
@@ -362,13 +378,16 @@ message ListEntitiesLightResponse {
|
|||||||
string name = 3;
|
string name = 3;
|
||||||
string unique_id = 4;
|
string unique_id = 4;
|
||||||
|
|
||||||
bool supports_brightness = 5;
|
repeated ColorMode supported_color_modes = 12;
|
||||||
bool supports_rgb = 6;
|
// next four supports_* are for legacy clients, newer clients should use color modes
|
||||||
bool supports_white_value = 7;
|
bool legacy_supports_brightness = 5 [deprecated=true];
|
||||||
bool supports_color_temperature = 8;
|
bool legacy_supports_rgb = 6 [deprecated=true];
|
||||||
|
bool legacy_supports_white_value = 7 [deprecated=true];
|
||||||
|
bool legacy_supports_color_temperature = 8 [deprecated=true];
|
||||||
float min_mireds = 9;
|
float min_mireds = 9;
|
||||||
float max_mireds = 10;
|
float max_mireds = 10;
|
||||||
repeated string effects = 11;
|
repeated string effects = 11;
|
||||||
|
bool disabled_by_default = 13;
|
||||||
}
|
}
|
||||||
message LightStateResponse {
|
message LightStateResponse {
|
||||||
option (id) = 24;
|
option (id) = 24;
|
||||||
@@ -379,12 +398,15 @@ message LightStateResponse {
|
|||||||
fixed32 key = 1;
|
fixed32 key = 1;
|
||||||
bool state = 2;
|
bool state = 2;
|
||||||
float brightness = 3;
|
float brightness = 3;
|
||||||
|
ColorMode color_mode = 11;
|
||||||
float color_brightness = 10;
|
float color_brightness = 10;
|
||||||
float red = 4;
|
float red = 4;
|
||||||
float green = 5;
|
float green = 5;
|
||||||
float blue = 6;
|
float blue = 6;
|
||||||
float white = 7;
|
float white = 7;
|
||||||
float color_temperature = 8;
|
float color_temperature = 8;
|
||||||
|
float cold_white = 12;
|
||||||
|
float warm_white = 13;
|
||||||
string effect = 9;
|
string effect = 9;
|
||||||
}
|
}
|
||||||
message LightCommandRequest {
|
message LightCommandRequest {
|
||||||
@@ -398,6 +420,8 @@ message LightCommandRequest {
|
|||||||
bool state = 3;
|
bool state = 3;
|
||||||
bool has_brightness = 4;
|
bool has_brightness = 4;
|
||||||
float brightness = 5;
|
float brightness = 5;
|
||||||
|
bool has_color_mode = 22;
|
||||||
|
ColorMode color_mode = 23;
|
||||||
bool has_color_brightness = 20;
|
bool has_color_brightness = 20;
|
||||||
float color_brightness = 21;
|
float color_brightness = 21;
|
||||||
bool has_rgb = 6;
|
bool has_rgb = 6;
|
||||||
@@ -408,6 +432,10 @@ message LightCommandRequest {
|
|||||||
float white = 11;
|
float white = 11;
|
||||||
bool has_color_temperature = 12;
|
bool has_color_temperature = 12;
|
||||||
float color_temperature = 13;
|
float color_temperature = 13;
|
||||||
|
bool has_cold_white = 24;
|
||||||
|
float cold_white = 25;
|
||||||
|
bool has_warm_white = 26;
|
||||||
|
float warm_white = 27;
|
||||||
bool has_transition_length = 14;
|
bool has_transition_length = 14;
|
||||||
uint32 transition_length = 15;
|
uint32 transition_length = 15;
|
||||||
bool has_flash_length = 16;
|
bool has_flash_length = 16;
|
||||||
@@ -420,6 +448,7 @@ message LightCommandRequest {
|
|||||||
enum SensorStateClass {
|
enum SensorStateClass {
|
||||||
STATE_CLASS_NONE = 0;
|
STATE_CLASS_NONE = 0;
|
||||||
STATE_CLASS_MEASUREMENT = 1;
|
STATE_CLASS_MEASUREMENT = 1;
|
||||||
|
STATE_CLASS_TOTAL_INCREASING = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum SensorLastResetType {
|
enum SensorLastResetType {
|
||||||
@@ -445,6 +474,7 @@ message ListEntitiesSensorResponse {
|
|||||||
string device_class = 9;
|
string device_class = 9;
|
||||||
SensorStateClass state_class = 10;
|
SensorStateClass state_class = 10;
|
||||||
SensorLastResetType last_reset_type = 11;
|
SensorLastResetType last_reset_type = 11;
|
||||||
|
bool disabled_by_default = 12;
|
||||||
}
|
}
|
||||||
message SensorStateResponse {
|
message SensorStateResponse {
|
||||||
option (id) = 25;
|
option (id) = 25;
|
||||||
@@ -472,6 +502,7 @@ message ListEntitiesSwitchResponse {
|
|||||||
|
|
||||||
string icon = 5;
|
string icon = 5;
|
||||||
bool assumed_state = 6;
|
bool assumed_state = 6;
|
||||||
|
bool disabled_by_default = 7;
|
||||||
}
|
}
|
||||||
message SwitchStateResponse {
|
message SwitchStateResponse {
|
||||||
option (id) = 26;
|
option (id) = 26;
|
||||||
@@ -504,6 +535,7 @@ message ListEntitiesTextSensorResponse {
|
|||||||
string unique_id = 4;
|
string unique_id = 4;
|
||||||
|
|
||||||
string icon = 5;
|
string icon = 5;
|
||||||
|
bool disabled_by_default = 6;
|
||||||
}
|
}
|
||||||
message TextSensorStateResponse {
|
message TextSensorStateResponse {
|
||||||
option (id) = 27;
|
option (id) = 27;
|
||||||
@@ -524,9 +556,10 @@ enum LogLevel {
|
|||||||
LOG_LEVEL_ERROR = 1;
|
LOG_LEVEL_ERROR = 1;
|
||||||
LOG_LEVEL_WARN = 2;
|
LOG_LEVEL_WARN = 2;
|
||||||
LOG_LEVEL_INFO = 3;
|
LOG_LEVEL_INFO = 3;
|
||||||
LOG_LEVEL_DEBUG = 4;
|
LOG_LEVEL_CONFIG = 4;
|
||||||
LOG_LEVEL_VERBOSE = 5;
|
LOG_LEVEL_DEBUG = 5;
|
||||||
LOG_LEVEL_VERY_VERBOSE = 6;
|
LOG_LEVEL_VERBOSE = 6;
|
||||||
|
LOG_LEVEL_VERY_VERBOSE = 7;
|
||||||
}
|
}
|
||||||
message SubscribeLogsRequest {
|
message SubscribeLogsRequest {
|
||||||
option (id) = 28;
|
option (id) = 28;
|
||||||
@@ -541,7 +574,6 @@ message SubscribeLogsResponse {
|
|||||||
option (no_delay) = false;
|
option (no_delay) = false;
|
||||||
|
|
||||||
LogLevel level = 1;
|
LogLevel level = 1;
|
||||||
string tag = 2;
|
|
||||||
string message = 3;
|
string message = 3;
|
||||||
bool send_failed = 4;
|
bool send_failed = 4;
|
||||||
}
|
}
|
||||||
@@ -663,6 +695,7 @@ message ListEntitiesCameraResponse {
|
|||||||
fixed32 key = 2;
|
fixed32 key = 2;
|
||||||
string name = 3;
|
string name = 3;
|
||||||
string unique_id = 4;
|
string unique_id = 4;
|
||||||
|
bool disabled_by_default = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
message CameraImageResponse {
|
message CameraImageResponse {
|
||||||
@@ -755,6 +788,7 @@ message ListEntitiesClimateResponse {
|
|||||||
repeated string supported_custom_fan_modes = 15;
|
repeated string supported_custom_fan_modes = 15;
|
||||||
repeated ClimatePreset supported_presets = 16;
|
repeated ClimatePreset supported_presets = 16;
|
||||||
repeated string supported_custom_presets = 17;
|
repeated string supported_custom_presets = 17;
|
||||||
|
bool disabled_by_default = 18;
|
||||||
}
|
}
|
||||||
message ClimateStateResponse {
|
message ClimateStateResponse {
|
||||||
option (id) = 47;
|
option (id) = 47;
|
||||||
@@ -822,6 +856,7 @@ message ListEntitiesNumberResponse {
|
|||||||
float min_value = 6;
|
float min_value = 6;
|
||||||
float max_value = 7;
|
float max_value = 7;
|
||||||
float step = 8;
|
float step = 8;
|
||||||
|
bool disabled_by_default = 9;
|
||||||
}
|
}
|
||||||
message NumberStateResponse {
|
message NumberStateResponse {
|
||||||
option (id) = 50;
|
option (id) = 50;
|
||||||
@@ -844,3 +879,40 @@ message NumberCommandRequest {
|
|||||||
fixed32 key = 1;
|
fixed32 key = 1;
|
||||||
float state = 2;
|
float state = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== SELECT ====================
|
||||||
|
message ListEntitiesSelectResponse {
|
||||||
|
option (id) = 52;
|
||||||
|
option (source) = SOURCE_SERVER;
|
||||||
|
option (ifdef) = "USE_SELECT";
|
||||||
|
|
||||||
|
string object_id = 1;
|
||||||
|
fixed32 key = 2;
|
||||||
|
string name = 3;
|
||||||
|
string unique_id = 4;
|
||||||
|
|
||||||
|
string icon = 5;
|
||||||
|
repeated string options = 6;
|
||||||
|
bool disabled_by_default = 7;
|
||||||
|
}
|
||||||
|
message SelectStateResponse {
|
||||||
|
option (id) = 53;
|
||||||
|
option (source) = SOURCE_SERVER;
|
||||||
|
option (ifdef) = "USE_SELECT";
|
||||||
|
option (no_delay) = true;
|
||||||
|
|
||||||
|
fixed32 key = 1;
|
||||||
|
string state = 2;
|
||||||
|
// If the select does not have a valid state yet.
|
||||||
|
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
|
||||||
|
bool missing_state = 3;
|
||||||
|
}
|
||||||
|
message SelectCommandRequest {
|
||||||
|
option (id) = 54;
|
||||||
|
option (source) = SOURCE_CLIENT;
|
||||||
|
option (ifdef) = "USE_SELECT";
|
||||||
|
option (no_delay) = true;
|
||||||
|
|
||||||
|
fixed32 key = 1;
|
||||||
|
string state = 2;
|
||||||
|
}
|
||||||
|
|||||||
@@ -176,6 +176,7 @@ bool APIConnection::send_binary_sensor_info(binary_sensor::BinarySensor *binary_
|
|||||||
msg.unique_id = get_default_unique_id("binary_sensor", binary_sensor);
|
msg.unique_id = get_default_unique_id("binary_sensor", binary_sensor);
|
||||||
msg.device_class = binary_sensor->get_device_class();
|
msg.device_class = binary_sensor->get_device_class();
|
||||||
msg.is_status_binary_sensor = binary_sensor->is_status_binary_sensor();
|
msg.is_status_binary_sensor = binary_sensor->is_status_binary_sensor();
|
||||||
|
msg.disabled_by_default = binary_sensor->is_disabled_by_default();
|
||||||
return this->send_list_entities_binary_sensor_response(msg);
|
return this->send_list_entities_binary_sensor_response(msg);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -207,6 +208,7 @@ bool APIConnection::send_cover_info(cover::Cover *cover) {
|
|||||||
msg.supports_position = traits.get_supports_position();
|
msg.supports_position = traits.get_supports_position();
|
||||||
msg.supports_tilt = traits.get_supports_tilt();
|
msg.supports_tilt = traits.get_supports_tilt();
|
||||||
msg.device_class = cover->get_device_class();
|
msg.device_class = cover->get_device_class();
|
||||||
|
msg.disabled_by_default = cover->is_disabled_by_default();
|
||||||
return this->send_list_entities_cover_response(msg);
|
return this->send_list_entities_cover_response(msg);
|
||||||
}
|
}
|
||||||
void APIConnection::cover_command(const CoverCommandRequest &msg) {
|
void APIConnection::cover_command(const CoverCommandRequest &msg) {
|
||||||
@@ -268,6 +270,7 @@ bool APIConnection::send_fan_info(fan::FanState *fan) {
|
|||||||
msg.supports_speed = traits.supports_speed();
|
msg.supports_speed = traits.supports_speed();
|
||||||
msg.supports_direction = traits.supports_direction();
|
msg.supports_direction = traits.supports_direction();
|
||||||
msg.supported_speed_count = traits.supported_speed_count();
|
msg.supported_speed_count = traits.supported_speed_count();
|
||||||
|
msg.disabled_by_default = fan->is_disabled_by_default();
|
||||||
return this->send_list_entities_fan_response(msg);
|
return this->send_list_entities_fan_response(msg);
|
||||||
}
|
}
|
||||||
void APIConnection::fan_command(const FanCommandRequest &msg) {
|
void APIConnection::fan_command(const FanCommandRequest &msg) {
|
||||||
@@ -301,22 +304,21 @@ bool APIConnection::send_light_state(light::LightState *light) {
|
|||||||
|
|
||||||
auto traits = light->get_traits();
|
auto traits = light->get_traits();
|
||||||
auto values = light->remote_values;
|
auto values = light->remote_values;
|
||||||
|
auto color_mode = values.get_color_mode();
|
||||||
LightStateResponse resp{};
|
LightStateResponse resp{};
|
||||||
|
|
||||||
resp.key = light->get_object_id_hash();
|
resp.key = light->get_object_id_hash();
|
||||||
resp.state = values.is_on();
|
resp.state = values.is_on();
|
||||||
if (traits.get_supports_brightness())
|
resp.color_mode = static_cast<enums::ColorMode>(color_mode);
|
||||||
resp.brightness = values.get_brightness();
|
resp.brightness = values.get_brightness();
|
||||||
if (traits.get_supports_rgb()) {
|
resp.color_brightness = values.get_color_brightness();
|
||||||
resp.color_brightness = values.get_color_brightness();
|
resp.red = values.get_red();
|
||||||
resp.red = values.get_red();
|
resp.green = values.get_green();
|
||||||
resp.green = values.get_green();
|
resp.blue = values.get_blue();
|
||||||
resp.blue = values.get_blue();
|
resp.white = values.get_white();
|
||||||
}
|
resp.color_temperature = values.get_color_temperature();
|
||||||
if (traits.get_supports_rgb_white_value())
|
resp.cold_white = values.get_cold_white();
|
||||||
resp.white = values.get_white();
|
resp.warm_white = values.get_warm_white();
|
||||||
if (traits.get_supports_color_temperature())
|
|
||||||
resp.color_temperature = values.get_color_temperature();
|
|
||||||
if (light->supports_effects())
|
if (light->supports_effects())
|
||||||
resp.effect = light->get_effect_name();
|
resp.effect = light->get_effect_name();
|
||||||
return this->send_light_state_response(resp);
|
return this->send_light_state_response(resp);
|
||||||
@@ -328,11 +330,21 @@ bool APIConnection::send_light_info(light::LightState *light) {
|
|||||||
msg.object_id = light->get_object_id();
|
msg.object_id = light->get_object_id();
|
||||||
msg.name = light->get_name();
|
msg.name = light->get_name();
|
||||||
msg.unique_id = get_default_unique_id("light", light);
|
msg.unique_id = get_default_unique_id("light", light);
|
||||||
msg.supports_brightness = traits.get_supports_brightness();
|
|
||||||
msg.supports_rgb = traits.get_supports_rgb();
|
msg.disabled_by_default = light->is_disabled_by_default();
|
||||||
msg.supports_white_value = traits.get_supports_rgb_white_value();
|
|
||||||
msg.supports_color_temperature = traits.get_supports_color_temperature();
|
for (auto mode : traits.get_supported_color_modes())
|
||||||
if (msg.supports_color_temperature) {
|
msg.supported_color_modes.push_back(static_cast<enums::ColorMode>(mode));
|
||||||
|
|
||||||
|
msg.legacy_supports_brightness = traits.supports_color_capability(light::ColorCapability::BRIGHTNESS);
|
||||||
|
msg.legacy_supports_rgb = traits.supports_color_capability(light::ColorCapability::RGB);
|
||||||
|
msg.legacy_supports_white_value =
|
||||||
|
msg.legacy_supports_rgb && (traits.supports_color_capability(light::ColorCapability::WHITE) ||
|
||||||
|
traits.supports_color_capability(light::ColorCapability::COLD_WARM_WHITE));
|
||||||
|
msg.legacy_supports_color_temperature = traits.supports_color_capability(light::ColorCapability::COLOR_TEMPERATURE) ||
|
||||||
|
traits.supports_color_capability(light::ColorCapability::COLD_WARM_WHITE);
|
||||||
|
|
||||||
|
if (msg.legacy_supports_color_temperature) {
|
||||||
msg.min_mireds = traits.get_min_mireds();
|
msg.min_mireds = traits.get_min_mireds();
|
||||||
msg.max_mireds = traits.get_max_mireds();
|
msg.max_mireds = traits.get_max_mireds();
|
||||||
}
|
}
|
||||||
@@ -353,6 +365,8 @@ void APIConnection::light_command(const LightCommandRequest &msg) {
|
|||||||
call.set_state(msg.state);
|
call.set_state(msg.state);
|
||||||
if (msg.has_brightness)
|
if (msg.has_brightness)
|
||||||
call.set_brightness(msg.brightness);
|
call.set_brightness(msg.brightness);
|
||||||
|
if (msg.has_color_mode)
|
||||||
|
call.set_color_mode(static_cast<light::ColorMode>(msg.color_mode));
|
||||||
if (msg.has_color_brightness)
|
if (msg.has_color_brightness)
|
||||||
call.set_color_brightness(msg.color_brightness);
|
call.set_color_brightness(msg.color_brightness);
|
||||||
if (msg.has_rgb) {
|
if (msg.has_rgb) {
|
||||||
@@ -364,6 +378,10 @@ void APIConnection::light_command(const LightCommandRequest &msg) {
|
|||||||
call.set_white(msg.white);
|
call.set_white(msg.white);
|
||||||
if (msg.has_color_temperature)
|
if (msg.has_color_temperature)
|
||||||
call.set_color_temperature(msg.color_temperature);
|
call.set_color_temperature(msg.color_temperature);
|
||||||
|
if (msg.has_cold_white)
|
||||||
|
call.set_cold_white(msg.cold_white);
|
||||||
|
if (msg.has_warm_white)
|
||||||
|
call.set_warm_white(msg.warm_white);
|
||||||
if (msg.has_transition_length)
|
if (msg.has_transition_length)
|
||||||
call.set_transition_length(msg.transition_length);
|
call.set_transition_length(msg.transition_length);
|
||||||
if (msg.has_flash_length)
|
if (msg.has_flash_length)
|
||||||
@@ -400,6 +418,7 @@ bool APIConnection::send_sensor_info(sensor::Sensor *sensor) {
|
|||||||
msg.device_class = sensor->get_device_class();
|
msg.device_class = sensor->get_device_class();
|
||||||
msg.state_class = static_cast<enums::SensorStateClass>(sensor->state_class);
|
msg.state_class = static_cast<enums::SensorStateClass>(sensor->state_class);
|
||||||
msg.last_reset_type = static_cast<enums::SensorLastResetType>(sensor->last_reset_type);
|
msg.last_reset_type = static_cast<enums::SensorLastResetType>(sensor->last_reset_type);
|
||||||
|
msg.disabled_by_default = sensor->is_disabled_by_default();
|
||||||
|
|
||||||
return this->send_list_entities_sensor_response(msg);
|
return this->send_list_entities_sensor_response(msg);
|
||||||
}
|
}
|
||||||
@@ -423,6 +442,7 @@ bool APIConnection::send_switch_info(switch_::Switch *a_switch) {
|
|||||||
msg.unique_id = get_default_unique_id("switch", a_switch);
|
msg.unique_id = get_default_unique_id("switch", a_switch);
|
||||||
msg.icon = a_switch->get_icon();
|
msg.icon = a_switch->get_icon();
|
||||||
msg.assumed_state = a_switch->assumed_state();
|
msg.assumed_state = a_switch->assumed_state();
|
||||||
|
msg.disabled_by_default = a_switch->is_disabled_by_default();
|
||||||
return this->send_list_entities_switch_response(msg);
|
return this->send_list_entities_switch_response(msg);
|
||||||
}
|
}
|
||||||
void APIConnection::switch_command(const SwitchCommandRequest &msg) {
|
void APIConnection::switch_command(const SwitchCommandRequest &msg) {
|
||||||
@@ -457,6 +477,7 @@ bool APIConnection::send_text_sensor_info(text_sensor::TextSensor *text_sensor)
|
|||||||
if (msg.unique_id.empty())
|
if (msg.unique_id.empty())
|
||||||
msg.unique_id = get_default_unique_id("text_sensor", text_sensor);
|
msg.unique_id = get_default_unique_id("text_sensor", text_sensor);
|
||||||
msg.icon = text_sensor->get_icon();
|
msg.icon = text_sensor->get_icon();
|
||||||
|
msg.disabled_by_default = text_sensor->is_disabled_by_default();
|
||||||
return this->send_list_entities_text_sensor_response(msg);
|
return this->send_list_entities_text_sensor_response(msg);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -500,6 +521,9 @@ bool APIConnection::send_climate_info(climate::Climate *climate) {
|
|||||||
msg.object_id = climate->get_object_id();
|
msg.object_id = climate->get_object_id();
|
||||||
msg.name = climate->get_name();
|
msg.name = climate->get_name();
|
||||||
msg.unique_id = get_default_unique_id("climate", climate);
|
msg.unique_id = get_default_unique_id("climate", climate);
|
||||||
|
|
||||||
|
msg.disabled_by_default = climate->is_disabled_by_default();
|
||||||
|
|
||||||
msg.supports_current_temperature = traits.get_supports_current_temperature();
|
msg.supports_current_temperature = traits.get_supports_current_temperature();
|
||||||
msg.supports_two_point_target_temperature = traits.get_supports_two_point_target_temperature();
|
msg.supports_two_point_target_temperature = traits.get_supports_two_point_target_temperature();
|
||||||
|
|
||||||
@@ -572,6 +596,7 @@ bool APIConnection::send_number_info(number::Number *number) {
|
|||||||
msg.name = number->get_name();
|
msg.name = number->get_name();
|
||||||
msg.unique_id = get_default_unique_id("number", number);
|
msg.unique_id = get_default_unique_id("number", number);
|
||||||
msg.icon = number->traits.get_icon();
|
msg.icon = number->traits.get_icon();
|
||||||
|
msg.disabled_by_default = number->is_disabled_by_default();
|
||||||
|
|
||||||
msg.min_value = number->traits.get_min_value();
|
msg.min_value = number->traits.get_min_value();
|
||||||
msg.max_value = number->traits.get_max_value();
|
msg.max_value = number->traits.get_max_value();
|
||||||
@@ -590,6 +615,42 @@ void APIConnection::number_command(const NumberCommandRequest &msg) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_SELECT
|
||||||
|
bool APIConnection::send_select_state(select::Select *select, std::string state) {
|
||||||
|
if (!this->state_subscription_)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
SelectStateResponse resp{};
|
||||||
|
resp.key = select->get_object_id_hash();
|
||||||
|
resp.state = std::move(state);
|
||||||
|
resp.missing_state = !select->has_state();
|
||||||
|
return this->send_select_state_response(resp);
|
||||||
|
}
|
||||||
|
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();
|
||||||
|
msg.unique_id = get_default_unique_id("select", select);
|
||||||
|
msg.icon = select->traits.get_icon();
|
||||||
|
msg.disabled_by_default = select->is_disabled_by_default();
|
||||||
|
|
||||||
|
for (const auto &option : select->traits.get_options())
|
||||||
|
msg.options.push_back(option);
|
||||||
|
|
||||||
|
return this->send_list_entities_select_response(msg);
|
||||||
|
}
|
||||||
|
void APIConnection::select_command(const SelectCommandRequest &msg) {
|
||||||
|
select::Select *select = App.get_select_by_key(msg.key);
|
||||||
|
if (select == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto call = select->make_call();
|
||||||
|
call.set_option(msg.state);
|
||||||
|
call.perform();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef USE_ESP32_CAMERA
|
#ifdef USE_ESP32_CAMERA
|
||||||
void APIConnection::send_camera_state(std::shared_ptr<esp32_camera::CameraImage> image) {
|
void APIConnection::send_camera_state(std::shared_ptr<esp32_camera::CameraImage> image) {
|
||||||
if (!this->state_subscription_)
|
if (!this->state_subscription_)
|
||||||
@@ -604,6 +665,7 @@ bool APIConnection::send_camera_info(esp32_camera::ESP32Camera *camera) {
|
|||||||
msg.object_id = camera->get_object_id();
|
msg.object_id = camera->get_object_id();
|
||||||
msg.name = camera->get_name();
|
msg.name = camera->get_name();
|
||||||
msg.unique_id = get_default_unique_id("camera", camera);
|
msg.unique_id = get_default_unique_id("camera", camera);
|
||||||
|
msg.disabled_by_default = camera->is_disabled_by_default();
|
||||||
return this->send_list_entities_camera_response(msg);
|
return this->send_list_entities_camera_response(msg);
|
||||||
}
|
}
|
||||||
void APIConnection::camera_image(const CameraImageRequest &msg) {
|
void APIConnection::camera_image(const CameraImageRequest &msg) {
|
||||||
@@ -632,8 +694,6 @@ bool APIConnection::send_log_message(int level, const char *tag, const char *lin
|
|||||||
auto buffer = this->create_buffer();
|
auto buffer = this->create_buffer();
|
||||||
// LogLevel level = 1;
|
// LogLevel level = 1;
|
||||||
buffer.encode_uint32(1, static_cast<uint32_t>(level));
|
buffer.encode_uint32(1, static_cast<uint32_t>(level));
|
||||||
// string tag = 2;
|
|
||||||
// buffer.encode_string(2, tag, strlen(tag));
|
|
||||||
// string message = 3;
|
// string message = 3;
|
||||||
buffer.encode_string(3, line, strlen(line));
|
buffer.encode_string(3, line, strlen(line));
|
||||||
// SubscribeLogsResponse - 29
|
// SubscribeLogsResponse - 29
|
||||||
@@ -655,7 +715,7 @@ HelloResponse APIConnection::hello(const HelloRequest &msg) {
|
|||||||
|
|
||||||
HelloResponse resp;
|
HelloResponse resp;
|
||||||
resp.api_version_major = 1;
|
resp.api_version_major = 1;
|
||||||
resp.api_version_minor = 5;
|
resp.api_version_minor = 6;
|
||||||
resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")";
|
resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")";
|
||||||
this->connection_state_ = ConnectionState::CONNECTED;
|
this->connection_state_ = ConnectionState::CONNECTED;
|
||||||
return resp;
|
return resp;
|
||||||
|
|||||||
@@ -67,6 +67,11 @@ class APIConnection : public APIServerConnection {
|
|||||||
bool send_number_state(number::Number *number, float state);
|
bool send_number_state(number::Number *number, float state);
|
||||||
bool send_number_info(number::Number *number);
|
bool send_number_info(number::Number *number);
|
||||||
void number_command(const NumberCommandRequest &msg) override;
|
void number_command(const NumberCommandRequest &msg) override;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_SELECT
|
||||||
|
bool send_select_state(select::Select *select, std::string state);
|
||||||
|
bool send_select_info(select::Select *select);
|
||||||
|
void select_command(const SelectCommandRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
bool send_log_message(int level, const char *tag, const char *line);
|
bool send_log_message(int level, const char *tag, const char *line);
|
||||||
void send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
|
void send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -120,6 +120,15 @@ class APIServerConnectionBase : public ProtoService {
|
|||||||
#endif
|
#endif
|
||||||
#ifdef USE_NUMBER
|
#ifdef USE_NUMBER
|
||||||
virtual void on_number_command_request(const NumberCommandRequest &value){};
|
virtual void on_number_command_request(const NumberCommandRequest &value){};
|
||||||
|
#endif
|
||||||
|
#ifdef USE_SELECT
|
||||||
|
bool send_list_entities_select_response(const ListEntitiesSelectResponse &msg);
|
||||||
|
#endif
|
||||||
|
#ifdef USE_SELECT
|
||||||
|
bool send_select_state_response(const SelectStateResponse &msg);
|
||||||
|
#endif
|
||||||
|
#ifdef USE_SELECT
|
||||||
|
virtual void on_select_command_request(const SelectCommandRequest &value){};
|
||||||
#endif
|
#endif
|
||||||
protected:
|
protected:
|
||||||
bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;
|
bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;
|
||||||
@@ -159,6 +168,9 @@ class APIServerConnection : public APIServerConnectionBase {
|
|||||||
#endif
|
#endif
|
||||||
#ifdef USE_NUMBER
|
#ifdef USE_NUMBER
|
||||||
virtual void number_command(const NumberCommandRequest &msg) = 0;
|
virtual void number_command(const NumberCommandRequest &msg) = 0;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_SELECT
|
||||||
|
virtual void select_command(const SelectCommandRequest &msg) = 0;
|
||||||
#endif
|
#endif
|
||||||
protected:
|
protected:
|
||||||
void on_hello_request(const HelloRequest &msg) override;
|
void on_hello_request(const HelloRequest &msg) override;
|
||||||
@@ -194,6 +206,9 @@ class APIServerConnection : public APIServerConnectionBase {
|
|||||||
#ifdef USE_NUMBER
|
#ifdef USE_NUMBER
|
||||||
void on_number_command_request(const NumberCommandRequest &msg) override;
|
void on_number_command_request(const NumberCommandRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_SELECT
|
||||||
|
void on_select_command_request(const SelectCommandRequest &msg) override;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace api
|
} // namespace api
|
||||||
|
|||||||
@@ -206,6 +206,15 @@ void APIServer::on_number_update(number::Number *obj, float state) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_SELECT
|
||||||
|
void APIServer::on_select_update(select::Select *obj, const std::string &state) {
|
||||||
|
if (obj->is_internal())
|
||||||
|
return;
|
||||||
|
for (auto *c : this->clients_)
|
||||||
|
c->send_select_state(obj, state);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
float APIServer::get_setup_priority() const { return setup_priority::AFTER_WIFI; }
|
float APIServer::get_setup_priority() const { return setup_priority::AFTER_WIFI; }
|
||||||
void APIServer::set_port(uint16_t port) { this->port_ = port; }
|
void APIServer::set_port(uint16_t port) { this->port_ = port; }
|
||||||
APIServer *global_api_server = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
APIServer *global_api_server = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "list_entities.h"
|
#include "list_entities.h"
|
||||||
#include "subscribe_state.h"
|
#include "subscribe_state.h"
|
||||||
#include "homeassistant_service.h"
|
|
||||||
#include "user_services.h"
|
#include "user_services.h"
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
@@ -63,6 +62,9 @@ class APIServer : public Component, public Controller {
|
|||||||
#endif
|
#endif
|
||||||
#ifdef USE_NUMBER
|
#ifdef USE_NUMBER
|
||||||
void on_number_update(number::Number *obj, float state) override;
|
void on_number_update(number::Number *obj, float state) override;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_SELECT
|
||||||
|
void on_select_update(select::Select *obj, const std::string &state) override;
|
||||||
#endif
|
#endif
|
||||||
void send_homeassistant_service_call(const HomeassistantServiceResponse &call);
|
void send_homeassistant_service_call(const HomeassistantServiceResponse &call);
|
||||||
void register_user_service(UserServiceDescriptor *descriptor) { this->user_services_.push_back(descriptor); }
|
void register_user_service(UserServiceDescriptor *descriptor) { this->user_services_.push_back(descriptor); }
|
||||||
|
|||||||
@@ -55,5 +55,9 @@ bool ListEntitiesIterator::on_climate(climate::Climate *climate) { return this->
|
|||||||
bool ListEntitiesIterator::on_number(number::Number *number) { return this->client_->send_number_info(number); }
|
bool ListEntitiesIterator::on_number(number::Number *number) { return this->client_->send_number_info(number); }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_SELECT
|
||||||
|
bool ListEntitiesIterator::on_select(select::Select *select) { return this->client_->send_select_info(select); }
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace api
|
} // namespace api
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|||||||
@@ -42,6 +42,9 @@ class ListEntitiesIterator : public ComponentIterator {
|
|||||||
#endif
|
#endif
|
||||||
#ifdef USE_NUMBER
|
#ifdef USE_NUMBER
|
||||||
bool on_number(number::Number *number) override;
|
bool on_number(number::Number *number) override;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_SELECT
|
||||||
|
bool on_select(select::Select *select) override;
|
||||||
#endif
|
#endif
|
||||||
bool on_end() override;
|
bool on_end() override;
|
||||||
|
|
||||||
|
|||||||
@@ -80,11 +80,13 @@ void ProtoMessage::decode(const uint8_t *buffer, size_t length) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
std::string ProtoMessage::dump() const {
|
std::string ProtoMessage::dump() const {
|
||||||
std::string out;
|
std::string out;
|
||||||
this->dump_to(out);
|
this->dump_to(out);
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace api
|
} // namespace api
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|||||||
@@ -1,8 +1,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
|
|
||||||
|
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
|
||||||
|
#define HAS_PROTO_MESSAGE_DUMP
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace api {
|
namespace api {
|
||||||
|
|
||||||
@@ -243,8 +248,10 @@ class ProtoMessage {
|
|||||||
public:
|
public:
|
||||||
virtual void encode(ProtoWriteBuffer buffer) const = 0;
|
virtual void encode(ProtoWriteBuffer buffer) const = 0;
|
||||||
void decode(const uint8_t *buffer, size_t length);
|
void decode(const uint8_t *buffer, size_t length);
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
std::string dump() const;
|
std::string dump() const;
|
||||||
virtual void dump_to(std::string &out) const = 0;
|
virtual void dump_to(std::string &out) const = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual bool decode_varint(uint32_t field_id, ProtoVarInt value) { return false; }
|
virtual bool decode_varint(uint32_t field_id, ProtoVarInt value) { return false; }
|
||||||
|
|||||||
@@ -42,6 +42,11 @@ bool InitialStateIterator::on_number(number::Number *number) {
|
|||||||
return this->client_->send_number_state(number, number->state);
|
return this->client_->send_number_state(number, number->state);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_SELECT
|
||||||
|
bool InitialStateIterator::on_select(select::Select *select) {
|
||||||
|
return this->client_->send_select_state(select, select->state);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
InitialStateIterator::InitialStateIterator(APIServer *server, APIConnection *client)
|
InitialStateIterator::InitialStateIterator(APIServer *server, APIConnection *client)
|
||||||
: ComponentIterator(server), client_(client) {}
|
: ComponentIterator(server), client_(client) {}
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,9 @@ class InitialStateIterator : public ComponentIterator {
|
|||||||
#endif
|
#endif
|
||||||
#ifdef USE_NUMBER
|
#ifdef USE_NUMBER
|
||||||
bool on_number(number::Number *number) override;
|
bool on_number(number::Number *number) override;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_SELECT
|
||||||
|
bool on_select(select::Select *select) override;
|
||||||
#endif
|
#endif
|
||||||
protected:
|
protected:
|
||||||
APIConnection *client_;
|
APIConnection *client_;
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ template<> std::string get_execute_arg_value<std::string>(const ExecuteServiceAr
|
|||||||
template<> std::vector<bool> get_execute_arg_value<std::vector<bool>>(const ExecuteServiceArgument &arg) {
|
template<> std::vector<bool> get_execute_arg_value<std::vector<bool>>(const ExecuteServiceArgument &arg) {
|
||||||
return arg.bool_array;
|
return arg.bool_array;
|
||||||
}
|
}
|
||||||
template<> std::vector<int> get_execute_arg_value<std::vector<int>>(const ExecuteServiceArgument &arg) {
|
template<> std::vector<int32_t> get_execute_arg_value<std::vector<int32_t>>(const ExecuteServiceArgument &arg) {
|
||||||
return arg.int_array;
|
return arg.int_array;
|
||||||
}
|
}
|
||||||
template<> std::vector<float> get_execute_arg_value<std::vector<float>>(const ExecuteServiceArgument &arg) {
|
template<> std::vector<float> get_execute_arg_value<std::vector<float>>(const ExecuteServiceArgument &arg) {
|
||||||
|
|||||||
@@ -182,6 +182,21 @@ void ComponentIterator::advance() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_SELECT
|
||||||
|
case IteratorState::SELECT:
|
||||||
|
if (this->at_ >= App.get_selects().size()) {
|
||||||
|
advance_platform = true;
|
||||||
|
} else {
|
||||||
|
auto *select = App.get_selects()[this->at_];
|
||||||
|
if (select->is_internal()) {
|
||||||
|
success = true;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
success = this->on_select(select);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
#endif
|
#endif
|
||||||
case IteratorState::MAX:
|
case IteratorState::MAX:
|
||||||
if (this->on_end()) {
|
if (this->on_end()) {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user