Compare commits

...

433 Commits

Author SHA1 Message Date
Andreas Hergert
e0555e140f
Improvement pipsolar crc (#3316)
Co-authored-by: Andreas <andreas.hergert@otrs.com>
2022-07-07 12:54:19 +12:00
Luca Adrian L
093989406f
Added more sensor device classes (#3624) 2022-07-07 11:32:38 +12:00
dab0g
cdb16f08f6
Update dfplayer condition function name (#3619) 2022-07-04 09:22:04 +12:00
dependabot[bot]
53139c293b
Bump pytest-mock from 3.7.0 to 3.8.1 (#3616) 2022-07-01 14:54:37 +02:00
dependabot[bot]
6a8bdcc315
Bump pylint from 2.14.3 to 2.14.4 (#3617) 2022-07-01 14:54:13 +02:00
dependabot[bot]
fe535939a3
Bump colorama from 0.4.4 to 0.4.5 (#3614) 2022-06-30 12:42:35 +02:00
dependabot[bot]
09e6c11d73
Bump black from 22.3.0 to 22.6.0 (#3613) 2022-06-30 12:42:28 +02:00
dependabot[bot]
8112bdfaa8
Bump pyupgrade from 2.32.1 to 2.34.0 (#3591)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Otto Winter <otto@otto-winter.com>
2022-06-30 11:59:53 +02:00
marshn
f7db9aaa9f
Fix slow_pwm output glitch (#3601) 2022-06-30 11:28:19 +02:00
Javier Peletier
435f972357
fix EDict to dict mapping in helpers.py (#3599) 2022-06-30 11:24:24 +02:00
dependabot[bot]
f82b46c16b
Bump zeroconf from 0.38.4 to 0.38.7 (#3562)
Bumps [zeroconf](https://github.com/jstasiak/python-zeroconf) from 0.38.4 to 0.38.7.
- [Release notes](https://github.com/jstasiak/python-zeroconf/releases)
- [Commits](https://github.com/jstasiak/python-zeroconf/compare/0.38.4...0.38.7)

---
updated-dependencies:
- dependency-name: zeroconf
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-06-30 11:06:22 +02:00
Jesse Hills
bca96f91b2
Remove min_save_interval from intergration and total_daily_energy (#3498) 2022-06-30 16:47:56 +12:00
Benoit3
6f83a49c63
#3358 Correct temperature validity detection issue in some conditions of pu… (#3545)
Co-authored-by: Otto Winter <otto@otto-winter.com>
2022-06-29 21:30:44 +02:00
André Klitzing
72cce391ab
Fix some typos found by codespell (#3598) 2022-06-27 18:02:46 -03:00
Jesse Hills
28d2949ebe
Add github-actions to dependabot (#3595) 2022-06-23 07:51:05 +12:00
Sven Serlier
c4a0015997
Update actions (#3592)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-06-23 07:50:55 +12:00
Jesse Hills
f564be6aea
Do two substitutions passes to allow substitutions inside substitutions (#3583) 2022-06-22 08:21:32 +12:00
dependabot[bot]
988f15e6af
Bump aioesphomeapi from 10.8.2 to 10.10.0 (#3590)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-06-21 17:19:41 +12:00
Zimmermann Zsolt
37b6d442bd
Correcting ESP32 flash save/load key calculation algorithm (#3416)
Co-authored-by: Otto Winter <otto@otto-winter.com>
Co-authored-by: Dr. Zimmermann Zsolt <cina@storage1.cina.hu>
2022-06-21 17:17:51 +12:00
Nick B
fb2467f6f0
DAC7678 support (#3441)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-06-21 17:12:15 +12:00
dependabot[bot]
29045b0435
Bump pylint from 2.13.9 to 2.14.3 (#3589)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-06-21 16:27:33 +12:00
Jesse Hills
311a48c64e
Bump platformio to 6.0.2 (#3566) 2022-06-21 11:53:51 +12:00
Mateus Demboski
01b3815f27
Map LOLIN C3 mini board pins (#3587) 2022-06-21 11:33:20 +12:00
Joe
b0d1c801bd
Fix 2 small issues in BLEClient (#3544) 2022-06-21 11:31:17 +12:00
RoboMagus
5aaac06f5b
RestoringGlobalsComponent: Store value on shutdown (#3586) 2022-06-21 11:27:53 +12:00
RoboMagus
34adbf0588
Fix / Reverse order shutdown (#3585) 2022-06-21 11:26:34 +12:00
Jesse Hills
0a4213182e
Merge branch 'release' into dev 2022-06-21 07:18:41 +12:00
Sergey Dudanov
b0c0258e70
Media Player: added play_media action (#3579)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-06-20 12:17:20 +12:00
kahrendt
8110e591d0
Fix wrong type for voc_state*_ in sgp4x component (#3581)
Co-authored-by: Martin <25747549+martgras@users.noreply.github.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-06-20 11:27:00 +12:00
ShellAddicted
fe05d7aec1
Fix: Make MQTT over TLS actually work (#3580) 2022-06-20 11:17:58 +12:00
lkomurcu
57f5884070
Move gas mbus config option being a define to being a build flag since its used in external libraries. (#3575) 2022-06-20 07:39:40 +12:00
Jesse Hills
f329c74a15
Merge pull request #3578 from esphome/bump-2022.6.1
2022.6.1
2022-06-18 22:31:45 +12:00
Jesse Hills
7c86f3fa9e
Bump version to 2022.6.1 2022-06-18 17:25:52 +12:00
Sergey Dudanov
203b8b01bf
Media Player: added triggers (#3576) 2022-06-18 17:25:51 +12:00
Benjamin Klotz
8a1034a92f
Bugfix for ExternalRAMAllocator copy constructor (#3571) 2022-06-18 17:25:51 +12:00
Jesse Hills
aa0c2dedd9
Setup the mute pin if configured (#3568) 2022-06-18 17:25:51 +12:00
Sergey Dudanov
d045908e05
Media Player: added triggers (#3576) 2022-06-18 17:21:42 +12:00
Guillermo Ruffino
f002a23d2d
Language schema 202204 (#3492) 2022-06-17 13:46:20 +12:00
gazoodle
29d6d0a906
Fix modbus user-defined function handling (#3527) 2022-06-17 13:35:25 +12:00
Benjamin Klotz
c8b58b5c23
Bugfix for ExternalRAMAllocator copy constructor (#3571) 2022-06-17 13:32:43 +12:00
Jesse Hills
01bfafc5f1
Setup the mute pin if configured (#3568) 2022-06-17 13:30:21 +12:00
Jesse Hills
8c9948bb56
Merge pull request #3567 from esphome/bump-2022.6.0
2022.6.0
2022-06-16 12:59:26 +12:00
Jesse Hills
2d1abaa68e
Bump version to 2022.6.0 2022-06-16 11:31:38 +12:00
Jesse Hills
664a3df0b4
Merge branch 'beta' into bump-2022.6.0 2022-06-16 11:31:38 +12:00
Jesse Hills
9ff893881c
Merge pull request #3560 from esphome/bump-2022.6.0b4
2022.6.0b4
2022-06-14 22:42:06 +12:00
Jesse Hills
94f6c6861a
Bump version to 2022.6.0b4 2022-06-14 20:41:46 +12:00
Martin
b1d614e6c4
Bm3xx: Fix typo (#3559) 2022-06-14 20:41:46 +12:00
André Klitzing
7fceb070e5
Fix compilation with ESP32-S3 (#3543)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-06-14 20:41:46 +12:00
Martin
06440d0202
Bm3xx: Fix typo (#3559) 2022-06-14 20:38:09 +12:00
André Klitzing
0ecf9f4f2f
Fix compilation with ESP32-S3 (#3543)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-06-14 20:36:38 +12:00
Jesse Hills
5c7c0834c0
Merge pull request #3554 from esphome/bump-2022.6.0b3
2022.6.0b3
2022-06-13 17:06:45 +12:00
Jesse Hills
f3a25de11d
Bump version to 2022.6.0b3 2022-06-13 13:32:43 +12:00
Jesse Hills
041bef8bcd
Implement media player volume actions (#3551) 2022-06-13 13:32:43 +12:00
Jesse Hills
8998c5f6dd
Implement media player volume actions (#3551) 2022-06-13 13:28:55 +12:00
Jesse Hills
6e83790308
Merge pull request #3540 from esphome/bump-2022.6.0b2
2022.6.0b2
2022-06-09 21:16:23 +12:00
Jesse Hills
d2d4eb4eae
Bump version to 2022.6.0b2 2022-06-09 20:27:22 +12:00
Viktor Nagy
5942a3898c
Nextion brightness setting requires an assignment (#3533) 2022-06-09 20:27:22 +12:00
Samuel Sieb
93421f0fa7
publish fan speed count for discovery (#3537)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
2022-06-09 20:27:22 +12:00
RoboMagus
3a9ab50dd2
Refactor vl53l0x to remove code from header. (#3536) 2022-06-09 16:24:56 +12:00
Viktor Nagy
5abd91d6d5
Nextion brightness setting requires an assignment (#3533) 2022-06-09 16:20:05 +12:00
Samuel Sieb
c3da42516b
publish fan speed count for discovery (#3537)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
2022-06-09 15:27:04 +12:00
Jesse Hills
6cb5cd48c2
Merge pull request #3535 from esphome/bump-2022.6.0b1
2022.6.0b1
2022-06-08 23:26:21 +12:00
Jesse Hills
ec1fae6883
Bump version to 2022.7.0-dev 2022-06-08 22:46:20 +12:00
Jesse Hills
746fd1122f
Bump version to 2022.6.0b1 2022-06-08 22:46:20 +12:00
Jesse Hills
9663760ec5
Merge branch 'dev' into bump-2022.6.0b1 2022-06-08 22:46:20 +12:00
Wolfgang Tremmel
a3d73d1e23
RG15 data is float/double, not int (#3512)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-06-08 22:34:50 +12:00
Jesse Hills
d63e14a4b6
Implement the media player actions (#3534) 2022-06-08 22:33:21 +12:00
DAVe3283
03944e6cd8
Fix bogus reading on no communication with MAX31865 (#3505) 2022-06-08 09:58:32 +12:00
Maurice Makaay
0d1028be2e
Cleanup deprecated EntityBase::hash_base() (#3525)
Co-authored-by: Maurice Makaay <mmakaay1@xs4all.net>
2022-06-08 09:13:11 +12:00
VitaliyKurokhtin
6a85259e4d
Block Tuya light from reacting to dp changes if transitioning (#3076) 2022-06-07 23:07:08 +12:00
Maurice Makaay
ebca936b7e
Fix percentage validation for wrong data type input (#3524)
Co-authored-by: Maurice Makaay <mmakaay1@xs4all.net>
2022-06-07 23:00:27 +12:00
Nicholas Peters
31c4551890
Fix sdp3x error checking (#3531) 2022-06-07 22:43:46 +12:00
Samuel Sieb
dd470d4197
support rotated ILI9341 (ILI9342) (#3526)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
2022-06-07 22:42:13 +12:00
Maurice Makaay
612822490b
Fix endless 'WiFi Unknown connection status 0' loop (#3530)
Co-authored-by: Maurice Makaay <mmakaay1@xs4all.net>
2022-06-07 12:08:29 +12:00
Maurice Makaay
f8969605e8
Suppress first rotary encoder event (#3532)
Co-authored-by: Maurice Makaay <mmakaay1@xs4all.net>
2022-06-07 11:36:54 +12:00
Carlos Garcia Saura
dd24ffa24e
Correct ADC auto-range for ESP32-S2 variant (13 bit adc) (#3158)
Co-authored-by: Otto Winter <otto@otto-winter.com>
2022-06-03 16:07:35 +12:00
guillempages
d0dda48932
Add display_type property to DisplayBuffer (#3430)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-06-03 15:39:04 +12:00
Eric van Blokland
6349b5f654
Added RC6 protocol support (#3514) 2022-06-03 15:37:04 +12:00
Joe
a6ff02a3cf
Refactor clock syncing (#3503)
* Expose `send_local_time()` as public, for use in lambdas.
  This will send the current time configured in `time_id`.
* Add a new `set_clock()` public method, separate from time_id.
  This allows setting the clock manually, without syncing from a Time
  Component. Again this can only be called from ESPHome; i.e.,
  generally from a lambda.
2022-06-03 13:53:20 +12:00
jimtng
4f57bf786b
Add mqtt.on_connect and mqtt.on_disconnect triggers (#3520) 2022-06-03 13:51:50 +12:00
Jesse Hills
6221f6d47d
Implement Media Player and I2S Media player (#3487) 2022-06-02 17:00:17 +12:00
Wolfgang Tremmel
a922efeafa
Change rain intensity sensor string (#3511) 2022-05-31 16:49:18 +12:00
jimtng
5aa42e5e66
Add variable substitutions for !include (#3510) 2022-05-31 16:45:18 +12:00
Joe
708672ec7e
[BedJet] Add configurable heating strategy (#3519)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-05-31 15:45:01 +12:00
Jan Grewe
d2cefbf224
Allow Prometheus component to export internal components (#3508)
Co-authored-by: Jan Grewe <jan.grewe@flixbus.com>
2022-05-31 07:29:57 +12:00
Michael Davidson
adb7aa6950
Thermostat preset with modes (#3298)
* Rework HOME/AWAY support to being driven via a map of ClimatePreset/ThermostatClimateTargetTempConfig
This opens up to theoretically being able to support other presets (ECO, SLEEP, etc)

* Add support for additional presets
Configuration takes the form;
```
climate:
  platform: preset
  ...
  preset:
    [eco | away | boost | comfort | home | sleep | activity]:
      default_target_temperature_low: 20
      default_target_temperature_high: 24
```

These will be available in the Home Assistant UI and, like the existing Home/Away config will reset the temperature in line with these defaults when selected. The existing away_config/home_config is still respected (although preset->home/preset->away will be applied after them and override them if both styles are specified)

* Add support for specifying MODE, FAN_MODE and SWING_MODE on a preset
When switching presets these will implicitly flow through to the controller. However calls to climate.control which specify any of these will take precedence even when changing the mode (think of the preset version as the default for that preset)

* Add `preset_change` mode trigger
When defined this trigger will fire when the preset for the thermostat has been changed. The intent of this is similar to `auto_mode` - it's not intended to be used to control the preset's state (eg. communicate with the physical thermostat) but instead might be used to update a visual indicator, for instance.

* Apply lint, clang-format, and clang-tidy fixes

* Additional clang-format fixes

* Wrap log related strings in LOG_STR_ARG

* Add support for custom presets
This also changes the configuration syntax to;
```yaml
  preset:
    # Standard preset
    - name: [eco | away | boost | comfort | home | sleep | activity]
      default_target_temperature_low: 20
      ...
    # Custom preset
    - name: My custom preset
      default_target_temperature_low: 18
```

For the end user there is no difference between a custom and built in preset. For developers custom presets are set via `climate.control` `custom_preset` property instead of the `preset`

* Lint/clang-format/clang-tidy fixes

* Additional lint/clang-format/clang-tidy fixes

* Clang-tidy changes

* Sort imports

* Improve configuration validation for presets
- Unify temperature validation across default, away, and preset configuration
- Validate modes for presets have the required actions

* Trigger a refresh after changing internals of the thermostat

* Apply formatting fixes

* Validate mode, fan_mode, and swing_mode on presets

* Add preset temperature validation against visual min/max configuration

* Apply code formatting fixes

* Fix preset temperature validation
2022-05-24 22:44:26 -05:00
Jesse Hills
77f322166e
Merge pull request #3499 from esphome/bump-2022.5.1
2022.5.1
2022-05-25 07:29:42 +12:00
Jesse Hills
f3f6e54818
Bump version to 2022.5.1 2022-05-24 21:56:18 +12:00
Martin
fb0fec1f25
esp32: fix NVS (#3497) 2022-05-24 21:56:18 +12:00
Jesse Hills
b66af9fb4d
Add missing import to bedjet (#3490) 2022-05-24 21:56:18 +12:00
user897943
6617d576a7
Update bedjet_const.h to remove blank spaces before speed steps, fixes Unknown Error when using climate.set_fan_mode in HA (#3476) 2022-05-24 21:56:18 +12:00
Jesse Hills
420dacb22d
Merge pull request #3488 from esphome/bump-2022.5.0
2022.5.0
2022-05-18 16:56:16 +12:00
Jesse Hills
ae2f6ad4d1
Bump version to 2022.5.0 2022-05-18 16:30:20 +12:00
Jesse Hills
2c28d79bf8
Merge branch 'beta' into bump-2022.5.0 2022-05-18 16:30:19 +12:00
Jesse Hills
c5069edc78
Merge pull request #3484 from esphome/bump-2022.5.0b4
2022.5.0b4
2022-05-17 23:42:51 +12:00
Jesse Hills
282d9e138c
Revert adding spaces 2022-05-17 23:31:55 +12:00
Jesse Hills
72fcf2cbe1
Bump version to 2022.5.0b4 2022-05-17 23:23:37 +12:00
Samuel Sieb
6f49f5465b
Retry Tuya init commands (#3482)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
2022-05-17 23:23:33 +12:00
Martin
17b8bd8316
ESP32: Only save to NVS if data was changed (#3479) 2022-05-17 23:16:33 +12:00
Jesse Hills
7e88938932
Merge pull request #3478 from esphome/bump-2022.5.0b3
2022.5.0b3
2022-05-16 13:42:05 +12:00
Jesse Hills
c707e64685
Bump version to 2022.5.0b3 2022-05-16 13:07:12 +12:00
Jesse Hills
a639690716
Mark improv_serial and ESP-IDF usb based serial on c3/s2/s3 unsupported (#3477) 2022-05-16 13:07:12 +12:00
[pʲɵs]
01222dbab7
Increase JSON buffer size on overflow (#3475) 2022-05-16 13:07:12 +12:00
Jesse Hills
ff72d6a146
Merge pull request #3465 from esphome/bump-2022.5.0b2
2022.5.0b2
2022-05-12 22:15:21 +12:00
Jesse Hills
603d0d0c7c
Bump version to 2022.5.0b2 2022-05-12 17:00:14 +12:00
Brian Kaufman
28883f711b
Update captive portal canHandle function (#3360) 2022-05-12 17:00:13 +12:00
Michael Davidson
e914828add
Make custom_fan and custom_preset templatable as per documentation (#3330) 2022-05-12 17:00:13 +12:00
James Szalay
c1480029fb
Use heat mode for heat. Move EXT HT to custom presets. (#3437)
* Use heat mode for heat. Move EXT HT to custom presets.

* Fix syntax error.
2022-05-12 17:00:13 +12:00
Niclas Larsson
40f622949e
Shelly dimmer: Use unique_ptr to handle the lifetime of stm32_t (#3400)
Co-authored-by: Martin <25747549+martgras@users.noreply.github.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-05-12 17:00:13 +12:00
Maurice Makaay
63096ac2bc
On epoch sync, restore local TZ (#3462)
Co-authored-by: Maurice Makaay <mmakaay1@xs4all.net>
2022-05-12 17:00:13 +12:00
Jesse Hills
c2a59cb476
Merge pull request #3460 from esphome/bump-2022.5.0b1
2022.5.0b1
2022-05-11 15:51:44 +12:00
Jesse Hills
d6e039a1d1
Bump version to 2022.5.0b1 2022-05-11 12:50:42 +12:00
Jesse Hills
0f1a7c2b69
Merge branch 'dev' into bump-2022.5.0b1 2022-05-11 12:50:41 +12:00
Jesse Hills
993044c870
Merge pull request #3408 from esphome/bump-2022.4.0
2022.4.0
2022-04-21 07:42:58 +12:00
Jesse Hills
a8c1b63edb
Bump version to 2022.4.0 2022-04-20 17:06:08 +12:00
Jesse Hills
db7d946e1b
Merge branch 'beta' into bump-2022.4.0 2022-04-20 17:06:08 +12:00
Jesse Hills
41d9059a2f
Merge pull request #3407 from esphome/bump-2022.4.0b4
2022.4.0b4
2022-04-20 16:55:11 +12:00
Jesse Hills
e26e0d7c01
Bump version to 2022.4.0b4 2022-04-20 16:35:43 +12:00
Jesse Hills
ad41c07a1f
Dont require {} for wifi ap with defaults (#3404) 2022-04-20 16:35:42 +12:00
Jesse Hills
5732f3b044
Merge pull request #3402 from esphome/bump-2022.4.0b3
2022.4.0b3
2022-04-19 15:31:05 +12:00
Jesse Hills
712115b6ce
Bump version to 2022.4.0b3 2022-04-19 12:33:38 +12:00
rnauber
9283559c6b
Shelly Dimmer: Delete obsolete LICENSE.txt (#3394) 2022-04-19 12:33:38 +12:00
Michel van de Wetering
6b393438e9
Fix power_delivered/produced_phase sensor deviceclass in DSMR (#3395) 2022-04-19 12:33:38 +12:00
Jesse Hills
343b9ab455
Merge pull request #3390 from esphome/bump-2022.4.0b2
2022.4.0b2
2022-04-14 15:37:31 +12:00
Jesse Hills
dcb226b202
Bump version to 2022.4.0b2 2022-04-14 13:48:35 +12:00
Janez Troha
2243021b58
Allocate smaller amount of buffer for JSON (#3384) 2022-04-14 13:48:35 +12:00
rnauber
d5134e88b1
Add support for Shelly Dimmer 2 (#2954)
Co-authored-by: Niclas Larsson <niclas@edgesystems.se>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
Co-authored-by: Jernej Kos <jernej@kos.mx>
Co-authored-by: Richard Nauber <richard@nauber.dev>
2022-04-14 13:48:35 +12:00
matthias882
c59adf612f
Changes accuracy of single cell voltage (#3387) 2022-04-14 13:48:35 +12:00
Jesse Hills
a82d8ea0c3
Merge pull request #3381 from esphome/bump-2022.4.0b1
2022.4.0b1
2022-04-13 16:14:08 +12:00
Jesse Hills
ad57faa9a9
Bump version to 2022.4.0b1 2022-04-13 13:42:28 +12:00
Jesse Hills
a9b5e8d036
Merge branch 'dev' into bump-2022.4.0b1 2022-04-13 13:42:27 +12:00
Jesse Hills
fc7348d46d
Merge pull request #3346 from esphome/bump-2022.3.2
2022.3.2
2022-03-30 13:28:58 +13:00
Jesse Hills
8be2456c7e
Bump version to 2022.3.2 2022-03-30 08:15:50 +13:00
Jesse Hills
bb5f7249a6
Actually increase request memory for json parsing (#3331) 2022-03-30 08:15:50 +13:00
Jesse Hills
fc94a5d0ee
Merge pull request #3324 from esphome/bump-2022.3.1
2022.3.1
2022-03-24 16:45:14 +13:00
Jesse Hills
24029cc918
Bump version to 2022.3.1 2022-03-23 12:26:02 +13:00
Jesse Hills
9a9d5964ee
Add small delay before setting up app in safe mode (#3323) 2022-03-23 12:26:02 +13:00
Jesse Hills
4e4a512107
Reserve less memory for json (#3289) 2022-03-23 12:26:02 +13:00
Jesse Hills
0729ed538e
Webserver utilize Component Iterator to not overload eventstream (#3310) 2022-03-23 12:26:01 +13:00
wysiwyng
24b75b7ed6
Fix WDT reset during dallas search algorithm (#3293) 2022-03-23 12:26:01 +13:00
Jesse Hills
ec3618ecb8
Merge pull request #3304 from esphome/bump-2022.3.0
2022.3.0
2022-03-16 23:38:01 +13:00
Jesse Hills
792a24f38d
Bump version to 2022.3.0 2022-03-16 22:32:24 +13:00
Jesse Hills
652e8a015b
Merge branch 'beta' into bump-2022.3.0 2022-03-16 22:32:24 +13:00
Jesse Hills
59e6e798dd
Merge pull request #3302 from esphome/bump-2022.3.0b2
2022.3.0b2
2022-03-16 15:38:32 +13:00
Jesse Hills
e5c2dbc7ec
Bump version to 2022.3.0b2 2022-03-16 14:06:00 +13:00
Jesse Hills
756f71c382
Allow custom register type for modbus number (#3202) 2022-03-16 14:06:00 +13:00
Jesse Hills
b7535693fa
Add helper overloads for hex print 16-bit (#3297) 2022-03-16 14:06:00 +13:00
stegm
06a3505698
Add optimistic config flag to modbus select. (#3267) 2022-03-16 14:06:00 +13:00
Jesse Hills
efa8f0730d
Merge pull request #3278 from esphome/bump-2022.3.0b1
2022.3.0b1
2022-03-09 20:57:07 +13:00
Jesse Hills
023d26f521
Bump version to 2022.3.0b1 2022-03-09 20:07:50 +13:00
Jesse Hills
5068619f1b
Merge branch 'dev' into bump-2022.3.0b1 2022-03-09 20:07:49 +13:00
Jesse Hills
1ef6fd8fb0
Merge pull request #3261 from esphome/bump-2022.2.6
2022.2.6
2022-03-02 22:54:51 +13:00
Jesse Hills
942b0de7fd
Bump version to 2022.2.6 2022-03-02 17:07:08 +13:00
Jesse Hills
859cca49d1
Only get free memory size from internal (#3259) 2022-03-02 17:07:08 +13:00
Jesse Hills
8f7ff25624
Merge pull request #3252 from esphome/bump-2022.2.5
2022.2.5
2022-02-24 07:28:56 +13:00
Jesse Hills
97aca8e54c
Bump version to 2022.2.5 2022-02-23 11:20:48 +13:00
Nicholas Peters
95acf19067
Fix regression caused by TSL2591 auto gain (#3249) 2022-02-23 11:20:48 +13:00
Martin Weinelt
3d0899aa58
Respect ESPHOME_USE_SUBPROCESS in esp32 post_build script (#3246) 2022-02-23 11:20:48 +13:00
Jesse Hills
138d6e505b
Merge pull request #3245 from esphome/bump-2022.2.4
2022.2.4
2022-02-21 10:54:54 +13:00
Jesse Hills
2748e6ba29
Bump version to 2022.2.4 2022-02-21 10:17:25 +13:00
Jesse Hills
dbd4e927d8
Fix fatal erroring in addon startup script (#3244) 2022-02-21 10:17:24 +13:00
Jesse Hills
e73d47918f
Fix lilygo touchscreen rotation (#3221) 2022-02-21 10:17:24 +13:00
Tyler Bules
b881bc071e
ESP32-C3 deep sleep fix (#3066) 2022-02-21 10:17:24 +13:00
Otto Winter
1d0395d1c7
Improve ESP8266 iram usage (#3223) 2022-02-21 10:17:24 +13:00
Otto Winter
616c787e37
Fix ESP8266 climate memaccess warning (#3226) 2022-02-21 10:17:24 +13:00
Otto Winter
0c4de2bc97
Publish NAN when dallas conversion failed (#3227) 2022-02-21 10:17:24 +13:00
Jesse Hills
c2f5ac9eba
Merge pull request #3220 from esphome/bump-2022.2.3
2022.2.3
2022-02-18 12:06:19 +13:00
Jesse Hills
5764c988af
Bump version to 2022.2.3 2022-02-18 11:51:56 +13:00
dependabot[bot]
ccc2fbfd67
Bump platformio from 5.2.4 to 5.2.5 (#3188)
* Bump platformio from 5.2.4 to 5.2.5

Bumps [platformio](https://github.com/platformio/platformio) from 5.2.4 to 5.2.5.
- [Release notes](https://github.com/platformio/platformio/releases)
- [Changelog](https://github.com/platformio/platformio-core/blob/develop/HISTORY.rst)
- [Commits](https://github.com/platformio/platformio/commits)

---
updated-dependencies:
- dependency-name: platformio
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update requirements.txt

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Otto Winter <otto@otto-winter.com>
2022-02-18 11:51:56 +13:00
Jesse Hills
10b4adb8e6
Merge pull request #3219 from esphome/bump-2022.2.2
2022.2.2
2022-02-18 10:36:33 +13:00
Jesse Hills
83b7181bcb
Bump version to 2022.2.2 2022-02-18 10:16:11 +13:00
Jesse Hills
8886b7e141
Add LONG LONG flag for arduinojson (#3212) 2022-02-18 10:16:11 +13:00
Otto Winter
7dcc4d030b
Fix platformio docker version mismstch (#3215) 2022-02-18 10:16:11 +13:00
Stewart
b9398897c1
Set entity-category to diagnostic for debug component (#3209)
Co-authored-by: Stewart Morgan <stewart@arnos-vale.net>
Co-authored-by: root <root@build.servers.arnos-vale.net>
2022-02-18 10:16:11 +13:00
Jesse Hills
657b1c60ae
Merge pull request #3208 from esphome/bump-2022.2.1
2022.2.1
2022-02-17 08:00:12 +13:00
Jesse Hills
dc54b17778
Bump version to 2022.2.1 2022-02-17 07:36:14 +13:00
Stewart
1fb214165b
Fix missed ARDUINO_VERSION_CODE to USE_ARDUINO_VERSION_CODE changes (#3206)
Co-authored-by: Stewart Morgan <stewart@arnos-vale.net>
2022-02-17 07:36:14 +13:00
Jesse Hills
81b2fd78f5
Merge pull request #3204 from esphome/bump-2022.2.0
2022.2.0
2022-02-16 21:12:45 +13:00
Jesse Hills
69002fb1e6
Bump version to 2022.2.0 2022-02-16 20:13:14 +13:00
Jesse Hills
75332a752d
Merge branch 'beta' into bump-2022.2.0 2022-02-16 20:13:13 +13:00
Jesse Hills
b528f48417
Merge pull request #3201 from esphome/bump-2022.2.0b3
2022.2.0b3
2022-02-16 11:10:31 +13:00
Jesse Hills
ec7a79049a
Bump version to 2022.2.0b3 2022-02-16 09:45:05 +13:00
Jesse Hills
6ddad6b299
Update HA addon token (#3200) 2022-02-16 09:45:05 +13:00
Maurice Makaay
16dc7762f9
Fix strlcpy() uses to make long SSIDs and passwords work (#3199)
Co-authored-by: Maurice Makaay <mmakaay1@xs4all.net>
2022-02-16 09:45:05 +13:00
Jesse Hills
dc0ed8857f
Merge pull request #3198 from esphome/bump-2022.2.0b2
2022.2.0b2
2022-02-15 12:48:34 +13:00
Jesse Hills
bb6b77bd98
Bump version to 2022.2.0b2 2022-02-15 12:00:12 +13:00
Jesse Hills
dcc80f9032
Allow framework version validator to be maximum version (#3197) 2022-02-15 12:00:12 +13:00
Otto Winter
dd554bcdf4
Make generating combined binary output verbose (#3127) 2022-02-15 12:00:12 +13:00
Jesse Hills
f376a39e55
Clamp rotary_encoder restored value to min and max (#3184) 2022-02-15 12:00:12 +13:00
dependabot[bot]
8dcc9d6b66
Bump aioesphomeapi from 10.8.1 to 10.8.2 (#3182) 2022-02-15 12:00:11 +13:00
Jesse Hills
a576c9f21f
Merge pull request #3180 from esphome/bump-2022.2.0b1
2022.2.0b1
2022-02-10 00:20:46 +13:00
Jesse Hills
71a438e2cb
Bump version to 2022.2.0b1 2022-02-09 23:47:36 +13:00
Jesse Hills
272d6f2a8b
Merge branch 'dev' into bump-2022.2.0b1 2022-02-09 23:47:36 +13:00
Jesse Hills
09ed1aed93
Merge pull request #3177 from esphome/bump-2022.1.4
2022.1.4
2022-02-09 12:42:47 +13:00
Jesse Hills
53d3718028
Bump version to 2022.1.4 2022-02-09 11:54:41 +13:00
Jesse Hills
2b5dce5232
Try fix canbus config validation (#3173) 2022-02-09 11:54:40 +13:00
Otto Winter
9ad84150aa
Enable mDNS during OTA safe mode (#3146) 2022-02-09 11:54:40 +13:00
Jesse Hills
c0523590b4
Merge pull request #3154 from esphome/bump-2022.1.3
2022.1.3
2022-02-03 07:17:52 +13:00
Jesse Hills
c7f091ab10
Bump version to 2022.1.3 2022-02-02 22:16:24 +13:00
Jesse Hills
7479e0aada
Fix backwards string case helpers (#3126) 2022-02-02 22:16:16 +13:00
Jesse Hills
5bbee1a1fe
Merge pull request #3111 from esphome/bump-2022.1.2
2022.1.2
2022-01-25 09:36:20 +13:00
Jesse Hills
bdb9546ca3 Bump version to 2022.1.2 2022-01-25 09:11:20 +13:00
Plácido Revilla
46af4cad6e Set the wrapped single light in light partition to internal (#3092) 2022-01-25 09:11:19 +13:00
Martin
76a238912b [modbus_controller] fix incorrect start address for number write (#3073) 2022-01-25 09:11:19 +13:00
Jesse Hills
909a526967
Merge pull request #3075 from esphome/bump-2022.1.1
2022.1.1
2022-01-20 09:08:57 +13:00
Jesse Hills
cd6f4fb93f
Bump version to 2022.1.1 2022-01-20 08:34:18 +13:00
Jesse Hills
c19458696e
Add *.py.script files to distributions (#3074) 2022-01-20 08:34:18 +13:00
Jesse Hills
318b930e9f
Merge pull request #3070 from esphome/bump-2022.1.0
2022.1.0
2022-01-19 19:43:54 +13:00
Jesse Hills
9296a078a7
Bump version to 2022.1.0 2022-01-19 16:08:27 +13:00
Jesse Hills
5dc776e55f
Merge pull request #3068 from esphome/bump-2022.1.0b4
2022.1.0b4
2022-01-19 07:40:37 +13:00
Jesse Hills
72d60f30f7 Bump version to 2022.1.0b4 2022-01-18 15:49:31 +13:00
Oxan van Leeuwen
869743a742 Fail hard if no random bytes available for encryption (#3067) 2022-01-18 15:49:31 +13:00
Martin
7b03e07908 [modbus_controller] add missing skip_updates (#3063) 2022-01-18 15:49:31 +13:00
Paulus Schoutsen
348f880e15 bump dashboard to 20220116.0 (#3061) 2022-01-18 15:49:31 +13:00
Jesse Hills
ead597d0fb
Merge pull request #3060 from esphome/bump-2022.1.0b3
2022.1.0b3
2022-01-17 13:13:40 +13:00
Jesse Hills
afbf989715
Bump version to 2022.1.0b3 2022-01-17 12:40:07 +13:00
Jesse Hills
01b62a16c3
Add number setting to web_server/rest_api (#3055) 2022-01-17 12:40:07 +13:00
Oxan van Leeuwen
c5eba04517
Remove deprecated attribute from virtual entity methods (#3056) 2022-01-17 12:40:07 +13:00
Oxan van Leeuwen
282313ab52
Rename post_build scripts to fix codeowners script (#3057) 2022-01-17 12:40:07 +13:00
Ohad Lutzky
d274545e77
Disable caching for binary download (#3054) 2022-01-17 12:40:07 +13:00
Jesse Hills
d3fda37615
Merge pull request #3042 from esphome/bump-2022.1.0b2
2022.1.0b2
2022-01-13 22:21:45 +13:00
Jesse Hills
cbe3092404
Bump version to 2022.1.0b2 2022-01-13 21:28:45 +13:00
Paulus Schoutsen
6dfe3039d0
Bump dashboard to 20220113.2 (#3041) 2022-01-13 21:28:45 +13:00
Paulus Schoutsen
d6009453df
Add factory to download name (#3040) 2022-01-13 21:28:45 +13:00
Jesse Hills
c81323ef91
Merge pull request #3039 from esphome/bump-2022.1.0b1
2022.1.0b1
2022-01-13 12:12:07 +13:00
Jesse Hills
961c27f1c2
Bump version to 2022.1.0b1 2022-01-13 11:02:07 +13:00
Jesse Hills
fe4a14e6cc
Merge branch 'dev' into bump-2022.1.0b1 2022-01-13 11:02:07 +13:00
Jesse Hills
50848c2f4d
Merge pull request #2966 from esphome/bump-2021.12.3
2021.12.3
2021-12-30 14:54:49 +13:00
Jesse Hills
d32633b3c7
Update curl package version in docker (#2939) 2021-12-30 14:32:29 +13:00
Jesse Hills
b37739eec2
Bump version to 2021.12.3 2021-12-30 13:58:47 +13:00
Jesse Hills
28f87dc804
Remove -e for hassio images (#2964) 2021-12-30 13:58:47 +13:00
Jesse Hills
41879e41e6
Workaround installing as editable package not working (#2936) 2021-12-30 13:58:47 +13:00
Jesse Hills
fc0a6546a2
Only allow internal pins for dht sensor (#2940) 2021-12-30 13:58:47 +13:00
Jesse Hills
ffd4280d6c
Require arduino in webserver for better validation (#2941) 2021-12-30 13:58:46 +13:00
Jesse Hills
db3b955b0f
Merge pull request #2932 from esphome/bump-2021.12.2
2021.12.2
2021-12-21 12:45:27 +13:00
Jesse Hills
5516f65971
Bump version to 2021.12.2 2021-12-21 08:24:08 +13:00
Oxan van Leeuwen
9471df0a1b
Fix MQTT button press action (#2917) 2021-12-21 08:24:07 +13:00
Oxan van Leeuwen
6d39f64be7
Don't disable idle task WDT when it's not enabled (#2856) 2021-12-21 08:24:07 +13:00
Jesse Hills
b89d0a9a73
Merge pull request #2915 from esphome/bump-2021.12.1
2021.12.1
2021-12-15 16:36:39 +13:00
Jesse Hills
4bb779d9a5
Bump version to 2021.12.1 2021-12-15 14:57:32 +13:00
wilberforce
386a5b6362
Allow button POST on press from web server (#2913) 2021-12-15 14:57:32 +13:00
Oxan van Leeuwen
e32a999cd0
Set text sensor state property to filter output (#2893) 2021-12-15 14:57:32 +13:00
Jesse Hills
bfbc6a4bad
Merge pull request #2907 from esphome/bump-2021.12.0
2021.12.0
2021-12-12 07:59:37 +13:00
Jesse Hills
8c9e0e552d
Bump version to 2021.12.0 2021-12-12 07:10:51 +13:00
Jesse Hills
8aaf9fd83f
Merge pull request #2905 from esphome/bump-2021.12.0b6
2021.12.0b6
2021-12-11 21:54:49 +13:00
Jesse Hills
08057720b8
Bump version to 2021.12.0b6 2021-12-11 21:07:07 +13:00
Jesse Hills
bfaa648837
Bump esphome-dashboard to 20211211.0 (#2904) 2021-12-11 21:07:07 +13:00
Keith Burzinski
d504daef91
Fix for two points setting when fan_only_cooling is disabled (#2903)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
Co-authored-by: Keith Burzinski <kburzinski@kbx-mbp2021.ad.kbx81.net>
2021-12-11 21:07:07 +13:00
Jesse Hills
b8d3ef2f49
Merge pull request #2899 from esphome/bump-2021.12.0b5
2021.12.0b5
2021-12-10 10:55:55 +13:00
Jesse Hills
3bf6320030
Bump version to 2021.12.0b5 2021-12-10 09:55:48 +13:00
Guillermo Ruffino
708b928c73
Modbus number/output use write single (#2896)
Co-authored-by: Martin <25747549+martgras@users.noreply.github.com>
2021-12-10 09:55:48 +13:00
Jesse Hills
649366ff44
Fix published state for modbus number (#2894) 2021-12-10 09:55:47 +13:00
Jesse Hills
e5c9e87fad
Merge pull request #2890 from esphome/bump-2021.12.0b4
2021.12.0b4
2021-12-10 09:50:29 +13:00
Jesse Hills
f3d9d707b6
Bump version to 2021.12.0b4 2021-12-08 12:58:14 +13:00
Jesse Hills
090e10730c
Bump esphome-dashboard to 20211208.0 (#2887) 2021-12-08 12:58:14 +13:00
Jesse Hills
fbc84861c7
Use new platform component config blocks for wizard (#2885) 2021-12-08 12:58:14 +13:00
Carlos Garcia Saura
e763469af8
Feed watchdog while setting up OTA (#2876) 2021-12-08 12:58:14 +13:00
Jesse Hills
3c0c514e44
Merge pull request #2880 from esphome/bump-2021.12.0b3
2021.12.0b3
2021-12-07 15:27:08 +13:00
Jesse Hills
ed5e2dd332
Bump version to 2021.12.0b3 2021-12-07 07:47:48 +13:00
Jesse Hills
09b7c6f550
Bump esphome-dashboard to 20211207.0 (#2877) 2021-12-07 07:47:48 +13:00
Oxan van Leeuwen
df315a1f51
Feed watchdog when no component loops (#2857) 2021-12-07 07:47:48 +13:00
Jesse Hills
7ee4bb621c
Allow wizard to specify secrets (#2875) 2021-12-07 07:47:48 +13:00
Jesse Hills
24874f4c3c
Adopt using wifi secrets that should exist at this point (#2874) 2021-12-07 07:47:48 +13:00
Jesse Hills
c128880033
Add endpoint to fetch secrets keys (#2873) 2021-12-07 07:47:48 +13:00
Massimiliano Ravelli
a66e94a0b0
Ignore already stopped dhcp for ethernet (#2862)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-12-07 07:47:48 +13:00
Oxan van Leeuwen
56870ed4a8
Fix MCP23x17 not disabling pullup after config change (#2855) 2021-12-07 07:47:48 +13:00
Martin
3ac720df47
SPS30 : fix i2c read size (#2866) 2021-12-07 07:47:48 +13:00
Carlos Garcia Saura
1bc757ad06
ADC: Turn verbose the debugging "got voltage" (#2863) 2021-12-07 07:47:48 +13:00
Martin
f72abc6f3d
tlc59208f : fix compilation error (#2867) 2021-12-07 07:47:48 +13:00
Jesse Hills
5ac88de985
Bump esphome-dashboard to 20211206.0 (#2870) 2021-12-07 07:47:48 +13:00
Jesse Hills
0826b367d6
Merge pull request #2853 from esphome/bump-2021.12.0b2
2021.12.0b2
2021-12-03 08:07:30 +13:00
Jesse Hills
329bf861d6
Bump version to 2021.12.0b2 2021-12-03 07:54:34 +13:00
Oxan van Leeuwen
9dcd3d18a0
Update ota_component.cpp (#2852) 2021-12-03 07:54:34 +13:00
Jesse Hills
db66cd88b6
Merge pull request #2851 from esphome/bump-2021.12.0b1
2021.12.0b1
2021-12-02 21:32:43 +13:00
Jesse Hills
86c205fe43
Remove blank line 2021-12-02 21:08:11 +13:00
Jesse Hills
c6414138c7
Bump version to 2021.12.0b1 2021-12-02 19:38:49 +13:00
Jesse Hills
36b355eb82
Merge branch 'dev' into bump-2021.12.0b1 2021-12-02 19:38:44 +13:00
Jesse Hills
7be9291b13
Merge pull request #2821 from esphome/bump-2021.11.4
2021.11.4
2021-11-29 13:23:47 +13:00
Jesse Hills
ea9e75039b
Bump version to 2021.11.4 2021-11-29 10:18:49 +13:00
Conclusio
a5fb036011
Add delay to improve stability (#2793) 2021-11-29 10:18:48 +13:00
Dave T
e55506f9db
Correct bitmask for third color (blue) scaling. (#2817) 2021-11-29 10:18:48 +13:00
Carlos Garcia Saura
50ec1d0445
Fix compilation error for WPA enterprise in ESP-IDF (#2815) 2021-11-29 10:18:48 +13:00
Oxan van Leeuwen
3d5e1d8d91
Fix parsing of multiple values in EZO sensor (#2814)
Co-authored-by: Lydia Sevelt <LydiaSevelt@gmail.com>
2021-11-29 10:18:48 +13:00
Oxan van Leeuwen
db2128a344
Fix parsing numbers in Anova (#2816) 2021-11-29 10:18:48 +13:00
Jesse Hills
21db43db06
Merge pull request #2808 from esphome/bump-2021.11.3
2021.11.3
2021-11-28 00:01:16 +13:00
Jesse Hills
5009b3029f
Bump version to 2021.11.3 2021-11-27 21:13:01 +13:00
Maurice Makaay
57a029189c
Add missing nvs_flash_init() to ESP32 preferences code (#2805)
Co-authored-by: Maurice Makaay <mmakaay1@xs4all.net>
2021-11-27 21:13:01 +13:00
Jesse Hills
0cb715bb76
Merge pull request #2799 from esphome/bump-2021.11.2
2021.11.2
2021-11-26 09:25:37 +13:00
Jesse Hills
7d03823afd
Bump version to 2021.11.2 2021-11-26 09:02:54 +13:00
Oxan van Leeuwen
8e1c9f5042
Fix parsing numbers from null-terminated buffers (#2755) 2021-11-26 09:02:54 +13:00
Samuel Sieb
980b7cda8f
Remove floating point ops from the ISR (#2751)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
2021-11-26 09:02:53 +13:00
Kamil Trzciński
3a72dd5cb6
esp32_camera_web_server: Improve support for MotionEye (#2777) 2021-11-26 09:02:53 +13:00
Dave T
3178243811
Fix frame scaling for animated gifs (#2750) 2021-11-26 09:02:53 +13:00
Maurice Makaay
d30e2f2a4f
Allow UART debug configuration with no after: definition (#2753) 2021-11-26 09:02:53 +13:00
Jesse Hills
6226dae05c
Merge pull request #2744 from esphome/bump-2021.11.1
2021.11.1
2021-11-17 23:45:43 +13:00
Jesse Hills
9c6a475a6e
Bump version to 2021.11.1 2021-11-17 23:31:38 +13:00
Franck Nijhof
8294d10d5b
Re-instate device class update for binary sensors (#2743) 2021-11-17 23:31:38 +13:00
Evgeny
67558bec47
Fix HM3301 AQI index calculator (#2739) 2021-11-17 23:31:38 +13:00
Jesse Hills
84873d4074
Merge pull request #2742 from esphome/bump-2021.11.0
2021.11.0
2021-11-17 22:18:29 +13:00
Jesse Hills
58a0b28a39
Bump version to 2021.11.0 2021-11-17 21:58:30 +13:00
Jesse Hills
b37d3a66cc
Merge pull request #2738 from esphome/bump-2021.11.0b9
2021.11.0b9
2021-11-17 10:27:41 +13:00
Jesse Hills
7e495a5e27
Bump version to 2021.11.0b9 2021-11-17 08:00:26 +13:00
rotarykite
c41547fd4a
Fix senseair component uart read timeout (#2658)
Co-authored-by: DAVe3283 <DAVe3283+GitHub@gmail.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
Co-authored-by: Chua Jun Chieh <junchieh.chua@softspace.com.my>
2021-11-17 08:00:26 +13:00
Ryan Hoffman
0d47d41c85
Use as_reversed_hex_array in ble_sensor to fix UUID parsing (#2737)
#1627 renamed as_hex_array to as_reversed_hex_array but forgot to rename these users.
2021-11-17 08:00:26 +13:00
Jesse Hills
41a3a17456
Merge pull request #2734 from esphome/bump-2021.11.0b8
2021.11.0b8
2021-11-16 13:50:10 +13:00
Jesse Hills
cbbafbcca2
Bump version to 2021.11.0b8 2021-11-16 12:53:56 +13:00
Jesse Hills
c75566b374
Fix zeroconf time comparisons (#2733)
Co-authored-by: J. Nick Koston <nick@koston.org>
2021-11-16 12:53:56 +13:00
Jesse Hills
7279f1fcc1
Merge pull request #2732 from esphome/bump-2021.11.0b7
2021.11.0b7
2021-11-16 12:19:07 +13:00
Jesse Hills
d7432f7c10
Bump version to 2021.11.0b7 2021-11-16 11:05:51 +13:00
Jesse Hills
b0a0a153f3
Improv serial/checksum changes (#2731)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2021-11-16 11:05:51 +13:00
Jesse Hills
024632dbd0
Merge pull request #2730 from esphome/bump-2021.11.0b6
2021.11.0b6
2021-11-16 10:53:11 +13:00
Jesse Hills
0a545a28b9
Bump version to 2021.11.0b6 2021-11-16 09:59:00 +13:00
Jesse Hills
0f2df59998
Add zeroconf as a direct dependency and lock the version (#2729) 2021-11-16 09:58:59 +13:00
Jesse Hills
29a7d32f77
Merge pull request #2725 from esphome/bump-2021.11.0b5
2021.11.0b5
2021-11-15 13:42:59 +13:00
Jesse Hills
687a7e9b2f
Bump version to 2021.11.0b5 2021-11-15 12:02:18 +13:00
Alexandre-Jacques St-Jacques
09e8782318
Remove unnecessary duplicate touch_pad_filter_start (#2724) 2021-11-15 12:02:18 +13:00
Jesse Hills
f2aea02210
Merge pull request #2723 from esphome/bump-2021.11.0b4
2021.11.0b4
2021-11-15 11:42:59 +13:00
Jesse Hills
194f922312
Bump version to 2021.11.0b4 2021-11-15 11:03:40 +13:00
Jesse Hills
fea3c48098
Fix indentation of write_lambda for modbus_controller number (#2722) 2021-11-15 11:03:39 +13:00
Sergey V. DUDANOV
c2f57baec2
RemoteTransmitter fix. Bug from version 2021.10. Some changes. (#2706) 2021-11-15 11:03:39 +13:00
Oxan van Leeuwen
f4a140e126
Feed WDT between doing ESP32 touchpad measurements (#2720) 2021-11-15 11:03:39 +13:00
Oxan van Leeuwen
ab506b09fe
Restore InterruptLock on wifi-less ESP8266 (#2712) 2021-11-15 11:03:39 +13:00
Krzysztof Białek
87e1cdeedb
Allow setting custom command_topic for Select and Number components (#2714) 2021-11-15 11:03:39 +13:00
Jesse Hills
81a36146ef
Bump ESPAsyncWebServer to 2.1.0 (#2686) 2021-11-15 11:03:39 +13:00
Jesse Hills
7fa4a68a27
Merge pull request #2704 from esphome/bump-2021.11.0b3
2021.11.0b3
2021-11-12 17:21:58 +13:00
Jesse Hills
f1c5e2ef81
Bump version to 2021.11.0b3 2021-11-12 16:12:31 +13:00
lcavalli
b526155cce
Update device classes for binary sensors (#2703) 2021-11-12 16:12:31 +13:00
Jesse Hills
62c3f301e7
Only allow prometheus when using arduino (#2697) 2021-11-12 16:12:31 +13:00
Jesse Hills
38cb988809
Remove my.ha links from improv (#2695) 2021-11-12 16:12:31 +13:00
Jesse Hills
b976ac54c8
Merge pull request #2693 from esphome/bump-2021.11.0b2
2021.11.0b2
2021-11-11 12:52:35 +13:00
Jesse Hills
78026e766f
Bump version to 2021.11.0b2 2021-11-11 12:25:41 +13:00
Oxan van Leeuwen
b4cd8d21a5
Enable addressable light power supply based on raw values (#2690) 2021-11-11 12:25:41 +13:00
Maurice Makaay
7552893311
Uart debugging support (#2478)
Co-authored-by: Maurice Makaay <mmakaay1@xs4all.net>
Co-authored-by: Maurice Makaay <account-github@makaay.nl>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-11-11 12:25:41 +13:00
Carlos Garcia Saura
21c896d8f8
[remote_transmitter] accurate pulse timing for ESP8266 (#2476) 2021-11-11 12:25:40 +13:00
Jesse Hills
4b7fe202ec
Fix template number initial value being NaN (#2692) 2021-11-11 12:25:40 +13:00
Jesse Hills
9f4519210f
Merge pull request #2691 from esphome/bump-2021.11.0b1
2021.11.0b1
2021-11-11 11:05:45 +13:00
Jesse Hills
b0506afa5b
Merge branch 'beta' into bump-2021.11.0b1 2021-11-11 10:48:23 +13:00
Jesse Hills
8cbb379898
Remove import (not sure how it got there) 2021-11-11 10:35:18 +13:00
Jesse Hills
b226215593
Bump version to 2021.11.0b1 2021-11-11 10:10:05 +13:00
Jesse Hills
19970729a9
Merge branch 'dev' into bump-2021.11.0b1 2021-11-11 10:10:04 +13:00
Jesse Hills
d2ebfd2833
Merge pull request #2634 from esphome/bump-2021.10.3
2021.10.3
2021-10-27 11:21:08 +13:00
Jesse Hills
bd782fc828
Bump version to 2021.10.3 2021-10-27 10:49:11 +13:00
Jesse Hills
23560e608c
Fix select.set using lambda (#2633) 2021-10-27 10:49:10 +13:00
Jan Čermák
f1377b560e
Fix pin number validation for sn74hc595 (#2621) 2021-10-27 10:49:10 +13:00
Martin
72108684ea
fix modbus output (#2630) 2021-10-27 10:49:10 +13:00
Jesse Hills
c6adaaea97
Remove power and energy from sensors that are not true power (#2628) 2021-10-27 10:49:10 +13:00
Otto Winter
91999a38ca
Fix glue code missing micros() (#2623) 2021-10-27 10:49:10 +13:00
0hax
b34eed125d
Teleinfo ptec (#2599)
* teleinfo: handle historical mode correctly.

In historical mode, tags like PTEC leads to an issue where we detect a
timestamp wheras this is not possible in historical mode.

PTEC teleinfo tag looks like:
    PTEC HP..
Instead of the usual format
    IINST1 001 I

This make our data parsing fails.

While at here, make sure we continue parsing other tags even if parsing
one of the tag fails.

Signed-off-by: 0hax <0hax@protonmail.com>

* teleinfo: fix compilation with loglevel set to debug.

Signed-off-by: 0hax <0hax@protonmail.com>
2021-10-27 10:49:10 +13:00
Stefan Agner
2abe09529a
Autodetect flash size (#2615) 2021-10-27 10:49:10 +13:00
Otto Winter
9aaaf4dd4b
Bump platform-espressif8266 from 2.6.2 to 2.6.3 (#2620) 2021-10-27 10:49:09 +13:00
Andreas Hergert
cbfbcf7f1b
fixed dependency for pca9685 component (#2614)
Co-authored-by: Otto Winter <otto@otto-winter.com>
Co-authored-by: Andreas <andreas.hergert@otrs.com>
2021-10-27 10:49:09 +13:00
Otto Winter
c7651dc40d
Merge pull request #2613 from esphome/bump-2021.10.2
2021.10.2
2021-10-22 18:35:28 +02:00
Otto winter
eda1c471ad
Bump version to 2021.10.2 2021-10-22 18:26:24 +02:00
Andreas Hergert
c7ef18fbc4
Bugfix tca9548a and idf refactor anh (#2612)
Co-authored-by: Andreas Hergert <andreas.hergert@otrs.com>
2021-10-22 18:26:23 +02:00
Otto Winter
901ec918b1
Fix ESP8266 OTA compression only starting framework v2.7.0 (#2610) 2021-10-22 18:26:23 +02:00
Otto Winter
6bdae55ee1
Fix compiler warnings and update platformio line filter (#2607) 2021-10-22 18:26:23 +02:00
Otto Winter
dfb96e4b7f
Add owner to all libraries used (#2604) 2021-10-22 18:26:22 +02:00
Otto Winter
ff2c316b18
Re-raise keyboardinterrupt (#2603) 2021-10-22 18:26:22 +02:00
Otto Winter
5be52f71f9
Add OTA upload compression for ESP8266 (#2601) 2021-10-22 18:26:22 +02:00
Otto Winter
42873dd37c
Bump noise-c from 0.1.3 to 0.1.4 (#2602) 2021-10-22 18:26:21 +02:00
Otto Winter
f93e7d4e3a
Fix socket connection closed not detected (#2587) 2021-10-22 18:26:21 +02:00
Oxan van Leeuwen
bbcd523967
Fix validation of addressable light IDs (#2588) 2021-10-22 18:26:21 +02:00
dependabot[bot]
68cbe58d00
Bump esphome-dashboard from 20211021.0 to 20211021.1 (#2594)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-22 18:26:12 +02:00
Oxan van Leeuwen
115bca98f1
Fix old-style arduino_version on ESP8266 and with magic values (#2591) 2021-10-22 18:24:48 +02:00
Oxan van Leeuwen
ed0b34b2fe
Fix pin/component switchup in SX1509 pin configuration (#2593) 2021-10-22 18:24:48 +02:00
Oxan van Leeuwen
ab34401421
Fix PlatformIO version for latest Arduino framework (#2590) 2021-10-22 18:24:48 +02:00
Otto Winter
eed0c18d65
Fix HeatpumpIR pin (#2585) 2021-10-22 18:24:47 +02:00
Otto Winter
e5a38ce748
Merge pull request #2580 from esphome/bump-2021.10.1
2021.10.1
2021-10-21 15:31:26 +02:00
Otto Winter
7d9d9fcf36
Fix platformio_install_deps no longer installing all lib_deps (#2584) 2021-10-21 15:29:53 +02:00
Otto Winter
f0aba6ceb2
Fix platformio version in Dockerfile doesn't match requirements (#2582) 2021-10-21 14:53:22 +02:00
Otto Winter
ab07ee57c6
Fix ESP8266 dallas GPIO16 INPUT_PULLUP (#2581) 2021-10-21 14:39:55 +02:00
Otto winter
eae3d72a4d
Bump version to 2021.10.1 2021-10-21 14:23:08 +02:00
Otto Winter
7b8d826704
Fix ESP8266 OTA adds unnecessary Update library (#2579) 2021-10-21 14:23:07 +02:00
Otto Winter
e7baa42e63
Arduino global delay/millis/... symbols workaround (#2575) 2021-10-21 14:23:07 +02:00
Otto Winter
2f32833a22
Fix wifi ble coexistence check (#2573) 2021-10-21 14:23:07 +02:00
Otto Winter
f6935a4b4b
Fix ESP8266 GPIO0 Pullup Validation (#2572) 2021-10-21 14:23:06 +02:00
Maurice Makaay
332c9e891b
Fix MDNS for ESP8266 devices (#2571)
Co-authored-by: Maurice Makaay <account-github@makaay.nl>
Co-authored-by: Otto winter <otto@otto-winter.com>
Co-authored-by: Maurice Makaay <mmakaay1@xs4all.net>
2021-10-21 14:23:06 +02:00
Jesse Hills
b91ee4847f
Merge pull request #2570 from esphome/bump-2021.10.0
2021.10.0
2021-10-21 08:37:26 +13:00
Jesse Hills
625463d871
Bump version to 2021.10.0 2021-10-21 07:57:10 +13:00
Jesse Hills
6433a01e07
Merge pull request #2567 from esphome/bump-2021.10.0b11
2021.10.0b11
2021-10-21 07:47:22 +13:00
Jesse Hills
56cc31e8e7
Bump version to 2021.10.0b11 2021-10-21 07:32:27 +13:00
Otto Winter
3af297aa76
Revert nextion clang-tidy changes (#2566) 2021-10-21 07:32:27 +13:00
Otto Winter
996ec59d28
Move running process log line to debug level (#2565) 2021-10-21 07:32:26 +13:00
Jesse Hills
95593eeeab
Bump esphome-dashboard to 20211021.0 (#2564) 2021-10-21 07:32:20 +13:00
Jesse Hills
dad244fb7a
A few esp32_ble_server/improv fixes (#2562) 2021-10-21 07:31:55 +13:00
Jesse Hills
adb5d27d95
Merge pull request #2561 from esphome/bump-2021.10.0b10
2021.10.0b10
2021-10-20 17:28:37 +13:00
Jesse Hills
8456a8cecb
Bump version to 2021.10.0b10 2021-10-20 16:39:24 +13:00
Jesse Hills
b9f66373c1
Bump esphome-dashboard to 20211020.1 (#2559) 2021-10-20 16:39:16 +13:00
Jesse Hills
9ac365feef
Fix HA addon so it does not have logout button (#2558) 2021-10-20 16:38:46 +13:00
Jesse Hills
43bbd58a44
Merge pull request #2557 from esphome/bump-2021.10.0b9
2021.10.0b9
2021-10-20 11:18:40 +13:00
Jesse Hills
7feffa64f3
Bump version to 2021.10.0b9 2021-10-20 11:00:02 +13:00
Jesse Hills
ea0977abb4
Bump dashboard to 20211020.0 (#2556) 2021-10-20 10:59:53 +13:00
Jesse Hills
4c83dc7c28
Merge pull request #2555 from esphome/bump-2021.10.0b8
2021.10.0b8
2021-10-20 10:30:59 +13:00
Jesse Hills
e10ab1da78
Bump version to 2021.10.0b8 2021-10-20 10:14:30 +13:00
Martin
1b0e60374b
ignore exception when not waiting for a response (#2552) 2021-10-20 10:14:30 +13:00
Oxan van Leeuwen
3a760fbb44
Fix ADC pin validation on ESP32-C3 (#2551) 2021-10-20 10:14:30 +13:00
Jesse Hills
6ef57a2973
Merge pull request #2550 from esphome/bump-2021.10.0b7
2021.10.0b7
2021-10-19 16:22:29 +13:00
Jesse Hills
3e9c7f2e9f
Bump version to 2021.10.0b7 2021-10-19 15:47:31 +13:00
Jesse Hills
430598b7a1
Bump dashboard to 20211019.0 (#2549) 2021-10-19 15:47:26 +13:00
Jesse Hills
91611b09b4
Merge pull request #2545 from esphome/bump-2021.10.0b6
2021.10.0b6
2021-10-18 21:41:22 +13:00
Jesse Hills
ecd115851f
Bump version to 2021.10.0b6 2021-10-18 21:26:36 +13:00
Maurice Makaay
4a1e50fed1
OTA firmware MD5 check + password support for esp-idf (#2507)
Co-authored-by: Maurice Makaay <account-github@makaay.nl>
2021-10-18 21:26:36 +13:00
Jesse Hills
d6d037047b
Merge pull request #2544 from esphome/bump-2021.10.0b5
2021.10.0b5
2021-10-18 16:37:43 +13:00
Jesse Hills
b5734c2b20
Bump version to 2021.10.0b5 2021-10-18 15:31:01 +13:00
Oxan van Leeuwen
723fb7eaac
Autodetect ESP32 variant (#2530)
Co-authored-by: Otto winter <otto@otto-winter.com>
2021-10-18 15:31:01 +13:00
Jesse Hills
63a9acaa19
Fix Bluetooth setup_priorities (#2458)
Co-authored-by: Otto Winter <otto@otto-winter.com>
2021-10-18 15:31:00 +13:00
Jesse Hills
0524f8c677
Fix const used for IDF recommended version (#2542) 2021-10-18 15:31:00 +13:00
Otto Winter
70b62f272e
Only show timestamp for dashboard access logs (#2540) 2021-10-18 15:31:00 +13:00
Jesse Hills
f0089b7940
Merge pull request #2539 from esphome/bump-2021.10.0b4
2021.10.0b4
2021-10-17 21:17:54 +13:00
Jesse Hills
4b44280d53
Bump version to 2021.10.0b4 2021-10-17 20:34:07 +13:00
Paulus Schoutsen
f045382d20
Bump dashboard to 20211015.0 (#2525)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-10-17 20:34:03 +13:00
Jesse Hills
db3fa1ade7
Allow downloading all bin files from backend in dashboard (#2514)
Co-authored-by: Otto Winter <otto@otto-winter.com>
2021-10-17 20:28:43 +13:00
Oxan van Leeuwen
f83950fd75
Fix bitshift on read in ADE7953 (#2537) 2021-10-17 20:28:43 +13:00
Oxan van Leeuwen
4dd1bf920d
Replace framework version_hint with source option (#2529) 2021-10-17 20:28:43 +13:00
Martin
98755f3621
Fix bug in register name definition (#2526) 2021-10-17 20:28:43 +13:00
Keith Burzinski
c3a8a044b9
Fix Nextion HTTPClient error for ESP32 (#2524) 2021-10-17 20:28:42 +13:00
Martin
15b5ea43a7
Add pressure compensation during runtime (#2493)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2021-10-17 20:28:42 +13:00
Jesse Hills
ec683fc227
Merge pull request #2523 from esphome/bump-2021.10.0b3
2021.10.0b3
2021-10-15 10:15:26 +13:00
Jesse Hills
d4e65eb82a
Bump version to 2021.10.0b3 2021-10-15 09:42:44 +13:00
Paul Monigatti
10c6601b0a
Revert "Added test for bme680_bsec" (#2518)
This reverts commit 7f6a50d291b14935b17802b4dce52135fad1e49e due to BSEC library license restrictions.
2021-10-15 09:42:44 +13:00
Oxan van Leeuwen
73940bc1bd
Don't define UART_SELECTION_UART2 when UART2 is unavailable (#2512) 2021-10-15 09:42:43 +13:00
Paul Monigatti
9b7fb829f9
Fix: Color modes not being correctly used in light partitions (#2513) 2021-10-15 09:42:43 +13:00
Dmitriy Lopatko
c51d8c9021
add missing include in sgp30 (#2517) 2021-10-15 09:42:43 +13:00
Paul Monigatti
d8a6dfe5ce
Fix BME680_BSEC compilation issue with ESP32 (#2516) 2021-10-15 09:42:43 +13:00
Oxan van Leeuwen
5f7cef0b06
Disallow using UART2 for logger on ESP-32 variants that lack it (#2510) 2021-10-15 09:42:43 +13:00
Paul Monigatti
48ff2ffc68
Fix: Light flash not restoring previous LightState (#2383)
* Update light state when transformer has finished

* Revert writing direct to output

* Correct handling of zero-length light transformers

* Allow transformers to handle zero-length transitions, and check more boundary conditions when transitioning back to start state

* Removed log.h

* Fixed race condition between LightFlashTransformer.apply() and is_finished()

* clang-format

* Step progress from 0.0f to 1.0f at t=start_time for zero-length transforms to avoid divide-by-zero
2021-10-15 09:42:43 +13:00
Oxan van Leeuwen
b3b9ccd314
Fix light state remaining on after turn off with transition (#2509) 2021-10-15 09:42:43 +13:00
Jesse Hills
e63c7b483b
Merge pull request #2505 from esphome/bump-2021.10.0b2
2021.10.0b2
2021-10-13 22:45:29 +13:00
Jesse Hills
f57980b069
Bump version to 2021.10.0b2 2021-10-13 22:30:29 +13:00
Jesse Hills
7006aa0d2a
Merge branch 'dev' into beta 2021-10-13 22:11:53 +13:00
Jesse Hills
8051c1ca99
Merge pull request #2504 from esphome/bump-2021.10.0b1
2021.10.0b1
2021-10-13 21:38:41 +13:00
Jesse Hills
a779592414
Bump version to 2021.10.0b1 2021-10-13 16:40:46 +13:00
Jesse Hills
112215848d
Merge branch 'dev' into bump-2021.10.0b1 2021-10-13 16:40:46 +13:00
205 changed files with 3422 additions and 1571 deletions

View File

@ -7,3 +7,8 @@ updates:
ignore:
# Hypotehsis is only used for testing and is updated quite often
- dependency-name: hypothesis
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: daily
open-pull-requests-limit: 10

View File

@ -30,15 +30,15 @@ jobs:
arch: [amd64, armv7, aarch64]
build_type: ["ha-addon", "docker", "lint"]
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v2
uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
uses: docker/setup-buildx-action@v2
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
uses: docker/setup-qemu-action@v2
- name: Set TAG
run: |

View File

@ -75,15 +75,15 @@ jobs:
pio_cache_key: tidyesp32-idf
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v2
uses: actions/setup-python@v4
id: python
with:
python-version: '3.8'
- name: Cache virtualenv
uses: actions/cache@v2
uses: actions/cache@v3
with:
path: .venv
key: venv-${{ steps.python.outputs.python-version }}-${{ hashFiles('requirements*.txt') }}
@ -102,7 +102,7 @@ jobs:
# Use per check platformio cache because checks use different parts
- name: Cache platformio
uses: actions/cache@v2
uses: actions/cache@v3
with:
path: ~/.platformio
key: platformio-${{ matrix.pio_cache_key }}-${{ hashFiles('platformio.ini') }}

View File

@ -17,7 +17,7 @@ jobs:
outputs:
tag: ${{ steps.tag.outputs.tag }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Get tag
id: tag
run: |
@ -35,9 +35,9 @@ jobs:
if: github.repository == 'esphome/esphome' && github.event_name == 'release'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v1
uses: actions/setup-python@v4
with:
python-version: '3.x'
- name: Set up python environment
@ -65,24 +65,24 @@ jobs:
arch: [amd64, armv7, aarch64]
build_type: ["ha-addon", "docker", "lint"]
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v2
uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
uses: docker/setup-buildx-action@v2
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
uses: docker/setup-qemu-action@v2
- name: Log in to docker hub
uses: docker/login-action@v1
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Log in to the GitHub container registry
uses: docker/login-action@v1
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
@ -108,9 +108,9 @@ jobs:
matrix:
build_type: ["ha-addon", "docker", "lint"]
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v2
uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Enable experimental manifest support
@ -119,12 +119,12 @@ jobs:
echo "{\"experimental\": \"enabled\"}" > ~/.docker/config.json
- name: Log in to docker hub
uses: docker/login-action@v1
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Log in to the GitHub container registry
uses: docker/login-action@v1
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}

View File

@ -16,7 +16,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v4
- uses: actions/stale@v5
with:
days-before-pr-stale: 90
days-before-pr-close: 7
@ -35,7 +35,7 @@ jobs:
close-issues:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v4
- uses: actions/stale@v5
with:
days-before-pr-stale: -1
days-before-pr-close: -1

View File

@ -2,7 +2,7 @@
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/ambv/black
rev: 22.3.0
rev: 22.6.0
hooks:
- id: black
args:
@ -26,7 +26,7 @@ repos:
- --branch=release
- --branch=beta
- repo: https://github.com/asottile/pyupgrade
rev: v2.31.1
rev: v2.34.0
hooks:
- id: pyupgrade
args: [--py38-plus]

View File

@ -52,6 +52,7 @@ esphome/components/cs5460a/* @balrog-kun
esphome/components/cse7761/* @berfenger
esphome/components/ct_clamp/* @jesserockz
esphome/components/current_based/* @djwmarcx
esphome/components/dac7678/* @NickB1
esphome/components/daly_bms/* @s1lvi0
esphome/components/dashboard_import/* @esphome/core
esphome/components/debug/* @OttoWinter
@ -88,6 +89,7 @@ esphome/components/honeywellabp/* @RubyBailey
esphome/components/hrxl_maxsonar_wr/* @netmikey
esphome/components/hydreon_rgxx/* @functionpointer
esphome/components/i2c/* @esphome/core
esphome/components/i2s_audio/* @jesserockz
esphome/components/improv_serial/* @esphome/core
esphome/components/ina260/* @MrEditor97
esphome/components/inkbird_ibsth1_mini/* @fkirill
@ -102,6 +104,7 @@ esphome/components/lilygo_t5_47/touchscreen/* @jesserockz
esphome/components/lock/* @esphome/core
esphome/components/logger/* @esphome/core
esphome/components/ltr390/* @sjtrny
esphome/components/max31865/* @DAVe3283
esphome/components/max44009/* @berfenger
esphome/components/max7219digit/* @rspaargaren
esphome/components/max9611/* @mckaymatthew
@ -119,6 +122,7 @@ esphome/components/mcp47a1/* @jesserockz
esphome/components/mcp9808/* @k7hpn
esphome/components/md5/* @esphome/core
esphome/components/mdns/* @esphome/core
esphome/components/media_player/* @jesserockz
esphome/components/midea/* @dudanov
esphome/components/midea_ir/* @dudanov
esphome/components/mitsubishi/* @RubyBailey

View File

@ -46,12 +46,10 @@ RUN \
# Ubuntu python3-pip is missing wheel
pip3 install --no-cache-dir \
wheel==0.37.1 \
platformio==5.2.5 \
platformio==6.0.2 \
# Change some platformio settings
&& platformio settings set enable_telemetry No \
&& platformio settings set check_libraries_interval 1000000 \
&& platformio settings set check_platformio_interval 1000000 \
&& platformio settings set check_platforms_interval 1000000 \
&& mkdir -p /piolibs

View File

@ -12,7 +12,7 @@ from esphome.const import (
CONF_TYPE_ID,
CONF_TIME,
)
from esphome.jsonschema import jschema_extractor
from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor
from esphome.util import Registry
@ -23,11 +23,10 @@ def maybe_simple_id(*validators):
def maybe_conf(conf, *validators):
validator = cv.All(*validators)
@jschema_extractor("maybe")
@schema_extractor("maybe")
def validate(value):
# pylint: disable=comparison-with-callable
if value == jschema_extractor:
return validator
if value == SCHEMA_EXTRACT:
return (validator, conf)
if isinstance(value, dict):
return validator(value)
@ -111,11 +110,9 @@ def validate_automation(extra_schema=None, extra_validators=None, single=False):
# This should only happen with invalid configs, but let's have a nice error message.
return [schema(value)]
@jschema_extractor("automation")
@schema_extractor("automation")
def validator(value):
# hack to get the schema
# pylint: disable=comparison-with-callable
if value == jschema_extractor:
if value == SCHEMA_EXTRACT:
return schema
value = validator_(value)

View File

@ -15,10 +15,21 @@ namespace esphome {
namespace adc {
static const char *const TAG = "adc";
// 13 bits for S3 / 12 bit for all other esp32 variants
// create a const to avoid the repated cast to enum
// 13bit for S2, and 12bit for all other esp32 variants
#ifdef USE_ESP32
static const adc_bits_width_t ADC_WIDTH_MAX_SOC_BITS = static_cast<adc_bits_width_t>(ADC_WIDTH_MAX - 1);
#ifndef SOC_ADC_RTC_MAX_BITWIDTH
#if USE_ESP32_VARIANT_ESP32S2
static const int SOC_ADC_RTC_MAX_BITWIDTH = 13;
#else
static const int SOC_ADC_RTC_MAX_BITWIDTH = 12;
#endif
#endif
static const int ADC_MAX = (1 << SOC_ADC_RTC_MAX_BITWIDTH) - 1; // 4095 (12 bit) or 8191 (13 bit)
static const int ADC_HALF = (1 << SOC_ADC_RTC_MAX_BITWIDTH) >> 1; // 2048 (12 bit) or 4096 (13 bit)
#endif
void ADCSensor::setup() {
@ -75,16 +86,16 @@ void ADCSensor::dump_config() {
} else {
switch (this->attenuation_) {
case ADC_ATTEN_DB_0:
ESP_LOGCONFIG(TAG, " Attenuation: 0db (max 1.1V)");
ESP_LOGCONFIG(TAG, " Attenuation: 0db");
break;
case ADC_ATTEN_DB_2_5:
ESP_LOGCONFIG(TAG, " Attenuation: 2.5db (max 1.5V)");
ESP_LOGCONFIG(TAG, " Attenuation: 2.5db");
break;
case ADC_ATTEN_DB_6:
ESP_LOGCONFIG(TAG, " Attenuation: 6db (max 2.2V)");
ESP_LOGCONFIG(TAG, " Attenuation: 6db");
break;
case ADC_ATTEN_DB_11:
ESP_LOGCONFIG(TAG, " Attenuation: 11db (max 3.9V)");
ESP_LOGCONFIG(TAG, " Attenuation: 11db");
break;
default: // This is to satisfy the unused ADC_ATTEN_MAX
break;
@ -129,16 +140,16 @@ float ADCSensor::sample() {
return mv / 1000.0f;
}
int raw11, raw6 = 4095, raw2 = 4095, raw0 = 4095;
int raw11, raw6 = ADC_MAX, raw2 = ADC_MAX, raw0 = ADC_MAX;
adc1_config_channel_atten(channel_, ADC_ATTEN_DB_11);
raw11 = adc1_get_raw(channel_);
if (raw11 < 4095) {
if (raw11 < ADC_MAX) {
adc1_config_channel_atten(channel_, ADC_ATTEN_DB_6);
raw6 = adc1_get_raw(channel_);
if (raw6 < 4095) {
if (raw6 < ADC_MAX) {
adc1_config_channel_atten(channel_, ADC_ATTEN_DB_2_5);
raw2 = adc1_get_raw(channel_);
if (raw2 < 4095) {
if (raw2 < ADC_MAX) {
adc1_config_channel_atten(channel_, ADC_ATTEN_DB_0);
raw0 = adc1_get_raw(channel_);
}
@ -154,15 +165,15 @@ float ADCSensor::sample() {
uint32_t mv2 = esp_adc_cal_raw_to_voltage(raw2, &cal_characteristics_[(int) ADC_ATTEN_DB_2_5]);
uint32_t mv0 = esp_adc_cal_raw_to_voltage(raw0, &cal_characteristics_[(int) ADC_ATTEN_DB_0]);
// Contribution of each value, in range 0-2048
uint32_t c11 = std::min(raw11, 2048);
uint32_t c6 = 2048 - std::abs(raw6 - 2048);
uint32_t c2 = 2048 - std::abs(raw2 - 2048);
uint32_t c0 = std::min(4095 - raw0, 2048);
// max theoretical csum value is 2048*4 = 8192
// Contribution of each value, in range 0-2048 (12 bit ADC) or 0-4096 (13 bit ADC)
uint32_t c11 = std::min(raw11, ADC_HALF);
uint32_t c6 = ADC_HALF - std::abs(raw6 - ADC_HALF);
uint32_t c2 = ADC_HALF - std::abs(raw2 - ADC_HALF);
uint32_t c0 = std::min(ADC_MAX - raw0, ADC_HALF);
// max theoretical csum value is 4096*4 = 16384
uint32_t csum = c11 + c6 + c2 + c0;
// each mv is max 3900; so max value is 3900*2048*4, fits in unsigned
// each mv is max 3900; so max value is 3900*4096*4, fits in unsigned32
uint32_t mv_scaled = (mv11 * c11) + (mv6 * c6) + (mv2 * c2) + (mv0 * c0);
return mv_scaled / (float) (csum * 1000U);
}

View File

@ -40,6 +40,8 @@ class AddressableLightDisplay : public display::DisplayBuffer, public PollingCom
void setup() override;
void display();
display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_COLOR; }
protected:
int get_width_internal() override;
int get_height_internal() override;

View File

@ -76,6 +76,8 @@ async def to_code(config):
pos = 0
for frameIndex in range(frames):
image.seek(frameIndex)
if CONF_RESIZE in config:
image.thumbnail(config[CONF_RESIZE])
frame = image.convert("RGB")
if CONF_RESIZE in config:
frame = frame.resize([width, height])

View File

@ -92,7 +92,7 @@ void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_
}
if (this->codec_->has_unit()) {
this->fahrenheit_ = (this->codec_->unit_ == 'f');
ESP_LOGD(TAG, "Anova units is %s", this->fahrenheit_ ? "fahrenheit" : "celcius");
ESP_LOGD(TAG, "Anova units is %s", this->fahrenheit_ ? "fahrenheit" : "celsius");
this->current_request_++;
}
this->publish_state();

View File

@ -42,6 +42,7 @@ service APIConnection {
rpc select_command (SelectCommandRequest) returns (void) {}
rpc button_command (ButtonCommandRequest) returns (void) {}
rpc lock_command (LockCommandRequest) returns (void) {}
rpc media_player_command (MediaPlayerCommandRequest) returns (void) {}
}
@ -991,7 +992,7 @@ message ListEntitiesLockResponse {
bool supports_open = 9;
bool requires_code = 10;
# Not yet implemented:
// Not yet implemented:
string code_format = 11;
}
message LockStateResponse {
@ -1010,7 +1011,7 @@ message LockCommandRequest {
fixed32 key = 1;
LockCommand command = 2;
# Not yet implemented:
// Not yet implemented:
bool has_code = 3;
string code = 4;
}
@ -1040,3 +1041,60 @@ message ButtonCommandRequest {
fixed32 key = 1;
}
// ==================== MEDIA PLAYER ====================
enum MediaPlayerState {
MEDIA_PLAYER_STATE_NONE = 0;
MEDIA_PLAYER_STATE_IDLE = 1;
MEDIA_PLAYER_STATE_PLAYING = 2;
MEDIA_PLAYER_STATE_PAUSED = 3;
}
enum MediaPlayerCommand {
MEDIA_PLAYER_COMMAND_PLAY = 0;
MEDIA_PLAYER_COMMAND_PAUSE = 1;
MEDIA_PLAYER_COMMAND_STOP = 2;
MEDIA_PLAYER_COMMAND_MUTE = 3;
MEDIA_PLAYER_COMMAND_UNMUTE = 4;
}
message ListEntitiesMediaPlayerResponse {
option (id) = 63;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_MEDIA_PLAYER";
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
string icon = 5;
bool disabled_by_default = 6;
EntityCategory entity_category = 7;
bool supports_pause = 8;
}
message MediaPlayerStateResponse {
option (id) = 64;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_MEDIA_PLAYER";
option (no_delay) = true;
fixed32 key = 1;
MediaPlayerState state = 2;
float volume = 3;
bool muted = 4;
}
message MediaPlayerCommandRequest {
option (id) = 65;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_MEDIA_PLAYER";
option (no_delay) = true;
fixed32 key = 1;
bool has_command = 2;
MediaPlayerCommand command = 3;
bool has_volume = 4;
float volume = 5;
bool has_media_url = 6;
string media_url = 7;
}

View File

@ -733,6 +733,52 @@ void APIConnection::lock_command(const LockCommandRequest &msg) {
}
#endif
#ifdef USE_MEDIA_PLAYER
bool APIConnection::send_media_player_state(media_player::MediaPlayer *media_player) {
if (!this->state_subscription_)
return false;
MediaPlayerStateResponse resp{};
resp.key = media_player->get_object_id_hash();
resp.state = static_cast<enums::MediaPlayerState>(media_player->state);
resp.volume = media_player->volume;
resp.muted = media_player->is_muted();
return this->send_media_player_state_response(resp);
}
bool APIConnection::send_media_player_info(media_player::MediaPlayer *media_player) {
ListEntitiesMediaPlayerResponse msg;
msg.key = media_player->get_object_id_hash();
msg.object_id = media_player->get_object_id();
msg.name = media_player->get_name();
msg.unique_id = get_default_unique_id("media_player", media_player);
msg.icon = media_player->get_icon();
msg.disabled_by_default = media_player->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(media_player->get_entity_category());
auto traits = media_player->get_traits();
msg.supports_pause = traits.get_supports_pause();
return this->send_list_entities_media_player_response(msg);
}
void APIConnection::media_player_command(const MediaPlayerCommandRequest &msg) {
media_player::MediaPlayer *media_player = App.get_media_player_by_key(msg.key);
if (media_player == nullptr)
return;
auto call = media_player->make_call();
if (msg.has_command) {
call.set_command(static_cast<media_player::MediaPlayerCommand>(msg.command));
}
if (msg.has_volume) {
call.set_volume(msg.volume);
}
if (msg.has_media_url) {
call.set_media_url(msg.media_url);
}
call.perform();
}
#endif
#ifdef USE_ESP32_CAMERA
void APIConnection::send_camera_state(std::shared_ptr<esp32_camera::CameraImage> image) {
if (!this->state_subscription_)

View File

@ -82,6 +82,11 @@ class APIConnection : public APIServerConnection {
bool send_lock_state(lock::Lock *a_lock, lock::LockState state);
bool send_lock_info(lock::Lock *a_lock);
void lock_command(const LockCommandRequest &msg) override;
#endif
#ifdef USE_MEDIA_PLAYER
bool send_media_player_state(media_player::MediaPlayer *media_player);
bool send_media_player_info(media_player::MediaPlayer *media_player);
void media_player_command(const MediaPlayerCommandRequest &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

@ -270,7 +270,7 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
*
* If the handshake is still active when this method returns and a read/write can't take place at
* the moment, returns WOULD_BLOCK.
* If an error occured, returns that error. Only returns OK if the transport is ready for data
* If an error occurred, returns that error. Only returns OK if the transport is ready for data
* traffic.
*/
APIError APINoiseFrameHelper::state_action_() {
@ -586,7 +586,7 @@ APIError APINoiseFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
}
return APIError::OK;
} else if (sent == -1) {
// an error occured
// an error occurred
state_ = State::FAILED;
HELPER_LOG("Socket write failed with errno %d", errno);
return APIError::SOCKET_WRITE_FAILED;
@ -980,7 +980,7 @@ APIError APIPlaintextFrameHelper::write_raw_(const struct iovec *iov, int iovcnt
}
return APIError::OK;
} else if (sent == -1) {
// an error occured
// an error occurred
state_ = State::FAILED;
HELPER_LOG("Socket write failed with errno %d", errno);
return APIError::SOCKET_WRITE_FAILED;

View File

@ -308,6 +308,36 @@ template<> const char *proto_enum_to_string<enums::LockCommand>(enums::LockComma
return "UNKNOWN";
}
}
template<> const char *proto_enum_to_string<enums::MediaPlayerState>(enums::MediaPlayerState value) {
switch (value) {
case enums::MEDIA_PLAYER_STATE_NONE:
return "MEDIA_PLAYER_STATE_NONE";
case enums::MEDIA_PLAYER_STATE_IDLE:
return "MEDIA_PLAYER_STATE_IDLE";
case enums::MEDIA_PLAYER_STATE_PLAYING:
return "MEDIA_PLAYER_STATE_PLAYING";
case enums::MEDIA_PLAYER_STATE_PAUSED:
return "MEDIA_PLAYER_STATE_PAUSED";
default:
return "UNKNOWN";
}
}
template<> const char *proto_enum_to_string<enums::MediaPlayerCommand>(enums::MediaPlayerCommand value) {
switch (value) {
case enums::MEDIA_PLAYER_COMMAND_PLAY:
return "MEDIA_PLAYER_COMMAND_PLAY";
case enums::MEDIA_PLAYER_COMMAND_PAUSE:
return "MEDIA_PLAYER_COMMAND_PAUSE";
case enums::MEDIA_PLAYER_COMMAND_STOP:
return "MEDIA_PLAYER_COMMAND_STOP";
case enums::MEDIA_PLAYER_COMMAND_MUTE:
return "MEDIA_PLAYER_COMMAND_MUTE";
case enums::MEDIA_PLAYER_COMMAND_UNMUTE:
return "MEDIA_PLAYER_COMMAND_UNMUTE";
default:
return "UNKNOWN";
}
}
bool HelloRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 1: {
@ -4574,6 +4604,254 @@ void ButtonCommandRequest::dump_to(std::string &out) const {
out.append("}");
}
#endif
bool ListEntitiesMediaPlayerResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 6: {
this->disabled_by_default = value.as_bool();
return true;
}
case 7: {
this->entity_category = value.as_enum<enums::EntityCategory>();
return true;
}
case 8: {
this->supports_pause = value.as_bool();
return true;
}
default:
return false;
}
}
bool ListEntitiesMediaPlayerResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 1: {
this->object_id = value.as_string();
return true;
}
case 3: {
this->name = value.as_string();
return true;
}
case 4: {
this->unique_id = value.as_string();
return true;
}
case 5: {
this->icon = value.as_string();
return true;
}
default:
return false;
}
}
bool ListEntitiesMediaPlayerResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
switch (field_id) {
case 2: {
this->key = value.as_fixed32();
return true;
}
default:
return false;
}
}
void ListEntitiesMediaPlayerResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(1, this->object_id);
buffer.encode_fixed32(2, this->key);
buffer.encode_string(3, this->name);
buffer.encode_string(4, this->unique_id);
buffer.encode_string(5, this->icon);
buffer.encode_bool(6, this->disabled_by_default);
buffer.encode_enum<enums::EntityCategory>(7, this->entity_category);
buffer.encode_bool(8, this->supports_pause);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesMediaPlayerResponse::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64];
out.append("ListEntitiesMediaPlayerResponse {\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");
out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default));
out.append("\n");
out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n");
out.append(" supports_pause: ");
out.append(YESNO(this->supports_pause));
out.append("\n");
out.append("}");
}
#endif
bool MediaPlayerStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 2: {
this->state = value.as_enum<enums::MediaPlayerState>();
return true;
}
case 4: {
this->muted = value.as_bool();
return true;
}
default:
return false;
}
}
bool MediaPlayerStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
switch (field_id) {
case 1: {
this->key = value.as_fixed32();
return true;
}
case 3: {
this->volume = value.as_float();
return true;
}
default:
return false;
}
}
void MediaPlayerStateResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_fixed32(1, this->key);
buffer.encode_enum<enums::MediaPlayerState>(2, this->state);
buffer.encode_float(3, this->volume);
buffer.encode_bool(4, this->muted);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void MediaPlayerStateResponse::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64];
out.append("MediaPlayerStateResponse {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
out.append(buffer);
out.append("\n");
out.append(" state: ");
out.append(proto_enum_to_string<enums::MediaPlayerState>(this->state));
out.append("\n");
out.append(" volume: ");
sprintf(buffer, "%g", this->volume);
out.append(buffer);
out.append("\n");
out.append(" muted: ");
out.append(YESNO(this->muted));
out.append("\n");
out.append("}");
}
#endif
bool MediaPlayerCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 2: {
this->has_command = value.as_bool();
return true;
}
case 3: {
this->command = value.as_enum<enums::MediaPlayerCommand>();
return true;
}
case 4: {
this->has_volume = value.as_bool();
return true;
}
case 6: {
this->has_media_url = value.as_bool();
return true;
}
default:
return false;
}
}
bool MediaPlayerCommandRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 7: {
this->media_url = value.as_string();
return true;
}
default:
return false;
}
}
bool MediaPlayerCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
switch (field_id) {
case 1: {
this->key = value.as_fixed32();
return true;
}
case 5: {
this->volume = value.as_float();
return true;
}
default:
return false;
}
}
void MediaPlayerCommandRequest::encode(ProtoWriteBuffer buffer) const {
buffer.encode_fixed32(1, this->key);
buffer.encode_bool(2, this->has_command);
buffer.encode_enum<enums::MediaPlayerCommand>(3, this->command);
buffer.encode_bool(4, this->has_volume);
buffer.encode_float(5, this->volume);
buffer.encode_bool(6, this->has_media_url);
buffer.encode_string(7, this->media_url);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void MediaPlayerCommandRequest::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64];
out.append("MediaPlayerCommandRequest {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
out.append(buffer);
out.append("\n");
out.append(" has_command: ");
out.append(YESNO(this->has_command));
out.append("\n");
out.append(" command: ");
out.append(proto_enum_to_string<enums::MediaPlayerCommand>(this->command));
out.append("\n");
out.append(" has_volume: ");
out.append(YESNO(this->has_volume));
out.append("\n");
out.append(" volume: ");
sprintf(buffer, "%g", this->volume);
out.append(buffer);
out.append("\n");
out.append(" has_media_url: ");
out.append(YESNO(this->has_media_url));
out.append("\n");
out.append(" media_url: ");
out.append("'").append(this->media_url).append("'");
out.append("\n");
out.append("}");
}
#endif
} // namespace api
} // namespace esphome

View File

@ -141,6 +141,19 @@ enum LockCommand : uint32_t {
LOCK_LOCK = 1,
LOCK_OPEN = 2,
};
enum MediaPlayerState : uint32_t {
MEDIA_PLAYER_STATE_NONE = 0,
MEDIA_PLAYER_STATE_IDLE = 1,
MEDIA_PLAYER_STATE_PLAYING = 2,
MEDIA_PLAYER_STATE_PAUSED = 3,
};
enum MediaPlayerCommand : uint32_t {
MEDIA_PLAYER_COMMAND_PLAY = 0,
MEDIA_PLAYER_COMMAND_PAUSE = 1,
MEDIA_PLAYER_COMMAND_STOP = 2,
MEDIA_PLAYER_COMMAND_MUTE = 3,
MEDIA_PLAYER_COMMAND_UNMUTE = 4,
};
} // namespace enums
@ -1146,6 +1159,60 @@ class ButtonCommandRequest : public ProtoMessage {
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
};
class ListEntitiesMediaPlayerResponse : public ProtoMessage {
public:
std::string object_id{};
uint32_t key{0};
std::string name{};
std::string unique_id{};
std::string icon{};
bool disabled_by_default{false};
enums::EntityCategory entity_category{};
bool supports_pause{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 MediaPlayerStateResponse : public ProtoMessage {
public:
uint32_t key{0};
enums::MediaPlayerState state{};
float volume{0.0f};
bool muted{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_varint(uint32_t field_id, ProtoVarInt value) override;
};
class MediaPlayerCommandRequest : public ProtoMessage {
public:
uint32_t key{0};
bool has_command{false};
enums::MediaPlayerCommand command{};
bool has_volume{false};
float volume{0.0f};
bool has_media_url{false};
std::string media_url{};
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;
};
} // namespace api
} // namespace esphome

View File

@ -310,6 +310,24 @@ bool APIServerConnectionBase::send_list_entities_button_response(const ListEntit
#endif
#ifdef USE_BUTTON
#endif
#ifdef USE_MEDIA_PLAYER
bool APIServerConnectionBase::send_list_entities_media_player_response(const ListEntitiesMediaPlayerResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_media_player_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesMediaPlayerResponse>(msg, 63);
}
#endif
#ifdef USE_MEDIA_PLAYER
bool APIServerConnectionBase::send_media_player_state_response(const MediaPlayerStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_media_player_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<MediaPlayerStateResponse>(msg, 64);
}
#endif
#ifdef USE_MEDIA_PLAYER
#endif
bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {
switch (msg_type) {
case 1: {
@ -563,6 +581,17 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
ESP_LOGVV(TAG, "on_button_command_request: %s", msg.dump().c_str());
#endif
this->on_button_command_request(msg);
#endif
break;
}
case 65: {
#ifdef USE_MEDIA_PLAYER
MediaPlayerCommandRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_media_player_command_request: %s", msg.dump().c_str());
#endif
this->on_media_player_command_request(msg);
#endif
break;
}
@ -813,6 +842,19 @@ void APIServerConnection::on_lock_command_request(const LockCommandRequest &msg)
this->lock_command(msg);
}
#endif
#ifdef USE_MEDIA_PLAYER
void APIServerConnection::on_media_player_command_request(const MediaPlayerCommandRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->media_player_command(msg);
}
#endif
} // namespace api
} // namespace esphome

View File

@ -144,6 +144,15 @@ class APIServerConnectionBase : public ProtoService {
#endif
#ifdef USE_BUTTON
virtual void on_button_command_request(const ButtonCommandRequest &value){};
#endif
#ifdef USE_MEDIA_PLAYER
bool send_list_entities_media_player_response(const ListEntitiesMediaPlayerResponse &msg);
#endif
#ifdef USE_MEDIA_PLAYER
bool send_media_player_state_response(const MediaPlayerStateResponse &msg);
#endif
#ifdef USE_MEDIA_PLAYER
virtual void on_media_player_command_request(const MediaPlayerCommandRequest &value){};
#endif
protected:
bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;
@ -192,6 +201,9 @@ class APIServerConnection : public APIServerConnectionBase {
#endif
#ifdef USE_LOCK
virtual void lock_command(const LockCommandRequest &msg) = 0;
#endif
#ifdef USE_MEDIA_PLAYER
virtual void media_player_command(const MediaPlayerCommandRequest &msg) = 0;
#endif
protected:
void on_hello_request(const HelloRequest &msg) override;
@ -236,6 +248,9 @@ class APIServerConnection : public APIServerConnectionBase {
#ifdef USE_LOCK
void on_lock_command_request(const LockCommandRequest &msg) override;
#endif
#ifdef USE_MEDIA_PLAYER
void on_media_player_command_request(const MediaPlayerCommandRequest &msg) override;
#endif
};
} // namespace api

View File

@ -272,6 +272,15 @@ void APIServer::on_lock_update(lock::Lock *obj) {
}
#endif
#ifdef USE_MEDIA_PLAYER
void APIServer::on_media_player_update(media_player::MediaPlayer *obj) {
if (obj->is_internal())
return;
for (auto &c : this->clients_)
c->send_media_player_state(obj);
}
#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

@ -68,6 +68,9 @@ class APIServer : public Component, public Controller {
#endif
#ifdef USE_LOCK
void on_lock_update(lock::Lock *obj) override;
#endif
#ifdef USE_MEDIA_PLAYER
void on_media_player_update(media_player::MediaPlayer *obj) override;
#endif
void send_homeassistant_service_call(const HomeassistantServiceResponse &call);
void register_user_service(UserServiceDescriptor *descriptor) { this->user_services_.push_back(descriptor); }

View File

@ -64,5 +64,11 @@ bool ListEntitiesIterator::on_number(number::Number *number) { return this->clie
bool ListEntitiesIterator::on_select(select::Select *select) { return this->client_->send_select_info(select); }
#endif
#ifdef USE_MEDIA_PLAYER
bool ListEntitiesIterator::on_media_player(media_player::MediaPlayer *media_player) {
return this->client_->send_media_player_info(media_player);
}
#endif
} // namespace api
} // namespace esphome

View File

@ -51,6 +51,9 @@ class ListEntitiesIterator : public ComponentIterator {
#endif
#ifdef USE_LOCK
bool on_lock(lock::Lock *a_lock) override;
#endif
#ifdef USE_MEDIA_PLAYER
bool on_media_player(media_player::MediaPlayer *media_player) override;
#endif
bool on_end() override;

View File

@ -50,6 +50,11 @@ bool InitialStateIterator::on_select(select::Select *select) {
#ifdef USE_LOCK
bool InitialStateIterator::on_lock(lock::Lock *a_lock) { return this->client_->send_lock_state(a_lock, a_lock->state); }
#endif
#ifdef USE_MEDIA_PLAYER
bool InitialStateIterator::on_media_player(media_player::MediaPlayer *media_player) {
return this->client_->send_media_player_state(media_player);
}
#endif
InitialStateIterator::InitialStateIterator(APIConnection *client) : client_(client) {}
} // namespace api

View File

@ -48,6 +48,9 @@ class InitialStateIterator : public ComponentIterator {
#endif
#ifdef USE_LOCK
bool on_lock(lock::Lock *a_lock) override;
#endif
#ifdef USE_MEDIA_PLAYER
bool on_media_player(media_player::MediaPlayer *media_player) override;
#endif
protected:
APIConnection *client_;

View File

@ -36,6 +36,14 @@ static uint8_t bedjet_fan_speed_to_step(const std::string &fan_step_percent) {
return -1;
}
static BedjetButton heat_button(BedjetHeatMode mode) {
BedjetButton btn = BTN_HEAT;
if (mode == HEAT_MODE_EXTENDED) {
btn = BTN_EXTHT;
}
return btn;
}
void Bedjet::upgrade_firmware() {
auto *pkt = this->codec_->get_button_request(MAGIC_UPDATE);
auto status = this->write_bedjet_packet_(pkt);
@ -117,7 +125,7 @@ void Bedjet::control(const ClimateCall &call) {
pkt = this->codec_->get_button_request(BTN_OFF);
break;
case climate::CLIMATE_MODE_HEAT:
pkt = this->codec_->get_button_request(BTN_HEAT);
pkt = this->codec_->get_button_request(heat_button(this->heating_mode_));
break;
case climate::CLIMATE_MODE_FAN_ONLY:
pkt = this->codec_->get_button_request(BTN_COOL);
@ -186,6 +194,8 @@ void Bedjet::control(const ClimateCall &call) {
pkt = this->codec_->get_button_request(BTN_M2);
} else if (preset == "M3") {
pkt = this->codec_->get_button_request(BTN_M3);
} else if (preset == "LTD HT") {
pkt = this->codec_->get_button_request(BTN_HEAT);
} else if (preset == "EXT HT") {
pkt = this->codec_->get_button_request(BTN_EXTHT);
} else {
@ -298,7 +308,7 @@ void Bedjet::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc
#ifdef USE_TIME
if (this->time_id_.has_value()) {
this->send_local_time_();
this->send_local_time();
}
#endif
break;
@ -453,40 +463,47 @@ uint8_t Bedjet::write_notify_config_descriptor_(bool enable) {
#ifdef USE_TIME
/** Attempts to sync the local time (via `time_id`) to the BedJet device. */
void Bedjet::send_local_time_() {
if (this->node_state != espbt::ClientState::ESTABLISHED) {
ESP_LOGV(TAG, "[%s] Not connected, cannot send time.", this->get_name().c_str());
return;
}
auto *time_id = *this->time_id_;
time::ESPTime now = time_id->now();
if (now.is_valid()) {
uint8_t hour = now.hour;
uint8_t minute = now.minute;
BedjetPacket *pkt = this->codec_->get_set_time_request(hour, minute);
auto status = this->write_bedjet_packet_(pkt);
if (status) {
ESP_LOGW(TAG, "Failed setting BedJet clock: %d", status);
} else {
ESP_LOGD(TAG, "[%s] BedJet clock set to: %d:%02d", this->get_name().c_str(), hour, minute);
void Bedjet::send_local_time() {
if (this->time_id_.has_value()) {
auto *time_id = *this->time_id_;
time::ESPTime now = time_id->now();
if (now.is_valid()) {
this->set_clock(now.hour, now.minute);
ESP_LOGD(TAG, "Using time component to set BedJet clock: %d:%02d", now.hour, now.minute);
}
} else {
ESP_LOGI(TAG, "`time_id` is not configured: will not sync BedJet clock.");
}
}
/** Initializes time sync callbacks to support syncing current time to the BedJet. */
void Bedjet::setup_time_() {
if (this->time_id_.has_value()) {
this->send_local_time_();
this->send_local_time();
auto *time_id = *this->time_id_;
time_id->add_on_time_sync_callback([this] { this->send_local_time_(); });
time::ESPTime now = time_id->now();
ESP_LOGD(TAG, "Using time component to set BedJet clock: %d:%02d", now.hour, now.minute);
time_id->add_on_time_sync_callback([this] { this->send_local_time(); });
} else {
ESP_LOGI(TAG, "`time_id` is not configured: will not sync BedJet clock.");
}
}
#endif
/** Attempt to set the BedJet device's clock to the specified time. */
void Bedjet::set_clock(uint8_t hour, uint8_t minute) {
if (this->node_state != espbt::ClientState::ESTABLISHED) {
ESP_LOGV(TAG, "[%s] Not connected, cannot send time.", this->get_name().c_str());
return;
}
BedjetPacket *pkt = this->codec_->get_set_time_request(hour, minute);
auto status = this->write_bedjet_packet_(pkt);
if (status) {
ESP_LOGW(TAG, "Failed setting BedJet clock: %d", status);
} else {
ESP_LOGD(TAG, "[%s] BedJet clock set to: %d:%02d", this->get_name().c_str(), hour, minute);
}
}
/** Writes one BedjetPacket to the BLE client on the BEDJET_COMMAND_UUID. */
uint8_t Bedjet::write_bedjet_packet_(BedjetPacket *pkt) {
if (this->node_state != espbt::ClientState::ESTABLISHED) {
@ -557,11 +574,25 @@ bool Bedjet::update_status_() {
break;
case MODE_HEAT:
this->mode = climate::CLIMATE_MODE_HEAT;
this->action = climate::CLIMATE_ACTION_HEATING;
this->preset.reset();
if (this->heating_mode_ == HEAT_MODE_EXTENDED) {
this->set_custom_preset_("LTD HT");
} else {
this->custom_preset.reset();
}
break;
case MODE_EXTHT:
this->mode = climate::CLIMATE_MODE_HEAT;
this->action = climate::CLIMATE_ACTION_HEATING;
this->custom_preset.reset();
this->preset.reset();
if (this->heating_mode_ == HEAT_MODE_EXTENDED) {
this->custom_preset.reset();
} else {
this->set_custom_preset_("EXT HT");
}
break;
case MODE_COOL:

View File

@ -38,8 +38,12 @@ class Bedjet : public climate::Climate, public esphome::ble_client::BLEClientNod
#ifdef USE_TIME
void set_time_id(time::RealTimeClock *time_id) { this->time_id_ = time_id; }
void send_local_time();
#endif
void set_clock(uint8_t hour, uint8_t minute);
void set_status_timeout(uint32_t timeout) { this->timeout_ = timeout; }
/** Sets the default strategy to use for climate::CLIMATE_MODE_HEAT. */
void set_heating_mode(BedjetHeatMode mode) { this->heating_mode_ = mode; }
/** Attempts to check for and apply firmware updates. */
void upgrade_firmware();
@ -74,6 +78,11 @@ class Bedjet : public climate::Climate, public esphome::ble_client::BLEClientNod
"M2",
"M3",
});
if (this->heating_mode_ == HEAT_MODE_EXTENDED) {
traits.add_supported_custom_preset("LTD HT");
} else {
traits.add_supported_custom_preset("EXT HT");
}
traits.set_visual_min_temperature(19.0);
traits.set_visual_max_temperature(43.0);
traits.set_visual_temperature_step(1.0);
@ -85,11 +94,11 @@ class Bedjet : public climate::Climate, public esphome::ble_client::BLEClientNod
#ifdef USE_TIME
void setup_time_();
void send_local_time_();
optional<time::RealTimeClock *> time_id_{};
#endif
uint32_t timeout_{DEFAULT_STATUS_TIMEOUT};
BedjetHeatMode heating_mode_ = HEAT_MODE_HEAT;
static const uint32_t MIN_NOTIFY_THROTTLE = 5000;
static const uint32_t NOTIFY_WARN_THRESHOLD = 300000;

View File

@ -24,6 +24,14 @@ enum BedjetMode : uint8_t {
MODE_WAIT = 6,
};
/** Optional heating strategies to use for climate::CLIMATE_MODE_HEAT. */
enum BedjetHeatMode {
/// HVACMode.HEAT is handled using BTN_HEAT (default)
HEAT_MODE_HEAT,
/// HVACMode.HEAT is handled using BTN_EXTHT
HEAT_MODE_EXTENDED,
};
enum BedjetButton : uint8_t {
/// Turn BedJet off
BTN_OFF = 0x1,

View File

@ -2,6 +2,7 @@ import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import climate, ble_client, time
from esphome.const import (
CONF_HEAT_MODE,
CONF_ID,
CONF_RECEIVE_TIMEOUT,
CONF_TIME_ID,
@ -14,11 +15,19 @@ bedjet_ns = cg.esphome_ns.namespace("bedjet")
Bedjet = bedjet_ns.class_(
"Bedjet", climate.Climate, ble_client.BLEClientNode, cg.PollingComponent
)
BedjetHeatMode = bedjet_ns.enum("BedjetHeatMode")
BEDJET_HEAT_MODES = {
"heat": BedjetHeatMode.HEAT_MODE_HEAT,
"extended": BedjetHeatMode.HEAT_MODE_EXTENDED,
}
CONFIG_SCHEMA = (
climate.CLIMATE_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(Bedjet),
cv.Optional(CONF_HEAT_MODE, default="heat"): cv.enum(
BEDJET_HEAT_MODES, lower=True
),
cv.Optional(CONF_TIME_ID): cv.use_id(time.RealTimeClock),
cv.Optional(
CONF_RECEIVE_TIMEOUT, default="0s"
@ -35,6 +44,7 @@ async def to_code(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_heating_mode(config[CONF_HEAT_MODE]))
if CONF_TIME_ID in config:
time_ = await cg.get_variable(config[CONF_TIME_ID])
cg.add(var.set_time_id(time_))

View File

@ -69,7 +69,6 @@ void BinarySensor::add_filters(const std::vector<Filter *> &filters) {
}
}
bool BinarySensor::has_state() const { return this->has_state_; }
uint32_t BinarySensor::hash_base() { return 1210250844UL; }
bool BinarySensor::is_status_binary_sensor() const { return false; }
} // namespace binary_sensor

View File

@ -76,8 +76,6 @@ class BinarySensor : public EntityBase {
virtual std::string device_class();
protected:
uint32_t hash_base() override;
CallbackManager<void(bool)> state_callback_{};
optional<std::string> device_class_{}; ///< Stores the override of the device class
Filter *filter_list_{nullptr};

View File

@ -7,7 +7,7 @@ namespace bl0939 {
static const char *const TAG = "bl0939";
// https://www.belling.com.cn/media/file_object/bel_product/BL0939/datasheet/BL0939_V1.2_cn.pdf
// (unfortunatelly chinese, but the protocol can be understood with some translation tool)
// (unfortunately chinese, but the protocol can be understood with some translation tool)
static const uint8_t BL0939_READ_COMMAND = 0x55; // 0x5{A4,A3,A2,A1}
static const uint8_t BL0939_FULL_PACKET = 0xAA;
static const uint8_t BL0939_PACKET_HEADER = 0x55;

View File

@ -8,7 +8,7 @@ namespace esphome {
namespace bl0939 {
// https://datasheet.lcsc.com/lcsc/2108071830_BL-Shanghai-Belling-BL0939_C2841044.pdf
// (unfortunatelly chinese, but the formulas can be easily understood)
// (unfortunately chinese, but the formulas can be easily understood)
// Sonoff Dual R3 V2 has the exact same resistor values for the current shunts (RL=1miliOhm)
// and for the voltage divider (R1=0.51kOhm, R2=5*390kOhm)
// as in the manufacturer's reference circuit, so the same formulas were used here (Vref=1.218V)

View File

@ -113,6 +113,7 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
}
case ESP_GATTC_OPEN_EVT: {
ESP_LOGV(TAG, "[%s] ESP_GATTC_OPEN_EVT", this->address_str().c_str());
this->conn_id = param->open.conn_id;
if (param->open.status != ESP_GATT_OK) {
ESP_LOGW(TAG, "connect to %s failed, status=%d", this->address_str().c_str(), param->open.status);
this->set_states_(espbt::ClientState::IDLE);
@ -122,7 +123,10 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
}
case ESP_GATTC_CONNECT_EVT: {
ESP_LOGV(TAG, "[%s] ESP_GATTC_CONNECT_EVT", this->address_str().c_str());
this->conn_id = param->connect.conn_id;
if (this->conn_id != param->connect.conn_id) {
ESP_LOGD(TAG, "[%s] Unexpected conn_id in CONNECT_EVT: param conn=%d, open conn=%d",
this->address_str().c_str(), param->connect.conn_id, this->conn_id);
}
auto ret = esp_ble_gattc_send_mtu_req(this->gattc_if, param->connect.conn_id);
if (ret) {
ESP_LOGW(TAG, "esp_ble_gattc_send_mtu_req failed, status=%x", ret);
@ -183,9 +187,10 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
descr->uuid.to_string().c_str());
break;
}
uint8_t notify_en = 1;
auto status = esp_ble_gattc_write_char_descr(this->gattc_if, this->conn_id, descr->handle, sizeof(notify_en),
&notify_en, ESP_GATT_WRITE_TYPE_RSP, ESP_GATT_AUTH_REQ_NONE);
uint16_t notify_en = 1;
auto status =
esp_ble_gattc_write_char_descr(this->gattc_if, this->conn_id, descr->handle, sizeof(notify_en),
(uint8_t *) &notify_en, ESP_GATT_WRITE_TYPE_RSP, ESP_GATT_AUTH_REQ_NONE);
if (status) {
ESP_LOGW(TAG, "esp_ble_gattc_write_char_descr error, status=%d", status);
}

View File

@ -11,8 +11,6 @@ namespace ble_client {
static const char *const TAG = "ble_sensor";
uint32_t BLESensor::hash_base() { return 343459825UL; }
void BLESensor::loop() {}
void BLESensor::dump_config() {

View File

@ -37,7 +37,6 @@ class BLESensor : public sensor::Sensor, public PollingComponent, public BLEClie
uint16_t handle;
protected:
uint32_t hash_base() override;
float parse_data_(uint8_t *value, uint16_t value_len);
optional<data_to_value_t> data_to_value_func_{};
bool notify_;

View File

@ -14,8 +14,6 @@ static const char *const TAG = "ble_text_sensor";
static const std::string EMPTY = "";
uint32_t BLETextSensor::hash_base() { return 193967603UL; }
void BLETextSensor::loop() {}
void BLETextSensor::dump_config() {

View File

@ -35,7 +35,6 @@ class BLETextSensor : public text_sensor::TextSensor, public PollingComponent, p
uint16_t handle;
protected:
uint32_t hash_base() override;
bool notify_;
espbt::ESPBTUUID service_uuid_;
espbt::ESPBTUUID char_uuid_;

View File

@ -25,7 +25,7 @@ OVERSAMPLING_OPTIONS = {
"4X": Oversampling.OVERSAMPLING_X4,
"8X": Oversampling.OVERSAMPLING_X8,
"16X": Oversampling.OVERSAMPLING_X16,
"32x": Oversampling.OVERSAMPLING_X32,
"32X": Oversampling.OVERSAMPLING_X32,
}
IIRFilter = bmp3xx_ns.enum("IIRFilter")

View File

@ -15,7 +15,6 @@ void Button::press() {
this->press_callback_.call();
}
void Button::add_on_press_callback(std::function<void()> &&callback) { this->press_callback_.add(std::move(callback)); }
uint32_t Button::hash_base() { return 1495763804UL; }
void Button::set_device_class(const std::string &device_class) { this->device_class_ = device_class; }
std::string Button::get_device_class() { return this->device_class_; }

View File

@ -47,8 +47,6 @@ class Button : public EntityBase {
*/
virtual void press_action() = 0;
uint32_t hash_base() override;
CallbackManager<void()> press_callback_{};
std::string device_class_{};
};

View File

@ -419,7 +419,6 @@ void Climate::publish_state() {
// Save state
this->save_state_();
}
uint32_t Climate::hash_base() { return 3104134496UL; }
ClimateTraits Climate::get_traits() {
auto traits = this->traits();

View File

@ -282,7 +282,6 @@ class Climate : public EntityBase {
*/
void save_state_();
uint32_t hash_base() override;
void dump_traits_(const char *tag);
CallbackManager<void()> state_callback_{};

View File

@ -33,8 +33,6 @@ const char *cover_operation_to_str(CoverOperation op) {
Cover::Cover(const std::string &name) : EntityBase(name), position{COVER_OPEN} {}
uint32_t Cover::hash_base() { return 1727367479UL; }
CoverCall::CoverCall(Cover *parent) : parent_(parent) {}
CoverCall &CoverCall::set_command(const char *command) {
if (strcasecmp(command, "OPEN") == 0) {

View File

@ -177,7 +177,6 @@ class Cover : public EntityBase {
virtual std::string device_class();
optional<CoverRestoreState> restore_state_();
uint32_t hash_base() override;
CallbackManager<void()> state_callback_{};
optional<std::string> device_class_override_{};

View File

@ -131,7 +131,7 @@ void CurrentBasedCover::dump_config() {
ESP_LOGCONFIG(TAG, " Close Duration: %.1fs", this->close_duration_ / 1e3f);
ESP_LOGCONFIG(TAG, "Obstacle Rollback: %.1f%%", this->obstacle_rollback_ * 100);
if (this->max_duration_ != UINT32_MAX) {
ESP_LOGCONFIG(TAG, "Maximun duration: %.1fs", this->max_duration_ / 1e3f);
ESP_LOGCONFIG(TAG, "Maximum duration: %.1fs", this->max_duration_ / 1e3f);
}
ESP_LOGCONFIG(TAG, "Start sensing delay: %.1fs", this->start_sensing_delay_ / 1e3f);
ESP_LOGCONFIG(TAG, "Malfunction detection: %s", YESNO(this->malfunction_detection_));

View File

@ -0,0 +1,32 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c
from esphome.const import CONF_ID
AUTO_LOAD = ["output"]
CODEOWNERS = ["@NickB1"]
DEPENDENCIES = ["i2c"]
MULTI_CONF = True
dac7678_ns = cg.esphome_ns.namespace("dac7678")
DAC7678Output = dac7678_ns.class_("DAC7678Output", cg.Component, i2c.I2CDevice)
CONF_INTERNAL_REFERENCE = "internal_reference"
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(DAC7678Output),
cv.Optional(CONF_INTERNAL_REFERENCE, default=False): cv.boolean,
}
)
.extend(cv.COMPONENT_SCHEMA)
.extend(i2c.i2c_device_schema(0x48))
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
cg.add(var.set_internal_reference(config[CONF_INTERNAL_REFERENCE]))
await i2c.register_i2c_device(var, config)
return var

View File

@ -0,0 +1,83 @@
#include "dac7678_output.h"
#include "esphome/core/log.h"
#include "esphome/core/helpers.h"
#include "esphome/core/hal.h"
namespace esphome {
namespace dac7678 {
static const char *const TAG = "dac7678";
static const uint8_t DAC7678_REG_INPUT_N = 0x00;
static const uint8_t DAC7678_REG_SELECT_UPDATE_N = 0x10;
static const uint8_t DAC7678_REG_WRITE_N_UPDATE_ALL = 0x20;
static const uint8_t DAC7678_REG_WRITE_N_UPDATE_N = 0x30;
static const uint8_t DAC7678_REG_POWER = 0x40;
static const uint8_t DAC7678_REG_CLEAR_CODE = 0x50;
static const uint8_t DAC7678_REG_LDAC = 0x60;
static const uint8_t DAC7678_REG_SOFTWARE_RESET = 0x70;
static const uint8_t DAC7678_REG_INTERNAL_REF_0 = 0x80;
static const uint8_t DAC7678_REG_INTERNAL_REF_1 = 0x90;
void DAC7678Output::setup() {
ESP_LOGCONFIG(TAG, "Setting up DAC7678OutputComponent...");
ESP_LOGV(TAG, "Resetting device...");
// Reset device
if (!this->write_byte_16(DAC7678_REG_SOFTWARE_RESET, 0x0000)) {
ESP_LOGE(TAG, "Reset failed");
this->mark_failed();
return;
} else
ESP_LOGV(TAG, "Reset succeeded");
delayMicroseconds(1000);
// Set internal reference
if (this->internal_reference_) {
if (!this->write_byte_16(DAC7678_REG_INTERNAL_REF_0, 1 << 4)) {
ESP_LOGE(TAG, "Set internal reference failed");
this->mark_failed();
return;
} else
ESP_LOGV(TAG, "Internal reference enabled");
}
}
void DAC7678Output::dump_config() {
if (this->is_failed()) {
ESP_LOGE(TAG, "Setting up DAC7678 failed!");
} else
ESP_LOGCONFIG(TAG, "DAC7678 initialised");
}
void DAC7678Output::register_channel(DAC7678Channel *channel) {
auto c = channel->channel_;
this->min_channel_ = std::min(this->min_channel_, c);
this->max_channel_ = std::max(this->max_channel_, c);
channel->set_parent(this);
ESP_LOGV(TAG, "Registered channel: %01u", channel->channel_);
}
void DAC7678Output::set_channel_value_(uint8_t channel, uint16_t value) {
if (this->dac_input_reg_[channel] != value) {
ESP_LOGV(TAG, "Channel %01u: input_reg=%04u ", channel, value);
if (!this->write_byte_16(DAC7678_REG_WRITE_N_UPDATE_N | channel, value << 4)) {
this->status_set_warning();
return;
}
}
this->dac_input_reg_[channel] = value;
this->status_clear_warning();
}
void DAC7678Channel::write_state(float state) {
const float input_rounded = roundf(state * this->full_scale_);
auto input = static_cast<uint16_t>(input_rounded);
this->parent_->set_channel_value_(this->channel_, input);
}
} // namespace dac7678
} // namespace esphome

View File

@ -0,0 +1,55 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/helpers.h"
#include "esphome/components/output/float_output.h"
#include "esphome/components/i2c/i2c.h"
namespace esphome {
namespace dac7678 {
class DAC7678Output;
class DAC7678Channel : public output::FloatOutput, public Parented<DAC7678Output> {
public:
void set_channel(uint8_t channel) { channel_ = channel; }
protected:
friend class DAC7678Output;
const uint16_t full_scale_ = 0xFFF;
void write_state(float state) override;
uint8_t channel_;
};
/// DAC7678 float output component.
class DAC7678Output : public Component, public i2c::I2CDevice {
public:
DAC7678Output() {}
void register_channel(DAC7678Channel *channel);
void set_internal_reference(const bool value) { this->internal_reference_ = value; }
void setup() override;
void dump_config() override;
float get_setup_priority() const override { return setup_priority::HARDWARE; }
protected:
friend DAC7678Channel;
bool internal_reference_;
void set_channel_value_(uint8_t channel, uint16_t value);
uint8_t min_channel_{0xFF};
uint8_t max_channel_{0x00};
uint16_t dac_input_reg_[8] = {
0,
};
};
} // namespace dac7678
} // namespace esphome

View File

@ -0,0 +1,27 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import output
from esphome.const import CONF_CHANNEL, CONF_ID
from . import DAC7678Output, dac7678_ns
DEPENDENCIES = ["dac7678"]
DAC7678Channel = dac7678_ns.class_("DAC7678Channel", output.FloatOutput)
CONF_DAC7678_ID = "dac7678_id"
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
{
cv.Required(CONF_ID): cv.declare_id(DAC7678Channel),
cv.GenerateID(CONF_DAC7678_ID): cv.use_id(DAC7678Output),
cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=7),
}
)
async def to_code(config):
paren = await cg.get_variable(config[CONF_DAC7678_ID])
var = cg.new_Pvariable(config[CONF_ID])
cg.add(var.set_channel(config[CONF_CHANNEL]))
cg.add(paren.register_channel(var))
await output.register_output(var, config)
return var

View File

@ -134,7 +134,6 @@ void DallasComponent::update() {
return;
}
if (!sensor->check_scratch_pad()) {
ESP_LOGW(TAG, "'%s' - Scratch pad checksum invalid!", sensor->get_name().c_str());
sensor->publish_state(NAN);
this->status_set_warning();
return;
@ -241,13 +240,29 @@ bool DallasTemperatureSensor::setup_sensor() {
return true;
}
bool DallasTemperatureSensor::check_scratch_pad() {
bool chksum_validity = (crc8(this->scratch_pad_, 8) == this->scratch_pad_[8]);
bool config_validity = false;
switch (this->get_address8()[0]) {
case DALLAS_MODEL_DS18B20:
config_validity = ((this->scratch_pad_[4] & 0x9F) == 0x1F);
break;
default:
config_validity = ((this->scratch_pad_[4] & 0x10) == 0x10);
}
#ifdef ESPHOME_LOG_LEVEL_VERY_VERBOSE
ESP_LOGVV(TAG, "Scratch pad: %02X.%02X.%02X.%02X.%02X.%02X.%02X.%02X.%02X (%02X)", this->scratch_pad_[0],
this->scratch_pad_[1], this->scratch_pad_[2], this->scratch_pad_[3], this->scratch_pad_[4],
this->scratch_pad_[5], this->scratch_pad_[6], this->scratch_pad_[7], this->scratch_pad_[8],
crc8(this->scratch_pad_, 8));
#endif
return crc8(this->scratch_pad_, 8) == this->scratch_pad_[8];
if (!chksum_validity) {
ESP_LOGW(TAG, "'%s' - Scratch pad checksum invalid!", this->get_name().c_str());
} else if (!config_validity) {
ESP_LOGW(TAG, "'%s' - Scratch pad config register invalid!", this->get_name().c_str());
}
return chksum_validity && config_validity;
}
float DallasTemperatureSensor::get_temp_c() {
int16_t temp = (int16_t(this->scratch_pad_[1]) << 11) | (int16_t(this->scratch_pad_[0]) << 3);

View File

@ -348,7 +348,7 @@ async def dfplayer_random_to_code(config, action_id, template_arg, args):
}
),
)
async def dfplyaer_is_playing_to_code(config, condition_id, template_arg, args):
async def dfplayer_is_playing_to_code(config, condition_id, template_arg, args):
var = cg.new_Pvariable(condition_id, template_arg)
await cg.register_parented(var, config[CONF_ID])
return var

View File

@ -85,6 +85,12 @@ enum ImageType {
IMAGE_TYPE_RGB565 = 4,
};
enum DisplayType {
DISPLAY_TYPE_BINARY = 1,
DISPLAY_TYPE_GRAYSCALE = 2,
DISPLAY_TYPE_COLOR = 3,
};
enum DisplayRotation {
DISPLAY_ROTATION_0_DEGREES = 0,
DISPLAY_ROTATION_90_DEGREES = 90,
@ -361,6 +367,11 @@ class DisplayBuffer {
virtual int get_width_internal() = 0;
DisplayRotation get_rotation() const { return this->rotation_; }
/** Get the type of display that the buffer corresponds to. In case of dynamically configurable displays,
* returns the type the display is currently configured to.
*/
virtual DisplayType get_display_type() = 0;
protected:
void vprintf_(int x, int y, Font *font, Color color, TextAlign align, const char *format, va_list arg);

View File

@ -132,7 +132,7 @@ class ColorUtil {
int16_t plt_r = (int16_t) palette[i * 3 + 0];
int16_t plt_g = (int16_t) palette[i * 3 + 1];
int16_t plt_b = (int16_t) palette[i * 3 + 2];
// Calculate euclidian distance (linear distance in rgb cube).
// Calculate euclidean distance (linear distance in rgb cube).
x = (uint32_t) std::abs(tgt_r - plt_r);
y = (uint32_t) std::abs(tgt_g - plt_g);
z = (uint32_t) std::abs(tgt_b - plt_b);

View File

@ -79,7 +79,7 @@ async def to_code(config):
cg.add(var.set_request_interval(config[CONF_REQUEST_INTERVAL].total_milliseconds))
cg.add(var.set_receive_timeout(config[CONF_RECEIVE_TIMEOUT].total_milliseconds))
cg.add_define("DSMR_GAS_MBUS_ID", config[CONF_GAS_MBUS_ID])
cg.add_build_flag("-DDSMR_GAS_MBUS_ID=" + str(config[CONF_GAS_MBUS_ID]))
# DSMR Parser
cg.add_library("glmnet/Dsmr", "0.5")

View File

@ -171,7 +171,7 @@ void Dsmr::receive_telegram_() {
this->telegram_[this->bytes_read_] = c;
this->bytes_read_++;
// Check for a footer, i.e. exlamation mark, followed by a hex checksum.
// Check for a footer, i.e. exclamation mark, followed by a hex checksum.
if (c == '!') {
ESP_LOGV(TAG, "Footer of telegram found");
this->footer_found_ = true;

View File

@ -199,7 +199,7 @@ void ENS210Component::update() {
});
}
// Extracts measurement 'data' and 'status' from a 'val' obtained from measurment.
// Extracts measurement 'data' and 'status' from a 'val' obtained from measurement.
void ENS210Component::extract_measurement_(uint32_t val, int *data, int *status) {
*data = (val >> 0) & 0xffff;
int valid = (val >> 16) & 0x1;

View File

@ -521,6 +521,33 @@ ESP32_BOARD_PINS = {
},
"lolin32": {"LED": 5},
"lolin32_lite": {"LED": 22},
"lolin_c3_mini": {
"TX": 21,
"RX": 20,
"SDA": 8,
"SCL": 10,
"SS": 5,
"MOSI": 4,
"MISO": 3,
"SCK": 2,
"A0": 0,
"A1": 1,
"A2": 2,
"A3": 3,
"A4": 4,
"A5": 5,
"D0": 1,
"D1": 10,
"D2": 8,
"D3": 7,
"D4": 6,
"D5": 2,
"D6": 3,
"D7": 4,
"D8": 5,
"LED": 7,
"BUTTON": 9,
},
"lolin_d32": {"LED": 5, "_VBAT": 35},
"lolin_d32_pro": {"LED": 5, "_VBAT": 35},
"lopy": {
@ -1026,6 +1053,7 @@ BOARD_TO_VARIANT = {
"labplus_mpython": VARIANT_ESP32,
"lolin32_lite": VARIANT_ESP32,
"lolin32": VARIANT_ESP32,
"lolin_c3_mini": VARIANT_ESP32C3,
"lolin_d32_pro": VARIANT_ESP32,
"lolin_d32": VARIANT_ESP32,
"lopy4": VARIANT_ESP32,

View File

@ -36,6 +36,7 @@ class ESP32PreferenceBackend : public ESPPreferenceBackend {
save.key = key;
save.data.assign(data, data + len);
s_pending_save.emplace_back(save);
ESP_LOGVV(TAG, "s_pending_save: key: %s, len: %d", key.c_str(), len);
return true;
}
bool load(uint8_t *data, size_t len) override {
@ -65,6 +66,8 @@ class ESP32PreferenceBackend : public ESPPreferenceBackend {
if (err != 0) {
ESP_LOGV(TAG, "nvs_get_blob('%s') failed: %s", key.c_str(), esp_err_to_name(err));
return false;
} else {
ESP_LOGVV(TAG, "nvs_get_blob: key: %s, len: %d", key.c_str(), len);
}
return true;
}
@ -73,7 +76,6 @@ class ESP32PreferenceBackend : public ESPPreferenceBackend {
class ESP32Preferences : public ESPPreferences {
public:
uint32_t nvs_handle;
uint32_t current_offset = 0;
void open() {
nvs_flash_init();
@ -97,12 +99,9 @@ class ESP32Preferences : public ESPPreferences {
ESPPreferenceObject make_preference(size_t length, uint32_t type) override {
auto *pref = new ESP32PreferenceBackend(); // NOLINT(cppcoreguidelines-owning-memory)
pref->nvs_handle = nvs_handle;
current_offset += length;
uint32_t keyval = current_offset ^ type;
char keybuf[16];
snprintf(keybuf, sizeof(keybuf), "%d", keyval);
pref->key = keybuf; // copied to std::string
uint32_t keyval = type;
pref->key = str_sprintf("%u", keyval);
return ESPPreferenceObject(pref);
}
@ -121,6 +120,7 @@ class ESP32Preferences : public ESPPreferences {
ESP_LOGVV(TAG, "Checking if NVS data %s has changed", save.key.c_str());
if (is_changed(nvs_handle, save)) {
esp_err_t err = nvs_set_blob(nvs_handle, save.key.c_str(), save.data.data(), save.data.size());
ESP_LOGV(TAG, "sync: key: %s, len: %d", save.key.c_str(), save.data.size());
if (err != 0) {
ESP_LOGV(TAG, "nvs_set_blob('%s', len=%u) failed: %s", save.key.c_str(), save.data.size(),
esp_err_to_name(err));

View File

@ -317,12 +317,11 @@ void ESP32Camera::update_camera_parameters() {
s->set_gainceiling(s, (gainceiling_t) this->agc_gain_ceiling_);
/* update white balance mode */
s->set_wb_mode(s, (int) this->wb_mode_); // 0 to 4
/* update test patern */
/* update test pattern */
s->set_colorbar(s, this->test_pattern_);
}
/* ---------------- Internal methods ---------------- */
uint32_t ESP32Camera::hash_base() { return 3010542557UL; }
bool ESP32Camera::has_requested_image_() const { return this->single_requesters_ || this->stream_requesters_; }
bool ESP32Camera::can_return_image_() const { return this->current_image_.use_count() == 1; }
void ESP32Camera::framebuffer_task(void *pv) {

View File

@ -151,7 +151,6 @@ class ESP32Camera : public Component, public EntityBase {
protected:
/* internal methods */
uint32_t hash_base() override;
bool has_requested_image_() const;
bool can_return_image_() const;

View File

@ -152,7 +152,6 @@ void Fan::dump_traits_(const char *tag, const char *prefix) {
if (this->get_traits().supports_direction())
ESP_LOGCONFIG(tag, "%s Direction: YES", prefix);
}
uint32_t Fan::hash_base() { return 418001110UL; }
} // namespace fan
} // namespace esphome

View File

@ -136,7 +136,6 @@ class Fan : public EntityBase {
void save_state_();
void dump_traits_(const char *tag, const char *prefix);
uint32_t hash_base() override;
CallbackManager<void()> state_callback_{};
ESPPreferenceObject rtc_;

View File

@ -44,7 +44,14 @@ template<typename T> class RestoringGlobalsComponent : public Component {
float get_setup_priority() const override { return setup_priority::HARDWARE; }
void loop() override {
void loop() override { store_value_(); }
void on_shutdown() override { store_value_(); }
void set_name_hash(uint32_t name_hash) { this->name_hash_ = name_hash; }
protected:
void store_value_() {
int diff = memcmp(&this->value_, &this->prev_value_, sizeof(T));
if (diff != 0) {
this->rtc_.save(&this->value_);
@ -52,9 +59,6 @@ template<typename T> class RestoringGlobalsComponent : public Component {
}
}
void set_name_hash(uint32_t name_hash) { this->name_hash_ = name_hash; }
protected:
T value_{};
T prev_value_{};
uint32_t name_hash_{};

View File

@ -118,7 +118,7 @@ def _relocate_fields_to_subfolder(config, subfolder, subschema):
fields = [k.schema for k in subschema.schema.keys()]
fields.remove(CONF_ID)
if subfolder in config:
# Ensure no ambigious fields in base of config
# Ensure no ambiguous fields in base of config
for f in fields:
if f in config:
raise cv.Invalid(

View File

@ -195,7 +195,7 @@ void HydreonRGxxComponent::process_line_() {
if (n == std::string::npos) {
continue;
}
int data = strtol(this->buffer_.substr(n + strlen(PROTOCOL_NAMES[i])).c_str(), nullptr, 10);
float data = strtof(this->buffer_.substr(n + strlen(PROTOCOL_NAMES[i])).c_str(), nullptr);
this->sensors_[i]->publish_state(data);
ESP_LOGD(TAG, "Received %s: %f", PROTOCOL_NAMES[i], this->sensors_[i]->get_raw_state());
this->sensors_received_ |= (1 << i);

View File

@ -37,7 +37,7 @@ SUPPORTED_SENSORS = {
PROTOCOL_NAMES = {
CONF_MOISTURE: "R",
CONF_ACC: "Acc",
CONF_R_INT: "Rint",
CONF_R_INT: "RInt",
CONF_EVENT_ACC: "EventAcc",
CONF_TOTAL_ACC: "TotalAcc",
}

View File

@ -224,7 +224,7 @@ void ArduinoI2CBus::recover_() {
digitalWrite(sda_pin_, LOW); // NOLINT
// By now, any stuck device ought to have sent all remaining bits of its
// transation, meaning that it should have freed up the SDA line, resulting
// transaction, meaning that it should have freed up the SDA line, resulting
// in SDA being pulled up.
if (digitalRead(sda_pin_) == LOW) { // NOLINT
ESP_LOGE(TAG, "Recovery failed: SDA is held LOW after clock pulse cycle");

View File

@ -285,7 +285,7 @@ void IDFI2CBus::recover_() {
}
// By now, any stuck device ought to have sent all remaining bits of its
// transation, meaning that it should have freed up the SDA line, resulting
// transaction, meaning that it should have freed up the SDA line, resulting
// in SDA being pulled up.
if (gpio_get_level(sda_pin) == 0) {
ESP_LOGE(TAG, "Recovery failed: SDA is held LOW after clock pulse cycle");

View File

View File

@ -0,0 +1,160 @@
#include "i2s_audio_media_player.h"
#ifdef USE_ESP32_FRAMEWORK_ARDUINO
#include "esphome/core/log.h"
namespace esphome {
namespace i2s_audio {
static const char *const TAG = "audio";
void I2SAudioMediaPlayer::control(const media_player::MediaPlayerCall &call) {
if (call.get_media_url().has_value()) {
if (this->audio_->isRunning())
this->audio_->stopSong();
this->high_freq_.start();
this->audio_->connecttohost(call.get_media_url().value().c_str());
this->state = media_player::MEDIA_PLAYER_STATE_PLAYING;
}
if (call.get_volume().has_value()) {
this->volume = call.get_volume().value();
this->set_volume_(volume);
this->unmute_();
}
if (call.get_command().has_value()) {
switch (call.get_command().value()) {
case media_player::MEDIA_PLAYER_COMMAND_PLAY:
if (!this->audio_->isRunning())
this->audio_->pauseResume();
this->state = media_player::MEDIA_PLAYER_STATE_PLAYING;
break;
case media_player::MEDIA_PLAYER_COMMAND_PAUSE:
if (this->audio_->isRunning())
this->audio_->pauseResume();
this->state = media_player::MEDIA_PLAYER_STATE_PAUSED;
break;
case media_player::MEDIA_PLAYER_COMMAND_STOP:
this->stop_();
break;
case media_player::MEDIA_PLAYER_COMMAND_MUTE:
this->mute_();
break;
case media_player::MEDIA_PLAYER_COMMAND_UNMUTE:
this->unmute_();
break;
case media_player::MEDIA_PLAYER_COMMAND_TOGGLE:
this->audio_->pauseResume();
if (this->audio_->isRunning()) {
this->state = media_player::MEDIA_PLAYER_STATE_PLAYING;
} else {
this->state = media_player::MEDIA_PLAYER_STATE_PAUSED;
}
break;
case media_player::MEDIA_PLAYER_COMMAND_VOLUME_UP: {
float new_volume = this->volume + 0.1f;
if (new_volume > 1.0f)
new_volume = 1.0f;
this->set_volume_(new_volume);
this->unmute_();
break;
}
case media_player::MEDIA_PLAYER_COMMAND_VOLUME_DOWN: {
float new_volume = this->volume - 0.1f;
if (new_volume < 0.0f)
new_volume = 0.0f;
this->set_volume_(new_volume);
this->unmute_();
break;
}
}
}
this->publish_state();
}
void I2SAudioMediaPlayer::mute_() {
if (this->mute_pin_ != nullptr) {
this->mute_pin_->digital_write(true);
} else {
this->set_volume_(0.0f, false);
}
this->muted_ = true;
}
void I2SAudioMediaPlayer::unmute_() {
if (this->mute_pin_ != nullptr) {
this->mute_pin_->digital_write(false);
} else {
this->set_volume_(this->volume, false);
}
this->muted_ = false;
}
void I2SAudioMediaPlayer::set_volume_(float volume, bool publish) {
this->audio_->setVolume(remap<uint8_t, float>(volume, 0.0f, 1.0f, 0, 21));
if (publish)
this->volume = volume;
}
void I2SAudioMediaPlayer::stop_() {
if (this->audio_->isRunning())
this->audio_->stopSong();
this->high_freq_.stop();
this->state = media_player::MEDIA_PLAYER_STATE_IDLE;
}
void I2SAudioMediaPlayer::setup() {
ESP_LOGCONFIG(TAG, "Setting up Audio...");
if (this->internal_dac_mode_ != I2S_DAC_CHANNEL_DISABLE) {
this->audio_ = make_unique<Audio>(true, this->internal_dac_mode_);
} else {
this->audio_ = make_unique<Audio>(false);
this->audio_->setPinout(this->bclk_pin_, this->lrclk_pin_, this->dout_pin_);
this->audio_->forceMono(this->external_dac_channels_ == 1);
if (this->mute_pin_ != nullptr) {
this->mute_pin_->setup();
this->mute_pin_->digital_write(false);
}
}
this->state = media_player::MEDIA_PLAYER_STATE_IDLE;
}
void I2SAudioMediaPlayer::loop() {
this->audio_->loop();
if (this->state == media_player::MEDIA_PLAYER_STATE_PLAYING && !this->audio_->isRunning()) {
this->stop_();
this->publish_state();
}
}
media_player::MediaPlayerTraits I2SAudioMediaPlayer::get_traits() {
auto traits = media_player::MediaPlayerTraits();
traits.set_supports_pause(true);
return traits;
};
void I2SAudioMediaPlayer::dump_config() {
ESP_LOGCONFIG(TAG, "Audio:");
if (this->is_failed()) {
ESP_LOGCONFIG(TAG, "Audio failed to initialize!");
return;
}
if (this->internal_dac_mode_ != I2S_DAC_CHANNEL_DISABLE) {
switch (this->internal_dac_mode_) {
case I2S_DAC_CHANNEL_LEFT_EN:
ESP_LOGCONFIG(TAG, " Internal DAC mode: Left");
break;
case I2S_DAC_CHANNEL_RIGHT_EN:
ESP_LOGCONFIG(TAG, " Internal DAC mode: Right");
break;
case I2S_DAC_CHANNEL_BOTH_EN:
ESP_LOGCONFIG(TAG, " Internal DAC mode: Left & Right");
break;
default:
break;
}
}
}
} // namespace i2s_audio
} // namespace esphome
#endif // USE_ESP32_FRAMEWORK_ARDUINO

View File

@ -0,0 +1,63 @@
#pragma once
#ifdef USE_ESP32_FRAMEWORK_ARDUINO
#include "esphome/components/media_player/media_player.h"
#include "esphome/core/component.h"
#include "esphome/core/gpio.h"
#include "esphome/core/helpers.h"
#include <Audio.h>
namespace esphome {
namespace i2s_audio {
class I2SAudioMediaPlayer : public Component, public media_player::MediaPlayer {
public:
void setup() override;
float get_setup_priority() const override { return esphome::setup_priority::LATE; }
void loop() override;
void dump_config() override;
void set_dout_pin(uint8_t pin) { this->dout_pin_ = pin; }
void set_bclk_pin(uint8_t pin) { this->bclk_pin_ = pin; }
void set_lrclk_pin(uint8_t pin) { this->lrclk_pin_ = pin; }
void set_mute_pin(GPIOPin *mute_pin) { this->mute_pin_ = mute_pin; }
void set_internal_dac_mode(i2s_dac_mode_t mode) { this->internal_dac_mode_ = mode; }
void set_external_dac_channels(uint8_t channels) { this->external_dac_channels_ = channels; }
media_player::MediaPlayerTraits get_traits() override;
bool is_muted() const override { return this->muted_; }
protected:
void control(const media_player::MediaPlayerCall &call) override;
void mute_();
void unmute_();
void set_volume_(float volume, bool publish = true);
void stop_();
std::unique_ptr<Audio> audio_;
uint8_t dout_pin_{0};
uint8_t din_pin_{0};
uint8_t bclk_pin_;
uint8_t lrclk_pin_;
GPIOPin *mute_pin_{nullptr};
bool muted_{false};
float unmuted_volume_{0};
i2s_dac_mode_t internal_dac_mode_{I2S_DAC_CHANNEL_DISABLE};
uint8_t external_dac_channels_;
HighFrequencyLoopRequester high_freq_;
};
} // namespace i2s_audio
} // namespace esphome
#endif // USE_ESP32_FRAMEWORK_ARDUINO

View File

@ -0,0 +1,94 @@
import esphome.codegen as cg
from esphome.components import media_player
import esphome.config_validation as cv
from esphome import pins
from esphome.const import CONF_ID, CONF_MODE
from esphome.core import CORE
CODEOWNERS = ["@jesserockz"]
DEPENDENCIES = ["esp32"]
i2s_audio_ns = cg.esphome_ns.namespace("i2s_audio")
I2SAudioMediaPlayer = i2s_audio_ns.class_(
"I2SAudioMediaPlayer", cg.Component, media_player.MediaPlayer
)
i2s_dac_mode_t = cg.global_ns.enum("i2s_dac_mode_t")
CONF_I2S_DOUT_PIN = "i2s_dout_pin"
CONF_I2S_BCLK_PIN = "i2s_bclk_pin"
CONF_I2S_LRCLK_PIN = "i2s_lrclk_pin"
CONF_MUTE_PIN = "mute_pin"
CONF_AUDIO_ID = "audio_id"
CONF_DAC_TYPE = "dac_type"
INTERNAL_DAC_OPTIONS = {
"left": i2s_dac_mode_t.I2S_DAC_CHANNEL_LEFT_EN,
"right": i2s_dac_mode_t.I2S_DAC_CHANNEL_RIGHT_EN,
"stereo": i2s_dac_mode_t.I2S_DAC_CHANNEL_BOTH_EN,
}
EXTERNAL_DAC_OPTIONS = ["mono", "stereo"]
CONFIG_SCHEMA = cv.All(
cv.typed_schema(
{
"internal": cv.Schema(
{
cv.GenerateID(): cv.declare_id(I2SAudioMediaPlayer),
cv.Required(CONF_MODE): cv.enum(INTERNAL_DAC_OPTIONS, lower=True),
}
)
.extend(media_player.MEDIA_PLAYER_SCHEMA)
.extend(cv.COMPONENT_SCHEMA),
"external": cv.Schema(
{
cv.GenerateID(): cv.declare_id(I2SAudioMediaPlayer),
cv.Required(
CONF_I2S_DOUT_PIN
): pins.internal_gpio_output_pin_number,
cv.Required(
CONF_I2S_BCLK_PIN
): pins.internal_gpio_output_pin_number,
cv.Required(
CONF_I2S_LRCLK_PIN
): pins.internal_gpio_output_pin_number,
cv.Optional(CONF_MUTE_PIN): pins.gpio_output_pin_schema,
cv.Optional(CONF_MODE, default="mono"): cv.one_of(
*EXTERNAL_DAC_OPTIONS, lower=True
),
}
)
.extend(media_player.MEDIA_PLAYER_SCHEMA)
.extend(cv.COMPONENT_SCHEMA),
},
key=CONF_DAC_TYPE,
),
cv.only_with_arduino,
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await media_player.register_media_player(var, config)
if config[CONF_DAC_TYPE] == "internal":
cg.add(var.set_internal_dac_mode(config[CONF_MODE]))
else:
cg.add(var.set_dout_pin(config[CONF_I2S_DOUT_PIN]))
cg.add(var.set_bclk_pin(config[CONF_I2S_BCLK_PIN]))
cg.add(var.set_lrclk_pin(config[CONF_I2S_LRCLK_PIN]))
if CONF_MUTE_PIN in config:
pin = await cg.gpio_pin_expression(config[CONF_MUTE_PIN])
cg.add(var.set_mute_pin(pin))
cg.add(var.set_external_dac_channels(2 if config[CONF_MODE] == "stereo" else 1))
if CORE.is_esp32:
cg.add_library("WiFiClientSecure", None)
cg.add_library("HTTPClient", None)
cg.add_library("esphome/ESP32-audioI2S", "2.1.0")
cg.add_build_flag("-DAUDIO_NO_SD_FS")

View File

@ -24,6 +24,7 @@ ili9341 = ili9341_ns.class_(
)
ILI9341M5Stack = ili9341_ns.class_("ILI9341M5Stack", ili9341)
ILI9341TFT24 = ili9341_ns.class_("ILI9341TFT24", ili9341)
ILI9341TFT24R = ili9341_ns.class_("ILI9341TFT24R", ili9341)
ILI9341Model = ili9341_ns.enum("ILI9341Model")
ILI9341ColorMode = ili9341_ns.enum("ILI9341ColorMode")
@ -31,6 +32,7 @@ ILI9341ColorMode = ili9341_ns.enum("ILI9341ColorMode")
MODELS = {
"M5STACK": ILI9341Model.M5STACK,
"TFT_2.4": ILI9341Model.TFT_24,
"TFT_2.4R": ILI9341Model.TFT_24R,
}
ILI9341_MODEL = cv.enum(MODELS, upper=True, space="_")
@ -60,6 +62,8 @@ async def to_code(config):
lcd_type = ILI9341M5Stack
if config[CONF_MODEL] == "TFT_2.4":
lcd_type = ILI9341TFT24
if config[CONF_MODEL] == "TFT_2.4R":
lcd_type = ILI9341TFT24R
rhs = lcd_type.new()
var = cg.Pvariable(config[CONF_ID], rhs)

View File

@ -263,5 +263,13 @@ void ILI9341TFT24::initialize() {
this->fill_internal_(Color::BLACK);
}
// 24_TFT rotated display
void ILI9341TFT24R::initialize() {
this->init_lcd_(INITCMD_TFT);
this->width_ = 320;
this->height_ = 240;
this->fill_internal_(Color::BLACK);
}
} // namespace ili9341
} // namespace esphome

View File

@ -12,6 +12,7 @@ namespace ili9341 {
enum ILI9341Model {
M5STACK = 0,
TFT_24,
TFT_24R,
};
enum ILI9341ColorMode {
@ -48,6 +49,8 @@ class ILI9341Display : public PollingComponent,
this->initialize();
}
display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_COLOR; }
protected:
void draw_absolute_pixel_internal(int x, int y, Color color) override;
void setup_pins_();
@ -100,5 +103,12 @@ class ILI9341TFT24 : public ILI9341Display {
public:
void initialize() override;
};
//----------- ILI9341_24_TFT rotated display --------------
class ILI9341TFT24R : public ILI9341Display {
public:
void initialize() override;
};
} // namespace ili9341
} // namespace esphome

View File

@ -86,6 +86,10 @@ class Inkplate6 : public PollingComponent, public display::DisplayBuffer, public
void block_partial() { this->block_partial_ = true; }
display::DisplayType get_display_type() override {
return get_greyscale() ? display::DisplayType::DISPLAY_TYPE_GRAYSCALE : display::DisplayType::DISPLAY_TYPE_BINARY;
}
protected:
void draw_absolute_pixel_internal(int x, int y, Color color) override;
void display1b_();

View File

@ -10,14 +10,13 @@ static const char *const TAG = "integration";
void IntegrationSensor::setup() {
if (this->restore_) {
this->rtc_ = global_preferences->make_preference<float>(this->get_object_id_hash());
this->pref_ = global_preferences->make_preference<float>(this->get_object_id_hash());
float preference_value = 0;
this->rtc_.load(&preference_value);
this->pref_.load(&preference_value);
this->result_ = preference_value;
}
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

@ -28,7 +28,6 @@ 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; }
@ -56,22 +55,18 @@ class IntegrationSensor : public sensor::Sensor, public Component {
void publish_and_save_(double result) {
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);
if (this->restore_) {
float result_f = result;
this->pref_.save(&result_f);
}
}
sensor::Sensor *sensor_;
IntegrationSensorTime time_;
IntegrationMethod method_;
bool restore_;
ESPPreferenceObject rtc_;
ESPPreferenceObject pref_;
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

@ -35,7 +35,6 @@ INTEGRATION_METHODS = {
CONF_TIME_UNIT = "time_unit"
CONF_INTEGRATION_METHOD = "integration_method"
CONF_MIN_SAVE_INTERVAL = "min_save_interval"
def inherit_unit_of_measurement(uom, config):
@ -58,9 +57,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,
cv.Optional("min_save_interval"): cv.invalid(
"min_save_interval was removed in 2022.8.0. Please use the `preferences` -> `flash_write_interval` to adjust."
),
}
).extend(cv.COMPONENT_SCHEMA)
@ -97,7 +96,6 @@ 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

@ -1,4 +1,4 @@
from esphome.jsonschema import jschema_extractor
from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
@ -479,11 +479,11 @@ async def addressable_flicker_effect_to_code(config, effect_id):
def validate_effects(allowed_effects):
@jschema_extractor("effects")
@schema_extractor("effects")
def validator(value):
# pylint: disable=comparison-with-callable
if value == jschema_extractor:
if value == SCHEMA_EXTRACT:
return (allowed_effects, EFFECTS_REGISTRY)
value = cv.validate_registry("effect", EFFECTS_REGISTRY)(value)
errors = []
names = set()

View File

@ -203,7 +203,7 @@ class LightColorValues {
*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.
} else { // Probably won't get here but put this here anyway.
*white_brightness = 0;
}
}

View File

@ -121,7 +121,7 @@ void LightJSONSchema::parse_color_json(LightState &state, LightCall &call, JsonO
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
// the HA scheme is ambiguous 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);

View File

@ -145,7 +145,6 @@ void LightState::loop() {
}
float LightState::get_setup_priority() const { return setup_priority::HARDWARE - 1.0f; }
uint32_t LightState::hash_base() { return 1114400283; }
void LightState::publish_state() { this->remote_values_callback_.call(); }

View File

@ -150,8 +150,6 @@ class LightState : public EntityBase, public Component {
friend LightCall;
friend class AddressableLight;
uint32_t hash_base() override;
/// Internal method to start an effect with the given index
void start_effect_(uint32_t effect_index);
/// Internal method to get the currently active effect

View File

@ -57,7 +57,6 @@ void Lock::publish_state(LockState state) {
}
void Lock::add_on_state_callback(std::function<void()> &&callback) { this->state_callback_.add(std::move(callback)); }
uint32_t Lock::hash_base() { return 856245656UL; }
void LockCall::perform() {
ESP_LOGD(TAG, "'%s' - Setting", this->parent_->get_name().c_str());

View File

@ -167,8 +167,6 @@ class Lock : public EntityBase {
*/
virtual void control(const LockCall &call) = 0;
uint32_t hash_base() override;
CallbackManager<void()> state_callback_{};
Deduplicator<LockState> publish_dedup_;
ESPPreferenceObject rtc_;

View File

@ -178,7 +178,8 @@ void Logger::pre_setup() {
Serial1.setDebugOutput(ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE);
#endif
break;
#if defined(USE_ESP32) && !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32S2)
#if defined(USE_ESP32) && !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32S2) && \
!defined(USE_ESP32_VARIANT_ESP32S3)
case UART_SELECTION_UART2:
this->hw_serial_ = &Serial2;
Serial2.begin(this->baud_rate_);

View File

@ -94,6 +94,14 @@ void MAX31865Sensor::read_data_() {
const uint16_t rtd_resistance_register = this->read_register_16_(RTD_RESISTANCE_MSB_REG);
this->write_config_(0b11000000, 0b00000000);
// Check for bad connection
if (rtd_resistance_register == 0b0000000000000000 || rtd_resistance_register == 0b1111111111111111) {
ESP_LOGE(TAG, "SPI bus read all 0 or all 1 (0x%04X), check MAX31865 wiring & power.", rtd_resistance_register);
this->publish_state(NAN);
this->status_set_error();
return;
}
// Check faults
const uint8_t faults = this->read_register_(FAULT_STATUS_REG);
if ((has_fault_ = faults & 0b00111100)) {

View File

@ -11,6 +11,9 @@ from esphome.const import (
UNIT_CELSIUS,
)
CODEOWNERS = ["@DAVe3283"]
DEPENDENCIES = ["spi"]
max31865_ns = cg.esphome_ns.namespace("max31865")
MAX31865Sensor = max31865_ns.class_(
"MAX31865Sensor", sensor.Sensor, cg.PollingComponent, spi.SPIDevice

View File

@ -93,6 +93,8 @@ class MAX7219Component : public PollingComponent,
uint8_t strftimedigit(const char *format, time::ESPTime time) __attribute__((format(strftime, 2, 0)));
#endif
display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_BINARY; }
protected:
void send_byte_(uint8_t a_register, uint8_t data);
void send_to_all_(uint8_t a_register, uint8_t data);

View File

@ -0,0 +1,179 @@
from esphome import automation
import esphome.config_validation as cv
import esphome.codegen as cg
from esphome.automation import maybe_simple_id
from esphome.const import CONF_ID, CONF_ON_STATE, CONF_TRIGGER_ID
from esphome.core import CORE
from esphome.coroutine import coroutine_with_priority
from esphome.cpp_helpers import setup_entity
CODEOWNERS = ["@jesserockz"]
IS_PLATFORM_COMPONENT = True
media_player_ns = cg.esphome_ns.namespace("media_player")
MediaPlayer = media_player_ns.class_("MediaPlayer")
PlayAction = media_player_ns.class_(
"PlayAction", automation.Action, cg.Parented.template(MediaPlayer)
)
PlayMediaAction = media_player_ns.class_(
"PlayMediaAction", automation.Action, cg.Parented.template(MediaPlayer)
)
ToggleAction = media_player_ns.class_(
"ToggleAction", automation.Action, cg.Parented.template(MediaPlayer)
)
PauseAction = media_player_ns.class_(
"PauseAction", automation.Action, cg.Parented.template(MediaPlayer)
)
StopAction = media_player_ns.class_(
"StopAction", automation.Action, cg.Parented.template(MediaPlayer)
)
VolumeUpAction = media_player_ns.class_(
"VolumeUpAction", automation.Action, cg.Parented.template(MediaPlayer)
)
VolumeDownAction = media_player_ns.class_(
"VolumeDownAction", automation.Action, cg.Parented.template(MediaPlayer)
)
VolumeSetAction = media_player_ns.class_(
"VolumeSetAction", automation.Action, cg.Parented.template(MediaPlayer)
)
CONF_VOLUME = "volume"
CONF_ON_IDLE = "on_idle"
CONF_ON_PLAY = "on_play"
CONF_ON_PAUSE = "on_pause"
CONF_MEDIA_URL = "media_url"
StateTrigger = media_player_ns.class_("StateTrigger", automation.Trigger.template())
IdleTrigger = media_player_ns.class_("IdleTrigger", automation.Trigger.template())
PlayTrigger = media_player_ns.class_("PlayTrigger", automation.Trigger.template())
PauseTrigger = media_player_ns.class_("PauseTrigger", automation.Trigger.template())
IsIdleCondition = media_player_ns.class_("IsIdleCondition", automation.Condition)
IsPlayingCondition = media_player_ns.class_("IsPlayingCondition", automation.Condition)
async def setup_media_player_core_(var, config):
await setup_entity(var, config)
for conf in config.get(CONF_ON_STATE, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf)
for conf in config.get(CONF_ON_IDLE, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf)
for conf in config.get(CONF_ON_PLAY, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf)
for conf in config.get(CONF_ON_PAUSE, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf)
async def register_media_player(var, config):
if not CORE.has_id(config[CONF_ID]):
var = cg.Pvariable(config[CONF_ID], var)
cg.add(cg.App.register_media_player(var))
await setup_media_player_core_(var, config)
MEDIA_PLAYER_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(
{
cv.Optional(CONF_ON_STATE): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateTrigger),
}
),
cv.Optional(CONF_ON_IDLE): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(IdleTrigger),
}
),
cv.Optional(CONF_ON_PLAY): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PlayTrigger),
}
),
cv.Optional(CONF_ON_PAUSE): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PauseTrigger),
}
),
}
)
MEDIA_PLAYER_ACTION_SCHEMA = maybe_simple_id({cv.GenerateID(): cv.use_id(MediaPlayer)})
@automation.register_action(
"media_player.play_media",
PlayMediaAction,
cv.maybe_simple_value(
{
cv.GenerateID(): cv.use_id(MediaPlayer),
cv.Required(CONF_MEDIA_URL): cv.templatable(cv.url),
},
key=CONF_MEDIA_URL,
),
)
async def media_player_play_media_action(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID])
media_url = await cg.templatable(config[CONF_MEDIA_URL], args, cg.std_string)
cg.add(var.set_media_url(media_url))
return var
@automation.register_action("media_player.play", PlayAction, MEDIA_PLAYER_ACTION_SCHEMA)
@automation.register_action(
"media_player.toggle", ToggleAction, MEDIA_PLAYER_ACTION_SCHEMA
)
@automation.register_action(
"media_player.pause", PauseAction, MEDIA_PLAYER_ACTION_SCHEMA
)
@automation.register_action("media_player.stop", StopAction, MEDIA_PLAYER_ACTION_SCHEMA)
@automation.register_action(
"media_player.volume_up", VolumeUpAction, MEDIA_PLAYER_ACTION_SCHEMA
)
@automation.register_action(
"media_player.volume_down", VolumeDownAction, MEDIA_PLAYER_ACTION_SCHEMA
)
@automation.register_condition(
"media_player.is_idle", IsIdleCondition, MEDIA_PLAYER_ACTION_SCHEMA
)
@automation.register_condition(
"media_player.is_playing", IsPlayingCondition, MEDIA_PLAYER_ACTION_SCHEMA
)
async def media_player_action(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID])
return var
@automation.register_action(
"media_player.volume_set",
VolumeSetAction,
cv.maybe_simple_value(
{
cv.GenerateID(): cv.use_id(MediaPlayer),
cv.Required(CONF_VOLUME): cv.templatable(cv.percentage),
},
key=CONF_VOLUME,
),
)
async def media_player_volume_set_action(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID])
volume = await cg.templatable(config[CONF_VOLUME], args, float)
cg.add(var.set_volume(volume))
return var
@coroutine_with_priority(100.0)
async def to_code(config):
cg.add_global(media_player_ns.using)
cg.add_define("USE_MEDIA_PLAYER")

View File

@ -0,0 +1,67 @@
#pragma once
#include "esphome/core/automation.h"
#include "media_player.h"
namespace esphome {
namespace media_player {
#define MEDIA_PLAYER_SIMPLE_COMMAND_ACTION(ACTION_CLASS, ACTION_COMMAND) \
template<typename... Ts> class ACTION_CLASS : public Action<Ts...>, public Parented<MediaPlayer> { \
void play(Ts... x) override { \
this->parent_->make_call().set_command(MediaPlayerCommand::MEDIA_PLAYER_COMMAND_##ACTION_COMMAND).perform(); \
} \
};
#define MEDIA_PLAYER_SIMPLE_STATE_TRIGGER(TRIGGER_CLASS, TRIGGER_STATE) \
class TRIGGER_CLASS : public Trigger<> { \
public: \
explicit TRIGGER_CLASS(MediaPlayer *player) { \
player->add_on_state_callback([this, player]() { \
if (player->state == MediaPlayerState::MEDIA_PLAYER_STATE_##TRIGGER_STATE) \
this->trigger(); \
}); \
} \
};
MEDIA_PLAYER_SIMPLE_COMMAND_ACTION(PlayAction, PLAY)
MEDIA_PLAYER_SIMPLE_COMMAND_ACTION(PauseAction, PAUSE)
MEDIA_PLAYER_SIMPLE_COMMAND_ACTION(StopAction, STOP)
MEDIA_PLAYER_SIMPLE_COMMAND_ACTION(ToggleAction, TOGGLE)
MEDIA_PLAYER_SIMPLE_COMMAND_ACTION(VolumeUpAction, VOLUME_UP)
MEDIA_PLAYER_SIMPLE_COMMAND_ACTION(VolumeDownAction, VOLUME_DOWN)
template<typename... Ts> class PlayMediaAction : public Action<Ts...>, public Parented<MediaPlayer> {
TEMPLATABLE_VALUE(std::string, media_url)
void play(Ts... x) override { this->parent_->make_call().set_media_url(this->media_url_.value(x...)).perform(); }
};
template<typename... Ts> class VolumeSetAction : public Action<Ts...>, public Parented<MediaPlayer> {
TEMPLATABLE_VALUE(float, volume)
void play(Ts... x) override { this->parent_->make_call().set_volume(this->volume_.value(x...)).perform(); }
};
class StateTrigger : public Trigger<> {
public:
explicit StateTrigger(MediaPlayer *player) {
player->add_on_state_callback([this]() { this->trigger(); });
}
};
MEDIA_PLAYER_SIMPLE_STATE_TRIGGER(IdleTrigger, IDLE)
MEDIA_PLAYER_SIMPLE_STATE_TRIGGER(PlayTrigger, PLAYING)
MEDIA_PLAYER_SIMPLE_STATE_TRIGGER(PauseTrigger, PAUSED)
template<typename... Ts> class IsIdleCondition : public Condition<Ts...>, public Parented<MediaPlayer> {
public:
bool check(Ts... x) override { return this->parent_->state == MediaPlayerState::MEDIA_PLAYER_STATE_IDLE; }
};
template<typename... Ts> class IsPlayingCondition : public Condition<Ts...>, public Parented<MediaPlayer> {
public:
bool check(Ts... x) override { return this->parent_->state == MediaPlayerState::MEDIA_PLAYER_STATE_PLAYING; }
};
} // namespace media_player
} // namespace esphome

View File

@ -0,0 +1,118 @@
#include "media_player.h"
#include "esphome/core/log.h"
namespace esphome {
namespace media_player {
static const char *const TAG = "media_player";
const char *media_player_state_to_string(MediaPlayerState state) {
switch (state) {
case MEDIA_PLAYER_STATE_IDLE:
return "IDLE";
case MEDIA_PLAYER_STATE_PLAYING:
return "PLAYING";
case MEDIA_PLAYER_STATE_PAUSED:
return "PAUSED";
case MEDIA_PLAYER_STATE_NONE:
default:
return "UNKNOWN";
}
}
const char *media_player_command_to_string(MediaPlayerCommand command) {
switch (command) {
case MEDIA_PLAYER_COMMAND_PLAY:
return "PLAY";
case MEDIA_PLAYER_COMMAND_PAUSE:
return "PAUSE";
case MEDIA_PLAYER_COMMAND_STOP:
return "STOP";
case MEDIA_PLAYER_COMMAND_MUTE:
return "MUTE";
case MEDIA_PLAYER_COMMAND_UNMUTE:
return "UNMUTE";
case MEDIA_PLAYER_COMMAND_TOGGLE:
return "TOGGLE";
default:
return "UNKNOWN";
}
}
void MediaPlayerCall::validate_() {
if (this->media_url_.has_value()) {
if (this->command_.has_value()) {
ESP_LOGW(TAG, "MediaPlayerCall: Setting both command and media_url is not needed.");
this->command_.reset();
}
}
if (this->volume_.has_value()) {
if (this->volume_.value() < 0.0f || this->volume_.value() > 1.0f) {
ESP_LOGW(TAG, "MediaPlayerCall: Volume must be between 0.0 and 1.0.");
this->volume_.reset();
}
}
}
void MediaPlayerCall::perform() {
ESP_LOGD(TAG, "'%s' - Setting", this->parent_->get_name().c_str());
this->validate_();
if (this->command_.has_value()) {
const char *command_s = media_player_command_to_string(this->command_.value());
ESP_LOGD(TAG, " Command: %s", command_s);
}
if (this->media_url_.has_value()) {
ESP_LOGD(TAG, " Media URL: %s", this->media_url_.value().c_str());
}
if (this->volume_.has_value()) {
ESP_LOGD(TAG, " Volume: %.2f", this->volume_.value());
}
this->parent_->control(*this);
}
MediaPlayerCall &MediaPlayerCall::set_command(MediaPlayerCommand command) {
this->command_ = command;
return *this;
}
MediaPlayerCall &MediaPlayerCall::set_command(optional<MediaPlayerCommand> command) {
this->command_ = command;
return *this;
}
MediaPlayerCall &MediaPlayerCall::set_command(const std::string &command) {
if (str_equals_case_insensitive(command, "PLAY")) {
this->set_command(MEDIA_PLAYER_COMMAND_PLAY);
} else if (str_equals_case_insensitive(command, "PAUSE")) {
this->set_command(MEDIA_PLAYER_COMMAND_PAUSE);
} else if (str_equals_case_insensitive(command, "STOP")) {
this->set_command(MEDIA_PLAYER_COMMAND_STOP);
} else if (str_equals_case_insensitive(command, "MUTE")) {
this->set_command(MEDIA_PLAYER_COMMAND_MUTE);
} else if (str_equals_case_insensitive(command, "UNMUTE")) {
this->set_command(MEDIA_PLAYER_COMMAND_UNMUTE);
} else if (str_equals_case_insensitive(command, "TOGGLE")) {
this->set_command(MEDIA_PLAYER_COMMAND_TOGGLE);
} else {
ESP_LOGW(TAG, "'%s' - Unrecognized command %s", this->parent_->get_name().c_str(), command.c_str());
}
return *this;
}
MediaPlayerCall &MediaPlayerCall::set_media_url(const std::string &media_url) {
this->media_url_ = media_url;
return *this;
}
MediaPlayerCall &MediaPlayerCall::set_volume(float volume) {
this->volume_ = volume;
return *this;
}
void MediaPlayer::add_on_state_callback(std::function<void()> &&callback) {
this->state_callback_.add(std::move(callback));
}
void MediaPlayer::publish_state() { this->state_callback_.call(); }
} // namespace media_player
} // namespace esphome

View File

@ -0,0 +1,93 @@
#pragma once
#include "esphome/core/entity_base.h"
#include "esphome/core/helpers.h"
namespace esphome {
namespace media_player {
enum MediaPlayerState : uint8_t {
MEDIA_PLAYER_STATE_NONE = 0,
MEDIA_PLAYER_STATE_IDLE = 1,
MEDIA_PLAYER_STATE_PLAYING = 2,
MEDIA_PLAYER_STATE_PAUSED = 3
};
const char *media_player_state_to_string(MediaPlayerState state);
enum MediaPlayerCommand : uint8_t {
MEDIA_PLAYER_COMMAND_PLAY = 0,
MEDIA_PLAYER_COMMAND_PAUSE = 1,
MEDIA_PLAYER_COMMAND_STOP = 2,
MEDIA_PLAYER_COMMAND_MUTE = 3,
MEDIA_PLAYER_COMMAND_UNMUTE = 4,
MEDIA_PLAYER_COMMAND_TOGGLE = 5,
MEDIA_PLAYER_COMMAND_VOLUME_UP = 6,
MEDIA_PLAYER_COMMAND_VOLUME_DOWN = 7,
};
const char *media_player_command_to_string(MediaPlayerCommand command);
class MediaPlayer;
class MediaPlayerTraits {
public:
MediaPlayerTraits() = default;
void set_supports_pause(bool supports_pause) { this->supports_pause_ = supports_pause; }
bool get_supports_pause() const { return this->supports_pause_; }
protected:
bool supports_pause_{false};
};
class MediaPlayerCall {
public:
MediaPlayerCall(MediaPlayer *parent) : parent_(parent) {}
MediaPlayerCall &set_command(MediaPlayerCommand command);
MediaPlayerCall &set_command(optional<MediaPlayerCommand> command);
MediaPlayerCall &set_command(const std::string &command);
MediaPlayerCall &set_media_url(const std::string &url);
MediaPlayerCall &set_volume(float volume);
void perform();
const optional<MediaPlayerCommand> &get_command() const { return command_; }
const optional<std::string> &get_media_url() const { return media_url_; }
const optional<float> &get_volume() const { return volume_; }
protected:
void validate_();
MediaPlayer *const parent_;
optional<MediaPlayerCommand> command_;
optional<std::string> media_url_;
optional<float> volume_;
};
class MediaPlayer : public EntityBase {
public:
MediaPlayerState state{MEDIA_PLAYER_STATE_NONE};
float volume{1.0f};
MediaPlayerCall make_call() { return MediaPlayerCall(this); }
void publish_state();
void add_on_state_callback(std::function<void()> &&callback);
virtual bool is_muted() const { return false; }
virtual MediaPlayerTraits get_traits() = 0;
protected:
friend MediaPlayerCall;
virtual void control(const MediaPlayerCall &call) = 0;
CallbackManager<void()> state_callback_{};
};
} // namespace media_player
} // namespace esphome

View File

@ -76,7 +76,12 @@ bool Modbus::parse_modbus_byte_(uint8_t byte) {
// installed, but wait, there is the CRC, and if we get a hit there is a good
// chance that this is a complete message ... admittedly there is a small chance is
// isn't but that is quite small given the purpose of the CRC in the first place
data_len = at;
// Fewer than 2 bytes can't calc CRC
if (at < 2)
return true;
data_len = at - 2;
data_offset = 1;
uint16_t computed_crc = crc16(raw, data_offset + data_len);
@ -95,7 +100,7 @@ bool Modbus::parse_modbus_byte_(uint8_t byte) {
}
// Error ( msb indicates error )
// response format: Byte[0] = device address, Byte[1] function code | 0x80 , Byte[2] excpetion code, Byte[3-4] crc
// response format: Byte[0] = device address, Byte[1] function code | 0x80 , Byte[2] exception code, Byte[3-4] crc
if ((function_code & 0x80) == 0x80) {
data_offset = 2;
data_len = 1;

View File

@ -70,7 +70,7 @@ void ModbusController::on_modbus_error(uint8_t function_code, uint8_t exception_
auto &current_command = this->command_queue_.front();
if (current_command != nullptr) {
ESP_LOGE(TAG,
"Modbus error - last command: function code=0x%X register adddress = 0x%X "
"Modbus error - last command: function code=0x%X register address = 0x%X "
"registers count=%d "
"payload size=%zu",
function_code, current_command->register_address, current_command->register_count,
@ -105,7 +105,7 @@ void ModbusController::on_register_data(ModbusRegisterType register_type, uint16
}
void ModbusController::queue_command(const ModbusCommandItem &command) {
// check if this commmand is already qeued.
// check if this command is already qeued.
// not very effective but the queue is never really large
for (auto &item : command_queue_) {
if (item->register_address == command.register_address && item->register_count == command.register_count &&
@ -299,7 +299,7 @@ void ModbusController::loop() {
incoming_queue_.pop();
} else {
// all messages processed send pending commmands
// all messages processed send pending commands
send_next_command_();
}
}

View File

@ -185,8 +185,8 @@ inline bool coil_from_vector(int coil, const std::vector<uint8_t> &data) {
/** Extract bits from value and shift right according to the bitmask
* if the bitmask is 0x00F0 we want the values frrom bit 5 - 8.
* the result is then shifted right by the postion if the first right set bit in the mask
* Usefull for modbus data where more than one value is packed in a 16 bit register
* the result is then shifted right by the position if the first right set bit in the mask
* Useful for modbus data where more than one value is packed in a 16 bit register
* Example: on Epever the "Length of night" register 0x9065 encodes values of the whole night length of time as
* D15 - D8 = hour, D7 - D0 = minute
* To get the hours use mask 0xFF00 and 0x00FF for the minute
@ -447,7 +447,7 @@ class ModbusController : public PollingComponent, public modbus::ModbusDevice {
void dump_sensors_();
/// Collection of all sensors for this component
SensorSet sensorset_;
/// Continous range of modbus registers
/// Continuous range of modbus registers
std::vector<RegisterRange> register_ranges_;
/// Hold the pending requests to be sent
std::list<std::unique_ptr<ModbusCommandItem>> command_queue_;

View File

@ -68,7 +68,7 @@ CONFIG_SCHEMA = cv.All(
),
cv.Optional(CONF_VALUE_TYPE, default="U_WORD"): cv.enum(SENSOR_VALUE_TYPE),
cv.Optional(CONF_WRITE_LAMBDA): cv.returning_lambda,
# 24 bits are the maximum value for fp32 before precison is lost
# 24 bits are the maximum value for fp32 before precision is lost
# 0x00FFFFFF = 16777215
cv.Optional(CONF_MAX_VALUE, default=16777215.0): cv.float_,
cv.Optional(CONF_MIN_VALUE, default=-16777215.0): cv.float_,

View File

@ -24,6 +24,8 @@ from esphome.const import (
CONF_LOG_TOPIC,
CONF_ON_JSON_MESSAGE,
CONF_ON_MESSAGE,
CONF_ON_CONNECT,
CONF_ON_DISCONNECT,
CONF_PASSWORD,
CONF_PAYLOAD,
CONF_PAYLOAD_AVAILABLE,
@ -90,6 +92,10 @@ MQTTMessageTrigger = mqtt_ns.class_(
MQTTJsonMessageTrigger = mqtt_ns.class_(
"MQTTJsonMessageTrigger", automation.Trigger.template(cg.JsonObjectConst)
)
MQTTConnectTrigger = mqtt_ns.class_("MQTTConnectTrigger", automation.Trigger.template())
MQTTDisconnectTrigger = mqtt_ns.class_(
"MQTTDisconnectTrigger", automation.Trigger.template()
)
MQTTComponent = mqtt_ns.class_("MQTTComponent", cg.Component)
MQTTConnectedCondition = mqtt_ns.class_("MQTTConnectedCondition", Condition)
@ -212,6 +218,18 @@ CONFIG_SCHEMA = cv.All(
cv.Optional(
CONF_REBOOT_TIMEOUT, default="15min"
): cv.positive_time_period_milliseconds,
cv.Optional(CONF_ON_CONNECT): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(MQTTConnectTrigger),
}
),
cv.Optional(CONF_ON_DISCONNECT): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
MQTTDisconnectTrigger
),
}
),
cv.Optional(CONF_ON_MESSAGE): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(MQTTMessageTrigger),
@ -362,6 +380,14 @@ async def to_code(config):
trig = cg.new_Pvariable(conf[CONF_TRIGGER_ID], conf[CONF_TOPIC], conf[CONF_QOS])
await automation.build_automation(trig, [(cg.JsonObjectConst, "x")], conf)
for conf in config.get(CONF_ON_CONNECT, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf)
for conf in config.get(CONF_ON_DISCONNECT, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf)
MQTT_PUBLISH_ACTION_SCHEMA = cv.Schema(
{

View File

@ -187,7 +187,7 @@ void MQTTClientComponent::start_connect_() {
this->mqtt_backend_.set_credentials(username, password);
this->mqtt_backend_.set_server((uint32_t) this->ip_, this->credentials_.port);
this->mqtt_backend_.set_server(this->credentials_.address.c_str(), this->credentials_.port);
if (!this->last_will_.topic.empty()) {
this->mqtt_backend_.set_will(this->last_will_.topic.c_str(), this->last_will_.qos, this->last_will_.retain,
this->last_will_.payload.c_str());
@ -572,6 +572,14 @@ void MQTTClientComponent::on_shutdown() {
this->mqtt_backend_.disconnect();
}
void MQTTClientComponent::set_on_connect(mqtt_on_connect_callback_t &&callback) {
this->mqtt_backend_.set_on_connect(std::forward<mqtt_on_connect_callback_t>(callback));
}
void MQTTClientComponent::set_on_disconnect(mqtt_on_disconnect_callback_t &&callback) {
this->mqtt_backend_.set_on_disconnect(std::forward<mqtt_on_disconnect_callback_t>(callback));
}
#if ASYNC_TCP_SSL_ENABLED
void MQTTClientComponent::add_ssl_fingerprint(const std::array<uint8_t, SHA1_SIZE> &fingerprint) {
this->mqtt_backend_.setSecure(true);

View File

@ -19,6 +19,11 @@
namespace esphome {
namespace mqtt {
/** Callback for MQTT events.
*/
using mqtt_on_connect_callback_t = std::function<MQTTBackend::on_connect_callback_t>;
using mqtt_on_disconnect_callback_t = std::function<MQTTBackend::on_disconnect_callback_t>;
/** Callback for MQTT subscriptions.
*
* First parameter is the topic, the second one is the payload.
@ -240,6 +245,8 @@ class MQTTClientComponent : public Component {
void set_username(const std::string &username) { this->credentials_.username = username; }
void set_password(const std::string &password) { this->credentials_.password = password; }
void set_client_id(const std::string &client_id) { this->credentials_.client_id = client_id; }
void set_on_connect(mqtt_on_connect_callback_t &&callback);
void set_on_disconnect(mqtt_on_disconnect_callback_t &&callback);
protected:
/// Reconnect to the MQTT broker if not already connected.
@ -328,6 +335,20 @@ class MQTTJsonMessageTrigger : public Trigger<JsonObjectConst> {
}
};
class MQTTConnectTrigger : public Trigger<> {
public:
explicit MQTTConnectTrigger(MQTTClientComponent *&client) {
client->set_on_connect([this](bool session_present) { this->trigger(); });
}
};
class MQTTDisconnectTrigger : public Trigger<> {
public:
explicit MQTTDisconnectTrigger(MQTTClientComponent *&client) {
client->set_on_disconnect([this](MQTTClientDisconnectReason reason) { this->trigger(); });
}
};
template<typename... Ts> class MQTTPublishAction : public Action<Ts...> {
public:
MQTTPublishAction(MQTTClientComponent *parent) : parent_(parent) {}

View File

@ -114,6 +114,7 @@ void MQTTFanComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig
if (this->state_->get_traits().supports_speed()) {
root[MQTT_PERCENTAGE_COMMAND_TOPIC] = this->get_speed_level_command_topic();
root[MQTT_PERCENTAGE_STATE_TOPIC] = this->get_speed_level_state_topic();
root[MQTT_SPEED_RANGE_MAX] = this->state_->get_traits().supported_speed_count();
}
}
bool MQTTFanComponent::publish_state() {

View File

@ -115,7 +115,7 @@ void Nextion::set_backlight_brightness(float brightness) {
ESP_LOGD(TAG, "Brightness out of bounds, percentage range 0-1.0");
return;
}
this->add_no_result_to_queue_with_set("backlight_brightness", "dim", static_cast<int>(brightness * 100));
this->add_no_result_to_queue_with_printf_("backlight_brightness", "dim=%d", static_cast<int>(brightness * 100));
}
void Nextion::set_auto_wake_on_touch(bool auto_wake) {

View File

@ -12,7 +12,7 @@ NdefRecord::NdefRecord(std::vector<uint8_t> payload_data) {
std::vector<uint8_t> NdefRecord::encode(bool first, bool last) {
std::vector<uint8_t> data;
// Get encoded payload, this is overriden by more specific record classes
// Get encoded payload, this is overridden by more specific record classes
std::vector<uint8_t> payload_data = get_encoded_payload();
size_t payload_length = payload_data.size();

View File

@ -6,8 +6,17 @@ namespace number {
static const char *const TAG = "number.automation";
union convert {
float from;
uint32_t to;
};
void ValueRangeTrigger::setup() {
this->rtc_ = global_preferences->make_preference<bool>(this->parent_->get_object_id_hash());
float local_min = this->min_.value(0.0);
float local_max = this->max_.value(0.0);
convert hash = {.from = (local_max - local_min)};
uint32_t myhash = hash.to ^ this->parent_->get_object_id_hash();
this->rtc_ = global_preferences->make_preference<bool>(myhash);
bool initial_state;
if (this->rtc_.load(&initial_state)) {
this->previous_in_range_ = initial_state;

View File

@ -17,7 +17,5 @@ void Number::add_on_state_callback(std::function<void(float)> &&callback) {
this->state_callback_.add(std::move(callback));
}
uint32_t Number::hash_base() { return 2282307003UL; }
} // namespace number
} // namespace esphome

View File

@ -52,8 +52,6 @@ class Number : public EntityBase {
*/
virtual void control(float value) = 0;
uint32_t hash_base() override;
CallbackManager<void(float)> state_callback_;
bool has_state_{false};
};

View File

@ -52,6 +52,8 @@ class PCD8544 : public PollingComponent,
this->initialize();
}
display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_BINARY; }
protected:
void draw_absolute_pixel_internal(int x, int y, Color color) override;

View File

@ -768,7 +768,7 @@ uint8_t Pipsolar::check_incoming_length_(uint8_t length) {
uint8_t Pipsolar::check_incoming_crc_() {
uint16_t crc16;
crc16 = calc_crc_(read_buffer_, read_pos_ - 3);
crc16 = cal_crc_half_(read_buffer_, read_pos_ - 3);
ESP_LOGD(TAG, "checking crc on incoming message");
if (((uint8_t)((crc16) >> 8)) == read_buffer_[read_pos_ - 3] &&
((uint8_t)((crc16) &0xff)) == read_buffer_[read_pos_ - 2]) {
@ -797,7 +797,7 @@ uint8_t Pipsolar::send_next_command_() {
this->command_start_millis_ = millis();
this->empty_uart_buffer_();
this->read_pos_ = 0;
crc16 = calc_crc_(byte_command, length);
crc16 = cal_crc_half_(byte_command, length);
this->write_str(command);
// checksum
this->write(((uint8_t)((crc16) >> 8))); // highbyte
@ -824,8 +824,8 @@ void Pipsolar::send_next_poll_() {
this->command_start_millis_ = millis();
this->empty_uart_buffer_();
this->read_pos_ = 0;
crc16 = calc_crc_(this->used_polling_commands_[this->last_polling_command_].command,
this->used_polling_commands_[this->last_polling_command_].length);
crc16 = cal_crc_half_(this->used_polling_commands_[this->last_polling_command_].command,
this->used_polling_commands_[this->last_polling_command_].length);
this->write_array(this->used_polling_commands_[this->last_polling_command_].command,
this->used_polling_commands_[this->last_polling_command_].length);
// checksum
@ -892,29 +892,41 @@ void Pipsolar::add_polling_command_(const char *command, ENUMPollingCommand poll
}
}
uint16_t Pipsolar::calc_crc_(uint8_t *msg, int n) {
// Initial value. xmodem uses 0xFFFF but this example
// requires an initial value of zero.
uint16_t x = 0;
while (n--) {
x = crc_xmodem_update_(x, (uint16_t) *msg++);
}
return (x);
}
uint16_t Pipsolar::cal_crc_half_(uint8_t *msg, uint8_t len) {
uint16_t crc;
// See bottom of this page: http://www.nongnu.org/avr-libc/user-manual/group__util__crc.html
// Polynomial: x^16 + x^12 + x^5 + 1 (0x1021)
uint16_t Pipsolar::crc_xmodem_update_(uint16_t crc, uint8_t data) {
int i;
crc = crc ^ ((uint16_t) data << 8);
for (i = 0; i < 8; i++) {
if (crc & 0x8000) {
crc = (crc << 1) ^ 0x1021; //(polynomial = 0x1021)
} else {
crc <<= 1;
}
uint8_t da;
uint8_t *ptr;
uint8_t b_crc_hign;
uint8_t b_crc_low;
uint16_t crc_ta[16] = {0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef};
ptr = msg;
crc = 0;
while (len-- != 0) {
da = ((uint8_t)(crc >> 8)) >> 4;
crc <<= 4;
crc ^= crc_ta[da ^ (*ptr >> 4)];
da = ((uint8_t)(crc >> 8)) >> 4;
crc <<= 4;
crc ^= crc_ta[da ^ (*ptr & 0x0f)];
ptr++;
}
return crc;
b_crc_low = crc;
b_crc_hign = (uint8_t)(crc >> 8);
if (b_crc_low == 0x28 || b_crc_low == 0x0d || b_crc_low == 0x0a)
b_crc_low++;
if (b_crc_hign == 0x28 || b_crc_hign == 0x0d || b_crc_hign == 0x0a)
b_crc_hign++;
crc = ((uint16_t) b_crc_hign) << 8;
crc += b_crc_low;
return (crc);
}
} // namespace pipsolar

View File

@ -193,8 +193,7 @@ class Pipsolar : public uart::UARTDevice, public PollingComponent {
void empty_uart_buffer_();
uint8_t check_incoming_crc_();
uint8_t check_incoming_length_(uint8_t length);
uint16_t calc_crc_(uint8_t *msg, int n);
uint16_t crc_xmodem_update_(uint16_t crc, uint8_t data);
uint16_t cal_crc_half_(uint8_t *msg, uint8_t len);
uint8_t send_next_command_();
void send_next_poll_();
void queue_command_(const char *command, uint8_t length);

View File

@ -1,6 +1,9 @@
import esphome.config_validation as cv
import esphome.codegen as cg
from esphome.const import CONF_ID
from esphome.const import (
CONF_ID,
CONF_INCLUDE_INTERNAL,
)
from esphome.components.web_server_base import CONF_WEB_SERVER_BASE_ID
from esphome.components import web_server_base
@ -15,6 +18,7 @@ CONFIG_SCHEMA = cv.Schema(
cv.GenerateID(CONF_WEB_SERVER_BASE_ID): cv.use_id(
web_server_base.WebServerBase
),
cv.Optional(CONF_INCLUDE_INTERNAL, default=False): cv.boolean,
},
cv.only_with_arduino,
).extend(cv.COMPONENT_SCHEMA)
@ -27,3 +31,5 @@ async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID], paren)
await cg.register_component(var, config)
cg.add(var.set_include_internal(config[CONF_INCLUDE_INTERNAL]))

View File

@ -61,7 +61,7 @@ void PrometheusHandler::sensor_type_(AsyncResponseStream *stream) {
stream->print(F("#TYPE esphome_sensor_failed GAUGE\n"));
}
void PrometheusHandler::sensor_row_(AsyncResponseStream *stream, sensor::Sensor *obj) {
if (obj->is_internal())
if (obj->is_internal() && !this->include_internal_)
return;
if (!std::isnan(obj->state)) {
// We have a valid value, output this value
@ -98,7 +98,7 @@ void PrometheusHandler::binary_sensor_type_(AsyncResponseStream *stream) {
stream->print(F("#TYPE esphome_binary_sensor_failed GAUGE\n"));
}
void PrometheusHandler::binary_sensor_row_(AsyncResponseStream *stream, binary_sensor::BinarySensor *obj) {
if (obj->is_internal())
if (obj->is_internal() && !this->include_internal_)
return;
if (obj->has_state()) {
// We have a valid value, output this value
@ -134,7 +134,7 @@ void PrometheusHandler::fan_type_(AsyncResponseStream *stream) {
stream->print(F("#TYPE esphome_fan_oscillation GAUGE\n"));
}
void PrometheusHandler::fan_row_(AsyncResponseStream *stream, fan::Fan *obj) {
if (obj->is_internal())
if (obj->is_internal() && !this->include_internal_)
return;
stream->print(F("esphome_fan_failed{id=\""));
stream->print(obj->get_object_id().c_str());
@ -179,7 +179,7 @@ void PrometheusHandler::light_type_(AsyncResponseStream *stream) {
stream->print(F("#TYPE esphome_light_effect_active GAUGE\n"));
}
void PrometheusHandler::light_row_(AsyncResponseStream *stream, light::LightState *obj) {
if (obj->is_internal())
if (obj->is_internal() && !this->include_internal_)
return;
// State
stream->print(F("esphome_light_state{id=\""));
@ -255,7 +255,7 @@ void PrometheusHandler::cover_type_(AsyncResponseStream *stream) {
stream->print(F("#TYPE esphome_cover_failed GAUGE\n"));
}
void PrometheusHandler::cover_row_(AsyncResponseStream *stream, cover::Cover *obj) {
if (obj->is_internal())
if (obj->is_internal() && !this->include_internal_)
return;
if (!std::isnan(obj->position)) {
// We have a valid value, output this value
@ -298,7 +298,7 @@ void PrometheusHandler::switch_type_(AsyncResponseStream *stream) {
stream->print(F("#TYPE esphome_switch_failed GAUGE\n"));
}
void PrometheusHandler::switch_row_(AsyncResponseStream *stream, switch_::Switch *obj) {
if (obj->is_internal())
if (obj->is_internal() && !this->include_internal_)
return;
stream->print(F("esphome_switch_failed{id=\""));
stream->print(obj->get_object_id().c_str());
@ -322,7 +322,7 @@ void PrometheusHandler::lock_type_(AsyncResponseStream *stream) {
stream->print(F("#TYPE esphome_lock_failed GAUGE\n"));
}
void PrometheusHandler::lock_row_(AsyncResponseStream *stream, lock::Lock *obj) {
if (obj->is_internal())
if (obj->is_internal() && !this->include_internal_)
return;
stream->print(F("esphome_lock_failed{id=\""));
stream->print(obj->get_object_id().c_str());

View File

@ -13,6 +13,13 @@ class PrometheusHandler : public AsyncWebHandler, public Component {
public:
PrometheusHandler(web_server_base::WebServerBase *base) : base_(base) {}
/** Determine whether internal components should be exported as metrics.
* Defaults to false.
*
* @param include_internal Whether internal components should be exported.
*/
void set_include_internal(bool include_internal) { include_internal_ = include_internal; }
bool canHandle(AsyncWebServerRequest *request) override {
if (request->method() == HTTP_GET) {
if (request->url() == "/metrics")
@ -84,6 +91,7 @@ class PrometheusHandler : public AsyncWebHandler, public Component {
#endif
web_server_base::WebServerBase *base_;
bool include_internal_{false};
};
} // namespace prometheus

View File

@ -32,7 +32,7 @@ from esphome.const import (
CONF_LEVEL,
)
from esphome.core import coroutine
from esphome.jsonschema import jschema_extractor
from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor
from esphome.util import Registry, SimpleRegistry
AUTO_LOAD = ["binary_sensor"]
@ -195,14 +195,14 @@ def validate_dumpers(value):
def validate_triggers(base_schema):
assert isinstance(base_schema, cv.Schema)
@jschema_extractor("triggers")
@schema_extractor("triggers")
def validator(config):
added_keys = {}
for key, (_, valid) in TRIGGER_REGISTRY.items():
added_keys[cv.Optional(key)] = valid
new_schema = base_schema.extend(added_keys)
# pylint: disable=comparison-with-callable
if config == jschema_extractor:
if config == SCHEMA_EXTRACT:
return new_schema
return new_schema(config)
@ -728,6 +728,48 @@ async def rc5_action(var, config, args):
cg.add(var.set_command(template_))
# RC6
RC6Data, RC6BinarySensor, RC6Trigger, RC6Action, RC6Dumper = declare_protocol("RC6")
RC6_SCHEMA = cv.Schema(
{
cv.Required(CONF_ADDRESS): cv.hex_uint8_t,
cv.Required(CONF_COMMAND): cv.hex_uint8_t,
}
)
@register_binary_sensor("rc6", RC6BinarySensor, RC6_SCHEMA)
def rc6_binary_sensor(var, config):
cg.add(
var.set_data(
cg.StructInitializer(
RC6Data,
("device", config[CONF_DEVICE]),
("address", config[CONF_ADDRESS]),
("command", config[CONF_COMMAND]),
)
)
)
@register_trigger("rc6", RC6Trigger, RC6Data)
def rc6_trigger(var, config):
pass
@register_dumper("rc6", RC6Dumper)
def rc6_dumper(var, config):
pass
@register_action("rc6", RC6Action, RC6_SCHEMA)
async def rc6_action(var, config, args):
template_ = await cg.templatable(config[CONF_ADDRESS], args, cg.uint8)
cg.add(var.set_address(template_))
template_ = await cg.templatable(config[CONF_COMMAND], args, cg.uint8)
cg.add(var.set_command(template_))
# RC Switch Raw
RC_SWITCH_TIMING_SCHEMA = cv.All([cv.uint8_t], cv.Length(min=2, max=2))

View File

@ -106,7 +106,7 @@ optional<NexaData> NexaProtocol::decode(RemoteReceiveData src) {
SHHHH HHHH HHHH HHHH HHHH HHHH HHGO EE BB DDDD 0 P
S = Sync bit.
H = The first 26 bits are transmitter unique codes, and it is this code that the reciever "learns" to recognize.
H = The first 26 bits are transmitter unique codes, and it is this code that the receiver "learns" to recognize.
G = Group code, set to one for the whole group.
O = On/Off bit. Set to 1 for on, 0 for off.
E = Unit to be turned on or off. The code is inverted, i.e. '11' equals 1, '00' equals 4.

View File

@ -0,0 +1,181 @@
#include "rc6_protocol.h"
#include "esphome/core/log.h"
namespace esphome {
namespace remote_base {
static const char *const RC6_TAG = "remote.rc6";
static const uint16_t RC6_FREQ = 36000;
static const uint16_t RC6_UNIT = 444;
static const uint16_t RC6_HEADER_MARK = (6 * RC6_UNIT);
static const uint16_t RC6_HEADER_SPACE = (2 * RC6_UNIT);
static const uint16_t RC6_MODE_MASK = 0x07;
void RC6Protocol::encode(RemoteTransmitData *dst, const RC6Data &data) {
dst->reserve(44);
dst->set_carrier_frequency(RC6_FREQ);
// Encode header
dst->item(RC6_HEADER_MARK, RC6_HEADER_SPACE);
int32_t next{0};
// Encode startbit+mode
uint8_t header{static_cast<uint8_t>((1 << 3) | data.mode)};
for (uint8_t mask = 0x8; mask; mask >>= 1) {
if (header & mask) {
if (next < 0) {
dst->space(-next);
next = 0;
}
if (next >= 0) {
next = next + RC6_UNIT;
dst->mark(next);
next = -RC6_UNIT;
}
} else {
if (next > 0) {
dst->mark(next);
next = 0;
}
if (next <= 0) {
next = next - RC6_UNIT;
dst->space(-next);
next = RC6_UNIT;
}
}
}
// Toggle
if (data.toggle) {
if (next < 0) {
dst->space(-next);
next = 0;
}
if (next >= 0) {
next = next + RC6_UNIT * 2;
dst->mark(next);
next = -RC6_UNIT * 2;
}
} else {
if (next > 0) {
dst->mark(next);
next = 0;
}
if (next <= 0) {
next = next - RC6_UNIT * 2;
dst->space(-next);
next = RC6_UNIT * 2;
}
}
// Encode data
uint16_t raw{static_cast<uint16_t>((data.address << 8) | data.command)};
for (uint16_t mask = 0x8000; mask; mask >>= 1) {
if (raw & mask) {
if (next < 0) {
dst->space(-next);
next = 0;
}
if (next >= 0) {
next = next + RC6_UNIT;
dst->mark(next);
next = -RC6_UNIT;
}
} else {
if (next > 0) {
dst->mark(next);
next = 0;
}
if (next <= 0) {
next = next - RC6_UNIT;
dst->space(-next);
next = RC6_UNIT;
}
}
}
if (next > 0) {
dst->mark(next);
} else {
dst->space(-next);
}
}
optional<RC6Data> RC6Protocol::decode(RemoteReceiveData src) {
RC6Data data{
.mode = 0,
.toggle = 0,
.address = 0,
.command = 0,
};
// Check if header matches
if (!src.expect_item(RC6_HEADER_MARK, RC6_HEADER_SPACE)) {
return {};
}
uint8_t bit{1};
uint8_t offset{0};
uint8_t header{0};
uint32_t buffer{0};
// Startbit + mode
while (offset < 4) {
bit = src.peek() > 0;
header = header + (bit << (3 - offset++));
src.advance();
if (src.peek_mark(RC6_UNIT) || src.peek_space(RC6_UNIT)) {
src.advance();
} else if (offset == 4) {
break;
} else if (!src.peek_mark(RC6_UNIT * 2) && !src.peek_space(RC6_UNIT * 2)) {
return {};
}
}
data.mode = header & RC6_MODE_MASK;
if (data.mode != 0) {
return {}; // I dont have a device to test other modes
}
// Toggle
data.toggle = src.peek() > 0;
src.advance();
if (src.peek_mark(RC6_UNIT * 2) || src.peek_space(RC6_UNIT * 2)) {
src.advance();
}
// Data
offset = 0;
while (offset < 16) {
bit = src.peek() > 0;
buffer = buffer + (bit << (15 - offset++));
src.advance();
if (offset == 16) {
break;
} else if (src.peek_mark(RC6_UNIT) || src.peek_space(RC6_UNIT)) {
src.advance();
} else if (!src.peek_mark(RC6_UNIT * 2) && !src.peek_space(RC6_UNIT * 2)) {
return {};
}
}
data.address = (0xFF00 & buffer) >> 8;
data.command = (0x00FF & buffer);
return data;
}
void RC6Protocol::dump(const RC6Data &data) {
ESP_LOGD(RC6_TAG, "Received RC6: mode=0x%X, address=0x%02X, command=0x%02X, toggle=0x%X", data.mode, data.address,
data.command, data.toggle);
}
} // namespace remote_base
} // namespace esphome

View File

@ -0,0 +1,46 @@
#pragma once
#include "remote_base.h"
namespace esphome {
namespace remote_base {
struct RC6Data {
uint8_t mode : 3;
uint8_t toggle : 1;
uint8_t address;
uint8_t command;
bool operator==(const RC6Data &rhs) const { return address == rhs.address && command == rhs.command; }
};
class RC6Protocol : public RemoteProtocol<RC6Data> {
public:
void encode(RemoteTransmitData *dst, const RC6Data &data) override;
optional<RC6Data> decode(RemoteReceiveData src) override;
void dump(const RC6Data &data) override;
};
DECLARE_REMOTE_PROTOCOL(RC6)
template<typename... Ts> class RC6Action : public RemoteTransmitterActionBase<Ts...> {
public:
TEMPLATABLE_VALUE(uint8_t, address)
TEMPLATABLE_VALUE(uint8_t, command)
void encode(RemoteTransmitData *dst, Ts... x) {
RC6Data data{};
data.mode = 0;
data.toggle = this->toggle_;
data.address = this->address_.value(x...);
data.command = this->command_.value(x...);
RC6Protocol().encode(dst, data);
this->toggle_ = !this->toggle_;
}
protected:
uint8_t toggle_{0};
};
} // namespace remote_base
} // namespace esphome

View File

@ -102,7 +102,7 @@ bool RCSwitchBase::expect_sync(RemoteReceiveData &src) const {
if (!src.peek_space(this->sync_low_, 1))
return false;
} else {
// We cant peek a space at the beginning because signals starts with a low to high transition.
// We can't peek a space at the beginning because signals starts with a low to high transition.
// this long space at the beginning is the separation between the transmissions itself, so it is actually
// added at the end kind of artificially (by the value given to "idle:" option by the user in the yaml)
if (!src.peek_mark(this->sync_low_))

View File

@ -103,7 +103,7 @@ void IRAM_ATTR HOT RotaryEncoderSensorStore::gpio_intr(RotaryEncoderSensorStore
rotation_dir = -1;
}
if (rotation_dir != 0) {
if (rotation_dir != 0 && !arg->first_read) {
auto *first_zero = std::find(arg->rotation_events.begin(), arg->rotation_events.end(), 0); // find first zero
if (first_zero == arg->rotation_events.begin() // are we at the start (first event this loop iteration)
|| std::signbit(*std::prev(first_zero)) !=
@ -119,6 +119,7 @@ void IRAM_ATTR HOT RotaryEncoderSensorStore::gpio_intr(RotaryEncoderSensorStore
*std::prev(first_zero) += rotation_dir; // store the rotation into the previous slot
}
}
arg->first_read = false;
arg->state = new_state;
}

View File

@ -34,6 +34,7 @@ struct RotaryEncoderSensorStore {
int32_t max_value{INT32_MAX};
int32_t last_read{0};
uint8_t state{0};
bool first_read{true};
std::array<int8_t, 8> rotation_events{};
bool rotation_events_overflow{false};

View File

@ -205,7 +205,7 @@ void SCD4XComponent::update() {
bool SCD4XComponent::perform_forced_calibration(uint16_t current_co2_concentration) {
/*
Operate the SCD4x in the operation mode later used in normal sensor operation (periodic measurement, low power
periodic measurement or single shot) for > 3 minutes in an environment with homogenous and constant CO2
periodic measurement or single shot) for > 3 minutes in an environment with homogeneous and constant CO2
concentration before performing a forced recalibration.
*/
if (!this->write_command(SCD4X_CMD_STOP_MEASUREMENTS)) {
@ -217,7 +217,7 @@ bool SCD4XComponent::perform_forced_calibration(uint16_t current_co2_concentrati
ESP_LOGD(TAG, "setting forced calibration Co2 level %d ppm", current_co2_concentration);
// frc takes 400 ms
// because this method will be used very rarly
// the simple aproach with delay is ok
// the simple approach with delay is ok
delay(400); // NOLINT'
if (!this->start_measurement_()) {
return false;

View File

@ -40,7 +40,7 @@ void SDP3XComponent::setup() {
}
uint16_t data[6];
if (this->read_data(data, 6) != i2c::ERROR_OK) {
if (!this->read_data(data, 6)) {
ESP_LOGE(TAG, "Read ID SDP3X failed!");
this->mark_failed();
return;
@ -78,8 +78,7 @@ void SDP3XComponent::setup() {
}
}
if (this->write_command(measurement_mode_ == DP_AVG ? SDP3X_START_DP_AVG : SDP3X_START_MASS_FLOW_AVG) !=
i2c::ERROR_OK) {
if (!this->write_command(measurement_mode_ == DP_AVG ? SDP3X_START_DP_AVG : SDP3X_START_MASS_FLOW_AVG)) {
ESP_LOGE(TAG, "Start Measurements SDP3X failed!");
this->mark_failed();
return;
@ -98,7 +97,7 @@ void SDP3XComponent::dump_config() {
void SDP3XComponent::read_pressure_() {
uint16_t data[3];
if (this->read_data(data, 3) != i2c::ERROR_OK) {
if (!this->read_data(data, 3)) {
ESP_LOGW(TAG, "Couldn't read SDP3X data!");
this->status_set_warning();
return;

View File

@ -58,7 +58,5 @@ optional<std::string> Select::at(size_t index) const {
}
}
uint32_t Select::hash_base() { return 2812997003UL; }
} // namespace select
} // namespace esphome

View File

@ -65,8 +65,6 @@ class Select : public EntityBase {
*/
virtual void control(const std::string &value) = 0;
uint32_t hash_base() override;
CallbackManager<void(std::string, size_t)> state_callback_;
bool has_state_{false};
};

View File

@ -33,7 +33,7 @@ bool SensirionI2CDevice::read_data(uint16_t *data, uint8_t len) {
}
/***
* write command with parameters and insert crc
* use stack array for less than 4 paramaters. Most sensirion i2c commands have less parameters
* use stack array for less than 4 parameters. Most sensirion i2c commands have less parameters
*/
bool SensirionI2CDevice::write_command_(uint16_t command, CommandLen command_len, const uint16_t *data,
uint8_t data_len) {
@ -63,7 +63,7 @@ bool SensirionI2CDevice::write_command_(uint16_t command, CommandLen command_len
temp[raw_idx++] = command >> 8;
#endif
}
// add parameters folllowed by crc
// add parameters followed by crc
// skipped if len == 0
for (size_t i = 0; i < data_len; i++) {
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__

View File

@ -20,13 +20,13 @@ class SensirionI2CDevice : public i2c::I2CDevice {
* handles crc check used by Sensirion sensors
* @param data pointer to raw result
* @param len number of words to read
* @return true if reading succeded
* @return true if reading succeeded
*/
bool read_data(uint16_t *data, uint8_t len);
/** Read 1 data word from i2c device.
* @param data reference to raw result
* @return true if reading succeded
* @return true if reading succeeded
*/
bool read_data(uint16_t &data) { return this->read_data(&data, 1); }
@ -35,8 +35,8 @@ class SensirionI2CDevice : public i2c::I2CDevice {
* @param i2c register
* @param data pointer to raw result
* @param len number of words to read
* @param delay milliseconds to to wait between sending the i2c commmand and reading the result
* @return true if reading succeded
* @param delay milliseconds to to wait between sending the i2c command and reading the result
* @return true if reading succeeded
*/
bool get_register(uint16_t command, uint16_t *data, uint8_t len, uint8_t delay = 0) {
return get_register_(command, ADDR_16_BIT, data, len, delay);
@ -44,8 +44,8 @@ class SensirionI2CDevice : public i2c::I2CDevice {
/** Read 1 data word from 16 bit i2c register.
* @param i2c register
* @param data reference to raw result
* @param delay milliseconds to to wait between sending the i2c commmand and reading the result
* @return true if reading succeded
* @param delay milliseconds to to wait between sending the i2c command and reading the result
* @return true if reading succeeded
*/
bool get_register(uint16_t i2c_register, uint16_t &data, uint8_t delay = 0) {
return this->get_register_(i2c_register, ADDR_16_BIT, &data, 1, delay);
@ -56,8 +56,8 @@ class SensirionI2CDevice : public i2c::I2CDevice {
* @param i2c register
* @param data pointer to raw result
* @param len number of words to read
* @param delay milliseconds to to wait between sending the i2c commmand and reading the result
* @return true if reading succeded
* @param delay milliseconds to to wait between sending the i2c command and reading the result
* @return true if reading succeeded
*/
bool get_8bit_register(uint8_t i2c_register, uint16_t *data, uint8_t len, uint8_t delay = 0) {
return get_register_(i2c_register, ADDR_8_BIT, data, len, delay);
@ -66,8 +66,8 @@ class SensirionI2CDevice : public i2c::I2CDevice {
/** Read 1 data word from 8 bit i2c register.
* @param i2c register
* @param data reference to raw result
* @param delay milliseconds to to wait between sending the i2c commmand and reading the result
* @return true if reading succeded
* @param delay milliseconds to to wait between sending the i2c command and reading the result
* @return true if reading succeeded
*/
bool get_8bit_register(uint8_t i2c_register, uint16_t &data, uint8_t delay = 0) {
return this->get_register_(i2c_register, ADDR_8_BIT, &data, 1, delay);
@ -75,21 +75,21 @@ class SensirionI2CDevice : public i2c::I2CDevice {
/** Write a command to the i2c device.
* @param command i2c command to send
* @return true if reading succeded
* @return true if reading succeeded
*/
template<class T> bool write_command(T i2c_register) { return write_command(i2c_register, nullptr, 0); }
/** Write a command and one data word to the i2c device .
* @param command i2c command to send
* @param data argument for the i2c command
* @return true if reading succeded
* @return true if reading succeeded
*/
template<class T> bool write_command(T i2c_register, uint16_t data) { return write_command(i2c_register, &data, 1); }
/** Write a command with arguments as words
* @param i2c_register i2c command to send - an be uint8_t or uint16_t
* @param data vector<uint16> arguments for the i2c command
* @return true if reading succeded
* @return true if reading succeeded
*/
template<class T> bool write_command(T i2c_register, const std::vector<uint16_t> &data) {
return write_command_(i2c_register, sizeof(T), data.data(), data.size());
@ -99,7 +99,7 @@ class SensirionI2CDevice : public i2c::I2CDevice {
* @param i2c_register i2c command to send - an be uint8_t or uint16_t
* @param data arguments for the i2c command
* @param len number of arguments (words)
* @return true if reading succeded
* @return true if reading succeeded
*/
template<class T> bool write_command(T i2c_register, const uint16_t *data, uint8_t len) {
// limit to 8 or 16 bit only
@ -115,7 +115,7 @@ class SensirionI2CDevice : public i2c::I2CDevice {
* @param command_len either 1 for short 8 bit command or 2 for 16 bit command codes
* @param data arguments for the i2c command
* @param data_len number of arguments (words)
* @return true if reading succeded
* @return true if reading succeeded
*/
bool write_command_(uint16_t command, CommandLen command_len, const uint16_t *data, uint8_t data_len);
@ -125,8 +125,8 @@ class SensirionI2CDevice : public i2c::I2CDevice {
* @param command_len either 1 for short 8 bit command or 2 for 16 bit command codes
* @param data pointer to raw result
* @param len number of words to read
* @param delay milliseconds to to wait between sending the i2c commmand and reading the result
* @return true if reading succeded
* @param delay milliseconds to to wait between sending the i2c command and reading the result
* @return true if reading succeeded
*/
bool get_register_(uint16_t reg, CommandLen command_len, uint16_t *data, uint8_t len, uint8_t delay);

View File

@ -31,12 +31,15 @@ from esphome.const import (
CONF_FORCE_UPDATE,
DEVICE_CLASS_DURATION,
DEVICE_CLASS_EMPTY,
DEVICE_CLASS_APPARENT_POWER,
DEVICE_CLASS_AQI,
DEVICE_CLASS_BATTERY,
DEVICE_CLASS_CARBON_DIOXIDE,
DEVICE_CLASS_CARBON_MONOXIDE,
DEVICE_CLASS_CURRENT,
DEVICE_CLASS_DATE,
DEVICE_CLASS_ENERGY,
DEVICE_CLASS_FREQUENCY,
DEVICE_CLASS_GAS,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_ILLUMINANCE,
@ -51,6 +54,7 @@ from esphome.const import (
DEVICE_CLASS_POWER,
DEVICE_CLASS_POWER_FACTOR,
DEVICE_CLASS_PRESSURE,
DEVICE_CLASS_REACTIVE_POWER,
DEVICE_CLASS_SIGNAL_STRENGTH,
DEVICE_CLASS_SULPHUR_DIOXIDE,
DEVICE_CLASS_TEMPERATURE,
@ -66,13 +70,16 @@ from esphome.util import Registry
CODEOWNERS = ["@esphome/core"]
DEVICE_CLASSES = [
DEVICE_CLASS_EMPTY,
DEVICE_CLASS_APPARENT_POWER,
DEVICE_CLASS_AQI,
DEVICE_CLASS_BATTERY,
DEVICE_CLASS_CARBON_DIOXIDE,
DEVICE_CLASS_CARBON_MONOXIDE,
DEVICE_CLASS_CURRENT,
DEVICE_CLASS_DATE,
DEVICE_CLASS_DURATION,
DEVICE_CLASS_ENERGY,
DEVICE_CLASS_FREQUENCY,
DEVICE_CLASS_GAS,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_ILLUMINANCE,
@ -87,6 +94,7 @@ DEVICE_CLASSES = [
DEVICE_CLASS_POWER,
DEVICE_CLASS_POWER_FACTOR,
DEVICE_CLASS_PRESSURE,
DEVICE_CLASS_REACTIVE_POWER,
DEVICE_CLASS_SIGNAL_STRENGTH,
DEVICE_CLASS_SULPHUR_DIOXIDE,
DEVICE_CLASS_TEMPERATURE,

View File

@ -126,7 +126,6 @@ void Sensor::internal_send_state_to_frontend(float state) {
this->callback_.call(state);
}
bool Sensor::has_state() const { return this->has_state_; }
uint32_t Sensor::hash_base() { return 2455723294UL; }
} // namespace sensor
} // namespace esphome

View File

@ -174,8 +174,6 @@ class Sensor : public EntityBase {
*/
virtual StateClass state_class(); // NOLINT
uint32_t hash_base() override;
CallbackManager<void(float)> raw_callback_; ///< Storage for raw state callbacks.
CallbackManager<void(float)> callback_; ///< Storage for filtered state callbacks.

View File

@ -140,5 +140,7 @@ async def to_code(config):
)
)
cg.add_library(
None, None, "https://github.com/Sensirion/arduino-gas-index-algorithm.git"
None,
None,
"https://github.com/Sensirion/arduino-gas-index-algorithm.git#3.2.1",
)

View File

@ -39,7 +39,7 @@ void SGP4xComponent::setup() {
ESP_LOGE(TAG, "Measuring NOx requires a SGP41 sensor but a SGP40 sensor is detected");
// disable the sensor
this->nox_sensor_->set_disabled_by_default(true);
// make sure it's not visiable in HA
// make sure it's not visible in HA
this->nox_sensor_->set_internal(true);
this->nox_sensor_->state = NAN;
// remove pointer to sensor
@ -104,8 +104,8 @@ void SGP4xComponent::setup() {
https://github.com/Sensirion/embedded-sgp/issues/136 indicate the algorithm should be a bit resilient to slight
timing variations so the software timer should be accurate enough for this.
This block starts sampling from the sensor at 1Hz, and is done seperately from the call
to the update method. This seperation is to support getting accurate measurements but
This block starts sampling from the sensor at 1Hz, and is done separately from the call
to the update method. This separation is to support getting accurate measurements but
limit the amount of communication done over wifi for power consumption or to keep the
number of records reported from being overwhelming.
*/
@ -170,8 +170,8 @@ bool SGP4xComponent::measure_gas_indices_(int32_t &voc, int32_t &nox) {
// much
if (this->store_baseline_ && this->seconds_since_last_store_ > SHORTEST_BASELINE_STORE_INTERVAL) {
voc_algorithm_.get_states(this->voc_state0_, this->voc_state1_);
if ((uint32_t) abs(this->voc_baselines_storage_.state0 - this->voc_state0_) > MAXIMUM_STORAGE_DIFF ||
(uint32_t) abs(this->voc_baselines_storage_.state1 - this->voc_state1_) > MAXIMUM_STORAGE_DIFF) {
if (std::abs(this->voc_baselines_storage_.state0 - this->voc_state0_) > MAXIMUM_STORAGE_DIFF ||
std::abs(this->voc_baselines_storage_.state1 - this->voc_state1_) > MAXIMUM_STORAGE_DIFF) {
this->seconds_since_last_store_ = 0;
this->voc_baselines_storage_.state0 = this->voc_state0_;
this->voc_baselines_storage_.state1 = this->voc_state1_;
@ -236,9 +236,9 @@ bool SGP4xComponent::measure_raw_(uint16_t &voc_raw, uint16_t &nox_raw) {
}
uint16_t rhticks = llround((uint16_t)((humidity * 65535) / 100));
uint16_t tempticks = (uint16_t)(((temperature + 45) * 65535) / 175);
// first paramater are the relative humidity ticks
// first parameter are the relative humidity ticks
data[0] = rhticks;
// secomd paramater are the temperature ticks
// secomd parameter are the temperature ticks
data[1] = tempticks;
if (!this->write_command(command, data, 2)) {

View File

@ -49,7 +49,7 @@ static const uint16_t SPG41_SELFTEST_TIME = 320; // 320 ms for self test
static const uint16_t SGP40_MEASURE_TIME = 30;
static const uint16_t SGP41_MEASURE_TIME = 55;
// Store anyway if the baseline difference exceeds the max storage diff value
const uint32_t MAXIMUM_STORAGE_DIFF = 50;
const float MAXIMUM_STORAGE_DIFF = 50.0f;
class SGP4xComponent;
@ -120,8 +120,8 @@ class SGP4xComponent : public PollingComponent, public sensor::Sensor, public se
sensor::Sensor *voc_sensor_{nullptr};
VOCGasIndexAlgorithm voc_algorithm_;
optional<GasTuning> voc_tuning_params_;
int32_t voc_state0_;
int32_t voc_state1_;
float voc_state0_;
float voc_state1_;
int32_t voc_index_ = 0;
sensor::Sensor *nox_sensor_{nullptr};

View File

@ -49,7 +49,7 @@ constexpr float POWER_SCALING_FACTOR = 880373;
constexpr float VOLTAGE_SCALING_FACTOR = 347800;
constexpr float CURRENT_SCALING_FACTOR = 1448;
// Esentially std::size() for pre c++17
// Essentially std::size() for pre c++17
template<typename T, size_t N> constexpr size_t size(const T (&/*unused*/)[N]) noexcept { return N; }
} // Anonymous namespace

View File

@ -42,12 +42,12 @@ void SlowPWMOutput::loop() {
uint32_t now = millis();
float scaled_state = this->state_ * this->period_;
if (now >= this->period_start_time_ + this->period_) {
if (now - this->period_start_time_ >= this->period_) {
ESP_LOGVV(TAG, "End of period. State: %f, Scaled state: %f", this->state_, scaled_state);
this->period_start_time_ += this->period_;
}
this->set_output_state_(now < this->period_start_time_ + scaled_state);
this->set_output_state_(scaled_state > now - this->period_start_time_);
}
void SlowPWMOutput::dump_config() {

View File

@ -161,7 +161,7 @@ bool SonoffD1Output::write_command_(uint8_t *cmd, const size_t len, bool needs_a
return false;
}
if ((cmd[5] + 7 /*mandatory header + suffix length*/) != len) {
ESP_LOGW(TAG, "[%04d] Payload length field does not match packet lenght (%d, expected %d)", this->write_count_,
ESP_LOGW(TAG, "[%04d] Payload length field does not match packet length (%d, expected %d)", this->write_count_,
cmd[5], len - 7);
return false;
}

View File

@ -48,6 +48,8 @@ class SSD1306 : public PollingComponent, public display::DisplayBuffer {
float get_setup_priority() const override { return setup_priority::PROCESSOR; }
void fill(Color color) override;
display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_BINARY; }
protected:
virtual void command(uint8_t value) = 0;
virtual void write_display_data() = 0;

View File

@ -30,6 +30,8 @@ class SSD1322 : public PollingComponent, public display::DisplayBuffer {
float get_setup_priority() const override { return setup_priority::PROCESSOR; }
void fill(Color color) override;
display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_GRAYSCALE; }
protected:
virtual void command(uint8_t value) = 0;
virtual void data(uint8_t value) = 0;

View File

@ -35,6 +35,8 @@ class SSD1325 : public PollingComponent, public display::DisplayBuffer {
float get_setup_priority() const override { return setup_priority::PROCESSOR; }
void fill(Color color) override;
display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_BINARY; }
protected:
virtual void command(uint8_t value) = 0;
virtual void write_display_data() = 0;

View File

@ -30,6 +30,8 @@ class SSD1327 : public PollingComponent, public display::DisplayBuffer {
float get_setup_priority() const override { return setup_priority::PROCESSOR; }
void fill(Color color) override;
display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_GRAYSCALE; }
protected:
virtual void command(uint8_t value) = 0;
virtual void write_display_data() = 0;

View File

@ -25,6 +25,8 @@ class SSD1331 : public PollingComponent, public display::DisplayBuffer {
float get_setup_priority() const override { return setup_priority::PROCESSOR; }
void fill(Color color) override;
display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_COLOR; }
protected:
virtual void command(uint8_t value) = 0;
virtual void write_display_data() = 0;

View File

@ -31,6 +31,8 @@ class SSD1351 : public PollingComponent, public display::DisplayBuffer {
float get_setup_priority() const override { return setup_priority::PROCESSOR; }
void fill(Color color) override;
display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_COLOR; }
protected:
virtual void command(uint8_t value) = 0;
virtual void data(uint8_t value) = 0;

View File

@ -53,6 +53,8 @@ class ST7735 : public PollingComponent,
void set_dc_pin(GPIOPin *value) { dc_pin_ = value; }
size_t get_buffer_length();
display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_COLOR; }
protected:
void sendcommand_(uint8_t cmd, const uint8_t *data_bytes, uint8_t num_data_bytes);
void senddata_(const uint8_t *data_bytes, uint8_t num_data_bytes);

View File

@ -126,6 +126,8 @@ class ST7789V : public PollingComponent,
void write_display_data();
display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_COLOR; }
protected:
GPIOPin *dc_pin_;
GPIOPin *reset_pin_{nullptr};

View File

@ -29,6 +29,8 @@ class ST7920 : public PollingComponent,
void fill(Color color) override;
void write_display_data();
display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_BINARY; }
protected:
void draw_absolute_pixel_internal(int x, int y, Color color) override;
int get_height_internal() override;

View File

@ -48,7 +48,7 @@ VARIABLE_PROG = re.compile(
)
def _expand_substitutions(substitutions, value, path):
def _expand_substitutions(substitutions, value, path, ignore_missing):
if "$" not in value:
return value
@ -66,13 +66,14 @@ def _expand_substitutions(substitutions, value, path):
if name.startswith("{") and name.endswith("}"):
name = name[1:-1]
if name not in substitutions:
_LOGGER.warning(
"Found '%s' (see %s) which looks like a substitution, but '%s' was "
"not declared",
orig_value,
"->".join(str(x) for x in path),
name,
)
if not ignore_missing:
_LOGGER.warning(
"Found '%s' (see %s) which looks like a substitution, but '%s' was "
"not declared",
orig_value,
"->".join(str(x) for x in path),
name,
)
i = j
continue
@ -92,37 +93,37 @@ def _expand_substitutions(substitutions, value, path):
return value
def _substitute_item(substitutions, item, path):
def _substitute_item(substitutions, item, path, ignore_missing):
if isinstance(item, list):
for i, it in enumerate(item):
sub = _substitute_item(substitutions, it, path + [i])
sub = _substitute_item(substitutions, it, path + [i], ignore_missing)
if sub is not None:
item[i] = sub
elif isinstance(item, dict):
replace_keys = []
for k, v in item.items():
if path or k != CONF_SUBSTITUTIONS:
sub = _substitute_item(substitutions, k, path + [k])
sub = _substitute_item(substitutions, k, path + [k], ignore_missing)
if sub is not None:
replace_keys.append((k, sub))
sub = _substitute_item(substitutions, v, path + [k])
sub = _substitute_item(substitutions, v, path + [k], ignore_missing)
if sub is not None:
item[k] = sub
for old, new in replace_keys:
item[new] = merge_config(item.get(old), item.get(new))
del item[old]
elif isinstance(item, str):
sub = _expand_substitutions(substitutions, item, path)
sub = _expand_substitutions(substitutions, item, path, ignore_missing)
if sub != item:
return sub
elif isinstance(item, core.Lambda):
sub = _expand_substitutions(substitutions, item.value, path)
sub = _expand_substitutions(substitutions, item.value, path, ignore_missing)
if sub != item:
item.value = sub
return None
def do_substitution_pass(config, command_line_substitutions):
def do_substitution_pass(config, command_line_substitutions, ignore_missing=False):
if CONF_SUBSTITUTIONS not in config and not command_line_substitutions:
return
@ -151,4 +152,4 @@ def do_substitution_pass(config, command_line_substitutions):
config[CONF_SUBSTITUTIONS] = substitutions
# Move substitutions to the first place to replace substitutions in them correctly
config.move_to_end(CONF_SUBSTITUTIONS, False)
_substitute_item(substitutions, config, [])
_substitute_item(substitutions, config, [], ignore_missing)

View File

@ -43,7 +43,6 @@ void Switch::add_on_state_callback(std::function<void(bool)> &&callback) {
this->state_callback_.add(std::move(callback));
}
void Switch::set_inverted(bool inverted) { this->inverted_ = inverted; }
uint32_t Switch::hash_base() { return 3129890955UL; }
bool Switch::is_inverted() const { return this->inverted_; }
std::string Switch::get_device_class() {

View File

@ -107,8 +107,6 @@ class Switch : public EntityBase {
*/
virtual void write_state(bool state) = 0;
uint32_t hash_base() override;
CallbackManager<void(bool)> state_callback_{};
bool inverted_{false};
Deduplicator<bool> publish_dedup_;

View File

@ -70,7 +70,6 @@ void TextSensor::internal_send_state_to_frontend(const std::string &state) {
std::string TextSensor::unique_id() { return ""; }
bool TextSensor::has_state() { return this->has_state_; }
uint32_t TextSensor::hash_base() { return 334300109UL; }
} // namespace text_sensor
} // namespace esphome

View File

@ -59,8 +59,6 @@ class TextSensor : public EntityBase {
void internal_send_state_to_frontend(const std::string &state);
protected:
uint32_t hash_base() override;
CallbackManager<void(std::string)> raw_callback_; ///< Storage for raw state callbacks.
CallbackManager<void(std::string)> callback_; ///< Storage for filtered state callbacks.

View File

@ -14,6 +14,7 @@ from esphome.const import (
CONF_DEFAULT_TARGET_TEMPERATURE_LOW,
CONF_DRY_ACTION,
CONF_DRY_MODE,
CONF_FAN_MODE,
CONF_FAN_MODE_ON_ACTION,
CONF_FAN_MODE_OFF_ACTION,
CONF_FAN_MODE_AUTO_ACTION,
@ -37,6 +38,7 @@ from esphome.const import (
CONF_IDLE_ACTION,
CONF_MAX_COOLING_RUN_TIME,
CONF_MAX_HEATING_RUN_TIME,
CONF_MAX_TEMPERATURE,
CONF_MIN_COOLING_OFF_TIME,
CONF_MIN_COOLING_RUN_TIME,
CONF_MIN_FAN_MODE_SWITCHING_TIME,
@ -45,7 +47,11 @@ from esphome.const import (
CONF_MIN_HEATING_OFF_TIME,
CONF_MIN_HEATING_RUN_TIME,
CONF_MIN_IDLE_TIME,
CONF_MIN_TEMPERATURE,
CONF_NAME,
CONF_MODE,
CONF_OFF_MODE,
CONF_PRESET,
CONF_SENSOR,
CONF_SET_POINT_MINIMUM_DIFFERENTIAL,
CONF_STARTUP_DELAY,
@ -55,11 +61,15 @@ from esphome.const import (
CONF_SUPPLEMENTAL_HEATING_DELTA,
CONF_SWING_BOTH_ACTION,
CONF_SWING_HORIZONTAL_ACTION,
CONF_SWING_MODE,
CONF_SWING_OFF_ACTION,
CONF_SWING_VERTICAL_ACTION,
CONF_TARGET_TEMPERATURE_CHANGE_ACTION,
CONF_VISUAL,
)
CONF_PRESET_CHANGE = "preset_change"
CODEOWNERS = ["@kbx81"]
climate_ns = cg.esphome_ns.namespace("climate")
@ -82,6 +92,38 @@ CLIMATE_MODES = {
}
validate_climate_mode = cv.enum(CLIMATE_MODES, upper=True)
ClimatePreset = climate_ns.enum("ClimatePreset")
PRESET_CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(ThermostatClimateTargetTempConfig),
cv.Required(CONF_NAME): cv.string_strict,
cv.Optional(CONF_MODE): validate_climate_mode,
cv.Optional(CONF_DEFAULT_TARGET_TEMPERATURE_HIGH): cv.temperature,
cv.Optional(CONF_DEFAULT_TARGET_TEMPERATURE_LOW): cv.temperature,
cv.Optional(CONF_FAN_MODE): cv.templatable(climate.validate_climate_fan_mode),
cv.Optional(CONF_SWING_MODE): cv.templatable(
climate.validate_climate_swing_mode
),
}
)
def validate_temperature_preset(preset, root_config, name, requirements):
# verify temperature settings for the provided preset / default / away configuration
for config_temp, req_actions in requirements.items():
for req_action in req_actions:
# verify corresponding default target temperature exists when a given climate action exists
if config_temp not in preset and req_action in root_config:
raise cv.Invalid(
f"{config_temp} must be defined in {name} config when using {req_action}"
)
# if a given climate action is NOT defined, it should not have a default target temperature
if config_temp in preset and req_action not in root_config:
raise cv.Invalid(
f"{config_temp} is defined in {name} config with no {req_action}"
)
def validate_thermostat(config):
# verify corresponding action(s) exist(s) for any defined climate mode or action
@ -235,33 +277,22 @@ def validate_thermostat(config):
CONF_DEFAULT_TARGET_TEMPERATURE_LOW: [CONF_HEAT_ACTION],
}
for config_temp, req_actions in requirements.items():
for req_action in req_actions:
# verify corresponding default target temperature exists when a given climate action exists
if config_temp not in config and req_action in config:
raise cv.Invalid(
f"{config_temp} must be defined when using {req_action}"
)
# if a given climate action is NOT defined, it should not have a default target temperature
if config_temp in config and req_action not in config:
raise cv.Invalid(f"{config_temp} is defined with no {req_action}")
# Validate temperature requirements for default configuraation
validate_temperature_preset(config, config, "default", requirements)
# Validate temperature requirements for away configuration
if CONF_AWAY_CONFIG in config:
away = config[CONF_AWAY_CONFIG]
for config_temp, req_actions in requirements.items():
for req_action in req_actions:
# verify corresponding default target temperature exists when a given climate action exists
if config_temp not in away and req_action in config:
raise cv.Invalid(
f"{config_temp} must be defined in away configuration when using {req_action}"
)
# if a given climate action is NOT defined, it should not have a default target temperature
if config_temp in away and req_action not in config:
raise cv.Invalid(
f"{config_temp} is defined in away configuration with no {req_action}"
)
validate_temperature_preset(away, config, "away", requirements)
# verify default climate mode is valid given above configuration
# Validate temperature requirements for presets
if CONF_PRESET in config:
for preset_config in config[CONF_PRESET]:
validate_temperature_preset(
preset_config, config, preset_config[CONF_NAME], requirements
)
# Verify default climate mode is valid given above configuration
default_mode = config[CONF_DEFAULT_MODE]
requirements = {
"HEAT_COOL": [CONF_COOL_ACTION, CONF_HEAT_ACTION],
@ -270,13 +301,108 @@ def validate_thermostat(config):
"DRY": [CONF_DRY_ACTION],
"FAN_ONLY": [CONF_FAN_ONLY_ACTION],
"AUTO": [CONF_COOL_ACTION, CONF_HEAT_ACTION],
}.get(default_mode, [])
for req in requirements:
"OFF": [],
}
actions_for_default_mode = requirements.get(default_mode, [])
for req in actions_for_default_mode:
if req not in config:
raise cv.Invalid(
f"{CONF_DEFAULT_MODE} is set to {default_mode} but {req} is not present in the configuration"
)
# Verify that the modes for presets are valid given the configuration
if CONF_PRESET in config:
# Preset temperature vs Visual temperature validation
# Default visual configuration from climate_traits.h
visual_min_temperature = 10.0
visual_max_temperature = 30.0
if CONF_VISUAL in config:
visual_config = config[CONF_VISUAL]
if CONF_MIN_TEMPERATURE in visual_config:
visual_min_temperature = visual_config[CONF_MIN_TEMPERATURE]
if CONF_MAX_TEMPERATURE in visual_config:
visual_max_temperature = visual_config[CONF_MAX_TEMPERATURE]
for preset_config in config[CONF_PRESET]:
if CONF_DEFAULT_TARGET_TEMPERATURE_LOW in preset_config:
preset_min_temperature = preset_config[
CONF_DEFAULT_TARGET_TEMPERATURE_LOW
]
if preset_min_temperature < visual_min_temperature:
raise cv.Invalid(
f"{CONF_DEFAULT_TARGET_TEMPERATURE_LOW} for {preset_config[CONF_NAME]} is set to {preset_min_temperature} which is less than the visual minimum temperature of {visual_min_temperature}"
)
if CONF_DEFAULT_TARGET_TEMPERATURE_HIGH in preset_config:
preset_max_temperature = preset_config[
CONF_DEFAULT_TARGET_TEMPERATURE_HIGH
]
if preset_max_temperature > visual_max_temperature:
raise cv.Invalid(
f"{CONF_DEFAULT_TARGET_TEMPERATURE_HIGH} for {preset_config[CONF_NAME]} is set to {preset_max_temperature} which is more than the visual maximum temperature of {visual_max_temperature}"
)
# Mode validation
for preset_config in config[CONF_PRESET]:
if CONF_MODE not in preset_config:
continue
mode = preset_config[CONF_MODE]
for req in requirements[mode]:
if req not in config:
raise cv.Invalid(
f"{CONF_MODE} is set to {mode} for {preset_config[CONF_NAME]} but {req} is not present in the configuration"
)
# Fan mode requirements
requirements = {
"ON": [CONF_FAN_MODE_ON_ACTION],
"OFF": [CONF_FAN_MODE_OFF_ACTION],
"AUTO": [CONF_FAN_MODE_AUTO_ACTION],
"LOW": [CONF_FAN_MODE_LOW_ACTION],
"MEDIUM": [CONF_FAN_MODE_MEDIUM_ACTION],
"HIGH": [CONF_FAN_MODE_HIGH_ACTION],
"MIDDLE": [CONF_FAN_MODE_MIDDLE_ACTION],
"FOCUS": [CONF_FAN_MODE_FOCUS_ACTION],
"DIFFUSE": [CONF_FAN_MODE_DIFFUSE_ACTION],
}
for preset_config in config[CONF_PRESET]:
if CONF_FAN_MODE not in preset_config:
continue
fan_mode = preset_config[CONF_FAN_MODE]
for req in requirements[fan_mode]:
if req not in config:
raise cv.Invalid(
f"{CONF_FAN_MODE} is set to {fan_mode} for {preset_config[CONF_NAME]} but {req} is not present in the configuration"
)
# Swing mode requirements
requirements = {
"OFF": [CONF_SWING_OFF_ACTION],
"BOTH": [CONF_SWING_BOTH_ACTION],
"VERTICAL": [CONF_SWING_VERTICAL_ACTION],
"HORIZONTAL": [CONF_SWING_HORIZONTAL_ACTION],
}
for preset_config in config[CONF_PRESET]:
if CONF_SWING_MODE not in preset_config:
continue
swing_mode = preset_config[CONF_SWING_MODE]
for req in requirements[swing_mode]:
if req not in config:
raise cv.Invalid(
f"{CONF_SWING_MODE} is set to {swing_mode} for {preset_config[CONF_NAME]} but {req} is not present in the configuration"
)
if config[CONF_FAN_WITH_COOLING] is True and CONF_FAN_ONLY_ACTION not in config:
raise cv.Invalid(
f"{CONF_FAN_ONLY_ACTION} must be defined to use {CONF_FAN_WITH_COOLING}"
@ -415,6 +541,10 @@ CONFIG_SCHEMA = cv.All(
cv.Optional(CONF_DEFAULT_TARGET_TEMPERATURE_LOW): cv.temperature,
}
),
cv.Optional(CONF_PRESET): cv.ensure_list(PRESET_CONFIG_SCHEMA),
cv.Optional(CONF_PRESET_CHANGE): automation.validate_automation(
single=True
),
}
).extend(cv.COMPONENT_SCHEMA),
cv.has_at_least_one_key(
@ -531,7 +661,7 @@ async def to_code(config):
cg.add(var.set_supports_fan_with_heating(config[CONF_FAN_WITH_HEATING]))
cg.add(var.set_use_startup_delay(config[CONF_STARTUP_DELAY]))
cg.add(var.set_normal_config(normal_config))
cg.add(var.set_preset_config(ClimatePreset.CLIMATE_PRESET_HOME, normal_config))
await automation.build_automation(
var.get_idle_action_trigger(), [], config[CONF_IDLE_ACTION]
@ -694,4 +824,55 @@ async def to_code(config):
away_config = ThermostatClimateTargetTempConfig(
away[CONF_DEFAULT_TARGET_TEMPERATURE_LOW]
)
cg.add(var.set_away_config(away_config))
cg.add(var.set_preset_config(ClimatePreset.CLIMATE_PRESET_AWAY, away_config))
if CONF_PRESET in config:
for preset_config in config[CONF_PRESET]:
name = preset_config[CONF_NAME]
standard_preset = None
if name.upper() in climate.CLIMATE_PRESETS:
standard_preset = climate.CLIMATE_PRESETS[name.upper()]
if two_points_available is True:
preset_target_config = ThermostatClimateTargetTempConfig(
preset_config[CONF_DEFAULT_TARGET_TEMPERATURE_LOW],
preset_config[CONF_DEFAULT_TARGET_TEMPERATURE_HIGH],
)
elif CONF_DEFAULT_TARGET_TEMPERATURE_HIGH in preset_config:
preset_target_config = ThermostatClimateTargetTempConfig(
preset_config[CONF_DEFAULT_TARGET_TEMPERATURE_HIGH]
)
elif CONF_DEFAULT_TARGET_TEMPERATURE_LOW in preset_config:
preset_target_config = ThermostatClimateTargetTempConfig(
preset_config[CONF_DEFAULT_TARGET_TEMPERATURE_LOW]
)
preset_target_variable = cg.new_variable(
preset_config[CONF_ID], preset_target_config
)
if CONF_MODE in preset_config:
cg.add(preset_target_variable.set_mode(preset_config[CONF_MODE]))
if CONF_FAN_MODE in preset_config:
cg.add(
preset_target_variable.set_fan_mode(preset_config[CONF_FAN_MODE])
)
if CONF_SWING_MODE in preset_config:
cg.add(
preset_target_variable.set_swing_mode(
preset_config[CONF_SWING_MODE]
)
)
if standard_preset is not None:
cg.add(var.set_preset_config(standard_preset, preset_target_variable))
else:
cg.add(var.set_custom_preset_config(name, preset_target_variable))
if CONF_PRESET_CHANGE in config:
await automation.build_automation(
var.get_preset_change_trigger(), [], config[CONF_PRESET_CHANGE]
)

View File

@ -32,7 +32,7 @@ void ThermostatClimate::setup() {
} else {
// restore from defaults, change_away handles temps for us
this->mode = this->default_mode_;
this->change_away_(false);
this->change_preset_(climate::CLIMATE_PRESET_HOME);
}
// refresh the climate action based on the restored settings, we'll publish_state() later
this->switch_to_action_(this->compute_action_(), false);
@ -162,11 +162,20 @@ void ThermostatClimate::control(const climate::ClimateCall &call) {
if (call.get_preset().has_value()) {
// setup_complete_ blocks modifying/resetting the temps immediately after boot
if (this->setup_complete_) {
this->change_away_(*call.get_preset() == climate::CLIMATE_PRESET_AWAY);
this->change_preset_(*call.get_preset());
} else {
this->preset = *call.get_preset();
}
}
if (call.get_custom_preset().has_value()) {
// setup_complete_ blocks modifying/resetting the temps immediately after boot
if (this->setup_complete_) {
this->change_custom_preset_(*call.get_custom_preset());
} else {
this->custom_preset = *call.get_custom_preset();
}
}
if (call.get_mode().has_value())
this->mode = *call.get_mode();
if (call.get_fan_mode().has_value())
@ -236,8 +245,12 @@ climate::ClimateTraits ThermostatClimate::traits() {
if (supports_swing_mode_vertical_)
traits.add_supported_swing_mode(climate::CLIMATE_SWING_VERTICAL);
if (supports_away_)
traits.set_supported_presets({climate::CLIMATE_PRESET_HOME, climate::CLIMATE_PRESET_AWAY});
for (auto &it : this->preset_config_) {
traits.add_supported_preset(it.first);
}
for (auto &it : this->custom_preset_config_) {
traits.add_supported_custom_preset(it.first);
}
traits.set_supports_two_point_target_temperature(this->supports_two_points_);
traits.set_supports_action(true);
@ -910,30 +923,112 @@ bool ThermostatClimate::supplemental_heating_required_() {
(this->supplemental_action_ == climate::CLIMATE_ACTION_HEATING));
}
void ThermostatClimate::change_away_(bool away) {
if (!away) {
void ThermostatClimate::dump_preset_config_(const std::string &preset,
const ThermostatClimateTargetTempConfig &config) {
const auto *preset_name = preset.c_str();
if (this->supports_heat_) {
if (this->supports_two_points_) {
this->target_temperature_low = this->normal_config_.default_temperature_low;
this->target_temperature_high = this->normal_config_.default_temperature_high;
} else
this->target_temperature = this->normal_config_.default_temperature;
} else {
if (this->supports_two_points_) {
this->target_temperature_low = this->away_config_.default_temperature_low;
this->target_temperature_high = this->away_config_.default_temperature_high;
} else
this->target_temperature = this->away_config_.default_temperature;
ESP_LOGCONFIG(TAG, " %s Default Target Temperature Low: %.1f°C", preset_name,
config.default_temperature_low);
} else {
ESP_LOGCONFIG(TAG, " %s Default Target Temperature Low: %.1f°C", preset_name, config.default_temperature);
}
}
if ((this->supports_cool_) || (this->supports_fan_only_)) {
if (this->supports_two_points_) {
ESP_LOGCONFIG(TAG, " %s Default Target Temperature High: %.1f°C", preset_name,
config.default_temperature_high);
} else {
ESP_LOGCONFIG(TAG, " %s Default Target Temperature High: %.1f°C", preset_name, config.default_temperature);
}
}
if (config.mode_.has_value()) {
ESP_LOGCONFIG(TAG, " %s Default Mode: %s", preset_name,
LOG_STR_ARG(climate::climate_mode_to_string(*config.mode_)));
}
if (config.fan_mode_.has_value()) {
ESP_LOGCONFIG(TAG, " %s Default Fan Mode: %s", preset_name,
LOG_STR_ARG(climate::climate_fan_mode_to_string(*config.fan_mode_)));
}
if (config.swing_mode_.has_value()) {
ESP_LOGCONFIG(TAG, " %s Default Swing Mode: %s", preset_name,
LOG_STR_ARG(climate::climate_swing_mode_to_string(*config.swing_mode_)));
}
this->preset = away ? climate::CLIMATE_PRESET_AWAY : climate::CLIMATE_PRESET_HOME;
}
void ThermostatClimate::set_normal_config(const ThermostatClimateTargetTempConfig &normal_config) {
this->normal_config_ = normal_config;
void ThermostatClimate::change_preset_(climate::ClimatePreset preset) {
auto config = this->preset_config_.find(preset);
if (config != this->preset_config_.end()) {
ESP_LOGI(TAG, "Switching to preset %s", LOG_STR_ARG(climate::climate_preset_to_string(preset)));
this->change_preset_internal_(config->second);
this->custom_preset.reset();
this->preset = preset;
} else {
ESP_LOGE(TAG, "Preset %s is not configured, ignoring.", LOG_STR_ARG(climate::climate_preset_to_string(preset)));
}
}
void ThermostatClimate::set_away_config(const ThermostatClimateTargetTempConfig &away_config) {
this->supports_away_ = true;
this->away_config_ = away_config;
void ThermostatClimate::change_custom_preset_(const std::string &custom_preset) {
auto config = this->custom_preset_config_.find(custom_preset);
if (config != this->custom_preset_config_.end()) {
ESP_LOGI(TAG, "Switching to custom preset %s", custom_preset.c_str());
this->change_preset_internal_(config->second);
this->preset.reset();
this->custom_preset = custom_preset;
} else {
ESP_LOGE(TAG, "Custom Preset %s is not configured, ignoring.", custom_preset.c_str());
}
}
void ThermostatClimate::change_preset_internal_(const ThermostatClimateTargetTempConfig &config) {
if (this->supports_two_points_) {
this->target_temperature_low = config.default_temperature_low;
this->target_temperature_high = config.default_temperature_high;
} else {
this->target_temperature = config.default_temperature;
}
// Note: The mode, fan_mode, and swing_mode can all be defined on the preset but if the climate.control call
// also specifies them then the control's version will override these for that call
if (config.mode_.has_value()) {
this->mode = *config.mode_;
ESP_LOGV(TAG, "Setting mode to %s", LOG_STR_ARG(climate::climate_mode_to_string(*config.mode_)));
}
if (config.fan_mode_.has_value()) {
this->fan_mode = *config.fan_mode_;
ESP_LOGV(TAG, "Setting fan mode to %s", LOG_STR_ARG(climate::climate_fan_mode_to_string(*config.fan_mode_)));
}
if (config.swing_mode_.has_value()) {
ESP_LOGV(TAG, "Setting swing mode to %s", LOG_STR_ARG(climate::climate_swing_mode_to_string(*config.swing_mode_)));
this->swing_mode = *config.swing_mode_;
}
// Fire any preset changed trigger if defined
if (this->preset != preset) {
Trigger<> *trig = this->preset_change_trigger_;
assert(trig != nullptr);
trig->trigger();
}
this->refresh();
}
void ThermostatClimate::set_preset_config(climate::ClimatePreset preset,
const ThermostatClimateTargetTempConfig &config) {
this->preset_config_[preset] = config;
}
void ThermostatClimate::set_custom_preset_config(const std::string &name,
const ThermostatClimateTargetTempConfig &config) {
this->custom_preset_config_[name] = config;
}
ThermostatClimate::ThermostatClimate()
@ -963,7 +1058,8 @@ ThermostatClimate::ThermostatClimate()
swing_mode_off_trigger_(new Trigger<>()),
swing_mode_horizontal_trigger_(new Trigger<>()),
swing_mode_vertical_trigger_(new Trigger<>()),
temperature_change_trigger_(new Trigger<>()) {}
temperature_change_trigger_(new Trigger<>()),
preset_change_trigger_(new Trigger<>()) {}
void ThermostatClimate::set_default_mode(climate::ClimateMode default_mode) { this->default_mode_ = default_mode; }
void ThermostatClimate::set_set_point_minimum_differential(float differential) {
@ -1112,23 +1208,11 @@ Trigger<> *ThermostatClimate::get_swing_mode_off_trigger() const { return this->
Trigger<> *ThermostatClimate::get_swing_mode_horizontal_trigger() const { return this->swing_mode_horizontal_trigger_; }
Trigger<> *ThermostatClimate::get_swing_mode_vertical_trigger() const { return this->swing_mode_vertical_trigger_; }
Trigger<> *ThermostatClimate::get_temperature_change_trigger() const { return this->temperature_change_trigger_; }
Trigger<> *ThermostatClimate::get_preset_change_trigger() const { return this->preset_change_trigger_; }
void ThermostatClimate::dump_config() {
LOG_CLIMATE("", "Thermostat", this);
if (this->supports_heat_) {
if (this->supports_two_points_) {
ESP_LOGCONFIG(TAG, " Default Target Temperature Low: %.1f°C", this->normal_config_.default_temperature_low);
} else {
ESP_LOGCONFIG(TAG, " Default Target Temperature Low: %.1f°C", this->normal_config_.default_temperature);
}
}
if ((this->supports_cool_) || (this->supports_fan_only_ && this->supports_fan_only_cooling_)) {
if (this->supports_two_points_) {
ESP_LOGCONFIG(TAG, " Default Target Temperature High: %.1f°C", this->normal_config_.default_temperature_high);
} else {
ESP_LOGCONFIG(TAG, " Default Target Temperature High: %.1f°C", this->normal_config_.default_temperature);
}
}
if (this->supports_two_points_)
ESP_LOGCONFIG(TAG, " Minimum Set Point Differential: %.1f°C", this->set_point_minimum_differential_);
ESP_LOGCONFIG(TAG, " Start-up Delay Enabled: %s", YESNO(this->use_startup_delay_));
@ -1194,24 +1278,21 @@ void ThermostatClimate::dump_config() {
ESP_LOGCONFIG(TAG, " Supports SWING MODE HORIZONTAL: %s", YESNO(this->supports_swing_mode_horizontal_));
ESP_LOGCONFIG(TAG, " Supports SWING MODE VERTICAL: %s", YESNO(this->supports_swing_mode_vertical_));
ESP_LOGCONFIG(TAG, " Supports TWO SET POINTS: %s", YESNO(this->supports_two_points_));
ESP_LOGCONFIG(TAG, " Supports AWAY mode: %s", YESNO(this->supports_away_));
if (this->supports_away_) {
if (this->supports_heat_) {
if (this->supports_two_points_) {
ESP_LOGCONFIG(TAG, " Away Default Target Temperature Low: %.1f°C",
this->away_config_.default_temperature_low);
} else {
ESP_LOGCONFIG(TAG, " Away Default Target Temperature Low: %.1f°C", this->away_config_.default_temperature);
}
}
if ((this->supports_cool_) || (this->supports_fan_only_)) {
if (this->supports_two_points_) {
ESP_LOGCONFIG(TAG, " Away Default Target Temperature High: %.1f°C",
this->away_config_.default_temperature_high);
} else {
ESP_LOGCONFIG(TAG, " Away Default Target Temperature High: %.1f°C", this->away_config_.default_temperature);
}
}
ESP_LOGCONFIG(TAG, " Supported PRESETS: ");
for (auto &it : this->preset_config_) {
const auto *preset_name = LOG_STR_ARG(climate::climate_preset_to_string(it.first));
ESP_LOGCONFIG(TAG, " Supports %s: %s", preset_name, YESNO(true));
this->dump_preset_config_(preset_name, it.second);
}
ESP_LOGCONFIG(TAG, " Supported CUSTOM PRESETS: ");
for (auto &it : this->custom_preset_config_) {
const auto *preset_name = it.first.c_str();
ESP_LOGCONFIG(TAG, " Supports %s: %s", preset_name, YESNO(true));
this->dump_preset_config_(preset_name, it.second);
}
}

View File

@ -4,6 +4,7 @@
#include "esphome/core/automation.h"
#include "esphome/components/climate/climate.h"
#include "esphome/components/sensor/sensor.h"
#include <map>
namespace esphome {
namespace thermostat {
@ -34,6 +35,10 @@ struct ThermostatClimateTargetTempConfig {
ThermostatClimateTargetTempConfig(float default_temperature);
ThermostatClimateTargetTempConfig(float default_temperature_low, float default_temperature_high);
void set_fan_mode(climate::ClimateFanMode fan_mode) { this->fan_mode_ = fan_mode; }
void set_swing_mode(climate::ClimateSwingMode swing_mode) { this->swing_mode_ = swing_mode; }
void set_mode(climate::ClimateMode mode) { this->mode_ = mode; }
float default_temperature{NAN};
float default_temperature_low{NAN};
float default_temperature_high{NAN};
@ -41,6 +46,9 @@ struct ThermostatClimateTargetTempConfig {
float cool_overrun_{NAN};
float heat_deadband_{NAN};
float heat_overrun_{NAN};
optional<climate::ClimateFanMode> fan_mode_{};
optional<climate::ClimateSwingMode> swing_mode_{};
optional<climate::ClimateMode> mode_{};
};
class ThermostatClimate : public climate::Climate, public Component {
@ -94,8 +102,8 @@ class ThermostatClimate : public climate::Climate, public Component {
void set_supports_swing_mode_vertical(bool supports_swing_mode_vertical);
void set_supports_two_points(bool supports_two_points);
void set_normal_config(const ThermostatClimateTargetTempConfig &normal_config);
void set_away_config(const ThermostatClimateTargetTempConfig &away_config);
void set_preset_config(climate::ClimatePreset preset, const ThermostatClimateTargetTempConfig &config);
void set_custom_preset_config(const std::string &name, const ThermostatClimateTargetTempConfig &config);
Trigger<> *get_cool_action_trigger() const;
Trigger<> *get_supplemental_cool_action_trigger() const;
@ -124,6 +132,7 @@ class ThermostatClimate : public climate::Climate, public Component {
Trigger<> *get_swing_mode_off_trigger() const;
Trigger<> *get_swing_mode_vertical_trigger() const;
Trigger<> *get_temperature_change_trigger() const;
Trigger<> *get_preset_change_trigger() const;
/// Get current hysteresis values
float cool_deadband();
float cool_overrun();
@ -149,8 +158,14 @@ class ThermostatClimate : public climate::Climate, public Component {
/// Override control to change settings of the climate device.
void control(const climate::ClimateCall &call) override;
/// Change the away setting, will reset target temperatures to defaults.
void change_away_(bool away);
/// Change to a provided preset setting; will reset temperature, mode, fan, and swing modes accordingly
void change_preset_(climate::ClimatePreset preset);
/// Change to a provided custom preset setting; will reset temperature, mode, fan, and swing modes accordingly
void change_custom_preset_(const std::string &custom_preset);
/// Applies the temperature, mode, fan, and swing modes of the provided config.
/// This is agnostic of custom vs built in preset
void change_preset_internal_(const ThermostatClimateTargetTempConfig &config);
/// Return the traits of this controller.
climate::ClimateTraits traits() override;
@ -210,6 +225,8 @@ class ThermostatClimate : public climate::Climate, public Component {
bool supplemental_cooling_required_();
bool supplemental_heating_required_();
void dump_preset_config_(const std::string &preset_name, const ThermostatClimateTargetTempConfig &config);
/// The sensor used for getting the current temperature
sensor::Sensor *sensor_{nullptr};
@ -267,11 +284,6 @@ class ThermostatClimate : public climate::Climate, public Component {
/// A false value means that the controller has no such support.
bool supports_two_points_{false};
/// Whether the controller supports an "away" mode
///
/// A false value means that the controller has no such mode.
bool supports_away_{false};
/// Flags indicating if maximum allowable run time was exceeded
bool cooling_max_runtime_exceeded_{false};
bool heating_max_runtime_exceeded_{false};
@ -368,6 +380,9 @@ class ThermostatClimate : public climate::Climate, public Component {
/// The trigger to call when the target temperature(s) change(es).
Trigger<> *temperature_change_trigger_{nullptr};
/// The triggr to call when the preset mode changes
Trigger<> *preset_change_trigger_{nullptr};
/// A reference to the trigger that was previously active.
///
/// This is so that the previous trigger can be stopped before enabling a new one
@ -409,10 +424,6 @@ class ThermostatClimate : public climate::Climate, public Component {
/// Minimum allowable duration in seconds for action timers
const uint8_t min_timer_duration_{1};
/// Temperature data for normal/home and away modes
ThermostatClimateTargetTempConfig normal_config_{};
ThermostatClimateTargetTempConfig away_config_{};
/// Climate action timers
std::vector<ThermostatClimateTimer> timer_{
{"cool_run", false, 0, std::bind(&ThermostatClimate::cooling_max_run_time_timer_callback_, this)},
@ -425,6 +436,11 @@ class ThermostatClimate : public climate::Climate, public Component {
{"heat_off", false, 0, std::bind(&ThermostatClimate::heating_off_timer_callback_, this)},
{"heat_on", false, 0, std::bind(&ThermostatClimate::heating_on_timer_callback_, this)},
{"idle_on", false, 0, std::bind(&ThermostatClimate::idle_on_timer_callback_, this)}};
/// The set of standard preset configurations this thermostat supports (Eg. AWAY, ECO, etc)
std::map<climate::ClimatePreset, ThermostatClimateTargetTempConfig> preset_config_{};
/// The set of custom preset configurations this thermostat supports (eg. "My Custom Preset")
std::map<std::string, ThermostatClimateTargetTempConfig> custom_preset_config_{};
};
} // namespace thermostat

View File

@ -126,10 +126,10 @@ def _parse_cron_part(part, min_value, max_value, special_mapping):
)
begin, end = data
begin_n = _parse_cron_int(
begin, special_mapping, "Number for time range must be integer, " "got {}"
begin, special_mapping, "Number for time range must be integer, got {}"
)
end_n = _parse_cron_int(
end, special_mapping, "Number for time range must be integer, " "got {}"
end, special_mapping, "Number for time range must be integer, got {}"
)
if end_n < begin_n:
return set(range(end_n, max_value + 1)) | set(range(min_value, begin_n + 1))
@ -139,7 +139,7 @@ def _parse_cron_part(part, min_value, max_value, special_mapping):
_parse_cron_int(
part,
special_mapping,
"Number for time expression must be an " "integer, got {}",
"Number for time expression must be an integer, got {}",
)
}

View File

@ -17,7 +17,6 @@ from esphome.core.entity_helpers import inherit_property_from
DEPENDENCIES = ["time"]
CONF_POWER_ID = "power_id"
CONF_MIN_SAVE_INTERVAL = "min_save_interval"
total_daily_energy_ns = cg.esphome_ns.namespace("total_daily_energy")
TotalDailyEnergyMethod = total_daily_energy_ns.enum("TotalDailyEnergyMethod")
TOTAL_DAILY_ENERGY_METHODS = {
@ -49,9 +48,9 @@ CONFIG_SCHEMA = (
cv.GenerateID(CONF_TIME_ID): cv.use_id(time.RealTimeClock),
cv.Required(CONF_POWER_ID): cv.use_id(sensor.Sensor),
cv.Optional(CONF_RESTORE, default=True): cv.boolean,
cv.Optional(
CONF_MIN_SAVE_INTERVAL, default="0s"
): cv.positive_time_period_milliseconds,
cv.Optional("min_save_interval"): cv.invalid(
"`min_save_interval` was removed in 2022.6.0. Please use the `preferences` -> `flash_write_interval` to adjust."
),
cv.Optional(CONF_METHOD, default="right"): cv.enum(
TOTAL_DAILY_ENERGY_METHODS, lower=True
),
@ -90,5 +89,4 @@ async def to_code(config):
time_ = await cg.get_variable(config[CONF_TIME_ID])
cg.add(var.set_time(time_))
cg.add(var.set_restore(config[CONF_RESTORE]))
cg.add(var.set_min_save_interval(config[CONF_MIN_SAVE_INTERVAL]))
cg.add(var.set_method(config[CONF_METHOD]))

View File

@ -16,7 +16,6 @@ void TotalDailyEnergy::setup() {
this->publish_state_and_save(initial_value);
this->last_update_ = millis();
this->last_save_ = this->last_update_;
this->parent_->add_on_state_callback([this](float state) { this->process_new_state_(state); });
}
@ -43,12 +42,9 @@ void TotalDailyEnergy::loop() {
void TotalDailyEnergy::publish_state_and_save(float state) {
this->total_energy_ = state;
this->publish_state(state);
const uint32_t now = millis();
if (now - this->last_save_ < this->min_save_interval_) {
return;
if (this->restore_) {
this->pref_.save(&state);
}
this->last_save_ = now;
this->pref_.save(&state);
}
void TotalDailyEnergy::process_new_state_(float state) {

View File

@ -18,7 +18,6 @@ enum TotalDailyEnergyMethod {
class TotalDailyEnergy : public sensor::Sensor, public Component {
public:
void set_restore(bool restore) { restore_ = restore; }
void set_min_save_interval(uint32_t min_interval) { this->min_save_interval_ = min_interval; }
void set_time(time::RealTimeClock *time) { time_ = time; }
void set_parent(Sensor *parent) { parent_ = parent; }
void set_method(TotalDailyEnergyMethod method) { method_ = method; }
@ -39,7 +38,6 @@ class TotalDailyEnergy : public sensor::Sensor, public Component {
uint16_t last_day_of_year_{};
uint32_t last_update_{0};
uint32_t last_save_{0};
uint32_t min_save_interval_{0};
bool restore_;
float total_energy_{0.0f};
float last_power_state_{0.0f};

View File

@ -10,6 +10,11 @@ static const char *const TAG = "tuya.light";
void TuyaLight::setup() {
if (this->color_temperature_id_.has_value()) {
this->parent_->register_listener(*this->color_temperature_id_, [this](const TuyaDatapoint &datapoint) {
if (this->state_->current_values != this->state_->remote_values) {
ESP_LOGD(TAG, "Light is transitioning, datapoint change ignored");
return;
}
auto datapoint_value = datapoint.value_uint;
if (this->color_temperature_invert_) {
datapoint_value = this->color_temperature_max_value_ - datapoint_value;
@ -23,6 +28,11 @@ void TuyaLight::setup() {
}
if (this->dimmer_id_.has_value()) {
this->parent_->register_listener(*this->dimmer_id_, [this](const TuyaDatapoint &datapoint) {
if (this->state_->current_values != this->state_->remote_values) {
ESP_LOGD(TAG, "Light is transitioning, datapoint change ignored");
return;
}
auto call = this->state_->make_call();
call.set_brightness(float(datapoint.value_uint) / this->max_value_);
call.perform();
@ -30,6 +40,11 @@ void TuyaLight::setup() {
}
if (switch_id_.has_value()) {
this->parent_->register_listener(*this->switch_id_, [this](const TuyaDatapoint &datapoint) {
if (this->state_->current_values != this->state_->remote_values) {
ESP_LOGD(TAG, "Light is transitioning, datapoint change ignored");
return;
}
auto call = this->state_->make_call();
call.set_state(datapoint.value_bool);
call.perform();
@ -41,6 +56,11 @@ void TuyaLight::setup() {
auto green = parse_hex<uint8_t>(datapoint.value_string.substr(2, 2));
auto blue = parse_hex<uint8_t>(datapoint.value_string.substr(4, 2));
if (red.has_value() && green.has_value() && blue.has_value()) {
if (this->state_->current_values != this->state_->remote_values) {
ESP_LOGD(TAG, "Light is transitioning, datapoint change ignored");
return;
}
auto call = this->state_->make_call();
call.set_rgb(float(*red) / 255, float(*green) / 255, float(*blue) / 255);
call.perform();
@ -52,6 +72,11 @@ void TuyaLight::setup() {
auto saturation = parse_hex<uint16_t>(datapoint.value_string.substr(4, 4));
auto value = parse_hex<uint16_t>(datapoint.value_string.substr(8, 4));
if (hue.has_value() && saturation.has_value() && value.has_value()) {
if (this->state_->current_values != this->state_->remote_values) {
ESP_LOGD(TAG, "Light is transitioning, datapoint change ignored");
return;
}
float red, green, blue;
hsv_to_rgb(*hue, float(*saturation) / 1000, float(*value) / 1000, red, green, blue);
auto call = this->state_->make_call();

View File

@ -1,7 +1,7 @@
#include "tuya.h"
#include "esphome/core/log.h"
#include "esphome/components/network/util.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include "esphome/core/util.h"
#include "esphome/core/gpio.h"

View File

@ -38,7 +38,7 @@ class UARTDebugger : public Component, public Trigger<UARTDirection, std::vector
/// Add a delimiter byte. This can be called multiple times to setup a
/// multi-byte delimiter (a typical example would be '\r\n').
/// When the constructued byte sequence is found in the data stream,
/// When the constructed byte sequence is found in the data stream,
/// logging will be triggered.
void add_delimiter_byte(uint8_t byte) { this->after_delimiter_.push_back(byte); }

View File

@ -257,6 +257,7 @@ void VL53L0XSensor::setup() {
ESP_LOGD(TAG, "'%s' - setup END", this->name_.c_str());
}
void VL53L0XSensor::update() {
if (this->initiated_read_ || this->waiting_for_interrupt_) {
this->publish_state(NAN);
@ -280,6 +281,7 @@ void VL53L0XSensor::update() {
this->initiated_read_ = true;
// wait for timeout
}
void VL53L0XSensor::loop() {
if (this->initiated_read_) {
if (reg(0x00).get() & 0x01) {
@ -311,5 +313,222 @@ void VL53L0XSensor::loop() {
}
}
uint32_t VL53L0XSensor::get_measurement_timing_budget_() {
SequenceStepEnables enables{};
SequenceStepTimeouts timeouts{};
uint16_t start_overhead = 1910;
uint16_t end_overhead = 960;
uint16_t msrc_overhead = 660;
uint16_t tcc_overhead = 590;
uint16_t dss_overhead = 690;
uint16_t pre_range_overhead = 660;
uint16_t final_range_overhead = 550;
// "Start and end overhead times always present"
uint32_t budget_us = start_overhead + end_overhead;
get_sequence_step_enables_(&enables);
get_sequence_step_timeouts_(&enables, &timeouts);
if (enables.tcc)
budget_us += (timeouts.msrc_dss_tcc_us + tcc_overhead);
if (enables.dss) {
budget_us += 2 * (timeouts.msrc_dss_tcc_us + dss_overhead);
} else if (enables.msrc) {
budget_us += (timeouts.msrc_dss_tcc_us + msrc_overhead);
}
if (enables.pre_range)
budget_us += (timeouts.pre_range_us + pre_range_overhead);
if (enables.final_range)
budget_us += (timeouts.final_range_us + final_range_overhead);
measurement_timing_budget_us_ = budget_us; // store for internal reuse
return budget_us;
}
bool VL53L0XSensor::set_measurement_timing_budget_(uint32_t budget_us) {
SequenceStepEnables enables{};
SequenceStepTimeouts timeouts{};
uint16_t start_overhead = 1320; // note that this is different than the value in get_
uint16_t end_overhead = 960;
uint16_t msrc_overhead = 660;
uint16_t tcc_overhead = 590;
uint16_t dss_overhead = 690;
uint16_t pre_range_overhead = 660;
uint16_t final_range_overhead = 550;
uint32_t min_timing_budget = 20000;
if (budget_us < min_timing_budget) {
return false;
}
uint32_t used_budget_us = start_overhead + end_overhead;
get_sequence_step_enables_(&enables);
get_sequence_step_timeouts_(&enables, &timeouts);
if (enables.tcc) {
used_budget_us += (timeouts.msrc_dss_tcc_us + tcc_overhead);
}
if (enables.dss) {
used_budget_us += 2 * (timeouts.msrc_dss_tcc_us + dss_overhead);
} else if (enables.msrc) {
used_budget_us += (timeouts.msrc_dss_tcc_us + msrc_overhead);
}
if (enables.pre_range) {
used_budget_us += (timeouts.pre_range_us + pre_range_overhead);
}
if (enables.final_range) {
used_budget_us += final_range_overhead;
// "Note that the final range timeout is determined by the timing
// budget and the sum of all other timeouts within the sequence.
// If there is no room for the final range timeout, then an error
// will be set. Otherwise the remaining time will be applied to
// the final range."
if (used_budget_us > budget_us) {
// "Requested timeout too big."
return false;
}
uint32_t final_range_timeout_us = budget_us - used_budget_us;
// set_sequence_step_timeout() begin
// (SequenceStepId == VL53L0X_SEQUENCESTEP_FINAL_RANGE)
// "For the final range timeout, the pre-range timeout
// must be added. To do this both final and pre-range
// timeouts must be expressed in macro periods MClks
// because they have different vcsel periods."
uint16_t final_range_timeout_mclks =
timeout_microseconds_to_mclks_(final_range_timeout_us, timeouts.final_range_vcsel_period_pclks);
if (enables.pre_range) {
final_range_timeout_mclks += timeouts.pre_range_mclks;
}
write_byte_16(0x71, encode_timeout_(final_range_timeout_mclks));
// set_sequence_step_timeout() end
measurement_timing_budget_us_ = budget_us; // store for internal reuse
}
return true;
}
void VL53L0XSensor::get_sequence_step_enables_(SequenceStepEnables *enables) {
uint8_t sequence_config = reg(0x01).get();
enables->tcc = (sequence_config >> 4) & 0x1;
enables->dss = (sequence_config >> 3) & 0x1;
enables->msrc = (sequence_config >> 2) & 0x1;
enables->pre_range = (sequence_config >> 6) & 0x1;
enables->final_range = (sequence_config >> 7) & 0x1;
}
void VL53L0XSensor::get_sequence_step_timeouts_(SequenceStepEnables const *enables, SequenceStepTimeouts *timeouts) {
timeouts->pre_range_vcsel_period_pclks = get_vcsel_pulse_period_(VCSEL_PERIOD_PRE_RANGE);
timeouts->msrc_dss_tcc_mclks = reg(0x46).get() + 1;
timeouts->msrc_dss_tcc_us =
timeout_mclks_to_microseconds_(timeouts->msrc_dss_tcc_mclks, timeouts->pre_range_vcsel_period_pclks);
uint16_t value;
read_byte_16(0x51, &value);
timeouts->pre_range_mclks = decode_timeout_(value);
timeouts->pre_range_us =
timeout_mclks_to_microseconds_(timeouts->pre_range_mclks, timeouts->pre_range_vcsel_period_pclks);
timeouts->final_range_vcsel_period_pclks = get_vcsel_pulse_period_(VCSEL_PERIOD_FINAL_RANGE);
read_byte_16(0x71, &value);
timeouts->final_range_mclks = decode_timeout_(value);
if (enables->pre_range) {
timeouts->final_range_mclks -= timeouts->pre_range_mclks;
}
timeouts->final_range_us =
timeout_mclks_to_microseconds_(timeouts->final_range_mclks, timeouts->final_range_vcsel_period_pclks);
}
uint8_t VL53L0XSensor::get_vcsel_pulse_period_(VcselPeriodType type) {
uint8_t vcsel;
if (type == VCSEL_PERIOD_PRE_RANGE) {
vcsel = reg(0x50).get();
} else if (type == VCSEL_PERIOD_FINAL_RANGE) {
vcsel = reg(0x70).get();
} else {
return 255;
}
return (vcsel + 1) << 1;
}
uint32_t VL53L0XSensor::get_macro_period_(uint8_t vcsel_period_pclks) {
return ((2304UL * vcsel_period_pclks * 1655UL) + 500UL) / 1000UL;
}
uint32_t VL53L0XSensor::timeout_mclks_to_microseconds_(uint16_t timeout_period_mclks, uint8_t vcsel_period_pclks) {
uint32_t macro_period_ns = get_macro_period_(vcsel_period_pclks);
return ((timeout_period_mclks * macro_period_ns) + (macro_period_ns / 2)) / 1000;
}
uint32_t VL53L0XSensor::timeout_microseconds_to_mclks_(uint32_t timeout_period_us, uint8_t vcsel_period_pclks) {
uint32_t macro_period_ns = get_macro_period_(vcsel_period_pclks);
return (((timeout_period_us * 1000) + (macro_period_ns / 2)) / macro_period_ns);
}
uint16_t VL53L0XSensor::decode_timeout_(uint16_t reg_val) {
// format: "(LSByte * 2^MSByte) + 1"
uint8_t msb = (reg_val >> 8) & 0xFF;
uint8_t lsb = (reg_val >> 0) & 0xFF;
return (uint16_t(lsb) << msb) + 1;
}
uint16_t VL53L0XSensor::encode_timeout_(uint16_t timeout_mclks) {
// format: "(LSByte * 2^MSByte) + 1"
uint32_t ls_byte = 0;
uint16_t ms_byte = 0;
if (timeout_mclks <= 0)
return 0;
ls_byte = timeout_mclks - 1;
while ((ls_byte & 0xFFFFFF00) > 0) {
ls_byte >>= 1;
ms_byte++;
}
return (ms_byte << 8) | (ls_byte & 0xFF);
}
bool VL53L0XSensor::perform_single_ref_calibration_(uint8_t vhv_init_byte) {
reg(0x00) = 0x01 | vhv_init_byte; // VL53L0X_REG_SYSRANGE_MODE_START_STOP
uint32_t start = millis();
while ((reg(0x13).get() & 0x07) == 0) {
if (millis() - start > 1000)
return false;
yield();
}
reg(0x0B) = 0x01;
reg(0x00) = 0x00;
return true;
}
} // namespace vl53l0x
} // namespace esphome

View File

@ -21,6 +21,8 @@ struct SequenceStepTimeouts {
uint32_t msrc_dss_tcc_us, pre_range_us, final_range_us;
};
enum VcselPeriodType { VCSEL_PERIOD_PRE_RANGE, VCSEL_PERIOD_FINAL_RANGE };
class VL53L0XSensor : public sensor::Sensor, public PollingComponent, public i2c::I2CDevice {
public:
VL53L0XSensor();
@ -39,222 +41,20 @@ class VL53L0XSensor : public sensor::Sensor, public PollingComponent, public i2c
void set_enable_pin(GPIOPin *enable) { this->enable_pin_ = enable; }
protected:
uint32_t get_measurement_timing_budget_() {
SequenceStepEnables enables{};
SequenceStepTimeouts timeouts{};
uint32_t get_measurement_timing_budget_();
bool set_measurement_timing_budget_(uint32_t budget_us);
void get_sequence_step_enables_(SequenceStepEnables *enables);
void get_sequence_step_timeouts_(SequenceStepEnables const *enables, SequenceStepTimeouts *timeouts);
uint8_t get_vcsel_pulse_period_(VcselPeriodType type);
uint32_t get_macro_period_(uint8_t vcsel_period_pclks);
uint16_t start_overhead = 1910;
uint16_t end_overhead = 960;
uint16_t msrc_overhead = 660;
uint16_t tcc_overhead = 590;
uint16_t dss_overhead = 690;
uint16_t pre_range_overhead = 660;
uint16_t final_range_overhead = 550;
uint32_t timeout_mclks_to_microseconds_(uint16_t timeout_period_mclks, uint8_t vcsel_period_pclks);
uint32_t timeout_microseconds_to_mclks_(uint32_t timeout_period_us, uint8_t vcsel_period_pclks);
// "Start and end overhead times always present"
uint32_t budget_us = start_overhead + end_overhead;
uint16_t decode_timeout_(uint16_t reg_val);
uint16_t encode_timeout_(uint16_t timeout_mclks);
get_sequence_step_enables_(&enables);
get_sequence_step_timeouts_(&enables, &timeouts);
if (enables.tcc)
budget_us += (timeouts.msrc_dss_tcc_us + tcc_overhead);
if (enables.dss) {
budget_us += 2 * (timeouts.msrc_dss_tcc_us + dss_overhead);
} else if (enables.msrc) {
budget_us += (timeouts.msrc_dss_tcc_us + msrc_overhead);
}
if (enables.pre_range)
budget_us += (timeouts.pre_range_us + pre_range_overhead);
if (enables.final_range)
budget_us += (timeouts.final_range_us + final_range_overhead);
measurement_timing_budget_us_ = budget_us; // store for internal reuse
return budget_us;
}
bool set_measurement_timing_budget_(uint32_t budget_us) {
SequenceStepEnables enables{};
SequenceStepTimeouts timeouts{};
uint16_t start_overhead = 1320; // note that this is different than the value in get_
uint16_t end_overhead = 960;
uint16_t msrc_overhead = 660;
uint16_t tcc_overhead = 590;
uint16_t dss_overhead = 690;
uint16_t pre_range_overhead = 660;
uint16_t final_range_overhead = 550;
uint32_t min_timing_budget = 20000;
if (budget_us < min_timing_budget) {
return false;
}
uint32_t used_budget_us = start_overhead + end_overhead;
get_sequence_step_enables_(&enables);
get_sequence_step_timeouts_(&enables, &timeouts);
if (enables.tcc) {
used_budget_us += (timeouts.msrc_dss_tcc_us + tcc_overhead);
}
if (enables.dss) {
used_budget_us += 2 * (timeouts.msrc_dss_tcc_us + dss_overhead);
} else if (enables.msrc) {
used_budget_us += (timeouts.msrc_dss_tcc_us + msrc_overhead);
}
if (enables.pre_range) {
used_budget_us += (timeouts.pre_range_us + pre_range_overhead);
}
if (enables.final_range) {
used_budget_us += final_range_overhead;
// "Note that the final range timeout is determined by the timing
// budget and the sum of all other timeouts within the sequence.
// If there is no room for the final range timeout, then an error
// will be set. Otherwise the remaining time will be applied to
// the final range."
if (used_budget_us > budget_us) {
// "Requested timeout too big."
return false;
}
uint32_t final_range_timeout_us = budget_us - used_budget_us;
// set_sequence_step_timeout() begin
// (SequenceStepId == VL53L0X_SEQUENCESTEP_FINAL_RANGE)
// "For the final range timeout, the pre-range timeout
// must be added. To do this both final and pre-range
// timeouts must be expressed in macro periods MClks
// because they have different vcsel periods."
uint16_t final_range_timeout_mclks =
timeout_microseconds_to_mclks_(final_range_timeout_us, timeouts.final_range_vcsel_period_pclks);
if (enables.pre_range) {
final_range_timeout_mclks += timeouts.pre_range_mclks;
}
write_byte_16(0x71, encode_timeout_(final_range_timeout_mclks));
// set_sequence_step_timeout() end
measurement_timing_budget_us_ = budget_us; // store for internal reuse
}
return true;
}
void get_sequence_step_enables_(SequenceStepEnables *enables) {
uint8_t sequence_config = reg(0x01).get();
enables->tcc = (sequence_config >> 4) & 0x1;
enables->dss = (sequence_config >> 3) & 0x1;
enables->msrc = (sequence_config >> 2) & 0x1;
enables->pre_range = (sequence_config >> 6) & 0x1;
enables->final_range = (sequence_config >> 7) & 0x1;
}
enum VcselPeriodType { VCSEL_PERIOD_PRE_RANGE, VCSEL_PERIOD_FINAL_RANGE };
void get_sequence_step_timeouts_(SequenceStepEnables const *enables, SequenceStepTimeouts *timeouts) {
timeouts->pre_range_vcsel_period_pclks = get_vcsel_pulse_period_(VCSEL_PERIOD_PRE_RANGE);
timeouts->msrc_dss_tcc_mclks = reg(0x46).get() + 1;
timeouts->msrc_dss_tcc_us =
timeout_mclks_to_microseconds_(timeouts->msrc_dss_tcc_mclks, timeouts->pre_range_vcsel_period_pclks);
uint16_t value;
read_byte_16(0x51, &value);
timeouts->pre_range_mclks = decode_timeout_(value);
timeouts->pre_range_us =
timeout_mclks_to_microseconds_(timeouts->pre_range_mclks, timeouts->pre_range_vcsel_period_pclks);
timeouts->final_range_vcsel_period_pclks = get_vcsel_pulse_period_(VCSEL_PERIOD_FINAL_RANGE);
read_byte_16(0x71, &value);
timeouts->final_range_mclks = decode_timeout_(value);
if (enables->pre_range) {
timeouts->final_range_mclks -= timeouts->pre_range_mclks;
}
timeouts->final_range_us =
timeout_mclks_to_microseconds_(timeouts->final_range_mclks, timeouts->final_range_vcsel_period_pclks);
}
uint8_t get_vcsel_pulse_period_(VcselPeriodType type) {
uint8_t vcsel;
if (type == VCSEL_PERIOD_PRE_RANGE) {
vcsel = reg(0x50).get();
} else if (type == VCSEL_PERIOD_FINAL_RANGE) {
vcsel = reg(0x70).get();
} else {
return 255;
}
return (vcsel + 1) << 1;
}
uint32_t get_macro_period_(uint8_t vcsel_period_pclks) {
return ((2304UL * vcsel_period_pclks * 1655UL) + 500UL) / 1000UL;
}
uint32_t timeout_mclks_to_microseconds_(uint16_t timeout_period_mclks, uint8_t vcsel_period_pclks) {
uint32_t macro_period_ns = get_macro_period_(vcsel_period_pclks);
return ((timeout_period_mclks * macro_period_ns) + (macro_period_ns / 2)) / 1000;
}
uint32_t timeout_microseconds_to_mclks_(uint32_t timeout_period_us, uint8_t vcsel_period_pclks) {
uint32_t macro_period_ns = get_macro_period_(vcsel_period_pclks);
return (((timeout_period_us * 1000) + (macro_period_ns / 2)) / macro_period_ns);
}
uint16_t decode_timeout_(uint16_t reg_val) {
// format: "(LSByte * 2^MSByte) + 1"
uint8_t msb = (reg_val >> 8) & 0xFF;
uint8_t lsb = (reg_val >> 0) & 0xFF;
return (uint16_t(lsb) << msb) + 1;
}
uint16_t encode_timeout_(uint16_t timeout_mclks) {
// format: "(LSByte * 2^MSByte) + 1"
uint32_t ls_byte = 0;
uint16_t ms_byte = 0;
if (timeout_mclks <= 0)
return 0;
ls_byte = timeout_mclks - 1;
while ((ls_byte & 0xFFFFFF00) > 0) {
ls_byte >>= 1;
ms_byte++;
}
return (ms_byte << 8) | (ls_byte & 0xFF);
}
bool perform_single_ref_calibration_(uint8_t vhv_init_byte) {
reg(0x00) = 0x01 | vhv_init_byte; // VL53L0X_REG_SYSRANGE_MODE_START_STOP
uint32_t start = millis();
while ((reg(0x13).get() & 0x07) == 0) {
if (millis() - start > 1000)
return false;
yield();
}
reg(0x0B) = 0x01;
reg(0x00) = 0x00;
return true;
}
bool perform_single_ref_calibration_(uint8_t vhv_init_byte);
float signal_rate_limit_;
bool long_range_;

View File

@ -36,6 +36,8 @@ class WaveshareEPaper : public PollingComponent,
void on_safe_shutdown() override;
display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_BINARY; }
protected:
void draw_absolute_pixel_internal(int x, int y, Color color) override;

View File

@ -555,6 +555,7 @@ void WiFiComponent::check_connecting_finished() {
}
ESP_LOGW(TAG, "WiFi Unknown connection status %d", (int) status);
this->retry_connect();
}
void WiFiComponent::retry_connect() {

View File

@ -195,7 +195,7 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) {
#if ESP_IDF_VERSION_MAJOR >= 4
// Protected Management Frame
// Device will prefer to connect in PMF mode if other device also advertizes PMF capability.
// Device will prefer to connect in PMF mode if other device also advertises PMF capability.
conf.sta.pmf_cfg.capable = true;
conf.sta.pmf_cfg.required = false;
#endif

View File

@ -303,7 +303,7 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) {
#if ESP_IDF_VERSION_MAJOR >= 4
// Protected Management Frame
// Device will prefer to connect in PMF mode if other device also advertizes PMF capability.
// Device will prefer to connect in PMF mode if other device also advertises PMF capability.
conf.sta.pmf_cfg.capable = true;
conf.sta.pmf_cfg.required = false;
#endif

View File

@ -38,7 +38,7 @@ bool XiaomiMiscale::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
if (this->impedance_ != nullptr) {
if (res->version == 1) {
ESP_LOGW(TAG, "Impedance is only supported on version 2. Your scale was identified as verison 1.");
ESP_LOGW(TAG, "Impedance is only supported on version 2. Your scale was identified as version 1.");
} else {
if (res->impedance.has_value()) {
this->impedance_->publish_state(*res->impedance);

View File

@ -679,6 +679,7 @@ def validate_config(config, command_line_substitutions) -> Config:
result.add_output_path([CONF_SUBSTITUTIONS], CONF_SUBSTITUTIONS)
try:
substitutions.do_substitution_pass(config, command_line_substitutions)
substitutions.do_substitution_pass(config, command_line_substitutions)
except vol.Invalid as err:
result.add_error(err)
return result

View File

@ -57,11 +57,12 @@ from esphome.core import (
TimePeriodMinutes,
)
from esphome.helpers import list_starts_with, add_class_to_obj
from esphome.jsonschema import (
jschema_list,
jschema_extractor,
jschema_registry,
jschema_typed,
from esphome.schema_extractors import (
SCHEMA_EXTRACT,
schema_extractor_list,
schema_extractor,
schema_extractor_registry,
schema_extractor_typed,
)
from esphome.util import parse_esphome_version
from esphome.voluptuous_schema import _Schema
@ -327,7 +328,7 @@ def boolean(value):
)
@jschema_list
@schema_extractor_list
def ensure_list(*validators):
"""Validate this configuration option to be a list.
@ -452,7 +453,11 @@ def validate_id_name(value):
def use_id(type):
"""Declare that this configuration option should point to an ID with the given type."""
@schema_extractor("use_id")
def validator(value):
if value == SCHEMA_EXTRACT:
return type
check_not_templatable(value)
if value is None:
return core.ID(None, is_declaration=False, type=type)
@ -475,7 +480,11 @@ def declare_id(type):
If two IDs with the same name exist, a validation error is thrown.
"""
@schema_extractor("declare_id")
def validator(value):
if value == SCHEMA_EXTRACT:
return type
check_not_templatable(value)
if value is None:
return core.ID(None, is_declaration=True, type=type)
@ -494,11 +503,11 @@ def templatable(other_validators):
"""
schema = Schema(other_validators)
@jschema_extractor("templatable")
@schema_extractor("templatable")
def validator(value):
# pylint: disable=comparison-with-callable
if value == jschema_extractor:
if value == SCHEMA_EXTRACT:
return other_validators
if isinstance(value, Lambda):
return returning_lambda(value)
if isinstance(other_validators, dict):
@ -963,9 +972,9 @@ def ipv4(value):
elif isinstance(value, IPAddress):
return value
else:
raise Invalid("IPv4 address must consist of either string or " "integer list")
raise Invalid("IPv4 address must consist of either string or integer list")
if len(parts) != 4:
raise Invalid("IPv4 address must consist of four point-separated " "integers")
raise Invalid("IPv4 address must consist of four point-separated integers")
parts_ = list(map(int, parts))
if not all(0 <= x < 256 for x in parts_):
raise Invalid("IPv4 address parts must be in range from 0 to 255")
@ -985,10 +994,10 @@ def _valid_topic(value):
raise Invalid("MQTT topic name/filter must not be empty.")
if len(raw_value) > 65535:
raise Invalid(
"MQTT topic name/filter must not be longer than " "65535 encoded bytes."
"MQTT topic name/filter must not be longer than 65535 encoded bytes."
)
if "\0" in value:
raise Invalid("MQTT topic name/filter must not contain null " "character.")
raise Invalid("MQTT topic name/filter must not contain null character.")
return value
@ -1000,7 +1009,7 @@ def subscribe_topic(value):
i < len(value) - 1 and value[i + 1] != "/"
):
raise Invalid(
"Single-level wildcard must occupy an entire " "level of the filter"
"Single-level wildcard must occupy an entire level of the filter"
)
index = value.find("#")
@ -1012,9 +1021,7 @@ def subscribe_topic(value):
"character in the topic filter."
)
if len(value) > 1 and value[index - 1] != "/":
raise Invalid(
"Multi-level wildcard must be after a topic " "level separator."
)
raise Invalid("Multi-level wildcard must be after a topic level separator.")
return value
@ -1086,16 +1093,21 @@ def possibly_negative_percentage(value):
except ValueError:
# pylint: disable=raise-missing-from
raise Invalid("invalid number")
if value > 1:
msg = "Percentage must not be higher than 100%."
if not has_percent_sign:
msg += " Please put a percent sign after the number!"
raise Invalid(msg)
if value < -1:
msg = "Percentage must not be smaller than -100%."
if not has_percent_sign:
msg += " Please put a percent sign after the number!"
raise Invalid(msg)
try:
if value > 1:
msg = "Percentage must not be higher than 100%."
if not has_percent_sign:
msg += " Please put a percent sign after the number!"
raise Invalid(msg)
if value < -1:
msg = "Percentage must not be smaller than -100%."
if not has_percent_sign:
msg += " Please put a percent sign after the number!"
raise Invalid(msg)
except TypeError:
raise Invalid( # pylint: disable=raise-missing-from
"Expected percentage or float between -1.0 and 1.0"
)
return negative_one_to_one_float(value)
@ -1172,10 +1184,9 @@ def one_of(*values, **kwargs):
if kwargs:
raise ValueError
@jschema_extractor("one_of")
@schema_extractor("one_of")
def validator(value):
# pylint: disable=comparison-with-callable
if value == jschema_extractor:
if value == SCHEMA_EXTRACT:
return values
if string_:
@ -1215,10 +1226,9 @@ def enum(mapping, **kwargs):
assert isinstance(mapping, dict)
one_of_validator = one_of(*mapping, **kwargs)
@jschema_extractor("enum")
@schema_extractor("enum")
def validator(value):
# pylint: disable=comparison-with-callable
if value == jschema_extractor:
if value == SCHEMA_EXTRACT:
return mapping
value = one_of_validator(value)
@ -1391,7 +1401,7 @@ def extract_keys(schema):
return keys
@jschema_typed
@schema_extractor_typed
def typed_schema(schemas, **kwargs):
"""Create a schema that has a key to distinguish between schemas"""
key = kwargs.pop("key", CONF_TYPE)
@ -1505,7 +1515,7 @@ def validate_registry_entry(name, registry):
)
ignore_keys = extract_keys(base_schema)
@jschema_registry(registry)
@schema_extractor_registry(registry)
def validator(value):
if isinstance(value, str):
value = {value: {}}
@ -1550,12 +1560,15 @@ def validate_registry(name, registry):
return ensure_list(validate_registry_entry(name, registry))
@jschema_list
def maybe_simple_value(*validators, **kwargs):
key = kwargs.pop("key", CONF_VALUE)
validator = All(*validators)
@schema_extractor("maybe")
def validate(value):
if value == SCHEMA_EXTRACT:
return (validator, key)
if isinstance(value, dict) and key in value:
return validator(value)
return validator({key: value})

View File

@ -1,6 +1,6 @@
"""Constants used by esphome."""
__version__ = "2022.6.0-dev"
__version__ = "2022.7.0-dev"
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
@ -905,12 +905,15 @@ DEVICE_CLASS_BATTERY = "battery"
DEVICE_CLASS_GAS = "gas"
DEVICE_CLASS_POWER = "power"
# device classes of sensor component
DEVICE_CLASS_APPARENT_POWER = "apparent_power"
DEVICE_CLASS_AQI = "aqi"
DEVICE_CLASS_CARBON_DIOXIDE = "carbon_dioxide"
DEVICE_CLASS_CARBON_MONOXIDE = "carbon_monoxide"
DEVICE_CLASS_CURRENT = "current"
DEVICE_CLASS_DATE = "date"
DEVICE_CLASS_DURATION = "duration"
DEVICE_CLASS_ENERGY = "energy"
DEVICE_CLASS_FREQUENCY = "frequency"
DEVICE_CLASS_HUMIDITY = "humidity"
DEVICE_CLASS_ILLUMINANCE = "illuminance"
DEVICE_CLASS_MONETARY = "monetary"
@ -923,6 +926,7 @@ DEVICE_CLASS_PM10 = "pm10"
DEVICE_CLASS_PM25 = "pm25"
DEVICE_CLASS_POWER_FACTOR = "power_factor"
DEVICE_CLASS_PRESSURE = "pressure"
DEVICE_CLASS_REACTIVE_POWER = "reactive_power"
DEVICE_CLASS_SIGNAL_STRENGTH = "signal_strength"
DEVICE_CLASS_SULPHUR_DIOXIDE = "sulphur_dioxide"
DEVICE_CLASS_TEMPERATURE = "temperature"

View File

@ -651,7 +651,7 @@ class EsphomeCore:
continue
if other.repository is not None:
if library.repository is None or other.repository == library.repository:
# Other is using a/the same repository, takes precendence
# Other is using a/the same repository, takes precedence
break
raise ValueError(
f"Adding named Library with repository failed! Libraries {library} and {other} "

View File

@ -125,19 +125,26 @@ void IRAM_ATTR HOT Application::feed_wdt() {
}
void Application::reboot() {
ESP_LOGI(TAG, "Forcing a reboot...");
for (auto *comp : this->components_)
comp->on_shutdown();
for (auto it = this->components_.rbegin(); it != this->components_.rend(); ++it) {
(*it)->on_shutdown();
}
arch_restart();
}
void Application::safe_reboot() {
ESP_LOGI(TAG, "Rebooting safely...");
for (auto *comp : this->components_)
comp->on_safe_shutdown();
for (auto *comp : this->components_)
comp->on_shutdown();
run_safe_shutdown_hooks();
arch_restart();
}
void Application::run_safe_shutdown_hooks() {
for (auto it = this->components_.rbegin(); it != this->components_.rend(); ++it) {
(*it)->on_safe_shutdown();
}
for (auto it = this->components_.rbegin(); it != this->components_.rend(); ++it) {
(*it)->on_shutdown();
}
}
void Application::calculate_looping_components_() {
for (auto *obj : this->components_) {
if (obj->has_overridden_loop())

View File

@ -45,6 +45,9 @@
#ifdef USE_LOCK
#include "esphome/components/lock/lock.h"
#endif
#ifdef USE_MEDIA_PLAYER
#include "esphome/components/media_player/media_player.h"
#endif
namespace esphome {
@ -111,6 +114,10 @@ class Application {
void register_lock(lock::Lock *a_lock) { this->locks_.push_back(a_lock); }
#endif
#ifdef USE_MEDIA_PLAYER
void register_media_player(media_player::MediaPlayer *media_player) { this->media_players_.push_back(media_player); }
#endif
/// Register the component in this Application instance.
template<class C> C *register_component(C *c) {
static_assert(std::is_base_of<Component, C>::value, "Only Component subclasses can be registered");
@ -154,14 +161,7 @@ class Application {
void safe_reboot();
void run_safe_shutdown_hooks() {
for (auto *comp : this->components_) {
comp->on_safe_shutdown();
}
for (auto *comp : this->components_) {
comp->on_shutdown();
}
}
void run_safe_shutdown_hooks();
uint32_t get_app_state() const { return this->app_state_; }
@ -273,6 +273,15 @@ class Application {
return nullptr;
}
#endif
#ifdef USE_MEDIA_PLAYER
const std::vector<media_player::MediaPlayer *> &get_media_players() { return this->media_players_; }
media_player::MediaPlayer *get_media_player_by_key(uint32_t key, bool include_internal = false) {
for (auto *obj : this->media_players_)
if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal()))
return obj;
return nullptr;
}
#endif
Scheduler scheduler;
@ -324,6 +333,9 @@ class Application {
#ifdef USE_LOCK
std::vector<lock::Lock *> locks_{};
#endif
#ifdef USE_MEDIA_PLAYER
std::vector<media_player::MediaPlayer *> media_players_{};
#endif
std::string name_;
std::string compilation_time_;

View File

@ -231,6 +231,21 @@ void ComponentIterator::advance() {
}
}
break;
#endif
#ifdef USE_MEDIA_PLAYER
case IteratorState::MEDIA_PLAYER:
if (this->at_ >= App.get_media_players().size()) {
advance_platform = true;
} else {
auto *media_player = App.get_media_players()[this->at_];
if (media_player->is_internal() && !this->include_internal_) {
success = true;
break;
} else {
success = this->on_media_player(media_player);
}
}
break;
#endif
case IteratorState::MAX:
if (this->on_end()) {
@ -254,4 +269,7 @@ bool ComponentIterator::on_service(api::UserServiceDescriptor *service) { return
#ifdef USE_ESP32_CAMERA
bool ComponentIterator::on_camera(esp32_camera::ESP32Camera *camera) { return true; }
#endif
#ifdef USE_MEDIA_PLAYER
bool ComponentIterator::on_media_player(media_player::MediaPlayer *media_player) { return true; }
#endif
} // namespace esphome

View File

@ -62,6 +62,9 @@ class ComponentIterator {
#endif
#ifdef USE_LOCK
virtual bool on_lock(lock::Lock *a_lock) = 0;
#endif
#ifdef USE_MEDIA_PLAYER
virtual bool on_media_player(media_player::MediaPlayer *media_player);
#endif
virtual bool on_end();
@ -110,6 +113,9 @@ class ComponentIterator {
#endif
#ifdef USE_LOCK
LOCK,
#endif
#ifdef USE_MEDIA_PLAYER
MEDIA_PLAYER,
#endif
MAX,
} state_{IteratorState::NONE};

View File

@ -73,6 +73,12 @@ void Controller::setup_controller(bool include_internal) {
obj->add_on_state_callback([this, obj]() { this->on_lock_update(obj); });
}
#endif
#ifdef USE_MEDIA_PLAYER
for (auto *obj : App.get_media_players()) {
if (include_internal || !obj->is_internal())
obj->add_on_state_callback([this, obj]() { this->on_media_player_update(obj); });
}
#endif
}
} // namespace esphome

View File

@ -37,6 +37,9 @@
#ifdef USE_LOCK
#include "esphome/components/lock/lock.h"
#endif
#ifdef USE_MEDIA_PLAYER
#include "esphome/components/media_player/media_player.h"
#endif
namespace esphome {
@ -76,6 +79,9 @@ class Controller {
#ifdef USE_LOCK
virtual void on_lock_update(lock::Lock *obj){};
#endif
#ifdef USE_MEDIA_PLAYER
virtual void on_media_player_update(media_player::MediaPlayer *obj){};
#endif
};
} // namespace esphome

View File

@ -29,6 +29,7 @@
#define USE_LOCK
#define USE_LOGGER
#define USE_MDNS
#define USE_MEDIA_PLAYER
#define USE_MQTT
#define USE_NUMBER
#define USE_OTA_PASSWORD

View File

@ -46,7 +46,9 @@ class EntityBase {
void set_icon(const std::string &name);
protected:
virtual uint32_t hash_base() = 0;
/// The hash_base() function has been deprecated. It is kept in this
/// class for now, to prevent external components from not compiling.
virtual uint32_t hash_base() { return 0L; }
void calc_object_id_();
std::string name_;

View File

@ -9,7 +9,7 @@ namespace esphome {
ESP_LOGCONFIG(TAG, prefix "%s", (pin)->dump_summary().c_str()); \
}
// put GPIO flags in a namepsace to not pollute esphome namespace
// put GPIO flags in a namespace to not pollute esphome namespace
namespace gpio {
enum Flags : uint8_t {

View File

@ -258,10 +258,10 @@ inline std::string to_string(const std::string &val) { return val; }
/// Truncate a string to a specific length.
std::string str_truncate(const std::string &str, size_t length);
/// Extract the part of the string until either the first occurence of the specified character, or the end (requires str
/// to be null-terminated).
/// Extract the part of the string until either the first occurrence of the specified character, or the end
/// (requires str to be null-terminated).
std::string str_until(const char *str, char ch);
/// Extract the part of the string until either the first occurence of the specified character, or the end.
/// Extract the part of the string until either the first occurrence of the specified character, or the end.
std::string str_until(const std::string &str, char ch);
/// Convert the string to lower case.
@ -600,7 +600,7 @@ template<class T> class ExternalRAMAllocator {
ExternalRAMAllocator() = default;
ExternalRAMAllocator(Flags flags) : flags_{flags} {}
template<class U> constexpr ExternalRAMAllocator(const ExternalRAMAllocator<U> &other) : flags_{other.flags} {}
template<class U> constexpr ExternalRAMAllocator(const ExternalRAMAllocator<U> &other) : flags_{other.flags_} {}
T *allocate(size_t n) {
size_t size = n * sizeof(T);

View File

@ -60,7 +60,6 @@ class ProgressBar:
sys.stderr.write(text)
sys.stderr.flush()
# pylint: disable=no-self-use
def done(self):
sys.stderr.write("\n")
sys.stderr.flush()

View File

@ -296,7 +296,7 @@ _TYPE_OVERLOADS = {
int: type("EInt", (int,), {}),
float: type("EFloat", (float,), {}),
str: type("EStr", (str,), {}),
dict: type("EDict", (str,), {}),
dict: type("EDict", (dict,), {}),
list: type("EList", (list,), {}),
}

View File

@ -20,7 +20,7 @@ def patch_structhash():
# removed/added. This might have unintended consequences, but this improves compile
# times greatly when adding/removing components and a simple clean build solves
# all issues
from platformio.commands.run import helpers, command
from platformio.run import helpers, cli
from os.path import join, isdir, getmtime
from os import makedirs
@ -39,7 +39,7 @@ def patch_structhash():
# pylint: disable=protected-access
helpers.clean_build_dir = patched_clean_build_dir
command.clean_build_dir = patched_clean_build_dir
cli.clean_build_dir = patched_clean_build_dir
IGNORE_LIB_WARNINGS = f"(?:{'|'.join(['Hash', 'Update'])})"

View File

@ -9,9 +9,9 @@ However there is a property to further disable decorator
impact."""
# This is set to true by script/build_jsonschema.py
# This is set to true by script/build_language_schema.py
# only, so data is collected (again functionality is not modified)
EnableJsonSchemaCollect = False
EnableSchemaExtraction = False
extended_schemas = {}
list_schemas = {}
@ -19,9 +19,12 @@ registry_schemas = {}
hidden_schemas = {}
typed_schemas = {}
# This key is used to generate schema files of Esphome configuration.
SCHEMA_EXTRACT = object()
def jschema_extractor(validator_name):
if EnableJsonSchemaCollect:
def schema_extractor(validator_name):
if EnableSchemaExtraction:
def decorator(func):
hidden_schemas[repr(func)] = validator_name
@ -35,8 +38,8 @@ def jschema_extractor(validator_name):
return dummy
def jschema_extended(func):
if EnableJsonSchemaCollect:
def schema_extractor_extended(func):
if EnableSchemaExtraction:
def decorate(*args, **kwargs):
ret = func(*args, **kwargs)
@ -49,8 +52,8 @@ def jschema_extended(func):
return func
def jschema_list(func):
if EnableJsonSchemaCollect:
def schema_extractor_list(func):
if EnableSchemaExtraction:
def decorate(*args, **kwargs):
ret = func(*args, **kwargs)
@ -63,8 +66,8 @@ def jschema_list(func):
return func
def jschema_registry(registry):
if EnableJsonSchemaCollect:
def schema_extractor_registry(registry):
if EnableSchemaExtraction:
def decorator(func):
registry_schemas[repr(func)] = registry
@ -78,8 +81,8 @@ def jschema_registry(registry):
return dummy
def jschema_typed(func):
if EnableJsonSchemaCollect:
def schema_extractor_typed(func):
if EnableSchemaExtraction:
def decorate(*args, **kwargs):
ret = func(*args, **kwargs)

View File

@ -152,7 +152,6 @@ class RedirectText:
# any caller.
return len(s)
# pylint: disable=no-self-use
def isatty(self):
return True

View File

@ -2,7 +2,7 @@ import difflib
import itertools
import voluptuous as vol
from esphome.jsonschema import jschema_extended
from esphome.schema_extractors import schema_extractor_extended
class ExtraKeysInvalid(vol.Invalid):
@ -203,7 +203,7 @@ class _Schema(vol.Schema):
self._extra_schemas.append(validator)
return self
@jschema_extended
@schema_extractor_extended
# pylint: disable=signature-differs
def extend(self, *schemas, **kwargs):
extra = kwargs.pop("extra", None)

View File

@ -343,7 +343,7 @@ def wizard(path):
sleep(1)
safe_print_step(3, WIFI_BIG)
safe_print("In this step, I'm going to create the configuration for " "WiFi.")
safe_print("In this step, I'm going to create the configuration for WiFi.")
safe_print()
sleep(1)
safe_print(

View File

@ -251,7 +251,49 @@ class ESPHomeLoader(yaml.SafeLoader): # pylint: disable=too-many-ancestors
@_add_data_ref
def construct_include(self, node):
return _load_yaml_internal(self._rel_path(node.value))
def extract_file_vars(node):
fields = self.construct_yaml_map(node)
file = fields.get("file")
if file is None:
raise yaml.MarkedYAMLError("Must include 'file'", node.start_mark)
vars = fields.get("vars")
if vars:
vars = {k: str(v) for k, v in vars.items()}
return file, vars
def substitute_vars(config, vars):
from esphome.const import CONF_SUBSTITUTIONS
from esphome.components import substitutions
org_subs = None
result = config
if not isinstance(config, dict):
# when the included yaml contains a list or a scalar
# wrap it into an OrderedDict because do_substitution_pass expects it
result = OrderedDict([("yaml", config)])
elif CONF_SUBSTITUTIONS in result:
org_subs = result.pop(CONF_SUBSTITUTIONS)
result[CONF_SUBSTITUTIONS] = vars
# Ignore missing vars that refer to the top level substitutions
substitutions.do_substitution_pass(result, None, ignore_missing=True)
result.pop(CONF_SUBSTITUTIONS)
if not isinstance(config, dict):
result = result["yaml"] # unwrap the result
elif org_subs:
result[CONF_SUBSTITUTIONS] = org_subs
return result
if isinstance(node, yaml.nodes.MappingNode):
file, vars = extract_file_vars(node)
else:
file, vars = node.value, None
result = _load_yaml_internal(self._rel_path(file))
if vars:
result = substitute_vars(result, vars)
return result
@_add_data_ref
def construct_include_dir_list(self, node):

View File

@ -40,7 +40,7 @@ lib_deps =
wjtje/qr-code-generator-library@1.7.0 ; qr_code
functionpointer/arduino-MLX90393@1.0.0 ; mlx90393
; This is using the repository until a new release is published to PlatformIO
https://github.com/Sensirion/arduino-gas-index-algorithm.git ; Sensirion Gas Index Algorithm Arduino Library
https://github.com/Sensirion/arduino-gas-index-algorithm.git#3.2.1 ; Sensirion Gas Index Algorithm Arduino Library
build_flags =
-DESPHOME_LOG_LEVEL=ESPHOME_LOG_LEVEL_VERY_VERBOSE
src_filter =
@ -120,10 +120,12 @@ lib_deps =
HTTPClient ; http_request,nextion (Arduino built-in)
ESPmDNS ; mdns (Arduino built-in)
DNSServer ; captive_portal (Arduino built-in)
esphome/ESP32-audioI2S@2.1.0 ; i2s_audio
build_flags =
${common:arduino.build_flags}
-DUSE_ESP32
-DUSE_ESP32_FRAMEWORK_ARDUINO
-DAUDIO_NO_SD_FS ; i2s_audio
extra_scripts = post:esphome/components/esp32/post_build.py.script
; This are common settings for the ESP32 (all variants) using IDF.

View File

@ -24,7 +24,6 @@ disable=
undefined-loop-variable,
useless-object-inheritance,
stop-iteration-return,
no-self-use,
import-outside-toplevel,
# Broken
unsupported-membership-test,

View File

@ -1,17 +1,17 @@
voluptuous==0.13.1
PyYAML==6.0
paho-mqtt==1.6.1
colorama==0.4.4
colorama==0.4.5
tornado==6.1
tzlocal==4.2 # from time
tzdata>=2021.1 # from time
pyserial==3.5
platformio==5.2.5 # When updating platformio, also update Dockerfile
platformio==6.0.2 # When updating platformio, also update Dockerfile
esptool==3.3.1
click==8.1.3
esphome-dashboard==20220508.0
aioesphomeapi==10.8.2
zeroconf==0.38.4
aioesphomeapi==10.10.0
zeroconf==0.38.7
# esp-idf requires this, but doesn't bundle it by default
# https://github.com/espressif/esp-idf/blob/220590d599e134d7a5e7f1e683cc4550349ffbf8/requirements.txt#L24

View File

@ -1,13 +1,13 @@
pylint==2.13.9
pylint==2.14.4
flake8==4.0.1
black==22.3.0
pyupgrade==2.32.1
black==22.6.0 # also change in .pre-commit-config.yaml when updating
pyupgrade==2.34.0 # also change in .pre-commit-config.yaml when updating
pre-commit
# Unit tests
pytest==7.1.1
pytest-cov==3.0.0
pytest-mock==3.7.0
pytest-mock==3.8.1
pytest-asyncio==0.18.3
asyncmock==0.4.2
hypothesis==5.49.0

View File

@ -1,828 +0,0 @@
#!/usr/bin/env python3
from esphome.cpp_generator import MockObj
import json
import argparse
import os
import re
from pathlib import Path
import voluptuous as vol
# NOTE: Cannot import other esphome components globally as a modification in jsonschema
# is needed before modules are loaded
import esphome.jsonschema as ejs
ejs.EnableJsonSchemaCollect = True
DUMP_COMMENTS = False
JSC_ACTION = "automation.ACTION_REGISTRY"
JSC_ALLOF = "allOf"
JSC_ANYOF = "anyOf"
JSC_COMMENT = "$comment"
JSC_CONDITION = "automation.CONDITION_REGISTRY"
JSC_DESCRIPTION = "description"
JSC_ONEOF = "oneOf"
JSC_PROPERTIES = "properties"
JSC_REF = "$ref"
# this should be required, but YAML Language server completion does not work properly if required are specified.
# still needed for other features / checks
JSC_REQUIRED = "required_"
SIMPLE_AUTOMATION = "simple_automation"
schema_names = {}
schema_registry = {}
components = {}
modules = {}
registries = []
pending_refs = []
definitions = {}
base_props = {}
parser = argparse.ArgumentParser()
parser.add_argument(
"--output", default="esphome.json", help="Output filename", type=os.path.abspath
)
args = parser.parse_args()
def get_ref(definition):
return {JSC_REF: "#/definitions/" + definition}
def is_ref(jschema):
return isinstance(jschema, dict) and JSC_REF in jschema
def unref(jschema):
return definitions.get(jschema[JSC_REF][len("#/definitions/") :])
def add_definition_array_or_single_object(ref):
return {JSC_ANYOF: [{"type": "array", "items": ref}, ref]}
def add_core():
from esphome.core.config import CONFIG_SCHEMA
base_props["esphome"] = get_jschema("esphome", CONFIG_SCHEMA)
def add_buses():
# uart
from esphome.components.uart import UART_DEVICE_SCHEMA
get_jschema("uart_bus", UART_DEVICE_SCHEMA)
# spi
from esphome.components.spi import spi_device_schema
get_jschema("spi_bus", spi_device_schema(False))
# i2c
from esphome.components.i2c import i2c_device_schema
get_jschema("i2c_bus", i2c_device_schema(None))
def add_registries():
for domain, module in modules.items():
add_module_registries(domain, module)
def add_module_registries(domain, module):
from esphome.util import Registry
for c in dir(module):
m = getattr(module, c)
if isinstance(m, Registry):
add_registry(domain + "." + c, m)
def add_registry(registry_name, registry):
validators = []
registries.append((registry, registry_name))
for name in registry.keys():
schema = get_jschema(str(name), registry[name].schema, create_return_ref=False)
if not schema:
schema = {"type": "null"}
o_schema = {"type": "object", JSC_PROPERTIES: {name: schema}}
o_schema = create_ref(
registry_name + "-" + name, str(registry[name].schema) + "x", o_schema
)
validators.append(o_schema)
definitions[registry_name] = {JSC_ANYOF: validators}
def get_registry_ref(registry):
# we don't know yet
ref = {JSC_REF: "pending"}
pending_refs.append((ref, registry))
return ref
def solve_pending_refs():
for ref, registry in pending_refs:
for registry_match, name in registries:
if registry == registry_match:
ref[JSC_REF] = "#/definitions/" + name
def add_module_schemas(name, module):
import esphome.config_validation as cv
for c in dir(module):
v = getattr(module, c)
if isinstance(v, cv.Schema):
get_jschema(name + "." + c, v)
def get_dirs():
from esphome.loader import CORE_COMPONENTS_PATH
dir_names = [
d
for d in os.listdir(CORE_COMPONENTS_PATH)
if not d.startswith("__")
and os.path.isdir(os.path.join(CORE_COMPONENTS_PATH, d))
]
return dir_names
def get_logger_tags():
from esphome.loader import CORE_COMPONENTS_PATH
import glob
pattern = re.compile(r'^static const char(\*\s|\s\*)TAG = "(\w.*)";', re.MULTILINE)
tags = [
"app",
"component",
"esphal",
"helpers",
"preferences",
"scheduler",
"api.service",
]
for x in os.walk(CORE_COMPONENTS_PATH):
for y in glob.glob(os.path.join(x[0], "*.cpp")):
with open(y) as file:
data = file.read()
match = pattern.search(data)
if match:
tags.append(match.group(2))
return tags
def load_components():
import esphome.config_validation as cv
from esphome.config import get_component
modules["cv"] = cv
from esphome import automation
modules["automation"] = automation
for domain in get_dirs():
components[domain] = get_component(domain)
modules[domain] = components[domain].module
def add_components():
from esphome.config import get_platform
for domain, c in components.items():
if c.is_platform_component:
# this is a platform_component, e.g. binary_sensor
platform_schema = [
{
"type": "object",
"properties": {"platform": {"type": "string"}},
}
]
if domain not in ("output", "display"):
# output bases are either FLOAT or BINARY so don't add common base for this
# display bases are either simple or FULL so don't add common base for this
platform_schema = [
{"$ref": f"#/definitions/{domain}.{domain.upper()}_SCHEMA"}
] + platform_schema
base_props[domain] = {"type": "array", "items": {"allOf": platform_schema}}
add_module_registries(domain, c.module)
add_module_schemas(domain, c.module)
# need first to iterate all platforms then iterate components
# a platform component can have other components as properties,
# e.g. climate components usually have a temperature sensor
for domain, c in components.items():
if (c.config_schema is not None) or c.is_platform_component:
if c.is_platform_component:
platform_schema = base_props[domain]["items"]["allOf"]
for platform in get_dirs():
p = get_platform(domain, platform)
if p is not None:
# this is a platform element, e.g.
# - platform: gpio
schema = get_jschema(
domain + "-" + platform,
p.config_schema,
create_return_ref=False,
)
if (
schema
): # for invalid schemas, None is returned thus is deprecated
platform_schema.append(
{
"if": {
JSC_PROPERTIES: {
"platform": {"const": platform}
}
},
"then": schema,
}
)
elif c.config_schema is not None:
# adds root components which are not platforms, e.g. api: logger:
if c.multi_conf:
schema = get_jschema(domain, c.config_schema)
schema = add_definition_array_or_single_object(schema)
else:
schema = get_jschema(domain, c.config_schema, False)
base_props[domain] = schema
def get_automation_schema(name, vschema):
from esphome.automation import AUTOMATION_SCHEMA
# ensure SIMPLE_AUTOMATION
if SIMPLE_AUTOMATION not in definitions:
simple_automation = add_definition_array_or_single_object(get_ref(JSC_ACTION))
simple_automation[JSC_ANYOF].append(
get_jschema(AUTOMATION_SCHEMA.__module__, AUTOMATION_SCHEMA)
)
definitions[schema_names[str(AUTOMATION_SCHEMA)]][JSC_PROPERTIES][
"then"
] = add_definition_array_or_single_object(get_ref(JSC_ACTION))
definitions[SIMPLE_AUTOMATION] = simple_automation
extra_vschema = None
if AUTOMATION_SCHEMA == ejs.extended_schemas[str(vschema)][0]:
extra_vschema = ejs.extended_schemas[str(vschema)][1]
if not extra_vschema:
return get_ref(SIMPLE_AUTOMATION)
# add then property
extra_jschema = get_jschema(name, extra_vschema, False)
if is_ref(extra_jschema):
return extra_jschema
if not JSC_PROPERTIES in extra_jschema:
# these are interval: and exposure_notifications, featuring automations a component
extra_jschema[JSC_ALLOF][0][JSC_PROPERTIES][
"then"
] = add_definition_array_or_single_object(get_ref(JSC_ACTION))
ref = create_ref(name, extra_vschema, extra_jschema)
return add_definition_array_or_single_object(ref)
# automations can be either
# * a single action,
# * an array of action,
# * an object with automation's schema and a then key
# with again a single action or an array of actions
if len(extra_jschema[JSC_PROPERTIES]) == 0:
return get_ref(SIMPLE_AUTOMATION)
extra_jschema[JSC_PROPERTIES]["then"] = add_definition_array_or_single_object(
get_ref(JSC_ACTION)
)
# if there is a required element in extra_jschema then this automation does not support
# directly a list of actions
if JSC_REQUIRED in extra_jschema:
return create_ref(name, extra_vschema, extra_jschema)
jschema = add_definition_array_or_single_object(get_ref(JSC_ACTION))
jschema[JSC_ANYOF].append(extra_jschema)
return create_ref(name, extra_vschema, jschema)
def get_entry(parent_key, vschema):
from esphome.voluptuous_schema import _Schema as schema_type
entry = {}
# annotate schema validator info
if DUMP_COMMENTS:
entry[JSC_COMMENT] = "entry: " + parent_key + "/" + str(vschema)
if isinstance(vschema, dict):
entry = {"what": "is_this"}
elif isinstance(vschema, list):
ref = get_jschema(parent_key + "[]", vschema[0])
entry = {"type": "array", "items": ref}
elif isinstance(vschema, schema_type) and hasattr(vschema, "schema"):
entry = get_jschema(parent_key, vschema, False)
elif hasattr(vschema, "validators"):
entry = get_jschema(parent_key, vschema, False)
elif vschema in schema_registry:
entry = schema_registry[vschema].copy()
elif str(vschema) in ejs.registry_schemas:
entry = get_registry_ref(ejs.registry_schemas[str(vschema)])
elif str(vschema) in ejs.list_schemas:
ref = get_jschema(parent_key, ejs.list_schemas[str(vschema)][0])
entry = {JSC_ANYOF: [ref, {"type": "array", "items": ref}]}
elif str(vschema) in ejs.typed_schemas:
schema_types = [{"type": "object", "properties": {"type": {"type": "string"}}}]
entry = {"allOf": schema_types}
for schema_key, vschema_type in ejs.typed_schemas[str(vschema)][0][0].items():
schema_types.append(
{
"if": {"properties": {"type": {"const": schema_key}}},
"then": get_jschema(f"{parent_key}-{schema_key}", vschema_type),
}
)
elif str(vschema) in ejs.hidden_schemas:
# get the schema from the automation schema
type = ejs.hidden_schemas[str(vschema)]
inner_vschema = vschema(ejs.jschema_extractor)
if type == "automation":
entry = get_automation_schema(parent_key, inner_vschema)
elif type == "maybe":
entry = get_jschema(parent_key, inner_vschema)
elif type == "one_of":
entry = {"enum": list(inner_vschema)}
elif type == "enum":
entry = {"enum": list(inner_vschema.keys())}
elif type == "effects":
# Like list schema but subset from list.
subset_list = inner_vschema[0]
# get_jschema('strobex', registry['strobe'].schema)
registry_schemas = []
for name in subset_list:
registry_schemas.append(get_ref("light.EFFECTS_REGISTRY-" + name))
entry = {
JSC_ANYOF: [{"type": "array", "items": {JSC_ANYOF: registry_schemas}}]
}
else:
raise ValueError("Unknown extracted schema type")
elif str(vschema).startswith("<function invalid."):
# deprecated options, don't list as valid schema
return None
else:
# everything else just accept string and let ESPHome validate
try:
from esphome.core import ID
from esphome.automation import Trigger, Automation
v = vschema(None)
if isinstance(v, ID):
if (
v.type.base != "script::Script"
and v.type.base != "switch_::Switch"
and (v.type.inherits_from(Trigger) or v.type == Automation)
):
return None
entry = {"type": "string", "id_type": v.type.base}
elif isinstance(v, str):
entry = {"type": "string"}
elif isinstance(v, list):
entry = {"type": "array"}
else:
entry = default_schema()
except:
entry = default_schema()
return entry
def default_schema():
# Accept anything
return {"type": ["null", "object", "string", "array", "number"]}
def is_default_schema(jschema):
if jschema is None:
return False
if is_ref(jschema):
jschema = unref(jschema)
if not jschema:
return False
return is_default_schema(jschema)
return "type" in jschema and jschema["type"] == default_schema()["type"]
def get_jschema(path, vschema, create_return_ref=True):
name = schema_names.get(get_schema_str(vschema))
if name:
return get_ref(name)
jschema = convert_schema(path, vschema)
if jschema is None:
return None
if is_ref(jschema):
# this can happen when returned extended
# schemas where all properties found in previous extended schema
return jschema
if not create_return_ref:
return jschema
return create_ref(path, vschema, jschema)
def get_schema_str(vschema):
# Hack on cs.use_id, in the future this can be improved by tracking which type is required by
# the id, this information can be added somehow to schema (not supported by jsonschema) and
# completion can be improved listing valid ids only Meanwhile it's a problem because it makes
# all partial schemas with cv.use_id different, e.g. i2c
return re.sub(
pattern="function use_id.<locals>.validator at 0[xX][0-9a-fA-F]+>",
repl="function use_id.<locals>.validator<>",
string=str(vschema),
)
def create_ref(name, vschema, jschema):
if jschema is None:
raise ValueError("Cannot create a ref with null jschema for " + name)
if name in schema_names:
raise ValueError("Not supported")
schema_str = get_schema_str(vschema)
schema_names[schema_str] = name
definitions[name] = jschema
return get_ref(name)
def get_all_properties(jschema):
if JSC_PROPERTIES in jschema:
return list(jschema[JSC_PROPERTIES].keys())
if is_ref(jschema):
return get_all_properties(unref(jschema))
arr = jschema.get(JSC_ALLOF, jschema.get(JSC_ANYOF))
props = []
for x in arr:
props = props + get_all_properties(x)
return props
def merge(arr, element):
# arr is an array of dicts, dicts can have keys like, properties, $ref, required:[], etc
# element is a single dict which might have several keys to
# the result should be an array with only one element containing properties, required, etc
# and other elements for needed $ref elements
# NOTE: json schema supports allof with properties in different elements, but that makes
# complex for later adding docs to the schema
for k, v in element.items():
if k == JSC_PROPERTIES:
props_found = False
for a_dict in arr:
if JSC_PROPERTIES in a_dict:
# found properties
arr_props = a_dict[JSC_PROPERTIES]
for v_k, v_v in v.items():
arr_props[v_k] = v_v # add or overwrite
props_found = True
if not props_found:
arr.append(element)
elif k == JSC_REF:
ref_found = False
for a_dict in arr:
if k in a_dict and a_dict[k] == v:
ref_found = True
continue
if not ref_found:
arr.append(element)
else:
# TODO: Required might require special handling
pass
def convert_schema(path, vschema, un_extend=True):
import esphome.config_validation as cv
# analyze input key, if it is not a Required or Optional, then it is an array
output = {}
if str(vschema) in ejs.hidden_schemas:
if ejs.hidden_schemas[str(vschema)] == "automation":
vschema = vschema(ejs.jschema_extractor)
jschema = get_jschema(path, vschema, True)
return add_definition_array_or_single_object(jschema)
else:
vschema = vschema(ejs.jschema_extractor)
if un_extend:
extended = ejs.extended_schemas.get(str(vschema))
if extended:
lhs = get_jschema(path, extended[0], False)
# The midea actions are extending an empty schema (resulted in the templatize not templatizing anything)
# this causes a recursion in that this extended looks the same in extended schema as the extended[1]
if ejs.extended_schemas.get(str(vschema)) == ejs.extended_schemas.get(
str(extended[1])
):
assert path.startswith("midea_ac")
return convert_schema(path, extended[1], False)
rhs = get_jschema(path, extended[1], False)
# check if we are not merging properties which are already in base component
lprops = get_all_properties(lhs)
rprops = get_all_properties(rhs)
if all(item in lprops for item in rprops):
return lhs
if all(item in rprops for item in lprops):
return rhs
# merge
if JSC_ALLOF in lhs and JSC_ALLOF in rhs:
output = lhs
for k in rhs[JSC_ALLOF]:
merge(output[JSC_ALLOF], k)
elif JSC_ALLOF in lhs:
output = lhs
merge(output[JSC_ALLOF], rhs)
elif JSC_ALLOF in rhs:
output = rhs
merge(output[JSC_ALLOF], lhs)
else:
output = {JSC_ALLOF: [lhs]}
merge(output[JSC_ALLOF], rhs)
return output
# When schema contains all, all also has a schema which points
# back to the containing schema
if isinstance(vschema, MockObj):
return output
while hasattr(vschema, "schema") and not hasattr(vschema, "validators"):
vschema = vschema.schema
if hasattr(vschema, "validators"):
output = default_schema()
for v in vschema.validators:
if v:
# we should take the valid schema,
# commonly all is used to validate a schema, and then a function which
# is not a schema es also given, get_schema will then return a default_schema()
if v == dict:
continue # this is a dict in the SCHEMA of packages
val_schema = get_jschema(path, v, False)
if is_default_schema(val_schema):
if not output:
output = val_schema
else:
if is_default_schema(output):
output = val_schema
else:
output = {**output, **val_schema}
return output
if not vschema:
return output
if not hasattr(vschema, "keys"):
return get_entry(path, vschema)
key = list(vschema.keys())[0]
# used for platformio_options in core_config
# pylint: disable=comparison-with-callable
if key == cv.string_strict:
output["type"] = "object"
return output
props = output[JSC_PROPERTIES] = {}
required = []
output["type"] = ["object", "null"]
if DUMP_COMMENTS:
output[JSC_COMMENT] = "converted: " + path + "/" + str(vschema)
if path == "logger-logs":
tags = get_logger_tags()
for k in tags:
props[k] = {
"enum": [
"NONE",
"ERROR",
"WARN",
"INFO",
"DEBUG",
"VERBOSE",
"VERY_VERBOSE",
]
}
else:
for k in vschema:
if str(k).startswith("<function"):
# generate all logger tags
# TODO handle key functions
continue
v = vschema[k]
prop = {}
if isinstance(v, vol.Schema):
prop = get_jschema(path + "-" + str(k), v.schema)
elif hasattr(v, "validators"):
prop = convert_schema(path + "-" + str(k), v, False)
else:
prop = get_entry(path + "-" + str(k), v)
if prop: # Deprecated (cv.Invalid) properties not added
props[str(k)] = prop
# TODO: see required, sometimes completions doesn't show up because of this...
if isinstance(k, cv.Required):
required.append(str(k))
try:
if str(k.default) != "...":
default_value = k.default()
# Yaml validator fails if `"default": null` ends up in the json schema
if default_value is not None:
if prop["type"] == "string":
default_value = str(default_value)
prop["default"] = default_value
except:
pass
if len(required) > 0:
output[JSC_REQUIRED] = required
return output
def add_pin_schema():
from esphome import pins
add_module_schemas("PIN", pins)
def add_pin_registry():
from esphome import pins
pin_registry = pins.PIN_SCHEMA_REGISTRY
assert len(pin_registry) > 0
# Here are schemas for pcf8574, mcp23xxx and other port expanders which add
# gpio registers
# ESPHome validates pins schemas if it founds a key in the pin configuration.
# This key is added to a required in jsonschema, and all options are part of a
# oneOf section, so only one is selected. Also internal schema adds number as required.
for mode in ("INPUT", "OUTPUT"):
schema_name = f"PIN.GPIO_FULL_{mode}_PIN_SCHEMA"
# TODO: get pin definitions properly
if schema_name not in definitions:
definitions[schema_name] = {"type": ["object", "null"], JSC_PROPERTIES: {}}
internal = definitions[schema_name]
definitions[schema_name]["additionalItems"] = False
definitions[f"PIN.{mode}_INTERNAL"] = internal
internal[JSC_PROPERTIES]["number"] = {"type": ["number", "string"]}
schemas = [get_ref(f"PIN.{mode}_INTERNAL")]
schemas[0]["required"] = ["number"]
# accept string and object, for internal shorthand pin IO:
definitions[schema_name] = {"oneOf": schemas, "type": ["string", "object"]}
for k, v in pin_registry.items():
if isinstance(v[1], vol.validators.All):
pin_jschema = get_jschema(f"PIN.{mode}_" + k, v[1])
if unref(pin_jschema):
pin_jschema["required"] = [k]
schemas.append(pin_jschema)
def dump_schema():
import esphome.config_validation as cv
from esphome import automation
from esphome.automation import validate_potentially_and_condition
from esphome import pins
from esphome.core import CORE
from esphome.helpers import write_file_if_changed
from esphome.components import remote_base
# The root directory of the repo
root = Path(__file__).parent.parent
# Fake some directory so that get_component works
CORE.config_path = str(root)
file_path = args.output
schema_registry[cv.boolean] = {"type": "boolean"}
for v in [
cv.int_,
cv.int_range,
cv.positive_int,
cv.float_,
cv.positive_float,
cv.positive_float,
cv.positive_not_null_int,
cv.negative_one_to_one_float,
cv.port,
]:
schema_registry[v] = {"type": "number"}
for v in [
cv.string,
cv.string_strict,
cv.valid_name,
cv.hex_int,
cv.hex_int_range,
pins.gpio_output_pin_schema,
pins.gpio_input_pin_schema,
pins.gpio_input_pullup_pin_schema,
cv.float_with_unit,
cv.subscribe_topic,
cv.publish_topic,
cv.mqtt_payload,
cv.ssid,
cv.percentage_int,
cv.percentage,
cv.possibly_negative_percentage,
cv.positive_time_period,
cv.positive_time_period_microseconds,
cv.positive_time_period_milliseconds,
cv.positive_time_period_minutes,
cv.positive_time_period_seconds,
]:
schema_registry[v] = {"type": "string"}
schema_registry[validate_potentially_and_condition] = get_ref("condition_list")
for v in [pins.gpio_input_pin_schema, pins.gpio_input_pullup_pin_schema]:
schema_registry[v] = get_ref("PIN.GPIO_FULL_INPUT_PIN_SCHEMA")
for v in [pins.internal_gpio_input_pin_schema, pins.gpio_input_pin_schema]:
schema_registry[v] = get_ref("PIN.INPUT_INTERNAL")
for v in [pins.gpio_output_pin_schema, pins.internal_gpio_output_pin_schema]:
schema_registry[v] = get_ref("PIN.GPIO_FULL_OUTPUT_PIN_SCHEMA")
for v in [pins.internal_gpio_output_pin_schema, pins.gpio_output_pin_schema]:
schema_registry[v] = get_ref("PIN.OUTPUT_INTERNAL")
add_module_schemas("CONFIG", cv)
get_jschema("POLLING_COMPONENT", cv.polling_component_schema("60s"))
add_pin_schema()
add_module_schemas("REMOTE_BASE", remote_base)
add_module_schemas("AUTOMATION", automation)
load_components()
add_registries()
definitions["condition_list"] = {
JSC_ONEOF: [
{"type": "array", "items": get_ref(JSC_CONDITION)},
get_ref(JSC_CONDITION),
]
}
output = {
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"definitions": definitions,
JSC_PROPERTIES: base_props,
}
add_core()
add_buses()
add_components()
add_registries() # need second pass, e.g. climate.pid.autotune
add_pin_registry()
solve_pending_refs()
write_file_if_changed(file_path, json.dumps(output))
print(f"Wrote {file_path}")
dump_schema()

View File

@ -1,18 +1,19 @@
import inspect
import json
import argparse
from operator import truediv
import os
import glob
import re
import voluptuous as vol
# NOTE: Cannot import other esphome components globally as a modification in jsonschema
# NOTE: Cannot import other esphome components globally as a modification in vol_schema
# is needed before modules are loaded
import esphome.jsonschema as ejs
import esphome.schema_extractors as ejs
ejs.EnableJsonSchemaCollect = True
ejs.EnableSchemaExtraction = True
# schema format:
# Schemas are splitted in several files in json format, one for core stuff, one for each platform (sensor, binary_sensor, etc) and
# Schemas are split in several files in json format, one for core stuff, one for each platform (sensor, binary_sensor, etc) and
# one for each component (dallas, sim800l, etc.) component can have schema for root component/hub and also for platform component,
# e.g. dallas has hub component which has pin and then has the sensor platform which has sensor name, index, etc.
# When files are loaded they are merged in a single object.
@ -60,15 +61,6 @@ solve_registry = []
def get_component_names():
# return [
# "esphome",
# "esp32",
# "esp8266",
# "logger",
# "sensor",
# "remote_receiver",
# "binary_sensor",
# ]
from esphome.loader import CORE_COMPONENTS_PATH
component_names = ["esphome", "sensor"]
@ -100,7 +92,7 @@ from esphome import automation
from esphome import pins
from esphome.components import remote_base
from esphome.const import CONF_TYPE
from esphome.loader import get_platform
from esphome.loader import get_platform, CORE_COMPONENTS_PATH
from esphome.helpers import write_file_if_changed
from esphome.util import Registry
@ -120,10 +112,12 @@ def write_file(name, obj):
def register_module_schemas(key, module, manifest=None):
for name, schema in module_schemas(module):
register_known_schema(key, name, schema)
if (
manifest and manifest.multi_conf and S_CONFIG_SCHEMA in output[key][S_SCHEMAS]
): # not sure about 2nd part of the if, might be useless config (e.g. as3935)
output[key][S_SCHEMAS][S_CONFIG_SCHEMA]["is_list"] = True
if manifest:
# Multi conf should allow list of components
# not sure about 2nd part of the if, might be useless config (e.g. as3935)
if manifest.multi_conf and S_CONFIG_SCHEMA in output[key][S_SCHEMAS]:
output[key][S_SCHEMAS][S_CONFIG_SCHEMA]["is_list"] = True
def register_known_schema(module, name, schema):
@ -265,13 +259,58 @@ def do_esp8266():
def fix_remote_receiver():
output["remote_receiver.binary_sensor"]["schemas"]["CONFIG_SCHEMA"] = {
remote_receiver_schema = output["remote_receiver.binary_sensor"]["schemas"]
remote_receiver_schema["CONFIG_SCHEMA"] = {
"type": "schema",
"schema": {
"extends": ["binary_sensor.BINARY_SENSOR_SCHEMA", "core.COMPONENT_SCHEMA"],
"config_vars": output["remote_base"]["binary"],
"config_vars": output["remote_base"].pop("binary"),
},
}
remote_receiver_schema["CONFIG_SCHEMA"]["schema"]["config_vars"]["receiver_id"] = {
"key": "GeneratedID",
"use_id_type": "remote_base::RemoteReceiverBase",
"type": "use_id",
}
def fix_script():
output["script"][S_SCHEMAS][S_CONFIG_SCHEMA][S_TYPE] = S_SCHEMA
config_schema = output["script"][S_SCHEMAS][S_CONFIG_SCHEMA]
config_schema[S_SCHEMA][S_CONFIG_VARS]["id"]["id_type"] = {
"class": "script::Script"
}
config_schema["is_list"] = True
def get_logger_tags():
pattern = re.compile(r'^static const char \*const TAG = "(\w.*)";', re.MULTILINE)
# tags not in components dir
tags = [
"app",
"component",
"entity_base",
"scheduler",
"api.service",
]
for x in os.walk(CORE_COMPONENTS_PATH):
for y in glob.glob(os.path.join(x[0], "*.cpp")):
with open(y, encoding="utf-8") as file:
data = file.read()
match = pattern.search(data)
if match:
tags.append(match.group(1))
return tags
def add_logger_tags():
tags = get_logger_tags()
logs = output["logger"]["schemas"]["CONFIG_SCHEMA"]["schema"]["config_vars"][
"logs"
]["schema"]["config_vars"]
for t in tags:
logs[t] = logs["string"].copy()
logs.pop("string")
def add_referenced_recursive(referenced_schemas, config_var, path, eat_schema=False):
@ -401,7 +440,7 @@ def shrink():
else:
print("expected extends here!" + x)
arr_s = merge(key_s, arr_s)
if arr_s[S_TYPE] == "enum":
if arr_s[S_TYPE] in ["enum", "typed"]:
arr_s.pop(S_SCHEMA)
else:
arr_s.pop(S_EXTENDS)
@ -491,14 +530,20 @@ def build_schema():
if domain not in platforms:
if manifest.config_schema is not None:
core_components[domain] = {}
if len(manifest.dependencies) > 0:
core_components[domain]["dependencies"] = manifest.dependencies
register_module_schemas(domain, manifest.module, manifest)
for platform in platforms:
platform_manifest = get_platform(domain=platform, platform=domain)
if platform_manifest is not None:
output[platform][S_COMPONENTS][domain] = {}
if len(platform_manifest.dependencies) > 0:
output[platform][S_COMPONENTS][domain][
"dependencies"
] = platform_manifest.dependencies
register_module_schemas(
f"{domain}.{platform}", platform_manifest.module
f"{domain}.{platform}", platform_manifest.module, platform_manifest
)
# Do registries
@ -517,6 +562,8 @@ def build_schema():
do_esp8266()
do_esp32()
fix_remote_receiver()
fix_script()
add_logger_tags()
shrink()
# aggregate components, so all component info is in same file, otherwise we have dallas.json, dallas.sensor.json, etc.
@ -585,7 +632,7 @@ def convert_1(schema, config_var, path):
assert S_EXTENDS not in config_var
if not S_TYPE in config_var:
config_var[S_TYPE] = S_SCHEMA
assert config_var[S_TYPE] == S_SCHEMA
# assert config_var[S_TYPE] == S_SCHEMA
if S_SCHEMA not in config_var:
config_var[S_SCHEMA] = {}
@ -662,7 +709,7 @@ def convert_1(schema, config_var, path):
elif repr_schema in ejs.hidden_schemas:
schema_type = ejs.hidden_schemas[repr_schema]
data = schema(ejs.jschema_extractor)
data = schema(ejs.SCHEMA_EXTRACT)
# enums, e.g. esp32/variant
if schema_type == "one_of":
@ -672,8 +719,9 @@ def convert_1(schema, config_var, path):
config_var[S_TYPE] = "enum"
config_var["values"] = list(data.keys())
elif schema_type == "maybe":
config_var[S_TYPE] = "maybe"
config_var["schema"] = convert_config(data, path + "/maybe")["schema"]
config_var[S_TYPE] = S_SCHEMA
config_var["maybe"] = data[1]
config_var["schema"] = convert_config(data[0], path + "/maybe")["schema"]
# esphome/on_boot
elif schema_type == "automation":
extra_schema = None
@ -717,8 +765,50 @@ def convert_1(schema, config_var, path):
elif schema_type == "sensor":
schema = data
convert_1(data, config_var, path + "/trigger")
elif schema_type == "declare_id":
# pylint: disable=protected-access
parents = data._parents
config_var["id_type"] = {
"class": str(data.base),
"parents": [str(x.base) for x in parents]
if isinstance(parents, list)
else None,
}
elif schema_type == "use_id":
if inspect.ismodule(data):
m_attr_obj = getattr(data, "CONFIG_SCHEMA")
use_schema = known_schemas.get(repr(m_attr_obj))
if use_schema:
[output_module, output_name] = use_schema[0][1].split(".")
use_id_config = output[output_module][S_SCHEMAS][output_name]
config_var["use_id_type"] = use_id_config["schema"]["config_vars"][
"id"
]["id_type"]["class"]
config_var[S_TYPE] = "use_id"
else:
print("TODO deferred?")
else:
if isinstance(data, str):
# TODO: Figure out why pipsolar does this
config_var["use_id_type"] = data
else:
config_var["use_id_type"] = str(data.base)
config_var[S_TYPE] = "use_id"
else:
raise Exception("Unknown extracted schema type")
elif config_var.get("key") == "GeneratedID":
if path == "i2c/CONFIG_SCHEMA/extL/all/id":
config_var["id_type"] = {"class": "i2c::I2CBus", "parents": ["Component"]}
elif path == "uart/CONFIG_SCHEMA/val 1/extL/all/id":
config_var["id_type"] = {
"class": "uart::UARTComponent",
"parents": ["Component"],
}
elif path == "pins/esp32/val 1/id":
config_var["id_type"] = "pin"
else:
raise Exception("Cannot determine id_type for " + path)
elif repr_schema in ejs.registry_schemas:
solve_registry.append((ejs.registry_schemas[repr_schema], config_var))
@ -787,7 +877,13 @@ def convert_keys(converted, schema, path):
result["key"] = "Optional"
else:
converted["key"] = "String"
converted["key_dump"] = str(k)
key_string_match = re.search(
r"<function (\w*) at \w*>", str(k), re.IGNORECASE
)
if key_string_match:
converted["key_type"] = key_string_match.group(1)
else:
converted["key_type"] = str(k)
esphome_core.CORE.data = {
esphome_core.KEY_CORE: {esphome_core.KEY_TARGET_PLATFORM: "esp8266"}
@ -808,6 +904,12 @@ def convert_keys(converted, schema, path):
if base_k in result and base_v == result[base_k]:
result.pop(base_k)
converted["schema"][S_CONFIG_VARS][str(k)] = result
if "key" in converted and converted["key"] == "String":
config_vars = converted["schema"]["config_vars"]
assert len(config_vars) == 1
key = list(config_vars.keys())[0]
assert key.startswith("<")
config_vars["string"] = config_vars.pop(key)
build_schema()

View File

@ -452,7 +452,7 @@ def lint_no_removed_in_idf_conversions(fname, match):
replacement = IDF_CONVERSION_FORBIDDEN[match.group(1)]
return (
f"The macro {highlight(match.group(1))} can no longer be used in ESPHome directly. "
f"Plese use {highlight(replacement)} instead."
f"Please use {highlight(replacement)} instead."
)

View File

@ -168,6 +168,13 @@ mqtt:
id: uart0
data: !lambda |-
return {};
on_connect:
- light.turn_on: ${roomname}_lights
- mqtt.publish:
topic: some/topic
payload: Hello
on_disconnect:
- light.turn_off: ${roomname}_lights
i2c:
sda: 21
@ -593,7 +600,6 @@ sensor:
sensor: hlw8012_power
name: "Integration Sensor lazy"
time_unit: s
min_save_interval: 60s
- platform: hmc5883l
address: 0x68
field_strength_x:
@ -1886,6 +1892,7 @@ climate:
- platform: bedjet
name: My Bedjet
ble_client_id: my_bedjet_ble_client
heat_mode: extended
script:
- id: climate_custom

View File

@ -80,6 +80,11 @@ sx1509:
mcp3204:
cs_pin: GPIO23
dac7678:
address: 0x4A
id: dac7678_hub1
internal_reference: true
sensor:
- platform: homeassistant
entity_id: sensor.hello_world
@ -518,6 +523,38 @@ output:
pipsolar_id: inverter0
battery_recharge_voltage:
id: inverter0_battery_recharge_voltage_out
- platform: dac7678
dac7678_id: 'dac7678_hub1'
channel: 0
id: 'dac7678_1_ch0'
- platform: dac7678
dac7678_id: 'dac7678_hub1'
channel: 1
id: 'dac7678_1_ch1'
- platform: dac7678
dac7678_id: 'dac7678_hub1'
channel: 2
id: 'dac7678_1_ch2'
- platform: dac7678
dac7678_id: 'dac7678_hub1'
channel: 3
id: 'dac7678_1_ch3'
- platform: dac7678
dac7678_id: 'dac7678_hub1'
channel: 4
id: 'dac7678_1_ch4'
- platform: dac7678
dac7678_id: 'dac7678_hub1'
channel: 5
id: 'dac7678_1_ch5'
- platform: dac7678
dac7678_id: 'dac7678_hub1'
channel: 6
id: 'dac7678_1_ch6'
- platform: dac7678
dac7678_id: 'dac7678_hub1'
channel: 7
id: 'dac7678_1_ch7'
esp32_camera:
name: ESP-32 Camera
data_pins: [GPIO17, GPIO35, GPIO34, GPIO5, GPIO39, GPIO18, GPIO36, GPIO19]
@ -604,3 +641,29 @@ touchscreen:
- logger.log:
format: Touch at (%d, %d)
args: ["touch.x", "touch.y"]
media_player:
- platform: i2s_audio
name: ${friendly_name}
dac_type: external
i2s_lrclk_pin: GPIO26
i2s_dout_pin: GPIO25
i2s_bclk_pin: GPIO27
mute_pin: GPIO14
on_state:
- media_player.play:
- media_player.play_media: http://localhost/media.mp3
- media_player.play_media: !lambda 'return "http://localhost/media.mp3";'
on_idle:
- media_player.pause:
on_play:
- media_player.stop:
on_pause:
- media_player.toggle:
- wait_until:
media_player.is_idle:
- wait_until:
media_player.is_playing:
- media_player.volume_up:
- media_player.volume_down:
- media_player.volume_set: 50%

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