1
0
mirror of https://github.com/esphome/esphome.git synced 2026-03-20 02:06:46 +01:00

Merge pull request #2176 from esphome/bump-2021.8.0

2021.8.0
This commit is contained in:
Jesse Hills 2021-08-18 15:03:48 +12:00 committed by GitHub
commit 1b8c9edcde
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
435 changed files with 17475 additions and 3805 deletions

View File

@ -14,7 +14,12 @@ Checks: >-
-cert-str34-c,
-clang-analyzer-optin.cplusplus.UninitializedObject,
-clang-analyzer-osx.*,
-clang-diagnostic-delete-abstract-non-virtual-dtor,
-clang-diagnostic-delete-non-abstract-non-virtual-dtor,
-clang-diagnostic-shadow-field,
-clang-diagnostic-sign-compare,
-clang-diagnostic-unused-variable,
-clang-diagnostic-unused-const-variable,
-cppcoreguidelines-avoid-c-arrays,
-cppcoreguidelines-avoid-goto,
-cppcoreguidelines-avoid-magic-numbers,
@ -80,7 +85,6 @@ Checks: >-
-readability-use-anyofallof,
-warnings-as-errors
WarningsAsErrors: '*'
HeaderFilterRegex: '^.*/src/esphome/.*'
AnalyzeTemporaryDtors: false
FormatStyle: google
CheckOptions:

View File

@ -36,26 +36,27 @@ jobs:
container: ghcr.io/esphome/esphome-lint:1.1
steps:
- 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
run: |
echo "::add-matcher::.github/workflows/matchers/clang-tidy.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
run: script/clang-format -i
run: |
script/clang-format -i
git diff-index --quiet HEAD --
if: ${{ matrix.id == 'clang-format' }}
- name: Run clang-tidy
run: script/clang-tidy --all-headers --fix --split-num 4 --split-at ${{ matrix.split }}
if: ${{ matrix.id == 'clang-tidy' }}
- name: Suggest changes
- name: Suggested changes
run: script/ci-suggest-changes
if: always()
ci:
# Don't use the esphome-lint docker image because it may contain outdated requirements.
@ -82,6 +83,9 @@ jobs:
- id: test
file: tests/test4.yaml
name: Test tests/test4.yaml
- id: test
file: tests/test5.yaml
name: Test tests/test5.yaml
- id: pytest
name: Run pytest

1
.gitignore vendored
View File

@ -125,4 +125,5 @@ config/
tests/build/
tests/.esphome/
/.temp-clang-tidy.cpp
/.temp/
.pio/

2
.vscode/tasks.json vendored
View File

@ -10,7 +10,7 @@
{
"label": "clang-tidy",
"type": "shell",
"command": "test -f .gcc-flags.json || pio init --silent --ide atom; ./script/clang-tidy",
"command": "./script/clang-tidy",
"problemMatcher": [
{
"owner": "clang-tidy",

View File

@ -14,6 +14,8 @@ esphome/core/* @esphome/core
esphome/components/ac_dimmer/* @glmnet
esphome/components/adc/* @esphome/core
esphome/components/addressable_light/* @justfalter
esphome/components/am43/* @buxtronix
esphome/components/am43/cover/* @buxtronix
esphome/components/animation/* @syndlex
esphome/components/anova/* @buxtronix
esphome/components/api/* @OttoWinter
@ -29,6 +31,7 @@ esphome/components/canbus/* @danielschramm @mvturnho
esphome/components/captive_portal/* @OttoWinter
esphome/components/climate/* @esphome/core
esphome/components/climate_ir/* @glmnet
esphome/components/color_temperature/* @jesserockz
esphome/components/coolix/* @glmnet
esphome/components/cover/* @esphome/core
esphome/components/cs5460a/* @balrog-kun
@ -37,6 +40,7 @@ esphome/components/debug/* @OttoWinter
esphome/components/dfplayer/* @glmnet
esphome/components/dht/* @OttoWinter
esphome/components/ds1307/* @badbadc0ffee
esphome/components/dsmr/* @glmnet @zuidwijk
esphome/components/esp32_ble/* @jesserockz
esphome/components/esp32_ble_server/* @jesserockz
esphome/components/esp32_improv/* @jesserockz
@ -48,7 +52,9 @@ esphome/components/globals/* @esphome/core
esphome/components/gpio/* @esphome/core
esphome/components/gps/* @coogle
esphome/components/havells_solar/* @sourabhjaiswal
esphome/components/hitachi_ac424/* @sourabhjaiswal
esphome/components/homeassistant/* @OttoWinter
esphome/components/hrxl_maxsonar_wr/* @netmikey
esphome/components/i2c/* @esphome/core
esphome/components/improv/* @jesserockz
esphome/components/inkbird_ibsth1_mini/* @fkirill
@ -83,19 +89,26 @@ esphome/components/number/* @esphome/core
esphome/components/ota/* @esphome/core
esphome/components/output/* @esphome/core
esphome/components/pid/* @OttoWinter
esphome/components/pipsolar/* @andreashergert1984
esphome/components/pmsa003i/* @sjtrny
esphome/components/pn532/* @OttoWinter @jesserockz
esphome/components/pn532_i2c/* @OttoWinter @jesserockz
esphome/components/pn532_spi/* @OttoWinter @jesserockz
esphome/components/power_supply/* @esphome/core
esphome/components/pulse_meter/* @stevebaxter
esphome/components/pvvx_mithermometer/* @pasiz
esphome/components/rc522/* @glmnet
esphome/components/rc522_i2c/* @glmnet
esphome/components/rc522_spi/* @glmnet
esphome/components/restart/* @esphome/core
esphome/components/rf_bridge/* @jesserockz
esphome/components/rgbct/* @jesserockz
esphome/components/rtttl/* @glmnet
esphome/components/script/* @esphome/core
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/sgp40/* @SenexCrenshaw
esphome/components/sht4x/* @sjtrny
@ -119,14 +132,19 @@ esphome/components/st7789v/* @kbx81
esphome/components/substitutions/* @esphome/core
esphome/components/sun/* @OttoWinter
esphome/components/switch/* @esphome/core
esphome/components/t6615/* @tylermenezes
esphome/components/tca9548a/* @andreashergert1984
esphome/components/tcl112/* @glmnet
esphome/components/teleinfo/* @0hax
esphome/components/thermostat/* @kbx81
esphome/components/time/* @OttoWinter
esphome/components/tlc5947/* @rnauber
esphome/components/tm1637/* @glmnet
esphome/components/tmp102/* @timsavage
esphome/components/tmp117/* @Azimath
esphome/components/tof10120/* @wstrzalka
esphome/components/toshiba/* @kbx81
esphome/components/tsl2591/* @wjcarpenter
esphome/components/tuya/binary_sensor/* @jesserockz
esphome/components/tuya/climate/* @jesserockz
esphome/components/tuya/sensor/* @jesserockz

View File

@ -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

View File

@ -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

View File

@ -11,6 +11,7 @@ from esphome.config import iter_components, read_config, strip_default_ids
from esphome.const import (
CONF_BAUD_RATE,
CONF_BROKER,
CONF_DEASSERT_RTS_DTR,
CONF_LOGGER,
CONF_OTA,
CONF_PASSWORD,
@ -99,10 +100,21 @@ def run_miniterm(config, port):
baud_rate = config["logger"][CONF_BAUD_RATE]
if baud_rate == 0:
_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)
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:
try:
raw = ser.readline()
@ -284,7 +296,6 @@ def command_vscode(args):
logging.disable(logging.INFO)
logging.disable(logging.WARNING)
CORE.config_path = args.configuration
vscode.read_config(args)
@ -394,7 +405,7 @@ def command_update_all(args):
import click
success = {}
files = list_yaml_files(args.configuration[0])
files = list_yaml_files(args.configuration)
twidth = 60
def print_bar(middle_text):
@ -408,7 +419,7 @@ def command_update_all(args):
print("-" * twidth)
print()
rc = run_external_process(
"esphome", "--dashboard", "run", "--no-logs", "--device", "OTA", f
"esphome", "--dashboard", "run", f, "--no-logs", "--device", "OTA"
)
if rc == 0:
print_bar("[{}] {}".format(color(Fore.BOLD_GREEN, "SUCCESS"), f))
@ -505,6 +516,7 @@ def parse_args(argv):
"clean",
"dashboard",
"vscode",
"update-all",
],
)
@ -681,14 +693,12 @@ def parse_args(argv):
)
parser_vscode = subparsers.add_parser("vscode")
parser_vscode.add_argument(
"configuration", help="Your YAML configuration file.", nargs=1
)
parser_vscode.add_argument("configuration", help="Your YAML configuration file.")
parser_vscode.add_argument("--ace", action="store_true")
parser_update = subparsers.add_parser("update-all")
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:])

875
esphome/boards.py Normal file
View File

@ -0,0 +1,875 @@
ESP8266_BASE_PINS = {
"A0": 17,
"SS": 15,
"MOSI": 13,
"MISO": 12,
"SCK": 14,
"SDA": 4,
"SCL": 5,
"RX": 3,
"TX": 1,
}
ESP8266_BOARD_PINS = {
"d1": {
"D0": 3,
"D1": 1,
"D2": 16,
"D3": 5,
"D4": 4,
"D5": 14,
"D6": 12,
"D7": 13,
"D8": 0,
"D9": 2,
"D10": 15,
"D11": 13,
"D12": 14,
"D13": 14,
"D14": 4,
"D15": 5,
"LED": 2,
},
"d1_mini": {
"D0": 16,
"D1": 5,
"D2": 4,
"D3": 0,
"D4": 2,
"D5": 14,
"D6": 12,
"D7": 13,
"D8": 15,
"LED": 2,
},
"d1_mini_lite": "d1_mini",
"d1_mini_pro": "d1_mini",
"esp01": {},
"esp01_1m": {},
"esp07": {},
"esp12e": {},
"esp210": {},
"esp8285": {},
"esp_wroom_02": {},
"espduino": {"LED": 16},
"espectro": {"LED": 15, "BUTTON": 2},
"espino": {"LED": 2, "LED_RED": 2, "LED_GREEN": 4, "LED_BLUE": 5, "BUTTON": 0},
"espinotee": {"LED": 16},
"espresso_lite_v1": {"LED": 16},
"espresso_lite_v2": {"LED": 2},
"gen4iod": {},
"heltec_wifi_kit_8": "d1_mini",
"huzzah": {
"LED": 0,
"LED_RED": 0,
"LED_BLUE": 2,
"D4": 4,
"D5": 5,
"D12": 12,
"D13": 13,
"D14": 14,
"D15": 15,
"D16": 16,
},
"inventone": {},
"modwifi": {},
"nodemcu": {
"D0": 16,
"D1": 5,
"D2": 4,
"D3": 0,
"D4": 2,
"D5": 14,
"D6": 12,
"D7": 13,
"D8": 15,
"D9": 3,
"D10": 1,
"LED": 16,
},
"nodemcuv2": "nodemcu",
"oak": {
"P0": 2,
"P1": 5,
"P2": 0,
"P3": 3,
"P4": 1,
"P5": 4,
"P6": 15,
"P7": 13,
"P8": 12,
"P9": 14,
"P10": 16,
"P11": 17,
"LED": 5,
},
"phoenix_v1": {"LED": 16},
"phoenix_v2": {"LED": 2},
"sparkfunBlynk": "thing",
"thing": {"LED": 5, "SDA": 2, "SCL": 14},
"thingdev": "thing",
"wifi_slot": {"LED": 2},
"wifiduino": {
"D0": 3,
"D1": 1,
"D2": 2,
"D3": 0,
"D4": 4,
"D5": 5,
"D6": 16,
"D7": 14,
"D8": 12,
"D9": 13,
"D10": 15,
"D11": 13,
"D12": 12,
"D13": 14,
},
"wifinfo": {
"LED": 12,
"D0": 16,
"D1": 5,
"D2": 4,
"D3": 0,
"D4": 2,
"D5": 14,
"D6": 12,
"D7": 13,
"D8": 15,
"D9": 3,
"D10": 1,
},
"wio_link": {"LED": 2, "GROVE": 15, "D0": 14, "D1": 12, "D2": 13, "BUTTON": 0},
"wio_node": {"LED": 2, "GROVE": 15, "D0": 3, "D1": 5, "BUTTON": 0},
"xinabox_cw01": {"SDA": 2, "SCL": 14, "LED": 5, "LED_RED": 12, "LED_GREEN": 13},
}
FLASH_SIZE_1_MB = 2 ** 20
FLASH_SIZE_512_KB = FLASH_SIZE_1_MB // 2
FLASH_SIZE_2_MB = 2 * FLASH_SIZE_1_MB
FLASH_SIZE_4_MB = 4 * FLASH_SIZE_1_MB
FLASH_SIZE_16_MB = 16 * FLASH_SIZE_1_MB
ESP8266_FLASH_SIZES = {
"d1": FLASH_SIZE_4_MB,
"d1_mini": FLASH_SIZE_4_MB,
"d1_mini_lite": FLASH_SIZE_1_MB,
"d1_mini_pro": FLASH_SIZE_16_MB,
"esp01": FLASH_SIZE_512_KB,
"esp01_1m": FLASH_SIZE_1_MB,
"esp07": FLASH_SIZE_4_MB,
"esp12e": FLASH_SIZE_4_MB,
"esp210": FLASH_SIZE_4_MB,
"esp8285": FLASH_SIZE_1_MB,
"esp_wroom_02": FLASH_SIZE_2_MB,
"espduino": FLASH_SIZE_4_MB,
"espectro": FLASH_SIZE_4_MB,
"espino": FLASH_SIZE_4_MB,
"espinotee": FLASH_SIZE_4_MB,
"espresso_lite_v1": FLASH_SIZE_4_MB,
"espresso_lite_v2": FLASH_SIZE_4_MB,
"gen4iod": FLASH_SIZE_512_KB,
"heltec_wifi_kit_8": FLASH_SIZE_4_MB,
"huzzah": FLASH_SIZE_4_MB,
"inventone": FLASH_SIZE_4_MB,
"modwifi": FLASH_SIZE_2_MB,
"nodemcu": FLASH_SIZE_4_MB,
"nodemcuv2": FLASH_SIZE_4_MB,
"oak": FLASH_SIZE_4_MB,
"phoenix_v1": FLASH_SIZE_4_MB,
"phoenix_v2": FLASH_SIZE_4_MB,
"sparkfunBlynk": FLASH_SIZE_4_MB,
"thing": FLASH_SIZE_512_KB,
"thingdev": FLASH_SIZE_512_KB,
"wifi_slot": FLASH_SIZE_1_MB,
"wifiduino": FLASH_SIZE_4_MB,
"wifinfo": FLASH_SIZE_1_MB,
"wio_link": FLASH_SIZE_4_MB,
"wio_node": FLASH_SIZE_4_MB,
"xinabox_cw01": FLASH_SIZE_4_MB,
}
ESP8266_LD_SCRIPTS = {
FLASH_SIZE_512_KB: ("eagle.flash.512k0.ld", "eagle.flash.512k.ld"),
FLASH_SIZE_1_MB: ("eagle.flash.1m0.ld", "eagle.flash.1m.ld"),
FLASH_SIZE_2_MB: ("eagle.flash.2m.ld", "eagle.flash.2m.ld"),
FLASH_SIZE_4_MB: ("eagle.flash.4m.ld", "eagle.flash.4m.ld"),
FLASH_SIZE_16_MB: ("eagle.flash.16m.ld", "eagle.flash.16m14m.ld"),
}
ESP32_BASE_PINS = {
"TX": 1,
"RX": 3,
"SDA": 21,
"SCL": 22,
"SS": 5,
"MOSI": 23,
"MISO": 19,
"SCK": 18,
"A0": 36,
"A3": 39,
"A4": 32,
"A5": 33,
"A6": 34,
"A7": 35,
"A10": 4,
"A11": 0,
"A12": 2,
"A13": 15,
"A14": 13,
"A15": 12,
"A16": 14,
"A17": 27,
"A18": 25,
"A19": 26,
"T0": 4,
"T1": 0,
"T2": 2,
"T3": 15,
"T4": 13,
"T5": 12,
"T6": 14,
"T7": 27,
"T8": 33,
"T9": 32,
"DAC1": 25,
"DAC2": 26,
"SVP": 36,
"SVN": 39,
}
ESP32_BOARD_PINS = {
"alksesp32": {
"A0": 32,
"A1": 33,
"A2": 25,
"A3": 26,
"A4": 27,
"A5": 14,
"A6": 12,
"A7": 15,
"D0": 40,
"D1": 41,
"D10": 19,
"D11": 21,
"D12": 22,
"D13": 23,
"D2": 15,
"D3": 2,
"D4": 0,
"D5": 4,
"D6": 16,
"D7": 17,
"D8": 5,
"D9": 18,
"DHT_PIN": 26,
"LED": 23,
"L_B": 5,
"L_G": 17,
"L_R": 22,
"L_RGB_B": 16,
"L_RGB_G": 21,
"L_RGB_R": 4,
"L_Y": 23,
"MISO": 22,
"MOSI": 21,
"PHOTO": 25,
"PIEZO1": 19,
"PIEZO2": 18,
"POT1": 32,
"POT2": 33,
"S1": 4,
"S2": 16,
"S3": 18,
"S4": 19,
"S5": 21,
"SCK": 23,
"SCL": 14,
"SDA": 27,
"SS": 19,
"SW1": 15,
"SW2": 2,
"SW3": 0,
},
"bpi-bit": {
"BUTTON_A": 35,
"BUTTON_B": 27,
"BUZZER": 25,
"LIGHT_SENSOR1": 36,
"LIGHT_SENSOR2": 39,
"MPU9250_INT": 0,
"P0": 25,
"P1": 32,
"P10": 26,
"P11": 27,
"P12": 2,
"P13": 18,
"P14": 19,
"P15": 23,
"P16": 5,
"P19": 22,
"P2": 33,
"P20": 21,
"P3": 13,
"P4": 15,
"P5": 35,
"P6": 12,
"P7": 14,
"P8": 16,
"P9": 17,
"RGB_LED": 4,
"TEMPERATURE_SENSOR": 34,
},
"d-duino-32": {
"D1": 5,
"D10": 1,
"D2": 4,
"D3": 0,
"D4": 2,
"D5": 14,
"D6": 12,
"D7": 13,
"D8": 15,
"D9": 3,
"MISO": 12,
"MOSI": 13,
"SCK": 14,
"SCL": 4,
"SDA": 5,
"SS": 15,
},
"esp-wrover-kit": {},
"esp32-devkitlipo": {},
"esp32-evb": {
"BUTTON": 34,
"MISO": 15,
"MOSI": 2,
"SCK": 14,
"SCL": 16,
"SDA": 13,
"SS": 17,
},
"esp32-gateway": {"BUTTON": 34, "LED": 33, "SCL": 16, "SDA": 32},
"esp32-poe-iso": {
"BUTTON": 34,
"MISO": 15,
"MOSI": 2,
"SCK": 14,
"SCL": 16,
"SDA": 13,
},
"esp32-poe": {"BUTTON": 34, "MISO": 15, "MOSI": 2, "SCK": 14, "SCL": 16, "SDA": 13},
"esp32-pro": {
"BUTTON": 34,
"MISO": 15,
"MOSI": 2,
"SCK": 14,
"SCL": 16,
"SDA": 13,
"SS": 17,
},
"esp320": {
"LED": 5,
"MISO": 12,
"MOSI": 13,
"SCK": 14,
"SCL": 14,
"SDA": 2,
"SS": 15,
},
"esp32cam": {},
"esp32dev": {},
"esp32doit-devkit-v1": {"LED": 2},
"esp32thing": {"BUTTON": 0, "LED": 5, "SS": 2},
"esp32vn-iot-uno": {},
"espea32": {"BUTTON": 0, "LED": 5},
"espectro32": {"LED": 15, "SD_SS": 33},
"espino32": {"BUTTON": 0, "LED": 16},
"featheresp32": {
"A0": 26,
"A1": 25,
"A10": 27,
"A11": 12,
"A12": 13,
"A13": 35,
"A2": 34,
"A4": 36,
"A5": 4,
"A6": 14,
"A7": 32,
"A8": 15,
"A9": 33,
"Ax": 2,
"LED": 13,
"MOSI": 18,
"RX": 16,
"SCK": 5,
"SDA": 23,
"SS": 33,
"TX": 17,
},
"firebeetle32": {"LED": 2},
"fm-devkit": {
"D0": 34,
"D1": 35,
"D10": 0,
"D2": 32,
"D3": 33,
"D4": 27,
"D5": 14,
"D6": 12,
"D7": 13,
"D8": 15,
"D9": 23,
"I2S_DOUT": 22,
"I2S_LRCLK": 25,
"I2S_MCLK": 2,
"I2S_SCLK": 26,
"LED": 5,
"SCL": 17,
"SDA": 16,
"SW1": 4,
"SW2": 18,
"SW3": 19,
"SW4": 21,
},
"frogboard": {},
"heltec_wifi_kit_32": {
"A1": 37,
"A2": 38,
"BUTTON": 0,
"LED": 25,
"RST_OLED": 16,
"SCL_OLED": 15,
"SDA_OLED": 4,
"Vext": 21,
},
"heltec_wifi_lora_32": {
"BUTTON": 0,
"DIO0": 26,
"DIO1": 33,
"DIO2": 32,
"LED": 25,
"MOSI": 27,
"RST_LoRa": 14,
"RST_OLED": 16,
"SCK": 5,
"SCL_OLED": 15,
"SDA_OLED": 4,
"SS": 18,
"Vext": 21,
},
"heltec_wifi_lora_32_V2": {
"BUTTON": 0,
"DIO0": 26,
"DIO1": 35,
"DIO2": 34,
"LED": 25,
"MOSI": 27,
"RST_LoRa": 14,
"RST_OLED": 16,
"SCK": 5,
"SCL_OLED": 15,
"SDA_OLED": 4,
"SS": 18,
"Vext": 21,
},
"heltec_wireless_stick": {
"BUTTON": 0,
"DIO0": 26,
"DIO1": 35,
"DIO2": 34,
"LED": 25,
"MOSI": 27,
"RST_LoRa": 14,
"RST_OLED": 16,
"SCK": 5,
"SCL_OLED": 15,
"SDA_OLED": 4,
"SS": 18,
"Vext": 21,
},
"hornbill32dev": {"BUTTON": 0, "LED": 13},
"hornbill32minima": {"SS": 2},
"intorobot": {
"A1": 39,
"A2": 35,
"A3": 25,
"A4": 26,
"A5": 14,
"A6": 12,
"A7": 15,
"A8": 13,
"A9": 2,
"BUTTON": 0,
"D0": 19,
"D1": 23,
"D2": 18,
"D3": 17,
"D4": 16,
"D5": 5,
"D6": 4,
"LED": 4,
"MISO": 17,
"MOSI": 16,
"RGB_B_BUILTIN": 22,
"RGB_G_BUILTIN": 21,
"RGB_R_BUILTIN": 27,
"SCL": 19,
"SDA": 23,
"T0": 19,
"T1": 23,
"T2": 18,
"T3": 17,
"T4": 16,
"T5": 5,
"T6": 4,
},
"iotaap_magnolia": {},
"iotbusio": {},
"iotbusproteus": {},
"lolin32": {"LED": 5},
"lolin32_lite": {"LED": 22},
"lolin_d32": {"LED": 5, "_VBAT": 35},
"lolin_d32_pro": {"LED": 5, "_VBAT": 35},
"lopy": {
"A1": 37,
"A2": 38,
"LED": 0,
"MISO": 37,
"MOSI": 22,
"SCK": 13,
"SCL": 13,
"SDA": 12,
"SS": 17,
},
"lopy4": {
"A1": 37,
"A2": 38,
"LED": 0,
"MISO": 37,
"MOSI": 22,
"SCK": 13,
"SCL": 13,
"SDA": 12,
"SS": 18,
},
"m5stack-core-esp32": {
"ADC1": 35,
"ADC2": 36,
"G0": 0,
"G1": 1,
"G12": 12,
"G13": 13,
"G15": 15,
"G16": 16,
"G17": 17,
"G18": 18,
"G19": 19,
"G2": 2,
"G21": 21,
"G22": 22,
"G23": 23,
"G25": 25,
"G26": 26,
"G3": 3,
"G34": 34,
"G35": 35,
"G36": 36,
"G5": 5,
"RXD2": 16,
"TXD2": 17,
},
"m5stack-fire": {
"ADC1": 35,
"ADC2": 36,
"G0": 0,
"G1": 1,
"G12": 12,
"G13": 13,
"G15": 15,
"G16": 16,
"G17": 17,
"G18": 18,
"G19": 19,
"G2": 2,
"G21": 21,
"G22": 22,
"G23": 23,
"G25": 25,
"G26": 26,
"G3": 3,
"G34": 34,
"G35": 35,
"G36": 36,
"G5": 5,
},
"m5stack-grey": {
"ADC1": 35,
"ADC2": 36,
"G0": 0,
"G1": 1,
"G12": 12,
"G13": 13,
"G15": 15,
"G16": 16,
"G17": 17,
"G18": 18,
"G19": 19,
"G2": 2,
"G21": 21,
"G22": 22,
"G23": 23,
"G25": 25,
"G26": 26,
"G3": 3,
"G34": 34,
"G35": 35,
"G36": 36,
"G5": 5,
"RXD2": 16,
"TXD2": 17,
},
"m5stick-c": {
"ADC1": 35,
"ADC2": 36,
"G0": 0,
"G10": 10,
"G26": 26,
"G32": 32,
"G33": 33,
"G36": 36,
"G37": 37,
"G39": 39,
"G9": 9,
"MISO": 36,
"MOSI": 15,
"SCK": 13,
"SCL": 33,
"SDA": 32,
},
"magicbit": {
"BLUE_LED": 17,
"BUZZER": 25,
"GREEN_LED": 16,
"LDR": 36,
"LED": 16,
"LEFT_BUTTON": 35,
"MOTOR1A": 27,
"MOTOR1B": 18,
"MOTOR2A": 16,
"MOTOR2B": 17,
"POT": 39,
"RED_LED": 27,
"RIGHT_PUTTON": 34,
"YELLOW_LED": 18,
},
"mhetesp32devkit": {"LED": 2},
"mhetesp32minikit": {"LED": 2},
"microduino-core-esp32": {
"A0": 12,
"A1": 13,
"A10": 25,
"A11": 26,
"A12": 27,
"A13": 14,
"A2": 15,
"A3": 4,
"A6": 38,
"A7": 37,
"A8": 32,
"A9": 33,
"D0": 3,
"D1": 1,
"D10": 5,
"D11": 23,
"D12": 19,
"D13": 18,
"D14": 12,
"D15": 13,
"D16": 15,
"D17": 4,
"D18": 22,
"D19": 21,
"D2": 16,
"D20": 38,
"D21": 37,
"D3": 17,
"D4": 32,
"D5": 33,
"D6": 25,
"D7": 26,
"D8": 27,
"D9": 14,
"SCL": 21,
"SCL1": 13,
"SDA": 22,
"SDA1": 12,
},
"nano32": {"BUTTON": 0, "LED": 16},
"nina_w10": {
"D0": 3,
"D1": 1,
"D10": 5,
"D11": 19,
"D12": 23,
"D13": 18,
"D14": 13,
"D15": 12,
"D16": 32,
"D17": 33,
"D18": 21,
"D19": 34,
"D2": 26,
"D20": 36,
"D21": 39,
"D3": 25,
"D4": 35,
"D5": 27,
"D6": 22,
"D7": 0,
"D8": 15,
"D9": 14,
"LED_BLUE": 21,
"LED_GREEN": 33,
"LED_RED": 23,
"SCL": 13,
"SDA": 12,
"SW1": 33,
"SW2": 27,
},
"node32s": {},
"nodemcu-32s": {"BUTTON": 0, "LED": 2},
"odroid_esp32": {"ADC1": 35, "ADC2": 36, "LED": 2, "SCL": 4, "SDA": 15, "SS": 22},
"onehorse32dev": {"A1": 37, "A2": 38, "BUTTON": 0, "LED": 5},
"oroca_edubot": {
"A0": 34,
"A1": 39,
"A2": 36,
"A3": 33,
"D0": 4,
"D1": 16,
"D2": 17,
"D3": 22,
"D4": 23,
"D5": 5,
"D6": 18,
"D7": 19,
"D8": 33,
"LED": 13,
"MOSI": 18,
"RX": 16,
"SCK": 5,
"SDA": 23,
"SS": 2,
"TX": 17,
"VBAT": 35,
},
"pico32": {},
"pocket_32": {"LED": 16},
"pycom_gpy": {
"A1": 37,
"A2": 38,
"LED": 0,
"MISO": 37,
"MOSI": 22,
"SCK": 13,
"SCL": 13,
"SDA": 12,
"SS": 17,
},
"quantum": {},
"sparkfun_lora_gateway_1-channel": {"MISO": 12, "MOSI": 13, "SCK": 14, "SS": 16},
"tinypico": {},
"ttgo-lora32-v1": {
"A1": 37,
"A2": 38,
"BUTTON": 0,
"LED": 2,
"MOSI": 27,
"SCK": 5,
"SS": 18,
},
"ttgo-t-beam": {"BUTTON": 39, "LED": 14, "MOSI": 27, "SCK": 5, "SS": 18},
"ttgo-t-watch": {"BUTTON": 36, "MISO": 2, "MOSI": 15, "SCK": 14, "SS": 13},
"ttgo-t1": {"LED": 22, "MISO": 2, "MOSI": 15, "SCK": 14, "SCL": 23, "SS": 13},
"ttgo-t7-v13-mini32": {"LED": 22},
"ttgo-t7-v14-mini32": {"LED": 19},
"turta_iot_node": {},
"vintlabs-devkit-v1": {
"LED": 2,
"PWM0": 12,
"PWM1": 13,
"PWM2": 14,
"PWM3": 15,
"PWM4": 16,
"PWM5": 17,
"PWM6": 18,
"PWM7": 19,
},
"wemos_d1_mini32": {
"D0": 26,
"D1": 22,
"D2": 21,
"D3": 17,
"D4": 16,
"D5": 18,
"D6": 19,
"D7": 23,
"D8": 5,
"LED": 2,
"RXD": 3,
"TXD": 1,
"_VBAT": 35,
},
"wemosbat": {"LED": 16},
"wesp32": {"MISO": 32, "SCL": 4, "SDA": 15},
"widora-air": {
"A1": 39,
"A2": 35,
"A3": 25,
"A4": 26,
"A5": 14,
"A6": 12,
"A7": 15,
"A8": 13,
"A9": 2,
"BUTTON": 0,
"D0": 19,
"D1": 23,
"D2": 18,
"D3": 17,
"D4": 16,
"D5": 5,
"D6": 4,
"LED": 25,
"MISO": 17,
"MOSI": 16,
"SCL": 19,
"SDA": 23,
"T0": 19,
"T1": 23,
"T2": 18,
"T3": 17,
"T4": 16,
"T5": 5,
"T6": 4,
},
"xinabox_cw02": {"LED": 27},
}
ESP32_C3_BASE_PINS = {
"TX": 21,
"RX": 20,
"ADC1_0": 0,
"ADC1_1": 1,
"ADC1_2": 2,
"ADC1_3": 3,
"ADC1_4": 4,
"ADC2_0": 5,
}
ESP32_C3_BOARD_PINS = {
"esp32-c3-devkitm-1": {"LED": 8},
"esp32-c3-devkitc-02": "esp32-c3-devkitm-1",
}

View File

@ -125,7 +125,7 @@ void ICACHE_RAM_ATTR HOT AcDimmerDataStore::gpio_intr() {
}
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.
// 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*.

View File

@ -42,7 +42,7 @@ void AdalightLightEffect::reset_frame_(light::AddressableLight &it) {
void AdalightLightEffect::blank_all_leds_(light::AddressableLight &it) {
for (int led = it.size(); led-- > 0;) {
it[led].set(COLOR_BLACK);
it[led].set(Color::BLACK);
}
}

View File

@ -14,6 +14,7 @@ static const char *const TAG = "adc";
void ADCSensor::set_attenuation(adc_atten_t attenuation) { this->attenuation_ = attenuation; }
inline adc1_channel_t gpio_to_adc1(uint8_t pin) {
#if CONFIG_IDF_TARGET_ESP32
switch (pin) {
case 36:
return ADC1_CHANNEL_0;
@ -34,6 +35,22 @@ inline adc1_channel_t gpio_to_adc1(uint8_t pin) {
default:
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
@ -46,8 +63,10 @@ void ADCSensor::setup() {
#ifdef ARDUINO_ARCH_ESP32
adc1_config_channel_atten(gpio_to_adc1(pin_), attenuation_);
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_));
#endif
#endif
}
void ADCSensor::dump_config() {
LOG_SENSOR("", "ADC Sensor", this);
@ -89,6 +108,7 @@ float ADCSensor::sample() {
#ifdef ARDUINO_ARCH_ESP32
int raw = adc1_get_raw(gpio_to_adc1(pin_));
float value_v = raw / 4095.0f;
#if CONFIG_IDF_TARGET_ESP32
switch (this->attenuation_) {
case ADC_ATTEN_DB_0:
value_v *= 1.1;
@ -105,6 +125,24 @@ float ADCSensor::sample() {
default: // This is to satisfy the unused ADC_ATTEN_MAX
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;
#endif

View File

@ -7,7 +7,6 @@ from esphome.const import (
CONF_ID,
CONF_PIN,
DEVICE_CLASS_VOLTAGE,
ICON_EMPTY,
STATE_CLASS_MEASUREMENT,
UNIT_VOLT,
)
@ -37,7 +36,10 @@ ADCSensor = adc_ns.class_(
CONFIG_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(
{

View File

@ -8,7 +8,6 @@ from esphome.const import (
DEVICE_CLASS_CURRENT,
DEVICE_CLASS_POWER,
DEVICE_CLASS_VOLTAGE,
ICON_EMPTY,
STATE_CLASS_MEASUREMENT,
UNIT_VOLT,
UNIT_AMPERE,
@ -32,27 +31,34 @@ CONFIG_SCHEMA = (
cv.GenerateID(): cv.declare_id(ADE7953),
cv.Optional(CONF_IRQ_PIN): pins.input_pin,
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(
UNIT_AMPERE,
ICON_EMPTY,
2,
DEVICE_CLASS_CURRENT,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_AMPERE,
accuracy_decimals=2,
device_class=DEVICE_CLASS_CURRENT,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_CURRENT_B): sensor.sensor_schema(
UNIT_AMPERE,
ICON_EMPTY,
2,
DEVICE_CLASS_CURRENT,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_AMPERE,
accuracy_decimals=2,
device_class=DEVICE_CLASS_CURRENT,
state_class=STATE_CLASS_MEASUREMENT,
),
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(
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,
),
}
)

View File

@ -5,7 +5,6 @@ from esphome.const import (
CONF_GAIN,
CONF_MULTIPLEXER,
DEVICE_CLASS_VOLTAGE,
ICON_EMPTY,
STATE_CLASS_MEASUREMENT,
UNIT_VOLT,
CONF_ID,
@ -53,7 +52,10 @@ ADS1115Sensor = ads1115_ns.class_(
CONF_ADS1115_ID = "ads1115_id"
CONFIG_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(
{

View File

@ -10,7 +10,7 @@
//
// 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
// 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 "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_DEFAULT_DELAY = 5; // ms, for calibration and temperature measurement
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() {
ESP_LOGCONFIG(TAG, "Setting up AHT10...");
@ -58,8 +58,8 @@ void AHT10Component::update() {
uint8_t delay = AHT10_DEFAULT_DELAY;
if (this->humidity_sensor_ != nullptr)
delay = AHT10_HUMIDITY_DELAY;
for (int i = 0; i < AHT10_ATTEMPS; ++i) {
ESP_LOGVV(TAG, "Attemps %u at %6ld", i, millis());
for (int i = 0; i < AHT10_ATTEMPTS; ++i) {
ESP_LOGVV(TAG, "Attempt %u at %6ld", i, millis());
delay_microseconds_accurate(4);
if (!this->read_bytes(0, data, 6, delay)) {
ESP_LOGD(TAG, "Communication with AHT10 failed, waiting...");

View File

@ -7,7 +7,6 @@ from esphome.const import (
CONF_TEMPERATURE,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_TEMPERATURE,
ICON_EMPTY,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
UNIT_PERCENT,
@ -23,18 +22,16 @@ CONFIG_SCHEMA = (
{
cv.GenerateID(): cv.declare_id(AHT10Component),
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS,
ICON_EMPTY,
2,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=2,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
UNIT_PERCENT,
ICON_EMPTY,
2,
DEVICE_CLASS_HUMIDITY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_PERCENT,
accuracy_decimals=2,
device_class=DEVICE_CLASS_HUMIDITY,
state_class=STATE_CLASS_MEASUREMENT,
),
}
)

View File

@ -9,7 +9,6 @@ from esphome.const import (
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
ICON_EMPTY,
UNIT_PERCENT,
)
@ -25,18 +24,16 @@ CONFIG_SCHEMA = (
{
cv.GenerateID(): cv.declare_id(AM2320Component),
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS,
ICON_EMPTY,
1,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
UNIT_PERCENT,
ICON_EMPTY,
1,
DEVICE_CLASS_HUMIDITY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_PERCENT,
accuracy_decimals=1,
device_class=DEVICE_CLASS_HUMIDITY,
state_class=STATE_CLASS_MEASUREMENT,
),
}
)

View File

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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))

View File

@ -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()) {
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();
if (this->current_request_ > 0) {
if (this->current_request_ > 1) {
AnovaPacket *pkt = nullptr;
switch (this->current_request_++) {
case 1:
case 2:
pkt = this->codec_->get_read_target_temp_request();
break;
case 2:
case 3:
pkt = this->codec_->get_read_current_temp_request();
break;
default:
this->current_request_ = 0;
this->current_request_ = 1;
break;
}
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() {
if (this->node_state != espbt::ClientState::Established)
return;
if (this->current_request_ == 0) {
if (this->current_request_ < 2) {
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_,
pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
if (status)

View File

@ -36,12 +36,14 @@ class Anova : public climate::Climate, public esphome::ble_client::BLEClientNode
traits.set_visual_temperature_step(0.1);
return traits;
}
void set_unit_of_measurement(const char *);
protected:
AnovaCodec *codec_;
void control(const climate::ClimateCall &call) override;
uint16_t char_handle_;
uint8_t current_request_;
bool fahrenheit_;
};
} // namespace anova

View File

@ -3,6 +3,10 @@
namespace esphome {
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_() {
this->packet_.length = strlen((char *) this->packet_.data);
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) {
this->current_query_ = SET_TARGET_TEMPERATURE;
if (this->fahrenheit_)
temperature = ctof(temperature);
sprintf((char *) this->packet_.data, CMD_SET_TARGET_TEMP, temperature);
return this->clean_packet_();
}
@ -67,7 +73,6 @@ AnovaPacket *AnovaCodec::get_stop_request() {
void AnovaCodec::decode(const uint8_t *data, uint16_t length) {
memset(this->buf_, 0, 32);
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;
switch (this->current_query_) {
case READ_DEVICE_STATUS: {
@ -97,19 +102,32 @@ void AnovaCodec::decode(const uint8_t *data, uint16_t length) {
}
case READ_TARGET_TEMPERATURE: {
this->target_temp_ = strtof(this->buf_, nullptr);
if (this->fahrenheit_)
this->target_temp_ = ftoc(this->target_temp_);
this->has_target_temp_ = true;
break;
}
case SET_TARGET_TEMPERATURE: {
this->target_temp_ = strtof(this->buf_, nullptr);
if (this->fahrenheit_)
this->target_temp_ = ftoc(this->target_temp_);
this->has_target_temp_ = true;
break;
}
case READ_CURRENT_TEMPERATURE: {
this->current_temp_ = strtof(this->buf_, nullptr);
if (this->fahrenheit_)
this->current_temp_ = ftoc(this->current_temp_);
this->has_current_temp_ = true;
break;
}
case SET_UNIT:
case READ_UNIT: {
this->unit_ = this->buf_[0];
this->fahrenheit_ = this->buf_[0] == 'f';
this->has_unit_ = true;
break;
}
default:
break;
}

View File

@ -71,6 +71,7 @@ class AnovaCodec {
bool has_unit_;
bool has_running_;
char buf_[32];
bool fahrenheit_;
CurrentQuery current_query_;
};

View File

@ -1,7 +1,12 @@
import esphome.codegen as cg
import esphome.config_validation as cv
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"]
DEPENDENCIES = ["ble_client"]
@ -12,14 +17,20 @@ Anova = anova_ns.class_(
)
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(cv.polling_component_schema("60s"))
)
def to_code(config):
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config)
yield climate.register_climate(var, config)
yield ble_client.register_ble_node(var, config)
await cg.register_component(var, config)
await climate.register_climate(var, config)
await ble_client.register_ble_node(var, config)
cg.add(var.set_unit_of_measurement(config[CONF_UNIT_OF_MEASUREMENT]))

View File

@ -3,7 +3,6 @@ import esphome.config_validation as cv
from esphome.components import sensor
from esphome.const import (
CONF_TYPE,
DEVICE_CLASS_EMPTY,
STATE_CLASS_MEASUREMENT,
UNIT_PERCENT,
ICON_LIGHTBULB,
@ -21,7 +20,10 @@ TYPES = {
}
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(
{
cv.Required(CONF_TYPE): cv.one_of(*TYPES, upper=True),

View File

@ -39,6 +39,7 @@ service APIConnection {
rpc camera_image (CameraImageRequest) returns (void) {}
rpc climate_command (ClimateCommandRequest) returns (void) {}
rpc number_command (NumberCommandRequest) returns (void) {}
rpc select_command (SelectCommandRequest) returns (void) {}
}
@ -213,6 +214,7 @@ message ListEntitiesBinarySensorResponse {
string device_class = 5;
bool is_status_binary_sensor = 6;
bool disabled_by_default = 7;
}
message BinarySensorStateResponse {
option (id) = 21;
@ -242,6 +244,7 @@ message ListEntitiesCoverResponse {
bool supports_position = 6;
bool supports_tilt = 7;
string device_class = 8;
bool disabled_by_default = 9;
}
enum LegacyCoverState {
@ -309,6 +312,7 @@ message ListEntitiesFanResponse {
bool supports_speed = 6;
bool supports_direction = 7;
int32 supported_speed_count = 8;
bool disabled_by_default = 9;
}
enum FanSpeed {
FAN_SPEED_LOW = 0;
@ -352,6 +356,18 @@ message FanCommandRequest {
}
// ==================== 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 {
option (id) = 15;
option (source) = SOURCE_SERVER;
@ -362,13 +378,16 @@ message ListEntitiesLightResponse {
string name = 3;
string unique_id = 4;
bool supports_brightness = 5;
bool supports_rgb = 6;
bool supports_white_value = 7;
bool supports_color_temperature = 8;
repeated ColorMode supported_color_modes = 12;
// next four supports_* are for legacy clients, newer clients should use color modes
bool legacy_supports_brightness = 5 [deprecated=true];
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 max_mireds = 10;
repeated string effects = 11;
bool disabled_by_default = 13;
}
message LightStateResponse {
option (id) = 24;
@ -379,12 +398,15 @@ message LightStateResponse {
fixed32 key = 1;
bool state = 2;
float brightness = 3;
ColorMode color_mode = 11;
float color_brightness = 10;
float red = 4;
float green = 5;
float blue = 6;
float white = 7;
float color_temperature = 8;
float cold_white = 12;
float warm_white = 13;
string effect = 9;
}
message LightCommandRequest {
@ -398,6 +420,8 @@ message LightCommandRequest {
bool state = 3;
bool has_brightness = 4;
float brightness = 5;
bool has_color_mode = 22;
ColorMode color_mode = 23;
bool has_color_brightness = 20;
float color_brightness = 21;
bool has_rgb = 6;
@ -408,6 +432,10 @@ message LightCommandRequest {
float white = 11;
bool has_color_temperature = 12;
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;
uint32 transition_length = 15;
bool has_flash_length = 16;
@ -420,6 +448,7 @@ message LightCommandRequest {
enum SensorStateClass {
STATE_CLASS_NONE = 0;
STATE_CLASS_MEASUREMENT = 1;
STATE_CLASS_TOTAL_INCREASING = 2;
}
enum SensorLastResetType {
@ -445,6 +474,7 @@ message ListEntitiesSensorResponse {
string device_class = 9;
SensorStateClass state_class = 10;
SensorLastResetType last_reset_type = 11;
bool disabled_by_default = 12;
}
message SensorStateResponse {
option (id) = 25;
@ -472,6 +502,7 @@ message ListEntitiesSwitchResponse {
string icon = 5;
bool assumed_state = 6;
bool disabled_by_default = 7;
}
message SwitchStateResponse {
option (id) = 26;
@ -504,6 +535,7 @@ message ListEntitiesTextSensorResponse {
string unique_id = 4;
string icon = 5;
bool disabled_by_default = 6;
}
message TextSensorStateResponse {
option (id) = 27;
@ -524,9 +556,10 @@ enum LogLevel {
LOG_LEVEL_ERROR = 1;
LOG_LEVEL_WARN = 2;
LOG_LEVEL_INFO = 3;
LOG_LEVEL_DEBUG = 4;
LOG_LEVEL_VERBOSE = 5;
LOG_LEVEL_VERY_VERBOSE = 6;
LOG_LEVEL_CONFIG = 4;
LOG_LEVEL_DEBUG = 5;
LOG_LEVEL_VERBOSE = 6;
LOG_LEVEL_VERY_VERBOSE = 7;
}
message SubscribeLogsRequest {
option (id) = 28;
@ -541,7 +574,6 @@ message SubscribeLogsResponse {
option (no_delay) = false;
LogLevel level = 1;
string tag = 2;
string message = 3;
bool send_failed = 4;
}
@ -663,6 +695,7 @@ message ListEntitiesCameraResponse {
fixed32 key = 2;
string name = 3;
string unique_id = 4;
bool disabled_by_default = 5;
}
message CameraImageResponse {
@ -755,6 +788,7 @@ message ListEntitiesClimateResponse {
repeated string supported_custom_fan_modes = 15;
repeated ClimatePreset supported_presets = 16;
repeated string supported_custom_presets = 17;
bool disabled_by_default = 18;
}
message ClimateStateResponse {
option (id) = 47;
@ -822,6 +856,7 @@ message ListEntitiesNumberResponse {
float min_value = 6;
float max_value = 7;
float step = 8;
bool disabled_by_default = 9;
}
message NumberStateResponse {
option (id) = 50;
@ -844,3 +879,40 @@ message NumberCommandRequest {
fixed32 key = 1;
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;
}

View File

@ -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.device_class = binary_sensor->get_device_class();
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);
}
#endif
@ -207,6 +208,7 @@ bool APIConnection::send_cover_info(cover::Cover *cover) {
msg.supports_position = traits.get_supports_position();
msg.supports_tilt = traits.get_supports_tilt();
msg.device_class = cover->get_device_class();
msg.disabled_by_default = cover->is_disabled_by_default();
return this->send_list_entities_cover_response(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_direction = traits.supports_direction();
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);
}
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 values = light->remote_values;
auto color_mode = values.get_color_mode();
LightStateResponse resp{};
resp.key = light->get_object_id_hash();
resp.state = values.is_on();
if (traits.get_supports_brightness())
resp.brightness = values.get_brightness();
if (traits.get_supports_rgb()) {
resp.color_brightness = values.get_color_brightness();
resp.red = values.get_red();
resp.green = values.get_green();
resp.blue = values.get_blue();
}
if (traits.get_supports_rgb_white_value())
resp.white = values.get_white();
if (traits.get_supports_color_temperature())
resp.color_temperature = values.get_color_temperature();
resp.color_mode = static_cast<enums::ColorMode>(color_mode);
resp.brightness = values.get_brightness();
resp.color_brightness = values.get_color_brightness();
resp.red = values.get_red();
resp.green = values.get_green();
resp.blue = values.get_blue();
resp.white = values.get_white();
resp.color_temperature = values.get_color_temperature();
resp.cold_white = values.get_cold_white();
resp.warm_white = values.get_warm_white();
if (light->supports_effects())
resp.effect = light->get_effect_name();
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.name = light->get_name();
msg.unique_id = get_default_unique_id("light", light);
msg.supports_brightness = traits.get_supports_brightness();
msg.supports_rgb = traits.get_supports_rgb();
msg.supports_white_value = traits.get_supports_rgb_white_value();
msg.supports_color_temperature = traits.get_supports_color_temperature();
if (msg.supports_color_temperature) {
msg.disabled_by_default = light->is_disabled_by_default();
for (auto mode : traits.get_supported_color_modes())
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.max_mireds = traits.get_max_mireds();
}
@ -353,6 +365,8 @@ void APIConnection::light_command(const LightCommandRequest &msg) {
call.set_state(msg.state);
if (msg.has_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)
call.set_color_brightness(msg.color_brightness);
if (msg.has_rgb) {
@ -364,6 +378,10 @@ void APIConnection::light_command(const LightCommandRequest &msg) {
call.set_white(msg.white);
if (msg.has_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)
call.set_transition_length(msg.transition_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.state_class = static_cast<enums::SensorStateClass>(sensor->state_class);
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);
}
@ -423,6 +442,7 @@ bool APIConnection::send_switch_info(switch_::Switch *a_switch) {
msg.unique_id = get_default_unique_id("switch", a_switch);
msg.icon = a_switch->get_icon();
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);
}
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())
msg.unique_id = get_default_unique_id("text_sensor", text_sensor);
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);
}
#endif
@ -500,6 +521,9 @@ bool APIConnection::send_climate_info(climate::Climate *climate) {
msg.object_id = climate->get_object_id();
msg.name = climate->get_name();
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_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.unique_id = get_default_unique_id("number", number);
msg.icon = number->traits.get_icon();
msg.disabled_by_default = number->is_disabled_by_default();
msg.min_value = number->traits.get_min_value();
msg.max_value = number->traits.get_max_value();
@ -590,6 +615,42 @@ void APIConnection::number_command(const NumberCommandRequest &msg) {
}
#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
void APIConnection::send_camera_state(std::shared_ptr<esp32_camera::CameraImage> image) {
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.name = camera->get_name();
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);
}
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();
// LogLevel level = 1;
buffer.encode_uint32(1, static_cast<uint32_t>(level));
// string tag = 2;
// buffer.encode_string(2, tag, strlen(tag));
// string message = 3;
buffer.encode_string(3, line, strlen(line));
// SubscribeLogsResponse - 29
@ -655,7 +715,7 @@ HelloResponse APIConnection::hello(const HelloRequest &msg) {
HelloResponse resp;
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 ")";
this->connection_state_ = ConnectionState::CONNECTED;
return resp;

View File

@ -67,6 +67,11 @@ class APIConnection : public APIServerConnection {
bool send_number_state(number::Number *number, float state);
bool send_number_info(number::Number *number);
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
bool send_log_message(int level, const char *tag, const char *line);
void send_homeassistant_service_call(const HomeassistantServiceResponse &call) {

View File

@ -62,12 +62,40 @@ template<> const char *proto_enum_to_string<enums::FanDirection>(enums::FanDirec
return "UNKNOWN";
}
}
template<> const char *proto_enum_to_string<enums::ColorMode>(enums::ColorMode value) {
switch (value) {
case enums::COLOR_MODE_UNKNOWN:
return "COLOR_MODE_UNKNOWN";
case enums::COLOR_MODE_ON_OFF:
return "COLOR_MODE_ON_OFF";
case enums::COLOR_MODE_BRIGHTNESS:
return "COLOR_MODE_BRIGHTNESS";
case enums::COLOR_MODE_WHITE:
return "COLOR_MODE_WHITE";
case enums::COLOR_MODE_COLOR_TEMPERATURE:
return "COLOR_MODE_COLOR_TEMPERATURE";
case enums::COLOR_MODE_COLD_WARM_WHITE:
return "COLOR_MODE_COLD_WARM_WHITE";
case enums::COLOR_MODE_RGB:
return "COLOR_MODE_RGB";
case enums::COLOR_MODE_RGB_WHITE:
return "COLOR_MODE_RGB_WHITE";
case enums::COLOR_MODE_RGB_COLOR_TEMPERATURE:
return "COLOR_MODE_RGB_COLOR_TEMPERATURE";
case enums::COLOR_MODE_RGB_COLD_WARM_WHITE:
return "COLOR_MODE_RGB_COLD_WARM_WHITE";
default:
return "UNKNOWN";
}
}
template<> const char *proto_enum_to_string<enums::SensorStateClass>(enums::SensorStateClass value) {
switch (value) {
case enums::STATE_CLASS_NONE:
return "STATE_CLASS_NONE";
case enums::STATE_CLASS_MEASUREMENT:
return "STATE_CLASS_MEASUREMENT";
case enums::STATE_CLASS_TOTAL_INCREASING:
return "STATE_CLASS_TOTAL_INCREASING";
default:
return "UNKNOWN";
}
@ -94,6 +122,8 @@ template<> const char *proto_enum_to_string<enums::LogLevel>(enums::LogLevel val
return "LOG_LEVEL_WARN";
case enums::LOG_LEVEL_INFO:
return "LOG_LEVEL_INFO";
case enums::LOG_LEVEL_CONFIG:
return "LOG_LEVEL_CONFIG";
case enums::LOG_LEVEL_DEBUG:
return "LOG_LEVEL_DEBUG";
case enums::LOG_LEVEL_VERBOSE:
@ -235,6 +265,7 @@ bool HelloRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value)
}
}
void HelloRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->client_info); }
#ifdef HAS_PROTO_MESSAGE_DUMP
void HelloRequest::dump_to(std::string &out) const {
char buffer[64];
out.append("HelloRequest {\n");
@ -243,6 +274,7 @@ void HelloRequest::dump_to(std::string &out) const {
out.append("\n");
out.append("}");
}
#endif
bool HelloResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 1: {
@ -272,6 +304,7 @@ void HelloResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_uint32(2, this->api_version_minor);
buffer.encode_string(3, this->server_info);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void HelloResponse::dump_to(std::string &out) const {
char buffer[64];
out.append("HelloResponse {\n");
@ -290,6 +323,7 @@ void HelloResponse::dump_to(std::string &out) const {
out.append("\n");
out.append("}");
}
#endif
bool ConnectRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 1: {
@ -301,6 +335,7 @@ bool ConnectRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value
}
}
void ConnectRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->password); }
#ifdef HAS_PROTO_MESSAGE_DUMP
void ConnectRequest::dump_to(std::string &out) const {
char buffer[64];
out.append("ConnectRequest {\n");
@ -309,6 +344,7 @@ void ConnectRequest::dump_to(std::string &out) const {
out.append("\n");
out.append("}");
}
#endif
bool ConnectResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 1: {
@ -320,6 +356,7 @@ bool ConnectResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
}
}
void ConnectResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(1, this->invalid_password); }
#ifdef HAS_PROTO_MESSAGE_DUMP
void ConnectResponse::dump_to(std::string &out) const {
char buffer[64];
out.append("ConnectResponse {\n");
@ -328,16 +365,27 @@ void ConnectResponse::dump_to(std::string &out) const {
out.append("\n");
out.append("}");
}
#endif
void DisconnectRequest::encode(ProtoWriteBuffer buffer) const {}
#ifdef HAS_PROTO_MESSAGE_DUMP
void DisconnectRequest::dump_to(std::string &out) const { out.append("DisconnectRequest {}"); }
#endif
void DisconnectResponse::encode(ProtoWriteBuffer buffer) const {}
#ifdef HAS_PROTO_MESSAGE_DUMP
void DisconnectResponse::dump_to(std::string &out) const { out.append("DisconnectResponse {}"); }
#endif
void PingRequest::encode(ProtoWriteBuffer buffer) const {}
#ifdef HAS_PROTO_MESSAGE_DUMP
void PingRequest::dump_to(std::string &out) const { out.append("PingRequest {}"); }
#endif
void PingResponse::encode(ProtoWriteBuffer buffer) const {}
#ifdef HAS_PROTO_MESSAGE_DUMP
void PingResponse::dump_to(std::string &out) const { out.append("PingResponse {}"); }
#endif
void DeviceInfoRequest::encode(ProtoWriteBuffer buffer) const {}
#ifdef HAS_PROTO_MESSAGE_DUMP
void DeviceInfoRequest::dump_to(std::string &out) const { out.append("DeviceInfoRequest {}"); }
#endif
bool DeviceInfoResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 1: {
@ -397,6 +445,7 @@ void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(8, this->project_name);
buffer.encode_string(9, this->project_version);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void DeviceInfoResponse::dump_to(std::string &out) const {
char buffer[64];
out.append("DeviceInfoResponse {\n");
@ -437,18 +486,29 @@ void DeviceInfoResponse::dump_to(std::string &out) const {
out.append("\n");
out.append("}");
}
#endif
void ListEntitiesRequest::encode(ProtoWriteBuffer buffer) const {}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesRequest::dump_to(std::string &out) const { out.append("ListEntitiesRequest {}"); }
#endif
void ListEntitiesDoneResponse::encode(ProtoWriteBuffer buffer) const {}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesDoneResponse::dump_to(std::string &out) const { out.append("ListEntitiesDoneResponse {}"); }
#endif
void SubscribeStatesRequest::encode(ProtoWriteBuffer buffer) const {}
#ifdef HAS_PROTO_MESSAGE_DUMP
void SubscribeStatesRequest::dump_to(std::string &out) const { out.append("SubscribeStatesRequest {}"); }
#endif
bool ListEntitiesBinarySensorResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 6: {
this->is_status_binary_sensor = value.as_bool();
return true;
}
case 7: {
this->disabled_by_default = value.as_bool();
return true;
}
default:
return false;
}
@ -492,7 +552,9 @@ void ListEntitiesBinarySensorResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(4, this->unique_id);
buffer.encode_string(5, this->device_class);
buffer.encode_bool(6, this->is_status_binary_sensor);
buffer.encode_bool(7, this->disabled_by_default);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesBinarySensorResponse::dump_to(std::string &out) const {
char buffer[64];
out.append("ListEntitiesBinarySensorResponse {\n");
@ -520,8 +582,13 @@ void ListEntitiesBinarySensorResponse::dump_to(std::string &out) const {
out.append(" is_status_binary_sensor: ");
out.append(YESNO(this->is_status_binary_sensor));
out.append("\n");
out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default));
out.append("\n");
out.append("}");
}
#endif
bool BinarySensorStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 2: {
@ -551,6 +618,7 @@ void BinarySensorStateResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(2, this->state);
buffer.encode_bool(3, this->missing_state);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void BinarySensorStateResponse::dump_to(std::string &out) const {
char buffer[64];
out.append("BinarySensorStateResponse {\n");
@ -568,6 +636,7 @@ void BinarySensorStateResponse::dump_to(std::string &out) const {
out.append("\n");
out.append("}");
}
#endif
bool ListEntitiesCoverResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 5: {
@ -582,6 +651,10 @@ bool ListEntitiesCoverResponse::decode_varint(uint32_t field_id, ProtoVarInt val
this->supports_tilt = value.as_bool();
return true;
}
case 9: {
this->disabled_by_default = value.as_bool();
return true;
}
default:
return false;
}
@ -627,7 +700,9 @@ void ListEntitiesCoverResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(6, this->supports_position);
buffer.encode_bool(7, this->supports_tilt);
buffer.encode_string(8, this->device_class);
buffer.encode_bool(9, this->disabled_by_default);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesCoverResponse::dump_to(std::string &out) const {
char buffer[64];
out.append("ListEntitiesCoverResponse {\n");
@ -663,8 +738,13 @@ void ListEntitiesCoverResponse::dump_to(std::string &out) const {
out.append(" device_class: ");
out.append("'").append(this->device_class).append("'");
out.append("\n");
out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default));
out.append("\n");
out.append("}");
}
#endif
bool CoverStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 2: {
@ -704,6 +784,7 @@ void CoverStateResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_float(4, this->tilt);
buffer.encode_enum<enums::CoverOperation>(5, this->current_operation);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void CoverStateResponse::dump_to(std::string &out) const {
char buffer[64];
out.append("CoverStateResponse {\n");
@ -731,6 +812,7 @@ void CoverStateResponse::dump_to(std::string &out) const {
out.append("\n");
out.append("}");
}
#endif
bool CoverCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 2: {
@ -785,6 +867,7 @@ void CoverCommandRequest::encode(ProtoWriteBuffer buffer) const {
buffer.encode_float(7, this->tilt);
buffer.encode_bool(8, this->stop);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void CoverCommandRequest::dump_to(std::string &out) const {
char buffer[64];
out.append("CoverCommandRequest {\n");
@ -824,6 +907,7 @@ void CoverCommandRequest::dump_to(std::string &out) const {
out.append("\n");
out.append("}");
}
#endif
bool ListEntitiesFanResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 5: {
@ -842,6 +926,10 @@ bool ListEntitiesFanResponse::decode_varint(uint32_t field_id, ProtoVarInt value
this->supported_speed_count = value.as_int32();
return true;
}
case 9: {
this->disabled_by_default = value.as_bool();
return true;
}
default:
return false;
}
@ -883,7 +971,9 @@ void ListEntitiesFanResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(6, this->supports_speed);
buffer.encode_bool(7, this->supports_direction);
buffer.encode_int32(8, this->supported_speed_count);
buffer.encode_bool(9, this->disabled_by_default);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesFanResponse::dump_to(std::string &out) const {
char buffer[64];
out.append("ListEntitiesFanResponse {\n");
@ -920,8 +1010,13 @@ void ListEntitiesFanResponse::dump_to(std::string &out) const {
sprintf(buffer, "%d", this->supported_speed_count);
out.append(buffer);
out.append("\n");
out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default));
out.append("\n");
out.append("}");
}
#endif
bool FanStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 2: {
@ -966,6 +1061,7 @@ void FanStateResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_enum<enums::FanDirection>(5, this->direction);
buffer.encode_int32(6, this->speed_level);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void FanStateResponse::dump_to(std::string &out) const {
char buffer[64];
out.append("FanStateResponse {\n");
@ -996,6 +1092,7 @@ void FanStateResponse::dump_to(std::string &out) const {
out.append("\n");
out.append("}");
}
#endif
bool FanCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 2: {
@ -1065,6 +1162,7 @@ void FanCommandRequest::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(10, this->has_speed_level);
buffer.encode_int32(11, this->speed_level);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void FanCommandRequest::dump_to(std::string &out) const {
char buffer[64];
out.append("FanCommandRequest {\n");
@ -1115,22 +1213,31 @@ void FanCommandRequest::dump_to(std::string &out) const {
out.append("\n");
out.append("}");
}
#endif
bool ListEntitiesLightResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 12: {
this->supported_color_modes.push_back(value.as_enum<enums::ColorMode>());
return true;
}
case 5: {
this->supports_brightness = value.as_bool();
this->legacy_supports_brightness = value.as_bool();
return true;
}
case 6: {
this->supports_rgb = value.as_bool();
this->legacy_supports_rgb = value.as_bool();
return true;
}
case 7: {
this->supports_white_value = value.as_bool();
this->legacy_supports_white_value = value.as_bool();
return true;
}
case 8: {
this->supports_color_temperature = value.as_bool();
this->legacy_supports_color_temperature = value.as_bool();
return true;
}
case 13: {
this->disabled_by_default = value.as_bool();
return true;
}
default:
@ -1182,16 +1289,21 @@ void ListEntitiesLightResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_fixed32(2, this->key);
buffer.encode_string(3, this->name);
buffer.encode_string(4, this->unique_id);
buffer.encode_bool(5, this->supports_brightness);
buffer.encode_bool(6, this->supports_rgb);
buffer.encode_bool(7, this->supports_white_value);
buffer.encode_bool(8, this->supports_color_temperature);
for (auto &it : this->supported_color_modes) {
buffer.encode_enum<enums::ColorMode>(12, it, true);
}
buffer.encode_bool(5, this->legacy_supports_brightness);
buffer.encode_bool(6, this->legacy_supports_rgb);
buffer.encode_bool(7, this->legacy_supports_white_value);
buffer.encode_bool(8, this->legacy_supports_color_temperature);
buffer.encode_float(9, this->min_mireds);
buffer.encode_float(10, this->max_mireds);
for (auto &it : this->effects) {
buffer.encode_string(11, it, true);
}
buffer.encode_bool(13, this->disabled_by_default);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesLightResponse::dump_to(std::string &out) const {
char buffer[64];
out.append("ListEntitiesLightResponse {\n");
@ -1212,20 +1324,26 @@ void ListEntitiesLightResponse::dump_to(std::string &out) const {
out.append("'").append(this->unique_id).append("'");
out.append("\n");
out.append(" supports_brightness: ");
out.append(YESNO(this->supports_brightness));
for (const auto &it : this->supported_color_modes) {
out.append(" supported_color_modes: ");
out.append(proto_enum_to_string<enums::ColorMode>(it));
out.append("\n");
}
out.append(" legacy_supports_brightness: ");
out.append(YESNO(this->legacy_supports_brightness));
out.append("\n");
out.append(" supports_rgb: ");
out.append(YESNO(this->supports_rgb));
out.append(" legacy_supports_rgb: ");
out.append(YESNO(this->legacy_supports_rgb));
out.append("\n");
out.append(" supports_white_value: ");
out.append(YESNO(this->supports_white_value));
out.append(" legacy_supports_white_value: ");
out.append(YESNO(this->legacy_supports_white_value));
out.append("\n");
out.append(" supports_color_temperature: ");
out.append(YESNO(this->supports_color_temperature));
out.append(" legacy_supports_color_temperature: ");
out.append(YESNO(this->legacy_supports_color_temperature));
out.append("\n");
out.append(" min_mireds: ");
@ -1243,14 +1361,23 @@ void ListEntitiesLightResponse::dump_to(std::string &out) const {
out.append("'").append(it).append("'");
out.append("\n");
}
out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default));
out.append("\n");
out.append("}");
}
#endif
bool LightStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 2: {
this->state = value.as_bool();
return true;
}
case 11: {
this->color_mode = value.as_enum<enums::ColorMode>();
return true;
}
default:
return false;
}
@ -1299,6 +1426,14 @@ bool LightStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
this->color_temperature = value.as_float();
return true;
}
case 12: {
this->cold_white = value.as_float();
return true;
}
case 13: {
this->warm_white = value.as_float();
return true;
}
default:
return false;
}
@ -1307,14 +1442,18 @@ void LightStateResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_fixed32(1, this->key);
buffer.encode_bool(2, this->state);
buffer.encode_float(3, this->brightness);
buffer.encode_enum<enums::ColorMode>(11, this->color_mode);
buffer.encode_float(10, this->color_brightness);
buffer.encode_float(4, this->red);
buffer.encode_float(5, this->green);
buffer.encode_float(6, this->blue);
buffer.encode_float(7, this->white);
buffer.encode_float(8, this->color_temperature);
buffer.encode_float(12, this->cold_white);
buffer.encode_float(13, this->warm_white);
buffer.encode_string(9, this->effect);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void LightStateResponse::dump_to(std::string &out) const {
char buffer[64];
out.append("LightStateResponse {\n");
@ -1332,6 +1471,10 @@ void LightStateResponse::dump_to(std::string &out) const {
out.append(buffer);
out.append("\n");
out.append(" color_mode: ");
out.append(proto_enum_to_string<enums::ColorMode>(this->color_mode));
out.append("\n");
out.append(" color_brightness: ");
sprintf(buffer, "%g", this->color_brightness);
out.append(buffer);
@ -1362,11 +1505,22 @@ void LightStateResponse::dump_to(std::string &out) const {
out.append(buffer);
out.append("\n");
out.append(" cold_white: ");
sprintf(buffer, "%g", this->cold_white);
out.append(buffer);
out.append("\n");
out.append(" warm_white: ");
sprintf(buffer, "%g", this->warm_white);
out.append(buffer);
out.append("\n");
out.append(" effect: ");
out.append("'").append(this->effect).append("'");
out.append("\n");
out.append("}");
}
#endif
bool LightCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 2: {
@ -1381,6 +1535,14 @@ bool LightCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
this->has_brightness = value.as_bool();
return true;
}
case 22: {
this->has_color_mode = value.as_bool();
return true;
}
case 23: {
this->color_mode = value.as_enum<enums::ColorMode>();
return true;
}
case 20: {
this->has_color_brightness = value.as_bool();
return true;
@ -1397,6 +1559,14 @@ bool LightCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
this->has_color_temperature = value.as_bool();
return true;
}
case 24: {
this->has_cold_white = value.as_bool();
return true;
}
case 26: {
this->has_warm_white = value.as_bool();
return true;
}
case 14: {
this->has_transition_length = value.as_bool();
return true;
@ -1465,6 +1635,14 @@ bool LightCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
this->color_temperature = value.as_float();
return true;
}
case 25: {
this->cold_white = value.as_float();
return true;
}
case 27: {
this->warm_white = value.as_float();
return true;
}
default:
return false;
}
@ -1475,6 +1653,8 @@ void LightCommandRequest::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(3, this->state);
buffer.encode_bool(4, this->has_brightness);
buffer.encode_float(5, this->brightness);
buffer.encode_bool(22, this->has_color_mode);
buffer.encode_enum<enums::ColorMode>(23, this->color_mode);
buffer.encode_bool(20, this->has_color_brightness);
buffer.encode_float(21, this->color_brightness);
buffer.encode_bool(6, this->has_rgb);
@ -1485,6 +1665,10 @@ void LightCommandRequest::encode(ProtoWriteBuffer buffer) const {
buffer.encode_float(11, this->white);
buffer.encode_bool(12, this->has_color_temperature);
buffer.encode_float(13, this->color_temperature);
buffer.encode_bool(24, this->has_cold_white);
buffer.encode_float(25, this->cold_white);
buffer.encode_bool(26, this->has_warm_white);
buffer.encode_float(27, this->warm_white);
buffer.encode_bool(14, this->has_transition_length);
buffer.encode_uint32(15, this->transition_length);
buffer.encode_bool(16, this->has_flash_length);
@ -1492,6 +1676,7 @@ void LightCommandRequest::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(18, this->has_effect);
buffer.encode_string(19, this->effect);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void LightCommandRequest::dump_to(std::string &out) const {
char buffer[64];
out.append("LightCommandRequest {\n");
@ -1517,6 +1702,14 @@ void LightCommandRequest::dump_to(std::string &out) const {
out.append(buffer);
out.append("\n");
out.append(" has_color_mode: ");
out.append(YESNO(this->has_color_mode));
out.append("\n");
out.append(" color_mode: ");
out.append(proto_enum_to_string<enums::ColorMode>(this->color_mode));
out.append("\n");
out.append(" has_color_brightness: ");
out.append(YESNO(this->has_color_brightness));
out.append("\n");
@ -1563,6 +1756,24 @@ void LightCommandRequest::dump_to(std::string &out) const {
out.append(buffer);
out.append("\n");
out.append(" has_cold_white: ");
out.append(YESNO(this->has_cold_white));
out.append("\n");
out.append(" cold_white: ");
sprintf(buffer, "%g", this->cold_white);
out.append(buffer);
out.append("\n");
out.append(" has_warm_white: ");
out.append(YESNO(this->has_warm_white));
out.append("\n");
out.append(" warm_white: ");
sprintf(buffer, "%g", this->warm_white);
out.append(buffer);
out.append("\n");
out.append(" has_transition_length: ");
out.append(YESNO(this->has_transition_length));
out.append("\n");
@ -1590,6 +1801,7 @@ void LightCommandRequest::dump_to(std::string &out) const {
out.append("\n");
out.append("}");
}
#endif
bool ListEntitiesSensorResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 7: {
@ -1608,6 +1820,10 @@ bool ListEntitiesSensorResponse::decode_varint(uint32_t field_id, ProtoVarInt va
this->last_reset_type = value.as_enum<enums::SensorLastResetType>();
return true;
}
case 12: {
this->disabled_by_default = value.as_bool();
return true;
}
default:
return false;
}
@ -1664,7 +1880,9 @@ void ListEntitiesSensorResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(9, this->device_class);
buffer.encode_enum<enums::SensorStateClass>(10, this->state_class);
buffer.encode_enum<enums::SensorLastResetType>(11, this->last_reset_type);
buffer.encode_bool(12, this->disabled_by_default);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesSensorResponse::dump_to(std::string &out) const {
char buffer[64];
out.append("ListEntitiesSensorResponse {\n");
@ -1713,8 +1931,13 @@ void ListEntitiesSensorResponse::dump_to(std::string &out) const {
out.append(" last_reset_type: ");
out.append(proto_enum_to_string<enums::SensorLastResetType>(this->last_reset_type));
out.append("\n");
out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default));
out.append("\n");
out.append("}");
}
#endif
bool SensorStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 3: {
@ -1744,6 +1967,7 @@ void SensorStateResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_float(2, this->state);
buffer.encode_bool(3, this->missing_state);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void SensorStateResponse::dump_to(std::string &out) const {
char buffer[64];
out.append("SensorStateResponse {\n");
@ -1762,12 +1986,17 @@ void SensorStateResponse::dump_to(std::string &out) const {
out.append("\n");
out.append("}");
}
#endif
bool ListEntitiesSwitchResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 6: {
this->assumed_state = value.as_bool();
return true;
}
case 7: {
this->disabled_by_default = value.as_bool();
return true;
}
default:
return false;
}
@ -1811,7 +2040,9 @@ void ListEntitiesSwitchResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(4, this->unique_id);
buffer.encode_string(5, this->icon);
buffer.encode_bool(6, this->assumed_state);
buffer.encode_bool(7, this->disabled_by_default);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesSwitchResponse::dump_to(std::string &out) const {
char buffer[64];
out.append("ListEntitiesSwitchResponse {\n");
@ -1839,8 +2070,13 @@ void ListEntitiesSwitchResponse::dump_to(std::string &out) const {
out.append(" assumed_state: ");
out.append(YESNO(this->assumed_state));
out.append("\n");
out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default));
out.append("\n");
out.append("}");
}
#endif
bool SwitchStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 2: {
@ -1865,6 +2101,7 @@ void SwitchStateResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_fixed32(1, this->key);
buffer.encode_bool(2, this->state);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void SwitchStateResponse::dump_to(std::string &out) const {
char buffer[64];
out.append("SwitchStateResponse {\n");
@ -1878,6 +2115,7 @@ void SwitchStateResponse::dump_to(std::string &out) const {
out.append("\n");
out.append("}");
}
#endif
bool SwitchCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 2: {
@ -1902,6 +2140,7 @@ void SwitchCommandRequest::encode(ProtoWriteBuffer buffer) const {
buffer.encode_fixed32(1, this->key);
buffer.encode_bool(2, this->state);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void SwitchCommandRequest::dump_to(std::string &out) const {
char buffer[64];
out.append("SwitchCommandRequest {\n");
@ -1915,6 +2154,17 @@ void SwitchCommandRequest::dump_to(std::string &out) const {
out.append("\n");
out.append("}");
}
#endif
bool ListEntitiesTextSensorResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 6: {
this->disabled_by_default = value.as_bool();
return true;
}
default:
return false;
}
}
bool ListEntitiesTextSensorResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 1: {
@ -1953,7 +2203,9 @@ void ListEntitiesTextSensorResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(3, this->name);
buffer.encode_string(4, this->unique_id);
buffer.encode_string(5, this->icon);
buffer.encode_bool(6, this->disabled_by_default);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesTextSensorResponse::dump_to(std::string &out) const {
char buffer[64];
out.append("ListEntitiesTextSensorResponse {\n");
@ -1977,8 +2229,13 @@ void ListEntitiesTextSensorResponse::dump_to(std::string &out) const {
out.append(" icon: ");
out.append("'").append(this->icon).append("'");
out.append("\n");
out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default));
out.append("\n");
out.append("}");
}
#endif
bool TextSensorStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 3: {
@ -2014,6 +2271,7 @@ void TextSensorStateResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(2, this->state);
buffer.encode_bool(3, this->missing_state);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void TextSensorStateResponse::dump_to(std::string &out) const {
char buffer[64];
out.append("TextSensorStateResponse {\n");
@ -2031,6 +2289,7 @@ void TextSensorStateResponse::dump_to(std::string &out) const {
out.append("\n");
out.append("}");
}
#endif
bool SubscribeLogsRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 1: {
@ -2049,6 +2308,7 @@ void SubscribeLogsRequest::encode(ProtoWriteBuffer buffer) const {
buffer.encode_enum<enums::LogLevel>(1, this->level);
buffer.encode_bool(2, this->dump_config);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void SubscribeLogsRequest::dump_to(std::string &out) const {
char buffer[64];
out.append("SubscribeLogsRequest {\n");
@ -2061,6 +2321,7 @@ void SubscribeLogsRequest::dump_to(std::string &out) const {
out.append("\n");
out.append("}");
}
#endif
bool SubscribeLogsResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 1: {
@ -2077,10 +2338,6 @@ bool SubscribeLogsResponse::decode_varint(uint32_t field_id, ProtoVarInt value)
}
bool SubscribeLogsResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 2: {
this->tag = value.as_string();
return true;
}
case 3: {
this->message = value.as_string();
return true;
@ -2091,10 +2348,10 @@ bool SubscribeLogsResponse::decode_length(uint32_t field_id, ProtoLengthDelimite
}
void SubscribeLogsResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_enum<enums::LogLevel>(1, this->level);
buffer.encode_string(2, this->tag);
buffer.encode_string(3, this->message);
buffer.encode_bool(4, this->send_failed);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void SubscribeLogsResponse::dump_to(std::string &out) const {
char buffer[64];
out.append("SubscribeLogsResponse {\n");
@ -2102,10 +2359,6 @@ void SubscribeLogsResponse::dump_to(std::string &out) const {
out.append(proto_enum_to_string<enums::LogLevel>(this->level));
out.append("\n");
out.append(" tag: ");
out.append("'").append(this->tag).append("'");
out.append("\n");
out.append(" message: ");
out.append("'").append(this->message).append("'");
out.append("\n");
@ -2115,10 +2368,13 @@ void SubscribeLogsResponse::dump_to(std::string &out) const {
out.append("\n");
out.append("}");
}
#endif
void SubscribeHomeassistantServicesRequest::encode(ProtoWriteBuffer buffer) const {}
#ifdef HAS_PROTO_MESSAGE_DUMP
void SubscribeHomeassistantServicesRequest::dump_to(std::string &out) const {
out.append("SubscribeHomeassistantServicesRequest {}");
}
#endif
bool HomeassistantServiceMap::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 1: {
@ -2137,6 +2393,7 @@ void HomeassistantServiceMap::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(1, this->key);
buffer.encode_string(2, this->value);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void HomeassistantServiceMap::dump_to(std::string &out) const {
char buffer[64];
out.append("HomeassistantServiceMap {\n");
@ -2149,6 +2406,7 @@ void HomeassistantServiceMap::dump_to(std::string &out) const {
out.append("\n");
out.append("}");
}
#endif
bool HomeassistantServiceResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 5: {
@ -2194,6 +2452,7 @@ void HomeassistantServiceResponse::encode(ProtoWriteBuffer buffer) const {
}
buffer.encode_bool(5, this->is_event);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void HomeassistantServiceResponse::dump_to(std::string &out) const {
char buffer[64];
out.append("HomeassistantServiceResponse {\n");
@ -2224,10 +2483,13 @@ void HomeassistantServiceResponse::dump_to(std::string &out) const {
out.append("\n");
out.append("}");
}
#endif
void SubscribeHomeAssistantStatesRequest::encode(ProtoWriteBuffer buffer) const {}
#ifdef HAS_PROTO_MESSAGE_DUMP
void SubscribeHomeAssistantStatesRequest::dump_to(std::string &out) const {
out.append("SubscribeHomeAssistantStatesRequest {}");
}
#endif
bool SubscribeHomeAssistantStateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 1: {
@ -2246,6 +2508,7 @@ void SubscribeHomeAssistantStateResponse::encode(ProtoWriteBuffer buffer) const
buffer.encode_string(1, this->entity_id);
buffer.encode_string(2, this->attribute);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void SubscribeHomeAssistantStateResponse::dump_to(std::string &out) const {
char buffer[64];
out.append("SubscribeHomeAssistantStateResponse {\n");
@ -2258,6 +2521,7 @@ void SubscribeHomeAssistantStateResponse::dump_to(std::string &out) const {
out.append("\n");
out.append("}");
}
#endif
bool HomeAssistantStateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 1: {
@ -2281,6 +2545,7 @@ void HomeAssistantStateResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(2, this->state);
buffer.encode_string(3, this->attribute);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void HomeAssistantStateResponse::dump_to(std::string &out) const {
char buffer[64];
out.append("HomeAssistantStateResponse {\n");
@ -2297,8 +2562,11 @@ void HomeAssistantStateResponse::dump_to(std::string &out) const {
out.append("\n");
out.append("}");
}
#endif
void GetTimeRequest::encode(ProtoWriteBuffer buffer) const {}
#ifdef HAS_PROTO_MESSAGE_DUMP
void GetTimeRequest::dump_to(std::string &out) const { out.append("GetTimeRequest {}"); }
#endif
bool GetTimeResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
switch (field_id) {
case 1: {
@ -2310,6 +2578,7 @@ bool GetTimeResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
}
}
void GetTimeResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->epoch_seconds); }
#ifdef HAS_PROTO_MESSAGE_DUMP
void GetTimeResponse::dump_to(std::string &out) const {
char buffer[64];
out.append("GetTimeResponse {\n");
@ -2319,6 +2588,7 @@ void GetTimeResponse::dump_to(std::string &out) const {
out.append("\n");
out.append("}");
}
#endif
bool ListEntitiesServicesArgument::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 2: {
@ -2343,6 +2613,7 @@ void ListEntitiesServicesArgument::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(1, this->name);
buffer.encode_enum<enums::ServiceArgType>(2, this->type);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesServicesArgument::dump_to(std::string &out) const {
char buffer[64];
out.append("ListEntitiesServicesArgument {\n");
@ -2355,6 +2626,7 @@ void ListEntitiesServicesArgument::dump_to(std::string &out) const {
out.append("\n");
out.append("}");
}
#endif
bool ListEntitiesServicesResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 1: {
@ -2386,6 +2658,7 @@ void ListEntitiesServicesResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_message<ListEntitiesServicesArgument>(3, it, true);
}
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesServicesResponse::dump_to(std::string &out) const {
char buffer[64];
out.append("ListEntitiesServicesResponse {\n");
@ -2405,6 +2678,7 @@ void ListEntitiesServicesResponse::dump_to(std::string &out) const {
}
out.append("}");
}
#endif
bool ExecuteServiceArgument::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 1: {
@ -2478,6 +2752,7 @@ void ExecuteServiceArgument::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(9, it, true);
}
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ExecuteServiceArgument::dump_to(std::string &out) const {
char buffer[64];
out.append("ExecuteServiceArgument {\n");
@ -2531,6 +2806,7 @@ void ExecuteServiceArgument::dump_to(std::string &out) const {
}
out.append("}");
}
#endif
bool ExecuteServiceRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 2: {
@ -2557,6 +2833,7 @@ void ExecuteServiceRequest::encode(ProtoWriteBuffer buffer) const {
buffer.encode_message<ExecuteServiceArgument>(2, it, true);
}
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ExecuteServiceRequest::dump_to(std::string &out) const {
char buffer[64];
out.append("ExecuteServiceRequest {\n");
@ -2572,6 +2849,17 @@ void ExecuteServiceRequest::dump_to(std::string &out) const {
}
out.append("}");
}
#endif
bool ListEntitiesCameraResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 5: {
this->disabled_by_default = value.as_bool();
return true;
}
default:
return false;
}
}
bool ListEntitiesCameraResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 1: {
@ -2605,7 +2893,9 @@ void ListEntitiesCameraResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_fixed32(2, this->key);
buffer.encode_string(3, this->name);
buffer.encode_string(4, this->unique_id);
buffer.encode_bool(5, this->disabled_by_default);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesCameraResponse::dump_to(std::string &out) const {
char buffer[64];
out.append("ListEntitiesCameraResponse {\n");
@ -2625,8 +2915,13 @@ void ListEntitiesCameraResponse::dump_to(std::string &out) const {
out.append(" unique_id: ");
out.append("'").append(this->unique_id).append("'");
out.append("\n");
out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default));
out.append("\n");
out.append("}");
}
#endif
bool CameraImageResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 3: {
@ -2662,6 +2957,7 @@ void CameraImageResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(2, this->data);
buffer.encode_bool(3, this->done);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void CameraImageResponse::dump_to(std::string &out) const {
char buffer[64];
out.append("CameraImageResponse {\n");
@ -2679,6 +2975,7 @@ void CameraImageResponse::dump_to(std::string &out) const {
out.append("\n");
out.append("}");
}
#endif
bool CameraImageRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 1: {
@ -2697,6 +2994,7 @@ void CameraImageRequest::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(1, this->single);
buffer.encode_bool(2, this->stream);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void CameraImageRequest::dump_to(std::string &out) const {
char buffer[64];
out.append("CameraImageRequest {\n");
@ -2709,6 +3007,7 @@ void CameraImageRequest::dump_to(std::string &out) const {
out.append("\n");
out.append("}");
}
#endif
bool ListEntitiesClimateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 5: {
@ -2743,6 +3042,10 @@ bool ListEntitiesClimateResponse::decode_varint(uint32_t field_id, ProtoVarInt v
this->supported_presets.push_back(value.as_enum<enums::ClimatePreset>());
return true;
}
case 18: {
this->disabled_by_default = value.as_bool();
return true;
}
default:
return false;
}
@ -2825,7 +3128,9 @@ void ListEntitiesClimateResponse::encode(ProtoWriteBuffer buffer) const {
for (auto &it : this->supported_custom_presets) {
buffer.encode_string(17, it, true);
}
buffer.encode_bool(18, this->disabled_by_default);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesClimateResponse::dump_to(std::string &out) const {
char buffer[64];
out.append("ListEntitiesClimateResponse {\n");
@ -2912,8 +3217,13 @@ void ListEntitiesClimateResponse::dump_to(std::string &out) const {
out.append("'").append(it).append("'");
out.append("\n");
}
out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default));
out.append("\n");
out.append("}");
}
#endif
bool ClimateStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 2: {
@ -2999,6 +3309,7 @@ void ClimateStateResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_enum<enums::ClimatePreset>(12, this->preset);
buffer.encode_string(13, this->custom_preset);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ClimateStateResponse::dump_to(std::string &out) const {
char buffer[64];
out.append("ClimateStateResponse {\n");
@ -3060,6 +3371,7 @@ void ClimateStateResponse::dump_to(std::string &out) const {
out.append("\n");
out.append("}");
}
#endif
bool ClimateCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 2: {
@ -3185,6 +3497,7 @@ void ClimateCommandRequest::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(20, this->has_custom_preset);
buffer.encode_string(21, this->custom_preset);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ClimateCommandRequest::dump_to(std::string &out) const {
char buffer[64];
out.append("ClimateCommandRequest {\n");
@ -3277,6 +3590,17 @@ void ClimateCommandRequest::dump_to(std::string &out) const {
out.append("\n");
out.append("}");
}
#endif
bool ListEntitiesNumberResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 9: {
this->disabled_by_default = value.as_bool();
return true;
}
default:
return false;
}
}
bool ListEntitiesNumberResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 1: {
@ -3330,7 +3654,9 @@ void ListEntitiesNumberResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_float(6, this->min_value);
buffer.encode_float(7, this->max_value);
buffer.encode_float(8, this->step);
buffer.encode_bool(9, this->disabled_by_default);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesNumberResponse::dump_to(std::string &out) const {
char buffer[64];
out.append("ListEntitiesNumberResponse {\n");
@ -3369,8 +3695,13 @@ void ListEntitiesNumberResponse::dump_to(std::string &out) const {
sprintf(buffer, "%g", this->step);
out.append(buffer);
out.append("\n");
out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default));
out.append("\n");
out.append("}");
}
#endif
bool NumberStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 3: {
@ -3400,6 +3731,7 @@ void NumberStateResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_float(2, this->state);
buffer.encode_bool(3, this->missing_state);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void NumberStateResponse::dump_to(std::string &out) const {
char buffer[64];
out.append("NumberStateResponse {\n");
@ -3418,6 +3750,7 @@ void NumberStateResponse::dump_to(std::string &out) const {
out.append("\n");
out.append("}");
}
#endif
bool NumberCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
switch (field_id) {
case 1: {
@ -3436,6 +3769,7 @@ void NumberCommandRequest::encode(ProtoWriteBuffer buffer) const {
buffer.encode_fixed32(1, this->key);
buffer.encode_float(2, this->state);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void NumberCommandRequest::dump_to(std::string &out) const {
char buffer[64];
out.append("NumberCommandRequest {\n");
@ -3450,6 +3784,194 @@ void NumberCommandRequest::dump_to(std::string &out) const {
out.append("\n");
out.append("}");
}
#endif
bool ListEntitiesSelectResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 7: {
this->disabled_by_default = value.as_bool();
return true;
}
default:
return false;
}
}
bool ListEntitiesSelectResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 1: {
this->object_id = value.as_string();
return true;
}
case 3: {
this->name = value.as_string();
return true;
}
case 4: {
this->unique_id = value.as_string();
return true;
}
case 5: {
this->icon = value.as_string();
return true;
}
case 6: {
this->options.push_back(value.as_string());
return true;
}
default:
return false;
}
}
bool ListEntitiesSelectResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
switch (field_id) {
case 2: {
this->key = value.as_fixed32();
return true;
}
default:
return false;
}
}
void ListEntitiesSelectResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(1, this->object_id);
buffer.encode_fixed32(2, this->key);
buffer.encode_string(3, this->name);
buffer.encode_string(4, this->unique_id);
buffer.encode_string(5, this->icon);
for (auto &it : this->options) {
buffer.encode_string(6, it, true);
}
buffer.encode_bool(7, this->disabled_by_default);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesSelectResponse::dump_to(std::string &out) const {
char buffer[64];
out.append("ListEntitiesSelectResponse {\n");
out.append(" object_id: ");
out.append("'").append(this->object_id).append("'");
out.append("\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
out.append(buffer);
out.append("\n");
out.append(" name: ");
out.append("'").append(this->name).append("'");
out.append("\n");
out.append(" unique_id: ");
out.append("'").append(this->unique_id).append("'");
out.append("\n");
out.append(" icon: ");
out.append("'").append(this->icon).append("'");
out.append("\n");
for (const auto &it : this->options) {
out.append(" options: ");
out.append("'").append(it).append("'");
out.append("\n");
}
out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default));
out.append("\n");
out.append("}");
}
#endif
bool SelectStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 3: {
this->missing_state = value.as_bool();
return true;
}
default:
return false;
}
}
bool SelectStateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 2: {
this->state = value.as_string();
return true;
}
default:
return false;
}
}
bool SelectStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
switch (field_id) {
case 1: {
this->key = value.as_fixed32();
return true;
}
default:
return false;
}
}
void SelectStateResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_fixed32(1, this->key);
buffer.encode_string(2, this->state);
buffer.encode_bool(3, this->missing_state);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void SelectStateResponse::dump_to(std::string &out) const {
char buffer[64];
out.append("SelectStateResponse {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
out.append(buffer);
out.append("\n");
out.append(" state: ");
out.append("'").append(this->state).append("'");
out.append("\n");
out.append(" missing_state: ");
out.append(YESNO(this->missing_state));
out.append("\n");
out.append("}");
}
#endif
bool SelectCommandRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 2: {
this->state = value.as_string();
return true;
}
default:
return false;
}
}
bool SelectCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
switch (field_id) {
case 1: {
this->key = value.as_fixed32();
return true;
}
default:
return false;
}
}
void SelectCommandRequest::encode(ProtoWriteBuffer buffer) const {
buffer.encode_fixed32(1, this->key);
buffer.encode_string(2, this->state);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void SelectCommandRequest::dump_to(std::string &out) const {
char buffer[64];
out.append("SelectCommandRequest {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
out.append(buffer);
out.append("\n");
out.append(" state: ");
out.append("'").append(this->state).append("'");
out.append("\n");
out.append("}");
}
#endif
} // namespace api
} // namespace esphome

View File

@ -32,9 +32,22 @@ enum FanDirection : uint32_t {
FAN_DIRECTION_FORWARD = 0,
FAN_DIRECTION_REVERSE = 1,
};
enum ColorMode : uint32_t {
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,
};
enum SensorStateClass : uint32_t {
STATE_CLASS_NONE = 0,
STATE_CLASS_MEASUREMENT = 1,
STATE_CLASS_TOTAL_INCREASING = 2,
};
enum SensorLastResetType : uint32_t {
LAST_RESET_NONE = 0,
@ -46,9 +59,10 @@ enum LogLevel : uint32_t {
LOG_LEVEL_ERROR = 1,
LOG_LEVEL_WARN = 2,
LOG_LEVEL_INFO = 3,
LOG_LEVEL_DEBUG = 4,
LOG_LEVEL_VERBOSE = 5,
LOG_LEVEL_VERY_VERBOSE = 6,
LOG_LEVEL_CONFIG = 4,
LOG_LEVEL_DEBUG = 5,
LOG_LEVEL_VERBOSE = 6,
LOG_LEVEL_VERY_VERBOSE = 7,
};
enum ServiceArgType : uint32_t {
SERVICE_ARG_TYPE_BOOL = 0,
@ -111,7 +125,9 @@ class HelloRequest : public ProtoMessage {
public:
std::string client_info{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
@ -122,7 +138,9 @@ class HelloResponse : public ProtoMessage {
uint32_t api_version_minor{0};
std::string server_info{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
@ -132,7 +150,9 @@ class ConnectRequest : public ProtoMessage {
public:
std::string password{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
@ -141,7 +161,9 @@ class ConnectResponse : public ProtoMessage {
public:
bool invalid_password{false};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
@ -149,35 +171,45 @@ class ConnectResponse : public ProtoMessage {
class DisconnectRequest : public ProtoMessage {
public:
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
};
class DisconnectResponse : public ProtoMessage {
public:
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
};
class PingRequest : public ProtoMessage {
public:
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
};
class PingResponse : public ProtoMessage {
public:
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
};
class DeviceInfoRequest : public ProtoMessage {
public:
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
};
@ -193,7 +225,9 @@ class DeviceInfoResponse : public ProtoMessage {
std::string project_name{};
std::string project_version{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
@ -202,21 +236,27 @@ class DeviceInfoResponse : public ProtoMessage {
class ListEntitiesRequest : public ProtoMessage {
public:
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
};
class ListEntitiesDoneResponse : public ProtoMessage {
public:
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
};
class SubscribeStatesRequest : public ProtoMessage {
public:
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
};
@ -228,8 +268,11 @@ class ListEntitiesBinarySensorResponse : public ProtoMessage {
std::string unique_id{};
std::string device_class{};
bool is_status_binary_sensor{false};
bool disabled_by_default{false};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
@ -242,7 +285,9 @@ class BinarySensorStateResponse : public ProtoMessage {
bool state{false};
bool missing_state{false};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
@ -258,8 +303,11 @@ class ListEntitiesCoverResponse : public ProtoMessage {
bool supports_position{false};
bool supports_tilt{false};
std::string device_class{};
bool disabled_by_default{false};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
@ -274,7 +322,9 @@ class CoverStateResponse : public ProtoMessage {
float tilt{0.0f};
enums::CoverOperation current_operation{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
@ -291,7 +341,9 @@ class CoverCommandRequest : public ProtoMessage {
float tilt{0.0f};
bool stop{false};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
@ -307,8 +359,11 @@ class ListEntitiesFanResponse : public ProtoMessage {
bool supports_speed{false};
bool supports_direction{false};
int32_t supported_speed_count{0};
bool disabled_by_default{false};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
@ -324,7 +379,9 @@ class FanStateResponse : public ProtoMessage {
enums::FanDirection direction{};
int32_t speed_level{0};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
@ -344,7 +401,9 @@ class FanCommandRequest : public ProtoMessage {
bool has_speed_level{false};
int32_t speed_level{0};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
@ -356,15 +415,19 @@ class ListEntitiesLightResponse : public ProtoMessage {
uint32_t key{0};
std::string name{};
std::string unique_id{};
bool supports_brightness{false};
bool supports_rgb{false};
bool supports_white_value{false};
bool supports_color_temperature{false};
std::vector<enums::ColorMode> supported_color_modes{};
bool legacy_supports_brightness{false};
bool legacy_supports_rgb{false};
bool legacy_supports_white_value{false};
bool legacy_supports_color_temperature{false};
float min_mireds{0.0f};
float max_mireds{0.0f};
std::vector<std::string> effects{};
bool disabled_by_default{false};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
@ -376,15 +439,20 @@ class LightStateResponse : public ProtoMessage {
uint32_t key{0};
bool state{false};
float brightness{0.0f};
enums::ColorMode color_mode{};
float color_brightness{0.0f};
float red{0.0f};
float green{0.0f};
float blue{0.0f};
float white{0.0f};
float color_temperature{0.0f};
float cold_white{0.0f};
float warm_white{0.0f};
std::string effect{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
@ -398,6 +466,8 @@ class LightCommandRequest : public ProtoMessage {
bool state{false};
bool has_brightness{false};
float brightness{0.0f};
bool has_color_mode{false};
enums::ColorMode color_mode{};
bool has_color_brightness{false};
float color_brightness{0.0f};
bool has_rgb{false};
@ -408,6 +478,10 @@ class LightCommandRequest : public ProtoMessage {
float white{0.0f};
bool has_color_temperature{false};
float color_temperature{0.0f};
bool has_cold_white{false};
float cold_white{0.0f};
bool has_warm_white{false};
float warm_white{0.0f};
bool has_transition_length{false};
uint32_t transition_length{0};
bool has_flash_length{false};
@ -415,7 +489,9 @@ class LightCommandRequest : public ProtoMessage {
bool has_effect{false};
std::string effect{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
@ -435,8 +511,11 @@ class ListEntitiesSensorResponse : public ProtoMessage {
std::string device_class{};
enums::SensorStateClass state_class{};
enums::SensorLastResetType last_reset_type{};
bool disabled_by_default{false};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
@ -449,7 +528,9 @@ class SensorStateResponse : public ProtoMessage {
float state{0.0f};
bool missing_state{false};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
@ -463,8 +544,11 @@ class ListEntitiesSwitchResponse : public ProtoMessage {
std::string unique_id{};
std::string icon{};
bool assumed_state{false};
bool disabled_by_default{false};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
@ -476,7 +560,9 @@ class SwitchStateResponse : public ProtoMessage {
uint32_t key{0};
bool state{false};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
@ -487,7 +573,9 @@ class SwitchCommandRequest : public ProtoMessage {
uint32_t key{0};
bool state{false};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
@ -500,12 +588,16 @@ class ListEntitiesTextSensorResponse : public ProtoMessage {
std::string name{};
std::string unique_id{};
std::string icon{};
bool disabled_by_default{false};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class TextSensorStateResponse : public ProtoMessage {
public:
@ -513,7 +605,9 @@ class TextSensorStateResponse : public ProtoMessage {
std::string state{};
bool missing_state{false};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
@ -525,7 +619,9 @@ class SubscribeLogsRequest : public ProtoMessage {
enums::LogLevel level{};
bool dump_config{false};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
@ -533,11 +629,12 @@ class SubscribeLogsRequest : public ProtoMessage {
class SubscribeLogsResponse : public ProtoMessage {
public:
enums::LogLevel level{};
std::string tag{};
std::string message{};
bool send_failed{false};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
@ -546,7 +643,9 @@ class SubscribeLogsResponse : public ProtoMessage {
class SubscribeHomeassistantServicesRequest : public ProtoMessage {
public:
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
};
@ -555,7 +654,9 @@ class HomeassistantServiceMap : public ProtoMessage {
std::string key{};
std::string value{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
@ -568,7 +669,9 @@ class HomeassistantServiceResponse : public ProtoMessage {
std::vector<HomeassistantServiceMap> variables{};
bool is_event{false};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
@ -577,7 +680,9 @@ class HomeassistantServiceResponse : public ProtoMessage {
class SubscribeHomeAssistantStatesRequest : public ProtoMessage {
public:
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
};
@ -586,7 +691,9 @@ class SubscribeHomeAssistantStateResponse : public ProtoMessage {
std::string entity_id{};
std::string attribute{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
@ -597,7 +704,9 @@ class HomeAssistantStateResponse : public ProtoMessage {
std::string state{};
std::string attribute{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
@ -605,7 +714,9 @@ class HomeAssistantStateResponse : public ProtoMessage {
class GetTimeRequest : public ProtoMessage {
public:
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
};
@ -613,7 +724,9 @@ class GetTimeResponse : public ProtoMessage {
public:
uint32_t epoch_seconds{0};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
@ -623,7 +736,9 @@ class ListEntitiesServicesArgument : public ProtoMessage {
std::string name{};
enums::ServiceArgType type{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
@ -635,7 +750,9 @@ class ListEntitiesServicesResponse : public ProtoMessage {
uint32_t key{0};
std::vector<ListEntitiesServicesArgument> args{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
@ -653,7 +770,9 @@ class ExecuteServiceArgument : public ProtoMessage {
std::vector<float> float_array{};
std::vector<std::string> string_array{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
@ -665,7 +784,9 @@ class ExecuteServiceRequest : public ProtoMessage {
uint32_t key{0};
std::vector<ExecuteServiceArgument> args{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
@ -677,12 +798,16 @@ class ListEntitiesCameraResponse : public ProtoMessage {
uint32_t key{0};
std::string name{};
std::string unique_id{};
bool disabled_by_default{false};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class CameraImageResponse : public ProtoMessage {
public:
@ -690,7 +815,9 @@ class CameraImageResponse : public ProtoMessage {
std::string data{};
bool done{false};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
@ -702,7 +829,9 @@ class CameraImageRequest : public ProtoMessage {
bool single{false};
bool stream{false};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
@ -726,8 +855,11 @@ class ListEntitiesClimateResponse : public ProtoMessage {
std::vector<std::string> supported_custom_fan_modes{};
std::vector<enums::ClimatePreset> supported_presets{};
std::vector<std::string> supported_custom_presets{};
bool disabled_by_default{false};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
@ -750,7 +882,9 @@ class ClimateStateResponse : public ProtoMessage {
enums::ClimatePreset preset{};
std::string custom_preset{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
@ -781,7 +915,9 @@ class ClimateCommandRequest : public ProtoMessage {
bool has_custom_preset{false};
std::string custom_preset{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
@ -798,12 +934,16 @@ class ListEntitiesNumberResponse : public ProtoMessage {
float min_value{0.0f};
float max_value{0.0f};
float step{0.0f};
bool disabled_by_default{false};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class NumberStateResponse : public ProtoMessage {
public:
@ -811,7 +951,9 @@ class NumberStateResponse : public ProtoMessage {
float state{0.0f};
bool missing_state{false};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
@ -822,11 +964,60 @@ class NumberCommandRequest : public ProtoMessage {
uint32_t key{0};
float state{0.0f};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
};
class ListEntitiesSelectResponse : public ProtoMessage {
public:
std::string object_id{};
uint32_t key{0};
std::string name{};
std::string unique_id{};
std::string icon{};
std::vector<std::string> options{};
bool disabled_by_default{false};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class SelectStateResponse : public ProtoMessage {
public:
uint32_t key{0};
std::string state{};
bool missing_state{false};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class SelectCommandRequest : public ProtoMessage {
public:
uint32_t key{0};
std::string state{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
};
} // namespace api
} // namespace esphome

View File

@ -9,58 +9,82 @@ namespace api {
static const char *const TAG = "api.service";
bool APIServerConnectionBase::send_hello_response(const HelloResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_hello_response: %s", msg.dump().c_str());
#endif
return this->send_message_<HelloResponse>(msg, 2);
}
bool APIServerConnectionBase::send_connect_response(const ConnectResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_connect_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ConnectResponse>(msg, 4);
}
bool APIServerConnectionBase::send_disconnect_request(const DisconnectRequest &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_disconnect_request: %s", msg.dump().c_str());
#endif
return this->send_message_<DisconnectRequest>(msg, 5);
}
bool APIServerConnectionBase::send_disconnect_response(const DisconnectResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_disconnect_response: %s", msg.dump().c_str());
#endif
return this->send_message_<DisconnectResponse>(msg, 6);
}
bool APIServerConnectionBase::send_ping_request(const PingRequest &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_ping_request: %s", msg.dump().c_str());
#endif
return this->send_message_<PingRequest>(msg, 7);
}
bool APIServerConnectionBase::send_ping_response(const PingResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_ping_response: %s", msg.dump().c_str());
#endif
return this->send_message_<PingResponse>(msg, 8);
}
bool APIServerConnectionBase::send_device_info_response(const DeviceInfoResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_device_info_response: %s", msg.dump().c_str());
#endif
return this->send_message_<DeviceInfoResponse>(msg, 10);
}
bool APIServerConnectionBase::send_list_entities_done_response(const ListEntitiesDoneResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_done_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesDoneResponse>(msg, 19);
}
#ifdef USE_BINARY_SENSOR
bool APIServerConnectionBase::send_list_entities_binary_sensor_response(const ListEntitiesBinarySensorResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_binary_sensor_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesBinarySensorResponse>(msg, 12);
}
#endif
#ifdef USE_BINARY_SENSOR
bool APIServerConnectionBase::send_binary_sensor_state_response(const BinarySensorStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_binary_sensor_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BinarySensorStateResponse>(msg, 21);
}
#endif
#ifdef USE_COVER
bool APIServerConnectionBase::send_list_entities_cover_response(const ListEntitiesCoverResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_cover_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesCoverResponse>(msg, 13);
}
#endif
#ifdef USE_COVER
bool APIServerConnectionBase::send_cover_state_response(const CoverStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_cover_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<CoverStateResponse>(msg, 22);
}
#endif
@ -68,13 +92,17 @@ bool APIServerConnectionBase::send_cover_state_response(const CoverStateResponse
#endif
#ifdef USE_FAN
bool APIServerConnectionBase::send_list_entities_fan_response(const ListEntitiesFanResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_fan_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesFanResponse>(msg, 14);
}
#endif
#ifdef USE_FAN
bool APIServerConnectionBase::send_fan_state_response(const FanStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_fan_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<FanStateResponse>(msg, 23);
}
#endif
@ -82,13 +110,17 @@ bool APIServerConnectionBase::send_fan_state_response(const FanStateResponse &ms
#endif
#ifdef USE_LIGHT
bool APIServerConnectionBase::send_list_entities_light_response(const ListEntitiesLightResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_light_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesLightResponse>(msg, 15);
}
#endif
#ifdef USE_LIGHT
bool APIServerConnectionBase::send_light_state_response(const LightStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_light_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<LightStateResponse>(msg, 24);
}
#endif
@ -96,25 +128,33 @@ bool APIServerConnectionBase::send_light_state_response(const LightStateResponse
#endif
#ifdef USE_SENSOR
bool APIServerConnectionBase::send_list_entities_sensor_response(const ListEntitiesSensorResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_sensor_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesSensorResponse>(msg, 16);
}
#endif
#ifdef USE_SENSOR
bool APIServerConnectionBase::send_sensor_state_response(const SensorStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_sensor_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<SensorStateResponse>(msg, 25);
}
#endif
#ifdef USE_SWITCH
bool APIServerConnectionBase::send_list_entities_switch_response(const ListEntitiesSwitchResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_switch_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesSwitchResponse>(msg, 17);
}
#endif
#ifdef USE_SWITCH
bool APIServerConnectionBase::send_switch_state_response(const SwitchStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_switch_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<SwitchStateResponse>(msg, 26);
}
#endif
@ -122,13 +162,17 @@ bool APIServerConnectionBase::send_switch_state_response(const SwitchStateRespon
#endif
#ifdef USE_TEXT_SENSOR
bool APIServerConnectionBase::send_list_entities_text_sensor_response(const ListEntitiesTextSensorResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_text_sensor_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesTextSensorResponse>(msg, 18);
}
#endif
#ifdef USE_TEXT_SENSOR
bool APIServerConnectionBase::send_text_sensor_state_response(const TextSensorStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_text_sensor_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<TextSensorStateResponse>(msg, 27);
}
#endif
@ -136,35 +180,49 @@ bool APIServerConnectionBase::send_subscribe_logs_response(const SubscribeLogsRe
return this->send_message_<SubscribeLogsResponse>(msg, 29);
}
bool APIServerConnectionBase::send_homeassistant_service_response(const HomeassistantServiceResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_homeassistant_service_response: %s", msg.dump().c_str());
#endif
return this->send_message_<HomeassistantServiceResponse>(msg, 35);
}
bool APIServerConnectionBase::send_subscribe_home_assistant_state_response(
const SubscribeHomeAssistantStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_subscribe_home_assistant_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<SubscribeHomeAssistantStateResponse>(msg, 39);
}
bool APIServerConnectionBase::send_get_time_request(const GetTimeRequest &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_get_time_request: %s", msg.dump().c_str());
#endif
return this->send_message_<GetTimeRequest>(msg, 36);
}
bool APIServerConnectionBase::send_get_time_response(const GetTimeResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_get_time_response: %s", msg.dump().c_str());
#endif
return this->send_message_<GetTimeResponse>(msg, 37);
}
bool APIServerConnectionBase::send_list_entities_services_response(const ListEntitiesServicesResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_services_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesServicesResponse>(msg, 41);
}
#ifdef USE_ESP32_CAMERA
bool APIServerConnectionBase::send_list_entities_camera_response(const ListEntitiesCameraResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_camera_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesCameraResponse>(msg, 43);
}
#endif
#ifdef USE_ESP32_CAMERA
bool APIServerConnectionBase::send_camera_image_response(const CameraImageResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_camera_image_response: %s", msg.dump().c_str());
#endif
return this->send_message_<CameraImageResponse>(msg, 44);
}
#endif
@ -172,13 +230,17 @@ bool APIServerConnectionBase::send_camera_image_response(const CameraImageRespon
#endif
#ifdef USE_CLIMATE
bool APIServerConnectionBase::send_list_entities_climate_response(const ListEntitiesClimateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_climate_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesClimateResponse>(msg, 46);
}
#endif
#ifdef USE_CLIMATE
bool APIServerConnectionBase::send_climate_state_response(const ClimateStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_climate_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ClimateStateResponse>(msg, 47);
}
#endif
@ -186,87 +248,129 @@ bool APIServerConnectionBase::send_climate_state_response(const ClimateStateResp
#endif
#ifdef USE_NUMBER
bool APIServerConnectionBase::send_list_entities_number_response(const ListEntitiesNumberResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_number_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesNumberResponse>(msg, 49);
}
#endif
#ifdef USE_NUMBER
bool APIServerConnectionBase::send_number_state_response(const NumberStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_number_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<NumberStateResponse>(msg, 50);
}
#endif
#ifdef USE_NUMBER
#endif
#ifdef USE_SELECT
bool APIServerConnectionBase::send_list_entities_select_response(const ListEntitiesSelectResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_select_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesSelectResponse>(msg, 52);
}
#endif
#ifdef USE_SELECT
bool APIServerConnectionBase::send_select_state_response(const SelectStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_select_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<SelectStateResponse>(msg, 53);
}
#endif
#ifdef USE_SELECT
#endif
bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {
switch (msg_type) {
case 1: {
HelloRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_hello_request: %s", msg.dump().c_str());
#endif
this->on_hello_request(msg);
break;
}
case 3: {
ConnectRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_connect_request: %s", msg.dump().c_str());
#endif
this->on_connect_request(msg);
break;
}
case 5: {
DisconnectRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_disconnect_request: %s", msg.dump().c_str());
#endif
this->on_disconnect_request(msg);
break;
}
case 6: {
DisconnectResponse msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_disconnect_response: %s", msg.dump().c_str());
#endif
this->on_disconnect_response(msg);
break;
}
case 7: {
PingRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_ping_request: %s", msg.dump().c_str());
#endif
this->on_ping_request(msg);
break;
}
case 8: {
PingResponse msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_ping_response: %s", msg.dump().c_str());
#endif
this->on_ping_response(msg);
break;
}
case 9: {
DeviceInfoRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_device_info_request: %s", msg.dump().c_str());
#endif
this->on_device_info_request(msg);
break;
}
case 11: {
ListEntitiesRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_list_entities_request: %s", msg.dump().c_str());
#endif
this->on_list_entities_request(msg);
break;
}
case 20: {
SubscribeStatesRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_subscribe_states_request: %s", msg.dump().c_str());
#endif
this->on_subscribe_states_request(msg);
break;
}
case 28: {
SubscribeLogsRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_subscribe_logs_request: %s", msg.dump().c_str());
#endif
this->on_subscribe_logs_request(msg);
break;
}
@ -274,7 +378,9 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
#ifdef USE_COVER
CoverCommandRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_cover_command_request: %s", msg.dump().c_str());
#endif
this->on_cover_command_request(msg);
#endif
break;
@ -283,7 +389,9 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
#ifdef USE_FAN
FanCommandRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_fan_command_request: %s", msg.dump().c_str());
#endif
this->on_fan_command_request(msg);
#endif
break;
@ -292,7 +400,9 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
#ifdef USE_LIGHT
LightCommandRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_light_command_request: %s", msg.dump().c_str());
#endif
this->on_light_command_request(msg);
#endif
break;
@ -301,7 +411,9 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
#ifdef USE_SWITCH
SwitchCommandRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_switch_command_request: %s", msg.dump().c_str());
#endif
this->on_switch_command_request(msg);
#endif
break;
@ -309,42 +421,54 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
case 34: {
SubscribeHomeassistantServicesRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_subscribe_homeassistant_services_request: %s", msg.dump().c_str());
#endif
this->on_subscribe_homeassistant_services_request(msg);
break;
}
case 36: {
GetTimeRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_get_time_request: %s", msg.dump().c_str());
#endif
this->on_get_time_request(msg);
break;
}
case 37: {
GetTimeResponse msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_get_time_response: %s", msg.dump().c_str());
#endif
this->on_get_time_response(msg);
break;
}
case 38: {
SubscribeHomeAssistantStatesRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_subscribe_home_assistant_states_request: %s", msg.dump().c_str());
#endif
this->on_subscribe_home_assistant_states_request(msg);
break;
}
case 40: {
HomeAssistantStateResponse msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_home_assistant_state_response: %s", msg.dump().c_str());
#endif
this->on_home_assistant_state_response(msg);
break;
}
case 42: {
ExecuteServiceRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_execute_service_request: %s", msg.dump().c_str());
#endif
this->on_execute_service_request(msg);
break;
}
@ -352,7 +476,9 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
#ifdef USE_ESP32_CAMERA
CameraImageRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_camera_image_request: %s", msg.dump().c_str());
#endif
this->on_camera_image_request(msg);
#endif
break;
@ -361,7 +487,9 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
#ifdef USE_CLIMATE
ClimateCommandRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_climate_command_request: %s", msg.dump().c_str());
#endif
this->on_climate_command_request(msg);
#endif
break;
@ -370,8 +498,21 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
#ifdef USE_NUMBER
NumberCommandRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_number_command_request: %s", msg.dump().c_str());
#endif
this->on_number_command_request(msg);
#endif
break;
}
case 54: {
#ifdef USE_SELECT
SelectCommandRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_select_command_request: %s", msg.dump().c_str());
#endif
this->on_select_command_request(msg);
#endif
break;
}
@ -583,6 +724,19 @@ void APIServerConnection::on_number_command_request(const NumberCommandRequest &
this->number_command(msg);
}
#endif
#ifdef USE_SELECT
void APIServerConnection::on_select_command_request(const SelectCommandRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->select_command(msg);
}
#endif
} // namespace api
} // namespace esphome

View File

@ -120,6 +120,15 @@ class APIServerConnectionBase : public ProtoService {
#endif
#ifdef USE_NUMBER
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
protected:
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
#ifdef USE_NUMBER
virtual void number_command(const NumberCommandRequest &msg) = 0;
#endif
#ifdef USE_SELECT
virtual void select_command(const SelectCommandRequest &msg) = 0;
#endif
protected:
void on_hello_request(const HelloRequest &msg) override;
@ -194,6 +206,9 @@ class APIServerConnection : public APIServerConnectionBase {
#ifdef USE_NUMBER
void on_number_command_request(const NumberCommandRequest &msg) override;
#endif
#ifdef USE_SELECT
void on_select_command_request(const SelectCommandRequest &msg) override;
#endif
};
} // namespace api

View File

@ -206,6 +206,15 @@ void APIServer::on_number_update(number::Number *obj, float state) {
}
#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; }
void APIServer::set_port(uint16_t port) { this->port_ = port; }
APIServer *global_api_server = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)

View File

@ -9,7 +9,6 @@
#include "util.h"
#include "list_entities.h"
#include "subscribe_state.h"
#include "homeassistant_service.h"
#include "user_services.h"
#ifdef ARDUINO_ARCH_ESP32
@ -63,6 +62,9 @@ class APIServer : public Component, public Controller {
#endif
#ifdef USE_NUMBER
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
void send_homeassistant_service_call(const HomeassistantServiceResponse &call);
void register_user_service(UserServiceDescriptor *descriptor) { this->user_services_.push_back(descriptor); }

View File

@ -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); }
#endif
#ifdef USE_SELECT
bool ListEntitiesIterator::on_select(select::Select *select) { return this->client_->send_select_info(select); }
#endif
} // namespace api
} // namespace esphome

View File

@ -42,6 +42,9 @@ class ListEntitiesIterator : public ComponentIterator {
#endif
#ifdef USE_NUMBER
bool on_number(number::Number *number) override;
#endif
#ifdef USE_SELECT
bool on_select(select::Select *select) override;
#endif
bool on_end() override;

View File

@ -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 out;
this->dump_to(out);
return out;
}
#endif
} // namespace api
} // namespace esphome

View File

@ -1,8 +1,13 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/log.h"
#include "esphome/core/helpers.h"
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
#define HAS_PROTO_MESSAGE_DUMP
#endif
namespace esphome {
namespace api {
@ -243,8 +248,10 @@ class ProtoMessage {
public:
virtual void encode(ProtoWriteBuffer buffer) const = 0;
void decode(const uint8_t *buffer, size_t length);
#ifdef HAS_PROTO_MESSAGE_DUMP
std::string dump() const;
virtual void dump_to(std::string &out) const = 0;
#endif
protected:
virtual bool decode_varint(uint32_t field_id, ProtoVarInt value) { return false; }

View File

@ -42,6 +42,11 @@ bool InitialStateIterator::on_number(number::Number *number) {
return this->client_->send_number_state(number, number->state);
}
#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)
: ComponentIterator(server), client_(client) {}

View File

@ -39,6 +39,9 @@ class InitialStateIterator : public ComponentIterator {
#endif
#ifdef USE_NUMBER
bool on_number(number::Number *number) override;
#endif
#ifdef USE_SELECT
bool on_select(select::Select *select) override;
#endif
protected:
APIConnection *client_;

View File

@ -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) {
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;
}
template<> std::vector<float> get_execute_arg_value<std::vector<float>>(const ExecuteServiceArgument &arg) {

View File

@ -182,6 +182,21 @@ void ComponentIterator::advance() {
}
}
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
case IteratorState::MAX:
if (this->on_end()) {

View File

@ -50,6 +50,9 @@ class ComponentIterator {
#endif
#ifdef USE_NUMBER
virtual bool on_number(number::Number *number) = 0;
#endif
#ifdef USE_SELECT
virtual bool on_select(select::Select *select) = 0;
#endif
virtual bool on_end();
@ -87,6 +90,9 @@ class ComponentIterator {
#endif
#ifdef USE_NUMBER
NUMBER,
#endif
#ifdef USE_SELECT
SELECT,
#endif
MAX,
} state_{IteratorState::NONE};

View File

@ -4,10 +4,8 @@ from esphome.components import sensor
from esphome.const import (
CONF_DISTANCE,
CONF_LIGHTNING_ENERGY,
DEVICE_CLASS_EMPTY,
STATE_CLASS_NONE,
UNIT_KILOMETER,
UNIT_EMPTY,
ICON_SIGNAL_DISTANCE_VARIANT,
ICON_FLASH,
)
@ -19,14 +17,15 @@ CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_AS3935_ID): cv.use_id(AS3935),
cv.Optional(CONF_DISTANCE): sensor.sensor_schema(
UNIT_KILOMETER,
ICON_SIGNAL_DISTANCE_VARIANT,
1,
DEVICE_CLASS_EMPTY,
STATE_CLASS_NONE,
unit_of_measurement=UNIT_KILOMETER,
icon=ICON_SIGNAL_DISTANCE_VARIANT,
accuracy_decimals=1,
state_class=STATE_CLASS_NONE,
),
cv.Optional(CONF_LIGHTNING_ENERGY): sensor.sensor_schema(
UNIT_EMPTY, ICON_FLASH, 1, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE
icon=ICON_FLASH,
accuracy_decimals=1,
state_class=STATE_CLASS_NONE,
),
}
).extend(cv.COMPONENT_SCHEMA)

View File

@ -12,7 +12,6 @@ from esphome.const import (
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_VOLTAGE,
ICON_EMPTY,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
UNIT_PERCENT,
@ -34,28 +33,28 @@ CONFIG_SCHEMA = (
cv.GenerateID(): cv.declare_id(ATCMiThermometer),
cv.Required(CONF_MAC_ADDRESS): cv.mac_address,
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS,
ICON_EMPTY,
1,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
UNIT_PERCENT,
ICON_EMPTY,
0,
DEVICE_CLASS_HUMIDITY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_PERCENT,
accuracy_decimals=0,
device_class=DEVICE_CLASS_HUMIDITY,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(
UNIT_PERCENT,
ICON_EMPTY,
0,
DEVICE_CLASS_BATTERY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_PERCENT,
accuracy_decimals=0,
device_class=DEVICE_CLASS_BATTERY,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_BATTERY_VOLTAGE): 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,
),
}
)

View File

@ -39,7 +39,7 @@ static const uint16_t ATM90E32_STATUS_S0_OVPHASEBST = 1 << 11; // Over voltage
static const uint16_t ATM90E32_STATUS_S0_OVPHASECST = 1 << 10; // Over voltage on phase C
static const uint16_t ATM90E32_STATUS_S0_UREVWNST = 1 << 9; // Voltage Phase Sequence Error status
static const uint16_t ATM90E32_STATUS_S0_IREVWNST = 1 << 8; // Current Phase Sequence Error status
static const uint16_t ATM90E32_STATUS_S0_INOV0ST = 1 << 7; // Calculated N line current greater tha INWarnTh reg
static const uint16_t ATM90E32_STATUS_S0_INOV0ST = 1 << 7; // Calculated N line current greater than INWarnTh reg
static const uint16_t ATM90E32_STATUS_S0_TQNOLOADST = 1 << 6; // All phase sum reactive power no-load condition status
static const uint16_t ATM90E32_STATUS_S0_TPNOLOADST = 1 << 5; // All phase sum active power no-load condition status
static const uint16_t ATM90E32_STATUS_S0_TASNOLOADST = 1 << 4; // All phase sum apparent power no-load status

View File

@ -12,13 +12,11 @@ from esphome.const import (
CONF_FORWARD_ACTIVE_ENERGY,
CONF_REVERSE_ACTIVE_ENERGY,
DEVICE_CLASS_CURRENT,
DEVICE_CLASS_EMPTY,
DEVICE_CLASS_ENERGY,
DEVICE_CLASS_POWER,
DEVICE_CLASS_POWER_FACTOR,
DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_VOLTAGE,
ICON_EMPTY,
ICON_LIGHTBULB,
ICON_CURRENT_AC,
LAST_RESET_TYPE_AUTO,
@ -27,7 +25,6 @@ from esphome.const import (
UNIT_VOLT,
UNIT_AMPERE,
UNIT_WATT,
UNIT_EMPTY,
UNIT_CELSIUS,
UNIT_VOLT_AMPS_REACTIVE,
UNIT_WATT_HOURS,
@ -65,47 +62,47 @@ ATM90E32Component = atm90e32_ns.class_(
ATM90E32_PHASE_SCHEMA = cv.Schema(
{
cv.Optional(CONF_VOLTAGE): 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,
),
cv.Optional(CONF_CURRENT): sensor.sensor_schema(
UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT, STATE_CLASS_MEASUREMENT
unit_of_measurement=UNIT_AMPERE,
accuracy_decimals=2,
device_class=DEVICE_CLASS_CURRENT,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_POWER): sensor.sensor_schema(
UNIT_WATT, ICON_EMPTY, 2, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
unit_of_measurement=UNIT_WATT,
accuracy_decimals=2,
device_class=DEVICE_CLASS_POWER,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_REACTIVE_POWER): sensor.sensor_schema(
UNIT_VOLT_AMPS_REACTIVE,
ICON_LIGHTBULB,
2,
DEVICE_CLASS_EMPTY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_VOLT_AMPS_REACTIVE,
icon=ICON_LIGHTBULB,
accuracy_decimals=2,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_POWER_FACTOR): sensor.sensor_schema(
UNIT_EMPTY,
ICON_EMPTY,
2,
DEVICE_CLASS_POWER_FACTOR,
STATE_CLASS_MEASUREMENT,
accuracy_decimals=2,
device_class=DEVICE_CLASS_POWER_FACTOR,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_FORWARD_ACTIVE_ENERGY): sensor.sensor_schema(
UNIT_WATT_HOURS,
ICON_EMPTY,
2,
DEVICE_CLASS_ENERGY,
STATE_CLASS_MEASUREMENT,
LAST_RESET_TYPE_AUTO,
unit_of_measurement=UNIT_WATT_HOURS,
accuracy_decimals=2,
device_class=DEVICE_CLASS_ENERGY,
state_class=STATE_CLASS_MEASUREMENT,
last_reset_type=LAST_RESET_TYPE_AUTO,
),
cv.Optional(CONF_REVERSE_ACTIVE_ENERGY): sensor.sensor_schema(
UNIT_WATT_HOURS,
ICON_EMPTY,
2,
DEVICE_CLASS_ENERGY,
STATE_CLASS_MEASUREMENT,
LAST_RESET_TYPE_AUTO,
unit_of_measurement=UNIT_WATT_HOURS,
accuracy_decimals=2,
device_class=DEVICE_CLASS_ENERGY,
state_class=STATE_CLASS_MEASUREMENT,
last_reset_type=LAST_RESET_TYPE_AUTO,
),
cv.Optional(CONF_GAIN_VOLTAGE, default=7305): cv.uint16_t,
cv.Optional(CONF_GAIN_CT, default=27961): cv.uint16_t,
@ -120,18 +117,16 @@ CONFIG_SCHEMA = (
cv.Optional(CONF_PHASE_B): ATM90E32_PHASE_SCHEMA,
cv.Optional(CONF_PHASE_C): ATM90E32_PHASE_SCHEMA,
cv.Optional(CONF_FREQUENCY): sensor.sensor_schema(
UNIT_HERTZ,
ICON_CURRENT_AC,
1,
DEVICE_CLASS_EMPTY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_HERTZ,
icon=ICON_CURRENT_AC,
accuracy_decimals=1,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_CHIP_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS,
ICON_EMPTY,
1,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Required(CONF_LINE_FREQUENCY): cv.enum(LINE_FREQS, upper=True),
cv.Optional(CONF_CURRENT_PHASES, default="3"): cv.enum(

View File

@ -47,7 +47,7 @@ bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
uint16_t battery_millivolt = data[2] << 8 | data[3];
float battery_voltage = battery_millivolt / 1000.0f;
// Temperature in 1000 * Celcius.
// Temperature in 1000 * Celsius.
uint16_t temp_millicelcius = data[4] << 8 | data[5];
float temp_celcius = temp_millicelcius / 1000.0f;

View File

@ -11,7 +11,6 @@ from esphome.const import (
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_VOLTAGE,
ICON_EMPTY,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
UNIT_PERCENT,
@ -33,28 +32,28 @@ CONFIG_SCHEMA = (
cv.GenerateID(): cv.declare_id(BParasite),
cv.Required(CONF_MAC_ADDRESS): cv.mac_address,
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS,
ICON_EMPTY,
1,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
UNIT_PERCENT,
ICON_EMPTY,
1,
DEVICE_CLASS_HUMIDITY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_PERCENT,
accuracy_decimals=1,
device_class=DEVICE_CLASS_HUMIDITY,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_BATTERY_VOLTAGE): 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,
),
cv.Optional(CONF_MOISTURE): sensor.sensor_schema(
UNIT_PERCENT,
ICON_EMPTY,
1,
DEVICE_CLASS_HUMIDITY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_PERCENT,
accuracy_decimals=1,
device_class=DEVICE_CLASS_HUMIDITY,
state_class=STATE_CLASS_MEASUREMENT,
),
}
)

View File

@ -5,7 +5,6 @@ from esphome.const import (
CONF_ID,
CONF_RESOLUTION,
DEVICE_CLASS_ILLUMINANCE,
ICON_EMPTY,
STATE_CLASS_MEASUREMENT,
UNIT_LUX,
CONF_MEASUREMENT_DURATION,
@ -28,7 +27,10 @@ BH1750Sensor = bh1750_ns.class_(
CONF_MEASUREMENT_TIME = "measurement_time"
CONFIG_SCHEMA = (
sensor.sensor_schema(
UNIT_LUX, ICON_EMPTY, 1, DEVICE_CLASS_ILLUMINANCE, STATE_CLASS_MEASUREMENT
unit_of_measurement=UNIT_LUX,
accuracy_decimals=1,
device_class=DEVICE_CLASS_ILLUMINANCE,
state_class=STATE_CLASS_MEASUREMENT,
)
.extend(
{

View File

@ -12,7 +12,7 @@ class BinaryLightOutput : public light::LightOutput {
void set_output(output::BinaryOutput *output) { output_ = output; }
light::LightTraits get_traits() override {
auto traits = light::LightTraits();
traits.set_supports_brightness(false);
traits.set_supported_color_modes({light::ColorMode::ON_OFF});
return traits;
}
void write_state(light::LightState *state) override {

View File

@ -6,6 +6,7 @@ from esphome.components import mqtt
from esphome.const import (
CONF_DELAY,
CONF_DEVICE_CLASS,
CONF_DISABLED_BY_DEFAULT,
CONF_FILTERS,
CONF_ID,
CONF_INTERNAL,
@ -315,7 +316,7 @@ def validate_multi_click_timing(value):
device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
BINARY_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend(
BINARY_SENSOR_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
{
cv.GenerateID(): cv.declare_id(BinarySensor),
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(
@ -377,6 +378,7 @@ BINARY_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend(
async def setup_binary_sensor_core_(var, config):
cg.add(var.set_name(config[CONF_NAME]))
cg.add(var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT]))
if CONF_INTERNAL in config:
cg.add(var.set_internal(config[CONF_INTERNAL]))
if CONF_DEVICE_CLASS in config:

View File

@ -7,8 +7,6 @@ from esphome.const import (
CONF_CHANNELS,
CONF_VALUE,
CONF_TYPE,
DEVICE_CLASS_EMPTY,
UNIT_EMPTY,
ICON_CHECK_CIRCLE_OUTLINE,
CONF_BINARY_SENSOR,
CONF_GROUP,
@ -35,11 +33,9 @@ entry = {
CONFIG_SCHEMA = cv.typed_schema(
{
CONF_GROUP: sensor.sensor_schema(
UNIT_EMPTY,
ICON_CHECK_CIRCLE_OUTLINE,
0,
DEVICE_CLASS_EMPTY,
STATE_CLASS_NONE,
icon=ICON_CHECK_CIRCLE_OUTLINE,
accuracy_decimals=0,
state_class=STATE_CLASS_NONE,
).extend(
{
cv.GenerateID(): cv.declare_id(BinarySensorMap),

View File

@ -25,7 +25,7 @@ class BLEClientNode {
public:
virtual void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) = 0;
virtual void loop() = 0;
virtual void loop(){};
void set_address(uint64_t address) { address_ = address; }
espbt::ESPBTClient *client;
// This should be transitioned to Established once the node no longer needs

View File

@ -2,12 +2,9 @@ import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor, ble_client, esp32_ble_tracker
from esphome.const import (
DEVICE_CLASS_EMPTY,
CONF_ID,
CONF_LAMBDA,
STATE_CLASS_NONE,
UNIT_EMPTY,
ICON_EMPTY,
CONF_TRIGGER_ID,
CONF_SERVICE_UUID,
)
@ -34,7 +31,8 @@ BLESensorNotifyTrigger = ble_client_ns.class_(
CONFIG_SCHEMA = cv.All(
sensor.sensor_schema(
UNIT_EMPTY, ICON_EMPTY, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE
accuracy_decimals=0,
state_class=STATE_CLASS_NONE,
)
.extend(
{

View File

@ -8,7 +8,6 @@ from esphome.const import (
DEVICE_CLASS_SIGNAL_STRENGTH,
STATE_CLASS_MEASUREMENT,
UNIT_DECIBEL,
ICON_EMPTY,
)
DEPENDENCIES = ["esp32_ble_tracker"]
@ -20,11 +19,10 @@ BLERSSISensor = ble_rssi_ns.class_(
CONFIG_SCHEMA = cv.All(
sensor.sensor_schema(
UNIT_DECIBEL,
ICON_EMPTY,
0,
DEVICE_CLASS_SIGNAL_STRENGTH,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_DECIBEL,
accuracy_decimals=0,
device_class=DEVICE_CLASS_SIGNAL_STRENGTH,
state_class=STATE_CLASS_MEASUREMENT,
)
.extend(
{

View File

@ -11,7 +11,6 @@ from esphome.const import (
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_PRESSURE,
DEVICE_CLASS_TEMPERATURE,
ICON_EMPTY,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
UNIT_HECTOPASCAL,
@ -49,11 +48,10 @@ CONFIG_SCHEMA = (
{
cv.GenerateID(): cv.declare_id(BME280Component),
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS,
ICON_EMPTY,
1,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
).extend(
{
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
@ -62,11 +60,10 @@ CONFIG_SCHEMA = (
}
),
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
UNIT_HECTOPASCAL,
ICON_EMPTY,
1,
DEVICE_CLASS_PRESSURE,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_HECTOPASCAL,
accuracy_decimals=1,
device_class=DEVICE_CLASS_PRESSURE,
state_class=STATE_CLASS_MEASUREMENT,
).extend(
{
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
@ -75,11 +72,10 @@ CONFIG_SCHEMA = (
}
),
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
UNIT_PERCENT,
ICON_EMPTY,
1,
DEVICE_CLASS_HUMIDITY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_PERCENT,
accuracy_decimals=1,
device_class=DEVICE_CLASS_HUMIDITY,
state_class=STATE_CLASS_MEASUREMENT,
).extend(
{
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(

View File

@ -12,7 +12,6 @@ from esphome.const import (
CONF_OVERSAMPLING,
CONF_PRESSURE,
CONF_TEMPERATURE,
DEVICE_CLASS_EMPTY,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_PRESSURE,
DEVICE_CLASS_TEMPERATURE,
@ -20,7 +19,6 @@ from esphome.const import (
UNIT_OHM,
ICON_GAS_CYLINDER,
UNIT_CELSIUS,
ICON_EMPTY,
UNIT_HECTOPASCAL,
UNIT_PERCENT,
)
@ -59,11 +57,10 @@ CONFIG_SCHEMA = (
{
cv.GenerateID(): cv.declare_id(BME680Component),
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS,
ICON_EMPTY,
1,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
).extend(
{
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
@ -72,11 +69,10 @@ CONFIG_SCHEMA = (
}
),
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
UNIT_HECTOPASCAL,
ICON_EMPTY,
1,
DEVICE_CLASS_PRESSURE,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_HECTOPASCAL,
accuracy_decimals=1,
device_class=DEVICE_CLASS_PRESSURE,
state_class=STATE_CLASS_MEASUREMENT,
).extend(
{
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
@ -85,11 +81,10 @@ CONFIG_SCHEMA = (
}
),
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
UNIT_PERCENT,
ICON_EMPTY,
1,
DEVICE_CLASS_HUMIDITY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_PERCENT,
accuracy_decimals=1,
device_class=DEVICE_CLASS_HUMIDITY,
state_class=STATE_CLASS_MEASUREMENT,
).extend(
{
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
@ -98,11 +93,10 @@ CONFIG_SCHEMA = (
}
),
cv.Optional(CONF_GAS_RESISTANCE): sensor.sensor_schema(
UNIT_OHM,
ICON_GAS_CYLINDER,
1,
DEVICE_CLASS_EMPTY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_OHM,
icon=ICON_GAS_CYLINDER,
accuracy_decimals=1,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_IIR_FILTER, default="OFF"): cv.enum(
IIR_FILTER_OPTIONS, upper=True

View File

@ -6,13 +6,11 @@ from esphome.const import (
CONF_HUMIDITY,
CONF_PRESSURE,
CONF_TEMPERATURE,
DEVICE_CLASS_EMPTY,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_PRESSURE,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
UNIT_EMPTY,
UNIT_HECTOPASCAL,
UNIT_OHM,
UNIT_PARTS_PER_MILLION,
@ -54,54 +52,60 @@ CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_BME680_BSEC_ID): cv.use_id(BME680BSECComponent),
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS,
ICON_THERMOMETER,
1,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_CELSIUS,
icon=ICON_THERMOMETER,
accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
).extend(
{cv.Optional(CONF_SAMPLE_RATE): cv.enum(SAMPLE_RATE_OPTIONS, upper=True)}
),
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
UNIT_HECTOPASCAL,
ICON_GAUGE,
1,
DEVICE_CLASS_PRESSURE,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_HECTOPASCAL,
icon=ICON_GAUGE,
accuracy_decimals=1,
device_class=DEVICE_CLASS_PRESSURE,
state_class=STATE_CLASS_MEASUREMENT,
).extend(
{cv.Optional(CONF_SAMPLE_RATE): cv.enum(SAMPLE_RATE_OPTIONS, upper=True)}
),
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
UNIT_PERCENT,
ICON_WATER_PERCENT,
1,
DEVICE_CLASS_HUMIDITY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_PERCENT,
icon=ICON_WATER_PERCENT,
accuracy_decimals=1,
device_class=DEVICE_CLASS_HUMIDITY,
state_class=STATE_CLASS_MEASUREMENT,
).extend(
{cv.Optional(CONF_SAMPLE_RATE): cv.enum(SAMPLE_RATE_OPTIONS, upper=True)}
),
cv.Optional(CONF_GAS_RESISTANCE): sensor.sensor_schema(
UNIT_OHM, ICON_GAS_CYLINDER, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_MEASUREMENT
unit_of_measurement=UNIT_OHM,
icon=ICON_GAS_CYLINDER,
accuracy_decimals=0,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_IAQ): sensor.sensor_schema(
UNIT_IAQ, ICON_GAUGE, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_MEASUREMENT
unit_of_measurement=UNIT_IAQ,
icon=ICON_GAUGE,
accuracy_decimals=0,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_IAQ_ACCURACY): sensor.sensor_schema(
UNIT_EMPTY, ICON_ACCURACY, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_MEASUREMENT
icon=ICON_ACCURACY,
accuracy_decimals=0,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_CO2_EQUIVALENT): sensor.sensor_schema(
UNIT_PARTS_PER_MILLION,
ICON_TEST_TUBE,
1,
DEVICE_CLASS_EMPTY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_PARTS_PER_MILLION,
icon=ICON_TEST_TUBE,
accuracy_decimals=1,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_BREATH_VOC_EQUIVALENT): sensor.sensor_schema(
UNIT_PARTS_PER_MILLION,
ICON_TEST_TUBE,
1,
DEVICE_CLASS_EMPTY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_PARTS_PER_MILLION,
icon=ICON_TEST_TUBE,
accuracy_decimals=1,
state_class=STATE_CLASS_MEASUREMENT,
),
}
)

View File

@ -9,7 +9,6 @@ from esphome.const import (
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
ICON_EMPTY,
UNIT_HECTOPASCAL,
)
@ -25,18 +24,16 @@ CONFIG_SCHEMA = (
{
cv.GenerateID(): cv.declare_id(BMP085Component),
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS,
ICON_EMPTY,
1,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
UNIT_HECTOPASCAL,
ICON_EMPTY,
1,
DEVICE_CLASS_PRESSURE,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_HECTOPASCAL,
accuracy_decimals=1,
device_class=DEVICE_CLASS_PRESSURE,
state_class=STATE_CLASS_MEASUREMENT,
),
}
)

View File

@ -9,7 +9,6 @@ from esphome.const import (
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
ICON_EMPTY,
UNIT_HECTOPASCAL,
CONF_IIR_FILTER,
CONF_OVERSAMPLING,
@ -46,11 +45,10 @@ CONFIG_SCHEMA = (
{
cv.GenerateID(): cv.declare_id(BMP280Component),
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS,
ICON_EMPTY,
1,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
).extend(
{
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
@ -59,11 +57,10 @@ CONFIG_SCHEMA = (
}
),
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
UNIT_HECTOPASCAL,
ICON_EMPTY,
1,
DEVICE_CLASS_PRESSURE,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_HECTOPASCAL,
accuracy_decimals=1,
device_class=DEVICE_CLASS_PRESSURE,
state_class=STATE_CLASS_MEASUREMENT,
).extend(
{
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(

View File

@ -3,7 +3,6 @@ import esphome.config_validation as cv
from esphome.components import i2c, sensor
from esphome.const import (
CONF_ID,
DEVICE_CLASS_EMPTY,
ICON_RADIATOR,
STATE_CLASS_MEASUREMENT,
UNIT_PARTS_PER_MILLION,
@ -28,18 +27,16 @@ CONFIG_SCHEMA = (
{
cv.GenerateID(): cv.declare_id(CCS811Component),
cv.Required(CONF_ECO2): sensor.sensor_schema(
UNIT_PARTS_PER_MILLION,
ICON_MOLECULE_CO2,
0,
DEVICE_CLASS_EMPTY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_PARTS_PER_MILLION,
icon=ICON_MOLECULE_CO2,
accuracy_decimals=0,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Required(CONF_TVOC): sensor.sensor_schema(
UNIT_PARTS_PER_BILLION,
ICON_RADIATOR,
0,
DEVICE_CLASS_EMPTY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_PARTS_PER_BILLION,
icon=ICON_RADIATOR,
accuracy_decimals=0,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_BASELINE): cv.hex_uint16_t,
cv.Optional(CONF_TEMPERATURE): cv.use_id(sensor.Sensor),

View File

@ -6,6 +6,7 @@ from esphome.const import (
CONF_AWAY,
CONF_CUSTOM_FAN_MODE,
CONF_CUSTOM_PRESET,
CONF_DISABLED_BY_DEFAULT,
CONF_ID,
CONF_INTERNAL,
CONF_MAX_TEMPERATURE,
@ -86,7 +87,7 @@ validate_climate_swing_mode = cv.enum(CLIMATE_SWING_MODES, upper=True)
# Actions
ControlAction = climate_ns.class_("ControlAction", automation.Action)
CLIMATE_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend(
CLIMATE_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
{
cv.GenerateID(): cv.declare_id(Climate),
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTClimateComponent),
@ -104,6 +105,7 @@ CLIMATE_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend(
async def setup_climate_core_(var, config):
cg.add(var.set_name(config[CONF_NAME]))
cg.add(var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT]))
if CONF_INTERNAL in config:
cg.add(var.set_internal(config[CONF_INTERNAL]))
visual = config[CONF_VISUAL]

View File

@ -63,9 +63,9 @@ class ClimateCall {
* For climate devices with two point target temperature control
*/
ClimateCall &set_target_temperature_high(optional<float> target_temperature_high);
ESPDEPRECATED("set_away() is deprecated, please use .set_preset(CLIMATE_PRESET_AWAY) instead")
ESPDEPRECATED("set_away() is deprecated, please use .set_preset(CLIMATE_PRESET_AWAY) instead", "v1.20")
ClimateCall &set_away(bool away);
ESPDEPRECATED("set_away() is deprecated, please use .set_preset(CLIMATE_PRESET_AWAY) instead")
ESPDEPRECATED("set_away() is deprecated, please use .set_preset(CLIMATE_PRESET_AWAY) instead", "v1.20")
ClimateCall &set_away(optional<bool> away);
/// Set the fan mode of the climate device.
ClimateCall &set_fan_mode(ClimateFanMode fan_mode);
@ -96,7 +96,7 @@ class ClimateCall {
const optional<float> &get_target_temperature() const;
const optional<float> &get_target_temperature_low() const;
const optional<float> &get_target_temperature_high() const;
ESPDEPRECATED("get_away() is deprecated, please use .get_preset() instead")
ESPDEPRECATED("get_away() is deprecated, please use .get_preset() instead", "v1.20")
optional<bool> get_away() const;
const optional<ClimateFanMode> &get_fan_mode() const;
const optional<ClimateSwingMode> &get_swing_mode() const;
@ -193,7 +193,7 @@ class Climate : public Nameable {
* Away allows climate devices to have two different target temperature configs:
* one for normal mode and one for away mode.
*/
ESPDEPRECATED("away is deprecated, use preset instead")
ESPDEPRECATED("away is deprecated, use preset instead", "v1.20")
bool away{false};
/// The active fan mode of the climate device.

View File

@ -50,19 +50,19 @@ class ClimateTraits {
}
void set_supported_modes(std::set<ClimateMode> modes) { supported_modes_ = std::move(modes); }
void add_supported_mode(ClimateMode mode) { supported_modes_.insert(mode); }
ESPDEPRECATED("This method is deprecated, use set_supported_modes() instead")
ESPDEPRECATED("This method is deprecated, use set_supported_modes() instead", "v1.20")
void set_supports_auto_mode(bool supports_auto_mode) { set_mode_support_(CLIMATE_MODE_AUTO, supports_auto_mode); }
ESPDEPRECATED("This method is deprecated, use set_supported_modes() instead")
ESPDEPRECATED("This method is deprecated, use set_supported_modes() instead", "v1.20")
void set_supports_cool_mode(bool supports_cool_mode) { set_mode_support_(CLIMATE_MODE_COOL, supports_cool_mode); }
ESPDEPRECATED("This method is deprecated, use set_supported_modes() instead")
ESPDEPRECATED("This method is deprecated, use set_supported_modes() instead", "v1.20")
void set_supports_heat_mode(bool supports_heat_mode) { set_mode_support_(CLIMATE_MODE_HEAT, supports_heat_mode); }
ESPDEPRECATED("This method is deprecated, use set_supported_modes() instead")
ESPDEPRECATED("This method is deprecated, use set_supported_modes() instead", "v1.20")
void set_supports_heat_cool_mode(bool supported) { set_mode_support_(CLIMATE_MODE_HEAT_COOL, supported); }
ESPDEPRECATED("This method is deprecated, use set_supported_modes() instead")
ESPDEPRECATED("This method is deprecated, use set_supported_modes() instead", "v1.20")
void set_supports_fan_only_mode(bool supports_fan_only_mode) {
set_mode_support_(CLIMATE_MODE_FAN_ONLY, supports_fan_only_mode);
}
ESPDEPRECATED("This method is deprecated, use set_supported_modes() instead")
ESPDEPRECATED("This method is deprecated, use set_supported_modes() instead", "v1.20")
void set_supports_dry_mode(bool supports_dry_mode) { set_mode_support_(CLIMATE_MODE_DRY, supports_dry_mode); }
bool supports_mode(ClimateMode mode) const { return supported_modes_.count(mode); }
const std::set<ClimateMode> get_supported_modes() const { return supported_modes_; }
@ -72,23 +72,23 @@ class ClimateTraits {
void set_supported_fan_modes(std::set<ClimateFanMode> modes) { supported_fan_modes_ = std::move(modes); }
void add_supported_fan_mode(ClimateFanMode mode) { supported_fan_modes_.insert(mode); }
ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead")
ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead", "v1.20")
void set_supports_fan_mode_on(bool supported) { set_fan_mode_support_(CLIMATE_FAN_ON, supported); }
ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead")
ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead", "v1.20")
void set_supports_fan_mode_off(bool supported) { set_fan_mode_support_(CLIMATE_FAN_OFF, supported); }
ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead")
ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead", "v1.20")
void set_supports_fan_mode_auto(bool supported) { set_fan_mode_support_(CLIMATE_FAN_AUTO, supported); }
ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead")
ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead", "v1.20")
void set_supports_fan_mode_low(bool supported) { set_fan_mode_support_(CLIMATE_FAN_LOW, supported); }
ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead")
ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead", "v1.20")
void set_supports_fan_mode_medium(bool supported) { set_fan_mode_support_(CLIMATE_FAN_MEDIUM, supported); }
ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead")
ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead", "v1.20")
void set_supports_fan_mode_high(bool supported) { set_fan_mode_support_(CLIMATE_FAN_HIGH, supported); }
ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead")
ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead", "v1.20")
void set_supports_fan_mode_middle(bool supported) { set_fan_mode_support_(CLIMATE_FAN_MIDDLE, supported); }
ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead")
ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead", "v1.20")
void set_supports_fan_mode_focus(bool supported) { set_fan_mode_support_(CLIMATE_FAN_FOCUS, supported); }
ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead")
ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead", "v1.20")
void set_supports_fan_mode_diffuse(bool supported) { set_fan_mode_support_(CLIMATE_FAN_DIFFUSE, supported); }
bool supports_fan_mode(ClimateFanMode fan_mode) const { return supported_fan_modes_.count(fan_mode); }
bool get_supports_fan_modes() const { return !supported_fan_modes_.empty() || !supported_custom_fan_modes_.empty(); }
@ -115,25 +115,25 @@ class ClimateTraits {
bool supports_custom_preset(const std::string &custom_preset) const {
return supported_custom_presets_.count(custom_preset);
}
ESPDEPRECATED("This method is deprecated, use set_supported_presets() instead")
ESPDEPRECATED("This method is deprecated, use set_supported_presets() instead", "v1.20")
void set_supports_away(bool supports) {
if (supports) {
supported_presets_.insert(CLIMATE_PRESET_AWAY);
supported_presets_.insert(CLIMATE_PRESET_HOME);
}
}
ESPDEPRECATED("This method is deprecated, use supports_preset() instead")
ESPDEPRECATED("This method is deprecated, use supports_preset() instead", "v1.20")
bool get_supports_away() const { return supports_preset(CLIMATE_PRESET_AWAY); }
void set_supported_swing_modes(std::set<ClimateSwingMode> modes) { supported_swing_modes_ = std::move(modes); }
void add_supported_swing_mode(ClimateSwingMode mode) { supported_swing_modes_.insert(mode); }
ESPDEPRECATED("This method is deprecated, use set_supported_swing_modes() instead")
ESPDEPRECATED("This method is deprecated, use set_supported_swing_modes() instead", "v1.20")
void set_supports_swing_mode_off(bool supported) { set_swing_mode_support_(CLIMATE_SWING_OFF, supported); }
ESPDEPRECATED("This method is deprecated, use set_supported_swing_modes() instead")
ESPDEPRECATED("This method is deprecated, use set_supported_swing_modes() instead", "v1.20")
void set_supports_swing_mode_both(bool supported) { set_swing_mode_support_(CLIMATE_SWING_BOTH, supported); }
ESPDEPRECATED("This method is deprecated, use set_supported_swing_modes() instead")
ESPDEPRECATED("This method is deprecated, use set_supported_swing_modes() instead", "v1.20")
void set_supports_swing_mode_vertical(bool supported) { set_swing_mode_support_(CLIMATE_SWING_VERTICAL, supported); }
ESPDEPRECATED("This method is deprecated, use set_supported_swing_modes() instead")
ESPDEPRECATED("This method is deprecated, use set_supported_swing_modes() instead", "v1.20")
void set_supports_swing_mode_horizontal(bool supported) {
set_swing_mode_support_(CLIMATE_SWING_HORIZONTAL, supported);
}

View File

@ -0,0 +1,38 @@
#pragma once
#include "esphome/components/light/light_output.h"
#include "esphome/components/output/float_output.h"
#include "esphome/core/component.h"
namespace esphome {
namespace color_temperature {
class CTLightOutput : public light::LightOutput {
public:
void set_color_temperature(output::FloatOutput *color_temperature) { color_temperature_ = color_temperature; }
void set_brightness(output::FloatOutput *brightness) { brightness_ = brightness; }
void set_cold_white_temperature(float cold_white_temperature) { cold_white_temperature_ = cold_white_temperature; }
void set_warm_white_temperature(float warm_white_temperature) { warm_white_temperature_ = warm_white_temperature; }
light::LightTraits get_traits() override {
auto traits = light::LightTraits();
traits.set_supported_color_modes({light::ColorMode::COLOR_TEMPERATURE});
traits.set_min_mireds(this->cold_white_temperature_);
traits.set_max_mireds(this->warm_white_temperature_);
return traits;
}
void write_state(light::LightState *state) override {
float color_temperature, brightness;
state->current_values_as_ct(&color_temperature, &brightness);
this->color_temperature_->set_level(color_temperature);
this->brightness_->set_level(brightness);
}
protected:
output::FloatOutput *color_temperature_;
output::FloatOutput *brightness_;
float cold_white_temperature_;
float warm_white_temperature_;
};
} // namespace color_temperature
} // namespace esphome

View File

@ -0,0 +1,42 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import light, output
from esphome.const import (
CONF_BRIGHTNESS,
CONF_COLOR_TEMPERATURE,
CONF_OUTPUT_ID,
CONF_COLD_WHITE_COLOR_TEMPERATURE,
CONF_WARM_WHITE_COLOR_TEMPERATURE,
)
CODEOWNERS = ["@jesserockz"]
color_temperature_ns = cg.esphome_ns.namespace("color_temperature")
CTLightOutput = color_temperature_ns.class_("CTLightOutput", light.LightOutput)
CONFIG_SCHEMA = cv.All(
light.RGB_LIGHT_SCHEMA.extend(
{
cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(CTLightOutput),
cv.Required(CONF_COLOR_TEMPERATURE): cv.use_id(output.FloatOutput),
cv.Required(CONF_BRIGHTNESS): cv.use_id(output.FloatOutput),
cv.Required(CONF_COLD_WHITE_COLOR_TEMPERATURE): cv.color_temperature,
cv.Required(CONF_WARM_WHITE_COLOR_TEMPERATURE): cv.color_temperature,
}
),
light.validate_color_temperature_channels,
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_OUTPUT_ID])
await light.register_light(var, config)
color_temperature = await cg.get_variable(config[CONF_COLOR_TEMPERATURE])
cg.add(var.set_color_temperature(color_temperature))
brightness = await cg.get_variable(config[CONF_BRIGHTNESS])
cg.add(var.set_brightness(brightness))
cg.add(var.set_cold_white_temperature(config[CONF_COLD_WHITE_COLOR_TEMPERATURE]))
cg.add(var.set_warm_white_temperature(config[CONF_WARM_WHITE_COLOR_TEMPERATURE]))

View File

@ -4,6 +4,7 @@ from esphome import automation
from esphome.automation import maybe_simple_id, Condition
from esphome.components import mqtt
from esphome.const import (
CONF_DISABLED_BY_DEFAULT,
CONF_ID,
CONF_INTERNAL,
CONF_DEVICE_CLASS,
@ -63,7 +64,7 @@ CoverPublishAction = cover_ns.class_("CoverPublishAction", automation.Action)
CoverIsOpenCondition = cover_ns.class_("CoverIsOpenCondition", Condition)
CoverIsClosedCondition = cover_ns.class_("CoverIsClosedCondition", Condition)
COVER_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend(
COVER_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
{
cv.GenerateID(): cv.declare_id(Cover),
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTCoverComponent),
@ -75,6 +76,7 @@ COVER_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend(
async def setup_cover_core_(var, config):
cg.add(var.set_name(config[CONF_NAME]))
cg.add(var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT]))
if CONF_INTERNAL in config:
cg.add(var.set_internal(config[CONF_INTERNAL]))
if CONF_DEVICE_CLASS in config:

View File

@ -9,7 +9,6 @@ from esphome.const import (
UNIT_VOLT,
UNIT_AMPERE,
UNIT_WATT,
ICON_EMPTY,
DEVICE_CLASS_POWER,
DEVICE_CLASS_CURRENT,
DEVICE_CLASS_VOLTAGE,
@ -80,13 +79,19 @@ CONFIG_SCHEMA = cv.All(
cv.Optional(CONF_VOLTAGE_HPF, default=True): cv.boolean,
cv.Optional(CONF_PULSE_ENERGY, default=10.0): validate_energy,
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
UNIT_VOLT, ICON_EMPTY, 0, DEVICE_CLASS_VOLTAGE
unit_of_measurement=UNIT_VOLT,
accuracy_decimals=0,
device_class=DEVICE_CLASS_VOLTAGE,
),
cv.Optional(CONF_CURRENT): sensor.sensor_schema(
UNIT_AMPERE, ICON_EMPTY, 1, DEVICE_CLASS_CURRENT
unit_of_measurement=UNIT_AMPERE,
accuracy_decimals=1,
device_class=DEVICE_CLASS_CURRENT,
),
cv.Optional(CONF_POWER): sensor.sensor_schema(
UNIT_WATT, ICON_EMPTY, 0, DEVICE_CLASS_POWER
unit_of_measurement=UNIT_WATT,
accuracy_decimals=0,
device_class=DEVICE_CLASS_POWER,
),
}
)

View File

@ -9,7 +9,6 @@ from esphome.const import (
DEVICE_CLASS_CURRENT,
DEVICE_CLASS_POWER,
DEVICE_CLASS_VOLTAGE,
ICON_EMPTY,
STATE_CLASS_MEASUREMENT,
UNIT_VOLT,
UNIT_AMPERE,
@ -28,17 +27,22 @@ CONFIG_SCHEMA = (
{
cv.GenerateID(): cv.declare_id(CSE7766Component),
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): sensor.sensor_schema(
UNIT_AMPERE,
ICON_EMPTY,
2,
DEVICE_CLASS_CURRENT,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_AMPERE,
accuracy_decimals=2,
device_class=DEVICE_CLASS_CURRENT,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_POWER): 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,
),
}
)

View File

@ -5,7 +5,6 @@ from esphome.const import (
CONF_SENSOR,
CONF_ID,
DEVICE_CLASS_CURRENT,
ICON_EMPTY,
STATE_CLASS_MEASUREMENT,
UNIT_AMPERE,
)
@ -20,7 +19,10 @@ CTClampSensor = ct_clamp_ns.class_("CTClampSensor", sensor.Sensor, cg.PollingCom
CONFIG_SCHEMA = (
sensor.sensor_schema(
UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT, STATE_CLASS_MEASUREMENT
unit_of_measurement=UNIT_AMPERE,
accuracy_decimals=2,
device_class=DEVICE_CLASS_CURRENT,
state_class=STATE_CLASS_MEASUREMENT,
)
.extend(
{

View File

@ -16,10 +16,7 @@ class CWWWLightOutput : public light::LightOutput {
void set_constant_brightness(bool constant_brightness) { constant_brightness_ = constant_brightness; }
light::LightTraits get_traits() override {
auto traits = light::LightTraits();
traits.set_supports_brightness(true);
traits.set_supports_rgb(false);
traits.set_supports_rgb_white_value(false);
traits.set_supports_color_temperature(true);
traits.set_supported_color_modes({light::ColorMode::COLD_WARM_WHITE});
traits.set_min_mireds(this->cold_white_temperature_);
traits.set_max_mireds(this->warm_white_temperature_);
return traits;
@ -34,8 +31,8 @@ class CWWWLightOutput : public light::LightOutput {
protected:
output::FloatOutput *cold_white_;
output::FloatOutput *warm_white_;
float cold_white_temperature_;
float warm_white_temperature_;
float cold_white_temperature_{0};
float warm_white_temperature_{0};
bool constant_brightness_;
};

View File

@ -2,6 +2,7 @@ import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import light, output
from esphome.const import (
CONF_CONSTANT_BRIGHTNESS,
CONF_OUTPUT_ID,
CONF_COLD_WHITE,
CONF_WARM_WHITE,
@ -12,19 +13,20 @@ from esphome.const import (
cwww_ns = cg.esphome_ns.namespace("cwww")
CWWWLightOutput = cwww_ns.class_("CWWWLightOutput", light.LightOutput)
CONF_CONSTANT_BRIGHTNESS = "constant_brightness"
CONFIG_SCHEMA = cv.All(
light.RGB_LIGHT_SCHEMA.extend(
{
cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(CWWWLightOutput),
cv.Required(CONF_COLD_WHITE): cv.use_id(output.FloatOutput),
cv.Required(CONF_WARM_WHITE): cv.use_id(output.FloatOutput),
cv.Required(CONF_COLD_WHITE_COLOR_TEMPERATURE): cv.color_temperature,
cv.Required(CONF_WARM_WHITE_COLOR_TEMPERATURE): cv.color_temperature,
cv.Optional(CONF_COLD_WHITE_COLOR_TEMPERATURE): cv.color_temperature,
cv.Optional(CONF_WARM_WHITE_COLOR_TEMPERATURE): cv.color_temperature,
cv.Optional(CONF_CONSTANT_BRIGHTNESS, default=False): cv.boolean,
}
),
cv.has_none_or_all_keys(
[CONF_COLD_WHITE_COLOR_TEMPERATURE, CONF_WARM_WHITE_COLOR_TEMPERATURE]
),
light.validate_color_temperature_channels,
)
@ -32,11 +34,19 @@ CONFIG_SCHEMA = cv.All(
async def to_code(config):
var = cg.new_Pvariable(config[CONF_OUTPUT_ID])
await light.register_light(var, config)
cwhite = await cg.get_variable(config[CONF_COLD_WHITE])
cg.add(var.set_cold_white(cwhite))
cg.add(var.set_cold_white_temperature(config[CONF_COLD_WHITE_COLOR_TEMPERATURE]))
if CONF_COLD_WHITE_COLOR_TEMPERATURE in config:
cg.add(
var.set_cold_white_temperature(config[CONF_COLD_WHITE_COLOR_TEMPERATURE])
)
wwhite = await cg.get_variable(config[CONF_WARM_WHITE])
cg.add(var.set_warm_white(wwhite))
cg.add(var.set_warm_white_temperature(config[CONF_WARM_WHITE_COLOR_TEMPERATURE]))
if CONF_WARM_WHITE_COLOR_TEMPERATURE in config:
cg.add(
var.set_warm_white_temperature(config[CONF_WARM_WHITE_COLOR_TEMPERATURE])
)
cg.add(var.set_constant_brightness(config[CONF_CONSTANT_BRIGHTNESS]))

View File

@ -137,7 +137,7 @@ void DallasComponent::update() {
}
if (!res) {
ESP_LOGW(TAG, "'%s' - Reseting bus for read failed!", sensor->get_name().c_str());
ESP_LOGW(TAG, "'%s' - Resetting bus for read failed!", sensor->get_name().c_str());
sensor->publish_state(NAN);
this->status_set_warning();
return;

View File

@ -7,7 +7,6 @@ from esphome.const import (
CONF_INDEX,
CONF_RESOLUTION,
DEVICE_CLASS_TEMPERATURE,
ICON_EMPTY,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
CONF_ID,
@ -18,7 +17,10 @@ DallasTemperatureSensor = dallas_ns.class_("DallasTemperatureSensor", sensor.Sen
CONFIG_SCHEMA = cv.All(
sensor.sensor_schema(
UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE, STATE_CLASS_MEASUREMENT
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
).extend(
{
cv.GenerateID(): cv.declare_id(DallasTemperatureSensor),

View File

@ -0,0 +1,451 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import (
binary_sensor,
climate,
cover,
fan,
light,
number,
sensor,
switch,
text_sensor,
)
from esphome.const import (
CONF_ACCURACY_DECIMALS,
CONF_BINARY_SENSORS,
CONF_DEVICE_CLASS,
CONF_FORCE_UPDATE,
CONF_ICON,
CONF_ID,
CONF_INVERTED,
CONF_LAST_RESET_TYPE,
CONF_MAX_VALUE,
CONF_MIN_VALUE,
CONF_NAME,
CONF_OUTPUT_ID,
CONF_SENSORS,
CONF_STATE_CLASS,
CONF_STEP,
CONF_SWITCHES,
CONF_TEXT_SENSORS,
CONF_TYPE,
CONF_UNIT_OF_MEASUREMENT,
DEVICE_CLASS_ENERGY,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_MOISTURE,
DEVICE_CLASS_MOTION,
DEVICE_CLASS_TEMPERATURE,
ICON_BLUETOOTH,
ICON_BLUR,
ICON_EMPTY,
ICON_THERMOMETER,
LAST_RESET_TYPE_AUTO,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
UNIT_EMPTY,
UNIT_PERCENT,
UNIT_WATT_HOURS,
)
AUTO_LOAD = [
"binary_sensor",
"climate",
"cover",
"fan",
"light",
"number",
"sensor",
"switch",
"text_sensor",
]
demo_ns = cg.esphome_ns.namespace("demo")
DemoBinarySensor = demo_ns.class_(
"DemoBinarySensor", binary_sensor.BinarySensor, cg.PollingComponent
)
DemoClimate = demo_ns.class_("DemoClimate", climate.Climate, cg.Component)
DemoClimateType = demo_ns.enum("DemoClimateType", is_class=True)
DemoCover = demo_ns.class_("DemoCover", cover.Cover, cg.Component)
DemoCoverType = demo_ns.enum("DemoCoverType", is_class=True)
DemoFan = demo_ns.class_("DemoFan", cg.Component)
DemoFanType = demo_ns.enum("DemoFanType", is_class=True)
DemoLight = demo_ns.class_("DemoLight", light.LightOutput, cg.Component)
DemoLightType = demo_ns.enum("DemoLightType", is_class=True)
DemoNumber = demo_ns.class_("DemoNumber", number.Number, cg.Component)
DemoNumberType = demo_ns.enum("DemoNumberType", is_class=True)
DemoSensor = demo_ns.class_("DemoSensor", sensor.Sensor, cg.PollingComponent)
DemoSwitch = demo_ns.class_("DemoSwitch", switch.Switch, cg.Component)
DemoTextSensor = demo_ns.class_(
"DemoTextSensor", text_sensor.TextSensor, cg.PollingComponent
)
CLIMATE_TYPES = {
1: DemoClimateType.TYPE_1,
2: DemoClimateType.TYPE_2,
3: DemoClimateType.TYPE_3,
}
COVER_TYPES = {
1: DemoCoverType.TYPE_1,
2: DemoCoverType.TYPE_2,
3: DemoCoverType.TYPE_3,
4: DemoCoverType.TYPE_4,
}
FAN_TYPES = {
1: DemoFanType.TYPE_1,
2: DemoFanType.TYPE_2,
3: DemoFanType.TYPE_3,
4: DemoFanType.TYPE_4,
}
LIGHT_TYPES = {
1: DemoLightType.TYPE_1,
2: DemoLightType.TYPE_2,
3: DemoLightType.TYPE_3,
4: DemoLightType.TYPE_4,
5: DemoLightType.TYPE_5,
6: DemoLightType.TYPE_6,
7: DemoLightType.TYPE_7,
}
NUMBER_TYPES = {
1: DemoNumberType.TYPE_1,
2: DemoNumberType.TYPE_2,
3: DemoNumberType.TYPE_3,
}
CONF_CLIMATES = "climates"
CONF_COVERS = "covers"
CONF_FANS = "fans"
CONF_LIGHTS = "lights"
CONF_NUMBERS = "numbers"
CONFIG_SCHEMA = cv.Schema(
{
cv.Optional(
CONF_BINARY_SENSORS,
default=[
{
CONF_NAME: "Demo Basement Floor Wet",
CONF_DEVICE_CLASS: DEVICE_CLASS_MOISTURE,
},
{
CONF_NAME: "Demo Movement Backyard",
CONF_DEVICE_CLASS: DEVICE_CLASS_MOTION,
},
],
): [
binary_sensor.BINARY_SENSOR_SCHEMA.extend(
cv.polling_component_schema("60s")
).extend(
{
cv.GenerateID(): cv.declare_id(DemoBinarySensor),
}
)
],
cv.Optional(
CONF_CLIMATES,
default=[
{
CONF_NAME: "Demo Heatpump",
CONF_TYPE: 1,
},
{
CONF_NAME: "Demo HVAC",
CONF_TYPE: 2,
},
{
CONF_NAME: "Demo Ecobee",
CONF_TYPE: 3,
},
],
): [
climate.CLIMATE_SCHEMA.extend(cv.COMPONENT_SCHEMA).extend(
{
cv.GenerateID(): cv.declare_id(DemoClimate),
cv.Required(CONF_TYPE): cv.enum(CLIMATE_TYPES, int=True),
}
)
],
cv.Optional(
CONF_COVERS,
default=[
{
CONF_NAME: "Demo Kitchen Window",
CONF_TYPE: 1,
},
{
CONF_NAME: "Demo Garage Door",
CONF_TYPE: 2,
CONF_DEVICE_CLASS: "garage",
},
{
CONF_NAME: "Demo Living Room Window",
CONF_TYPE: 3,
},
{
CONF_NAME: "Demo Hall Window",
CONF_TYPE: 4,
CONF_DEVICE_CLASS: "window",
},
],
): [
cover.COVER_SCHEMA.extend(cv.COMPONENT_SCHEMA).extend(
{
cv.GenerateID(): cv.declare_id(DemoCover),
cv.Required(CONF_TYPE): cv.enum(COVER_TYPES, int=True),
}
)
],
cv.Optional(
CONF_FANS,
default=[
{
CONF_NAME: "Demo Living Room Fan",
CONF_TYPE: 1,
},
{
CONF_NAME: "Demo Ceiling Fan",
CONF_TYPE: 2,
},
{
CONF_NAME: "Demo Percentage Limited Fan",
CONF_TYPE: 3,
},
{
CONF_NAME: "Demo Percentage Full Fan",
CONF_TYPE: 4,
},
],
): [
fan.FAN_SCHEMA.extend(cv.COMPONENT_SCHEMA).extend(
{
cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(DemoFan),
cv.Required(CONF_TYPE): cv.enum(FAN_TYPES, int=True),
}
)
],
cv.Optional(
CONF_LIGHTS,
default=[
{
CONF_NAME: "Demo Binary Light",
CONF_TYPE: 1,
},
{
CONF_NAME: "Demo Brightness Light",
CONF_TYPE: 2,
},
{
CONF_NAME: "Demo RGB Light",
CONF_TYPE: 3,
},
{
CONF_NAME: "Demo RGBW Light",
CONF_TYPE: 4,
},
{
CONF_NAME: "Demo RGBWW Light",
CONF_TYPE: 5,
},
{
CONF_NAME: "Demo CWWW Light",
CONF_TYPE: 6,
},
{
CONF_NAME: "Demo RGBW interlock Light",
CONF_TYPE: 7,
},
],
): [
light.RGB_LIGHT_SCHEMA.extend(cv.COMPONENT_SCHEMA).extend(
{
cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(DemoLight),
cv.Required(CONF_TYPE): cv.enum(LIGHT_TYPES, int=True),
}
)
],
cv.Optional(
CONF_NUMBERS,
default=[
{
CONF_NAME: "Demo Number 0-100",
CONF_TYPE: 1,
CONF_MIN_VALUE: 0,
CONF_MAX_VALUE: 100,
CONF_STEP: 1,
},
{
CONF_NAME: "Demo Number -50-50",
CONF_TYPE: 2,
CONF_MIN_VALUE: -50,
CONF_MAX_VALUE: 50,
CONF_STEP: 5,
},
{
CONF_NAME: "Demo Number 40-60",
CONF_TYPE: 3,
CONF_MIN_VALUE: 40,
CONF_MAX_VALUE: 60,
CONF_STEP: 0.2,
},
],
): [
number.NUMBER_SCHEMA.extend(cv.COMPONENT_SCHEMA).extend(
{
cv.GenerateID(): cv.declare_id(DemoNumber),
cv.Required(CONF_TYPE): cv.enum(NUMBER_TYPES, int=True),
cv.Required(CONF_MIN_VALUE): cv.float_,
cv.Required(CONF_MAX_VALUE): cv.float_,
cv.Required(CONF_STEP): cv.float_,
}
)
],
cv.Optional(
CONF_SENSORS,
default=[
{
CONF_NAME: "Demo Plain Sensor",
},
{
CONF_NAME: "Demo Temperature Sensor",
CONF_UNIT_OF_MEASUREMENT: UNIT_CELSIUS,
CONF_ICON: ICON_THERMOMETER,
CONF_ACCURACY_DECIMALS: 1,
CONF_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
CONF_STATE_CLASS: STATE_CLASS_MEASUREMENT,
},
{
CONF_NAME: "Demo Temperature Sensor",
CONF_UNIT_OF_MEASUREMENT: UNIT_CELSIUS,
CONF_ICON: ICON_THERMOMETER,
CONF_ACCURACY_DECIMALS: 1,
CONF_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
CONF_STATE_CLASS: STATE_CLASS_MEASUREMENT,
},
{
CONF_NAME: "Demo Force Update Sensor",
CONF_UNIT_OF_MEASUREMENT: UNIT_PERCENT,
CONF_ACCURACY_DECIMALS: 0,
CONF_DEVICE_CLASS: DEVICE_CLASS_HUMIDITY,
CONF_STATE_CLASS: STATE_CLASS_MEASUREMENT,
CONF_FORCE_UPDATE: True,
},
{
CONF_NAME: "Demo Energy Sensor",
CONF_UNIT_OF_MEASUREMENT: UNIT_WATT_HOURS,
CONF_ACCURACY_DECIMALS: 0,
CONF_DEVICE_CLASS: DEVICE_CLASS_ENERGY,
CONF_STATE_CLASS: STATE_CLASS_MEASUREMENT,
CONF_LAST_RESET_TYPE: LAST_RESET_TYPE_AUTO,
},
],
): [
sensor.sensor_schema(UNIT_EMPTY, ICON_EMPTY, 0)
.extend(cv.polling_component_schema("60s"))
.extend(
{
cv.GenerateID(): cv.declare_id(DemoSensor),
}
)
],
cv.Optional(
CONF_SWITCHES,
default=[
{
CONF_NAME: "Demo Switch 1",
},
{
CONF_NAME: "Demo Switch 2",
CONF_INVERTED: True,
CONF_ICON: ICON_BLUETOOTH,
},
],
): [
switch.SWITCH_SCHEMA.extend(cv.COMPONENT_SCHEMA).extend(
{
cv.GenerateID(): cv.declare_id(DemoSwitch),
}
)
],
cv.Optional(
CONF_TEXT_SENSORS,
default=[
{
CONF_NAME: "Demo Text Sensor 1",
},
{
CONF_NAME: "Demo Text Sensor 2",
CONF_ICON: ICON_BLUR,
},
],
): [
text_sensor.TEXT_SENSOR_SCHEMA.extend(
cv.polling_component_schema("60s")
).extend(
{
cv.GenerateID(): cv.declare_id(DemoTextSensor),
}
)
],
}
)
async def to_code(config):
for conf in config[CONF_BINARY_SENSORS]:
var = cg.new_Pvariable(conf[CONF_ID])
await cg.register_component(var, conf)
await binary_sensor.register_binary_sensor(var, conf)
for conf in config[CONF_CLIMATES]:
var = cg.new_Pvariable(conf[CONF_ID])
await cg.register_component(var, conf)
await climate.register_climate(var, conf)
cg.add(var.set_type(conf[CONF_TYPE]))
for conf in config[CONF_COVERS]:
var = cg.new_Pvariable(conf[CONF_ID])
await cg.register_component(var, conf)
await cover.register_cover(var, conf)
cg.add(var.set_type(conf[CONF_TYPE]))
for conf in config[CONF_FANS]:
var = cg.new_Pvariable(conf[CONF_OUTPUT_ID])
await cg.register_component(var, conf)
fan_ = await fan.create_fan_state(conf)
cg.add(var.set_fan(fan_))
cg.add(var.set_type(conf[CONF_TYPE]))
for conf in config[CONF_LIGHTS]:
var = cg.new_Pvariable(conf[CONF_OUTPUT_ID])
await cg.register_component(var, conf)
await light.register_light(var, conf)
cg.add(var.set_type(conf[CONF_TYPE]))
for conf in config[CONF_NUMBERS]:
var = cg.new_Pvariable(conf[CONF_ID])
await cg.register_component(var, conf)
await number.register_number(
var,
conf,
min_value=conf[CONF_MIN_VALUE],
max_value=conf[CONF_MAX_VALUE],
step=conf[CONF_STEP],
)
cg.add(var.set_type(conf[CONF_TYPE]))
for conf in config[CONF_SENSORS]:
var = cg.new_Pvariable(conf[CONF_ID])
await cg.register_component(var, conf)
await sensor.register_sensor(var, conf)
for conf in config[CONF_SWITCHES]:
var = cg.new_Pvariable(conf[CONF_ID])
await cg.register_component(var, conf)
await switch.register_switch(var, conf)
for conf in config[CONF_TEXT_SENSORS]:
var = cg.new_Pvariable(conf[CONF_ID])
await cg.register_component(var, conf)
await text_sensor.register_text_sensor(var, conf)

View File

@ -0,0 +1,22 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/binary_sensor/binary_sensor.h"
namespace esphome {
namespace demo {
class DemoBinarySensor : public binary_sensor::BinarySensor, public PollingComponent {
public:
void setup() override { this->publish_initial_state(false); }
void update() override {
bool new_state = last_state_ = !last_state_;
this->publish_state(new_state);
}
protected:
bool last_state_ = false;
};
} // namespace demo
} // namespace esphome

View File

@ -0,0 +1,157 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/climate/climate.h"
namespace esphome {
namespace demo {
enum class DemoClimateType {
TYPE_1,
TYPE_2,
TYPE_3,
};
class DemoClimate : public climate::Climate, public Component {
public:
void set_type(DemoClimateType type) { type_ = type; }
void setup() override {
switch (type_) {
case DemoClimateType::TYPE_1:
this->current_temperature = 20.0;
this->target_temperature = 21.0;
this->mode = climate::CLIMATE_MODE_HEAT;
this->action = climate::CLIMATE_ACTION_HEATING;
break;
case DemoClimateType::TYPE_2:
this->target_temperature = 21.5;
this->mode = climate::CLIMATE_MODE_AUTO;
this->action = climate::CLIMATE_ACTION_COOLING;
this->fan_mode = climate::CLIMATE_FAN_HIGH;
this->custom_preset = {"My Preset"};
break;
case DemoClimateType::TYPE_3:
this->current_temperature = 21.5;
this->target_temperature_low = 21.0;
this->target_temperature_high = 22.5;
this->mode = climate::CLIMATE_MODE_HEAT_COOL;
this->custom_fan_mode = {"Auto Low"};
this->swing_mode = climate::CLIMATE_SWING_HORIZONTAL;
this->preset = climate::CLIMATE_PRESET_AWAY;
break;
}
this->publish_state();
}
protected:
void control(const climate::ClimateCall &call) override {
if (call.get_mode().has_value()) {
this->mode = *call.get_mode();
}
if (call.get_target_temperature().has_value()) {
this->target_temperature = *call.get_target_temperature();
}
if (call.get_target_temperature_low().has_value()) {
this->target_temperature_low = *call.get_target_temperature_low();
}
if (call.get_target_temperature_high().has_value()) {
this->target_temperature_high = *call.get_target_temperature_high();
}
if (call.get_fan_mode().has_value()) {
this->fan_mode = *call.get_fan_mode();
this->custom_fan_mode.reset();
}
if (call.get_swing_mode().has_value()) {
this->swing_mode = *call.get_swing_mode();
}
if (call.get_custom_fan_mode().has_value()) {
this->custom_fan_mode = *call.get_custom_fan_mode();
this->fan_mode.reset();
}
if (call.get_preset().has_value()) {
this->preset = *call.get_preset();
this->custom_preset.reset();
}
if (call.get_custom_preset().has_value()) {
this->custom_preset = *call.get_custom_preset();
this->preset.reset();
}
this->publish_state();
}
climate::ClimateTraits traits() override {
climate::ClimateTraits traits{};
switch (type_) {
case DemoClimateType::TYPE_1:
traits.set_supports_current_temperature(true);
traits.set_supported_modes({
climate::CLIMATE_MODE_OFF,
climate::CLIMATE_MODE_HEAT,
});
traits.set_supports_action(true);
traits.set_visual_temperature_step(0.5);
break;
case DemoClimateType::TYPE_2:
traits.set_supports_current_temperature(false);
traits.set_supported_modes({
climate::CLIMATE_MODE_OFF,
climate::CLIMATE_MODE_HEAT,
climate::CLIMATE_MODE_COOL,
climate::CLIMATE_MODE_AUTO,
climate::CLIMATE_MODE_DRY,
climate::CLIMATE_MODE_FAN_ONLY,
});
traits.set_supports_action(true);
traits.set_supported_fan_modes({
climate::CLIMATE_FAN_ON,
climate::CLIMATE_FAN_OFF,
climate::CLIMATE_FAN_AUTO,
climate::CLIMATE_FAN_LOW,
climate::CLIMATE_FAN_MEDIUM,
climate::CLIMATE_FAN_HIGH,
climate::CLIMATE_FAN_MIDDLE,
climate::CLIMATE_FAN_FOCUS,
climate::CLIMATE_FAN_DIFFUSE,
});
traits.set_supported_custom_fan_modes({"Auto Low", "Auto High"});
traits.set_supported_swing_modes({
climate::CLIMATE_SWING_OFF,
climate::CLIMATE_SWING_BOTH,
climate::CLIMATE_SWING_VERTICAL,
climate::CLIMATE_SWING_HORIZONTAL,
});
traits.set_supported_custom_presets({"My Preset"});
break;
case DemoClimateType::TYPE_3:
traits.set_supports_current_temperature(true);
traits.set_supports_two_point_target_temperature(true);
traits.set_supported_modes({
climate::CLIMATE_MODE_OFF,
climate::CLIMATE_MODE_COOL,
climate::CLIMATE_MODE_HEAT,
climate::CLIMATE_MODE_HEAT_COOL,
});
traits.set_supported_custom_fan_modes({"Auto Low", "Auto High"});
traits.set_supported_swing_modes({
climate::CLIMATE_SWING_OFF,
climate::CLIMATE_SWING_HORIZONTAL,
});
traits.set_supported_presets({
climate::CLIMATE_PRESET_NONE,
climate::CLIMATE_PRESET_HOME,
climate::CLIMATE_PRESET_AWAY,
climate::CLIMATE_PRESET_BOOST,
climate::CLIMATE_PRESET_COMFORT,
climate::CLIMATE_PRESET_ECO,
climate::CLIMATE_PRESET_SLEEP,
climate::CLIMATE_PRESET_ACTIVITY,
});
break;
}
return traits;
}
DemoClimateType type_;
};
} // namespace demo
} // namespace esphome

View File

@ -0,0 +1,86 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/cover/cover.h"
namespace esphome {
namespace demo {
enum class DemoCoverType {
TYPE_1,
TYPE_2,
TYPE_3,
TYPE_4,
};
class DemoCover : public cover::Cover, public Component {
public:
void set_type(DemoCoverType type) { type_ = type; }
void setup() override {
switch (type_) {
case DemoCoverType::TYPE_1:
this->position = cover::COVER_OPEN;
break;
case DemoCoverType::TYPE_2:
this->position = 0.7;
break;
case DemoCoverType::TYPE_3:
this->position = 0.1;
this->tilt = 0.8;
break;
case DemoCoverType::TYPE_4:
this->position = cover::COVER_CLOSED;
this->tilt = 1.0;
break;
}
this->publish_state();
}
protected:
void control(const cover::CoverCall &call) override {
if (call.get_position().has_value()) {
float target = *call.get_position();
this->current_operation =
target > this->position ? cover::COVER_OPERATION_OPENING : cover::COVER_OPERATION_CLOSING;
this->set_timeout("move", 2000, [this, target]() {
this->current_operation = cover::COVER_OPERATION_IDLE;
this->position = target;
this->publish_state();
});
}
if (call.get_tilt().has_value()) {
this->tilt = *call.get_tilt();
}
if (call.get_stop()) {
this->cancel_timeout("move");
}
this->publish_state();
}
cover::CoverTraits get_traits() override {
cover::CoverTraits traits{};
switch (type_) {
case DemoCoverType::TYPE_1:
traits.set_is_assumed_state(true);
break;
case DemoCoverType::TYPE_2:
traits.set_supports_position(true);
break;
case DemoCoverType::TYPE_3:
traits.set_supports_position(true);
traits.set_supports_tilt(true);
break;
case DemoCoverType::TYPE_4:
traits.set_is_assumed_state(true);
traits.set_supports_tilt(true);
break;
}
return traits;
}
DemoCoverType type_;
};
} // namespace demo
} // namespace esphome

View File

@ -0,0 +1,54 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/fan/fan_state.h"
namespace esphome {
namespace demo {
enum class DemoFanType {
TYPE_1,
TYPE_2,
TYPE_3,
TYPE_4,
};
class DemoFan : public Component {
public:
void set_type(DemoFanType type) { type_ = type; }
void set_fan(fan::FanState *fan) { fan_ = fan; }
void setup() override {
fan::FanTraits traits{};
// oscillation
// speed
// direction
// speed_count
switch (type_) {
case DemoFanType::TYPE_1:
break;
case DemoFanType::TYPE_2:
traits.set_oscillation(true);
break;
case DemoFanType::TYPE_3:
traits.set_direction(true);
traits.set_speed(true);
traits.set_supported_speed_count(5);
break;
case DemoFanType::TYPE_4:
traits.set_direction(true);
traits.set_speed(true);
traits.set_supported_speed_count(100);
traits.set_oscillation(true);
break;
}
this->fan_->set_traits(traits);
}
fan::FanState *fan_;
DemoFanType type_;
};
} // namespace demo
} // namespace esphome

View File

@ -0,0 +1,68 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/light/light_output.h"
namespace esphome {
namespace demo {
enum class DemoLightType {
// binary
TYPE_1,
// brightness
TYPE_2,
// RGB
TYPE_3,
// RGBW
TYPE_4,
// RGBWW
TYPE_5,
// CWWW
TYPE_6,
// RGBW + color_interlock
TYPE_7,
};
class DemoLight : public light::LightOutput, public Component {
public:
void set_type(DemoLightType type) { type_ = type; }
light::LightTraits get_traits() override {
light::LightTraits traits{};
switch (type_) {
case DemoLightType::TYPE_1:
traits.set_supported_color_modes({light::ColorMode::ON_OFF});
break;
case DemoLightType::TYPE_2:
traits.set_supported_color_modes({light::ColorMode::BRIGHTNESS});
break;
case DemoLightType::TYPE_3:
traits.set_supported_color_modes({light::ColorMode::RGB});
break;
case DemoLightType::TYPE_4:
traits.set_supported_color_modes({light::ColorMode::RGB_WHITE});
break;
case DemoLightType::TYPE_5:
traits.set_supported_color_modes({light::ColorMode::RGB_COLOR_TEMPERATURE});
traits.set_min_mireds(153);
traits.set_max_mireds(500);
break;
case DemoLightType::TYPE_6:
traits.set_supported_color_modes({light::ColorMode::COLD_WARM_WHITE});
traits.set_min_mireds(153);
traits.set_max_mireds(500);
break;
case DemoLightType::TYPE_7:
traits.set_supported_color_modes({light::ColorMode::RGB, light::ColorMode::WHITE});
break;
}
return traits;
}
void write_state(light::LightState *state) override {
// do nothing
}
DemoLightType type_;
};
} // namespace demo
} // namespace esphome

View File

@ -0,0 +1,39 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/number/number.h"
namespace esphome {
namespace demo {
enum class DemoNumberType {
TYPE_1,
TYPE_2,
TYPE_3,
};
class DemoNumber : public number::Number, public Component {
public:
void set_type(DemoNumberType type) { type_ = type; }
void setup() override {
switch (type_) {
case DemoNumberType::TYPE_1:
this->publish_state(50);
break;
case DemoNumberType::TYPE_2:
this->publish_state(-10);
break;
case DemoNumberType::TYPE_3:
this->publish_state(42);
break;
}
}
protected:
void control(float value) override { this->publish_state(value); }
DemoNumberType type_;
};
} // namespace demo
} // namespace esphome

View File

@ -0,0 +1,28 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/helpers.h"
#include "esphome/components/sensor/sensor.h"
namespace esphome {
namespace demo {
class DemoSensor : public sensor::Sensor, public PollingComponent {
public:
void update() override {
float val = random_float();
bool is_auto = this->last_reset_type == sensor::LAST_RESET_TYPE_AUTO;
if (is_auto) {
float base = isnan(this->state) ? 0.0f : this->state;
this->publish_state(base + val * 10);
} else {
if (val < 0.1)
this->publish_state(NAN);
else
this->publish_state(val * 100);
}
}
};
} // namespace demo
} // namespace esphome

View File

@ -0,0 +1,22 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/helpers.h"
#include "esphome/components/switch/switch.h"
namespace esphome {
namespace demo {
class DemoSwitch : public switch_::Switch, public Component {
public:
void setup() override {
bool initial = random_float() < 0.5;
this->publish_state(initial);
}
protected:
void write_state(bool state) override { this->publish_state(state); }
};
} // namespace demo
} // namespace esphome

View File

@ -0,0 +1,25 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/helpers.h"
#include "esphome/components/text_sensor/text_sensor.h"
namespace esphome {
namespace demo {
class DemoTextSensor : public text_sensor::TextSensor, public PollingComponent {
public:
void update() override {
float val = random_float();
if (val < 0.33) {
this->publish_state("foo");
} else if (val < 0.66) {
this->publish_state("bar");
} else {
this->publish_state("foobar");
}
}
};
} // namespace demo
} // namespace esphome

View File

@ -116,7 +116,7 @@ DFPLAYER_SIMPLE_ACTION(PreviousAction, previous)
template<typename... Ts> class PlayFileAction : public Action<Ts...>, public Parented<DFPlayer> {
public:
TEMPLATABLE_VALUE(uint16_t, file)
TEMPLATABLE_VALUE(boolean, loop)
TEMPLATABLE_VALUE(bool, loop)
void play(Ts... x) override {
auto file = this->file_.value(x...);
@ -133,7 +133,7 @@ template<typename... Ts> class PlayFolderAction : public Action<Ts...>, public P
public:
TEMPLATABLE_VALUE(uint16_t, folder)
TEMPLATABLE_VALUE(uint16_t, file)
TEMPLATABLE_VALUE(boolean, loop)
TEMPLATABLE_VALUE(bool, loop)
void play(Ts... x) override {
auto folder = this->folder_.value(x...);

View File

@ -203,7 +203,7 @@ bool HOT ICACHE_RAM_ATTR DHT::read_sensor_(float *temperature, float *humidity,
const uint16_t raw_humidity = uint16_t(data[0]) * 10 + data[1];
*humidity = raw_humidity / 10.0f;
} else {
// For compatibily with DHT11 models which might only use 2 bytes checksums, only use the data from these two
// For compatibility with DHT11 models which might only use 2 bytes checksums, only use the data from these two
// bytes
*temperature = data[2];
*humidity = data[0];

View File

@ -8,7 +8,6 @@ from esphome.const import (
CONF_MODEL,
CONF_PIN,
CONF_TEMPERATURE,
ICON_EMPTY,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
UNIT_PERCENT,
@ -36,14 +35,16 @@ CONFIG_SCHEMA = cv.Schema(
cv.GenerateID(): cv.declare_id(DHT),
cv.Required(CONF_PIN): pins.gpio_input_pin_schema,
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS,
ICON_EMPTY,
1,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
UNIT_PERCENT, ICON_EMPTY, 0, DEVICE_CLASS_HUMIDITY, STATE_CLASS_MEASUREMENT
unit_of_measurement=UNIT_PERCENT,
accuracy_decimals=0,
device_class=DEVICE_CLASS_HUMIDITY,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_MODEL, default="auto detect"): cv.enum(
DHT_MODELS, upper=True, space="_"

View File

@ -7,7 +7,6 @@ from esphome.const import (
CONF_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
ICON_EMPTY,
UNIT_PERCENT,
DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_HUMIDITY,
@ -23,18 +22,16 @@ CONFIG_SCHEMA = (
{
cv.GenerateID(): cv.declare_id(DHT12Component),
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS,
ICON_EMPTY,
1,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
UNIT_PERCENT,
ICON_EMPTY,
1,
DEVICE_CLASS_HUMIDITY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_PERCENT,
accuracy_decimals=1,
device_class=DEVICE_CLASS_HUMIDITY,
state_class=STATE_CLASS_MEASUREMENT,
),
}
)

View File

@ -14,7 +14,7 @@ const Color COLOR_OFF(0, 0, 0, 0);
const Color COLOR_ON(255, 255, 255, 255);
void DisplayBuffer::init_internal_(uint32_t buffer_length) {
this->buffer_ = new uint8_t[buffer_length];
this->buffer_ = new (std::nothrow) uint8_t[buffer_length];
if (this->buffer_ == nullptr) {
ESP_LOGE(TAG, "Could not allocate buffer for display!");
return;
@ -461,7 +461,7 @@ bool Image::get_pixel(int x, int y) const {
}
Color Image::get_color_pixel(int x, int y) const {
if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_)
return 0;
return Color::BLACK;
const uint32_t pos = (x + y * this->width_) * 3;
const uint32_t color32 = (pgm_read_byte(this->data_start_ + pos + 2) << 0) |
(pgm_read_byte(this->data_start_ + pos + 1) << 8) |
@ -470,7 +470,7 @@ Color Image::get_color_pixel(int x, int y) const {
}
Color Image::get_grayscale_pixel(int x, int y) const {
if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_)
return 0;
return Color::BLACK;
const uint32_t pos = (x + y * this->width_);
const uint8_t gray = pgm_read_byte(this->data_start_ + pos);
return Color(gray | gray << 8 | gray << 16 | gray << 24);
@ -493,10 +493,10 @@ bool Animation::get_pixel(int x, int y) const {
}
Color Animation::get_color_pixel(int x, int y) const {
if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_)
return 0;
return Color::BLACK;
const uint32_t frame_index = this->width_ * this->height_ * this->current_frame_;
if (frame_index >= this->width_ * this->height_ * this->animation_frame_count_)
return 0;
return Color::BLACK;
const uint32_t pos = (x + y * this->width_ + frame_index) * 3;
const uint32_t color32 = (pgm_read_byte(this->data_start_ + pos + 2) << 0) |
(pgm_read_byte(this->data_start_ + pos + 1) << 8) |
@ -505,10 +505,10 @@ Color Animation::get_color_pixel(int x, int y) const {
}
Color Animation::get_grayscale_pixel(int x, int y) const {
if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_)
return 0;
return Color::BLACK;
const uint32_t frame_index = this->width_ * this->height_ * this->current_frame_;
if (frame_index >= this->width_ * this->height_ * this->animation_frame_count_)
return 0;
return Color::BLACK;
const uint32_t pos = (x + y * this->width_ + frame_index);
const uint8_t gray = pgm_read_byte(this->data_start_ + pos);
return Color(gray | gray << 8 | gray << 16 | gray << 24);

View File

@ -0,0 +1,59 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import uart
from esphome.const import (
CONF_ID,
CONF_UART_ID,
)
CODEOWNERS = ["@glmnet", "@zuidwijk"]
DEPENDENCIES = ["uart"]
AUTO_LOAD = ["sensor", "text_sensor"]
CONF_DSMR_ID = "dsmr_id"
CONF_DECRYPTION_KEY = "decryption_key"
# Hack to prevent compile error due to ambiguity with lib namespace
dsmr_ns = cg.esphome_ns.namespace("esphome::dsmr")
Dsmr = dsmr_ns.class_("Dsmr", cg.Component, uart.UARTDevice)
def _validate_key(value):
value = cv.string_strict(value)
parts = [value[i : i + 2] for i in range(0, len(value), 2)]
if len(parts) != 16:
raise cv.Invalid("Decryption key must consist of 16 hexadecimal numbers")
parts_int = []
if any(len(part) != 2 for part in parts):
raise cv.Invalid("Decryption key must be format XX")
for part in parts:
try:
parts_int.append(int(part, 16))
except ValueError:
# pylint: disable=raise-missing-from
raise cv.Invalid("Decryption key must be hex values from 00 to FF")
return "".join(f"{part:02X}" for part in parts_int)
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(Dsmr),
cv.Optional(CONF_DECRYPTION_KEY): _validate_key,
}
).extend(uart.UART_DEVICE_SCHEMA)
async def to_code(config):
uart_component = await cg.get_variable(config[CONF_UART_ID])
var = cg.new_Pvariable(config[CONF_ID], uart_component)
if CONF_DECRYPTION_KEY in config:
cg.add(var.set_decryption_key(config[CONF_DECRYPTION_KEY]))
await cg.register_component(var, config)
# DSMR Parser
cg.add_library("glmnet/Dsmr", "0.3")
# Crypto
cg.add_library("rweather/Crypto", "0.2.0")

View File

@ -0,0 +1,182 @@
#include "dsmr.h"
#include "esphome/core/log.h"
#include <AES.h>
#include <Crypto.h>
#include <GCM.h>
namespace esphome {
namespace dsmr {
static const char *const TAG = "dsmr";
void Dsmr::loop() {
if (this->decryption_key_.empty())
this->receive_telegram_();
else
this->receive_encrypted_();
}
void Dsmr::receive_telegram_() {
while (available()) {
const char c = read();
if (c == '/') { // header: forward slash
ESP_LOGV(TAG, "Header found");
header_found_ = true;
footer_found_ = false;
telegram_len_ = 0;
}
if (!header_found_)
continue;
if (telegram_len_ >= MAX_TELEGRAM_LENGTH) { // Buffer overflow
header_found_ = false;
footer_found_ = false;
ESP_LOGE(TAG, "Error: Message larger than buffer");
return;
}
telegram_[telegram_len_] = c;
telegram_len_++;
if (c == '!') { // footer: exclamation mark
ESP_LOGV(TAG, "Footer found");
footer_found_ = true;
} else {
if (footer_found_ && c == 10) { // last \n after footer
header_found_ = false;
// Parse message
if (parse_telegram())
return;
}
}
}
}
void Dsmr::receive_encrypted_() {
// Encrypted buffer
uint8_t buffer[MAX_TELEGRAM_LENGTH];
size_t buffer_length = 0;
size_t packet_size = 0;
while (available()) {
const char c = read();
if (!header_found_) {
if ((uint8_t) c == 0xdb) {
ESP_LOGV(TAG, "Start byte 0xDB found");
header_found_ = true;
}
}
// Sanity check
if (!header_found_ || buffer_length >= MAX_TELEGRAM_LENGTH) {
if (buffer_length == 0) {
ESP_LOGE(TAG, "First byte of encrypted telegram should be 0xDB, aborting.");
} else {
ESP_LOGW(TAG, "Unexpected data");
}
this->status_momentary_warning("unexpected_data");
this->flush();
while (available())
read();
return;
}
buffer[buffer_length++] = c;
if (packet_size == 0 && buffer_length > 20) {
// Complete header + a few bytes of data
packet_size = buffer[11] << 8 | buffer[12];
}
if (buffer_length == packet_size + 13 && packet_size > 0) {
ESP_LOGV(TAG, "Encrypted data: %d bytes", buffer_length);
GCM<AES128> *gcmaes128{new GCM<AES128>()};
gcmaes128->setKey(this->decryption_key_.data(), gcmaes128->keySize());
// the iv is 8 bytes of the system title + 4 bytes frame counter
// system title is at byte 2 and frame counter at byte 15
for (int i = 10; i < 14; i++)
buffer[i] = buffer[i + 4];
constexpr uint16_t iv_size{12};
gcmaes128->setIV(&buffer[2], iv_size);
gcmaes128->decrypt(reinterpret_cast<uint8_t *>(this->telegram_),
// the ciphertext start at byte 18
&buffer[18],
// cipher size
buffer_length - 17);
delete gcmaes128;
telegram_len_ = strnlen(this->telegram_, sizeof(this->telegram_));
ESP_LOGV(TAG, "Decrypted data length: %d", telegram_len_);
ESP_LOGVV(TAG, "Decrypted data %s", this->telegram_);
parse_telegram();
telegram_len_ = 0;
return;
}
if (!available()) {
// baud rate is 115200 for encrypted data, this means a few byte should arrive every time
// program runs faster than buffer loading then available() might return false in the middle
delay(4); // Wait for data
}
}
if (buffer_length > 0)
ESP_LOGW(TAG, "Timeout while waiting for encrypted data or invalid data received.");
}
bool Dsmr::parse_telegram() {
MyData data;
ESP_LOGV(TAG, "Trying to parse");
::dsmr::ParseResult<void> res =
::dsmr::P1Parser::parse(&data, telegram_, telegram_len_,
false); // Parse telegram according to data definition. Ignore unknown values.
if (res.err) {
// Parsing error, show it
auto err_str = res.fullError(telegram_, telegram_ + telegram_len_);
ESP_LOGE(TAG, "%s", err_str.c_str());
return false;
} else {
this->status_clear_warning();
publish_sensors(data);
return true;
}
}
void Dsmr::dump_config() {
ESP_LOGCONFIG(TAG, "dsmr:");
#define DSMR_LOG_SENSOR(s) LOG_SENSOR(" ", #s, this->s_##s##_);
DSMR_SENSOR_LIST(DSMR_LOG_SENSOR, )
#define DSMR_LOG_TEXT_SENSOR(s) LOG_TEXT_SENSOR(" ", #s, this->s_##s##_);
DSMR_TEXT_SENSOR_LIST(DSMR_LOG_TEXT_SENSOR, )
}
void Dsmr::set_decryption_key(const std::string &decryption_key) {
if (decryption_key.length() == 0) {
ESP_LOGI(TAG, "Disabling decryption");
this->decryption_key_.clear();
return;
}
if (decryption_key.length() != 32) {
ESP_LOGE(TAG, "Error, decryption key must be 32 character long.");
return;
}
this->decryption_key_.clear();
ESP_LOGI(TAG, "Decryption key is set.");
// Verbose level prints decryption key
ESP_LOGV(TAG, "Using decryption key: %s", decryption_key.c_str());
char temp[3] = {0};
for (int i = 0; i < 16; i++) {
strncpy(temp, &(decryption_key.c_str()[i * 2]), 2);
decryption_key_.push_back(std::strtoul(temp, nullptr, 16));
}
}
} // namespace dsmr
} // namespace esphome

View File

@ -0,0 +1,104 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/text_sensor/text_sensor.h"
#include "esphome/components/uart/uart.h"
#include "esphome/core/log.h"
#include "esphome/core/defines.h"
// don't include <dsmr.h> because it puts everything in global namespace
#include <dsmr/parser.h>
#include <dsmr/fields.h>
namespace esphome {
namespace dsmr {
static constexpr uint32_t MAX_TELEGRAM_LENGTH = 1500;
static constexpr uint32_t POLL_TIMEOUT = 1000;
using namespace ::dsmr::fields;
// DSMR_**_LIST generated by ESPHome and written in esphome/core/defines
#if !defined(DSMR_SENSOR_LIST) && !defined(DSMR_TEXT_SENSOR_LIST)
// Neither set, set it to a dummy value to not break build
#define DSMR_TEXT_SENSOR_LIST(F, SEP) F(identification)
#endif
#if defined(DSMR_SENSOR_LIST) && defined(DSMR_TEXT_SENSOR_LIST)
#define DSMR_BOTH ,
#else
#define DSMR_BOTH
#endif
#ifndef DSMR_SENSOR_LIST
#define DSMR_SENSOR_LIST(F, SEP)
#endif
#ifndef DSMR_TEXT_SENSOR_LIST
#define DSMR_TEXT_SENSOR_LIST(F, SEP)
#endif
#define DSMR_DATA_SENSOR(s) s
#define DSMR_COMMA ,
using MyData = ::dsmr::ParsedData<DSMR_TEXT_SENSOR_LIST(DSMR_DATA_SENSOR, DSMR_COMMA)
DSMR_BOTH DSMR_SENSOR_LIST(DSMR_DATA_SENSOR, DSMR_COMMA)>;
class Dsmr : public Component, public uart::UARTDevice {
public:
Dsmr(uart::UARTComponent *uart) : uart::UARTDevice(uart) {}
void loop() override;
bool parse_telegram();
void publish_sensors(MyData &data) {
#define DSMR_PUBLISH_SENSOR(s) \
if (data.s##_present && this->s_##s##_ != nullptr) \
s_##s##_->publish_state(data.s);
DSMR_SENSOR_LIST(DSMR_PUBLISH_SENSOR, )
#define DSMR_PUBLISH_TEXT_SENSOR(s) \
if (data.s##_present && this->s_##s##_ != nullptr) \
s_##s##_->publish_state(data.s.c_str());
DSMR_TEXT_SENSOR_LIST(DSMR_PUBLISH_TEXT_SENSOR, )
};
void dump_config() override;
void set_decryption_key(const std::string &decryption_key);
// Sensor setters
#define DSMR_SET_SENSOR(s) \
void set_##s(sensor::Sensor *sensor) { s_##s##_ = sensor; }
DSMR_SENSOR_LIST(DSMR_SET_SENSOR, )
#define DSMR_SET_TEXT_SENSOR(s) \
void set_##s(text_sensor::TextSensor *sensor) { s_##s##_ = sensor; }
DSMR_TEXT_SENSOR_LIST(DSMR_SET_TEXT_SENSOR, )
protected:
void receive_telegram_();
void receive_encrypted_();
// Telegram buffer
char telegram_[MAX_TELEGRAM_LENGTH];
int telegram_len_{0};
// Serial parser
bool header_found_{false};
bool footer_found_{false};
// Sensor member pointers
#define DSMR_DECLARE_SENSOR(s) sensor::Sensor *s_##s##_{nullptr};
DSMR_SENSOR_LIST(DSMR_DECLARE_SENSOR, )
#define DSMR_DECLARE_TEXT_SENSOR(s) text_sensor::TextSensor *s_##s##_{nullptr};
DSMR_TEXT_SENSOR_LIST(DSMR_DECLARE_TEXT_SENSOR, )
std::vector<uint8_t> decryption_key_{};
};
} // namespace dsmr
} // namespace esphome

View File

@ -0,0 +1,211 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor
from esphome.const import (
DEVICE_CLASS_CURRENT,
DEVICE_CLASS_EMPTY,
DEVICE_CLASS_ENERGY,
DEVICE_CLASS_GAS,
DEVICE_CLASS_POWER,
DEVICE_CLASS_VOLTAGE,
ICON_EMPTY,
LAST_RESET_TYPE_NEVER,
STATE_CLASS_MEASUREMENT,
STATE_CLASS_NONE,
UNIT_AMPERE,
UNIT_EMPTY,
UNIT_VOLT,
UNIT_WATT,
)
from . import Dsmr, CONF_DSMR_ID
AUTO_LOAD = ["dsmr"]
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_DSMR_ID): cv.use_id(Dsmr),
cv.Optional("energy_delivered_lux"): sensor.sensor_schema(
"kWh",
ICON_EMPTY,
3,
DEVICE_CLASS_ENERGY,
STATE_CLASS_MEASUREMENT,
LAST_RESET_TYPE_NEVER,
),
cv.Optional("energy_delivered_tariff1"): sensor.sensor_schema(
"kWh",
ICON_EMPTY,
3,
DEVICE_CLASS_ENERGY,
STATE_CLASS_MEASUREMENT,
LAST_RESET_TYPE_NEVER,
),
cv.Optional("energy_delivered_tariff2"): sensor.sensor_schema(
"kWh",
ICON_EMPTY,
3,
DEVICE_CLASS_ENERGY,
STATE_CLASS_MEASUREMENT,
LAST_RESET_TYPE_NEVER,
),
cv.Optional("energy_returned_lux"): sensor.sensor_schema(
"kWh",
ICON_EMPTY,
3,
DEVICE_CLASS_ENERGY,
STATE_CLASS_MEASUREMENT,
LAST_RESET_TYPE_NEVER,
),
cv.Optional("energy_returned_tariff1"): sensor.sensor_schema(
"kWh",
ICON_EMPTY,
3,
DEVICE_CLASS_ENERGY,
STATE_CLASS_MEASUREMENT,
LAST_RESET_TYPE_NEVER,
),
cv.Optional("energy_returned_tariff2"): sensor.sensor_schema(
"kWh",
ICON_EMPTY,
3,
DEVICE_CLASS_ENERGY,
STATE_CLASS_MEASUREMENT,
LAST_RESET_TYPE_NEVER,
),
cv.Optional("total_imported_energy"): sensor.sensor_schema(
"kvarh", ICON_EMPTY, 3, DEVICE_CLASS_ENERGY, STATE_CLASS_NONE
),
cv.Optional("total_exported_energy"): sensor.sensor_schema(
"kvarh", ICON_EMPTY, 3, DEVICE_CLASS_ENERGY, STATE_CLASS_NONE
),
cv.Optional("power_delivered"): sensor.sensor_schema(
UNIT_WATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
),
cv.Optional("power_returned"): sensor.sensor_schema(
UNIT_WATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
),
cv.Optional("reactive_power_delivered"): sensor.sensor_schema(
"kvar", ICON_EMPTY, 3, DEVICE_CLASS_ENERGY, STATE_CLASS_NONE
),
cv.Optional("reactive_power_returned"): sensor.sensor_schema(
"kvar", ICON_EMPTY, 3, DEVICE_CLASS_ENERGY, STATE_CLASS_MEASUREMENT
),
cv.Optional("electricity_threshold"): sensor.sensor_schema(
UNIT_EMPTY, ICON_EMPTY, 3, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE
),
cv.Optional("electricity_switch_position"): sensor.sensor_schema(
UNIT_EMPTY, ICON_EMPTY, 3, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE
),
cv.Optional("electricity_failures"): sensor.sensor_schema(
UNIT_EMPTY, ICON_EMPTY, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE
),
cv.Optional("electricity_long_failures"): sensor.sensor_schema(
UNIT_EMPTY, ICON_EMPTY, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE
),
cv.Optional("electricity_sags_l1"): sensor.sensor_schema(
UNIT_EMPTY, ICON_EMPTY, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE
),
cv.Optional("electricity_sags_l2"): sensor.sensor_schema(
UNIT_EMPTY, ICON_EMPTY, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_MEASUREMENT
),
cv.Optional("electricity_sags_l3"): sensor.sensor_schema(
UNIT_EMPTY, ICON_EMPTY, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE
),
cv.Optional("electricity_swells_l1"): sensor.sensor_schema(
UNIT_EMPTY, ICON_EMPTY, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_MEASUREMENT
),
cv.Optional("electricity_swells_l2"): sensor.sensor_schema(
UNIT_EMPTY, ICON_EMPTY, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE
),
cv.Optional("electricity_swells_l3"): sensor.sensor_schema(
UNIT_EMPTY, ICON_EMPTY, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE
),
cv.Optional("current_l1"): sensor.sensor_schema(
UNIT_AMPERE, ICON_EMPTY, 1, DEVICE_CLASS_CURRENT, STATE_CLASS_MEASUREMENT
),
cv.Optional("current_l2"): sensor.sensor_schema(
UNIT_AMPERE, ICON_EMPTY, 1, DEVICE_CLASS_CURRENT, STATE_CLASS_MEASUREMENT
),
cv.Optional("current_l3"): sensor.sensor_schema(
UNIT_AMPERE, ICON_EMPTY, 1, DEVICE_CLASS_CURRENT, STATE_CLASS_MEASUREMENT
),
cv.Optional("power_delivered_l1"): sensor.sensor_schema(
UNIT_WATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
),
cv.Optional("power_delivered_l2"): sensor.sensor_schema(
UNIT_WATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
),
cv.Optional("power_delivered_l3"): sensor.sensor_schema(
UNIT_WATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
),
cv.Optional("power_returned_l1"): sensor.sensor_schema(
UNIT_WATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
),
cv.Optional("power_returned_l2"): sensor.sensor_schema(
UNIT_WATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
),
cv.Optional("power_returned_l3"): sensor.sensor_schema(
UNIT_WATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
),
cv.Optional("reactive_power_delivered_l1"): sensor.sensor_schema(
UNIT_WATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
),
cv.Optional("reactive_power_delivered_l2"): sensor.sensor_schema(
UNIT_WATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
),
cv.Optional("reactive_power_delivered_l3"): sensor.sensor_schema(
UNIT_WATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
),
cv.Optional("reactive_power_returned_l1"): sensor.sensor_schema(
UNIT_WATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
),
cv.Optional("reactive_power_returned_l2"): sensor.sensor_schema(
UNIT_WATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
),
cv.Optional("reactive_power_returned_l3"): sensor.sensor_schema(
UNIT_WATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
),
cv.Optional("voltage_l1"): sensor.sensor_schema(
UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE, STATE_CLASS_NONE
),
cv.Optional("voltage_l2"): sensor.sensor_schema(
UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE, STATE_CLASS_NONE
),
cv.Optional("voltage_l3"): sensor.sensor_schema(
UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE, STATE_CLASS_NONE
),
cv.Optional("gas_delivered"): sensor.sensor_schema(
"",
ICON_EMPTY,
3,
DEVICE_CLASS_GAS,
STATE_CLASS_MEASUREMENT,
LAST_RESET_TYPE_NEVER,
),
cv.Optional("gas_delivered_be"): sensor.sensor_schema(
"",
ICON_EMPTY,
3,
DEVICE_CLASS_GAS,
STATE_CLASS_MEASUREMENT,
LAST_RESET_TYPE_NEVER,
),
}
).extend(cv.COMPONENT_SCHEMA)
async def to_code(config):
hub = await cg.get_variable(config[CONF_DSMR_ID])
sensors = []
for key, conf in config.items():
if not isinstance(conf, dict):
continue
id = conf.get("id")
if id and id.type == sensor.Sensor:
s = await sensor.new_sensor(conf)
cg.add(getattr(hub, f"set_{key}")(s))
sensors.append(f"F({key})")
cg.add_define("DSMR_SENSOR_LIST(F, sep)", cg.RawExpression(" sep ".join(sensors)))

View File

@ -0,0 +1,94 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import text_sensor
from esphome.const import (
CONF_ID,
)
from . import Dsmr, CONF_DSMR_ID
AUTO_LOAD = ["dsmr"]
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_DSMR_ID): cv.use_id(Dsmr),
cv.Optional("identification"): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
}
),
cv.Optional("p1_version"): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
}
),
cv.Optional("p1_version_be"): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
}
),
cv.Optional("timestamp"): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
}
),
cv.Optional("electricity_tariff"): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
}
),
cv.Optional("electricity_failure_log"): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
}
),
cv.Optional("message_short"): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
}
),
cv.Optional("message_long"): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
}
),
cv.Optional("gas_equipment_id"): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
}
),
cv.Optional("thermal_equipment_id"): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
}
),
cv.Optional("water_equipment_id"): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
}
),
cv.Optional("sub_equipment_id"): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
}
),
}
).extend(cv.COMPONENT_SCHEMA)
async def to_code(config):
hub = await cg.get_variable(config[CONF_DSMR_ID])
text_sensors = []
for key, conf in config.items():
if not isinstance(conf, dict):
continue
id = conf.get("id")
if id and id.type == text_sensor.TextSensor:
var = cg.new_Pvariable(conf[CONF_ID])
await text_sensor.register_text_sensor(var, conf)
cg.add(getattr(hub, f"set_{key}")(var))
text_sensors.append(f"F({key})")
cg.add_define(
"DSMR_TEXT_SENSOR_LIST(F, sep)", cg.RawExpression(" sep ".join(text_sensors))
)

View File

@ -5,7 +5,6 @@ from esphome.components import sensor
from esphome.const import (
CONF_ID,
CONF_PIN,
DEVICE_CLASS_EMPTY,
STATE_CLASS_MEASUREMENT,
UNIT_PERCENT,
ICON_PERCENT,
@ -18,7 +17,10 @@ DutyCycleSensor = duty_cycle_ns.class_(
CONFIG_SCHEMA = (
sensor.sensor_schema(
UNIT_PERCENT, ICON_PERCENT, 1, DEVICE_CLASS_EMPTY, STATE_CLASS_MEASUREMENT
unit_of_measurement=UNIT_PERCENT,
icon=ICON_PERCENT,
accuracy_decimals=1,
state_class=STATE_CLASS_MEASUREMENT,
)
.extend(
{

View File

@ -50,7 +50,7 @@ void E131Component::loop() {
}
if (!packet_(payload, universe, packet)) {
ESP_LOGV(TAG, "Invalid packet recevied of size %zu.", payload.size());
ESP_LOGV(TAG, "Invalid packet received of size %zu.", payload.size());
continue;
}

View File

@ -7,6 +7,8 @@
namespace esphome {
namespace esp32_ble {
static const char *const TAG = "esp32_ble";
BLEAdvertising::BLEAdvertising() {
this->advertising_data_.set_scan_rsp = false;
this->advertising_data_.include_name = true;

View File

@ -5,6 +5,8 @@
namespace esphome {
namespace esp32_ble {
static const char *const TAG = "esp32_ble";
ESPBTUUID::ESPBTUUID() : uuid_() {}
ESPBTUUID ESPBTUUID::from_uint16(uint16_t uuid) {
ESPBTUUID ret;

View File

@ -13,7 +13,7 @@
/*
* BLE events come in from a separate Task (thread) in the ESP32 stack. Rather
* than trying to deal wth various locking strategies, all incoming GAP and GATT
* than trying to deal with various locking strategies, all incoming GAP and GATT
* events will simply be placed on a semaphore guarded queue. The next time the
* component runs loop(), these events are popped off the queue and handed at
* this safer time.

View File

@ -372,7 +372,7 @@ std::string ESPBTUUID::to_string() {
for (int8_t i = 15; i >= 0; i--) {
sprintf(bpos, "%02X", this->uuid_.uuid.uuid128[i]);
bpos += 2;
if (i == 3 || i == 5 || i == 7 || i == 9)
if (i == 6 || i == 8 || i == 10 || i == 12)
sprintf(bpos++, "-");
}
sbuf[47] = '\0';

View File

@ -12,7 +12,7 @@
/*
* BLE events come in from a separate Task (thread) in the ESP32 stack. Rather
* than trying to deal wth various locking strategies, all incoming GAP and GATT
* than trying to deal with various locking strategies, all incoming GAP and GATT
* events will simply be placed on a semaphore guarded queue. The next time the
* component runs loop(), these events are popped off the queue and handed at
* this safer time.

View File

@ -2,6 +2,7 @@ import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import pins
from esphome.const import (
CONF_DISABLED_BY_DEFAULT,
CONF_FREQUENCY,
CONF_ID,
CONF_NAME,
@ -66,6 +67,7 @@ CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(ESP32Camera),
cv.Required(CONF_NAME): cv.string,
cv.Optional(CONF_DISABLED_BY_DEFAULT, default=False): cv.boolean,
cv.Required(CONF_DATA_PINS): cv.All([pins.input_pin], cv.Length(min=8, max=8)),
cv.Required(CONF_VSYNC_PIN): pins.input_pin,
cv.Required(CONF_HREF_PIN): pins.input_pin,
@ -124,6 +126,7 @@ SETTERS = {
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME])
cg.add(var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT]))
await cg.register_component(var, config)
for key, setter in SETTERS.items():

View File

@ -3,7 +3,6 @@ import esphome.config_validation as cv
from esphome.components import sensor
from esphome.const import (
CONF_ID,
DEVICE_CLASS_EMPTY,
ESP_PLATFORM_ESP32,
STATE_CLASS_MEASUREMENT,
UNIT_MICROTESLA,
@ -19,7 +18,10 @@ ESP32HallSensor = esp32_hall_ns.class_(
CONFIG_SCHEMA = (
sensor.sensor_schema(
UNIT_MICROTESLA, ICON_MAGNET, 1, DEVICE_CLASS_EMPTY, STATE_CLASS_MEASUREMENT
unit_of_measurement=UNIT_MICROTESLA,
icon=ICON_MAGNET,
accuracy_decimals=1,
state_class=STATE_CLASS_MEASUREMENT,
)
.extend(
{

View File

@ -9,7 +9,7 @@
#include <eth_phy/phy_tlk110.h>
#include <lwip/dns.h>
/// Macro for IDF version comparision
/// Macro for IDF version comparison
#ifndef ESP_IDF_VERSION_VAL
#define ESP_IDF_VERSION_VAL(major, minor, patch) (((major) << 16) | ((minor) << 8) | (patch))
#endif
@ -102,8 +102,6 @@ void EthernetComponent::loop() {
this->dump_connect_params_();
this->status_clear_warning();
network_tick_mdns();
} else if (now - this->connect_begin_ > 15000) {
ESP_LOGW(TAG, "Connecting via ethernet failed! Re-connecting...");
this->start_connect_();
@ -120,6 +118,8 @@ void EthernetComponent::loop() {
}
break;
}
network_tick_mdns();
}
void EthernetComponent::dump_config() {
ESP_LOGCONFIG(TAG, "Ethernet:");

View File

@ -4,6 +4,7 @@ from esphome import automation
from esphome.automation import maybe_simple_id
from esphome.components import mqtt
from esphome.const import (
CONF_DISABLED_BY_DEFAULT,
CONF_ID,
CONF_INTERNAL,
CONF_MQTT_ID,
@ -34,7 +35,7 @@ ToggleAction = fan_ns.class_("ToggleAction", automation.Action)
FanTurnOnTrigger = fan_ns.class_("FanTurnOnTrigger", automation.Trigger.template())
FanTurnOffTrigger = fan_ns.class_("FanTurnOffTrigger", automation.Trigger.template())
FAN_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend(
FAN_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
{
cv.GenerateID(): cv.declare_id(FanState),
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTFanComponent),
@ -66,6 +67,7 @@ FAN_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend(
async def setup_fan_core_(var, config):
cg.add(var.set_name(config[CONF_NAME]))
cg.add(var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT]))
if CONF_INTERNAL in config:
cg.add(var.set_internal(config[CONF_INTERNAL]))

View File

@ -208,8 +208,7 @@ class FastLEDLightOutput : public light::AddressableLight {
// (In most use cases you won't need these)
light::LightTraits get_traits() override {
auto traits = light::LightTraits();
traits.set_supports_brightness(true);
traits.set_supports_rgb(true);
traits.set_supported_color_modes({light::ColorMode::RGB});
return traits;
}
void setup() override;

View File

@ -31,6 +31,7 @@ CHIPSETS = [
"GW6205_400",
"LPD1886",
"LPD1886_8BIT",
"SM16703",
]

View File

@ -8,15 +8,12 @@ from esphome.const import (
CONF_LAST_FINGER_ID,
CONF_SECURITY_LEVEL,
CONF_STATUS,
DEVICE_CLASS_EMPTY,
ICON_ACCOUNT,
ICON_ACCOUNT_CHECK,
ICON_DATABASE,
ICON_EMPTY,
ICON_FINGERPRINT,
ICON_SECURITY,
STATE_CLASS_NONE,
UNIT_EMPTY,
)
from . import CONF_FINGERPRINT_GROW_ID, FingerprintGrowComponent
@ -26,22 +23,33 @@ CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_FINGERPRINT_GROW_ID): cv.use_id(FingerprintGrowComponent),
cv.Optional(CONF_FINGERPRINT_COUNT): sensor.sensor_schema(
UNIT_EMPTY, ICON_FINGERPRINT, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE
icon=ICON_FINGERPRINT,
accuracy_decimals=0,
state_class=STATE_CLASS_NONE,
),
cv.Optional(CONF_STATUS): sensor.sensor_schema(
UNIT_EMPTY, ICON_EMPTY, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE
accuracy_decimals=0,
state_class=STATE_CLASS_NONE,
),
cv.Optional(CONF_CAPACITY): sensor.sensor_schema(
UNIT_EMPTY, ICON_DATABASE, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE
icon=ICON_DATABASE,
accuracy_decimals=0,
state_class=STATE_CLASS_NONE,
),
cv.Optional(CONF_SECURITY_LEVEL): sensor.sensor_schema(
UNIT_EMPTY, ICON_SECURITY, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE
icon=ICON_SECURITY,
accuracy_decimals=0,
state_class=STATE_CLASS_NONE,
),
cv.Optional(CONF_LAST_FINGER_ID): sensor.sensor_schema(
UNIT_EMPTY, ICON_ACCOUNT, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE
icon=ICON_ACCOUNT,
accuracy_decimals=0,
state_class=STATE_CLASS_NONE,
),
cv.Optional(CONF_LAST_CONFIDENCE): sensor.sensor_schema(
UNIT_EMPTY, ICON_ACCOUNT_CHECK, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE
icon=ICON_ACCOUNT_CHECK,
accuracy_decimals=0,
state_class=STATE_CLASS_NONE,
),
}
)

View File

@ -62,7 +62,7 @@ class FujitsuGeneralClimate : public climate_ir::ClimateIR {
/// Transmit via IR power off command.
void transmit_off_();
/// Parse incomming message
/// Parse incoming message
bool on_receive(remote_base::RemoteReceiveData data) override;
/// Transmit message as IR pulses

View File

@ -15,9 +15,6 @@ from esphome.const import (
UNIT_DEGREES,
UNIT_KILOMETER_PER_HOUR,
UNIT_METER,
UNIT_EMPTY,
ICON_EMPTY,
DEVICE_CLASS_EMPTY,
)
DEPENDENCIES = ["uart"]
@ -36,26 +33,33 @@ CONFIG_SCHEMA = (
{
cv.GenerateID(): cv.declare_id(GPS),
cv.Optional(CONF_LATITUDE): sensor.sensor_schema(
UNIT_DEGREES, ICON_EMPTY, 6, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE
unit_of_measurement=UNIT_DEGREES,
accuracy_decimals=6,
state_class=STATE_CLASS_NONE,
),
cv.Optional(CONF_LONGITUDE): sensor.sensor_schema(
UNIT_DEGREES, ICON_EMPTY, 6, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE
unit_of_measurement=UNIT_DEGREES,
accuracy_decimals=6,
state_class=STATE_CLASS_NONE,
),
cv.Optional(CONF_SPEED): sensor.sensor_schema(
UNIT_KILOMETER_PER_HOUR,
ICON_EMPTY,
6,
DEVICE_CLASS_EMPTY,
STATE_CLASS_NONE,
unit_of_measurement=UNIT_KILOMETER_PER_HOUR,
accuracy_decimals=6,
state_class=STATE_CLASS_NONE,
),
cv.Optional(CONF_COURSE): sensor.sensor_schema(
UNIT_DEGREES, ICON_EMPTY, 2, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE
unit_of_measurement=UNIT_DEGREES,
accuracy_decimals=2,
state_class=STATE_CLASS_NONE,
),
cv.Optional(CONF_ALTITUDE): sensor.sensor_schema(
UNIT_METER, ICON_EMPTY, 1, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE
unit_of_measurement=UNIT_METER,
accuracy_decimals=1,
state_class=STATE_CLASS_NONE,
),
cv.Optional(CONF_SATELLITES): sensor.sensor_schema(
UNIT_EMPTY, ICON_EMPTY, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_MEASUREMENT
accuracy_decimals=0,
state_class=STATE_CLASS_MEASUREMENT,
),
}
)

View File

@ -9,12 +9,10 @@ from esphome.const import (
CONF_REACTIVE_POWER,
CONF_VOLTAGE,
DEVICE_CLASS_CURRENT,
DEVICE_CLASS_EMPTY,
DEVICE_CLASS_ENERGY,
DEVICE_CLASS_POWER,
DEVICE_CLASS_VOLTAGE,
ICON_CURRENT_AC,
ICON_EMPTY,
LAST_RESET_TYPE_AUTO,
STATE_CLASS_MEASUREMENT,
STATE_CLASS_NONE,
@ -63,24 +61,47 @@ HavellsSolar = havells_solar_ns.class_(
)
PHASE_SENSORS = {
CONF_VOLTAGE: sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 2, DEVICE_CLASS_VOLTAGE),
CONF_VOLTAGE: sensor.sensor_schema(
unit_of_measurement=UNIT_VOLT,
accuracy_decimals=2,
device_class=DEVICE_CLASS_VOLTAGE,
),
CONF_CURRENT: sensor.sensor_schema(
UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT, STATE_CLASS_MEASUREMENT
unit_of_measurement=UNIT_AMPERE,
accuracy_decimals=2,
device_class=DEVICE_CLASS_CURRENT,
state_class=STATE_CLASS_MEASUREMENT,
),
}
PV_SENSORS = {
CONF_VOLTAGE: sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 2, DEVICE_CLASS_VOLTAGE),
CONF_VOLTAGE: sensor.sensor_schema(
unit_of_measurement=UNIT_VOLT,
accuracy_decimals=2,
device_class=DEVICE_CLASS_VOLTAGE,
),
CONF_CURRENT: sensor.sensor_schema(
UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT, STATE_CLASS_MEASUREMENT
unit_of_measurement=UNIT_AMPERE,
accuracy_decimals=2,
device_class=DEVICE_CLASS_CURRENT,
state_class=STATE_CLASS_MEASUREMENT,
),
CONF_ACTIVE_POWER: sensor.sensor_schema(
UNIT_WATT, ICON_EMPTY, 0, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
unit_of_measurement=UNIT_WATT,
accuracy_decimals=0,
device_class=DEVICE_CLASS_POWER,
state_class=STATE_CLASS_MEASUREMENT,
),
CONF_VOLTAGE_SAMPLED_BY_SECONDARY_CPU: sensor.sensor_schema(
UNIT_VOLT, ICON_EMPTY, 0, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
unit_of_measurement=UNIT_VOLT,
accuracy_decimals=0,
device_class=DEVICE_CLASS_POWER,
state_class=STATE_CLASS_MEASUREMENT,
),
CONF_INSULATION_OF_P_TO_GROUND: sensor.sensor_schema(
UNIT_KOHM, ICON_EMPTY, 0, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
unit_of_measurement=UNIT_KOHM,
accuracy_decimals=0,
device_class=DEVICE_CLASS_POWER,
state_class=STATE_CLASS_MEASUREMENT,
),
}
@ -101,107 +122,86 @@ CONFIG_SCHEMA = (
cv.Optional(CONF_PV1): PV_SCHEMA,
cv.Optional(CONF_PV2): PV_SCHEMA,
cv.Optional(CONF_FREQUENCY): sensor.sensor_schema(
UNIT_HERTZ,
ICON_CURRENT_AC,
2,
DEVICE_CLASS_EMPTY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_HERTZ,
icon=ICON_CURRENT_AC,
accuracy_decimals=2,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_ACTIVE_POWER): sensor.sensor_schema(
UNIT_WATT, ICON_EMPTY, 0, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
unit_of_measurement=UNIT_WATT,
accuracy_decimals=0,
device_class=DEVICE_CLASS_POWER,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_REACTIVE_POWER): sensor.sensor_schema(
UNIT_VOLT_AMPS_REACTIVE,
ICON_EMPTY,
2,
DEVICE_CLASS_POWER,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_VOLT_AMPS_REACTIVE,
accuracy_decimals=2,
device_class=DEVICE_CLASS_POWER,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_ENERGY_PRODUCTION_DAY): sensor.sensor_schema(
UNIT_KILOWATT_HOURS,
ICON_EMPTY,
2,
DEVICE_CLASS_ENERGY,
STATE_CLASS_MEASUREMENT,
LAST_RESET_TYPE_AUTO,
unit_of_measurement=UNIT_KILOWATT_HOURS,
accuracy_decimals=2,
device_class=DEVICE_CLASS_ENERGY,
state_class=STATE_CLASS_MEASUREMENT,
last_reset_type=LAST_RESET_TYPE_AUTO,
),
cv.Optional(CONF_TOTAL_ENERGY_PRODUCTION): sensor.sensor_schema(
UNIT_KILOWATT_HOURS,
ICON_EMPTY,
0,
DEVICE_CLASS_ENERGY,
STATE_CLASS_MEASUREMENT,
LAST_RESET_TYPE_AUTO,
unit_of_measurement=UNIT_KILOWATT_HOURS,
accuracy_decimals=0,
device_class=DEVICE_CLASS_ENERGY,
state_class=STATE_CLASS_MEASUREMENT,
last_reset_type=LAST_RESET_TYPE_AUTO,
),
cv.Optional(CONF_TOTAL_GENERATION_TIME): sensor.sensor_schema(
UNIT_HOURS,
ICON_EMPTY,
0,
DEVICE_CLASS_EMPTY,
STATE_CLASS_NONE,
unit_of_measurement=UNIT_HOURS,
accuracy_decimals=0,
state_class=STATE_CLASS_NONE,
),
cv.Optional(CONF_TODAY_GENERATION_TIME): sensor.sensor_schema(
UNIT_MINUTE,
ICON_EMPTY,
0,
DEVICE_CLASS_EMPTY,
STATE_CLASS_NONE,
unit_of_measurement=UNIT_MINUTE,
accuracy_decimals=0,
state_class=STATE_CLASS_NONE,
),
cv.Optional(CONF_INVERTER_MODULE_TEMP): sensor.sensor_schema(
UNIT_DEGREES,
ICON_EMPTY,
0,
DEVICE_CLASS_EMPTY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_DEGREES,
accuracy_decimals=0,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_INVERTER_INNER_TEMP): sensor.sensor_schema(
UNIT_DEGREES,
ICON_EMPTY,
0,
DEVICE_CLASS_EMPTY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_DEGREES,
accuracy_decimals=0,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_INVERTER_BUS_VOLTAGE): sensor.sensor_schema(
UNIT_VOLT,
ICON_EMPTY,
0,
DEVICE_CLASS_EMPTY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_VOLT,
accuracy_decimals=0,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_INSULATION_OF_PV_N_TO_GROUND): sensor.sensor_schema(
UNIT_KOHM,
ICON_EMPTY,
0,
DEVICE_CLASS_EMPTY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_KOHM,
accuracy_decimals=0,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_GFCI_VALUE): sensor.sensor_schema(
UNIT_MILLIAMPERE,
ICON_EMPTY,
0,
DEVICE_CLASS_EMPTY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_MILLIAMPERE,
accuracy_decimals=0,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_DCI_OF_R): sensor.sensor_schema(
UNIT_MILLIAMPERE,
ICON_EMPTY,
0,
DEVICE_CLASS_EMPTY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_MILLIAMPERE,
accuracy_decimals=0,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_DCI_OF_S): sensor.sensor_schema(
UNIT_MILLIAMPERE,
ICON_EMPTY,
0,
DEVICE_CLASS_EMPTY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_MILLIAMPERE,
accuracy_decimals=0,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_DCI_OF_T): sensor.sensor_schema(
UNIT_MILLIAMPERE,
ICON_EMPTY,
0,
DEVICE_CLASS_EMPTY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_MILLIAMPERE,
accuracy_decimals=0,
state_class=STATE_CLASS_MEASUREMENT,
),
}
)

View File

@ -18,10 +18,9 @@ class HBridgeLightOutput : public PollingComponent, public light::LightOutput {
light::LightTraits get_traits() override {
auto traits = light::LightTraits();
traits.set_supports_brightness(true); // Dimming
traits.set_supports_rgb(false);
traits.set_supports_rgb_white_value(true); // hbridge color
traits.set_supports_color_temperature(false);
traits.set_supported_color_modes({light::ColorMode::COLD_WARM_WHITE});
traits.set_min_mireds(153);
traits.set_max_mireds(500);
return traits;
}
@ -31,11 +30,11 @@ class HBridgeLightOutput : public PollingComponent, public light::LightOutput {
// This method runs around 60 times per second
// We cannot do the PWM ourselves so we are reliant on the hardware PWM
if (!this->forward_direction_) { // First LED Direction
this->pinb_pin_->set_level(this->duty_off_);
this->pina_pin_->set_level(this->pina_duty_);
this->pinb_pin_->set_level(0);
this->forward_direction_ = true;
} else { // Second LED Direction
this->pina_pin_->set_level(this->duty_off_);
this->pina_pin_->set_level(0);
this->pinb_pin_->set_level(this->pinb_duty_);
this->forward_direction_ = false;
}
@ -44,23 +43,7 @@ class HBridgeLightOutput : public PollingComponent, public light::LightOutput {
float get_setup_priority() const override { return setup_priority::HARDWARE; }
void write_state(light::LightState *state) override {
float bright;
state->current_values_as_brightness(&bright);
state->set_gamma_correct(0);
float red, green, blue, white;
state->current_values_as_rgbw(&red, &green, &blue, &white);
if ((white / bright) > 0.55) {
this->pina_duty_ = (bright * (1 - (white / bright)));
this->pinb_duty_ = bright;
} else if (white < 0.45) {
this->pina_duty_ = bright;
this->pinb_duty_ = white;
} else {
this->pina_duty_ = bright;
this->pinb_duty_ = bright;
}
state->current_values_as_cwww(&this->pina_duty_, &this->pinb_duty_, false);
}
protected:
@ -68,7 +51,6 @@ class HBridgeLightOutput : public PollingComponent, public light::LightOutput {
output::FloatOutput *pinb_pin_;
float pina_duty_ = 0;
float pinb_duty_ = 0;
float duty_off_ = 0;
bool forward_direction_ = false;
};

View File

@ -7,7 +7,6 @@ from esphome.const import (
CONF_TEMPERATURE,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_TEMPERATURE,
ICON_EMPTY,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
UNIT_PERCENT,
@ -25,18 +24,16 @@ CONFIG_SCHEMA = (
{
cv.GenerateID(): cv.declare_id(HDC1080Component),
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS,
ICON_EMPTY,
1,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
UNIT_PERCENT,
ICON_EMPTY,
0,
DEVICE_CLASS_HUMIDITY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_PERCENT,
accuracy_decimals=0,
device_class=DEVICE_CLASS_HUMIDITY,
state_class=STATE_CLASS_MEASUREMENT,
),
}
)

View File

@ -0,0 +1 @@
CODEOWNERS = ["@sourabhjaiswal"]

View File

@ -0,0 +1,20 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import climate_ir
from esphome.const import CONF_ID
AUTO_LOAD = ["climate_ir"]
hitachi_ac424_ns = cg.esphome_ns.namespace("hitachi_ac424")
HitachiClimate = hitachi_ac424_ns.class_("HitachiClimate", climate_ir.ClimateIR)
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(HitachiClimate),
}
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await climate_ir.register_climate_ir(var, config)

View File

@ -0,0 +1,368 @@
#include "hitachi_ac424.h"
namespace esphome {
namespace hitachi_ac424 {
static const char *const TAG = "climate.hitachi_ac424";
void set_bits(uint8_t *const dst, const uint8_t offset, const uint8_t nbits, const uint8_t data) {
if (offset >= 8 || !nbits)
return; // Short circuit as it won't change.
// Calculate the mask for the supplied value.
uint8_t mask = UINT8_MAX >> (8 - ((nbits > 8) ? 8 : nbits));
// Calculate the mask & clear the space for the data.
// Clear the destination bits.
*dst &= ~(uint8_t)(mask << offset);
// Merge in the data.
*dst |= ((data & mask) << offset);
}
void set_bit(uint8_t *const data, const uint8_t position, const bool on) {
uint8_t mask = 1 << position;
if (on)
*data |= mask;
else
*data &= ~mask;
}
uint8_t *invert_byte_pairs(uint8_t *ptr, const uint16_t length) {
for (uint16_t i = 1; i < length; i += 2) {
// Code done this way to avoid a compiler warning bug.
uint8_t inv = ~*(ptr + i - 1);
*(ptr + i) = inv;
}
return ptr;
}
bool HitachiClimate::get_power_() { return remote_state_[HITACHI_AC424_POWER_BYTE] == HITACHI_AC424_POWER_ON; }
void HitachiClimate::set_power_(bool on) {
set_button_(HITACHI_AC424_BUTTON_POWER);
remote_state_[HITACHI_AC424_POWER_BYTE] = on ? HITACHI_AC424_POWER_ON : HITACHI_AC424_POWER_OFF;
}
uint8_t HitachiClimate::get_mode_() { return remote_state_[HITACHI_AC424_MODE_BYTE] & 0xF; }
void HitachiClimate::set_mode_(uint8_t mode) {
uint8_t new_mode = mode;
switch (mode) {
// Fan mode sets a special temp.
case HITACHI_AC424_MODE_FAN:
set_temp_(HITACHI_AC424_TEMP_FAN, false);
break;
case HITACHI_AC424_MODE_HEAT:
case HITACHI_AC424_MODE_COOL:
case HITACHI_AC424_MODE_DRY:
break;
default:
new_mode = HITACHI_AC424_MODE_COOL;
}
set_bits(&remote_state_[HITACHI_AC424_MODE_BYTE], 0, 4, new_mode);
if (new_mode != HITACHI_AC424_MODE_FAN)
set_temp_(previous_temp_);
set_fan_(get_fan_()); // Reset the fan speed after the mode change.
set_power_(true);
}
void HitachiClimate::set_temp_(uint8_t celsius, bool set_previous) {
uint8_t temp;
temp = std::min(celsius, HITACHI_AC424_TEMP_MAX);
temp = std::max(temp, HITACHI_AC424_TEMP_MIN);
set_bits(&remote_state_[HITACHI_AC424_TEMP_BYTE], HITACHI_AC424_TEMP_OFFSET, HITACHI_AC424_TEMP_SIZE, temp);
if (previous_temp_ > temp)
set_button_(HITACHI_AC424_BUTTON_TEMP_DOWN);
else if (previous_temp_ < temp)
set_button_(HITACHI_AC424_BUTTON_TEMP_UP);
if (set_previous)
previous_temp_ = temp;
}
uint8_t HitachiClimate::get_fan_() { return remote_state_[HITACHI_AC424_FAN_BYTE] >> 4 & 0xF; }
void HitachiClimate::set_fan_(uint8_t speed) {
uint8_t new_speed = std::max(speed, HITACHI_AC424_FAN_MIN);
uint8_t fan_max = HITACHI_AC424_FAN_MAX;
// Only 2 x low speeds in Dry mode or Auto
if (get_mode_() == HITACHI_AC424_MODE_DRY && speed == HITACHI_AC424_FAN_AUTO) {
fan_max = HITACHI_AC424_FAN_AUTO;
} else if (get_mode_() == HITACHI_AC424_MODE_DRY) {
fan_max = HITACHI_AC424_FAN_MAX_DRY;
} else if (get_mode_() == HITACHI_AC424_MODE_FAN && speed == HITACHI_AC424_FAN_AUTO) {
// Fan Mode does not have auto. Set to safe low
new_speed = HITACHI_AC424_FAN_MIN;
}
new_speed = std::min(new_speed, fan_max);
// Handle the setting the button value if we are going to change the value.
if (new_speed != get_fan_())
set_button_(HITACHI_AC424_BUTTON_FAN);
// Set the values
set_bits(&remote_state_[HITACHI_AC424_FAN_BYTE], 4, 4, new_speed);
remote_state_[9] = 0x92;
// When fan is at min/max, additional bytes seem to be set
if (new_speed == HITACHI_AC424_FAN_MIN)
remote_state_[9] = 0x98;
remote_state_[29] = 0x01;
}
void HitachiClimate::set_swing_v_toggle_(bool on) {
uint8_t button = get_button_(); // Get the current button value.
if (on)
button = HITACHI_AC424_BUTTON_SWINGV; // Set the button to SwingV.
else if (button == HITACHI_AC424_BUTTON_SWINGV) // Asked to unset it
// It was set previous, so use Power as a default
button = HITACHI_AC424_BUTTON_POWER;
set_button_(button);
}
bool HitachiClimate::get_swing_v_toggle_() { return get_button_() == HITACHI_AC424_BUTTON_SWINGV; }
void HitachiClimate::set_swing_v_(bool on) {
set_swing_v_toggle_(on); // Set the button value.
set_bit(&remote_state_[HITACHI_AC424_SWINGV_BYTE], HITACHI_AC424_SWINGV_OFFSET, on);
}
bool HitachiClimate::get_swing_v_() {
return HITACHI_AC424_GETBIT8(remote_state_[HITACHI_AC424_SWINGV_BYTE], HITACHI_AC424_SWINGV_OFFSET);
}
void HitachiClimate::set_swing_h_(uint8_t position) {
if (position > HITACHI_AC424_SWINGH_LEFT_MAX)
return set_swing_h_(HITACHI_AC424_SWINGH_MIDDLE);
set_bits(&remote_state_[HITACHI_AC424_SWINGH_BYTE], HITACHI_AC424_SWINGH_OFFSET, HITACHI_AC424_SWINGH_SIZE, position);
set_button_(HITACHI_AC424_BUTTON_SWINGH);
}
uint8_t HitachiClimate::get_swing_h_() {
return HITACHI_AC424_GETBITS8(remote_state_[HITACHI_AC424_SWINGH_BYTE], HITACHI_AC424_SWINGH_OFFSET,
HITACHI_AC424_SWINGH_SIZE);
}
uint8_t HitachiClimate::get_button_() { return remote_state_[HITACHI_AC424_BUTTON_BYTE]; }
void HitachiClimate::set_button_(uint8_t button) { remote_state_[HITACHI_AC424_BUTTON_BYTE] = button; }
void HitachiClimate::transmit_state() {
switch (this->mode) {
case climate::CLIMATE_MODE_COOL:
set_mode_(HITACHI_AC424_MODE_COOL);
break;
case climate::CLIMATE_MODE_DRY:
set_mode_(HITACHI_AC424_MODE_DRY);
break;
case climate::CLIMATE_MODE_HEAT:
set_mode_(HITACHI_AC424_MODE_HEAT);
break;
case climate::CLIMATE_MODE_HEAT_COOL:
set_mode_(HITACHI_AC424_MODE_AUTO);
break;
case climate::CLIMATE_MODE_FAN_ONLY:
set_mode_(HITACHI_AC424_MODE_FAN);
break;
case climate::CLIMATE_MODE_OFF:
set_power_(false);
break;
default:
ESP_LOGW(TAG, "Unsupported mode: %s", climate_mode_to_string(this->mode));
}
set_temp_(static_cast<uint8_t>(this->target_temperature));
switch (this->fan_mode.value()) {
case climate::CLIMATE_FAN_LOW:
set_fan_(HITACHI_AC424_FAN_LOW);
break;
case climate::CLIMATE_FAN_MEDIUM:
set_fan_(HITACHI_AC424_FAN_MEDIUM);
break;
case climate::CLIMATE_FAN_HIGH:
set_fan_(HITACHI_AC424_FAN_HIGH);
break;
case climate::CLIMATE_FAN_ON:
case climate::CLIMATE_FAN_AUTO:
default:
set_fan_(HITACHI_AC424_FAN_AUTO);
}
switch (this->swing_mode) {
case climate::CLIMATE_SWING_BOTH:
set_swing_v_(true);
set_swing_h_(HITACHI_AC424_SWINGH_AUTO);
break;
case climate::CLIMATE_SWING_VERTICAL:
set_swing_v_(true);
set_swing_h_(HITACHI_AC424_SWINGH_MIDDLE);
break;
case climate::CLIMATE_SWING_HORIZONTAL:
set_swing_v_(false);
set_swing_h_(HITACHI_AC424_SWINGH_AUTO);
break;
case climate::CLIMATE_SWING_OFF:
set_swing_v_(false);
set_swing_h_(HITACHI_AC424_SWINGH_MIDDLE);
break;
}
// TODO: find change value to set button, now always set to power button
set_button_(HITACHI_AC424_BUTTON_POWER);
invert_byte_pairs(remote_state_ + 3, HITACHI_AC424_STATE_LENGTH - 3);
auto transmit = this->transmitter_->transmit();
auto data = transmit.get_data();
data->set_carrier_frequency(HITACHI_AC424_FREQ);
uint8_t repeat = 0;
for (uint8_t r = 0; r <= repeat; r++) {
// Header
data->item(HITACHI_AC424_HDR_MARK, HITACHI_AC424_HDR_SPACE);
// Data
for (uint8_t i : remote_state_) {
for (uint8_t j = 0; j < 8; j++) {
data->mark(HITACHI_AC424_BIT_MARK);
bool bit = i & (1 << j);
data->space(bit ? HITACHI_AC424_ONE_SPACE : HITACHI_AC424_ZERO_SPACE);
}
}
// Footer
data->item(HITACHI_AC424_BIT_MARK, HITACHI_AC424_MIN_GAP);
}
transmit.perform();
dump_state_("Sent", remote_state_);
}
bool HitachiClimate::parse_mode_(const uint8_t remote_state[]) {
uint8_t power = remote_state[HITACHI_AC424_POWER_BYTE];
ESP_LOGV(TAG, "Power: %02X %02X", remote_state[HITACHI_AC424_POWER_BYTE], power);
uint8_t mode = remote_state[HITACHI_AC424_MODE_BYTE] & 0xF;
ESP_LOGV(TAG, "Mode: %02X %02X", remote_state[HITACHI_AC424_MODE_BYTE], mode);
if (power == HITACHI_AC424_POWER_ON) {
switch (mode) {
case HITACHI_AC424_MODE_COOL:
this->mode = climate::CLIMATE_MODE_COOL;
break;
case HITACHI_AC424_MODE_DRY:
this->mode = climate::CLIMATE_MODE_DRY;
break;
case HITACHI_AC424_MODE_HEAT:
this->mode = climate::CLIMATE_MODE_HEAT;
break;
case HITACHI_AC424_MODE_AUTO:
this->mode = climate::CLIMATE_MODE_HEAT_COOL;
break;
case HITACHI_AC424_MODE_FAN:
this->mode = climate::CLIMATE_MODE_FAN_ONLY;
break;
}
} else {
this->mode = climate::CLIMATE_MODE_OFF;
}
return true;
}
bool HitachiClimate::parse_temperature_(const uint8_t remote_state[]) {
uint8_t temperature =
HITACHI_AC424_GETBITS8(remote_state[HITACHI_AC424_TEMP_BYTE], HITACHI_AC424_TEMP_OFFSET, HITACHI_AC424_TEMP_SIZE);
this->target_temperature = temperature;
ESP_LOGV(TAG, "Temperature: %02X %02u %04f", remote_state[HITACHI_AC424_TEMP_BYTE], temperature,
this->target_temperature);
return true;
}
bool HitachiClimate::parse_fan_(const uint8_t remote_state[]) {
uint8_t fan_mode = remote_state[HITACHI_AC424_FAN_BYTE] >> 4 & 0xF;
ESP_LOGV(TAG, "Fan: %02X %02X", remote_state[HITACHI_AC424_FAN_BYTE], fan_mode);
switch (fan_mode) {
case HITACHI_AC424_FAN_MIN:
case HITACHI_AC424_FAN_LOW:
this->fan_mode = climate::CLIMATE_FAN_LOW;
break;
case HITACHI_AC424_FAN_MEDIUM:
this->fan_mode = climate::CLIMATE_FAN_MEDIUM;
break;
case HITACHI_AC424_FAN_HIGH:
case HITACHI_AC424_FAN_MAX:
this->fan_mode = climate::CLIMATE_FAN_HIGH;
break;
case HITACHI_AC424_FAN_AUTO:
this->fan_mode = climate::CLIMATE_FAN_AUTO;
break;
}
return true;
}
bool HitachiClimate::parse_swing_(const uint8_t remote_state[]) {
uint8_t swing_modeh = HITACHI_AC424_GETBITS8(remote_state[HITACHI_AC424_SWINGH_BYTE], HITACHI_AC424_SWINGH_OFFSET,
HITACHI_AC424_SWINGH_SIZE);
ESP_LOGV(TAG, "SwingH: %02X %02X", remote_state[HITACHI_AC424_SWINGH_BYTE], swing_modeh);
if ((swing_modeh & 0x7) == 0x0) {
this->swing_mode = climate::CLIMATE_SWING_HORIZONTAL;
} else if ((swing_modeh & 0x3) == 0x3) {
this->swing_mode = climate::CLIMATE_SWING_OFF;
} else {
this->swing_mode = climate::CLIMATE_SWING_HORIZONTAL;
}
return true;
}
bool HitachiClimate::on_receive(remote_base::RemoteReceiveData data) {
// Validate header
if (!data.expect_item(HITACHI_AC424_HDR_MARK, HITACHI_AC424_HDR_SPACE)) {
ESP_LOGVV(TAG, "Header fail");
return false;
}
uint8_t recv_state[HITACHI_AC424_STATE_LENGTH] = {0};
// Read all bytes.
for (uint8_t pos = 0; pos < HITACHI_AC424_STATE_LENGTH; pos++) {
// Read bit
for (int8_t bit = 0; bit < 8; bit++) {
if (data.expect_item(HITACHI_AC424_BIT_MARK, HITACHI_AC424_ONE_SPACE))
recv_state[pos] |= 1 << bit;
else if (!data.expect_item(HITACHI_AC424_BIT_MARK, HITACHI_AC424_ZERO_SPACE)) {
ESP_LOGVV(TAG, "Byte %d bit %d fail", pos, bit);
return false;
}
}
}
// Validate footer
if (!data.expect_mark(HITACHI_AC424_BIT_MARK)) {
ESP_LOGVV(TAG, "Footer fail");
return false;
}
dump_state_("Recv", recv_state);
// parse mode
this->parse_mode_(recv_state);
// parse temperature
this->parse_temperature_(recv_state);
// parse fan
this->parse_fan_(recv_state);
// parse swingv
this->parse_swing_(recv_state);
this->publish_state();
for (uint8_t i = 0; i < HITACHI_AC424_STATE_LENGTH; i++)
remote_state_[i] = recv_state[i];
return true;
}
void HitachiClimate::dump_state_(const char action[], uint8_t state[]) {
for (uint16_t i = 0; i < HITACHI_AC424_STATE_LENGTH - 10; i += 10) {
ESP_LOGV(TAG, "%s: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", action, state[i + 0], state[i + 1],
state[i + 2], state[i + 3], state[i + 4], state[i + 5], state[i + 6], state[i + 7], state[i + 8],
state[i + 9]);
}
ESP_LOGV(TAG, "%s: %02X %02X %02X", action, state[40], state[41], state[42]);
}
} // namespace hitachi_ac424
} // namespace esphome

View File

@ -0,0 +1,123 @@
#pragma once
#include "esphome/core/log.h"
#include "esphome/components/climate_ir/climate_ir.h"
namespace esphome {
namespace hitachi_ac424 {
const uint16_t HITACHI_AC424_HDR_MARK = 3416; // ac
const uint16_t HITACHI_AC424_HDR_SPACE = 1604; // ac
const uint16_t HITACHI_AC424_BIT_MARK = 463;
const uint16_t HITACHI_AC424_ONE_SPACE = 1208;
const uint16_t HITACHI_AC424_ZERO_SPACE = 372;
const uint32_t HITACHI_AC424_MIN_GAP = 100000; // just a guess.
const uint16_t HITACHI_AC424_FREQ = 38000; // Hz.
const uint8_t HITACHI_AC424_BUTTON_BYTE = 11;
const uint8_t HITACHI_AC424_BUTTON_POWER = 0x13;
const uint8_t HITACHI_AC424_BUTTON_SLEEP = 0x31;
const uint8_t HITACHI_AC424_BUTTON_MODE = 0x41;
const uint8_t HITACHI_AC424_BUTTON_FAN = 0x42;
const uint8_t HITACHI_AC424_BUTTON_TEMP_DOWN = 0x43;
const uint8_t HITACHI_AC424_BUTTON_TEMP_UP = 0x44;
const uint8_t HITACHI_AC424_BUTTON_SWINGV = 0x81;
const uint8_t HITACHI_AC424_BUTTON_SWINGH = 0x8C;
const uint8_t HITACHI_AC424_BUTTON_MILDEWPROOF = 0xE2;
const uint8_t HITACHI_AC424_TEMP_BYTE = 13;
const uint8_t HITACHI_AC424_TEMP_OFFSET = 2;
const uint8_t HITACHI_AC424_TEMP_SIZE = 6;
const uint8_t HITACHI_AC424_TEMP_MIN = 16; // 16C
const uint8_t HITACHI_AC424_TEMP_MAX = 32; // 32C
const uint8_t HITACHI_AC424_TEMP_FAN = 27; // 27C
const uint8_t HITACHI_AC424_TIMER_BYTE = 15;
const uint8_t HITACHI_AC424_MODE_BYTE = 25;
const uint8_t HITACHI_AC424_MODE_FAN = 1;
const uint8_t HITACHI_AC424_MODE_COOL = 3;
const uint8_t HITACHI_AC424_MODE_DRY = 5;
const uint8_t HITACHI_AC424_MODE_HEAT = 6;
const uint8_t HITACHI_AC424_MODE_AUTO = 14;
const uint8_t HITACHI_AC424_MODE_POWERFUL = 19;
const uint8_t HITACHI_AC424_FAN_BYTE = HITACHI_AC424_MODE_BYTE;
const uint8_t HITACHI_AC424_FAN_MIN = 1;
const uint8_t HITACHI_AC424_FAN_LOW = 2;
const uint8_t HITACHI_AC424_FAN_MEDIUM = 3;
const uint8_t HITACHI_AC424_FAN_HIGH = 4;
const uint8_t HITACHI_AC424_FAN_AUTO = 5;
const uint8_t HITACHI_AC424_FAN_MAX = 6;
const uint8_t HITACHI_AC424_FAN_MAX_DRY = 2;
const uint8_t HITACHI_AC424_POWER_BYTE = 27;
const uint8_t HITACHI_AC424_POWER_ON = 0xF1;
const uint8_t HITACHI_AC424_POWER_OFF = 0xE1;
const uint8_t HITACHI_AC424_SWINGH_BYTE = 35;
const uint8_t HITACHI_AC424_SWINGH_OFFSET = 0; // Mask 0b00000xxx
const uint8_t HITACHI_AC424_SWINGH_SIZE = 3; // Mask 0b00000xxx
const uint8_t HITACHI_AC424_SWINGH_AUTO = 0; // 0b000
const uint8_t HITACHI_AC424_SWINGH_RIGHT_MAX = 1; // 0b001
const uint8_t HITACHI_AC424_SWINGH_RIGHT = 2; // 0b010
const uint8_t HITACHI_AC424_SWINGH_MIDDLE = 3; // 0b011
const uint8_t HITACHI_AC424_SWINGH_LEFT = 4; // 0b100
const uint8_t HITACHI_AC424_SWINGH_LEFT_MAX = 5; // 0b101
const uint8_t HITACHI_AC424_SWINGV_BYTE = 37;
const uint8_t HITACHI_AC424_SWINGV_OFFSET = 5; // Mask 0b00x00000
const uint8_t HITACHI_AC424_MILDEWPROOF_BYTE = HITACHI_AC424_SWINGV_BYTE;
const uint8_t HITACHI_AC424_MILDEWPROOF_OFFSET = 2; // Mask 0b00000x00
const uint16_t HITACHI_AC424_STATE_LENGTH = 53;
const uint16_t HITACHI_AC424_BITS = HITACHI_AC424_STATE_LENGTH * 8;
#define HITACHI_AC424_GETBIT8(a, b) ((a) & ((uint8_t) 1 << (b)))
#define HITACHI_AC424_GETBITS8(data, offset, size) \
(((data) & (((uint8_t) UINT8_MAX >> (8 - (size))) << (offset))) >> (offset))
class HitachiClimate : public climate_ir::ClimateIR {
public:
HitachiClimate()
: climate_ir::ClimateIR(HITACHI_AC424_TEMP_MIN, HITACHI_AC424_TEMP_MAX, 1.0F, true, true,
{climate::CLIMATE_FAN_AUTO, climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM,
climate::CLIMATE_FAN_HIGH},
{climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_HORIZONTAL}) {}
protected:
uint8_t remote_state_[HITACHI_AC424_STATE_LENGTH]{
0x01, 0x10, 0x00, 0x40, 0xBF, 0xFF, 0x00, 0xCC, 0x33, 0x92, 0x6D, 0x13, 0xEC, 0x5C, 0xA3, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x53, 0xAC, 0xF1, 0x0E, 0x00, 0xFF, 0x00, 0xFF, 0x80, 0x7F, 0x03,
0xFC, 0x01, 0xFE, 0x88, 0x77, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00};
uint8_t previous_temp_{27};
// Transmit via IR the state of this climate controller.
void transmit_state() override;
bool get_power_();
void set_power_(bool on);
uint8_t get_mode_();
void set_mode_(uint8_t mode);
void set_temp_(uint8_t celsius, bool set_previous = false);
uint8_t get_fan_();
void set_fan_(uint8_t speed);
void set_swing_v_toggle_(bool on);
bool get_swing_v_toggle_();
void set_swing_v_(bool on);
bool get_swing_v_();
void set_swing_h_(uint8_t position);
uint8_t get_swing_h_();
uint8_t get_button_();
void set_button_(uint8_t button);
// Handle received IR Buffer
bool on_receive(remote_base::RemoteReceiveData data) override;
bool parse_mode_(const uint8_t remote_state[]);
bool parse_temperature_(const uint8_t remote_state[]);
bool parse_fan_(const uint8_t remote_state[]);
bool parse_swing_(const uint8_t remote_state[]);
bool parse_state_frame_(const uint8_t frame[]);
void dump_state_(const char action[], uint8_t remote_state[]);
};
} // namespace hitachi_ac424
} // namespace esphome

View File

@ -18,7 +18,6 @@ from esphome.const import (
DEVICE_CLASS_ENERGY,
DEVICE_CLASS_POWER,
DEVICE_CLASS_VOLTAGE,
ICON_EMPTY,
LAST_RESET_TYPE_AUTO,
STATE_CLASS_MEASUREMENT,
UNIT_VOLT,
@ -58,21 +57,29 @@ CONFIG_SCHEMA = cv.Schema(
pins.internal_gpio_input_pullup_pin_schema, pins.validate_has_interrupt
),
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): sensor.sensor_schema(
UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT, STATE_CLASS_MEASUREMENT
unit_of_measurement=UNIT_AMPERE,
accuracy_decimals=2,
device_class=DEVICE_CLASS_CURRENT,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_POWER): 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_ENERGY): sensor.sensor_schema(
UNIT_WATT_HOURS,
ICON_EMPTY,
1,
DEVICE_CLASS_ENERGY,
STATE_CLASS_MEASUREMENT,
LAST_RESET_TYPE_AUTO,
unit_of_measurement=UNIT_WATT_HOURS,
accuracy_decimals=1,
device_class=DEVICE_CLASS_ENERGY,
state_class=STATE_CLASS_MEASUREMENT,
last_reset_type=LAST_RESET_TYPE_AUTO,
),
cv.Optional(CONF_CURRENT_RESISTOR, default=0.001): cv.resistance,
cv.Optional(CONF_VOLTAGE_DIVIDER, default=2351): cv.positive_float,

View File

@ -6,7 +6,6 @@ from esphome.const import (
CONF_PM_2_5,
CONF_PM_10_0,
CONF_PM_1_0,
DEVICE_CLASS_EMPTY,
STATE_CLASS_MEASUREMENT,
UNIT_MICROGRAMS_PER_CUBIC_METER,
ICON_CHEMICAL_WEAPON,
@ -43,32 +42,28 @@ CONFIG_SCHEMA = cv.All(
{
cv.GenerateID(): cv.declare_id(HM3301Component),
cv.Optional(CONF_PM_1_0): sensor.sensor_schema(
UNIT_MICROGRAMS_PER_CUBIC_METER,
ICON_CHEMICAL_WEAPON,
0,
DEVICE_CLASS_EMPTY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_MICROGRAMS_PER_CUBIC_METER,
icon=ICON_CHEMICAL_WEAPON,
accuracy_decimals=0,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_PM_2_5): sensor.sensor_schema(
UNIT_MICROGRAMS_PER_CUBIC_METER,
ICON_CHEMICAL_WEAPON,
0,
DEVICE_CLASS_EMPTY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_MICROGRAMS_PER_CUBIC_METER,
icon=ICON_CHEMICAL_WEAPON,
accuracy_decimals=0,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_PM_10_0): sensor.sensor_schema(
UNIT_MICROGRAMS_PER_CUBIC_METER,
ICON_CHEMICAL_WEAPON,
0,
DEVICE_CLASS_EMPTY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_MICROGRAMS_PER_CUBIC_METER,
icon=ICON_CHEMICAL_WEAPON,
accuracy_decimals=0,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_AQI): sensor.sensor_schema(
UNIT_INDEX,
ICON_CHEMICAL_WEAPON,
0,
DEVICE_CLASS_EMPTY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_INDEX,
icon=ICON_CHEMICAL_WEAPON,
accuracy_decimals=0,
state_class=STATE_CLASS_MEASUREMENT,
).extend(
{
cv.Required(CONF_CALCULATION_TYPE): cv.enum(

View File

@ -6,7 +6,6 @@ from esphome.const import (
CONF_ID,
CONF_OVERSAMPLING,
CONF_RANGE,
DEVICE_CLASS_EMPTY,
ICON_MAGNET,
STATE_CLASS_MEASUREMENT,
STATE_CLASS_NONE,
@ -80,10 +79,16 @@ def validate_enum(enum_values, units=None, int=True):
field_strength_schema = sensor.sensor_schema(
UNIT_MICROTESLA, ICON_MAGNET, 1, DEVICE_CLASS_EMPTY, STATE_CLASS_MEASUREMENT
unit_of_measurement=UNIT_MICROTESLA,
icon=ICON_MAGNET,
accuracy_decimals=1,
state_class=STATE_CLASS_MEASUREMENT,
)
heading_schema = sensor.sensor_schema(
UNIT_DEGREES, ICON_SCREEN_ROTATION, 1, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE
unit_of_measurement=UNIT_DEGREES,
icon=ICON_SCREEN_ROTATION,
accuracy_decimals=1,
state_class=STATE_CLASS_NONE,
)
CONFIG_SCHEMA = (

View File

@ -5,10 +5,7 @@ from esphome.const import (
CONF_ATTRIBUTE,
CONF_ENTITY_ID,
CONF_ID,
ICON_EMPTY,
STATE_CLASS_NONE,
UNIT_EMPTY,
DEVICE_CLASS_EMPTY,
)
from .. import homeassistant_ns
@ -19,7 +16,8 @@ HomeassistantSensor = homeassistant_ns.class_(
)
CONFIG_SCHEMA = sensor.sensor_schema(
UNIT_EMPTY, ICON_EMPTY, 1, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE
accuracy_decimals=1,
state_class=STATE_CLASS_NONE,
).extend(
{
cv.GenerateID(): cv.declare_id(HomeassistantSensor),

View File

@ -0,0 +1,66 @@
// Official Datasheet:
// https://www.maxbotix.com/documents/HRXL-MaxSonar-WR_Datasheet.pdf
//
// This implementation is designed to work with the TTL Versions of the
// MaxBotix HRXL MaxSonar WR sensor series. The sensor's TTL Pin (5) should be
// wired to one of the ESP's input pins and configured as uart rx_pin.
#include "hrxl_maxsonar_wr.h"
#include "esphome/core/log.h"
namespace esphome {
namespace hrxl_maxsonar_wr {
static const char *const TAG = "hrxl.maxsonar.wr.sensor";
static const uint8_t ASCII_CR = 0x0D;
static const uint8_t ASCII_NBSP = 0xFF;
static const int MAX_DATA_LENGTH_BYTES = 6;
/**
* The sensor outputs something like "R1234\r" at a fixed rate of 6 Hz. Where
* 1234 means a distance of 1,234 m.
*/
void HrxlMaxsonarWrComponent::loop() {
uint8_t data;
while (this->available() > 0) {
if (this->read_byte(&data)) {
buffer_ += (char) data;
this->check_buffer_();
}
}
}
void HrxlMaxsonarWrComponent::check_buffer_() {
// The sensor seems to inject a rogue ASCII 255 byte from time to time. Get rid of that.
if (this->buffer_.back() == static_cast<char>(ASCII_NBSP)) {
this->buffer_.pop_back();
return;
}
// Stop reading at ASCII_CR. Also prevent the buffer from growing
// indefinitely if no ASCII_CR is received after MAX_DATA_LENGTH_BYTES.
if (this->buffer_.back() == static_cast<char>(ASCII_CR) || this->buffer_.length() >= MAX_DATA_LENGTH_BYTES) {
ESP_LOGV(TAG, "Read from serial: %s", this->buffer_.c_str());
if (this->buffer_.length() == MAX_DATA_LENGTH_BYTES && this->buffer_[0] == 'R' &&
this->buffer_.back() == static_cast<char>(ASCII_CR)) {
int millimeters = strtol(this->buffer_.substr(1, MAX_DATA_LENGTH_BYTES - 2).c_str(), nullptr, 10);
float meters = float(millimeters) / 1000.0;
ESP_LOGV(TAG, "Distance from sensor: %d mm, %f m", millimeters, meters);
this->publish_state(meters);
} else {
ESP_LOGW(TAG, "Invalid data read from sensor: %s", this->buffer_.c_str());
}
this->buffer_.clear();
}
}
void HrxlMaxsonarWrComponent::dump_config() {
ESP_LOGCONFIG(TAG, "HRXL MaxSonar WR Sensor:");
LOG_SENSOR(" ", "Distance", this);
// As specified in the sensor's data sheet
this->check_uart_settings(9600, 1, esphome::uart::UART_CONFIG_PARITY_NONE, 8);
}
} // namespace hrxl_maxsonar_wr
} // namespace esphome

View File

@ -0,0 +1,25 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/uart/uart.h"
namespace esphome {
namespace hrxl_maxsonar_wr {
class HrxlMaxsonarWrComponent : public sensor::Sensor, public Component, public uart::UARTDevice {
public:
// Nothing really public.
// ========== INTERNAL METHODS ==========
void loop() override;
void dump_config() override;
protected:
void check_buffer_();
std::string buffer_;
};
} // namespace hrxl_maxsonar_wr
} // namespace esphome

View File

@ -0,0 +1,39 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor, uart
from esphome.const import (
CONF_ID,
STATE_CLASS_MEASUREMENT,
UNIT_METER,
ICON_ARROW_EXPAND_VERTICAL,
)
CODEOWNERS = ["@netmikey"]
DEPENDENCIES = ["uart"]
hrxlmaxsonarwr_ns = cg.esphome_ns.namespace("hrxl_maxsonar_wr")
HrxlMaxsonarWrComponent = hrxlmaxsonarwr_ns.class_(
"HrxlMaxsonarWrComponent", sensor.Sensor, cg.Component, uart.UARTDevice
)
CONFIG_SCHEMA = (
sensor.sensor_schema(
unit_of_measurement=UNIT_METER,
icon=ICON_ARROW_EXPAND_VERTICAL,
accuracy_decimals=3,
state_class=STATE_CLASS_MEASUREMENT,
)
.extend(
{
cv.GenerateID(): cv.declare_id(HrxlMaxsonarWrComponent),
}
)
.extend(uart.UART_DEVICE_SCHEMA)
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await sensor.register_sensor(var, config)
await uart.register_uart_device(var, config)

View File

@ -7,7 +7,6 @@ from esphome.const import (
CONF_TEMPERATURE,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_TEMPERATURE,
ICON_EMPTY,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
UNIT_PERCENT,
@ -25,18 +24,16 @@ CONFIG_SCHEMA = (
{
cv.GenerateID(): cv.declare_id(HTU21DComponent),
cv.Required(CONF_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS,
ICON_EMPTY,
1,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Required(CONF_HUMIDITY): sensor.sensor_schema(
UNIT_PERCENT,
ICON_EMPTY,
1,
DEVICE_CLASS_HUMIDITY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_PERCENT,
accuracy_decimals=1,
device_class=DEVICE_CLASS_HUMIDITY,
state_class=STATE_CLASS_MEASUREMENT,
),
}
)

View File

@ -6,10 +6,8 @@ from esphome.const import (
CONF_CLK_PIN,
CONF_GAIN,
CONF_ID,
DEVICE_CLASS_EMPTY,
ICON_SCALE,
STATE_CLASS_MEASUREMENT,
UNIT_EMPTY,
)
hx711_ns = cg.esphome_ns.namespace("hx711")
@ -26,7 +24,9 @@ GAINS = {
CONFIG_SCHEMA = (
sensor.sensor_schema(
UNIT_EMPTY, ICON_SCALE, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_MEASUREMENT
icon=ICON_SCALE,
accuracy_decimals=0,
state_class=STATE_CLASS_MEASUREMENT,
)
.extend(
{

View File

@ -225,7 +225,7 @@ void ILI9341M5Stack::initialize() {
this->width_ = 320;
this->height_ = 240;
this->invert_display_(true);
this->fill_internal_(COLOR_BLACK);
this->fill_internal_(Color::BLACK);
}
// 24_TFT display
@ -233,7 +233,7 @@ void ILI9341TFT24::initialize() {
this->init_lcd_(INITCMD_TFT);
this->width_ = 240;
this->height_ = 320;
this->fill_internal_(COLOR_BLACK);
this->fill_internal_(Color::BLACK);
}
} // namespace ili9341

View File

@ -13,7 +13,6 @@ from esphome.const import (
DEVICE_CLASS_CURRENT,
DEVICE_CLASS_POWER,
DEVICE_CLASS_VOLTAGE,
ICON_EMPTY,
STATE_CLASS_MEASUREMENT,
UNIT_VOLT,
UNIT_AMPERE,
@ -32,20 +31,28 @@ CONFIG_SCHEMA = (
{
cv.GenerateID(): cv.declare_id(INA219Component),
cv.Optional(CONF_BUS_VOLTAGE): 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,
),
cv.Optional(CONF_SHUNT_VOLTAGE): 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,
),
cv.Optional(CONF_CURRENT): sensor.sensor_schema(
UNIT_AMPERE,
ICON_EMPTY,
3,
DEVICE_CLASS_CURRENT,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_AMPERE,
accuracy_decimals=3,
device_class=DEVICE_CLASS_CURRENT,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_POWER): sensor.sensor_schema(
UNIT_WATT, ICON_EMPTY, 2, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
unit_of_measurement=UNIT_WATT,
accuracy_decimals=2,
device_class=DEVICE_CLASS_POWER,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_SHUNT_RESISTANCE, default=0.1): cv.All(
cv.resistance, cv.Range(min=0.0, max=32.0)

View File

@ -12,7 +12,6 @@ from esphome.const import (
DEVICE_CLASS_VOLTAGE,
DEVICE_CLASS_CURRENT,
DEVICE_CLASS_POWER,
ICON_EMPTY,
STATE_CLASS_MEASUREMENT,
UNIT_VOLT,
UNIT_AMPERE,
@ -31,20 +30,28 @@ CONFIG_SCHEMA = (
{
cv.GenerateID(): cv.declare_id(INA226Component),
cv.Optional(CONF_BUS_VOLTAGE): 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,
),
cv.Optional(CONF_SHUNT_VOLTAGE): 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,
),
cv.Optional(CONF_CURRENT): sensor.sensor_schema(
UNIT_AMPERE,
ICON_EMPTY,
3,
DEVICE_CLASS_CURRENT,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_AMPERE,
accuracy_decimals=3,
device_class=DEVICE_CLASS_CURRENT,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_POWER): sensor.sensor_schema(
UNIT_WATT, ICON_EMPTY, 2, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
unit_of_measurement=UNIT_WATT,
accuracy_decimals=2,
device_class=DEVICE_CLASS_POWER,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_SHUNT_RESISTANCE, default=0.1): cv.All(
cv.resistance, cv.Range(min=0.0)

View File

@ -11,7 +11,6 @@ from esphome.const import (
DEVICE_CLASS_VOLTAGE,
DEVICE_CLASS_CURRENT,
DEVICE_CLASS_POWER,
ICON_EMPTY,
STATE_CLASS_MEASUREMENT,
UNIT_VOLT,
UNIT_AMPERE,
@ -32,16 +31,28 @@ INA3221Component = ina3221_ns.class_(
INA3221_CHANNEL_SCHEMA = cv.Schema(
{
cv.Optional(CONF_BUS_VOLTAGE): 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,
),
cv.Optional(CONF_SHUNT_VOLTAGE): 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,
),
cv.Optional(CONF_CURRENT): sensor.sensor_schema(
UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT, STATE_CLASS_MEASUREMENT
unit_of_measurement=UNIT_AMPERE,
accuracy_decimals=2,
device_class=DEVICE_CLASS_CURRENT,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_POWER): sensor.sensor_schema(
UNIT_WATT, ICON_EMPTY, 2, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
unit_of_measurement=UNIT_WATT,
accuracy_decimals=2,
device_class=DEVICE_CLASS_POWER,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_SHUNT_RESISTANCE, default=0.1): cv.All(
cv.resistance, cv.Range(min=0.0, max=32.0)

View File

@ -23,9 +23,9 @@ bool InkbirdIBSTH1_MINI::parse_device(const esp32_ble_tracker::ESPBTDevice &devi
// for Inkbird IBS-TH1 Mini device we expect
// 1) expected mac address
// 2) device address type == PUBLIC
// 3) no service datas
// 4) one manufacturer datas
// 5) the manufacturer datas should contain a 16-bit uuid amd a 7-byte data vector
// 3) no service data
// 4) one manufacturer data
// 5) the manufacturer data should contain a 16-bit uuid amd a 7-byte data vector
// 6) the 7-byte data component should have data[2] == 0 and data[6] == 8
// the address should match the address we declared
@ -63,7 +63,7 @@ bool InkbirdIBSTH1_MINI::parse_device(const esp32_ble_tracker::ESPBTDevice &devi
// sensor output encoding
// data[5] is a battery level
// data[0] and data[1] is humidity * 100 (in pct)
// uuid is a temperature * 100 (in Celcius)
// uuid is a temperature * 100 (in Celsius)
// when data[2] == 0 temperature is from internal sensor (IBS-TH1 or IBS-TH1 Mini)
// when data[2] == 1 temperature is from external sensor (IBS-TH1 only)

View File

@ -9,7 +9,6 @@ from esphome.const import (
DEVICE_CLASS_BATTERY,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_TEMPERATURE,
ICON_EMPTY,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
UNIT_PERCENT,
@ -32,32 +31,28 @@ CONFIG_SCHEMA = (
cv.GenerateID(): cv.declare_id(InkbirdUBSTH1_MINI),
cv.Required(CONF_MAC_ADDRESS): cv.mac_address,
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS,
ICON_EMPTY,
1,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_EXTERNAL_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS,
ICON_EMPTY,
1,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
UNIT_PERCENT,
ICON_EMPTY,
1,
DEVICE_CLASS_HUMIDITY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_PERCENT,
accuracy_decimals=1,
device_class=DEVICE_CLASS_HUMIDITY,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(
UNIT_PERCENT,
ICON_EMPTY,
0,
DEVICE_CLASS_BATTERY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_PERCENT,
accuracy_decimals=0,
device_class=DEVICE_CLASS_BATTERY,
state_class=STATE_CLASS_MEASUREMENT,
),
}
)

View File

@ -16,6 +16,8 @@ void IntegrationSensor::setup() {
}
this->last_update_ = millis();
this->last_save_ = this->last_update_;
this->publish_and_save_(this->result_);
this->sensor_->add_on_state_callback([this](float state) { this->process_sensor_value_(state); });
}

View File

@ -27,6 +27,7 @@ class IntegrationSensor : public sensor::Sensor, public Component {
void setup() override;
void dump_config() override;
float get_setup_priority() const override { return setup_priority::DATA; }
void set_min_save_interval(uint32_t min_interval) { this->min_save_interval_ = min_interval; }
void set_sensor(Sensor *sensor) { sensor_ = sensor; }
void set_time(IntegrationSensorTime time) { time_ = time; }
void set_method(IntegrationMethod method) { method_ = method; }
@ -55,6 +56,10 @@ class IntegrationSensor : public sensor::Sensor, public Component {
this->result_ = result;
this->publish_state(result);
float result_f = result;
const uint32_t now = millis();
if (now - this->last_save_ < this->min_save_interval_)
return;
this->last_save_ = now;
this->rtc_.save(&result_f);
}
std::string unit_of_measurement() override;
@ -67,6 +72,8 @@ class IntegrationSensor : public sensor::Sensor, public Component {
bool restore_;
ESPPreferenceObject rtc_;
uint32_t last_save_{0};
uint32_t min_save_interval_{0};
uint32_t last_update_;
double result_{0.0f};
float last_value_{0.0f};

View File

@ -27,6 +27,8 @@ INTEGRATION_METHODS = {
CONF_TIME_UNIT = "time_unit"
CONF_INTEGRATION_METHOD = "integration_method"
CONF_MIN_SAVE_INTERVAL = "min_save_interval"
CONFIG_SCHEMA = sensor.SENSOR_SCHEMA.extend(
{
@ -37,6 +39,9 @@ CONFIG_SCHEMA = sensor.SENSOR_SCHEMA.extend(
INTEGRATION_METHODS, lower=True
),
cv.Optional(CONF_RESTORE, default=False): cv.boolean,
cv.Optional(
CONF_MIN_SAVE_INTERVAL, default="0s"
): cv.positive_time_period_milliseconds,
}
).extend(cv.COMPONENT_SCHEMA)
@ -52,6 +57,7 @@ async def to_code(config):
cg.add(var.set_time(config[CONF_TIME_UNIT]))
cg.add(var.set_method(config[CONF_INTEGRATION_METHOD]))
cg.add(var.set_restore(config[CONF_RESTORE]))
cg.add(var.set_min_save_interval(config[CONF_MIN_SAVE_INTERVAL]))
@automation.register_action(

View File

@ -5,6 +5,7 @@ from esphome.components import mqtt, power_supply
from esphome.const import (
CONF_COLOR_CORRECT,
CONF_DEFAULT_TRANSITION_LENGTH,
CONF_DISABLED_BY_DEFAULT,
CONF_EFFECTS,
CONF_GAMMA_CORRECT,
CONF_ID,
@ -52,7 +53,7 @@ RESTORE_MODES = {
"RESTORE_INVERTED_DEFAULT_ON": LightRestoreMode.LIGHT_RESTORE_INVERTED_DEFAULT_ON,
}
LIGHT_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend(
LIGHT_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
{
cv.GenerateID(): cv.declare_id(LightState),
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTJSONLightComponent),
@ -108,7 +109,9 @@ ADDRESSABLE_LIGHT_SCHEMA = RGB_LIGHT_SCHEMA.extend(
def validate_color_temperature_channels(value):
if (
value[CONF_COLD_WHITE_COLOR_TEMPERATURE]
CONF_COLD_WHITE_COLOR_TEMPERATURE in value
and CONF_WARM_WHITE_COLOR_TEMPERATURE in value
and value[CONF_COLD_WHITE_COLOR_TEMPERATURE]
>= value[CONF_WARM_WHITE_COLOR_TEMPERATURE]
):
raise cv.Invalid(
@ -119,6 +122,7 @@ def validate_color_temperature_channels(value):
async def setup_light_core_(light_var, output_var, config):
cg.add(light_var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT]))
cg.add(light_var.set_restore_mode(config[CONF_RESTORE_MODE]))
if CONF_INTERNAL in config:
cg.add(light_var.set_internal(config[CONF_INTERNAL]))

View File

@ -24,79 +24,84 @@ void AddressableLight::call_setup() {
#endif
}
std::unique_ptr<LightTransformer> AddressableLight::create_default_transition() {
return make_unique<AddressableLightTransformer>(*this);
}
Color esp_color_from_light_color_values(LightColorValues val) {
auto r = static_cast<uint8_t>(roundf(val.get_color_brightness() * val.get_red() * 255.0f));
auto g = static_cast<uint8_t>(roundf(val.get_color_brightness() * val.get_green() * 255.0f));
auto b = static_cast<uint8_t>(roundf(val.get_color_brightness() * val.get_blue() * 255.0f));
auto w = static_cast<uint8_t>(roundf(val.get_white() * 255.0f));
auto r = to_uint8_scale(val.get_color_brightness() * val.get_red());
auto g = to_uint8_scale(val.get_color_brightness() * val.get_green());
auto b = to_uint8_scale(val.get_color_brightness() * val.get_blue());
auto w = to_uint8_scale(val.get_white());
return Color(r, g, b, w);
}
void AddressableLight::write_state(LightState *state) {
auto val = state->current_values;
auto max_brightness = static_cast<uint8_t>(roundf(val.get_brightness() * val.get_state() * 255.0f));
auto max_brightness = to_uint8_scale(val.get_brightness() * val.get_state());
this->correction_.set_local_brightness(max_brightness);
this->last_transition_progress_ = 0.0f;
this->accumulated_alpha_ = 0.0f;
if (this->is_effect_active())
return;
// don't use LightState helper, gamma correction+brightness is handled by ESPColorView
this->all() = esp_color_from_light_color_values(val);
}
if (state->transformer_ == nullptr || !state->transformer_->is_transition()) {
// no transformer active or non-transition one
this->all() = esp_color_from_light_color_values(val);
} else {
// transition transformer active, activate specialized transition for addressable effects
// instead of using a unified transition for all LEDs, we use the current state each LED as the
// start. Warning: ugly
void AddressableLightTransformer::start() {
auto end_values = this->target_values_;
this->target_color_ = esp_color_from_light_color_values(end_values);
// We can't use a direct lerp smoothing here though - that would require creating a copy of the original
// state of each LED at the start of the transition
// Instead, we "fake" the look of the LERP by using an exponential average over time and using
// dynamically-calculated alpha values to match the look of the
// our transition will handle brightness, disable brightness in correction.
this->light_.correction_.set_local_brightness(255);
this->target_color_ *= to_uint8_scale(end_values.get_brightness() * end_values.get_state());
}
float new_progress = state->transformer_->get_progress();
float prev_smoothed = LightTransitionTransformer::smoothed_progress(last_transition_progress_);
float new_smoothed = LightTransitionTransformer::smoothed_progress(new_progress);
this->last_transition_progress_ = new_progress;
optional<LightColorValues> AddressableLightTransformer::apply() {
// Don't try to transition over running effects, instead immediately use the target values. write_state() and the
// effects pick up the change from current_values.
if (this->light_.is_effect_active())
return this->target_values_;
auto end_values = state->transformer_->get_end_values();
Color target_color = esp_color_from_light_color_values(end_values);
// Use a specialized transition for addressable lights: instead of using a unified transition for
// all LEDs, we use the current state of each LED as the start.
// our transition will handle brightness, disable brightness in correction.
this->correction_.set_local_brightness(255);
target_color *= static_cast<uint8_t>(roundf(end_values.get_brightness() * end_values.get_state() * 255.0f));
// We can't use a direct lerp smoothing here though - that would require creating a copy of the original
// state of each LED at the start of the transition.
// Instead, we "fake" the look of the LERP by using an exponential average over time and using
// dynamically-calculated alpha values to match the look.
float denom = (1.0f - new_smoothed);
float alpha = denom == 0.0f ? 0.0f : (new_smoothed - prev_smoothed) / denom;
float smoothed_progress = LightTransitionTransformer::smoothed_progress(this->get_progress_());
// We need to use a low-resolution alpha here which makes the transition set in only after ~half of the length
// We solve this by accumulating the fractional part of the alpha over time.
float alpha255 = alpha * 255.0f;
float alpha255int = floorf(alpha255);
float alpha255remainder = alpha255 - alpha255int;
float denom = (1.0f - smoothed_progress);
float alpha = denom == 0.0f ? 0.0f : (smoothed_progress - this->last_transition_progress_) / denom;
this->accumulated_alpha_ += alpha255remainder;
float alpha_add = floorf(this->accumulated_alpha_);
this->accumulated_alpha_ -= alpha_add;
// We need to use a low-resolution alpha here which makes the transition set in only after ~half of the length
// We solve this by accumulating the fractional part of the alpha over time.
float alpha255 = alpha * 255.0f;
float alpha255int = floorf(alpha255);
float alpha255remainder = alpha255 - alpha255int;
alpha255 += alpha_add;
alpha255 = clamp(alpha255, 0.0f, 255.0f);
auto alpha8 = static_cast<uint8_t>(alpha255);
this->accumulated_alpha_ += alpha255remainder;
float alpha_add = floorf(this->accumulated_alpha_);
this->accumulated_alpha_ -= alpha_add;
if (alpha8 != 0) {
uint8_t inv_alpha8 = 255 - alpha8;
Color add = target_color * alpha8;
alpha255 += alpha_add;
alpha255 = clamp(alpha255, 0.0f, 255.0f);
auto alpha8 = static_cast<uint8_t>(alpha255);
for (auto led : *this)
led = add + led.get() * inv_alpha8;
}
if (alpha8 != 0) {
uint8_t inv_alpha8 = 255 - alpha8;
Color add = this->target_color_ * alpha8;
for (auto led : this->light_)
led.set(add + led.get() * inv_alpha8);
}
this->schedule_show();
this->last_transition_progress_ = smoothed_progress;
this->light_.schedule_show();
return {};
}
} // namespace light

View File

@ -8,6 +8,7 @@
#include "esp_range_view.h"
#include "light_output.h"
#include "light_state.h"
#include "transformers.h"
#ifdef USE_POWER_SUPPLY
#include "esphome/components/power_supply/power_supply.h"
@ -16,7 +17,7 @@
namespace esphome {
namespace light {
using ESPColor = Color;
using ESPColor ESPDEPRECATED("esphome::light::ESPColor is deprecated, use esphome::Color instead.", "v1.21") = Color;
class AddressableLight : public LightOutput, public Component {
public:
@ -53,9 +54,10 @@ class AddressableLight : public LightOutput, public Component {
bool is_effect_active() const { return this->effect_active_; }
void set_effect_active(bool effect_active) { this->effect_active_ = effect_active; }
void write_state(LightState *state) override;
std::unique_ptr<LightTransformer> create_default_transition() override;
void set_correction(float red, float green, float blue, float white = 1.0f) {
this->correction_.set_max_brightness(Color(uint8_t(roundf(red * 255.0f)), uint8_t(roundf(green * 255.0f)),
uint8_t(roundf(blue * 255.0f)), uint8_t(roundf(white * 255.0f))));
this->correction_.set_max_brightness(
Color(to_uint8_scale(red), to_uint8_scale(green), to_uint8_scale(blue), to_uint8_scale(white)));
}
void setup_state(LightState *state) override {
this->correction_.calculate_gamma_table(state->get_gamma_correct());
@ -70,6 +72,8 @@ class AddressableLight : public LightOutput, public Component {
void call_setup() override;
protected:
friend class AddressableLightTransformer;
bool should_show_() const { return this->effect_active_ || this->next_show_; }
void mark_shown_() {
this->next_show_ = false;
@ -92,6 +96,18 @@ class AddressableLight : public LightOutput, public Component {
power_supply::PowerSupplyRequester power_;
#endif
LightState *state_parent_{nullptr};
};
class AddressableLightTransformer : public LightTransitionTransformer {
public:
AddressableLightTransformer(AddressableLight &light) : light_(light) {}
void start() override;
optional<LightColorValues> apply() override;
protected:
AddressableLight &light_;
Color target_color_{};
float last_transition_progress_{0.0f};
float accumulated_alpha_{0.0f};
};

View File

@ -151,7 +151,7 @@ class AddressableScanEffect : public AddressableLightEffect {
void set_move_interval(uint32_t move_interval) { this->move_interval_ = move_interval; }
void set_scan_width(uint32_t scan_width) { this->scan_width_ = scan_width; }
void apply(AddressableLight &it, const Color &current_color) override {
it.all() = COLOR_BLACK;
it.all() = Color::BLACK;
for (auto i = 0; i < this->scan_width_; i++) {
it[this->at_led_ + i] = current_color;
@ -201,7 +201,7 @@ class AddressableTwinkleEffect : public AddressableLightEffect {
else
view.set_effect_data(new_pos);
} else {
view = COLOR_BLACK;
view = Color::BLACK;
}
}
while (random_float() < this->twinkle_probability_) {
@ -272,7 +272,7 @@ class AddressableFireworksEffect : public AddressableLightEffect {
explicit AddressableFireworksEffect(const std::string &name) : AddressableLightEffect(name) {}
void start() override {
auto &it = *this->get_addressable_();
it.all() = COLOR_BLACK;
it.all() = Color::BLACK;
}
void apply(AddressableLight &it, const Color &current_color) override {
const uint32_t now = millis();
@ -337,7 +337,7 @@ class AddressableFlickerEffect : public AddressableLightEffect {
}
}
void set_update_interval(uint32_t update_interval) { this->update_interval_ = update_interval; }
void set_intensity(float intensity) { this->intensity_ = static_cast<uint8_t>(roundf(intensity * 255.0f)); }
void set_intensity(float intensity) { this->intensity_ = to_uint8_scale(intensity); }
protected:
uint32_t update_interval_{16};

View File

@ -0,0 +1,15 @@
#include "automation.h"
#include "esphome/core/log.h"
namespace esphome {
namespace light {
static const char *const TAG = "light.automation";
void addressableset_warn_about_scale(const char *field) {
ESP_LOGW(TAG, "Lambda for parameter %s of light.addressable_set should return values in range 0-1 instead of 0-255.",
field);
}
} // namespace light
} // namespace esphome

View File

@ -27,6 +27,7 @@ template<typename... Ts> class LightControlAction : public Action<Ts...> {
public:
explicit LightControlAction(LightState *parent) : parent_(parent) {}
TEMPLATABLE_VALUE(ColorMode, color_mode)
TEMPLATABLE_VALUE(bool, state)
TEMPLATABLE_VALUE(uint32_t, transition_length)
TEMPLATABLE_VALUE(uint32_t, flash_length)
@ -37,10 +38,13 @@ template<typename... Ts> class LightControlAction : public Action<Ts...> {
TEMPLATABLE_VALUE(float, blue)
TEMPLATABLE_VALUE(float, white)
TEMPLATABLE_VALUE(float, color_temperature)
TEMPLATABLE_VALUE(float, cold_white)
TEMPLATABLE_VALUE(float, warm_white)
TEMPLATABLE_VALUE(std::string, effect)
void play(Ts... x) override {
auto call = this->parent_->make_call();
call.set_color_mode(this->color_mode_.optional_value(x...));
call.set_state(this->state_.optional_value(x...));
call.set_brightness(this->brightness_.optional_value(x...));
call.set_color_brightness(this->color_brightness_.optional_value(x...));
@ -49,6 +53,8 @@ template<typename... Ts> class LightControlAction : public Action<Ts...> {
call.set_blue(this->blue_.optional_value(x...));
call.set_white(this->white_.optional_value(x...));
call.set_color_temperature(this->color_temperature_.optional_value(x...));
call.set_cold_white(this->cold_white_.optional_value(x...));
call.set_warm_white(this->warm_white_.optional_value(x...));
call.set_effect(this->effect_.optional_value(x...));
call.set_flash_length(this->flash_length_.optional_value(x...));
call.set_transition_length(this->transition_length_.optional_value(x...));
@ -135,39 +141,57 @@ class LightTurnOffTrigger : public Trigger<> {
}
};
// This is slightly ugly, but we can't log in headers, and can't make this a static method on AddressableSet
// due to the template. It's just a temporary warning anyway.
void addressableset_warn_about_scale(const char *field);
template<typename... Ts> class AddressableSet : public Action<Ts...> {
public:
explicit AddressableSet(LightState *parent) : parent_(parent) {}
TEMPLATABLE_VALUE(int32_t, range_from)
TEMPLATABLE_VALUE(int32_t, range_to)
TEMPLATABLE_VALUE(uint8_t, color_brightness)
TEMPLATABLE_VALUE(uint8_t, red)
TEMPLATABLE_VALUE(uint8_t, green)
TEMPLATABLE_VALUE(uint8_t, blue)
TEMPLATABLE_VALUE(uint8_t, white)
TEMPLATABLE_VALUE(float, color_brightness)
TEMPLATABLE_VALUE(float, red)
TEMPLATABLE_VALUE(float, green)
TEMPLATABLE_VALUE(float, blue)
TEMPLATABLE_VALUE(float, white)
void play(Ts... x) override {
auto *out = (AddressableLight *) this->parent_->get_output();
int32_t range_from = this->range_from_.value_or(x..., 0);
int32_t range_to = this->range_to_.value_or(x..., out->size() - 1) + 1;
uint8_t remote_color_brightness =
static_cast<uint8_t>(roundf(this->parent_->remote_values.get_color_brightness() * 255.0f));
uint8_t color_brightness = this->color_brightness_.value_or(x..., remote_color_brightness);
int32_t range_from = interpret_index(this->range_from_.value_or(x..., 0), out->size());
if (range_from < 0 || range_from >= out->size())
range_from = 0;
int32_t range_to = interpret_index(this->range_to_.value_or(x..., out->size() - 1) + 1, out->size());
if (range_to < 0 || range_to >= out->size())
range_to = out->size();
uint8_t color_brightness =
to_uint8_scale(this->color_brightness_.value_or(x..., this->parent_->remote_values.get_color_brightness()));
auto range = out->range(range_from, range_to);
if (this->red_.has_value())
range.set_red(esp_scale8(this->red_.value(x...), color_brightness));
range.set_red(esp_scale8(to_uint8_compat(this->red_.value(x...), "red"), color_brightness));
if (this->green_.has_value())
range.set_green(esp_scale8(this->green_.value(x...), color_brightness));
range.set_green(esp_scale8(to_uint8_compat(this->green_.value(x...), "green"), color_brightness));
if (this->blue_.has_value())
range.set_blue(esp_scale8(this->blue_.value(x...), color_brightness));
range.set_blue(esp_scale8(to_uint8_compat(this->blue_.value(x...), "blue"), color_brightness));
if (this->white_.has_value())
range.set_white(this->white_.value(x...));
range.set_white(to_uint8_compat(this->white_.value(x...), "white"));
out->schedule_show();
}
protected:
LightState *parent_;
// Historically, this action required uint8_t (0-255) for RGBW values from lambdas. Keep compatibility.
static inline uint8_t to_uint8_compat(float value, const char *field) {
if (value > 1.0f) {
addressableset_warn_about_scale(field);
return static_cast<uint8_t>(value);
}
return to_uint8_scale(value);
}
};
} // namespace light

View File

@ -3,6 +3,7 @@ import esphome.config_validation as cv
from esphome import automation
from esphome.const import (
CONF_ID,
CONF_COLOR_MODE,
CONF_TRANSITION_LENGTH,
CONF_STATE,
CONF_FLASH_LENGTH,
@ -14,10 +15,14 @@ from esphome.const import (
CONF_BLUE,
CONF_WHITE,
CONF_COLOR_TEMPERATURE,
CONF_COLD_WHITE,
CONF_WARM_WHITE,
CONF_RANGE_FROM,
CONF_RANGE_TO,
)
from .types import (
ColorMode,
COLOR_MODES,
DimRelativeAction,
ToggleAction,
LightState,
@ -55,6 +60,7 @@ async def light_toggle_to_code(config, action_id, template_arg, args):
LIGHT_CONTROL_ACTION_SCHEMA = cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(LightState),
cv.Optional(CONF_COLOR_MODE): cv.enum(COLOR_MODES, upper=True, space="_"),
cv.Optional(CONF_STATE): cv.templatable(cv.boolean),
cv.Exclusive(CONF_TRANSITION_LENGTH, "transformer"): cv.templatable(
cv.positive_time_period_milliseconds
@ -70,6 +76,8 @@ LIGHT_CONTROL_ACTION_SCHEMA = cv.Schema(
cv.Optional(CONF_BLUE): cv.templatable(cv.percentage),
cv.Optional(CONF_WHITE): cv.templatable(cv.percentage),
cv.Optional(CONF_COLOR_TEMPERATURE): cv.templatable(cv.color_temperature),
cv.Optional(CONF_COLD_WHITE): cv.templatable(cv.percentage),
cv.Optional(CONF_WARM_WHITE): cv.templatable(cv.percentage),
}
)
LIGHT_TURN_OFF_ACTION_SCHEMA = automation.maybe_simple_id(
@ -102,6 +110,9 @@ LIGHT_TURN_ON_ACTION_SCHEMA = automation.maybe_simple_id(
async def light_control_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren)
if CONF_COLOR_MODE in config:
template_ = await cg.templatable(config[CONF_COLOR_MODE], args, ColorMode)
cg.add(var.set_color_mode(template_))
if CONF_STATE in config:
template_ = await cg.templatable(config[CONF_STATE], args, bool)
cg.add(var.set_state(template_))
@ -134,6 +145,12 @@ async def light_control_to_code(config, action_id, template_arg, args):
if CONF_COLOR_TEMPERATURE in config:
template_ = await cg.templatable(config[CONF_COLOR_TEMPERATURE], args, float)
cg.add(var.set_color_temperature(template_))
if CONF_COLD_WHITE in config:
template_ = await cg.templatable(config[CONF_COLD_WHITE], args, float)
cg.add(var.set_cold_white(template_))
if CONF_WARM_WHITE in config:
template_ = await cg.templatable(config[CONF_WARM_WHITE], args, float)
cg.add(var.set_warm_white(template_))
if CONF_EFFECT in config:
template_ = await cg.templatable(config[CONF_EFFECT], args, cg.std_string)
cg.add(var.set_effect(template_))
@ -173,6 +190,7 @@ LIGHT_ADDRESSABLE_SET_ACTION_SCHEMA = cv.Schema(
cv.Required(CONF_ID): cv.use_id(AddressableLightState),
cv.Optional(CONF_RANGE_FROM): cv.templatable(cv.positive_int),
cv.Optional(CONF_RANGE_TO): cv.templatable(cv.positive_int),
cv.Optional(CONF_COLOR_BRIGHTNESS): cv.templatable(cv.percentage),
cv.Optional(CONF_RED): cv.templatable(cv.percentage),
cv.Optional(CONF_GREEN): cv.templatable(cv.percentage),
cv.Optional(CONF_BLUE): cv.templatable(cv.percentage),
@ -194,28 +212,20 @@ async def light_addressable_set_to_code(config, action_id, template_arg, args):
templ = await cg.templatable(config[CONF_RANGE_TO], args, cg.int32)
cg.add(var.set_range_to(templ))
def rgbw_to_exp(x):
return int(round(x * 255))
if CONF_COLOR_BRIGHTNESS in config:
templ = await cg.templatable(config[CONF_COLOR_BRIGHTNESS], args, cg.float_)
cg.add(var.set_color_brightness(templ))
if CONF_RED in config:
templ = await cg.templatable(
config[CONF_RED], args, cg.uint8, to_exp=rgbw_to_exp
)
templ = await cg.templatable(config[CONF_RED], args, cg.float_)
cg.add(var.set_red(templ))
if CONF_GREEN in config:
templ = await cg.templatable(
config[CONF_GREEN], args, cg.uint8, to_exp=rgbw_to_exp
)
templ = await cg.templatable(config[CONF_GREEN], args, cg.float_)
cg.add(var.set_green(templ))
if CONF_BLUE in config:
templ = await cg.templatable(
config[CONF_BLUE], args, cg.uint8, to_exp=rgbw_to_exp
)
templ = await cg.templatable(config[CONF_BLUE], args, cg.float_)
cg.add(var.set_blue(templ))
if CONF_WHITE in config:
templ = await cg.templatable(
config[CONF_WHITE], args, cg.uint8, to_exp=rgbw_to_exp
)
templ = await cg.templatable(config[CONF_WHITE], args, cg.float_)
cg.add(var.set_white(templ))
return var

View File

@ -57,16 +57,31 @@ class RandomLightEffect : public LightEffect {
if (now - this->last_color_change_ < this->update_interval_) {
return;
}
auto color_mode = this->state_->remote_values.get_color_mode();
auto call = this->state_->turn_on();
if (this->state_->get_traits().get_supports_rgb()) {
call.set_red_if_supported(random_float());
call.set_green_if_supported(random_float());
call.set_blue_if_supported(random_float());
call.set_white_if_supported(random_float());
} else {
call.set_brightness_if_supported(random_float());
bool changed = false;
if (color_mode & ColorCapability::RGB) {
call.set_red(random_float());
call.set_green(random_float());
call.set_blue(random_float());
changed = true;
}
if (color_mode & ColorCapability::COLOR_TEMPERATURE) {
float min = this->state_->get_traits().get_min_mireds();
float max = this->state_->get_traits().get_max_mireds();
call.set_color_temperature(min + random_float() * (max - min));
changed = true;
}
if (color_mode & ColorCapability::COLD_WARM_WHITE) {
call.set_cold_white(random_float());
call.set_warm_white(random_float());
changed = true;
}
if (!changed) {
// only randomize brightness if there's no colored option available
call.set_brightness(random_float());
}
call.set_color_temperature_if_supported(random_float());
call.set_transition_length_if_supported(this->transition_length_);
call.set_publish(true);
call.set_save(false);
@ -142,7 +157,6 @@ class StrobeLightEffect : public LightEffect {
if (!color.is_on()) {
// Don't turn the light off, otherwise the light effect will be stopped
call.set_brightness_if_supported(0.0f);
call.set_white_if_supported(0.0f);
call.set_state(true);
}
call.set_publish(false);
@ -177,13 +191,16 @@ class FlickerLightEffect : public LightEffect {
out.set_green(remote.get_green() * beta + current.get_green() * alpha + (random_cubic_float() * this->intensity_));
out.set_blue(remote.get_blue() * beta + current.get_blue() * alpha + (random_cubic_float() * this->intensity_));
out.set_white(remote.get_white() * beta + current.get_white() * alpha + (random_cubic_float() * this->intensity_));
out.set_cold_white(remote.get_cold_white() * beta + current.get_cold_white() * alpha +
(random_cubic_float() * this->intensity_));
out.set_warm_white(remote.get_warm_white() * beta + current.get_warm_white() * alpha +
(random_cubic_float() * this->intensity_));
auto traits = this->state_->get_traits();
auto call = this->state_->make_call();
call.set_publish(false);
call.set_save(false);
if (traits.get_supports_brightness())
call.set_transition_length(0);
call.set_transition_length_if_supported(0);
call.from_light_color_values(out);
call.set_state(true);
call.perform();

View File

@ -0,0 +1,107 @@
#pragma once
#include <cstdint>
namespace esphome {
namespace light {
/// Color capabilities are the various outputs that a light has and that can be independently controlled by the user.
enum class ColorCapability : uint8_t {
/// Light can be turned on/off.
ON_OFF = 1 << 0,
/// Master brightness of the light can be controlled.
BRIGHTNESS = 1 << 1,
/// Brightness of white channel can be controlled separately from other channels.
WHITE = 1 << 2,
/// Color temperature can be controlled.
COLOR_TEMPERATURE = 1 << 3,
/// Brightness of cold and warm white output can be controlled.
COLD_WARM_WHITE = 1 << 4,
/// Color can be controlled using RGB format (includes a brightness control for the color).
RGB = 1 << 5
};
/// Helper class to allow bitwise operations on ColorCapability
class ColorCapabilityHelper {
public:
constexpr ColorCapabilityHelper(ColorCapability val) : val_(val) {}
constexpr operator ColorCapability() const { return val_; }
constexpr operator uint8_t() const { return static_cast<uint8_t>(val_); }
constexpr operator bool() const { return static_cast<uint8_t>(val_) != 0; }
protected:
ColorCapability val_;
};
constexpr ColorCapabilityHelper operator&(ColorCapability lhs, ColorCapability rhs) {
return static_cast<ColorCapability>(static_cast<uint8_t>(lhs) & static_cast<uint8_t>(rhs));
}
constexpr ColorCapabilityHelper operator&(ColorCapabilityHelper lhs, ColorCapability rhs) {
return static_cast<ColorCapability>(static_cast<uint8_t>(lhs) & static_cast<uint8_t>(rhs));
}
constexpr ColorCapabilityHelper operator|(ColorCapability lhs, ColorCapability rhs) {
return static_cast<ColorCapability>(static_cast<uint8_t>(lhs) | static_cast<uint8_t>(rhs));
}
constexpr ColorCapabilityHelper operator|(ColorCapabilityHelper lhs, ColorCapability rhs) {
return static_cast<ColorCapability>(static_cast<uint8_t>(lhs) | static_cast<uint8_t>(rhs));
}
/// Color modes are a combination of color capabilities that can be used at the same time.
enum class ColorMode : uint8_t {
/// No color mode configured (cannot be a supported mode, only active when light is off).
UNKNOWN = 0,
/// Only on/off control.
ON_OFF = (uint8_t) ColorCapability::ON_OFF,
/// Dimmable light.
BRIGHTNESS = (uint8_t) ColorCapability::BRIGHTNESS,
/// White output only (use only if the light also has another color mode such as RGB).
WHITE = (uint8_t)(ColorCapability::ON_OFF | ColorCapability::BRIGHTNESS | ColorCapability::WHITE),
/// Controllable color temperature output.
COLOR_TEMPERATURE =
(uint8_t)(ColorCapability::ON_OFF | ColorCapability::BRIGHTNESS | ColorCapability::COLOR_TEMPERATURE),
/// Cold and warm white output with individually controllable brightness.
COLD_WARM_WHITE = (uint8_t)(ColorCapability::ON_OFF | ColorCapability::BRIGHTNESS | ColorCapability::COLD_WARM_WHITE),
/// RGB color output.
RGB = (uint8_t)(ColorCapability::ON_OFF | ColorCapability::BRIGHTNESS | ColorCapability::RGB),
/// RGB color output and a separate white output.
RGB_WHITE =
(uint8_t)(ColorCapability::ON_OFF | ColorCapability::BRIGHTNESS | ColorCapability::RGB | ColorCapability::WHITE),
/// RGB color output and a separate white output with controllable color temperature.
RGB_COLOR_TEMPERATURE = (uint8_t)(ColorCapability::ON_OFF | ColorCapability::BRIGHTNESS | ColorCapability::RGB |
ColorCapability::WHITE | ColorCapability::COLOR_TEMPERATURE),
/// RGB color output, and separate cold and warm white outputs.
RGB_COLD_WARM_WHITE = (uint8_t)(ColorCapability::ON_OFF | ColorCapability::BRIGHTNESS | ColorCapability::RGB |
ColorCapability::COLD_WARM_WHITE),
};
/// Helper class to allow bitwise operations on ColorMode with ColorCapability
class ColorModeHelper {
public:
constexpr ColorModeHelper(ColorMode val) : val_(val) {}
constexpr operator ColorMode() const { return val_; }
constexpr operator uint8_t() const { return static_cast<uint8_t>(val_); }
constexpr operator bool() const { return static_cast<uint8_t>(val_) != 0; }
protected:
ColorMode val_;
};
constexpr ColorModeHelper operator&(ColorMode lhs, ColorMode rhs) {
return static_cast<ColorMode>(static_cast<uint8_t>(lhs) & static_cast<uint8_t>(rhs));
}
constexpr ColorModeHelper operator&(ColorMode lhs, ColorCapability rhs) {
return static_cast<ColorMode>(static_cast<uint8_t>(lhs) & static_cast<uint8_t>(rhs));
}
constexpr ColorModeHelper operator&(ColorModeHelper lhs, ColorMode rhs) {
return static_cast<ColorMode>(static_cast<uint8_t>(lhs) & static_cast<uint8_t>(rhs));
}
constexpr ColorModeHelper operator|(ColorMode lhs, ColorMode rhs) {
return static_cast<ColorMode>(static_cast<uint8_t>(lhs) | static_cast<uint8_t>(rhs));
}
constexpr ColorModeHelper operator|(ColorMode lhs, ColorCapability rhs) {
return static_cast<ColorMode>(static_cast<uint8_t>(lhs) | static_cast<uint8_t>(rhs));
}
constexpr ColorModeHelper operator|(ColorModeHelper lhs, ColorMode rhs) {
return static_cast<ColorMode>(static_cast<uint8_t>(lhs) | static_cast<uint8_t>(rhs));
}
} // namespace light
} // namespace esphome

View File

@ -12,11 +12,15 @@ from esphome.const import (
CONF_STATE,
CONF_DURATION,
CONF_BRIGHTNESS,
CONF_COLOR_MODE,
CONF_COLOR_BRIGHTNESS,
CONF_RED,
CONF_GREEN,
CONF_BLUE,
CONF_WHITE,
CONF_COLOR_TEMPERATURE,
CONF_COLD_WHITE,
CONF_WARM_WHITE,
CONF_ALPHA,
CONF_INTENSITY,
CONF_SPEED,
@ -27,6 +31,8 @@ from esphome.const import (
)
from esphome.util import Registry
from .types import (
ColorMode,
COLOR_MODES,
LambdaLightEffect,
PulseLightEffect,
RandomLightEffect,
@ -212,11 +218,17 @@ async def random_effect_to_code(config, effect_id):
{
cv.Optional(CONF_STATE, default=True): cv.boolean,
cv.Optional(CONF_BRIGHTNESS, default=1.0): cv.percentage,
cv.Optional(CONF_COLOR_MODE): cv.enum(
COLOR_MODES, upper=True, space="_"
),
cv.Optional(CONF_COLOR_BRIGHTNESS, default=1.0): cv.percentage,
cv.Optional(CONF_RED, default=1.0): cv.percentage,
cv.Optional(CONF_GREEN, default=1.0): cv.percentage,
cv.Optional(CONF_BLUE, default=1.0): cv.percentage,
cv.Optional(CONF_WHITE, default=1.0): cv.percentage,
cv.Optional(CONF_COLOR_TEMPERATURE): cv.color_temperature,
cv.Optional(CONF_COLD_WHITE, default=1.0): cv.percentage,
cv.Optional(CONF_WARM_WHITE, default=1.0): cv.percentage,
cv.Required(
CONF_DURATION
): cv.positive_time_period_milliseconds,
@ -225,11 +237,15 @@ async def random_effect_to_code(config, effect_id):
cv.has_at_least_one_key(
CONF_STATE,
CONF_BRIGHTNESS,
CONF_COLOR_MODE,
CONF_COLOR_BRIGHTNESS,
CONF_RED,
CONF_GREEN,
CONF_BLUE,
CONF_WHITE,
CONF_COLOR_TEMPERATURE,
CONF_COLD_WHITE,
CONF_WARM_WHITE,
),
),
cv.Length(min=2),
@ -246,6 +262,7 @@ async def strobe_effect_to_code(config, effect_id):
(
"color",
LightColorValues(
color.get(CONF_COLOR_MODE, ColorMode.UNKNOWN),
color[CONF_STATE],
color[CONF_BRIGHTNESS],
color[CONF_COLOR_BRIGHTNESS],
@ -253,6 +270,9 @@ async def strobe_effect_to_code(config, effect_id):
color[CONF_GREEN],
color[CONF_BLUE],
color[CONF_WHITE],
color.get(CONF_COLOR_TEMPERATURE, 0.0),
color[CONF_COLD_WHITE],
color[CONF_WARM_WHITE],
),
),
("duration", color[CONF_DURATION]),

View File

@ -1,4 +1,5 @@
#include "esp_color_correction.h"
#include "light_color_values.h"
#include "esphome/core/log.h"
namespace esphome {
@ -7,7 +8,7 @@ namespace light {
void ESPColorCorrection::calculate_gamma_table(float gamma) {
for (uint16_t i = 0; i < 256; i++) {
// corrected = val ^ gamma
auto corrected = static_cast<uint8_t>(roundf(255.0f * gamma_correct(i / 255.0f, gamma)));
auto corrected = to_uint8_scale(gamma_correct(i / 255.0f, gamma));
this->gamma_table_[i] = corrected;
}
if (gamma == 0.0f) {
@ -17,7 +18,7 @@ void ESPColorCorrection::calculate_gamma_table(float gamma) {
}
for (uint16_t i = 0; i < 256; i++) {
// val = corrected ^ (1/gamma)
auto uncorrected = static_cast<uint8_t>(roundf(255.0f * powf(i / 255.0f, 1.0f / gamma)));
auto uncorrected = to_uint8_scale(powf(i / 255.0f, 1.0f / gamma));
this->gamma_reverse_table_[i] = uncorrected;
}
}

View File

@ -11,6 +11,9 @@ int32_t interpret_index(int32_t index, int32_t size);
class AddressableLight;
class ESPRangeIterator;
/**
* A half-open range of LEDs, inclusive of the begin index and exclusive of the end index, using zero-based numbering.
*/
class ESPRangeView : public ESPColorSettable {
public:
ESPRangeView(AddressableLight *parent, int32_t begin, int32_t end)

View File

@ -7,95 +7,42 @@ namespace light {
static const char *const TAG = "light";
#ifdef USE_JSON
LightCall &LightCall::parse_color_json(JsonObject &root) {
if (root.containsKey("state")) {
auto val = parse_on_off(root["state"]);
switch (val) {
case PARSE_ON:
this->set_state(true);
break;
case PARSE_OFF:
this->set_state(false);
break;
case PARSE_TOGGLE:
this->set_state(!this->parent_->remote_values.is_on());
break;
case PARSE_NONE:
break;
}
static const char *color_mode_to_human(ColorMode color_mode) {
switch (color_mode) {
case ColorMode::UNKNOWN:
return "Unknown";
case ColorMode::WHITE:
return "White";
case ColorMode::COLOR_TEMPERATURE:
return "Color temperature";
case ColorMode::COLD_WARM_WHITE:
return "Cold/warm white";
case ColorMode::RGB:
return "RGB";
case ColorMode::RGB_WHITE:
return "RGBW";
case ColorMode::RGB_COLD_WARM_WHITE:
return "RGB + cold/warm white";
case ColorMode::RGB_COLOR_TEMPERATURE:
return "RGB + color temperature";
default:
return "";
}
if (root.containsKey("brightness")) {
this->set_brightness(float(root["brightness"]) / 255.0f);
}
if (root.containsKey("color")) {
JsonObject &color = root["color"];
// HA also encodes brightness information in the r, g, b values, so extract that and set it as color brightness.
float max_rgb = 0.0f;
if (color.containsKey("r")) {
float r = float(color["r"]) / 255.0f;
max_rgb = fmaxf(max_rgb, r);
this->set_red(r);
}
if (color.containsKey("g")) {
float g = float(color["g"]) / 255.0f;
max_rgb = fmaxf(max_rgb, g);
this->set_green(g);
}
if (color.containsKey("b")) {
float b = float(color["b"]) / 255.0f;
max_rgb = fmaxf(max_rgb, b);
this->set_blue(b);
}
if (color.containsKey("r") || color.containsKey("g") || color.containsKey("b")) {
this->set_color_brightness(max_rgb);
}
}
if (root.containsKey("white_value")) {
this->set_white(float(root["white_value"]) / 255.0f);
}
if (root.containsKey("color_temp")) {
this->set_color_temperature(float(root["color_temp"]));
}
return *this;
}
LightCall &LightCall::parse_json(JsonObject &root) {
this->parse_color_json(root);
if (root.containsKey("flash")) {
auto length = uint32_t(float(root["flash"]) * 1000);
this->set_flash_length(length);
}
if (root.containsKey("transition")) {
auto length = uint32_t(float(root["transition"]) * 1000);
this->set_transition_length(length);
}
if (root.containsKey("effect")) {
const char *effect = root["effect"];
this->set_effect(effect);
}
return *this;
}
#endif
void LightCall::perform() {
// use remote values for fallback
const char *name = this->parent_->get_name().c_str();
if (this->publish_) {
ESP_LOGD(TAG, "'%s' Setting:", name);
}
LightColorValues v = this->validate_();
if (this->publish_) {
ESP_LOGD(TAG, "'%s' Setting:", name);
// Only print color mode when it's being changed
ColorMode current_color_mode = this->parent_->remote_values.get_color_mode();
if (this->color_mode_.value_or(current_color_mode) != current_color_mode) {
ESP_LOGD(TAG, " Color mode: %s", color_mode_to_human(v.get_color_mode()));
}
// Only print state when it's being changed
bool current_state = this->parent_->remote_values.is_on();
if (this->state_.value_or(current_state) != current_state) {
@ -106,30 +53,38 @@ void LightCall::perform() {
ESP_LOGD(TAG, " Brightness: %.0f%%", v.get_brightness() * 100.0f);
}
if (this->color_temperature_.has_value()) {
ESP_LOGD(TAG, " Color Temperature: %.1f mireds", v.get_color_temperature());
if (this->color_brightness_.has_value()) {
ESP_LOGD(TAG, " Color brightness: %.0f%%", v.get_color_brightness() * 100.0f);
}
if (this->red_.has_value() || this->green_.has_value() || this->blue_.has_value()) {
ESP_LOGD(TAG, " Red=%.0f%%, Green=%.0f%%, Blue=%.0f%%", v.get_red() * 100.0f, v.get_green() * 100.0f,
ESP_LOGD(TAG, " Red: %.0f%%, Green: %.0f%%, Blue: %.0f%%", v.get_red() * 100.0f, v.get_green() * 100.0f,
v.get_blue() * 100.0f);
}
if (this->white_.has_value()) {
ESP_LOGD(TAG, " White Value: %.0f%%", v.get_white() * 100.0f);
ESP_LOGD(TAG, " White: %.0f%%", v.get_white() * 100.0f);
}
if (this->color_temperature_.has_value()) {
ESP_LOGD(TAG, " Color temperature: %.1f mireds", v.get_color_temperature());
}
if (this->cold_white_.has_value() || this->warm_white_.has_value()) {
ESP_LOGD(TAG, " Cold white: %.0f%%, warm white: %.0f%%", v.get_cold_white() * 100.0f,
v.get_warm_white() * 100.0f);
}
}
if (this->has_flash_()) {
// FLASH
if (this->publish_) {
ESP_LOGD(TAG, " Flash Length: %.1fs", *this->flash_length_ / 1e3f);
ESP_LOGD(TAG, " Flash length: %.1fs", *this->flash_length_ / 1e3f);
}
this->parent_->start_flash_(v, *this->flash_length_);
} else if (this->has_transition_()) {
// TRANSITION
if (this->publish_) {
ESP_LOGD(TAG, " Transition Length: %.1fs", *this->transition_length_ / 1e3f);
ESP_LOGD(TAG, " Transition length: %.1fs", *this->transition_length_ / 1e3f);
}
// Special case: Transition and effect can be set when turning off
@ -177,32 +132,49 @@ void LightCall::perform() {
}
LightColorValues LightCall::validate_() {
// use remote values for fallback
auto *name = this->parent_->get_name().c_str();
auto traits = this->parent_->get_traits();
// Color mode check
if (this->color_mode_.has_value() && !traits.supports_color_mode(this->color_mode_.value())) {
ESP_LOGW(TAG, "'%s' - This light does not support color mode %s!", name,
color_mode_to_human(this->color_mode_.value()));
this->color_mode_.reset();
}
// Ensure there is always a color mode set
if (!this->color_mode_.has_value()) {
this->color_mode_ = this->compute_color_mode_();
}
auto color_mode = *this->color_mode_;
// Transform calls that use non-native parameters for the current mode.
this->transform_parameters_();
// Brightness exists check
if (this->brightness_.has_value() && !traits.get_supports_brightness()) {
if (this->brightness_.has_value() && *this->brightness_ > 0.0f && !(color_mode & ColorCapability::BRIGHTNESS)) {
ESP_LOGW(TAG, "'%s' - This light does not support setting brightness!", name);
this->brightness_.reset();
}
// Transition length possible check
if (this->transition_length_.has_value() && *this->transition_length_ != 0 && !traits.get_supports_brightness()) {
if (this->transition_length_.has_value() && *this->transition_length_ != 0 &&
!(color_mode & ColorCapability::BRIGHTNESS)) {
ESP_LOGW(TAG, "'%s' - This light does not support transitions!", name);
this->transition_length_.reset();
}
// Color brightness exists check
if (this->color_brightness_.has_value() && !traits.get_supports_rgb()) {
ESP_LOGW(TAG, "'%s' - This light does not support setting RGB brightness!", name);
if (this->color_brightness_.has_value() && *this->color_brightness_ > 0.0f && !(color_mode & ColorCapability::RGB)) {
ESP_LOGW(TAG, "'%s' - This color mode does not support setting RGB brightness!", name);
this->color_brightness_.reset();
}
// RGB exists check
if (this->red_.has_value() || this->green_.has_value() || this->blue_.has_value()) {
if (!traits.get_supports_rgb()) {
ESP_LOGW(TAG, "'%s' - This light does not support setting RGB color!", name);
if ((this->red_.has_value() && *this->red_ > 0.0f) || (this->green_.has_value() && *this->green_ > 0.0f) ||
(this->blue_.has_value() && *this->blue_ > 0.0f)) {
if (!(color_mode & ColorCapability::RGB)) {
ESP_LOGW(TAG, "'%s' - This color mode does not support setting RGB color!", name);
this->red_.reset();
this->green_.reset();
this->blue_.reset();
@ -210,80 +182,38 @@ LightColorValues LightCall::validate_() {
}
// White value exists check
if (this->white_.has_value() && !traits.get_supports_rgb_white_value()) {
ESP_LOGW(TAG, "'%s' - This light does not support setting white value!", name);
if (this->white_.has_value() && *this->white_ > 0.0f &&
!(color_mode & ColorCapability::WHITE || color_mode & ColorCapability::COLD_WARM_WHITE)) {
ESP_LOGW(TAG, "'%s' - This color mode does not support setting white value!", name);
this->white_.reset();
}
// Color temperature exists check
if (this->color_temperature_.has_value() && !traits.get_supports_color_temperature()) {
ESP_LOGW(TAG, "'%s' - This light does not support setting color temperature!", name);
if (this->color_temperature_.has_value() &&
!(color_mode & ColorCapability::COLOR_TEMPERATURE || color_mode & ColorCapability::COLD_WARM_WHITE)) {
ESP_LOGW(TAG, "'%s' - This color mode does not support setting color temperature!", name);
this->color_temperature_.reset();
}
// Set color brightness to 100% if currently zero and a color is set. This is both for compatibility with older
// clients that don't know about color brightness, and it's intuitive UX anyway: if I set a color, it should show up.
if (this->red_.has_value() || this->green_.has_value() || this->blue_.has_value()) {
if (!this->color_brightness_.has_value() && this->parent_->remote_values.get_color_brightness() == 0.0f)
this->color_brightness_ = optional<float>(1.0f);
}
// Handle interaction between RGB and white for color interlock
if (traits.get_supports_color_interlock()) {
// Find out which channel (white or color) the user wanted to enable
bool output_white = this->white_.has_value() && *this->white_ > 0.0f;
bool output_color = (this->color_brightness_.has_value() && *this->color_brightness_ > 0.0f) ||
this->red_.has_value() || this->green_.has_value() || this->blue_.has_value();
// Interpret setting the color to white as setting the white channel.
if (output_color && *this->red_ == 1.0f && *this->green_ == 1.0f && *this->blue_ == 1.0f) {
output_white = true;
output_color = false;
if (!this->white_.has_value())
this->white_ = optional<float>(this->color_brightness_.value_or(1.0f));
}
// Ensure either the white value or the color brightness is always zero.
if (output_white && output_color) {
ESP_LOGW(TAG, "'%s' - Cannot enable color and white channel simultaneously with interlock!", name);
// For compatibility with historic behaviour, prefer white channel in this case.
this->color_brightness_ = optional<float>(0.0f);
} else if (output_white) {
this->color_brightness_ = optional<float>(0.0f);
} else if (output_color) {
this->white_ = optional<float>(0.0f);
// Cold/warm white value exists check
if ((this->cold_white_.has_value() && *this->cold_white_ > 0.0f) ||
(this->warm_white_.has_value() && *this->warm_white_ > 0.0f)) {
if (!(color_mode & ColorCapability::COLD_WARM_WHITE)) {
ESP_LOGW(TAG, "'%s' - This color mode does not support setting cold/warm white value!", name);
this->cold_white_.reset();
this->warm_white_.reset();
}
}
// If only a color temperature is specified, change to white light
if (this->color_temperature_.has_value() && !this->white_.has_value() && !this->red_.has_value() &&
!this->green_.has_value() && !this->blue_.has_value()) {
// Disable color LEDs explicitly if not already set
if (traits.get_supports_rgb() && !this->color_brightness_.has_value())
this->color_brightness_ = optional<float>(0.0f);
this->red_ = optional<float>(1.0f);
this->green_ = optional<float>(1.0f);
this->blue_ = optional<float>(1.0f);
// if setting color temperature from color (i.e. switching to white light), set White to 100%
auto cv = this->parent_->remote_values;
bool was_color = cv.get_red() != 1.0f || cv.get_blue() != 1.0f || cv.get_green() != 1.0f;
if (traits.get_supports_color_interlock() || was_color) {
this->white_ = optional<float>(1.0f);
}
}
#define VALIDATE_RANGE_(name_, upper_name) \
#define VALIDATE_RANGE_(name_, upper_name, min, max) \
if (name_##_.has_value()) { \
auto val = *name_##_; \
if (val < 0.0f || val > 1.0f) { \
ESP_LOGW(TAG, "'%s' - %s value %.2f is out of range [0.0 - 1.0]!", name, upper_name, val); \
name_##_ = clamp(val, 0.0f, 1.0f); \
if (val < (min) || val > (max)) { \
ESP_LOGW(TAG, "'%s' - %s value %.2f is out of range [%.1f - %.1f]!", name, upper_name, val, (min), (max)); \
name_##_ = clamp(val, (min), (max)); \
} \
}
#define VALIDATE_RANGE(name, upper_name) VALIDATE_RANGE_(name, upper_name)
#define VALIDATE_RANGE(name, upper_name) VALIDATE_RANGE_(name, upper_name, 0.0f, 1.0f)
// Range checks
VALIDATE_RANGE(brightness, "Brightness")
@ -292,13 +222,33 @@ LightColorValues LightCall::validate_() {
VALIDATE_RANGE(green, "Green")
VALIDATE_RANGE(blue, "Blue")
VALIDATE_RANGE(white, "White")
VALIDATE_RANGE(cold_white, "Cold white")
VALIDATE_RANGE(warm_white, "Warm white")
VALIDATE_RANGE_(color_temperature, "Color temperature", traits.get_min_mireds(), traits.get_max_mireds())
// Flag whether an explicit turn off was requested, in which case we'll also stop the effect.
bool explicit_turn_off_request = this->state_.has_value() && !*this->state_;
// Turn off when brightness is set to zero, and reset brightness (so that it has nonzero brightness when turned on).
if (this->brightness_.has_value() && *this->brightness_ == 0.0f) {
this->state_ = optional<float>(false);
this->brightness_ = optional<float>(1.0f);
}
// Set color brightness to 100% if currently zero and a color is set.
if (this->red_.has_value() || this->green_.has_value() || this->blue_.has_value()) {
if (!this->color_brightness_.has_value() && this->parent_->remote_values.get_color_brightness() == 0.0f)
this->color_brightness_ = optional<float>(1.0f);
}
// Create color values for the light with this call applied.
auto v = this->parent_->remote_values;
if (this->color_mode_.has_value())
v.set_color_mode(*this->color_mode_);
if (this->state_.has_value())
v.set_state(*this->state_);
if (this->brightness_.has_value())
v.set_brightness(*this->brightness_);
if (this->color_brightness_.has_value())
v.set_color_brightness(*this->color_brightness_);
if (this->red_.has_value())
@ -309,11 +259,14 @@ LightColorValues LightCall::validate_() {
v.set_blue(*this->blue_);
if (this->white_.has_value())
v.set_white(*this->white_);
if (this->color_temperature_.has_value())
v.set_color_temperature(*this->color_temperature_);
if (this->cold_white_.has_value())
v.set_cold_white(*this->cold_white_);
if (this->warm_white_.has_value())
v.set_warm_white(*this->warm_white_);
v.normalize_color(traits);
v.normalize_color();
// Flash length check
if (this->has_flash_() && *this->flash_length_ == 0) {
@ -322,7 +275,7 @@ LightColorValues LightCall::validate_() {
}
// validate transition length/flash length/effect not used at the same time
bool supports_transition = traits.get_supports_brightness();
bool supports_transition = color_mode & ColorCapability::BRIGHTNESS;
// If effect is already active, remove effect start
if (this->has_effect_() && *this->effect_ == this->parent_->active_effect_index_) {
@ -331,7 +284,7 @@ LightColorValues LightCall::validate_() {
// validate effect index
if (this->has_effect_() && *this->effect_ > this->parent_->effects_.size()) {
ESP_LOGW(TAG, "'%s' Invalid effect index %u", name, *this->effect_);
ESP_LOGW(TAG, "'%s' - Invalid effect index %u!", name, *this->effect_);
this->effect_.reset();
}
@ -369,7 +322,7 @@ LightColorValues LightCall::validate_() {
if (this->has_effect_()) {
ESP_LOGW(TAG, "'%s' - Cannot start an effect when turning off!", name);
this->effect_.reset();
} else if (this->parent_->active_effect_index_ != 0) {
} else if (this->parent_->active_effect_index_ != 0 && explicit_turn_off_request) {
// Auto turn off effect
this->effect_ = 0;
}
@ -381,6 +334,127 @@ LightColorValues LightCall::validate_() {
return v;
}
void LightCall::transform_parameters_() {
auto traits = this->parent_->get_traits();
// Allow CWWW modes to be set with a white value and/or color temperature. This is used by HA,
// which doesn't support CWWW modes (yet?), and for compatibility with the pre-colormode model,
// as CWWW and RGBWW lights used to represent their values as white + color temperature.
if (((this->white_.has_value() && *this->white_ > 0.0f) || this->color_temperature_.has_value()) && //
(*this->color_mode_ & ColorCapability::COLD_WARM_WHITE) && //
!(*this->color_mode_ & ColorCapability::WHITE) && //
!(*this->color_mode_ & ColorCapability::COLOR_TEMPERATURE) && //
traits.get_min_mireds() > 0.0f && traits.get_max_mireds() > 0.0f) {
ESP_LOGD(TAG, "'%s' - Setting cold/warm white channels using white/color temperature values.",
this->parent_->get_name().c_str());
auto current_values = this->parent_->remote_values;
if (this->color_temperature_.has_value()) {
const float white =
this->white_.value_or(fmaxf(current_values.get_cold_white(), current_values.get_warm_white()));
const float color_temp = clamp(*this->color_temperature_, traits.get_min_mireds(), traits.get_max_mireds());
const float ww_fraction =
(color_temp - traits.get_min_mireds()) / (traits.get_max_mireds() - traits.get_min_mireds());
const float cw_fraction = 1.0f - ww_fraction;
const float max_cw_ww = std::max(ww_fraction, cw_fraction);
this->cold_white_ = white * gamma_uncorrect(cw_fraction / max_cw_ww, this->parent_->get_gamma_correct());
this->warm_white_ = white * gamma_uncorrect(ww_fraction / max_cw_ww, this->parent_->get_gamma_correct());
} else {
const float max_cw_ww = std::max(current_values.get_warm_white(), current_values.get_cold_white());
this->cold_white_ = *this->white_ * current_values.get_cold_white() / max_cw_ww;
this->warm_white_ = *this->white_ * current_values.get_warm_white() / max_cw_ww;
}
}
}
ColorMode LightCall::compute_color_mode_() {
auto supported_modes = this->parent_->get_traits().get_supported_color_modes();
int supported_count = supported_modes.size();
// Some lights don't support any color modes (e.g. monochromatic light), leave it at unknown.
if (supported_count == 0)
return ColorMode::UNKNOWN;
// In the common case of lights supporting only a single mode, use that one.
if (supported_count == 1)
return *supported_modes.begin();
// Don't change if the light is being turned off.
ColorMode current_mode = this->parent_->remote_values.get_color_mode();
if (this->state_.has_value() && !*this->state_)
return current_mode;
// If no color mode is specified, we try to guess the color mode. This is needed for backward compatibility to
// pre-colormode clients and automations, but also for the MQTT API, where HA doesn't let us know which color mode
// was used for some reason.
std::set<ColorMode> suitable_modes = this->get_suitable_color_modes_();
// Don't change if the current mode is suitable.
if (suitable_modes.count(current_mode) > 0) {
ESP_LOGI(TAG, "'%s' - Keeping current color mode %s for call without color mode.",
this->parent_->get_name().c_str(), color_mode_to_human(current_mode));
return current_mode;
}
// Use the preferred suitable mode.
for (auto mode : suitable_modes) {
if (supported_modes.count(mode) == 0)
continue;
ESP_LOGI(TAG, "'%s' - Using color mode %s for call without color mode.", this->parent_->get_name().c_str(),
color_mode_to_human(mode));
return mode;
}
// There's no supported mode for this call, so warn, use the current more or a mode at random and let validation strip
// out whatever we don't support.
auto color_mode = current_mode != ColorMode::UNKNOWN ? current_mode : *supported_modes.begin();
ESP_LOGW(TAG, "'%s' - No color mode suitable for this call supported, defaulting to %s!",
this->parent_->get_name().c_str(), color_mode_to_human(color_mode));
return color_mode;
}
std::set<ColorMode> LightCall::get_suitable_color_modes_() {
bool has_white = this->white_.has_value() && *this->white_ > 0.0f;
bool has_ct = this->color_temperature_.has_value();
bool has_cwww = (this->cold_white_.has_value() && *this->cold_white_ > 0.0f) ||
(this->warm_white_.has_value() && *this->warm_white_ > 0.0f);
bool has_rgb = (this->color_brightness_.has_value() && *this->color_brightness_ > 0.0f) ||
(this->red_.has_value() || this->green_.has_value() || this->blue_.has_value());
#define KEY(white, ct, cwww, rgb) ((white) << 0 | (ct) << 1 | (cwww) << 2 | (rgb) << 3)
#define ENTRY(white, ct, cwww, rgb, ...) \
std::make_tuple<uint8_t, std::set<ColorMode>>(KEY(white, ct, cwww, rgb), __VA_ARGS__)
// Flag order: white, color temperature, cwww, rgb
std::array<std::tuple<uint8_t, std::set<ColorMode>>, 10> lookup_table{
ENTRY(true, false, false, false,
{ColorMode::WHITE, ColorMode::RGB_WHITE, ColorMode::RGB_COLOR_TEMPERATURE, ColorMode::COLD_WARM_WHITE,
ColorMode::RGB_COLD_WARM_WHITE}),
ENTRY(false, true, false, false,
{ColorMode::COLOR_TEMPERATURE, ColorMode::RGB_COLOR_TEMPERATURE, ColorMode::COLD_WARM_WHITE,
ColorMode::RGB_COLD_WARM_WHITE}),
ENTRY(true, true, false, false,
{ColorMode::COLD_WARM_WHITE, ColorMode::RGB_COLOR_TEMPERATURE, ColorMode::RGB_COLD_WARM_WHITE}),
ENTRY(false, false, true, false, {ColorMode::COLD_WARM_WHITE, ColorMode::RGB_COLD_WARM_WHITE}),
ENTRY(false, false, false, false,
{ColorMode::RGB_WHITE, ColorMode::RGB_COLOR_TEMPERATURE, ColorMode::RGB_COLD_WARM_WHITE, ColorMode::RGB,
ColorMode::WHITE, ColorMode::COLOR_TEMPERATURE, ColorMode::COLD_WARM_WHITE}),
ENTRY(true, false, false, true,
{ColorMode::RGB_WHITE, ColorMode::RGB_COLOR_TEMPERATURE, ColorMode::RGB_COLD_WARM_WHITE}),
ENTRY(false, true, false, true, {ColorMode::RGB_COLOR_TEMPERATURE, ColorMode::RGB_COLD_WARM_WHITE}),
ENTRY(true, true, false, true, {ColorMode::RGB_COLOR_TEMPERATURE, ColorMode::RGB_COLD_WARM_WHITE}),
ENTRY(false, false, true, true, {ColorMode::RGB_COLD_WARM_WHITE}),
ENTRY(false, false, false, true,
{ColorMode::RGB, ColorMode::RGB_WHITE, ColorMode::RGB_COLOR_TEMPERATURE, ColorMode::RGB_COLD_WARM_WHITE}),
};
auto key = KEY(has_white, has_ct, has_cwww, has_rgb);
for (auto &item : lookup_table)
if (std::get<0>(item) == key)
return std::get<1>(item);
// This happens if there are conflicting flags given.
return {};
}
LightCall &LightCall::set_effect(const std::string &effect) {
if (strcasecmp(effect.c_str(), "none") == 0) {
this->set_effect(0);
@ -406,53 +480,74 @@ LightCall &LightCall::from_light_color_values(const LightColorValues &values) {
this->set_state(values.is_on());
this->set_brightness_if_supported(values.get_brightness());
this->set_color_brightness_if_supported(values.get_color_brightness());
this->set_color_mode_if_supported(values.get_color_mode());
this->set_red_if_supported(values.get_red());
this->set_green_if_supported(values.get_green());
this->set_blue_if_supported(values.get_blue());
this->set_white_if_supported(values.get_white());
this->set_color_temperature_if_supported(values.get_color_temperature());
this->set_cold_white_if_supported(values.get_cold_white());
this->set_warm_white_if_supported(values.get_warm_white());
return *this;
}
ColorMode LightCall::get_active_color_mode_() {
return this->color_mode_.value_or(this->parent_->remote_values.get_color_mode());
}
LightCall &LightCall::set_transition_length_if_supported(uint32_t transition_length) {
if (this->parent_->get_traits().get_supports_brightness())
if (this->get_active_color_mode_() & ColorCapability::BRIGHTNESS)
this->set_transition_length(transition_length);
return *this;
}
LightCall &LightCall::set_brightness_if_supported(float brightness) {
if (this->parent_->get_traits().get_supports_brightness())
if (this->get_active_color_mode_() & ColorCapability::BRIGHTNESS)
this->set_brightness(brightness);
return *this;
}
LightCall &LightCall::set_color_mode_if_supported(ColorMode color_mode) {
if (this->parent_->get_traits().supports_color_mode(color_mode))
this->color_mode_ = color_mode;
return *this;
}
LightCall &LightCall::set_color_brightness_if_supported(float brightness) {
if (this->parent_->get_traits().get_supports_rgb_white_value())
if (this->get_active_color_mode_() & ColorCapability::RGB)
this->set_color_brightness(brightness);
return *this;
}
LightCall &LightCall::set_red_if_supported(float red) {
if (this->parent_->get_traits().get_supports_rgb())
if (this->get_active_color_mode_() & ColorCapability::RGB)
this->set_red(red);
return *this;
}
LightCall &LightCall::set_green_if_supported(float green) {
if (this->parent_->get_traits().get_supports_rgb())
if (this->get_active_color_mode_() & ColorCapability::RGB)
this->set_green(green);
return *this;
}
LightCall &LightCall::set_blue_if_supported(float blue) {
if (this->parent_->get_traits().get_supports_rgb())
if (this->get_active_color_mode_() & ColorCapability::RGB)
this->set_blue(blue);
return *this;
}
LightCall &LightCall::set_white_if_supported(float white) {
if (this->parent_->get_traits().get_supports_rgb_white_value())
if (this->get_active_color_mode_() & ColorCapability::WHITE)
this->set_white(white);
return *this;
}
LightCall &LightCall::set_color_temperature_if_supported(float color_temperature) {
if (this->parent_->get_traits().get_supports_color_temperature())
if (this->get_active_color_mode_() & ColorCapability::COLOR_TEMPERATURE)
this->set_color_temperature(color_temperature);
return *this;
}
LightCall &LightCall::set_cold_white_if_supported(float cold_white) {
if (this->get_active_color_mode_() & ColorCapability::COLD_WARM_WHITE)
this->set_cold_white(cold_white);
return *this;
}
LightCall &LightCall::set_warm_white_if_supported(float warm_white) {
if (this->get_active_color_mode_() & ColorCapability::COLD_WARM_WHITE)
this->set_warm_white(warm_white);
return *this;
}
LightCall &LightCall::set_state(optional<bool> state) {
this->state_ = state;
return *this;
@ -485,6 +580,14 @@ LightCall &LightCall::set_brightness(float brightness) {
this->brightness_ = brightness;
return *this;
}
LightCall &LightCall::set_color_mode(optional<ColorMode> color_mode) {
this->color_mode_ = color_mode;
return *this;
}
LightCall &LightCall::set_color_mode(ColorMode color_mode) {
this->color_mode_ = color_mode;
return *this;
}
LightCall &LightCall::set_color_brightness(optional<float> brightness) {
this->color_brightness_ = brightness;
return *this;
@ -533,6 +636,22 @@ LightCall &LightCall::set_color_temperature(float color_temperature) {
this->color_temperature_ = color_temperature;
return *this;
}
LightCall &LightCall::set_cold_white(optional<float> cold_white) {
this->cold_white_ = cold_white;
return *this;
}
LightCall &LightCall::set_cold_white(float cold_white) {
this->cold_white_ = cold_white;
return *this;
}
LightCall &LightCall::set_warm_white(optional<float> warm_white) {
this->warm_white_ = warm_white;
return *this;
}
LightCall &LightCall::set_warm_white(float warm_white) {
this->warm_white_ = warm_white;
return *this;
}
LightCall &LightCall::set_effect(optional<std::string> effect) {
if (effect.has_value())
this->set_effect(*effect);

View File

@ -2,6 +2,7 @@
#include "esphome/core/optional.h"
#include "light_color_values.h"
#include <set>
namespace esphome {
namespace light {
@ -44,6 +45,14 @@ class LightCall {
LightCall &set_brightness(float brightness);
/// Set the brightness property if the light supports brightness.
LightCall &set_brightness_if_supported(float brightness);
/// Set the color mode of the light.
LightCall &set_color_mode(optional<ColorMode> color_mode);
/// Set the color mode of the light.
LightCall &set_color_mode(ColorMode color_mode);
/// Set the color mode of the light, if this mode is supported.
LightCall &set_color_mode_if_supported(ColorMode color_mode);
/// Set the color brightness of the light from 0.0 (no color) to 1.0 (fully on)
LightCall &set_color_brightness(optional<float> brightness);
/// Set the color brightness of the light from 0.0 (no color) to 1.0 (fully on)
@ -98,6 +107,18 @@ class LightCall {
LightCall &set_color_temperature(float color_temperature);
/// Set the color_temperature property if the light supports color temperature.
LightCall &set_color_temperature_if_supported(float color_temperature);
/// Set the cold white value of the light from 0.0 to 1.0.
LightCall &set_cold_white(optional<float> cold_white);
/// Set the cold white value of the light from 0.0 to 1.0.
LightCall &set_cold_white(float cold_white);
/// Set the cold white property if the light supports cold white output.
LightCall &set_cold_white_if_supported(float cold_white);
/// Set the warm white value of the light from 0.0 to 1.0.
LightCall &set_warm_white(optional<float> warm_white);
/// Set the warm white value of the light from 0.0 to 1.0.
LightCall &set_warm_white(float warm_white);
/// Set the warm white property if the light supports cold white output.
LightCall &set_warm_white_if_supported(float warm_white);
/// Set the effect of the light by its name.
LightCall &set_effect(optional<std::string> effect);
/// Set the effect of the light by its name.
@ -131,18 +152,24 @@ class LightCall {
* @return The light call for chaining setters.
*/
LightCall &set_rgbw(float red, float green, float blue, float white);
#ifdef USE_JSON
LightCall &parse_color_json(JsonObject &root);
LightCall &parse_json(JsonObject &root);
#endif
LightCall &from_light_color_values(const LightColorValues &values);
void perform();
protected:
/// Get the currently targeted, or active if none set, color mode.
ColorMode get_active_color_mode_();
/// Validate all properties and return the target light color values.
LightColorValues validate_();
//// Compute the color mode that should be used for this call.
ColorMode compute_color_mode_();
/// Get potential color modes for this light call.
std::set<ColorMode> get_suitable_color_modes_();
/// Some color modes also can be set using non-native parameters, transform those calls.
void transform_parameters_();
bool has_transition_() { return this->transition_length_.has_value(); }
bool has_flash_() { return this->flash_length_.has_value(); }
bool has_effect_() { return this->effect_.has_value(); }
@ -151,6 +178,7 @@ class LightCall {
optional<bool> state_;
optional<uint32_t> transition_length_;
optional<uint32_t> flash_length_;
optional<ColorMode> color_mode_;
optional<float> brightness_;
optional<float> color_brightness_;
optional<float> red_;
@ -158,6 +186,8 @@ class LightCall {
optional<float> blue_;
optional<float> white_;
optional<float> color_temperature_;
optional<float> cold_white_;
optional<float> warm_white_;
optional<uint32_t> effect_;
bool publish_{true};
bool save_{true};

View File

@ -1,52 +1,65 @@
#pragma once
#include "esphome/core/helpers.h"
#include "esphome/core/defines.h"
#include "light_traits.h"
#ifdef USE_JSON
#include "esphome/components/json/json_util.h"
#endif
#include "color_mode.h"
namespace esphome {
namespace light {
inline static uint8_t to_uint8_scale(float x) { return static_cast<uint8_t>(roundf(x * 255.0f)); }
/** This class represents the color state for a light object.
*
* All values in this class (except color temperature) are represented using floats in the range
* from 0.0 (off) to 1.0 (on). Please note that all values are automatically clamped to this range.
* The representation of the color state is dependent on the active color mode. A color mode consists of multiple
* color capabilities, and each color capability has its own representation in this class. The fields available are as
* follows:
*
* This class has the following properties:
* - state: Whether the light should be on/off. Represented as a float for transitions. Used for
* all lights.
* - brightness: The master brightness of the light, applied to all channels. Used for all lights
* with brightness control.
* - color_brightness: The brightness of the color channels of the light. Used for RGB, RGBW and
* RGBWW lights.
* - red, green, blue: The RGB values of the current color. They are normalized, so at least one of
* them is always 1.0.
* - white: The brightness of the white channel of the light. Used for RGBW and RGBWW lights.
* - color_temperature: The color temperature of the white channel in mireds. Used for RGBWW and
* CWWW lights.
* Always:
* - color_mode: The currently active color mode.
*
* For lights with a color interlock (RGB lights and white light cannot be on at the same time), a
* valid state has always either color_brightness or white (or both) set to zero.
* For ON_OFF capability:
* - state: Whether the light should be on/off. Represented as a float for transitions.
*
* For BRIGHTNESS capability:
* - brightness: The master brightness of the light, should be applied to all channels.
*
* For RGB capability:
* - color_brightness: The brightness of the color channels of the light.
* - red, green, blue: The RGB values of the current color. They are normalized, so at least one of them is always 1.0.
*
* For WHITE capability:
* - white: The brightness of the white channel of the light.
*
* For COLOR_TEMPERATURE capability:
* - color_temperature: The color temperature of the white channel in mireds. Note that it is not clamped to the valid
* range as set in the traits, so the output needs to do this.
*
* For COLD_WARM_WHITE capability:
* - cold_white, warm_white: The brightness of the cald and warm white channels of the light.
*
* All values (except color temperature) are represented using floats in the range 0.0 (off) to 1.0 (on), and are
* automatically clamped to this range. Properties not used in the current color mode can still have (invalid) values
* and must not be accessed by the light output.
*/
class LightColorValues {
public:
/// Construct the LightColorValues with all attributes enabled, but state set to 0.0
/// Construct the LightColorValues with all attributes enabled, but state set to off.
LightColorValues()
: state_(0.0f),
: color_mode_(ColorMode::UNKNOWN),
state_(0.0f),
brightness_(1.0f),
color_brightness_(1.0f),
red_(1.0f),
green_(1.0f),
blue_(1.0f),
white_(1.0f),
color_temperature_{1.0f} {}
color_temperature_{0.0f},
cold_white_{1.0f},
warm_white_{1.0f} {}
LightColorValues(float state, float brightness, float color_brightness, float red, float green, float blue,
float white, float color_temperature = 1.0f) {
LightColorValues(ColorMode color_mode, float state, float brightness, float color_brightness, float red, float green,
float blue, float white, float color_temperature, float cold_white, float warm_white) {
this->set_color_mode(color_mode);
this->set_state(state);
this->set_brightness(brightness);
this->set_color_brightness(color_brightness);
@ -55,49 +68,8 @@ class LightColorValues {
this->set_blue(blue);
this->set_white(white);
this->set_color_temperature(color_temperature);
}
LightColorValues(bool state, float brightness, float color_brightness, float red, float green, float blue,
float white, float color_temperature = 1.0f)
: LightColorValues(state ? 1.0f : 0.0f, brightness, color_brightness, red, green, blue, white,
color_temperature) {}
/// Create light color values from a binary true/false state.
static LightColorValues from_binary(bool state) { return {state, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f}; }
/// Create light color values from a monochromatic brightness state.
static LightColorValues from_monochromatic(float brightness) {
if (brightness == 0.0f)
return {0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f};
else
return {1.0f, brightness, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f};
}
/// Create light color values from an RGB state.
static LightColorValues from_rgb(float r, float g, float b) {
float brightness = std::max(r, std::max(g, b));
if (brightness == 0.0f) {
return {0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f};
} else {
return {1.0f, brightness, 1.0f, r / brightness, g / brightness, b / brightness, 1.0f};
}
}
/// Create light color values from an RGBW state.
static LightColorValues from_rgbw(float r, float g, float b, float w) {
float color_brightness = std::max(r, std::max(g, b));
float master_brightness = std::max(color_brightness, w);
if (master_brightness == 0.0f) {
return {0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f};
} else {
return {1.0f,
master_brightness,
color_brightness / master_brightness,
r / color_brightness,
g / color_brightness,
b / color_brightness,
w / master_brightness};
}
this->set_cold_white(cold_white);
this->set_warm_white(warm_white);
}
/** Linearly interpolate between the values in start to the values in end.
@ -112,6 +84,7 @@ class LightColorValues {
*/
static LightColorValues lerp(const LightColorValues &start, const LightColorValues &end, float completion) {
LightColorValues v;
v.set_color_mode(end.color_mode_);
v.set_state(esphome::lerp(completion, start.get_state(), end.get_state()));
v.set_brightness(esphome::lerp(completion, start.get_brightness(), end.get_brightness()));
v.set_color_brightness(esphome::lerp(completion, start.get_color_brightness(), end.get_color_brightness()));
@ -120,33 +93,11 @@ class LightColorValues {
v.set_blue(esphome::lerp(completion, start.get_blue(), end.get_blue()));
v.set_white(esphome::lerp(completion, start.get_white(), end.get_white()));
v.set_color_temperature(esphome::lerp(completion, start.get_color_temperature(), end.get_color_temperature()));
v.set_cold_white(esphome::lerp(completion, start.get_cold_white(), end.get_cold_white()));
v.set_warm_white(esphome::lerp(completion, start.get_warm_white(), end.get_warm_white()));
return v;
}
#ifdef USE_JSON
/** Dump this color into a JsonObject. Only dumps values if the corresponding traits are marked supported by traits.
*
* @param root The json root object.
* @param traits The traits object used for determining whether to include certain attributes.
*/
void dump_json(JsonObject &root, const LightTraits &traits) const {
root["state"] = (this->get_state() != 0.0f) ? "ON" : "OFF";
if (traits.get_supports_brightness())
root["brightness"] = uint8_t(this->get_brightness() * 255);
if (traits.get_supports_rgb()) {
JsonObject &color = root.createNestedObject("color");
color["r"] = uint8_t(this->get_color_brightness() * this->get_red() * 255);
color["g"] = uint8_t(this->get_color_brightness() * this->get_green() * 255);
color["b"] = uint8_t(this->get_color_brightness() * this->get_blue() * 255);
}
if (traits.get_supports_rgb_white_value()) {
root["white_value"] = uint8_t(this->get_white() * 255);
}
if (traits.get_supports_color_temperature())
root["color_temp"] = uint32_t(this->get_color_temperature());
}
#endif
/** Normalize the color (RGB/W) component.
*
* Divides all color attributes by the maximum attribute, so effectively set at least one attribute to 1.
@ -156,8 +107,8 @@ class LightColorValues {
*
* @param traits Used for determining which attributes to consider.
*/
void normalize_color(const LightTraits &traits) {
if (traits.get_supports_rgb()) {
void normalize_color() {
if (this->color_mode_ & ColorCapability::RGB) {
float max_value = fmaxf(this->get_red(), fmaxf(this->get_green(), this->get_blue()));
if (max_value == 0.0f) {
this->set_red(1.0f);
@ -169,15 +120,11 @@ class LightColorValues {
this->set_blue(this->get_blue() / max_value);
}
}
if (traits.get_supports_brightness() && this->get_brightness() == 0.0f) {
// 0% brightness means off
this->set_state(false);
// reset brightness to 100%
this->set_brightness(1.0f);
}
}
// Note that method signature of as_* methods is kept as-is for compatibility reasons, so not all parameters
// are always used or necessary. Methods will be deprecated later.
/// Convert these light color values to a binary representation and write them to binary.
void as_binary(bool *binary) const { *binary = this->state_ == 1.0f; }
@ -188,64 +135,92 @@ class LightColorValues {
/// Convert these light color values to an RGB representation and write them to red, green, blue.
void as_rgb(float *red, float *green, float *blue, float gamma = 0, bool color_interlock = false) const {
float brightness = this->state_ * this->brightness_ * this->color_brightness_;
if (color_interlock && this->white_ > 0.0f) {
brightness = 0;
if (this->color_mode_ & ColorCapability::RGB) {
float brightness = this->state_ * this->brightness_ * this->color_brightness_;
*red = gamma_correct(brightness * this->red_, gamma);
*green = gamma_correct(brightness * this->green_, gamma);
*blue = gamma_correct(brightness * this->blue_, gamma);
} else {
*red = *green = *blue = 0;
}
*red = gamma_correct(brightness * this->red_, gamma);
*green = gamma_correct(brightness * this->green_, gamma);
*blue = gamma_correct(brightness * this->blue_, gamma);
}
/// Convert these light color values to an RGBW representation and write them to red, green, blue, white.
void as_rgbw(float *red, float *green, float *blue, float *white, float gamma = 0,
bool color_interlock = false) const {
this->as_rgb(red, green, blue, gamma, color_interlock);
*white = gamma_correct(this->state_ * this->brightness_ * this->white_, gamma);
}
/// Convert these light color values to an RGBWW representation with the given parameters.
void as_rgbww(float color_temperature_cw, float color_temperature_ww, float *red, float *green, float *blue,
float *cold_white, float *warm_white, float gamma = 0, bool constant_brightness = false,
bool color_interlock = false) const {
this->as_rgb(red, green, blue, gamma, color_interlock);
const float color_temp = clamp(this->color_temperature_, color_temperature_cw, color_temperature_ww);
const float ww_fraction = (color_temp - color_temperature_cw) / (color_temperature_ww - color_temperature_cw);
const float cw_fraction = 1.0f - ww_fraction;
const float white_level = gamma_correct(this->state_ * this->brightness_ * this->white_, gamma);
*cold_white = white_level * cw_fraction;
*warm_white = white_level * ww_fraction;
if (!constant_brightness) {
const float max_cw_ww = std::max(ww_fraction, cw_fraction);
*cold_white /= max_cw_ww;
*warm_white /= max_cw_ww;
this->as_rgb(red, green, blue, gamma);
if (this->color_mode_ & ColorCapability::WHITE) {
*white = gamma_correct(this->state_ * this->brightness_ * this->white_, gamma);
} else {
*white = 0;
}
}
/// Convert these light color values to an RGBWW representation with the given parameters.
void as_rgbww(float *red, float *green, float *blue, float *cold_white, float *warm_white, float gamma = 0,
bool constant_brightness = false) const {
this->as_rgb(red, green, blue, gamma);
this->as_cwww(cold_white, warm_white, gamma, constant_brightness);
}
/// Convert these light color values to an RGB+CT+BR representation with the given parameters.
void as_rgbct(float color_temperature_cw, float color_temperature_ww, float *red, float *green, float *blue,
float *color_temperature, float *white_brightness, float gamma = 0) const {
this->as_rgb(red, green, blue, gamma);
this->as_ct(color_temperature_cw, color_temperature_ww, color_temperature, white_brightness, gamma);
}
/// Convert these light color values to an CWWW representation with the given parameters.
void as_cwww(float color_temperature_cw, float color_temperature_ww, float *cold_white, float *warm_white,
float gamma = 0, bool constant_brightness = false) const {
const float color_temp = clamp(this->color_temperature_, color_temperature_cw, color_temperature_ww);
const float ww_fraction = (color_temp - color_temperature_cw) / (color_temperature_ww - color_temperature_cw);
const float cw_fraction = 1.0f - ww_fraction;
const float white_level = gamma_correct(this->state_ * this->brightness_ * this->white_, gamma);
*cold_white = white_level * cw_fraction;
*warm_white = white_level * ww_fraction;
if (!constant_brightness) {
const float max_cw_ww = std::max(ww_fraction, cw_fraction);
*cold_white /= max_cw_ww;
*warm_white /= max_cw_ww;
void as_cwww(float *cold_white, float *warm_white, float gamma = 0, bool constant_brightness = false) const {
if (this->color_mode_ & ColorCapability::COLD_WARM_WHITE) {
const float cw_level = gamma_correct(this->cold_white_, gamma);
const float ww_level = gamma_correct(this->warm_white_, gamma);
const float white_level = gamma_correct(this->state_ * this->brightness_, gamma);
if (!constant_brightness) {
*cold_white = white_level * cw_level;
*warm_white = white_level * ww_level;
} else {
// Just multiplying by cw_level / (cw_level + ww_level) would divide out the brightness information from the
// cold_white and warm_white settings (i.e. cw=0.8, ww=0.4 would be identical to cw=0.4, ww=0.2), which breaks
// transitions. Use the highest value as the brightness for the white channels (the alternative, using cw+ww/2,
// reduces to cw/2 and ww/2, which would still limit brightness to 100% of a single channel, but isn't very
// useful in all other aspects -- that behaviour can also be achieved by limiting the output power).
const float sum = cw_level > 0 || ww_level > 0 ? cw_level + ww_level : 1; // Don't divide by zero.
*cold_white = white_level * std::max(cw_level, ww_level) * cw_level / sum;
*warm_white = white_level * std::max(cw_level, ww_level) * ww_level / sum;
}
} else {
*cold_white = *warm_white = 0;
}
}
/// Convert these light color values to a CT+BR representation with the given parameters.
void as_ct(float color_temperature_cw, float color_temperature_ww, float *color_temperature, float *white_brightness,
float gamma = 0) const {
const float white_level = this->color_mode_ & ColorCapability::RGB ? this->white_ : 1;
if (this->color_mode_ & ColorCapability::COLOR_TEMPERATURE) {
*color_temperature =
(this->color_temperature_ - color_temperature_cw) / (color_temperature_ww - color_temperature_cw);
*white_brightness = gamma_correct(this->state_ * this->brightness_ * white_level, gamma);
} else { // Probably wont get here but put this here anyway.
*white_brightness = 0;
}
}
/// Compare this LightColorValues to rhs, return true if and only if all attributes match.
bool operator==(const LightColorValues &rhs) const {
return state_ == rhs.state_ && brightness_ == rhs.brightness_ && color_brightness_ == rhs.color_brightness_ &&
red_ == rhs.red_ && green_ == rhs.green_ && blue_ == rhs.blue_ && white_ == rhs.white_ &&
color_temperature_ == rhs.color_temperature_;
return color_mode_ == rhs.color_mode_ && state_ == rhs.state_ && brightness_ == rhs.brightness_ &&
color_brightness_ == rhs.color_brightness_ && red_ == rhs.red_ && green_ == rhs.green_ &&
blue_ == rhs.blue_ && white_ == rhs.white_ && color_temperature_ == rhs.color_temperature_ &&
cold_white_ == rhs.cold_white_ && warm_white_ == rhs.warm_white_;
}
bool operator!=(const LightColorValues &rhs) const { return !(rhs == *this); }
/// Get the color mode of these light color values.
ColorMode get_color_mode() const { return this->color_mode_; }
/// Set the color mode of these light color values.
void set_color_mode(ColorMode color_mode) { this->color_mode_ = color_mode; }
/// Get the state of these light color values. In range from 0.0 (off) to 1.0 (on)
float get_state() const { return this->state_; }
/// Get the binary true/false state of these light color values.
@ -288,11 +263,20 @@ class LightColorValues {
/// Get the color temperature property of these light color values in mired.
float get_color_temperature() const { return this->color_temperature_; }
/// Set the color temperature property of these light color values in mired.
void set_color_temperature(float color_temperature) {
this->color_temperature_ = std::max(0.000001f, color_temperature);
}
void set_color_temperature(float color_temperature) { this->color_temperature_ = color_temperature; }
/// Get the cold white property of these light color values. In range 0.0 to 1.0.
float get_cold_white() const { return this->cold_white_; }
/// Set the cold white property of these light color values. In range 0.0 to 1.0.
void set_cold_white(float cold_white) { this->cold_white_ = clamp(cold_white, 0.0f, 1.0f); }
/// Get the warm white property of these light color values. In range 0.0 to 1.0.
float get_warm_white() const { return this->warm_white_; }
/// Set the warm white property of these light color values. In range 0.0 to 1.0.
void set_warm_white(float warm_white) { this->warm_white_ = clamp(warm_white, 0.0f, 1.0f); }
protected:
ColorMode color_mode_;
float state_; ///< ON / OFF, float for transition
float brightness_;
float color_brightness_;
@ -301,6 +285,8 @@ class LightColorValues {
float blue_;
float white_;
float color_temperature_; ///< Color Temperature in Mired
float cold_white_;
float warm_white_;
};
} // namespace light

View File

@ -3,8 +3,6 @@
#include <utility>
#include "esphome/core/component.h"
#include "light_color_values.h"
#include "light_state.h"
namespace esphome {
namespace light {

View File

@ -0,0 +1,165 @@
#include "light_json_schema.h"
#include "light_output.h"
#ifdef USE_JSON
namespace esphome {
namespace light {
// See https://www.home-assistant.io/integrations/light.mqtt/#json-schema for documentation on the schema
void LightJSONSchema::dump_json(LightState &state, JsonObject &root) {
if (state.supports_effects())
root["effect"] = state.get_effect_name();
auto values = state.remote_values;
auto traits = state.get_output()->get_traits();
switch (values.get_color_mode()) {
case ColorMode::UNKNOWN: // don't need to set color mode if we don't know it
break;
case ColorMode::ON_OFF:
root["color_mode"] = "onoff";
break;
case ColorMode::BRIGHTNESS:
root["color_mode"] = "brightness";
break;
case ColorMode::WHITE: // not supported by HA in MQTT
root["color_mode"] = "white";
break;
case ColorMode::COLOR_TEMPERATURE:
root["color_mode"] = "color_temp";
break;
case ColorMode::COLD_WARM_WHITE: // not supported by HA
root["color_mode"] = "cwww";
break;
case ColorMode::RGB:
root["color_mode"] = "rgb";
break;
case ColorMode::RGB_WHITE:
root["color_mode"] = "rgbw";
break;
case ColorMode::RGB_COLOR_TEMPERATURE: // not supported by HA
root["color_mode"] = "rgbct";
break;
case ColorMode::RGB_COLD_WARM_WHITE:
root["color_mode"] = "rgbww";
break;
}
if (values.get_color_mode() & ColorCapability::ON_OFF)
root["state"] = (values.get_state() != 0.0f) ? "ON" : "OFF";
if (values.get_color_mode() & ColorCapability::BRIGHTNESS)
root["brightness"] = uint8_t(values.get_brightness() * 255);
JsonObject &color = root.createNestedObject("color");
if (values.get_color_mode() & ColorCapability::RGB) {
color["r"] = uint8_t(values.get_color_brightness() * values.get_red() * 255);
color["g"] = uint8_t(values.get_color_brightness() * values.get_green() * 255);
color["b"] = uint8_t(values.get_color_brightness() * values.get_blue() * 255);
}
if (values.get_color_mode() & ColorCapability::WHITE) {
color["w"] = uint8_t(values.get_white() * 255);
root["white_value"] = uint8_t(values.get_white() * 255); // legacy API
}
if (values.get_color_mode() & ColorCapability::COLOR_TEMPERATURE) {
// this one isn't under the color subkey for some reason
root["color_temp"] = uint32_t(values.get_color_temperature());
}
if (values.get_color_mode() & ColorCapability::COLD_WARM_WHITE) {
color["c"] = uint8_t(values.get_cold_white() * 255);
color["w"] = uint8_t(values.get_warm_white() * 255);
}
}
void LightJSONSchema::parse_color_json(LightState &state, LightCall &call, JsonObject &root) {
if (root.containsKey("state")) {
auto val = parse_on_off(root["state"]);
switch (val) {
case PARSE_ON:
call.set_state(true);
break;
case PARSE_OFF:
call.set_state(false);
break;
case PARSE_TOGGLE:
call.set_state(!state.remote_values.is_on());
break;
case PARSE_NONE:
break;
}
}
if (root.containsKey("brightness")) {
call.set_brightness(float(root["brightness"]) / 255.0f);
}
if (root.containsKey("color")) {
JsonObject &color = root["color"];
// HA also encodes brightness information in the r, g, b values, so extract that and set it as color brightness.
float max_rgb = 0.0f;
if (color.containsKey("r")) {
float r = float(color["r"]) / 255.0f;
max_rgb = fmaxf(max_rgb, r);
call.set_red(r);
}
if (color.containsKey("g")) {
float g = float(color["g"]) / 255.0f;
max_rgb = fmaxf(max_rgb, g);
call.set_green(g);
}
if (color.containsKey("b")) {
float b = float(color["b"]) / 255.0f;
max_rgb = fmaxf(max_rgb, b);
call.set_blue(b);
}
if (color.containsKey("r") || color.containsKey("g") || color.containsKey("b")) {
call.set_color_brightness(max_rgb);
}
if (color.containsKey("c")) {
call.set_cold_white(float(color["c"]) / 255.0f);
}
if (color.containsKey("w")) {
// the HA scheme is ambigious here, the same key is used for white channel in RGBW and warm
// white channel in RGBWW.
if (color.containsKey("c")) {
call.set_warm_white(float(color["w"]) / 255.0f);
} else {
call.set_white(float(color["w"]) / 255.0f);
}
}
}
if (root.containsKey("white_value")) { // legacy API
call.set_white(float(root["white_value"]) / 255.0f);
}
if (root.containsKey("color_temp")) {
call.set_color_temperature(float(root["color_temp"]));
}
}
void LightJSONSchema::parse_json(LightState &state, LightCall &call, JsonObject &root) {
LightJSONSchema::parse_color_json(state, call, root);
if (root.containsKey("flash")) {
auto length = uint32_t(float(root["flash"]) * 1000);
call.set_flash_length(length);
}
if (root.containsKey("transition")) {
auto length = uint32_t(float(root["transition"]) * 1000);
call.set_transition_length(length);
}
if (root.containsKey("effect")) {
const char *effect = root["effect"];
call.set_effect(effect);
}
}
} // namespace light
} // namespace esphome
#endif

View File

@ -0,0 +1,28 @@
#pragma once
#include "esphome/core/defines.h"
#ifdef USE_JSON
#include "esphome/components/json/json_util.h"
#include "light_call.h"
#include "light_state.h"
namespace esphome {
namespace light {
class LightJSONSchema {
public:
/// Dump the state of a light as JSON.
static void dump_json(LightState &state, JsonObject &root);
/// Parse the JSON state of a light to a LightCall.
static void parse_json(LightState &state, LightCall &call, JsonObject &root);
protected:
static void parse_color_json(LightState &state, LightCall &call, JsonObject &root);
};
} // namespace light
} // namespace esphome
#endif

View File

@ -0,0 +1,12 @@
#include "light_output.h"
#include "transformers.h"
namespace esphome {
namespace light {
std::unique_ptr<LightTransformer> LightOutput::create_default_transition() {
return make_unique<LightTransitionTransformer>();
}
} // namespace light
} // namespace esphome

View File

@ -3,18 +3,20 @@
#include "esphome/core/component.h"
#include "light_traits.h"
#include "light_state.h"
#include "light_transformer.h"
namespace esphome {
namespace light {
class LightState;
/// Interface to write LightStates to hardware.
class LightOutput {
public:
/// Return the LightTraits of this LightOutput.
virtual LightTraits get_traits() = 0;
/// Return the default transformer used for transitions.
virtual std::unique_ptr<LightTransformer> create_default_transition();
virtual void setup_state(LightState *state) {}
virtual void write_state(LightState *state) = 0;

View File

@ -1,6 +1,7 @@
#include "esphome/core/log.h"
#include "light_state.h"
#include "light_output.h"
#include "esphome/core/log.h"
#include "transformers.h"
namespace esphome {
namespace light {
@ -16,6 +17,7 @@ LightCall LightState::toggle() { return this->make_call().set_state(!this->remot
LightCall LightState::make_call() { return LightCall(this); }
struct LightStateRTCState {
ColorMode color_mode{ColorMode::UNKNOWN};
bool state{false};
float brightness{1.0f};
float color_brightness{1.0f};
@ -24,6 +26,8 @@ struct LightStateRTCState {
float blue{1.0f};
float white{1.0f};
float color_temp{1.0f};
float cold_white{1.0f};
float warm_white{1.0f};
uint32_t effect{0};
};
@ -35,6 +39,13 @@ void LightState::setup() {
effect->init_internal(this);
}
// When supported color temperature range is known, initialize color temperature setting within bounds.
float min_mireds = this->get_traits().get_min_mireds();
if (min_mireds > 0) {
this->remote_values.set_color_temperature(min_mireds);
this->current_values.set_color_temperature(min_mireds);
}
auto call = this->make_call();
LightStateRTCState recovered{};
switch (this->restore_mode_) {
@ -64,6 +75,7 @@ void LightState::setup() {
break;
}
call.set_color_mode_if_supported(recovered.color_mode);
call.set_state(recovered.state);
call.set_brightness_if_supported(recovered.brightness);
call.set_color_brightness_if_supported(recovered.color_brightness);
@ -72,6 +84,8 @@ void LightState::setup() {
call.set_blue_if_supported(recovered.blue);
call.set_white_if_supported(recovered.white);
call.set_color_temperature_if_supported(recovered.color_temp);
call.set_cold_white_if_supported(recovered.cold_white);
call.set_warm_white_if_supported(recovered.warm_white);
if (recovered.effect != 0) {
call.set_effect(recovered.effect);
} else {
@ -81,11 +95,11 @@ void LightState::setup() {
}
void LightState::dump_config() {
ESP_LOGCONFIG(TAG, "Light '%s'", this->get_name().c_str());
if (this->get_traits().get_supports_brightness()) {
if (this->get_traits().supports_color_capability(ColorCapability::BRIGHTNESS)) {
ESP_LOGCONFIG(TAG, " Default Transition Length: %.1fs", this->default_transition_length_ / 1e3f);
ESP_LOGCONFIG(TAG, " Gamma Correct: %.2f", this->gamma_correct_);
}
if (this->get_traits().get_supports_color_temperature()) {
if (this->get_traits().supports_color_capability(ColorCapability::COLOR_TEMPERATURE)) {
ESP_LOGCONFIG(TAG, " Min Mireds: %.1f", this->get_traits().get_min_mireds());
ESP_LOGCONFIG(TAG, " Max Mireds: %.1f", this->get_traits().get_max_mireds());
}
@ -99,19 +113,19 @@ void LightState::loop() {
// Apply transformer (if any)
if (this->transformer_ != nullptr) {
auto values = this->transformer_->apply();
this->next_write_ = values.has_value(); // don't write if transformer doesn't want us to
if (values.has_value())
this->current_values = *values;
if (this->transformer_->is_finished()) {
this->remote_values = this->current_values = this->transformer_->get_end_values();
this->target_state_reached_callback_.call();
if (this->transformer_->publish_at_end())
this->publish_state();
this->transformer_->stop();
this->transformer_ = nullptr;
} else {
this->current_values = this->transformer_->get_values();
this->remote_values = this->transformer_->get_remote_values();
this->target_state_reached_callback_.call();
}
this->next_write_ = true;
}
// Write state to the light
if (this->next_write_) {
this->output_->write_state(this);
this->next_write_ = false;
@ -141,14 +155,6 @@ void LightState::add_new_target_state_reached_callback(std::function<void()> &&s
this->target_state_reached_callback_.add(std::move(send_callback));
}
#ifdef USE_JSON
void LightState::dump_json(JsonObject &root) {
if (this->supports_effects())
root["effect"] = this->get_effect_name();
this->remote_values.dump_json(root, this->output_->get_traits());
}
#endif
void LightState::set_default_transition_length(uint32_t default_transition_length) {
this->default_transition_length_ = default_transition_length;
}
@ -169,23 +175,30 @@ void LightState::current_values_as_brightness(float *brightness) {
}
void LightState::current_values_as_rgb(float *red, float *green, float *blue, bool color_interlock) {
auto traits = this->get_traits();
this->current_values.as_rgb(red, green, blue, this->gamma_correct_, traits.get_supports_color_interlock());
this->current_values.as_rgb(red, green, blue, this->gamma_correct_, false);
}
void LightState::current_values_as_rgbw(float *red, float *green, float *blue, float *white, bool color_interlock) {
auto traits = this->get_traits();
this->current_values.as_rgbw(red, green, blue, white, this->gamma_correct_, traits.get_supports_color_interlock());
this->current_values.as_rgbw(red, green, blue, white, this->gamma_correct_, false);
}
void LightState::current_values_as_rgbww(float *red, float *green, float *blue, float *cold_white, float *warm_white,
bool constant_brightness, bool color_interlock) {
bool constant_brightness) {
this->current_values.as_rgbww(red, green, blue, cold_white, warm_white, this->gamma_correct_, constant_brightness);
}
void LightState::current_values_as_rgbct(float *red, float *green, float *blue, float *color_temperature,
float *white_brightness) {
auto traits = this->get_traits();
this->current_values.as_rgbww(traits.get_min_mireds(), traits.get_max_mireds(), red, green, blue, cold_white,
warm_white, this->gamma_correct_, constant_brightness,
traits.get_supports_color_interlock());
this->current_values.as_rgbct(traits.get_min_mireds(), traits.get_max_mireds(), red, green, blue, color_temperature,
white_brightness, this->gamma_correct_);
}
void LightState::current_values_as_cwww(float *cold_white, float *warm_white, bool constant_brightness) {
auto traits = this->get_traits();
this->current_values.as_cwww(traits.get_min_mireds(), traits.get_max_mireds(), cold_white, warm_white,
this->gamma_correct_, constant_brightness);
this->current_values.as_cwww(cold_white, warm_white, this->gamma_correct_, constant_brightness);
}
void LightState::current_values_as_ct(float *color_temperature, float *white_brightness) {
auto traits = this->get_traits();
this->current_values.as_ct(traits.get_min_mireds(), traits.get_max_mireds(), color_temperature, white_brightness,
this->gamma_correct_);
}
void LightState::start_effect_(uint32_t effect_index) {
@ -212,18 +225,21 @@ void LightState::stop_effect_() {
}
void LightState::start_transition_(const LightColorValues &target, uint32_t length) {
this->transformer_ = make_unique<LightTransitionTransformer>(millis(), length, this->current_values, target);
this->remote_values = this->transformer_->get_remote_values();
this->transformer_ = this->output_->create_default_transition();
this->transformer_->setup(this->current_values, target, length);
this->remote_values = target;
}
void LightState::start_flash_(const LightColorValues &target, uint32_t length) {
LightColorValues end_colors = this->current_values;
LightColorValues end_colors = this->remote_values;
// If starting a flash if one is already happening, set end values to end values of current flash
// Hacky but works
if (this->transformer_ != nullptr)
end_colors = this->transformer_->get_end_values();
this->transformer_ = make_unique<LightFlashTransformer>(millis(), length, end_colors, target);
this->remote_values = this->transformer_->get_remote_values();
end_colors = this->transformer_->get_target_values();
this->transformer_ = make_unique<LightFlashTransformer>(*this);
this->transformer_->setup(end_colors, target, length);
this->remote_values = target;
}
void LightState::set_immediately_(const LightColorValues &target, bool set_remote_values) {
@ -235,12 +251,9 @@ void LightState::set_immediately_(const LightColorValues &target, bool set_remot
this->next_write_ = true;
}
void LightState::set_transformer_(std::unique_ptr<LightTransformer> transformer) {
this->transformer_ = std::move(transformer);
}
void LightState::save_remote_values_() {
LightStateRTCState saved;
saved.color_mode = this->remote_values.get_color_mode();
saved.state = this->remote_values.is_on();
saved.brightness = this->remote_values.get_brightness();
saved.color_brightness = this->remote_values.get_color_brightness();
@ -249,6 +262,8 @@ void LightState::save_remote_values_() {
saved.blue = this->remote_values.get_blue();
saved.white = this->remote_values.get_white();
saved.color_temp = this->remote_values.get_color_temperature();
saved.cold_white = this->remote_values.get_cold_white();
saved.warm_white = this->remote_values.get_warm_white();
saved.effect = this->active_effect_index_;
this->rtc_.save(&saved);
}

View File

@ -3,9 +3,9 @@
#include "esphome/core/component.h"
#include "esphome/core/optional.h"
#include "esphome/core/preferences.h"
#include "light_effect.h"
#include "light_color_values.h"
#include "light_call.h"
#include "light_color_values.h"
#include "light_effect.h"
#include "light_traits.h"
#include "light_transformer.h"
@ -54,6 +54,8 @@ class LightState : public Nameable, public Component {
* property will be changed continuously (in contrast to .remote_values, where they
* are constant during transitions).
*
* This value does not have gamma correction applied.
*
* This property is read-only for users. Any changes to it will be ignored.
*/
LightColorValues current_values;
@ -64,6 +66,8 @@ class LightState : public Nameable, public Component {
* continuously change the "current" values. But the remote values will immediately
* switch to the target value for a transition, reducing the number of packets sent.
*
* This value does not have gamma correction applied.
*
* This property is read-only for users. Any changes to it will be ignored.
*/
LightColorValues remote_values;
@ -93,11 +97,6 @@ class LightState : public Nameable, public Component {
*/
void add_new_target_state_reached_callback(std::function<void()> &&send_callback);
#ifdef USE_JSON
/// Dump the state of this light as JSON.
void dump_json(JsonObject &root);
#endif
/// Set the default transition length, i.e. the transition length when no transition is provided.
void set_default_transition_length(uint32_t default_transition_length);
@ -117,6 +116,7 @@ class LightState : public Nameable, public Component {
/// Add effects for this light state.
void add_effects(const std::vector<LightEffect *> &effects);
/// The result of all the current_values_as_* methods have gamma correction applied.
void current_values_as_binary(bool *binary);
void current_values_as_brightness(float *brightness);
@ -126,10 +126,15 @@ class LightState : public Nameable, public Component {
void current_values_as_rgbw(float *red, float *green, float *blue, float *white, bool color_interlock = false);
void current_values_as_rgbww(float *red, float *green, float *blue, float *cold_white, float *warm_white,
bool constant_brightness = false, bool color_interlock = false);
bool constant_brightness = false);
void current_values_as_rgbct(float *red, float *green, float *blue, float *color_temperature,
float *white_brightness);
void current_values_as_cwww(float *cold_white, float *warm_white, bool constant_brightness = false);
void current_values_as_ct(float *color_temperature, float *white_brightness);
protected:
friend LightOutput;
friend LightCall;
@ -152,9 +157,6 @@ class LightState : public Nameable, public Component {
/// Internal method to set the color values to target immediately (with no transition).
void set_immediately_(const LightColorValues &target, bool set_remote_values);
/// Internal method to start a transformer.
void set_transformer_(std::unique_ptr<LightTransformer> transformer);
/// Internal method to save the current remote_values to the preferences
void save_remote_values_();

View File

@ -1,5 +1,9 @@
#pragma once
#include "esphome/core/helpers.h"
#include "color_mode.h"
#include <set>
namespace esphome {
namespace light {
@ -8,35 +12,49 @@ class LightTraits {
public:
LightTraits() = default;
bool get_supports_brightness() const { return this->supports_brightness_; }
void set_supports_brightness(bool supports_brightness) { this->supports_brightness_ = supports_brightness; }
bool get_supports_rgb() const { return this->supports_rgb_; }
void set_supports_rgb(bool supports_rgb) { this->supports_rgb_ = supports_rgb; }
bool get_supports_rgb_white_value() const { return this->supports_rgb_white_value_; }
void set_supports_rgb_white_value(bool supports_rgb_white_value) {
this->supports_rgb_white_value_ = supports_rgb_white_value;
const std::set<ColorMode> &get_supported_color_modes() const { return this->supported_color_modes_; }
void set_supported_color_modes(std::set<ColorMode> supported_color_modes) {
this->supported_color_modes_ = std::move(supported_color_modes);
}
bool get_supports_color_temperature() const { return this->supports_color_temperature_; }
void set_supports_color_temperature(bool supports_color_temperature) {
this->supports_color_temperature_ = supports_color_temperature;
bool supports_color_mode(ColorMode color_mode) const { return this->supported_color_modes_.count(color_mode); }
bool supports_color_capability(ColorCapability color_capability) const {
for (auto mode : this->supported_color_modes_) {
if (mode & color_capability)
return true;
}
return false;
}
bool get_supports_color_interlock() const { return this->supports_color_interlock_; }
void set_supports_color_interlock(bool supports_color_interlock) {
this->supports_color_interlock_ = supports_color_interlock;
ESPDEPRECATED("get_supports_brightness() is deprecated, use color modes instead.", "v1.21")
bool get_supports_brightness() const { return this->supports_color_capability(ColorCapability::BRIGHTNESS); }
ESPDEPRECATED("get_supports_rgb() is deprecated, use color modes instead.", "v1.21")
bool get_supports_rgb() const { return this->supports_color_capability(ColorCapability::RGB); }
ESPDEPRECATED("get_supports_rgb_white_value() is deprecated, use color modes instead.", "v1.21")
bool get_supports_rgb_white_value() const {
return this->supports_color_mode(ColorMode::RGB_WHITE) ||
this->supports_color_mode(ColorMode::RGB_COLOR_TEMPERATURE);
}
ESPDEPRECATED("get_supports_color_temperature() is deprecated, use color modes instead.", "v1.21")
bool get_supports_color_temperature() const {
return this->supports_color_capability(ColorCapability::COLOR_TEMPERATURE);
}
ESPDEPRECATED("get_supports_color_interlock() is deprecated, use color modes instead.", "v1.21")
bool get_supports_color_interlock() const {
return this->supports_color_mode(ColorMode::RGB) &&
(this->supports_color_mode(ColorMode::WHITE) || this->supports_color_mode(ColorMode::COLD_WARM_WHITE) ||
this->supports_color_mode(ColorMode::COLOR_TEMPERATURE));
}
float get_min_mireds() const { return this->min_mireds_; }
void set_min_mireds(float min_mireds) { this->min_mireds_ = min_mireds; }
float get_max_mireds() const { return this->max_mireds_; }
void set_max_mireds(float max_mireds) { this->max_mireds_ = max_mireds; }
protected:
bool supports_brightness_{false};
bool supports_rgb_{false};
bool supports_rgb_white_value_{false};
bool supports_color_temperature_{false};
std::set<ColorMode> supported_color_modes_{};
float min_mireds_{0};
float max_mireds_{0};
bool supports_color_interlock_{false};
};
} // namespace light

View File

@ -1,42 +1,42 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/helpers.h"
#include "light_color_values.h"
namespace esphome {
namespace light {
/// Base-class for all light color transformers, such as transitions or flashes.
/// Base class for all light color transformers, such as transitions or flashes.
class LightTransformer {
public:
LightTransformer(uint32_t start_time, uint32_t length, const LightColorValues &start_values,
const LightColorValues &target_values)
: start_time_(start_time), length_(length), start_values_(start_values), target_values_(target_values) {}
void setup(const LightColorValues &start_values, const LightColorValues &target_values, uint32_t length) {
this->start_time_ = millis();
this->length_ = length;
this->start_values_ = start_values;
this->target_values_ = target_values;
this->start();
}
LightTransformer() = delete;
/// Indicates whether this transformation is finished.
virtual bool is_finished() { return this->get_progress_() >= 1.0f; }
/// Whether this transformation is finished
virtual bool is_finished() { return this->get_progress() >= 1.0f; }
/// This will be called before the transition is started.
virtual void start() {}
/// This will be called to get the current values for output.
virtual LightColorValues get_values() = 0;
/// This will be called while the transformer is active to apply the transition to the light. Can either write to the
/// light directly, or return LightColorValues that will be applied.
virtual optional<LightColorValues> apply() = 0;
/// The values that should be reported to the front-end.
virtual LightColorValues get_remote_values() { return this->get_target_values_(); }
/// This will be called after transition is finished.
virtual void stop() {}
/// The values that should be set after this transformation is complete.
virtual LightColorValues get_end_values() { return this->get_target_values_(); }
const LightColorValues &get_start_values() const { return this->start_values_; }
virtual bool publish_at_end() = 0;
virtual bool is_transition() = 0;
float get_progress() { return clamp((millis() - this->start_time_) / float(this->length_), 0.0f, 1.0f); }
const LightColorValues &get_target_values() const { return this->target_values_; }
protected:
const LightColorValues &get_start_values_() const { return this->start_values_; }
const LightColorValues &get_target_values_() const { return this->target_values_; }
/// The progress of this transition, on a scale of 0 to 1.
float get_progress_() { return clamp((millis() - this->start_time_) / float(this->length_), 0.0f, 1.0f); }
uint32_t start_time_;
uint32_t length_;
@ -44,46 +44,5 @@ class LightTransformer {
LightColorValues target_values_;
};
class LightTransitionTransformer : public LightTransformer {
public:
LightTransitionTransformer(uint32_t start_time, uint32_t length, const LightColorValues &start_values,
const LightColorValues &target_values)
: LightTransformer(start_time, length, start_values, target_values) {
// When turning light on from off state, use colors from new.
if (!this->start_values_.is_on() && this->target_values_.is_on()) {
this->start_values_.set_brightness(0.0f);
this->start_values_.set_red(target_values.get_red());
this->start_values_.set_green(target_values.get_green());
this->start_values_.set_blue(target_values.get_blue());
this->start_values_.set_white(target_values.get_white());
this->start_values_.set_color_temperature(target_values.get_color_temperature());
}
}
LightColorValues get_values() override {
float v = LightTransitionTransformer::smoothed_progress(this->get_progress());
return LightColorValues::lerp(this->get_start_values_(), this->get_target_values_(), v);
}
bool publish_at_end() override { return false; }
bool is_transition() override { return true; }
static float smoothed_progress(float x) { return x * x * x * (x * (x * 6.0f - 15.0f) + 10.0f); }
};
class LightFlashTransformer : public LightTransformer {
public:
LightFlashTransformer(uint32_t start_time, uint32_t length, const LightColorValues &start_values,
const LightColorValues &target_values)
: LightTransformer(start_time, length, start_values, target_values) {}
LightColorValues get_values() override { return this->get_target_values_(); }
LightColorValues get_end_values() override { return this->get_start_values_(); }
bool publish_at_end() override { return true; }
bool is_transition() override { return false; }
};
} // namespace light
} // namespace esphome

View File

@ -0,0 +1,75 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/helpers.h"
#include "light_color_values.h"
#include "light_state.h"
#include "light_transformer.h"
namespace esphome {
namespace light {
class LightTransitionTransformer : public LightTransformer {
public:
void start() override {
// When turning light on from off state, use colors from target state.
if (!this->start_values_.is_on() && this->target_values_.is_on()) {
this->start_values_ = LightColorValues(this->target_values_);
this->start_values_.set_brightness(0.0f);
}
// When changing color mode, go through off state, as color modes are orthogonal and there can't be two active.
if (this->start_values_.get_color_mode() != this->target_values_.get_color_mode()) {
this->changing_color_mode_ = true;
this->intermediate_values_ = this->start_values_;
this->intermediate_values_.set_state(false);
}
}
optional<LightColorValues> apply() override {
float p = this->get_progress_();
// Halfway through, when intermediate state (off) is reached, flip it to the target, but remain off.
if (this->changing_color_mode_ && p > 0.5f &&
this->intermediate_values_.get_color_mode() != this->target_values_.get_color_mode()) {
this->intermediate_values_ = this->target_values_;
this->intermediate_values_.set_state(false);
}
LightColorValues &start = this->changing_color_mode_ && p > 0.5f ? this->intermediate_values_ : this->start_values_;
LightColorValues &end = this->changing_color_mode_ && p < 0.5f ? this->intermediate_values_ : this->target_values_;
if (this->changing_color_mode_)
p = p < 0.5f ? p * 2 : (p - 0.5) * 2;
float v = LightTransitionTransformer::smoothed_progress(p);
return LightColorValues::lerp(start, end, v);
}
protected:
// This looks crazy, but it reduces to 6x^5 - 15x^4 + 10x^3 which is just a smooth sigmoid-like
// transition from 0 to 1 on x = [0, 1]
static float smoothed_progress(float x) { return x * x * x * (x * (x * 6.0f - 15.0f) + 10.0f); }
bool changing_color_mode_{false};
LightColorValues intermediate_values_{};
};
class LightFlashTransformer : public LightTransformer {
public:
LightFlashTransformer(LightState &state) : state_(state) {}
optional<LightColorValues> apply() override { return this->get_target_values(); }
// Restore the original values after the flash.
void stop() override {
this->state_.current_values = this->get_start_values();
this->state_.remote_values = this->get_start_values();
this->state_.publish_state();
}
protected:
LightState &state_;
};
} // namespace light
} // namespace esphome

View File

@ -13,6 +13,20 @@ AddressableLightRef = AddressableLight.operator("ref")
Color = cg.esphome_ns.class_("Color")
LightColorValues = light_ns.class_("LightColorValues")
# Color modes
ColorMode = light_ns.enum("ColorMode", is_class=True)
COLOR_MODES = {
"ON_OFF": ColorMode.ON_OFF,
"BRIGHTNESS": ColorMode.BRIGHTNESS,
"WHITE": ColorMode.WHITE,
"COLOR_TEMPERATURE": ColorMode.COLOR_TEMPERATURE,
"COLD_WARM_WHITE": ColorMode.COLD_WARM_WHITE,
"RGB": ColorMode.RGB,
"RGB_WHITE": ColorMode.RGB_WHITE,
"RGB_COLOR_TEMPERATURE": ColorMode.RGB_COLOR_TEMPERATURE,
"RGB_COLD_WARM_WHITE": ColorMode.RGB_COLD_WARM_WHITE,
}
# Actions
ToggleAction = light_ns.class_("ToggleAction", automation.Action)
LightControlAction = light_ns.class_("LightControlAction", automation.Action)

View File

@ -7,6 +7,7 @@ from esphome.automation import LambdaAction
from esphome.const import (
CONF_ARGS,
CONF_BAUD_RATE,
CONF_DEASSERT_RTS_DTR,
CONF_FORMAT,
CONF_HARDWARE_UART,
CONF_ID,
@ -104,6 +105,7 @@ CONFIG_SCHEMA = cv.All(
cv.GenerateID(): cv.declare_id(Logger),
cv.Optional(CONF_BAUD_RATE, default=115200): cv.positive_int,
cv.Optional(CONF_TX_BUFFER_SIZE, default=512): cv.validate_bytes,
cv.Optional(CONF_DEASSERT_RTS_DTR, default=False): cv.boolean,
cv.Optional(CONF_HARDWARE_UART, default="UART0"): uart_selection,
cv.Optional(CONF_LEVEL, default="DEBUG"): is_log_level,
cv.Optional(CONF_LOGS, default={}): cv.Schema(

View File

@ -5,7 +5,6 @@ from esphome.const import (
CONF_ID,
CONF_REFERENCE_TEMPERATURE,
DEVICE_CLASS_TEMPERATURE,
ICON_EMPTY,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
)
@ -16,16 +15,19 @@ MAX31855Sensor = max31855_ns.class_(
)
CONFIG_SCHEMA = (
sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE)
sensor.sensor_schema(
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE,
)
.extend(
{
cv.GenerateID(): cv.declare_id(MAX31855Sensor),
cv.Optional(CONF_REFERENCE_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS,
ICON_EMPTY,
2,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=2,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
),
}
)

View File

@ -5,7 +5,6 @@ from esphome.const import (
CONF_ID,
CONF_MAINS_FILTER,
DEVICE_CLASS_TEMPERATURE,
ICON_EMPTY,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
)
@ -23,11 +22,10 @@ FILTER = {
CONFIG_SCHEMA = (
sensor.sensor_schema(
UNIT_CELSIUS,
ICON_EMPTY,
1,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
)
.extend(
{

View File

@ -8,7 +8,6 @@ from esphome.const import (
CONF_RTD_NOMINAL_RESISTANCE,
CONF_RTD_WIRES,
DEVICE_CLASS_TEMPERATURE,
ICON_EMPTY,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
)
@ -26,7 +25,10 @@ FILTER = {
CONFIG_SCHEMA = (
sensor.sensor_schema(
UNIT_CELSIUS, ICON_EMPTY, 2, DEVICE_CLASS_TEMPERATURE, STATE_CLASS_MEASUREMENT
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=2,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
)
.extend(
{

View File

@ -4,7 +4,6 @@ from esphome.components import sensor, spi
from esphome.const import (
CONF_ID,
DEVICE_CLASS_TEMPERATURE,
ICON_EMPTY,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
)
@ -16,7 +15,10 @@ MAX6675Sensor = max6675_ns.class_(
CONFIG_SCHEMA = (
sensor.sensor_schema(
UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE, STATE_CLASS_MEASUREMENT
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
)
.extend(
{

View File

@ -104,7 +104,7 @@ void MAX7219Component::display() {
uint8_t pixels[8];
// Run this loop for every MAX CHIP (GRID OF 64 leds)
// Run this routine for the rows of every chip 8x row 0 top to 7 bottom
// Fill the pixel parameter with diplay data
// Fill the pixel parameter with display data
// Send the data to the chip
for (uint8_t i = 0; i < this->num_chips_; i++) {
for (uint8_t j = 0; j < 8; j++) {
@ -119,7 +119,7 @@ void MAX7219Component::display() {
}
int MAX7219Component::get_height_internal() {
return 8; // TO BE DONE -> STACK TWO DISPLAYS ON TOP OF EACH OTHE
return 8; // TO BE DONE -> STACK TWO DISPLAYS ON TOP OF EACH OTHER
// TO BE DONE -> CREATE Virtual size of screen and scroll
}
@ -238,7 +238,7 @@ void MAX7219Component::send64pixels(uint8_t chip, const uint8_t pixels[8]) {
} else {
b = pixels[7 - col];
}
// send this byte to dispay at selected chip
// send this byte to display at selected chip
if (this->invert_) {
this->send_byte_(col + 1, ~b);
} else {

View File

@ -4,7 +4,6 @@ from esphome.components import i2c, sensor
from esphome.const import (
CONF_ID,
DEVICE_CLASS_TEMPERATURE,
ICON_EMPTY,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
)
@ -19,7 +18,10 @@ MCP9808Sensor = mcp9808_ns.class_(
CONFIG_SCHEMA = (
sensor.sensor_schema(
UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE, STATE_CLASS_MEASUREMENT
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
)
.extend(
{

View File

@ -7,13 +7,11 @@ from esphome.const import (
CONF_CO2,
CONF_ID,
CONF_TEMPERATURE,
DEVICE_CLASS_EMPTY,
DEVICE_CLASS_TEMPERATURE,
ICON_MOLECULE_CO2,
STATE_CLASS_MEASUREMENT,
UNIT_PARTS_PER_MILLION,
UNIT_CELSIUS,
ICON_EMPTY,
)
DEPENDENCIES = ["uart"]
@ -33,18 +31,16 @@ CONFIG_SCHEMA = (
{
cv.GenerateID(): cv.declare_id(MHZ19Component),
cv.Required(CONF_CO2): sensor.sensor_schema(
UNIT_PARTS_PER_MILLION,
ICON_MOLECULE_CO2,
0,
DEVICE_CLASS_EMPTY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_PARTS_PER_MILLION,
icon=ICON_MOLECULE_CO2,
accuracy_decimals=0,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS,
ICON_EMPTY,
0,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=0,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_AUTOMATIC_BASELINE_CALIBRATION): cv.boolean,
}

View File

@ -63,21 +63,25 @@ CONFIG_SCHEMA = cv.All(
cv.Optional(CONF_PRESET_SLEEP, default=False): cv.boolean,
cv.Optional(CONF_PRESET_BOOST, default=False): cv.boolean,
cv.Optional(CONF_OUTDOOR_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS,
ICON_THERMOMETER,
0,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_CELSIUS,
icon=ICON_THERMOMETER,
accuracy_decimals=0,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_POWER_USAGE): sensor.sensor_schema(
UNIT_WATT, ICON_POWER, 0, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
unit_of_measurement=UNIT_WATT,
icon=ICON_POWER,
accuracy_decimals=0,
device_class=DEVICE_CLASS_POWER,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_HUMIDITY_SETPOINT): sensor.sensor_schema(
UNIT_PERCENT,
ICON_WATER_PERCENT,
0,
DEVICE_CLASS_HUMIDITY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_PERCENT,
icon=ICON_WATER_PERCENT,
accuracy_decimals=0,
device_class=DEVICE_CLASS_HUMIDITY,
state_class=STATE_CLASS_MEASUREMENT,
),
}
).extend(cv.COMPONENT_SCHEMA)

View File

@ -12,7 +12,7 @@ class MonochromaticLightOutput : public light::LightOutput {
void set_output(output::FloatOutput *output) { output_ = output; }
light::LightTraits get_traits() override {
auto traits = light::LightTraits();
traits.set_supports_brightness(true);
traits.set_supported_color_modes({light::ColorMode::BRIGHTNESS});
return traits;
}
void write_state(light::LightState *state) override {

View File

@ -4,10 +4,8 @@ from esphome.components import i2c, sensor
from esphome.const import (
CONF_ID,
CONF_TEMPERATURE,
DEVICE_CLASS_EMPTY,
DEVICE_CLASS_TEMPERATURE,
ICON_BRIEFCASE_DOWNLOAD,
ICON_EMPTY,
STATE_CLASS_MEASUREMENT,
UNIT_METER_PER_SECOND_SQUARED,
ICON_SCREEN_ROTATION,
@ -30,21 +28,22 @@ MPU6050Component = mpu6050_ns.class_(
)
accel_schema = sensor.sensor_schema(
UNIT_METER_PER_SECOND_SQUARED,
ICON_BRIEFCASE_DOWNLOAD,
2,
DEVICE_CLASS_EMPTY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_METER_PER_SECOND_SQUARED,
icon=ICON_BRIEFCASE_DOWNLOAD,
accuracy_decimals=2,
state_class=STATE_CLASS_MEASUREMENT,
)
gyro_schema = sensor.sensor_schema(
UNIT_DEGREE_PER_SECOND,
ICON_SCREEN_ROTATION,
2,
DEVICE_CLASS_EMPTY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_DEGREE_PER_SECOND,
icon=ICON_SCREEN_ROTATION,
accuracy_decimals=2,
state_class=STATE_CLASS_MEASUREMENT,
)
temperature_schema = sensor.sensor_schema(
UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE, STATE_CLASS_MEASUREMENT
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
)
CONFIG_SCHEMA = (

View File

@ -92,6 +92,7 @@ MQTTSensorComponent = mqtt_ns.class_("MQTTSensorComponent", MQTTComponent)
MQTTSwitchComponent = mqtt_ns.class_("MQTTSwitchComponent", MQTTComponent)
MQTTTextSensor = mqtt_ns.class_("MQTTTextSensor", MQTTComponent)
MQTTNumberComponent = mqtt_ns.class_("MQTTNumberComponent", MQTTComponent)
MQTTSelectComponent = mqtt_ns.class_("MQTTSelectComponent", MQTTComponent)
def validate_config(value):

View File

@ -61,6 +61,9 @@ void MQTTCoverComponent::dump_config() {
}
}
void MQTTCoverComponent::send_discovery(JsonObject &root, mqtt::SendDiscoveryConfig &config) {
if (!this->cover_->get_device_class().empty())
root["device_class"] = this->cover_->get_device_class();
auto traits = this->cover_->get_traits();
if (traits.get_is_assumed_state()) {
root["optimistic"] = true;

View File

@ -3,6 +3,7 @@
#ifdef USE_LIGHT
#include "esphome/components/light/light_json_schema.h"
namespace esphome {
namespace mqtt {
@ -14,7 +15,9 @@ std::string MQTTJSONLightComponent::component_type() const { return "light"; }
void MQTTJSONLightComponent::setup() {
this->subscribe_json(this->get_command_topic_(), [this](const std::string &topic, JsonObject &root) {
this->state_->make_call().parse_json(root).perform();
LightCall call = this->state_->make_call();
LightJSONSchema::parse_json(*this->state_, call, root);
call.perform();
});
auto f = std::bind(&MQTTJSONLightComponent::publish_state_, this);
@ -24,21 +27,40 @@ void MQTTJSONLightComponent::setup() {
MQTTJSONLightComponent::MQTTJSONLightComponent(LightState *state) : MQTTComponent(), state_(state) {}
bool MQTTJSONLightComponent::publish_state_() {
return this->publish_json(this->get_state_topic_(), [this](JsonObject &root) { this->state_->dump_json(root); });
return this->publish_json(this->get_state_topic_(),
[this](JsonObject &root) { LightJSONSchema::dump_json(*this->state_, root); });
}
LightState *MQTTJSONLightComponent::get_state() const { return this->state_; }
std::string MQTTJSONLightComponent::friendly_name() const { return this->state_->get_name(); }
void MQTTJSONLightComponent::send_discovery(JsonObject &root, mqtt::SendDiscoveryConfig &config) {
root["schema"] = "json";
auto traits = this->state_->get_traits();
if (traits.get_supports_brightness())
root["color_mode"] = true;
JsonArray &color_modes = root.createNestedArray("supported_color_modes");
if (traits.supports_color_mode(ColorMode::COLOR_TEMPERATURE))
color_modes.add("color_temp");
if (traits.supports_color_mode(ColorMode::RGB))
color_modes.add("rgb");
if (traits.supports_color_mode(ColorMode::RGB_WHITE))
color_modes.add("rgbw");
if (traits.supports_color_mode(ColorMode::RGB_COLD_WARM_WHITE))
color_modes.add("rgbww");
if (traits.supports_color_mode(ColorMode::BRIGHTNESS))
color_modes.add("brightness");
if (traits.supports_color_mode(ColorMode::ON_OFF))
color_modes.add("onoff");
// legacy API
if (traits.supports_color_capability(ColorCapability::BRIGHTNESS))
root["brightness"] = true;
if (traits.get_supports_rgb())
if (traits.supports_color_capability(ColorCapability::RGB))
root["rgb"] = true;
if (traits.get_supports_color_temperature())
if (traits.supports_color_capability(ColorCapability::COLOR_TEMPERATURE))
root["color_temp"] = true;
if (traits.get_supports_rgb_white_value())
if (traits.supports_color_capability(ColorCapability::WHITE))
root["white_value"] = true;
if (this->state_->supports_effects()) {
root["effect"] = true;
JsonArray &effect_list = root.createNestedArray("effect_list");

View File

@ -0,0 +1,58 @@
#include "mqtt_select.h"
#include "esphome/core/log.h"
#ifdef USE_SELECT
namespace esphome {
namespace mqtt {
static const char *const TAG = "mqtt.select";
using namespace esphome::select;
MQTTSelectComponent::MQTTSelectComponent(Select *select) : MQTTComponent(), select_(select) {}
void MQTTSelectComponent::setup() {
this->subscribe(this->get_command_topic_(), [this](const std::string &topic, const std::string &state) {
auto call = this->select_->make_call();
call.set_option(state);
call.perform();
});
this->select_->add_on_state_callback([this](const std::string &state) { this->publish_state(state); });
}
void MQTTSelectComponent::dump_config() {
ESP_LOGCONFIG(TAG, "MQTT Select '%s':", this->select_->get_name().c_str());
LOG_MQTT_COMPONENT(true, false)
}
std::string MQTTSelectComponent::component_type() const { return "select"; }
std::string MQTTSelectComponent::friendly_name() const { return this->select_->get_name(); }
void MQTTSelectComponent::send_discovery(JsonObject &root, mqtt::SendDiscoveryConfig &config) {
const auto &traits = select_->traits;
// https://www.home-assistant.io/integrations/select.mqtt/
if (!traits.get_icon().empty())
root["icon"] = traits.get_icon();
JsonArray &options = root.createNestedArray("options");
for (const auto &option : traits.get_options())
options.add(option);
config.command_topic = true;
}
bool MQTTSelectComponent::send_initial_state() {
if (this->select_->has_state()) {
return this->publish_state(this->select_->state);
} else {
return true;
}
}
bool MQTTSelectComponent::is_internal() { return this->select_->is_internal(); }
bool MQTTSelectComponent::publish_state(const std::string &value) {
return this->publish(this->get_state_topic_(), value);
}
} // namespace mqtt
} // namespace esphome
#endif

View File

@ -0,0 +1,46 @@
#pragma once
#include "esphome/core/defines.h"
#ifdef USE_SELECT
#include "esphome/components/select/select.h"
#include "mqtt_component.h"
namespace esphome {
namespace mqtt {
class MQTTSelectComponent : public mqtt::MQTTComponent {
public:
/** Construct this MQTTSelectComponent instance with the provided friendly_name and select
*
* @param select The select.
*/
explicit MQTTSelectComponent(select::Select *select);
// ========== INTERNAL METHODS ==========
// (In most use cases you won't need these)
/// Override setup.
void setup() override;
void dump_config() override;
void send_discovery(JsonObject &root, mqtt::SendDiscoveryConfig &config) override;
bool send_initial_state() override;
bool is_internal() override;
bool publish_state(const std::string &value);
protected:
/// Override for MQTTComponent, returns "select".
std::string component_type() const override;
std::string friendly_name() const override;
select::Select *select_;
};
} // namespace mqtt
} // namespace esphome
#endif

View File

@ -61,6 +61,9 @@ void MQTTSensorComponent::send_discovery(JsonObject &root, mqtt::SendDiscoveryCo
if (this->sensor_->get_force_update())
root["force_update"] = true;
if (this->sensor_->state_class == sensor::STATE_CLASS_MEASUREMENT)
root["state_class"] = "measurement";
config.command_topic = false;
}
bool MQTTSensorComponent::send_initial_state() {

View File

@ -6,9 +6,6 @@ from esphome.const import (
CONF_QOS,
CONF_TOPIC,
STATE_CLASS_NONE,
UNIT_EMPTY,
ICON_EMPTY,
DEVICE_CLASS_EMPTY,
)
from .. import mqtt_subscribe_ns
@ -21,7 +18,8 @@ MQTTSubscribeSensor = mqtt_subscribe_ns.class_(
CONFIG_SCHEMA = (
sensor.sensor_schema(
UNIT_EMPTY, ICON_EMPTY, 1, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE
accuracy_decimals=1,
state_class=STATE_CLASS_NONE,
)
.extend(
{

View File

@ -7,7 +7,6 @@ from esphome.const import (
CONF_TEMPERATURE,
DEVICE_CLASS_PRESSURE,
DEVICE_CLASS_TEMPERATURE,
ICON_EMPTY,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
ICON_GAUGE,
@ -26,18 +25,17 @@ CONFIG_SCHEMA = (
{
cv.GenerateID(): cv.declare_id(MS5611Component),
cv.Required(CONF_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS,
ICON_EMPTY,
1,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Required(CONF_PRESSURE): sensor.sensor_schema(
UNIT_HECTOPASCAL,
ICON_GAUGE,
1,
DEVICE_CLASS_PRESSURE,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_HECTOPASCAL,
icon=ICON_GAUGE,
accuracy_decimals=1,
device_class=DEVICE_CLASS_PRESSURE,
state_class=STATE_CLASS_MEASUREMENT,
),
}
)

View File

@ -115,8 +115,7 @@ class NeoPixelRGBLightOutput : public NeoPixelBusLightOutputBase<T_METHOD, T_COL
public:
light::LightTraits get_traits() override {
auto traits = light::LightTraits();
traits.set_supports_brightness(true);
traits.set_supports_rgb(true);
traits.set_supported_color_modes({light::ColorMode::RGB});
return traits;
}
@ -133,9 +132,7 @@ class NeoPixelRGBWLightOutput : public NeoPixelBusLightOutputBase<T_METHOD, T_CO
public:
light::LightTraits get_traits() override {
auto traits = light::LightTraits();
traits.set_supports_brightness(true);
traits.set_supports_rgb(true);
traits.set_supports_rgb_white_value(true);
traits.set_supported_color_modes({light::ColorMode::RGB_WHITE});
return traits;
}

View File

@ -2,7 +2,9 @@ from string import ascii_letters, digits
import esphome.config_validation as cv
import esphome.codegen as cg
from esphome.components import color
from esphome.const import (
CONF_VISIBLE,
)
from . import CONF_NEXTION_ID
from . import Nextion
@ -25,7 +27,6 @@ CONF_BACKGROUND_PRESSED_COLOR = "background_pressed_color"
CONF_FOREGROUND_COLOR = "foreground_color"
CONF_FOREGROUND_PRESSED_COLOR = "foreground_pressed_color"
CONF_FONT_ID = "font_id"
CONF_VISIBLE = "visible"
def NextionName(value):

View File

@ -543,6 +543,7 @@ void Nextion::process_nextion_commands_() {
ESP_LOGVV(TAG, "Received Nextion leaves sleep automatically");
this->is_sleeping_ = false;
this->wake_callback_.call();
this->all_components_send_state_(false);
break;
}
case 0x88: // system successful start up
@ -1015,7 +1016,7 @@ void Nextion::add_no_result_to_queue_with_set_internal_(const std::string &varia
*
* @param variable_name Variable name for the queue
* @param variable_name_to_send Variable name for the left of the command
* @param state_value Sting value to set
* @param state_value String value to set
* @param is_sleep_safe The command is safe to send when the Nextion is sleeping
*/
void Nextion::add_no_result_to_queue_with_set(NextionComponentBase *component, const std::string &state_value) {
@ -1086,8 +1087,8 @@ void Nextion::add_addt_command_to_queue(NextionComponentBase *component) {
void Nextion::set_writer(const nextion_writer_t &writer) { this->writer_ = writer; }
ESPDEPRECATED("set_wait_for_ack(bool) is deprecated and has no effect")
void Nextion::set_wait_for_ack(bool wait_for_ack) { ESP_LOGE(TAG, "This command is depreciated"); }
ESPDEPRECATED("set_wait_for_ack(bool) is deprecated and has no effect", "v1.20")
void Nextion::set_wait_for_ack(bool wait_for_ack) { ESP_LOGE(TAG, "This command is deprecated"); }
} // namespace nextion
} // namespace esphome

View File

@ -419,7 +419,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
* fill_area(50, 50, 100, 100, "RED");
* ```
*
* Fills an area that starts at x coordiante `50` and y coordinate `50` with a height of `100` and width of `100` with
* Fills an area that starts at x coordinate `50` and y coordinate `50` with a height of `100` and width of `100` with
* the color of blue. Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to
* convert color codes to Nextion HMI colors
*/
@ -437,7 +437,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
* fill_area(50, 50, 100, 100, color);
* ```
*
* Fills an area that starts at x coordiante `50` and y coordinate `50` with a height of `100` and width of `100` with
* Fills an area that starts at x coordinate `50` and y coordinate `50` with a height of `100` and width of `100` with
* the color of blue. Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to
* convert color codes to Nextion HMI colors
*/
@ -546,7 +546,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
* it.filled_cricle(25, 25, 10, "17013");
* ```
*
* Makes a filled circle at the x cordinates `25` and y coordinate `25` with a radius of `10` with a color of blue.
* Makes a filled circle at the x coordinate `25` and y coordinate `25` with a radius of `10` with a color of blue.
* Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to
* Nextion HMI colors.
*/
@ -563,7 +563,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
* it.filled_cricle(25, 25, 10, color);
* ```
*
* Makes a filled circle at the x cordinates `25` and y coordinate `25` with a radius of `10` with a color of blue.
* Makes a filled circle at the x coordinate `25` and y coordinate `25` with a radius of `10` with a color of blue.
* Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to
* Nextion HMI colors.
*/

View File

@ -275,8 +275,8 @@ void Nextion::upload_tft() {
} else {
#endif
ESP_LOGD(TAG, "Allocating buffer size %d, Heap size is %u", chunk_size, ESP.getFreeHeap());
this->transfer_buffer_ = new uint8_t[chunk_size];
if (!this->transfer_buffer_) { // Try a smaller size
this->transfer_buffer_ = new (std::nothrow) uint8_t[chunk_size];
if (this->transfer_buffer_ == nullptr) { // Try a smaller size
ESP_LOGD(TAG, "Could not allocate buffer size: %d trying 4096 instead", chunk_size);
chunk_size = 4096;
ESP_LOGD(TAG, "Allocating %d buffer", chunk_size);
@ -305,7 +305,7 @@ void Nextion::upload_tft() {
App.feed_wdt();
ESP_LOGD(TAG, "Heap Size %d, Bytes left %d", ESP.getFreeHeap(), this->content_length_);
}
ESP_LOGD(TAG, "Succesfully updated Nextion!");
ESP_LOGD(TAG, "Successfully updated Nextion!");
this->upload_end_();
}

View File

@ -4,10 +4,7 @@ from esphome.components import sensor
from esphome.const import (
CONF_ID,
UNIT_EMPTY,
ICON_EMPTY,
CONF_COMPONENT_ID,
DEVICE_CLASS_EMPTY,
)
from .. import nextion_ns, CONF_NEXTION_ID
@ -46,7 +43,9 @@ def _validate(config):
CONFIG_SCHEMA = cv.All(
sensor.sensor_schema(UNIT_EMPTY, ICON_EMPTY, 2, DEVICE_CLASS_EMPTY)
sensor.sensor_schema(
accuracy_decimals=2,
)
.extend(
{
cv.GenerateID(): cv.declare_id(NextionSensor),

View File

@ -12,7 +12,6 @@ from esphome.const import (
CONF_TEMPERATURE,
CONF_VALUE,
DEVICE_CLASS_TEMPERATURE,
ICON_EMPTY,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
)
@ -120,7 +119,10 @@ def process_calibration(value):
CONFIG_SCHEMA = (
sensor.sensor_schema(
UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE, STATE_CLASS_MEASUREMENT
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
)
.extend(
{

View File

@ -6,6 +6,7 @@ from esphome.components import mqtt
from esphome.const import (
CONF_ABOVE,
CONF_BELOW,
CONF_DISABLED_BY_DEFAULT,
CONF_ICON,
CONF_ID,
CONF_INTERNAL,
@ -45,7 +46,7 @@ NumberInRangeCondition = number_ns.class_(
icon = cv.icon
NUMBER_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend(
NUMBER_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
{
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTNumberComponent),
cv.GenerateID(): cv.declare_id(Number),
@ -71,6 +72,7 @@ async def setup_number_core_(
var, config, *, min_value: float, max_value: float, step: Optional[float]
):
cg.add(var.set_name(config[CONF_NAME]))
cg.add(var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT]))
if CONF_INTERNAL in config:
cg.add(var.set_internal(config[CONF_INTERNAL]))

View File

@ -17,6 +17,8 @@ from esphome.core import CORE
CODEOWNERS = ["@esphome/core"]
IS_PLATFORM_COMPONENT = True
CONF_ZERO_MEANS_ZERO = "zero_means_zero"
BINARY_OUTPUT_SCHEMA = cv.Schema(
{
cv.Optional(CONF_POWER_SUPPLY): cv.use_id(power_supply.PowerSupply),
@ -28,6 +30,7 @@ FLOAT_OUTPUT_SCHEMA = BINARY_OUTPUT_SCHEMA.extend(
{
cv.Optional(CONF_MAX_POWER): cv.percentage,
cv.Optional(CONF_MIN_POWER): cv.percentage,
cv.Optional(CONF_ZERO_MEANS_ZERO, default=False): cv.boolean,
}
)
@ -53,6 +56,8 @@ async def setup_output_platform_(obj, config):
cg.add(obj.set_max_power(config[CONF_MAX_POWER]))
if CONF_MIN_POWER in config:
cg.add(obj.set_min_power(config[CONF_MIN_POWER]))
if CONF_ZERO_MEANS_ZERO in config:
cg.add(obj.set_zero_means_zero(config[CONF_ZERO_MEANS_ZERO]))
async def register_output(var, config):

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