mirror of
				https://github.com/esphome/esphome.git
				synced 2025-11-04 00:51:49 +00:00 
			
		
		
		
	Compare commits
	
		
			5 Commits
		
	
	
		
			2024.10.0b
			...
			jesserockz
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					6c132be24c | ||
| 
						 | 
					08480d24f0 | ||
| 
						 | 
					41eb178f5a | ||
| 
						 | 
					d7b0060265 | ||
| 
						 | 
					e07f9a5b9c | 
							
								
								
									
										4
									
								
								.github/actions/build-image/action.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/actions/build-image/action.yaml
									
									
									
									
										vendored
									
									
								
							@@ -46,7 +46,7 @@ runs:
 | 
			
		||||
 | 
			
		||||
    - name: Build and push to ghcr by digest
 | 
			
		||||
      id: build-ghcr
 | 
			
		||||
      uses: docker/build-push-action@v6.9.0
 | 
			
		||||
      uses: docker/build-push-action@v6.7.0
 | 
			
		||||
      env:
 | 
			
		||||
        DOCKER_BUILD_SUMMARY: false
 | 
			
		||||
        DOCKER_BUILD_RECORD_UPLOAD: false
 | 
			
		||||
@@ -72,7 +72,7 @@ runs:
 | 
			
		||||
 | 
			
		||||
    - name: Build and push to dockerhub by digest
 | 
			
		||||
      id: build-dockerhub
 | 
			
		||||
      uses: docker/build-push-action@v6.9.0
 | 
			
		||||
      uses: docker/build-push-action@v6.7.0
 | 
			
		||||
      env:
 | 
			
		||||
        DOCKER_BUILD_SUMMARY: false
 | 
			
		||||
        DOCKER_BUILD_RECORD_UPLOAD: false
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/actions/restore-python/action.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/actions/restore-python/action.yml
									
									
									
									
										vendored
									
									
								
							@@ -22,7 +22,7 @@ runs:
 | 
			
		||||
        python-version: ${{ inputs.python-version }}
 | 
			
		||||
    - name: Restore Python virtual environment
 | 
			
		||||
      id: cache-venv
 | 
			
		||||
      uses: actions/cache/restore@v4.1.1
 | 
			
		||||
      uses: actions/cache/restore@v4.0.2
 | 
			
		||||
      with:
 | 
			
		||||
        path: venv
 | 
			
		||||
        # yamllint disable-line rule:line-length
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/ci-docker.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/ci-docker.yml
									
									
									
									
										vendored
									
									
								
							@@ -46,7 +46,7 @@ jobs:
 | 
			
		||||
        with:
 | 
			
		||||
          python-version: "3.9"
 | 
			
		||||
      - name: Set up Docker Buildx
 | 
			
		||||
        uses: docker/setup-buildx-action@v3.7.1
 | 
			
		||||
        uses: docker/setup-buildx-action@v3.6.1
 | 
			
		||||
      - name: Set up QEMU
 | 
			
		||||
        uses: docker/setup-qemu-action@v3.2.0
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										18
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										18
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							@@ -46,7 +46,7 @@ jobs:
 | 
			
		||||
          python-version: ${{ env.DEFAULT_PYTHON }}
 | 
			
		||||
      - name: Restore Python virtual environment
 | 
			
		||||
        id: cache-venv
 | 
			
		||||
        uses: actions/cache@v4.1.1
 | 
			
		||||
        uses: actions/cache@v4.0.2
 | 
			
		||||
        with:
 | 
			
		||||
          path: venv
 | 
			
		||||
          # yamllint disable-line rule:line-length
 | 
			
		||||
@@ -302,22 +302,20 @@ jobs:
 | 
			
		||||
 | 
			
		||||
      - name: Cache platformio
 | 
			
		||||
        if: github.ref == 'refs/heads/dev'
 | 
			
		||||
        uses: actions/cache@v4.1.1
 | 
			
		||||
        uses: actions/cache@v4.0.2
 | 
			
		||||
        with:
 | 
			
		||||
          path: ~/.platformio
 | 
			
		||||
          key: platformio-${{ matrix.pio_cache_key }}
 | 
			
		||||
 | 
			
		||||
      - name: Cache platformio
 | 
			
		||||
        if: github.ref != 'refs/heads/dev'
 | 
			
		||||
        uses: actions/cache/restore@v4.1.1
 | 
			
		||||
        uses: actions/cache/restore@v4.0.2
 | 
			
		||||
        with:
 | 
			
		||||
          path: ~/.platformio
 | 
			
		||||
          key: platformio-${{ matrix.pio_cache_key }}
 | 
			
		||||
 | 
			
		||||
      - name: Install clang-tidy
 | 
			
		||||
        run: |
 | 
			
		||||
          sudo apt-get update
 | 
			
		||||
          sudo apt-get install clang-tidy-14
 | 
			
		||||
        run: sudo apt-get install clang-tidy-14
 | 
			
		||||
 | 
			
		||||
      - name: Register problem matchers
 | 
			
		||||
        run: |
 | 
			
		||||
@@ -399,9 +397,7 @@ jobs:
 | 
			
		||||
        file: ${{ fromJson(needs.list-components.outputs.components) }}
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Install dependencies
 | 
			
		||||
        run: |
 | 
			
		||||
          sudo apt-get update
 | 
			
		||||
          sudo apt-get install libsdl2-dev
 | 
			
		||||
        run: sudo apt-get install libsdl2-dev
 | 
			
		||||
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
@@ -455,9 +451,7 @@ jobs:
 | 
			
		||||
        run: echo ${{ matrix.components }}
 | 
			
		||||
 | 
			
		||||
      - name: Install dependencies
 | 
			
		||||
        run: |
 | 
			
		||||
          sudo apt-get update
 | 
			
		||||
          sudo apt-get install libsdl2-dev
 | 
			
		||||
        run: sudo apt-get install libsdl2-dev
 | 
			
		||||
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										8
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							@@ -65,7 +65,7 @@ jobs:
 | 
			
		||||
          pip3 install build
 | 
			
		||||
          python3 -m build
 | 
			
		||||
      - name: Publish
 | 
			
		||||
        uses: pypa/gh-action-pypi-publish@v1.10.3
 | 
			
		||||
        uses: pypa/gh-action-pypi-publish@v1.10.1
 | 
			
		||||
 | 
			
		||||
  deploy-docker:
 | 
			
		||||
    name: Build ESPHome ${{ matrix.platform }}
 | 
			
		||||
@@ -90,7 +90,7 @@ jobs:
 | 
			
		||||
          python-version: "3.9"
 | 
			
		||||
 | 
			
		||||
      - name: Set up Docker Buildx
 | 
			
		||||
        uses: docker/setup-buildx-action@v3.7.1
 | 
			
		||||
        uses: docker/setup-buildx-action@v3.6.1
 | 
			
		||||
      - name: Set up QEMU
 | 
			
		||||
        if: matrix.platform != 'linux/amd64'
 | 
			
		||||
        uses: docker/setup-qemu-action@v3.2.0
 | 
			
		||||
@@ -141,7 +141,7 @@ jobs:
 | 
			
		||||
          echo name=$(cat /tmp/platform) >> $GITHUB_OUTPUT
 | 
			
		||||
 | 
			
		||||
      - name: Upload digests
 | 
			
		||||
        uses: actions/upload-artifact@v4.4.2
 | 
			
		||||
        uses: actions/upload-artifact@v4.4.0
 | 
			
		||||
        with:
 | 
			
		||||
          name: digests-${{ steps.sanitize.outputs.name }}
 | 
			
		||||
          path: /tmp/digests
 | 
			
		||||
@@ -184,7 +184,7 @@ jobs:
 | 
			
		||||
          merge-multiple: true
 | 
			
		||||
 | 
			
		||||
      - name: Set up Docker Buildx
 | 
			
		||||
        uses: docker/setup-buildx-action@v3.7.1
 | 
			
		||||
        uses: docker/setup-buildx-action@v3.6.1
 | 
			
		||||
 | 
			
		||||
      - name: Log in to docker hub
 | 
			
		||||
        if: matrix.registry == 'dockerhub'
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/sync-device-classes.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/sync-device-classes.yml
									
									
									
									
										vendored
									
									
								
							@@ -36,7 +36,7 @@ jobs:
 | 
			
		||||
          python ./script/sync-device_class.py
 | 
			
		||||
 | 
			
		||||
      - name: Commit changes
 | 
			
		||||
        uses: peter-evans/create-pull-request@v7.0.5
 | 
			
		||||
        uses: peter-evans/create-pull-request@v7.0.0
 | 
			
		||||
        with:
 | 
			
		||||
          commit-message: "Synchronise Device Classes from Home Assistant"
 | 
			
		||||
          committer: esphomebot <esphome@nabucasa.com>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								CODEOWNERS
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								CODEOWNERS
									
									
									
									
									
								
							@@ -24,7 +24,6 @@ esphome/components/ade7953_i2c/* @angelnu
 | 
			
		||||
esphome/components/ade7953_spi/* @angelnu
 | 
			
		||||
esphome/components/ads1118/* @solomondg1
 | 
			
		||||
esphome/components/ags10/* @mak-42
 | 
			
		||||
esphome/components/aic3204/* @kbx81
 | 
			
		||||
esphome/components/airthings_ble/* @jeromelaban
 | 
			
		||||
esphome/components/airthings_wave_base/* @jeromelaban @kpfleming @ncareau
 | 
			
		||||
esphome/components/airthings_wave_mini/* @ncareau
 | 
			
		||||
@@ -48,7 +47,6 @@ esphome/components/at581x/* @X-Ryl669
 | 
			
		||||
esphome/components/atc_mithermometer/* @ahpohl
 | 
			
		||||
esphome/components/atm90e26/* @danieltwagner
 | 
			
		||||
esphome/components/atm90e32/* @circuitsetup @descipher
 | 
			
		||||
esphome/components/audio_dac/* @kbx81
 | 
			
		||||
esphome/components/b_parasite/* @rbaron
 | 
			
		||||
esphome/components/ballu/* @bazuchan
 | 
			
		||||
esphome/components/bang_bang/* @OttoWinter
 | 
			
		||||
@@ -88,7 +86,7 @@ esphome/components/cap1188/* @mreditor97
 | 
			
		||||
esphome/components/captive_portal/* @OttoWinter
 | 
			
		||||
esphome/components/ccs811/* @habbie
 | 
			
		||||
esphome/components/cd74hc4067/* @asoehlke
 | 
			
		||||
esphome/components/ch422g/* @clydebarrow @jesterret
 | 
			
		||||
esphome/components/ch422g/* @jesterret
 | 
			
		||||
esphome/components/climate/* @esphome/core
 | 
			
		||||
esphome/components/climate_ir/* @glmnet
 | 
			
		||||
esphome/components/color_temperature/* @jesserockz
 | 
			
		||||
@@ -154,7 +152,6 @@ esphome/components/ft63x6/* @gpambrozio
 | 
			
		||||
esphome/components/gcja5/* @gcormier
 | 
			
		||||
esphome/components/gdk101/* @Szewcson
 | 
			
		||||
esphome/components/globals/* @esphome/core
 | 
			
		||||
esphome/components/gp2y1010au0f/* @zry98
 | 
			
		||||
esphome/components/gp8403/* @jesserockz
 | 
			
		||||
esphome/components/gpio/* @esphome/core
 | 
			
		||||
esphome/components/gpio/one_wire/* @ssieb
 | 
			
		||||
@@ -162,7 +159,6 @@ esphome/components/gps/* @coogle
 | 
			
		||||
esphome/components/graph/* @synco
 | 
			
		||||
esphome/components/graphical_display_menu/* @MrMDavidson
 | 
			
		||||
esphome/components/gree/* @orestismers
 | 
			
		||||
esphome/components/grove_gas_mc_v2/* @YorkshireIoT
 | 
			
		||||
esphome/components/grove_tb6612fng/* @max246
 | 
			
		||||
esphome/components/growatt_solar/* @leeuwte
 | 
			
		||||
esphome/components/gt911/* @clydebarrow @jesserockz
 | 
			
		||||
@@ -170,7 +166,6 @@ esphome/components/haier/* @paveldn
 | 
			
		||||
esphome/components/haier/binary_sensor/* @paveldn
 | 
			
		||||
esphome/components/haier/button/* @paveldn
 | 
			
		||||
esphome/components/haier/sensor/* @paveldn
 | 
			
		||||
esphome/components/haier/switch/* @paveldn
 | 
			
		||||
esphome/components/haier/text_sensor/* @paveldn
 | 
			
		||||
esphome/components/havells_solar/* @sourabhjaiswal
 | 
			
		||||
esphome/components/hbridge/fan/* @WeekendWarrior
 | 
			
		||||
@@ -283,7 +278,6 @@ esphome/components/mopeka_std_check/* @Fabian-Schmidt
 | 
			
		||||
esphome/components/mpl3115a2/* @kbickar
 | 
			
		||||
esphome/components/mpu6886/* @fabaff
 | 
			
		||||
esphome/components/ms8607/* @e28eta
 | 
			
		||||
esphome/components/nau7802/* @cujomalainey
 | 
			
		||||
esphome/components/network/* @esphome/core
 | 
			
		||||
esphome/components/nextion/* @edwardtfn @senexcrenshaw
 | 
			
		||||
esphome/components/nextion/binary_sensor/* @senexcrenshaw
 | 
			
		||||
@@ -292,11 +286,9 @@ esphome/components/nextion/switch/* @senexcrenshaw
 | 
			
		||||
esphome/components/nextion/text_sensor/* @senexcrenshaw
 | 
			
		||||
esphome/components/nfc/* @jesserockz @kbx81
 | 
			
		||||
esphome/components/noblex/* @AGalfra
 | 
			
		||||
esphome/components/npi19/* @bakerkj
 | 
			
		||||
esphome/components/number/* @esphome/core
 | 
			
		||||
esphome/components/one_wire/* @ssieb
 | 
			
		||||
esphome/components/online_image/* @guillempages
 | 
			
		||||
esphome/components/opentherm/* @olegtarasov
 | 
			
		||||
esphome/components/ota/* @esphome/core
 | 
			
		||||
esphome/components/output/* @esphome/core
 | 
			
		||||
esphome/components/pca6416a/* @Mat931
 | 
			
		||||
@@ -404,11 +396,9 @@ esphome/components/sun_gtil2/* @Mat931
 | 
			
		||||
esphome/components/switch/* @esphome/core
 | 
			
		||||
esphome/components/t6615/* @tylermenezes
 | 
			
		||||
esphome/components/tca9548a/* @andreashergert1984
 | 
			
		||||
esphome/components/tca9555/* @mobrembski
 | 
			
		||||
esphome/components/tcl112/* @glmnet
 | 
			
		||||
esphome/components/tee501/* @Stock-M
 | 
			
		||||
esphome/components/teleinfo/* @0hax
 | 
			
		||||
esphome/components/tem3200/* @bakerkj
 | 
			
		||||
esphome/components/template/alarm_control_panel/* @grahambrown11 @hwstar
 | 
			
		||||
esphome/components/template/datetime/* @rfdarter
 | 
			
		||||
esphome/components/template/event/* @nohat
 | 
			
		||||
 
 | 
			
		||||
@@ -7,5 +7,3 @@
 | 
			
		||||
For issues, please go to [the issue tracker](https://github.com/esphome/issues/issues).
 | 
			
		||||
 | 
			
		||||
For feature requests, please see [feature requests](https://github.com/esphome/feature-requests/issues).
 | 
			
		||||
 | 
			
		||||
[](https://www.openhomefoundation.org/)
 | 
			
		||||
 
 | 
			
		||||
@@ -33,7 +33,7 @@ RUN \
 | 
			
		||||
        python3-venv=3.11.2-1+b1 \
 | 
			
		||||
        python3-wheel=0.38.4-2 \
 | 
			
		||||
        iputils-ping=3:20221126-1 \
 | 
			
		||||
        git=1:2.39.5-0+deb12u1 \
 | 
			
		||||
        git=1:2.39.2-1.1 \
 | 
			
		||||
        curl=7.88.1-10+deb12u7 \
 | 
			
		||||
        openssh-client=1:9.2p1-2+deb12u3 \
 | 
			
		||||
        python3-cffi=1.15.1-5 \
 | 
			
		||||
 
 | 
			
		||||
@@ -1,173 +0,0 @@
 | 
			
		||||
#include "aic3204.h"
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace aic3204 {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "aic3204";
 | 
			
		||||
 | 
			
		||||
#define ERROR_CHECK(err, msg) \
 | 
			
		||||
  if (!(err)) { \
 | 
			
		||||
    ESP_LOGE(TAG, msg); \
 | 
			
		||||
    this->mark_failed(); \
 | 
			
		||||
    return; \
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
void AIC3204::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up AIC3204...");
 | 
			
		||||
 | 
			
		||||
  // Set register page to 0
 | 
			
		||||
  ERROR_CHECK(this->write_byte(AIC3204_PAGE_CTRL, 0x00), "Set page 0 failed");
 | 
			
		||||
  // Initiate SW reset (PLL is powered off as part of reset)
 | 
			
		||||
  ERROR_CHECK(this->write_byte(AIC3204_SW_RST, 0x01), "Software reset failed");
 | 
			
		||||
  // *** Program clock settings ***
 | 
			
		||||
  // Default is CODEC_CLKIN is from MCLK pin. Don't need to change this.
 | 
			
		||||
  // MDAC*NDAC*FOSR*48Khz = mClk (24.576 MHz when the XMOS is expecting 48kHz audio)
 | 
			
		||||
  // (See page 51 of https://www.ti.com/lit/ml/slaa557/slaa557.pdf)
 | 
			
		||||
  // We do need MDAC*DOSR/32 >= the resource compute level for the processing block
 | 
			
		||||
  // So here 2*128/32 = 8, which is equal to processing block 1 's resource compute
 | 
			
		||||
  // See page 5 of https://www.ti.com/lit/an/slaa404c/slaa404c.pdf for the workflow
 | 
			
		||||
  // for determining these settings.
 | 
			
		||||
 | 
			
		||||
  // Power up NDAC and set to 2
 | 
			
		||||
  ERROR_CHECK(this->write_byte(AIC3204_NDAC, 0x82), "Set NDAC failed");
 | 
			
		||||
  // Power up MDAC and set to 2
 | 
			
		||||
  ERROR_CHECK(this->write_byte(AIC3204_MDAC, 0x82), "Set MDAC failed");
 | 
			
		||||
  // Program DOSR = 128
 | 
			
		||||
  ERROR_CHECK(this->write_byte(AIC3204_DOSR, 0x80), "Set DOSR failed");
 | 
			
		||||
  // Set Audio Interface Config: I2S, 32 bits, DOUT always driving
 | 
			
		||||
  ERROR_CHECK(this->write_byte(AIC3204_CODEC_IF, 0x30), "Set CODEC_IF failed");
 | 
			
		||||
  // For I2S Firmware only, set SCLK/MFP3 pin as Audio Data In
 | 
			
		||||
  ERROR_CHECK(this->write_byte(AIC3204_SCLK_MFP3, 0x02), "Set SCLK/MFP3 failed");
 | 
			
		||||
  ERROR_CHECK(this->write_byte(AIC3204_AUDIO_IF_4, 0x01), "Set AUDIO_IF_4 failed");
 | 
			
		||||
  ERROR_CHECK(this->write_byte(AIC3204_AUDIO_IF_5, 0x01), "Set AUDIO_IF_5 failed");
 | 
			
		||||
  // Program the DAC processing block to be used - PRB_P1
 | 
			
		||||
  ERROR_CHECK(this->write_byte(AIC3204_DAC_SIG_PROC, 0x01), "Set DAC_SIG_PROC failed");
 | 
			
		||||
 | 
			
		||||
  // *** Select Page 1 ***
 | 
			
		||||
  ERROR_CHECK(this->write_byte(AIC3204_PAGE_CTRL, 0x01), "Set page 1 failed");
 | 
			
		||||
  // Enable the internal AVDD_LDO:
 | 
			
		||||
  ERROR_CHECK(this->write_byte(AIC3204_LDO_CTRL, 0x09), "Set LDO_CTRL failed");
 | 
			
		||||
  // *** Program Analog Blocks ***
 | 
			
		||||
  // Disable Internal Crude AVdd in presence of external AVdd supply or before powering up internal AVdd LDO
 | 
			
		||||
  ERROR_CHECK(this->write_byte(AIC3204_PWR_CFG, 0x08), "Set PWR_CFG failed");
 | 
			
		||||
  // Enable Master Analog Power Control
 | 
			
		||||
  ERROR_CHECK(this->write_byte(AIC3204_LDO_CTRL, 0x01), "Set LDO_CTRL failed");
 | 
			
		||||
  // Page 125: Common mode control register, set d6 to 1 to make the full chip common mode = 0.75 v
 | 
			
		||||
  // We are using the internal AVdd regulator with a nominal output of 1.72 V (see LDO_CTRL_REGISTER on page 123)
 | 
			
		||||
  // Page 86 says to only set the common mode voltage to 0.9 v if AVdd >= 1.8... but it isn't on our hardware
 | 
			
		||||
  // We also adjust the HPL and HPR gains to -2dB gian later in this config flow compensate (see page 47)
 | 
			
		||||
  // (All pages refer to the TLV320AIC3204 Application Reference Guide)
 | 
			
		||||
  ERROR_CHECK(this->write_byte(AIC3204_CM_CTRL, 0x40), "Set CM_CTRL failed");
 | 
			
		||||
  // *** Set PowerTune Modes ***
 | 
			
		||||
  // Set the Left & Right DAC PowerTune mode to PTM_P3/4. Use Class-AB driver.
 | 
			
		||||
  ERROR_CHECK(this->write_byte(AIC3204_PLAY_CFG1, 0x00), "Set PLAY_CFG1 failed");
 | 
			
		||||
  ERROR_CHECK(this->write_byte(AIC3204_PLAY_CFG2, 0x00), "Set PLAY_CFG2 failed");
 | 
			
		||||
  // Set the REF charging time to 40ms
 | 
			
		||||
  ERROR_CHECK(this->write_byte(AIC3204_REF_STARTUP, 0x01), "Set REF_STARTUP failed");
 | 
			
		||||
  // HP soft stepping settings for optimal pop performance at power up
 | 
			
		||||
  // Rpop used is 6k with N = 6 and soft step = 20usec. This should work with 47uF coupling
 | 
			
		||||
  // capacitor. Can try N=5,6 or 7 time constants as well. Trade-off delay vs “pop” sound.
 | 
			
		||||
  ERROR_CHECK(this->write_byte(AIC3204_HP_START, 0x25), "Set HP_START failed");
 | 
			
		||||
  // Route Left DAC to HPL
 | 
			
		||||
  ERROR_CHECK(this->write_byte(AIC3204_HPL_ROUTE, 0x08), "Set HPL_ROUTE failed");
 | 
			
		||||
  // Route Right DAC to HPR
 | 
			
		||||
  ERROR_CHECK(this->write_byte(AIC3204_HPR_ROUTE, 0x08), "Set HPR_ROUTE failed");
 | 
			
		||||
  // Route Left DAC to LOL
 | 
			
		||||
  ERROR_CHECK(this->write_byte(AIC3204_LOL_ROUTE, 0x08), "Set LOL_ROUTE failed");
 | 
			
		||||
  // Route Right DAC to LOR
 | 
			
		||||
  ERROR_CHECK(this->write_byte(AIC3204_LOR_ROUTE, 0x08), "Set LOR_ROUTE failed");
 | 
			
		||||
 | 
			
		||||
  // Unmute HPL and set gain to -2dB (see comment before configuring the AIC3204_CM_CTRL register)
 | 
			
		||||
  ERROR_CHECK(this->write_byte(AIC3204_HPL_GAIN, 0x3e), "Set HPL_GAIN failed");
 | 
			
		||||
  // Unmute HPR and set gain to -2dB (see comment before configuring the AIC3204_CM_CTRL register)
 | 
			
		||||
  ERROR_CHECK(this->write_byte(AIC3204_HPR_GAIN, 0x3e), "Set HPR_GAIN failed");
 | 
			
		||||
  // Unmute LOL and set gain to 0dB
 | 
			
		||||
  ERROR_CHECK(this->write_byte(AIC3204_LOL_DRV_GAIN, 0x00), "Set LOL_DRV_GAIN failed");
 | 
			
		||||
  // Unmute LOR and set gain to 0dB
 | 
			
		||||
  ERROR_CHECK(this->write_byte(AIC3204_LOR_DRV_GAIN, 0x00), "Set LOR_DRV_GAIN failed");
 | 
			
		||||
 | 
			
		||||
  // Power up HPL and HPR, LOL and LOR drivers
 | 
			
		||||
  ERROR_CHECK(this->write_byte(AIC3204_OP_PWR_CTRL, 0x3C), "Set OP_PWR_CTRL failed");
 | 
			
		||||
 | 
			
		||||
  // Wait for 2.5 sec for soft stepping to take effect before attempting power-up
 | 
			
		||||
  this->set_timeout(2500, [this]() {
 | 
			
		||||
    // *** Power Up DAC ***
 | 
			
		||||
    // Select Page 0
 | 
			
		||||
    ERROR_CHECK(this->write_byte(AIC3204_PAGE_CTRL, 0x00), "Set PAGE_CTRL failed");
 | 
			
		||||
    // Power up the Left and Right DAC Channels. Route Left data to Left DAC and Right data to Right DAC.
 | 
			
		||||
    // DAC Vol control soft step 1 step per DAC word clock.
 | 
			
		||||
    ERROR_CHECK(this->write_byte(AIC3204_DAC_CH_SET1, 0xd4), "Set DAC_CH_SET1 failed");
 | 
			
		||||
    // Set left and right DAC digital volume control
 | 
			
		||||
    ERROR_CHECK(this->write_volume_(), "Set volume failed");
 | 
			
		||||
    // Unmute left and right channels
 | 
			
		||||
    ERROR_CHECK(this->write_mute_(), "Set mute failed");
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AIC3204::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "AIC3204:");
 | 
			
		||||
  LOG_I2C_DEVICE(this);
 | 
			
		||||
 | 
			
		||||
  if (this->is_failed()) {
 | 
			
		||||
    ESP_LOGE(TAG, "Communication with AIC3204 failed");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AIC3204::set_mute_off() {
 | 
			
		||||
  this->is_muted_ = false;
 | 
			
		||||
  return this->write_mute_();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AIC3204::set_mute_on() {
 | 
			
		||||
  this->is_muted_ = true;
 | 
			
		||||
  return this->write_mute_();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AIC3204::set_auto_mute_mode(uint8_t auto_mute_mode) {
 | 
			
		||||
  this->auto_mute_mode_ = auto_mute_mode & 0x07;
 | 
			
		||||
  ESP_LOGVV(TAG, "Setting auto_mute_mode to 0x%.2x", this->auto_mute_mode_);
 | 
			
		||||
  return this->write_mute_();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AIC3204::set_volume(float volume) {
 | 
			
		||||
  this->volume_ = clamp<float>(volume, 0.0, 1.0);
 | 
			
		||||
  return this->write_volume_();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AIC3204::is_muted() { return this->is_muted_; }
 | 
			
		||||
 | 
			
		||||
float AIC3204::volume() { return this->volume_; }
 | 
			
		||||
 | 
			
		||||
bool AIC3204::write_mute_() {
 | 
			
		||||
  uint8_t mute_mode_byte = this->auto_mute_mode_ << 4;  // auto-mute control is bits 4-6
 | 
			
		||||
  mute_mode_byte |= this->is_muted_ ? 0x0c : 0x00;      // mute bits are 2-3
 | 
			
		||||
  if (!this->write_byte(AIC3204_PAGE_CTRL, 0x00) || !this->write_byte(AIC3204_DAC_CH_SET2, mute_mode_byte)) {
 | 
			
		||||
    ESP_LOGE(TAG, "Writing mute modes failed");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AIC3204::write_volume_() {
 | 
			
		||||
  const int8_t dvc_min_byte = -127;
 | 
			
		||||
  const int8_t dvc_max_byte = 48;
 | 
			
		||||
 | 
			
		||||
  int8_t volume_byte = dvc_min_byte + (this->volume_ * (dvc_max_byte - dvc_min_byte));
 | 
			
		||||
  volume_byte = clamp<int8_t>(volume_byte, dvc_min_byte, dvc_max_byte);
 | 
			
		||||
 | 
			
		||||
  ESP_LOGVV(TAG, "Setting volume to 0x%.2x", volume_byte & 0xFF);
 | 
			
		||||
 | 
			
		||||
  if ((!this->write_byte(AIC3204_PAGE_CTRL, 0x00)) || (!this->write_byte(AIC3204_DACL_VOL_D, volume_byte)) ||
 | 
			
		||||
      (!this->write_byte(AIC3204_DACR_VOL_D, volume_byte))) {
 | 
			
		||||
    ESP_LOGE(TAG, "Writing volume failed");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace aic3204
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -1,88 +0,0 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/components/audio_dac/audio_dac.h"
 | 
			
		||||
#include "esphome/components/i2c/i2c.h"
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#include "esphome/core/hal.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace aic3204 {
 | 
			
		||||
 | 
			
		||||
// TLV320AIC3204 Register Addresses
 | 
			
		||||
// Page 0
 | 
			
		||||
static const uint8_t AIC3204_PAGE_CTRL = 0x00;     // Register 0  - Page Control
 | 
			
		||||
static const uint8_t AIC3204_SW_RST = 0x01;        // Register 1  - Software Reset
 | 
			
		||||
static const uint8_t AIC3204_CLK_PLL1 = 0x04;      // Register 4  - Clock Setting Register 1, Multiplexers
 | 
			
		||||
static const uint8_t AIC3204_CLK_PLL2 = 0x05;      // Register 5  - Clock Setting Register 2, P and R values
 | 
			
		||||
static const uint8_t AIC3204_CLK_PLL3 = 0x06;      // Register 6  - Clock Setting Register 3, J values
 | 
			
		||||
static const uint8_t AIC3204_NDAC = 0x0B;          // Register 11 - NDAC Divider Value
 | 
			
		||||
static const uint8_t AIC3204_MDAC = 0x0C;          // Register 12 - MDAC Divider Value
 | 
			
		||||
static const uint8_t AIC3204_DOSR = 0x0E;          // Register 14 - DOSR Divider Value (LS Byte)
 | 
			
		||||
static const uint8_t AIC3204_NADC = 0x12;          // Register 18 - NADC Divider Value
 | 
			
		||||
static const uint8_t AIC3204_MADC = 0x13;          // Register 19 - MADC Divider Value
 | 
			
		||||
static const uint8_t AIC3204_AOSR = 0x14;          // Register 20 - AOSR Divider Value
 | 
			
		||||
static const uint8_t AIC3204_CODEC_IF = 0x1B;      // Register 27 - CODEC Interface Control
 | 
			
		||||
static const uint8_t AIC3204_AUDIO_IF_4 = 0x1F;    // Register 31 - Audio Interface Setting Register 4
 | 
			
		||||
static const uint8_t AIC3204_AUDIO_IF_5 = 0x20;    // Register 32 - Audio Interface Setting Register 5
 | 
			
		||||
static const uint8_t AIC3204_SCLK_MFP3 = 0x38;     // Register 56 - SCLK/MFP3 Function Control
 | 
			
		||||
static const uint8_t AIC3204_DAC_SIG_PROC = 0x3C;  // Register 60 - DAC Sig Processing Block Control
 | 
			
		||||
static const uint8_t AIC3204_ADC_SIG_PROC = 0x3D;  // Register 61 - ADC Sig Processing Block Control
 | 
			
		||||
static const uint8_t AIC3204_DAC_CH_SET1 = 0x3F;   // Register 63 - DAC Channel Setup 1
 | 
			
		||||
static const uint8_t AIC3204_DAC_CH_SET2 = 0x40;   // Register 64 - DAC Channel Setup 2
 | 
			
		||||
static const uint8_t AIC3204_DACL_VOL_D = 0x41;    // Register 65 - DAC Left Digital Vol Control
 | 
			
		||||
static const uint8_t AIC3204_DACR_VOL_D = 0x42;    // Register 66 - DAC Right Digital Vol Control
 | 
			
		||||
static const uint8_t AIC3204_DRC_ENABLE = 0x44;
 | 
			
		||||
static const uint8_t AIC3204_ADC_CH_SET = 0x51;    // Register 81 - ADC Channel Setup
 | 
			
		||||
static const uint8_t AIC3204_ADC_FGA_MUTE = 0x52;  // Register 82 - ADC Fine Gain Adjust/Mute
 | 
			
		||||
 | 
			
		||||
// Page 1
 | 
			
		||||
static const uint8_t AIC3204_PWR_CFG = 0x01;       // Register 1  - Power Config
 | 
			
		||||
static const uint8_t AIC3204_LDO_CTRL = 0x02;      // Register 2  - LDO Control
 | 
			
		||||
static const uint8_t AIC3204_PLAY_CFG1 = 0x03;     // Register 3  - Playback Config 1
 | 
			
		||||
static const uint8_t AIC3204_PLAY_CFG2 = 0x04;     // Register 4  - Playback Config 2
 | 
			
		||||
static const uint8_t AIC3204_OP_PWR_CTRL = 0x09;   // Register 9  - Output Driver Power Control
 | 
			
		||||
static const uint8_t AIC3204_CM_CTRL = 0x0A;       // Register 10 - Common Mode Control
 | 
			
		||||
static const uint8_t AIC3204_HPL_ROUTE = 0x0C;     // Register 12 - HPL Routing Select
 | 
			
		||||
static const uint8_t AIC3204_HPR_ROUTE = 0x0D;     // Register 13 - HPR Routing Select
 | 
			
		||||
static const uint8_t AIC3204_LOL_ROUTE = 0x0E;     // Register 14 - LOL Routing Selection
 | 
			
		||||
static const uint8_t AIC3204_LOR_ROUTE = 0x0F;     // Register 15 - LOR Routing Selection
 | 
			
		||||
static const uint8_t AIC3204_HPL_GAIN = 0x10;      // Register 16 - HPL Driver Gain
 | 
			
		||||
static const uint8_t AIC3204_HPR_GAIN = 0x11;      // Register 17 - HPR Driver Gain
 | 
			
		||||
static const uint8_t AIC3204_LOL_DRV_GAIN = 0x12;  // Register 18 - LOL Driver Gain Setting
 | 
			
		||||
static const uint8_t AIC3204_LOR_DRV_GAIN = 0x13;  // Register 19 - LOR Driver Gain Setting
 | 
			
		||||
static const uint8_t AIC3204_HP_START = 0x14;      // Register 20 - Headphone Driver Startup
 | 
			
		||||
static const uint8_t AIC3204_LPGA_P_ROUTE = 0x34;  // Register 52 - Left PGA Positive Input Route
 | 
			
		||||
static const uint8_t AIC3204_LPGA_N_ROUTE = 0x36;  // Register 54 - Left PGA Negative Input Route
 | 
			
		||||
static const uint8_t AIC3204_RPGA_P_ROUTE = 0x37;  // Register 55 - Right PGA Positive Input Route
 | 
			
		||||
static const uint8_t AIC3204_RPGA_N_ROUTE = 0x39;  // Register 57 - Right PGA Negative Input Route
 | 
			
		||||
static const uint8_t AIC3204_LPGA_VOL = 0x3B;      // Register 59 - Left PGA Volume
 | 
			
		||||
static const uint8_t AIC3204_RPGA_VOL = 0x3C;      // Register 60 - Right PGA Volume
 | 
			
		||||
static const uint8_t AIC3204_ADC_PTM = 0x3D;       // Register 61 - ADC Power Tune Config
 | 
			
		||||
static const uint8_t AIC3204_AN_IN_CHRG = 0x47;    // Register 71 - Analog Input Quick Charging Config
 | 
			
		||||
static const uint8_t AIC3204_REF_STARTUP = 0x7B;   // Register 123 - Reference Power Up Config
 | 
			
		||||
 | 
			
		||||
class AIC3204 : public audio_dac::AudioDac, public Component, public i2c::I2CDevice {
 | 
			
		||||
 public:
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
  float get_setup_priority() const override { return setup_priority::DATA; }
 | 
			
		||||
 | 
			
		||||
  bool set_mute_off() override;
 | 
			
		||||
  bool set_mute_on() override;
 | 
			
		||||
  bool set_auto_mute_mode(uint8_t auto_mute_mode);
 | 
			
		||||
  bool set_volume(float volume) override;
 | 
			
		||||
 | 
			
		||||
  bool is_muted() override;
 | 
			
		||||
  float volume() override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool write_mute_();
 | 
			
		||||
  bool write_volume_();
 | 
			
		||||
 | 
			
		||||
  uint8_t auto_mute_mode_{0};
 | 
			
		||||
  float volume_{0};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace aic3204
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -1,52 +0,0 @@
 | 
			
		||||
from esphome import automation
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
from esphome.components import i2c
 | 
			
		||||
from esphome.components.audio_dac import AudioDac
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.const import CONF_ID, CONF_MODE
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@kbx81"]
 | 
			
		||||
DEPENDENCIES = ["i2c"]
 | 
			
		||||
 | 
			
		||||
aic3204_ns = cg.esphome_ns.namespace("aic3204")
 | 
			
		||||
AIC3204 = aic3204_ns.class_("AIC3204", AudioDac, cg.Component, i2c.I2CDevice)
 | 
			
		||||
 | 
			
		||||
SetAutoMuteAction = aic3204_ns.class_("SetAutoMuteAction", automation.Action)
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = (
 | 
			
		||||
    cv.Schema(
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(AIC3204),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
    .extend(cv.COMPONENT_SCHEMA)
 | 
			
		||||
    .extend(i2c.i2c_device_schema(0x18))
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
SET_AUTO_MUTE_ACTION_SCHEMA = cv.maybe_simple_value(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(): cv.use_id(AIC3204),
 | 
			
		||||
        cv.Required(CONF_MODE): cv.templatable(cv.int_range(max=7, min=0)),
 | 
			
		||||
    },
 | 
			
		||||
    key=CONF_MODE,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@automation.register_action(
 | 
			
		||||
    "aic3204.set_auto_mute_mode", SetAutoMuteAction, SET_AUTO_MUTE_ACTION_SCHEMA
 | 
			
		||||
)
 | 
			
		||||
async def aic3204_set_volume_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    paren = await cg.get_variable(config[CONF_ID])
 | 
			
		||||
    var = cg.new_Pvariable(action_id, template_arg, paren)
 | 
			
		||||
 | 
			
		||||
    template_ = await cg.templatable(config.get(CONF_MODE), args, int)
 | 
			
		||||
    cg.add(var.set_auto_mute_mode(template_))
 | 
			
		||||
 | 
			
		||||
    return var
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await i2c.register_i2c_device(var, config)
 | 
			
		||||
@@ -1,23 +0,0 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/automation.h"
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "aic3204.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace aic3204 {
 | 
			
		||||
 | 
			
		||||
template<typename... Ts> class SetAutoMuteAction : public Action<Ts...> {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit SetAutoMuteAction(AIC3204 *aic3204) : aic3204_(aic3204) {}
 | 
			
		||||
 | 
			
		||||
  TEMPLATABLE_VALUE(uint8_t, auto_mute_mode)
 | 
			
		||||
 | 
			
		||||
  void play(Ts... x) override { this->aic3204_->set_auto_mute_mode(this->auto_mute_mode_.value(x...)); }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  AIC3204 *aic3204_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace aic3204
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -9,7 +9,7 @@ from esphome.const import (
 | 
			
		||||
    CONF_MQTT_ID,
 | 
			
		||||
    CONF_ON_STATE,
 | 
			
		||||
    CONF_TRIGGER_ID,
 | 
			
		||||
    CONF_WEB_SERVER,
 | 
			
		||||
    CONF_WEB_SERVER_ID,
 | 
			
		||||
)
 | 
			
		||||
from esphome.core import CORE, coroutine_with_priority
 | 
			
		||||
from esphome.cpp_helpers import setup_entity
 | 
			
		||||
@@ -195,8 +195,9 @@ async def setup_alarm_control_panel_core_(var, config):
 | 
			
		||||
    for conf in config.get(CONF_ON_READY, []):
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
			
		||||
        await automation.build_automation(trigger, [], conf)
 | 
			
		||||
    if web_server_config := config.get(CONF_WEB_SERVER):
 | 
			
		||||
        await web_server.add_entity_config(var, web_server_config)
 | 
			
		||||
    if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None:
 | 
			
		||||
        web_server_ = await cg.get_variable(webserver_id)
 | 
			
		||||
        web_server.add_entity_to_sorting_list(web_server_, var, config)
 | 
			
		||||
    if mqtt_id := config.get(CONF_MQTT_ID):
 | 
			
		||||
        mqtt_ = cg.new_Pvariable(mqtt_id, var)
 | 
			
		||||
        await mqtt.register_mqtt_component(mqtt_, config)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,26 +1,26 @@
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
from esphome import automation, core
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
from esphome.components import font
 | 
			
		||||
import esphome.components.image as espImage
 | 
			
		||||
from esphome.components.image import (
 | 
			
		||||
    CONF_USE_TRANSPARENCY,
 | 
			
		||||
    LOCAL_SCHEMA,
 | 
			
		||||
    SOURCE_LOCAL,
 | 
			
		||||
    SOURCE_WEB,
 | 
			
		||||
    WEB_SCHEMA,
 | 
			
		||||
    SOURCE_WEB,
 | 
			
		||||
    SOURCE_LOCAL,
 | 
			
		||||
)
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_FILE,
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_PATH,
 | 
			
		||||
    CONF_RAW_DATA_ID,
 | 
			
		||||
    CONF_REPEAT,
 | 
			
		||||
    CONF_RESIZE,
 | 
			
		||||
    CONF_SOURCE,
 | 
			
		||||
    CONF_TYPE,
 | 
			
		||||
    CONF_SOURCE,
 | 
			
		||||
    CONF_PATH,
 | 
			
		||||
    CONF_URL,
 | 
			
		||||
)
 | 
			
		||||
from esphome.core import CORE, HexInt
 | 
			
		||||
@@ -172,9 +172,6 @@ async def to_code(config):
 | 
			
		||||
        path = CORE.relative_config_path(conf_file[CONF_PATH])
 | 
			
		||||
    elif conf_file[CONF_SOURCE] == SOURCE_WEB:
 | 
			
		||||
        path = espImage.compute_local_image_path(conf_file).as_posix()
 | 
			
		||||
    else:
 | 
			
		||||
        raise core.EsphomeError(f"Unknown animation source: {conf_file[CONF_SOURCE]}")
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        image = Image.open(path)
 | 
			
		||||
    except Exception as e:
 | 
			
		||||
@@ -186,12 +183,13 @@ async def to_code(config):
 | 
			
		||||
        new_width_max, new_height_max = config[CONF_RESIZE]
 | 
			
		||||
        ratio = min(new_width_max / width, new_height_max / height)
 | 
			
		||||
        width, height = int(width * ratio), int(height * ratio)
 | 
			
		||||
    elif width > 500 or height > 500:
 | 
			
		||||
        _LOGGER.warning(
 | 
			
		||||
            'The image "%s" you requested is very big. Please consider'
 | 
			
		||||
            " using the resize parameter.",
 | 
			
		||||
            path,
 | 
			
		||||
        )
 | 
			
		||||
    else:
 | 
			
		||||
        if width > 500 or height > 500:
 | 
			
		||||
            _LOGGER.warning(
 | 
			
		||||
                'The image "%s" you requested is very big. Please consider'
 | 
			
		||||
                " using the resize parameter.",
 | 
			
		||||
                path,
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
    transparent = config[CONF_USE_TRANSPARENCY]
 | 
			
		||||
 | 
			
		||||
@@ -308,8 +306,6 @@ async def to_code(config):
 | 
			
		||||
            if transparent:
 | 
			
		||||
                alpha = image.split()[-1]
 | 
			
		||||
                has_alpha = alpha.getextrema()[0] < 0xFF
 | 
			
		||||
            else:
 | 
			
		||||
                has_alpha = False
 | 
			
		||||
            frame = image.convert("1", dither=Image.Dither.NONE)
 | 
			
		||||
            if CONF_RESIZE in config:
 | 
			
		||||
                frame = frame.resize([width, height])
 | 
			
		||||
 
 | 
			
		||||
@@ -62,8 +62,6 @@ service APIConnection {
 | 
			
		||||
  rpc unsubscribe_bluetooth_le_advertisements(UnsubscribeBluetoothLEAdvertisementsRequest) returns (void) {}
 | 
			
		||||
 | 
			
		||||
  rpc subscribe_voice_assistant(SubscribeVoiceAssistantRequest) returns (void) {}
 | 
			
		||||
  rpc voice_assistant_get_configuration(VoiceAssistantConfigurationRequest) returns (VoiceAssistantConfigurationResponse) {}
 | 
			
		||||
  rpc voice_assistant_set_configuration(VoiceAssistantSetConfiguration) returns (void) {}
 | 
			
		||||
 | 
			
		||||
  rpc alarm_control_panel_command (AlarmControlPanelCommandRequest) returns (void) {}
 | 
			
		||||
}
 | 
			
		||||
@@ -1120,7 +1118,6 @@ message MediaPlayerSupportedFormat {
 | 
			
		||||
  uint32 sample_rate = 2;
 | 
			
		||||
  uint32 num_channels = 3;
 | 
			
		||||
  MediaPlayerFormatPurpose purpose = 4;
 | 
			
		||||
  uint32 sample_bytes = 5;
 | 
			
		||||
}
 | 
			
		||||
message ListEntitiesMediaPlayerResponse {
 | 
			
		||||
  option (id) = 63;
 | 
			
		||||
@@ -1573,36 +1570,6 @@ message VoiceAssistantAnnounceFinished {
 | 
			
		||||
  bool success = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message VoiceAssistantWakeWord {
 | 
			
		||||
  string id = 1;
 | 
			
		||||
  string wake_word = 2;
 | 
			
		||||
  repeated string trained_languages = 3;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message VoiceAssistantConfigurationRequest {
 | 
			
		||||
  option (id) = 121;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  option (ifdef) = "USE_VOICE_ASSISTANT";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message VoiceAssistantConfigurationResponse {
 | 
			
		||||
  option (id) = 122;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_VOICE_ASSISTANT";
 | 
			
		||||
 | 
			
		||||
  repeated VoiceAssistantWakeWord available_wake_words = 1;
 | 
			
		||||
  repeated string active_wake_words = 2;
 | 
			
		||||
  uint32 max_active_wake_words = 3;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message VoiceAssistantSetConfiguration {
 | 
			
		||||
  option (id) = 123;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  option (ifdef) = "USE_VOICE_ASSISTANT";
 | 
			
		||||
 | 
			
		||||
  repeated string active_wake_words = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ==================== ALARM CONTROL PANEL ====================
 | 
			
		||||
enum AlarmControlPanelState {
 | 
			
		||||
  ALARM_STATE_DISARMED = 0;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,4 @@
 | 
			
		||||
#include "api_connection.h"
 | 
			
		||||
#ifdef USE_API
 | 
			
		||||
#include <cerrno>
 | 
			
		||||
#include <cinttypes>
 | 
			
		||||
#include <utility>
 | 
			
		||||
@@ -1033,7 +1032,6 @@ bool APIConnection::send_media_player_info(media_player::MediaPlayer *media_play
 | 
			
		||||
    media_format.sample_rate = supported_format.sample_rate;
 | 
			
		||||
    media_format.num_channels = supported_format.num_channels;
 | 
			
		||||
    media_format.purpose = static_cast<enums::MediaPlayerFormatPurpose>(supported_format.purpose);
 | 
			
		||||
    media_format.sample_bytes = supported_format.sample_bytes;
 | 
			
		||||
    msg.supported_formats.push_back(media_format);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -1225,42 +1223,6 @@ void APIConnection::on_voice_assistant_announce_request(const VoiceAssistantAnno
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
VoiceAssistantConfigurationResponse APIConnection::voice_assistant_get_configuration(
 | 
			
		||||
    const VoiceAssistantConfigurationRequest &msg) {
 | 
			
		||||
  VoiceAssistantConfigurationResponse resp;
 | 
			
		||||
  if (voice_assistant::global_voice_assistant != nullptr) {
 | 
			
		||||
    if (voice_assistant::global_voice_assistant->get_api_connection() != this) {
 | 
			
		||||
      return resp;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto &config = voice_assistant::global_voice_assistant->get_configuration();
 | 
			
		||||
    for (auto &wake_word : config.available_wake_words) {
 | 
			
		||||
      VoiceAssistantWakeWord resp_wake_word;
 | 
			
		||||
      resp_wake_word.id = wake_word.id;
 | 
			
		||||
      resp_wake_word.wake_word = wake_word.wake_word;
 | 
			
		||||
      for (const auto &lang : wake_word.trained_languages) {
 | 
			
		||||
        resp_wake_word.trained_languages.push_back(lang);
 | 
			
		||||
      }
 | 
			
		||||
      resp.available_wake_words.push_back(std::move(resp_wake_word));
 | 
			
		||||
    }
 | 
			
		||||
    for (auto &wake_word_id : config.active_wake_words) {
 | 
			
		||||
      resp.active_wake_words.push_back(wake_word_id);
 | 
			
		||||
    }
 | 
			
		||||
    resp.max_active_wake_words = config.max_active_wake_words;
 | 
			
		||||
  }
 | 
			
		||||
  return resp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void APIConnection::voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) {
 | 
			
		||||
  if (voice_assistant::global_voice_assistant != nullptr) {
 | 
			
		||||
    if (voice_assistant::global_voice_assistant->get_api_connection() != this) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    voice_assistant::global_voice_assistant->on_set_configuration(msg.active_wake_words);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ALARM_CONTROL_PANEL
 | 
			
		||||
@@ -1569,4 +1531,3 @@ void APIConnection::on_fatal_error() {
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,12 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#ifdef USE_API
 | 
			
		||||
#include "api_frame_helper.h"
 | 
			
		||||
#include "api_pb2.h"
 | 
			
		||||
#include "api_pb2_service.h"
 | 
			
		||||
#include "api_server.h"
 | 
			
		||||
#include "esphome/core/application.h"
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
@@ -153,9 +152,6 @@ class APIConnection : public APIServerConnection {
 | 
			
		||||
  void on_voice_assistant_audio(const VoiceAssistantAudio &msg) override;
 | 
			
		||||
  void on_voice_assistant_timer_event_response(const VoiceAssistantTimerEventResponse &msg) override;
 | 
			
		||||
  void on_voice_assistant_announce_request(const VoiceAssistantAnnounceRequest &msg) override;
 | 
			
		||||
  VoiceAssistantConfigurationResponse voice_assistant_get_configuration(
 | 
			
		||||
      const VoiceAssistantConfigurationRequest &msg) override;
 | 
			
		||||
  void voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ALARM_CONTROL_PANEL
 | 
			
		||||
@@ -269,4 +265,3 @@ class APIConnection : public APIServerConnection {
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
#include "api_frame_helper.h"
 | 
			
		||||
#ifdef USE_API
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/core/hal.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
@@ -1028,4 +1028,3 @@ APIError APIPlaintextFrameHelper::shutdown(int how) {
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#ifdef USE_API
 | 
			
		||||
 | 
			
		||||
#ifdef USE_API_NOISE
 | 
			
		||||
#include "noise/protocol.h"
 | 
			
		||||
#endif
 | 
			
		||||
@@ -190,4 +190,3 @@ class APIPlaintextFrameHelper : public APIFrameHelper {
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -5149,10 +5149,6 @@ bool MediaPlayerSupportedFormat::decode_varint(uint32_t field_id, ProtoVarInt va
 | 
			
		||||
      this->purpose = value.as_enum<enums::MediaPlayerFormatPurpose>();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 5: {
 | 
			
		||||
      this->sample_bytes = value.as_uint32();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
@@ -5172,7 +5168,6 @@ void MediaPlayerSupportedFormat::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
  buffer.encode_uint32(2, this->sample_rate);
 | 
			
		||||
  buffer.encode_uint32(3, this->num_channels);
 | 
			
		||||
  buffer.encode_enum<enums::MediaPlayerFormatPurpose>(4, this->purpose);
 | 
			
		||||
  buffer.encode_uint32(5, this->sample_bytes);
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void MediaPlayerSupportedFormat::dump_to(std::string &out) const {
 | 
			
		||||
@@ -5195,11 +5190,6 @@ void MediaPlayerSupportedFormat::dump_to(std::string &out) const {
 | 
			
		||||
  out.append("  purpose: ");
 | 
			
		||||
  out.append(proto_enum_to_string<enums::MediaPlayerFormatPurpose>(this->purpose));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  sample_bytes: ");
 | 
			
		||||
  sprintf(buffer, "%" PRIu32, this->sample_bytes);
 | 
			
		||||
  out.append(buffer);
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
@@ -7124,140 +7114,6 @@ void VoiceAssistantAnnounceFinished::dump_to(std::string &out) const {
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
bool VoiceAssistantWakeWord::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 1: {
 | 
			
		||||
      this->id = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 2: {
 | 
			
		||||
      this->wake_word = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 3: {
 | 
			
		||||
      this->trained_languages.push_back(value.as_string());
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void VoiceAssistantWakeWord::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
  buffer.encode_string(1, this->id);
 | 
			
		||||
  buffer.encode_string(2, this->wake_word);
 | 
			
		||||
  for (auto &it : this->trained_languages) {
 | 
			
		||||
    buffer.encode_string(3, it, true);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void VoiceAssistantWakeWord::dump_to(std::string &out) const {
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("VoiceAssistantWakeWord {\n");
 | 
			
		||||
  out.append("  id: ");
 | 
			
		||||
  out.append("'").append(this->id).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  wake_word: ");
 | 
			
		||||
  out.append("'").append(this->wake_word).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  for (const auto &it : this->trained_languages) {
 | 
			
		||||
    out.append("  trained_languages: ");
 | 
			
		||||
    out.append("'").append(it).append("'");
 | 
			
		||||
    out.append("\n");
 | 
			
		||||
  }
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
void VoiceAssistantConfigurationRequest::encode(ProtoWriteBuffer buffer) const {}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void VoiceAssistantConfigurationRequest::dump_to(std::string &out) const {
 | 
			
		||||
  out.append("VoiceAssistantConfigurationRequest {}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
bool VoiceAssistantConfigurationResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 3: {
 | 
			
		||||
      this->max_active_wake_words = value.as_uint32();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
bool VoiceAssistantConfigurationResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 1: {
 | 
			
		||||
      this->available_wake_words.push_back(value.as_message<VoiceAssistantWakeWord>());
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 2: {
 | 
			
		||||
      this->active_wake_words.push_back(value.as_string());
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void VoiceAssistantConfigurationResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
  for (auto &it : this->available_wake_words) {
 | 
			
		||||
    buffer.encode_message<VoiceAssistantWakeWord>(1, it, true);
 | 
			
		||||
  }
 | 
			
		||||
  for (auto &it : this->active_wake_words) {
 | 
			
		||||
    buffer.encode_string(2, it, true);
 | 
			
		||||
  }
 | 
			
		||||
  buffer.encode_uint32(3, this->max_active_wake_words);
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void VoiceAssistantConfigurationResponse::dump_to(std::string &out) const {
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("VoiceAssistantConfigurationResponse {\n");
 | 
			
		||||
  for (const auto &it : this->available_wake_words) {
 | 
			
		||||
    out.append("  available_wake_words: ");
 | 
			
		||||
    it.dump_to(out);
 | 
			
		||||
    out.append("\n");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for (const auto &it : this->active_wake_words) {
 | 
			
		||||
    out.append("  active_wake_words: ");
 | 
			
		||||
    out.append("'").append(it).append("'");
 | 
			
		||||
    out.append("\n");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  out.append("  max_active_wake_words: ");
 | 
			
		||||
  sprintf(buffer, "%" PRIu32, this->max_active_wake_words);
 | 
			
		||||
  out.append(buffer);
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
bool VoiceAssistantSetConfiguration::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 1: {
 | 
			
		||||
      this->active_wake_words.push_back(value.as_string());
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void VoiceAssistantSetConfiguration::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
  for (auto &it : this->active_wake_words) {
 | 
			
		||||
    buffer.encode_string(1, it, true);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void VoiceAssistantSetConfiguration::dump_to(std::string &out) const {
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("VoiceAssistantSetConfiguration {\n");
 | 
			
		||||
  for (const auto &it : this->active_wake_words) {
 | 
			
		||||
    out.append("  active_wake_words: ");
 | 
			
		||||
    out.append("'").append(it).append("'");
 | 
			
		||||
    out.append("\n");
 | 
			
		||||
  }
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
bool ListEntitiesAlarmControlPanelResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 6: {
 | 
			
		||||
 
 | 
			
		||||
@@ -1277,7 +1277,6 @@ class MediaPlayerSupportedFormat : public ProtoMessage {
 | 
			
		||||
  uint32_t sample_rate{0};
 | 
			
		||||
  uint32_t num_channels{0};
 | 
			
		||||
  enums::MediaPlayerFormatPurpose purpose{};
 | 
			
		||||
  uint32_t sample_bytes{0};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
@@ -1849,53 +1848,6 @@ class VoiceAssistantAnnounceFinished : public ProtoMessage {
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class VoiceAssistantWakeWord : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string id{};
 | 
			
		||||
  std::string wake_word{};
 | 
			
		||||
  std::vector<std::string> trained_languages{};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
};
 | 
			
		||||
class VoiceAssistantConfigurationRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
};
 | 
			
		||||
class VoiceAssistantConfigurationResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::vector<VoiceAssistantWakeWord> available_wake_words{};
 | 
			
		||||
  std::vector<std::string> active_wake_words{};
 | 
			
		||||
  uint32_t max_active_wake_words{0};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class VoiceAssistantSetConfiguration : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::vector<std::string> active_wake_words{};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
};
 | 
			
		||||
class ListEntitiesAlarmControlPanelResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string object_id{};
 | 
			
		||||
 
 | 
			
		||||
@@ -496,19 +496,6 @@ bool APIServerConnectionBase::send_voice_assistant_announce_finished(const Voice
 | 
			
		||||
  return this->send_message_<VoiceAssistantAnnounceFinished>(msg, 120);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
bool APIServerConnectionBase::send_voice_assistant_configuration_response(
 | 
			
		||||
    const VoiceAssistantConfigurationResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_voice_assistant_configuration_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<VoiceAssistantConfigurationResponse>(msg, 122);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_ALARM_CONTROL_PANEL
 | 
			
		||||
bool APIServerConnectionBase::send_list_entities_alarm_control_panel_response(
 | 
			
		||||
    const ListEntitiesAlarmControlPanelResponse &msg) {
 | 
			
		||||
@@ -1169,28 +1156,6 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
 | 
			
		||||
      ESP_LOGVV(TAG, "on_voice_assistant_announce_request: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
      this->on_voice_assistant_announce_request(msg);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 121: {
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
      VoiceAssistantConfigurationRequest msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
      ESP_LOGVV(TAG, "on_voice_assistant_configuration_request: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
      this->on_voice_assistant_configuration_request(msg);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 123: {
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
      VoiceAssistantSetConfiguration msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
      ESP_LOGVV(TAG, "on_voice_assistant_set_configuration: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
      this->on_voice_assistant_set_configuration(msg);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
@@ -1681,35 +1646,6 @@ void APIServerConnection::on_subscribe_voice_assistant_request(const SubscribeVo
 | 
			
		||||
  this->subscribe_voice_assistant(msg);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
void APIServerConnection::on_voice_assistant_configuration_request(const VoiceAssistantConfigurationRequest &msg) {
 | 
			
		||||
  if (!this->is_connection_setup()) {
 | 
			
		||||
    this->on_no_setup_connection();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (!this->is_authenticated()) {
 | 
			
		||||
    this->on_unauthenticated_access();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  VoiceAssistantConfigurationResponse ret = this->voice_assistant_get_configuration(msg);
 | 
			
		||||
  if (!this->send_voice_assistant_configuration_response(ret)) {
 | 
			
		||||
    this->on_fatal_error();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
void APIServerConnection::on_voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) {
 | 
			
		||||
  if (!this->is_connection_setup()) {
 | 
			
		||||
    this->on_no_setup_connection();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (!this->is_authenticated()) {
 | 
			
		||||
    this->on_unauthenticated_access();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->voice_assistant_set_configuration(msg);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_ALARM_CONTROL_PANEL
 | 
			
		||||
void APIServerConnection::on_alarm_control_panel_command_request(const AlarmControlPanelCommandRequest &msg) {
 | 
			
		||||
  if (!this->is_connection_setup()) {
 | 
			
		||||
 
 | 
			
		||||
@@ -253,15 +253,6 @@ class APIServerConnectionBase : public ProtoService {
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
  bool send_voice_assistant_announce_finished(const VoiceAssistantAnnounceFinished &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
  virtual void on_voice_assistant_configuration_request(const VoiceAssistantConfigurationRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
  bool send_voice_assistant_configuration_response(const VoiceAssistantConfigurationResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
  virtual void on_voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_ALARM_CONTROL_PANEL
 | 
			
		||||
  bool send_list_entities_alarm_control_panel_response(const ListEntitiesAlarmControlPanelResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
@@ -434,13 +425,6 @@ class APIServerConnection : public APIServerConnectionBase {
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
  virtual void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
  virtual VoiceAssistantConfigurationResponse voice_assistant_get_configuration(
 | 
			
		||||
      const VoiceAssistantConfigurationRequest &msg) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
  virtual void voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_ALARM_CONTROL_PANEL
 | 
			
		||||
  virtual void alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
@@ -542,12 +526,6 @@ class APIServerConnection : public APIServerConnectionBase {
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
  void on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
  void on_voice_assistant_configuration_request(const VoiceAssistantConfigurationRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
  void on_voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_ALARM_CONTROL_PANEL
 | 
			
		||||
  void on_alarm_control_panel_command_request(const AlarmControlPanelCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,4 @@
 | 
			
		||||
#include "api_server.h"
 | 
			
		||||
#ifdef USE_API
 | 
			
		||||
#include <cerrno>
 | 
			
		||||
#include "api_connection.h"
 | 
			
		||||
#include "esphome/components/network/util.h"
 | 
			
		||||
@@ -404,4 +403,3 @@ void APIServer::on_alarm_control_panel_update(alarm_control_panel::AlarmControlP
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,5 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#ifdef USE_API
 | 
			
		||||
#include "api_noise_context.h"
 | 
			
		||||
#include "api_pb2.h"
 | 
			
		||||
#include "api_pb2_service.h"
 | 
			
		||||
@@ -9,6 +7,7 @@
 | 
			
		||||
#include "esphome/core/automation.h"
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/controller.h"
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "list_entities.h"
 | 
			
		||||
#include "subscribe_state.h"
 | 
			
		||||
@@ -154,4 +153,3 @@ template<typename... Ts> class APIConnectedCondition : public Condition<Ts...> {
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,9 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <map>
 | 
			
		||||
#include "api_server.h"
 | 
			
		||||
#ifdef USE_API
 | 
			
		||||
#include "user_services.h"
 | 
			
		||||
#include "api_server.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
@@ -216,4 +216,3 @@ class CustomAPIDevice {
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,10 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "api_server.h"
 | 
			
		||||
#ifdef USE_API
 | 
			
		||||
#include "api_pb2.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
#include "esphome/core/automation.h"
 | 
			
		||||
#include "api_pb2.h"
 | 
			
		||||
#include "api_server.h"
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
@@ -81,4 +81,3 @@ template<typename... Ts> class HomeAssistantServiceCallAction : public Action<Ts
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,4 @@
 | 
			
		||||
#include "list_entities.h"
 | 
			
		||||
#ifdef USE_API
 | 
			
		||||
#include "api_connection.h"
 | 
			
		||||
#include "esphome/core/application.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
@@ -105,4 +104,3 @@ bool ListEntitiesIterator::on_update(update::UpdateEntity *update) { return this
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,9 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#ifdef USE_API
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/component_iterator.h"
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
@@ -87,4 +87,3 @@ class ListEntitiesIterator : public ComponentIterator {
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,4 @@
 | 
			
		||||
#include "subscribe_state.h"
 | 
			
		||||
#ifdef USE_API
 | 
			
		||||
#include "api_connection.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
@@ -85,4 +84,3 @@ InitialStateIterator::InitialStateIterator(APIConnection *client) : client_(clie
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,10 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#ifdef USE_API
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/component_iterator.h"
 | 
			
		||||
#include "esphome/core/controller.h"
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
@@ -82,4 +82,3 @@ class InitialStateIterator : public ComponentIterator {
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -1,57 +0,0 @@
 | 
			
		||||
from esphome import automation
 | 
			
		||||
from esphome.automation import maybe_simple_id
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.const import CONF_ID, CONF_VOLUME
 | 
			
		||||
from esphome.core import coroutine_with_priority
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@kbx81"]
 | 
			
		||||
IS_PLATFORM_COMPONENT = True
 | 
			
		||||
 | 
			
		||||
audio_dac_ns = cg.esphome_ns.namespace("audio_dac")
 | 
			
		||||
AudioDac = audio_dac_ns.class_("AudioDac")
 | 
			
		||||
 | 
			
		||||
MuteOffAction = audio_dac_ns.class_("MuteOffAction", automation.Action)
 | 
			
		||||
MuteOnAction = audio_dac_ns.class_("MuteOnAction", automation.Action)
 | 
			
		||||
SetVolumeAction = audio_dac_ns.class_("SetVolumeAction", automation.Action)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
MUTE_ACTION_SCHEMA = maybe_simple_id(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(): cv.use_id(AudioDac),
 | 
			
		||||
    }
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
SET_VOLUME_ACTION_SCHEMA = cv.maybe_simple_value(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(): cv.use_id(AudioDac),
 | 
			
		||||
        cv.Required(CONF_VOLUME): cv.templatable(cv.percentage),
 | 
			
		||||
    },
 | 
			
		||||
    key=CONF_VOLUME,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@automation.register_action("audio_dac.mute_off", MuteOffAction, MUTE_ACTION_SCHEMA)
 | 
			
		||||
@automation.register_action("audio_dac.mute_on", MuteOnAction, MUTE_ACTION_SCHEMA)
 | 
			
		||||
async def audio_dac_mute_action_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    paren = await cg.get_variable(config[CONF_ID])
 | 
			
		||||
    return cg.new_Pvariable(action_id, template_arg, paren)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@automation.register_action(
 | 
			
		||||
    "audio_dac.set_volume", SetVolumeAction, SET_VOLUME_ACTION_SCHEMA
 | 
			
		||||
)
 | 
			
		||||
async def audio_dac_set_volume_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    paren = await cg.get_variable(config[CONF_ID])
 | 
			
		||||
    var = cg.new_Pvariable(action_id, template_arg, paren)
 | 
			
		||||
 | 
			
		||||
    template_ = await cg.templatable(config.get(CONF_VOLUME), args, float)
 | 
			
		||||
    cg.add(var.set_volume(template_))
 | 
			
		||||
 | 
			
		||||
    return var
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@coroutine_with_priority(100.0)
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    cg.add_define("USE_AUDIO_DAC")
 | 
			
		||||
    cg.add_global(audio_dac_ns.using)
 | 
			
		||||
@@ -1,23 +0,0 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#include "esphome/core/hal.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace audio_dac {
 | 
			
		||||
 | 
			
		||||
class AudioDac {
 | 
			
		||||
 public:
 | 
			
		||||
  virtual bool set_mute_off() = 0;
 | 
			
		||||
  virtual bool set_mute_on() = 0;
 | 
			
		||||
  virtual bool set_volume(float volume) = 0;
 | 
			
		||||
 | 
			
		||||
  virtual bool is_muted() = 0;
 | 
			
		||||
  virtual float volume() = 0;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool is_muted_{false};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace audio_dac
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -1,43 +0,0 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/automation.h"
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "audio_dac.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace audio_dac {
 | 
			
		||||
 | 
			
		||||
template<typename... Ts> class MuteOffAction : public Action<Ts...> {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit MuteOffAction(AudioDac *audio_dac) : audio_dac_(audio_dac) {}
 | 
			
		||||
 | 
			
		||||
  void play(Ts... x) override { this->audio_dac_->set_mute_off(); }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  AudioDac *audio_dac_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<typename... Ts> class MuteOnAction : public Action<Ts...> {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit MuteOnAction(AudioDac *audio_dac) : audio_dac_(audio_dac) {}
 | 
			
		||||
 | 
			
		||||
  void play(Ts... x) override { this->audio_dac_->set_mute_on(); }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  AudioDac *audio_dac_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<typename... Ts> class SetVolumeAction : public Action<Ts...> {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit SetVolumeAction(AudioDac *audio_dac) : audio_dac_(audio_dac) {}
 | 
			
		||||
 | 
			
		||||
  TEMPLATABLE_VALUE(float, volume)
 | 
			
		||||
 | 
			
		||||
  void play(Ts... x) override { this->audio_dac_->set_volume(this->volume_.value(x...)); }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  AudioDac *audio_dac_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace audio_dac
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -157,11 +157,8 @@ void BangBangClimate::switch_to_action_(climate::ClimateAction action) {
 | 
			
		||||
    default:
 | 
			
		||||
      trig = nullptr;
 | 
			
		||||
  }
 | 
			
		||||
  if (trig != nullptr) {
 | 
			
		||||
    trig->trigger();
 | 
			
		||||
  } else {
 | 
			
		||||
    ESP_LOGW(TAG, "trig not set - unsupported action");
 | 
			
		||||
  }
 | 
			
		||||
  assert(trig != nullptr);
 | 
			
		||||
  trig->trigger();
 | 
			
		||||
  this->action = action;
 | 
			
		||||
  this->prev_trigger_ = trig;
 | 
			
		||||
  this->publish_state();
 | 
			
		||||
 
 | 
			
		||||
@@ -13,10 +13,8 @@ float bedjet_temp_to_f(const uint8_t temp) {
 | 
			
		||||
 | 
			
		||||
/** Cleans up the packet before sending. */
 | 
			
		||||
BedjetPacket *BedjetCodec::clean_packet_() {
 | 
			
		||||
  // So far no commands require more than 2 bytes of data
 | 
			
		||||
  if (this->packet_.data_length > 2) {
 | 
			
		||||
    ESP_LOGW(TAG, "Packet may be malformed");
 | 
			
		||||
  }
 | 
			
		||||
  // So far no commands require more than 2 bytes of data.
 | 
			
		||||
  assert(this->packet_.data_length <= 2);
 | 
			
		||||
  for (int i = this->packet_.data_length; i < 2; i++) {
 | 
			
		||||
    this->packet_.data[i] = '\0';
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,7 @@ from esphome.const import (
 | 
			
		||||
    CONF_STATE,
 | 
			
		||||
    CONF_TIMING,
 | 
			
		||||
    CONF_TRIGGER_ID,
 | 
			
		||||
    CONF_WEB_SERVER,
 | 
			
		||||
    CONF_WEB_SERVER_ID,
 | 
			
		||||
    DEVICE_CLASS_BATTERY,
 | 
			
		||||
    DEVICE_CLASS_BATTERY_CHARGING,
 | 
			
		||||
    DEVICE_CLASS_CARBON_MONOXIDE,
 | 
			
		||||
@@ -543,8 +543,9 @@ async def setup_binary_sensor_core_(var, config):
 | 
			
		||||
        mqtt_ = cg.new_Pvariable(mqtt_id, var)
 | 
			
		||||
        await mqtt.register_mqtt_component(mqtt_, config)
 | 
			
		||||
 | 
			
		||||
    if web_server_config := config.get(CONF_WEB_SERVER):
 | 
			
		||||
        await web_server.add_entity_config(var, web_server_config)
 | 
			
		||||
    if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None:
 | 
			
		||||
        web_server_ = await cg.get_variable(webserver_id)
 | 
			
		||||
        web_server.add_entity_to_sorting_list(web_server_, var, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def register_binary_sensor(var, config):
 | 
			
		||||
 
 | 
			
		||||
@@ -145,9 +145,8 @@ FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema(
 | 
			
		||||
    ),
 | 
			
		||||
)
 | 
			
		||||
async def reset_energy_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    var = cg.new_Pvariable(action_id, template_arg)
 | 
			
		||||
    await cg.register_parented(var, config[CONF_ID])
 | 
			
		||||
    return var
 | 
			
		||||
    paren = await cg.get_variable(config[CONF_ID])
 | 
			
		||||
    return cg.new_Pvariable(action_id, template_arg, paren)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
 
 | 
			
		||||
@@ -41,7 +41,7 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
            cv.Optional(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid,
 | 
			
		||||
            cv.Optional(CONF_IBEACON_MAJOR): cv.uint16_t,
 | 
			
		||||
            cv.Optional(CONF_IBEACON_MINOR): cv.uint16_t,
 | 
			
		||||
            cv.Optional(CONF_IBEACON_UUID): esp32_ble_tracker.bt_uuid,
 | 
			
		||||
            cv.Optional(CONF_IBEACON_UUID): cv.uuid,
 | 
			
		||||
            cv.Optional(CONF_TIMEOUT, default="5min"): cv.positive_time_period,
 | 
			
		||||
            cv.Optional(CONF_MIN_RSSI): cv.All(
 | 
			
		||||
                cv.decibel, cv.int_range(min=-100, max=-30)
 | 
			
		||||
@@ -83,7 +83,7 @@ async def to_code(config):
 | 
			
		||||
            cg.add(var.set_service_uuid128(uuid128))
 | 
			
		||||
 | 
			
		||||
    if ibeacon_uuid := config.get(CONF_IBEACON_UUID):
 | 
			
		||||
        ibeacon_uuid = esp32_ble_tracker.as_reversed_hex_array(ibeacon_uuid)
 | 
			
		||||
        ibeacon_uuid = esp32_ble_tracker.as_hex_array(str(ibeacon_uuid))
 | 
			
		||||
        cg.add(var.set_ibeacon_uuid(ibeacon_uuid))
 | 
			
		||||
 | 
			
		||||
        if (ibeacon_major := config.get(CONF_IBEACON_MAJOR)) is not None:
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,7 @@ from esphome.const import (
 | 
			
		||||
    CONF_MQTT_ID,
 | 
			
		||||
    CONF_ON_PRESS,
 | 
			
		||||
    CONF_TRIGGER_ID,
 | 
			
		||||
    CONF_WEB_SERVER,
 | 
			
		||||
    CONF_WEB_SERVER_ID,
 | 
			
		||||
    DEVICE_CLASS_EMPTY,
 | 
			
		||||
    DEVICE_CLASS_IDENTIFY,
 | 
			
		||||
    DEVICE_CLASS_RESTART,
 | 
			
		||||
@@ -97,8 +97,9 @@ async def setup_button_core_(var, config):
 | 
			
		||||
        mqtt_ = cg.new_Pvariable(mqtt_id, var)
 | 
			
		||||
        await mqtt.register_mqtt_component(mqtt_, config)
 | 
			
		||||
 | 
			
		||||
    if web_server_config := config.get(CONF_WEB_SERVER):
 | 
			
		||||
        await web_server.add_entity_config(var, web_server_config)
 | 
			
		||||
    if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None:
 | 
			
		||||
        web_server_ = await cg.get_variable(webserver_id)
 | 
			
		||||
        web_server.add_entity_to_sorting_list(web_server_, var, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def register_button(var, config):
 | 
			
		||||
 
 | 
			
		||||
@@ -1,20 +1,18 @@
 | 
			
		||||
from esphome import pins
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
from esphome.components import i2c
 | 
			
		||||
from esphome.components.i2c import I2CBus
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_I2C_ID,
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_INPUT,
 | 
			
		||||
    CONF_INVERTED,
 | 
			
		||||
    CONF_MODE,
 | 
			
		||||
    CONF_NUMBER,
 | 
			
		||||
    CONF_OPEN_DRAIN,
 | 
			
		||||
    CONF_OUTPUT,
 | 
			
		||||
    CONF_RESTORE_VALUE,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@jesterret", "@clydebarrow"]
 | 
			
		||||
CODEOWNERS = ["@jesterret"]
 | 
			
		||||
DEPENDENCIES = ["i2c"]
 | 
			
		||||
MULTI_CONF = True
 | 
			
		||||
ch422g_ns = cg.esphome_ns.namespace("ch422g")
 | 
			
		||||
@@ -25,36 +23,29 @@ CH422GGPIOPin = ch422g_ns.class_(
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CONF_CH422G = "ch422g"
 | 
			
		||||
 | 
			
		||||
# Note that no address is configurable - each register in the CH422G has a dedicated i2c address
 | 
			
		||||
CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(CONF_ID): cv.declare_id(CH422GComponent),
 | 
			
		||||
        cv.GenerateID(CONF_I2C_ID): cv.use_id(I2CBus),
 | 
			
		||||
    }
 | 
			
		||||
).extend(cv.COMPONENT_SCHEMA)
 | 
			
		||||
CONFIG_SCHEMA = (
 | 
			
		||||
    cv.Schema(
 | 
			
		||||
        {
 | 
			
		||||
            cv.Required(CONF_ID): cv.declare_id(CH422GComponent),
 | 
			
		||||
            cv.Optional(CONF_RESTORE_VALUE, default=False): cv.boolean,
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
    .extend(cv.COMPONENT_SCHEMA)
 | 
			
		||||
    .extend(i2c.i2c_device_schema(0x24))
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    cg.add(var.set_restore_value(config[CONF_RESTORE_VALUE]))
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    # Can't use register_i2c_device because there is no CONF_ADDRESS
 | 
			
		||||
    parent = await cg.get_variable(config[CONF_I2C_ID])
 | 
			
		||||
    cg.add(var.set_i2c_bus(parent))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# This is used as a final validation step so that modes have been fully transformed.
 | 
			
		||||
def pin_mode_check(pin_config, _):
 | 
			
		||||
    if pin_config[CONF_MODE][CONF_INPUT] and pin_config[CONF_NUMBER] >= 8:
 | 
			
		||||
        raise cv.Invalid("CH422G only supports input on pins 0-7")
 | 
			
		||||
    if pin_config[CONF_MODE][CONF_OPEN_DRAIN] and pin_config[CONF_NUMBER] < 8:
 | 
			
		||||
        raise cv.Invalid("CH422G only supports open drain output on pins 8-11")
 | 
			
		||||
    await i2c.register_i2c_device(var, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CH422G_PIN_SCHEMA = pins.gpio_base_schema(
 | 
			
		||||
    CH422GGPIOPin,
 | 
			
		||||
    cv.int_range(min=0, max=11),
 | 
			
		||||
    modes=[CONF_INPUT, CONF_OUTPUT, CONF_OPEN_DRAIN],
 | 
			
		||||
    cv.int_range(min=0, max=7),
 | 
			
		||||
    modes=[CONF_INPUT, CONF_OUTPUT],
 | 
			
		||||
).extend(
 | 
			
		||||
    {
 | 
			
		||||
        cv.Required(CONF_CH422G): cv.use_id(CH422GComponent),
 | 
			
		||||
@@ -62,7 +53,7 @@ CH422G_PIN_SCHEMA = pins.gpio_base_schema(
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pins.PIN_SCHEMA_REGISTRY.register(CONF_CH422G, CH422G_PIN_SCHEMA, pin_mode_check)
 | 
			
		||||
@pins.PIN_SCHEMA_REGISTRY.register(CONF_CH422G, CH422G_PIN_SCHEMA)
 | 
			
		||||
async def ch422g_pin_to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    parent = await cg.get_variable(config[CONF_CH422G])
 | 
			
		||||
 
 | 
			
		||||
@@ -4,33 +4,33 @@
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace ch422g {
 | 
			
		||||
 | 
			
		||||
static const uint8_t CH422G_REG_MODE = 0x24;
 | 
			
		||||
static const uint8_t CH422G_MODE_OUTPUT = 0x01;      // enables output mode on 0-7
 | 
			
		||||
static const uint8_t CH422G_MODE_OPEN_DRAIN = 0x04;  // enables open drain mode on 8-11
 | 
			
		||||
static const uint8_t CH422G_REG_IN = 0x26;           // read reg for input bits
 | 
			
		||||
static const uint8_t CH422G_REG_OUT = 0x38;          // write reg for output bits 0-7
 | 
			
		||||
static const uint8_t CH422G_REG_OUT_UPPER = 0x23;    // write reg for output bits 8-11
 | 
			
		||||
const uint8_t CH422G_REG_IN = 0x26;
 | 
			
		||||
const uint8_t CH422G_REG_OUT = 0x38;
 | 
			
		||||
const uint8_t OUT_REG_DEFAULT_VAL = 0xdf;
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "ch422g";
 | 
			
		||||
 | 
			
		||||
void CH422GComponent::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up CH422G...");
 | 
			
		||||
  // set outputs before mode
 | 
			
		||||
  this->write_outputs_();
 | 
			
		||||
  // Set mode and check for errors
 | 
			
		||||
  if (!this->set_mode_(this->mode_value_) || !this->read_inputs_()) {
 | 
			
		||||
  // Test to see if device exists
 | 
			
		||||
  if (!this->read_inputs_()) {
 | 
			
		||||
    ESP_LOGE(TAG, "CH422G not detected at 0x%02X", this->address_);
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Initialization complete. Warning: %d, Error: %d", this->status_has_warning(),
 | 
			
		||||
                this->status_has_error());
 | 
			
		||||
  // restore defaults over whatever got saved on last boot
 | 
			
		||||
  if (!this->restore_value_) {
 | 
			
		||||
    this->write_output_(OUT_REG_DEFAULT_VAL);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ESP_LOGD(TAG, "Initialization complete. Warning: %d, Error: %d", this->status_has_warning(),
 | 
			
		||||
           this->status_has_error());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CH422GComponent::loop() {
 | 
			
		||||
  // Clear all the previously read flags.
 | 
			
		||||
  this->pin_read_flags_ = 0x00;
 | 
			
		||||
  this->pin_read_cache_ = 0x00;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CH422GComponent::dump_config() {
 | 
			
		||||
@@ -41,82 +41,68 @@ void CH422GComponent::dump_config() {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CH422GComponent::pin_mode(uint8_t pin, gpio::Flags flags) {
 | 
			
		||||
  if (pin < 8) {
 | 
			
		||||
    if (flags & gpio::FLAG_OUTPUT) {
 | 
			
		||||
      this->mode_value_ |= CH422G_MODE_OUTPUT;
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    if (flags & gpio::FLAG_OPEN_DRAIN) {
 | 
			
		||||
      this->mode_value_ |= CH422G_MODE_OPEN_DRAIN;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
// ch422g doesn't have any flag support (needs docs?)
 | 
			
		||||
void CH422GComponent::pin_mode(uint8_t pin, gpio::Flags flags) {}
 | 
			
		||||
 | 
			
		||||
bool CH422GComponent::digital_read(uint8_t pin) {
 | 
			
		||||
  if (this->pin_read_flags_ == 0 || this->pin_read_flags_ & (1 << pin)) {
 | 
			
		||||
  if (this->pin_read_cache_ == 0 || this->pin_read_cache_ & (1 << pin)) {
 | 
			
		||||
    // Read values on first access or in case it's being read again in the same loop
 | 
			
		||||
    this->read_inputs_();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this->pin_read_flags_ |= (1 << pin);
 | 
			
		||||
  return (this->input_bits_ & (1 << pin)) != 0;
 | 
			
		||||
  this->pin_read_cache_ |= (1 << pin);
 | 
			
		||||
  return this->state_mask_ & (1 << pin);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CH422GComponent::digital_write(uint8_t pin, bool value) {
 | 
			
		||||
  if (value) {
 | 
			
		||||
    this->output_bits_ |= (1 << pin);
 | 
			
		||||
    this->write_output_(this->state_mask_ | (1 << pin));
 | 
			
		||||
  } else {
 | 
			
		||||
    this->output_bits_ &= ~(1 << pin);
 | 
			
		||||
    this->write_output_(this->state_mask_ & ~(1 << pin));
 | 
			
		||||
  }
 | 
			
		||||
  this->write_outputs_();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool CH422GComponent::read_inputs_() {
 | 
			
		||||
  if (this->is_failed()) {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  uint8_t result;
 | 
			
		||||
  // reading inputs requires the chip to be in input mode, possibly temporarily.
 | 
			
		||||
  if (this->mode_value_ & CH422G_MODE_OUTPUT) {
 | 
			
		||||
    this->set_mode_(this->mode_value_ & ~CH422G_MODE_OUTPUT);
 | 
			
		||||
    result = this->read_reg_(CH422G_REG_IN);
 | 
			
		||||
    this->set_mode_(this->mode_value_);
 | 
			
		||||
  } else {
 | 
			
		||||
    result = this->read_reg_(CH422G_REG_IN);
 | 
			
		||||
  }
 | 
			
		||||
  this->input_bits_ = result;
 | 
			
		||||
  this->status_clear_warning();
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Write a register. Can't use the standard write_byte() method because there is no single pre-configured i2c address.
 | 
			
		||||
bool CH422GComponent::write_reg_(uint8_t reg, uint8_t value) {
 | 
			
		||||
  auto err = this->bus_->write(reg, &value, 1);
 | 
			
		||||
  if (err != i2c::ERROR_OK) {
 | 
			
		||||
    this->status_set_warning(str_sprintf("write failed for register 0x%X, error %d", reg, err).c_str());
 | 
			
		||||
  uint8_t temp = 0;
 | 
			
		||||
  if ((this->last_error_ = this->read(&temp, 1)) != esphome::i2c::ERROR_OK) {
 | 
			
		||||
    this->status_set_warning(str_sprintf("read_inputs_(): I2C I/O error: %d", (int) this->last_error_).c_str());
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint8_t output = 0;
 | 
			
		||||
  if ((this->last_error_ = this->bus_->read(CH422G_REG_IN, &output, 1)) != esphome::i2c::ERROR_OK) {
 | 
			
		||||
    this->status_set_warning(str_sprintf("read_inputs_(): I2C I/O error: %d", (int) this->last_error_).c_str());
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this->state_mask_ = output;
 | 
			
		||||
  this->status_clear_warning();
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t CH422GComponent::read_reg_(uint8_t reg) {
 | 
			
		||||
  uint8_t value;
 | 
			
		||||
  auto err = this->bus_->read(reg, &value, 1);
 | 
			
		||||
  if (err != i2c::ERROR_OK) {
 | 
			
		||||
    this->status_set_warning(str_sprintf("read failed for register 0x%X, error %d", reg, err).c_str());
 | 
			
		||||
    return 0;
 | 
			
		||||
bool CH422GComponent::write_output_(uint8_t value) {
 | 
			
		||||
  const uint8_t temp = 1;
 | 
			
		||||
  if ((this->last_error_ = this->write(&temp, 1, false)) != esphome::i2c::ERROR_OK) {
 | 
			
		||||
    this->status_set_warning(str_sprintf("write_output_(): I2C I/O error: %d", (int) this->last_error_).c_str());
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint8_t write_mask = value;
 | 
			
		||||
  if ((this->last_error_ = this->bus_->write(CH422G_REG_OUT, &write_mask, 1)) != esphome::i2c::ERROR_OK) {
 | 
			
		||||
    this->status_set_warning(
 | 
			
		||||
        str_sprintf("write_output_(): I2C I/O error: %d for write_mask: %d", (int) this->last_error_, (int) write_mask)
 | 
			
		||||
            .c_str());
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this->state_mask_ = value;
 | 
			
		||||
  this->status_clear_warning();
 | 
			
		||||
  return value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool CH422GComponent::set_mode_(uint8_t mode) { return this->write_reg_(CH422G_REG_MODE, mode); }
 | 
			
		||||
 | 
			
		||||
bool CH422GComponent::write_outputs_() {
 | 
			
		||||
  return this->write_reg_(CH422G_REG_OUT, static_cast<uint8_t>(this->output_bits_)) &&
 | 
			
		||||
         this->write_reg_(CH422G_REG_OUT_UPPER, static_cast<uint8_t>(this->output_bits_ >> 8));
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float CH422GComponent::get_setup_priority() const { return setup_priority::IO; }
 | 
			
		||||
@@ -125,15 +111,12 @@ float CH422GComponent::get_setup_priority() const { return setup_priority::IO; }
 | 
			
		||||
// before other components call our digital_read() method.
 | 
			
		||||
float CH422GComponent::get_loop_priority() const { return 9.0f; }  // Just after WIFI
 | 
			
		||||
 | 
			
		||||
void CH422GGPIOPin::setup() { pin_mode(flags_); }
 | 
			
		||||
void CH422GGPIOPin::pin_mode(gpio::Flags flags) { this->parent_->pin_mode(this->pin_, flags); }
 | 
			
		||||
bool CH422GGPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) ^ this->inverted_; }
 | 
			
		||||
bool CH422GGPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; }
 | 
			
		||||
 | 
			
		||||
void CH422GGPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value ^ this->inverted_); }
 | 
			
		||||
void CH422GGPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); }
 | 
			
		||||
std::string CH422GGPIOPin::dump_summary() const { return str_sprintf("EXIO%u via CH422G", pin_); }
 | 
			
		||||
void CH422GGPIOPin::set_flags(gpio::Flags flags) {
 | 
			
		||||
  flags_ = flags;
 | 
			
		||||
  this->parent_->pin_mode(this->pin_, flags);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace ch422g
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -23,30 +23,32 @@ class CH422GComponent : public Component, public i2c::I2CDevice {
 | 
			
		||||
  void pin_mode(uint8_t pin, gpio::Flags flags);
 | 
			
		||||
 | 
			
		||||
  float get_setup_priority() const override;
 | 
			
		||||
 | 
			
		||||
  float get_loop_priority() const override;
 | 
			
		||||
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
 | 
			
		||||
  void set_restore_value(bool restore_value) { this->restore_value_ = restore_value; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool write_reg_(uint8_t reg, uint8_t value);
 | 
			
		||||
  uint8_t read_reg_(uint8_t reg);
 | 
			
		||||
  bool set_mode_(uint8_t mode);
 | 
			
		||||
  bool read_inputs_();
 | 
			
		||||
  bool write_outputs_();
 | 
			
		||||
 | 
			
		||||
  bool write_output_(uint8_t value);
 | 
			
		||||
 | 
			
		||||
  /// The mask to write as output state - 1 means HIGH, 0 means LOW
 | 
			
		||||
  uint16_t output_bits_{0x00};
 | 
			
		||||
  uint8_t state_mask_{0x00};
 | 
			
		||||
  /// Flags to check if read previously during this loop
 | 
			
		||||
  uint8_t pin_read_flags_ = {0x00};
 | 
			
		||||
  /// Copy of last read values
 | 
			
		||||
  uint8_t input_bits_ = {0x00};
 | 
			
		||||
  /// Copy of the mode value
 | 
			
		||||
  uint8_t mode_value_{};
 | 
			
		||||
  uint8_t pin_read_cache_ = {0x00};
 | 
			
		||||
  /// Storage for last I2C error seen
 | 
			
		||||
  esphome::i2c::ErrorCode last_error_;
 | 
			
		||||
  /// Whether we want to override stored values on expander
 | 
			
		||||
  bool restore_value_{false};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Helper class to expose a CH422G pin as a GPIO pin.
 | 
			
		||||
/// Helper class to expose a CH422G pin as an internal input GPIO pin.
 | 
			
		||||
class CH422GGPIOPin : public GPIOPin {
 | 
			
		||||
 public:
 | 
			
		||||
  void setup() override{};
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void pin_mode(gpio::Flags flags) override;
 | 
			
		||||
  bool digital_read() override;
 | 
			
		||||
  void digital_write(bool value) override;
 | 
			
		||||
@@ -55,13 +57,13 @@ class CH422GGPIOPin : public GPIOPin {
 | 
			
		||||
  void set_parent(CH422GComponent *parent) { parent_ = parent; }
 | 
			
		||||
  void set_pin(uint8_t pin) { pin_ = pin; }
 | 
			
		||||
  void set_inverted(bool inverted) { inverted_ = inverted; }
 | 
			
		||||
  void set_flags(gpio::Flags flags);
 | 
			
		||||
  void set_flags(gpio::Flags flags) { flags_ = flags; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  CH422GComponent *parent_{};
 | 
			
		||||
  uint8_t pin_{};
 | 
			
		||||
  bool inverted_{};
 | 
			
		||||
  gpio::Flags flags_{};
 | 
			
		||||
  CH422GComponent *parent_;
 | 
			
		||||
  uint8_t pin_;
 | 
			
		||||
  bool inverted_;
 | 
			
		||||
  gpio::Flags flags_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace ch422g
 | 
			
		||||
 
 | 
			
		||||
@@ -43,7 +43,7 @@ from esphome.const import (
 | 
			
		||||
    CONF_TEMPERATURE_STEP,
 | 
			
		||||
    CONF_TRIGGER_ID,
 | 
			
		||||
    CONF_VISUAL,
 | 
			
		||||
    CONF_WEB_SERVER,
 | 
			
		||||
    CONF_WEB_SERVER_ID,
 | 
			
		||||
)
 | 
			
		||||
from esphome.core import CORE, coroutine_with_priority
 | 
			
		||||
from esphome.cpp_helpers import setup_entity
 | 
			
		||||
@@ -408,8 +408,9 @@ async def setup_climate_core_(var, config):
 | 
			
		||||
            trigger, [(ClimateCall.operator("ref"), "x")], conf
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    if web_server_config := config.get(CONF_WEB_SERVER):
 | 
			
		||||
        await web_server.add_entity_config(var, web_server_config)
 | 
			
		||||
    if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None:
 | 
			
		||||
        web_server_ = await cg.get_variable(webserver_id)
 | 
			
		||||
        web_server.add_entity_to_sorting_list(web_server_, var, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def register_climate(var, config):
 | 
			
		||||
 
 | 
			
		||||
@@ -17,7 +17,7 @@ from esphome.const import (
 | 
			
		||||
    CONF_TILT_COMMAND_TOPIC,
 | 
			
		||||
    CONF_TILT_STATE_TOPIC,
 | 
			
		||||
    CONF_TRIGGER_ID,
 | 
			
		||||
    CONF_WEB_SERVER,
 | 
			
		||||
    CONF_WEB_SERVER_ID,
 | 
			
		||||
    DEVICE_CLASS_AWNING,
 | 
			
		||||
    DEVICE_CLASS_BLIND,
 | 
			
		||||
    DEVICE_CLASS_CURTAIN,
 | 
			
		||||
@@ -137,6 +137,10 @@ async def setup_cover_core_(var, config):
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
			
		||||
        await automation.build_automation(trigger, [], conf)
 | 
			
		||||
 | 
			
		||||
    if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None:
 | 
			
		||||
        web_server_ = await cg.get_variable(webserver_id)
 | 
			
		||||
        web_server.add_entity_to_sorting_list(web_server_, var, config)
 | 
			
		||||
 | 
			
		||||
    if (mqtt_id := config.get(CONF_MQTT_ID)) is not None:
 | 
			
		||||
        mqtt_ = cg.new_Pvariable(mqtt_id, var)
 | 
			
		||||
        await mqtt.register_mqtt_component(mqtt_, config)
 | 
			
		||||
@@ -152,9 +156,6 @@ async def setup_cover_core_(var, config):
 | 
			
		||||
        if (tilt_command_topic := config.get(CONF_TILT_COMMAND_TOPIC)) is not None:
 | 
			
		||||
            cg.add(mqtt_.set_custom_tilt_command_topic(tilt_command_topic))
 | 
			
		||||
 | 
			
		||||
    if web_server_config := config.get(CONF_WEB_SERVER):
 | 
			
		||||
        await web_server.add_entity_config(var, web_server_config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def register_cover(var, config):
 | 
			
		||||
    if not CORE.has_id(config[CONF_ID]):
 | 
			
		||||
 
 | 
			
		||||
@@ -147,7 +147,6 @@ void CSE7766Component::parse_data_() {
 | 
			
		||||
  float power = 0.0f;
 | 
			
		||||
  if (power_cycle_exceeds_range) {
 | 
			
		||||
    // Datasheet: power cycle exceeding range means active power is 0
 | 
			
		||||
    have_power = true;
 | 
			
		||||
    if (this->power_sensor_ != nullptr) {
 | 
			
		||||
      this->power_sensor_->publish_state(0.0f);
 | 
			
		||||
    }
 | 
			
		||||
@@ -179,15 +178,6 @@ void CSE7766Component::parse_data_() {
 | 
			
		||||
    if (this->apparent_power_sensor_ != nullptr) {
 | 
			
		||||
      this->apparent_power_sensor_->publish_state(apparent_power);
 | 
			
		||||
    }
 | 
			
		||||
    if (have_power && this->reactive_power_sensor_ != nullptr) {
 | 
			
		||||
      const float reactive_power = apparent_power - power;
 | 
			
		||||
      if (reactive_power < 0.0f) {
 | 
			
		||||
        ESP_LOGD(TAG, "Impossible reactive power: %.4f is negative", reactive_power);
 | 
			
		||||
        this->reactive_power_sensor_->publish_state(0.0f);
 | 
			
		||||
      } else {
 | 
			
		||||
        this->reactive_power_sensor_->publish_state(reactive_power);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    if (this->power_factor_sensor_ != nullptr && (have_power || power_cycle_exceeds_range)) {
 | 
			
		||||
      float pf = NAN;
 | 
			
		||||
      if (apparent_power > 0) {
 | 
			
		||||
@@ -242,9 +232,8 @@ void CSE7766Component::dump_config() {
 | 
			
		||||
  LOG_SENSOR("  ", "Power", this->power_sensor_);
 | 
			
		||||
  LOG_SENSOR("  ", "Energy", this->energy_sensor_);
 | 
			
		||||
  LOG_SENSOR("  ", "Apparent Power", this->apparent_power_sensor_);
 | 
			
		||||
  LOG_SENSOR("  ", "Reactive Power", this->reactive_power_sensor_);
 | 
			
		||||
  LOG_SENSOR("  ", "Power Factor", this->power_factor_sensor_);
 | 
			
		||||
  this->check_uart_settings(4800, 1, uart::UART_CONFIG_PARITY_EVEN);
 | 
			
		||||
  this->check_uart_settings(4800);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace cse7766
 | 
			
		||||
 
 | 
			
		||||
@@ -16,9 +16,6 @@ class CSE7766Component : public Component, public uart::UARTDevice {
 | 
			
		||||
  void set_apparent_power_sensor(sensor::Sensor *apparent_power_sensor) {
 | 
			
		||||
    apparent_power_sensor_ = apparent_power_sensor;
 | 
			
		||||
  }
 | 
			
		||||
  void set_reactive_power_sensor(sensor::Sensor *reactive_power_sensor) {
 | 
			
		||||
    reactive_power_sensor_ = reactive_power_sensor;
 | 
			
		||||
  }
 | 
			
		||||
  void set_power_factor_sensor(sensor::Sensor *power_factor_sensor) { power_factor_sensor_ = power_factor_sensor; }
 | 
			
		||||
 | 
			
		||||
  void loop() override;
 | 
			
		||||
@@ -38,7 +35,6 @@ class CSE7766Component : public Component, public uart::UARTDevice {
 | 
			
		||||
  sensor::Sensor *power_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *energy_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *apparent_power_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *reactive_power_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *power_factor_sensor_{nullptr};
 | 
			
		||||
  uint32_t cf_pulses_total_{0};
 | 
			
		||||
  uint16_t cf_pulses_last_{0};
 | 
			
		||||
 
 | 
			
		||||
@@ -8,21 +8,18 @@ from esphome.const import (
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_POWER,
 | 
			
		||||
    CONF_POWER_FACTOR,
 | 
			
		||||
    CONF_REACTIVE_POWER,
 | 
			
		||||
    CONF_VOLTAGE,
 | 
			
		||||
    DEVICE_CLASS_APPARENT_POWER,
 | 
			
		||||
    DEVICE_CLASS_CURRENT,
 | 
			
		||||
    DEVICE_CLASS_ENERGY,
 | 
			
		||||
    DEVICE_CLASS_POWER,
 | 
			
		||||
    DEVICE_CLASS_POWER_FACTOR,
 | 
			
		||||
    DEVICE_CLASS_REACTIVE_POWER,
 | 
			
		||||
    DEVICE_CLASS_VOLTAGE,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    STATE_CLASS_TOTAL_INCREASING,
 | 
			
		||||
    UNIT_AMPERE,
 | 
			
		||||
    UNIT_VOLT,
 | 
			
		||||
    UNIT_VOLT_AMPS,
 | 
			
		||||
    UNIT_VOLT_AMPS_REACTIVE,
 | 
			
		||||
    UNIT_WATT,
 | 
			
		||||
    UNIT_WATT_HOURS,
 | 
			
		||||
)
 | 
			
		||||
@@ -65,12 +62,6 @@ CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
            device_class=DEVICE_CLASS_APPARENT_POWER,
 | 
			
		||||
            state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_REACTIVE_POWER): sensor.sensor_schema(
 | 
			
		||||
            unit_of_measurement=UNIT_VOLT_AMPS_REACTIVE,
 | 
			
		||||
            accuracy_decimals=1,
 | 
			
		||||
            device_class=DEVICE_CLASS_REACTIVE_POWER,
 | 
			
		||||
            state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_POWER_FACTOR): sensor.sensor_schema(
 | 
			
		||||
            accuracy_decimals=2,
 | 
			
		||||
            device_class=DEVICE_CLASS_POWER_FACTOR,
 | 
			
		||||
@@ -79,7 +70,7 @@ CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
    }
 | 
			
		||||
).extend(uart.UART_DEVICE_SCHEMA)
 | 
			
		||||
FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema(
 | 
			
		||||
    "cse7766", baud_rate=4800, parity="EVEN", require_rx=True
 | 
			
		||||
    "cse7766", baud_rate=4800, require_rx=True
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -103,9 +94,6 @@ async def to_code(config):
 | 
			
		||||
    if apparent_power_config := config.get(CONF_APPARENT_POWER):
 | 
			
		||||
        sens = await sensor.new_sensor(apparent_power_config)
 | 
			
		||||
        cg.add(var.set_apparent_power_sensor(sens))
 | 
			
		||||
    if reactive_power_config := config.get(CONF_REACTIVE_POWER):
 | 
			
		||||
        sens = await sensor.new_sensor(reactive_power_config)
 | 
			
		||||
        cg.add(var.set_reactive_power_sensor(sens))
 | 
			
		||||
    if power_factor_config := config.get(CONF_POWER_FACTOR):
 | 
			
		||||
        sens = await sensor.new_sensor(power_factor_config)
 | 
			
		||||
        cg.add(var.set_power_factor_sensor(sens))
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,12 @@
 | 
			
		||||
from esphome import pins
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
from esphome.components import i2c, touchscreen
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.const import CONF_ID, CONF_INTERRUPT_PIN, CONF_RESET_PIN
 | 
			
		||||
 | 
			
		||||
from esphome import pins
 | 
			
		||||
from esphome.components import i2c, touchscreen
 | 
			
		||||
from esphome.const import CONF_INTERRUPT_PIN, CONF_ID, CONF_RESET_PIN
 | 
			
		||||
from .. import cst816_ns
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CST816Touchscreen = cst816_ns.class_(
 | 
			
		||||
    "CST816Touchscreen",
 | 
			
		||||
    touchscreen.Touchscreen,
 | 
			
		||||
@@ -13,14 +14,11 @@ CST816Touchscreen = cst816_ns.class_(
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CST816ButtonListener = cst816_ns.class_("CST816ButtonListener")
 | 
			
		||||
 | 
			
		||||
CONF_SKIP_PROBE = "skip_probe"
 | 
			
		||||
CONFIG_SCHEMA = touchscreen.TOUCHSCREEN_SCHEMA.extend(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(): cv.declare_id(CST816Touchscreen),
 | 
			
		||||
        cv.Optional(CONF_INTERRUPT_PIN): pins.internal_gpio_input_pin_schema,
 | 
			
		||||
        cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema,
 | 
			
		||||
        cv.Optional(CONF_SKIP_PROBE, default=False): cv.boolean,
 | 
			
		||||
    }
 | 
			
		||||
).extend(i2c.i2c_device_schema(0x15))
 | 
			
		||||
 | 
			
		||||
@@ -30,7 +28,6 @@ async def to_code(config):
 | 
			
		||||
    await touchscreen.register_touchscreen(var, config)
 | 
			
		||||
    await i2c.register_i2c_device(var, config)
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_skip_probe(config[CONF_SKIP_PROBE]))
 | 
			
		||||
    if interrupt_pin := config.get(CONF_INTERRUPT_PIN):
 | 
			
		||||
        cg.add(var.set_interrupt_pin(await cg.gpio_pin_expression(interrupt_pin)))
 | 
			
		||||
    if reset_pin := config.get(CONF_RESET_PIN):
 | 
			
		||||
 
 | 
			
		||||
@@ -8,33 +8,32 @@ void CST816Touchscreen::continue_setup_() {
 | 
			
		||||
    this->interrupt_pin_->setup();
 | 
			
		||||
    this->attach_interrupt_(this->interrupt_pin_, gpio::INTERRUPT_FALLING_EDGE);
 | 
			
		||||
  }
 | 
			
		||||
  if (this->read_byte(REG_CHIP_ID, &this->chip_id_)) {
 | 
			
		||||
    switch (this->chip_id_) {
 | 
			
		||||
      case CST820_CHIP_ID:
 | 
			
		||||
      case CST826_CHIP_ID:
 | 
			
		||||
      case CST716_CHIP_ID:
 | 
			
		||||
      case CST816S_CHIP_ID:
 | 
			
		||||
      case CST816D_CHIP_ID:
 | 
			
		||||
      case CST816T_CHIP_ID:
 | 
			
		||||
        break;
 | 
			
		||||
      default:
 | 
			
		||||
        this->mark_failed();
 | 
			
		||||
        this->status_set_error(str_sprintf("Unknown chip ID 0x%02X", this->chip_id_).c_str());
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    this->write_byte(REG_IRQ_CTL, IRQ_EN_MOTION);
 | 
			
		||||
  } else if (!this->skip_probe_) {
 | 
			
		||||
    this->status_set_error("Failed to read chip id");
 | 
			
		||||
  if (!this->read_byte(REG_CHIP_ID, &this->chip_id_)) {
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    esph_log_e(TAG, "Failed to read chip id");
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  switch (this->chip_id_) {
 | 
			
		||||
    case CST820_CHIP_ID:
 | 
			
		||||
    case CST826_CHIP_ID:
 | 
			
		||||
    case CST716_CHIP_ID:
 | 
			
		||||
    case CST816S_CHIP_ID:
 | 
			
		||||
    case CST816D_CHIP_ID:
 | 
			
		||||
    case CST816T_CHIP_ID:
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      this->mark_failed();
 | 
			
		||||
      esph_log_e(TAG, "Unknown chip ID 0x%02X", this->chip_id_);
 | 
			
		||||
      return;
 | 
			
		||||
  }
 | 
			
		||||
  this->write_byte(REG_IRQ_CTL, IRQ_EN_MOTION);
 | 
			
		||||
  if (this->x_raw_max_ == this->x_raw_min_) {
 | 
			
		||||
    this->x_raw_max_ = this->display_->get_native_width();
 | 
			
		||||
  }
 | 
			
		||||
  if (this->y_raw_max_ == this->y_raw_min_) {
 | 
			
		||||
    this->y_raw_max_ = this->display_->get_native_height();
 | 
			
		||||
  }
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "CST816 Touchscreen setup complete");
 | 
			
		||||
  esph_log_config(TAG, "CST816 Touchscreen setup complete");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CST816Touchscreen::update_button_state_(bool state) {
 | 
			
		||||
@@ -46,7 +45,7 @@ void CST816Touchscreen::update_button_state_(bool state) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CST816Touchscreen::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up CST816 Touchscreen...");
 | 
			
		||||
  esph_log_config(TAG, "Setting up CST816 Touchscreen...");
 | 
			
		||||
  if (this->reset_pin_ != nullptr) {
 | 
			
		||||
    this->reset_pin_->setup();
 | 
			
		||||
    this->reset_pin_->digital_write(true);
 | 
			
		||||
@@ -74,7 +73,7 @@ void CST816Touchscreen::update_touches() {
 | 
			
		||||
 | 
			
		||||
  uint16_t x = encode_uint16(data[REG_XPOS_HIGH] & 0xF, data[REG_XPOS_LOW]);
 | 
			
		||||
  uint16_t y = encode_uint16(data[REG_YPOS_HIGH] & 0xF, data[REG_YPOS_LOW]);
 | 
			
		||||
  ESP_LOGV(TAG, "Read touch %d/%d", x, y);
 | 
			
		||||
  esph_log_v(TAG, "Read touch %d/%d", x, y);
 | 
			
		||||
  if (x >= this->x_raw_max_) {
 | 
			
		||||
    this->update_button_state_(true);
 | 
			
		||||
  } else {
 | 
			
		||||
 
 | 
			
		||||
@@ -45,7 +45,6 @@ class CST816Touchscreen : public touchscreen::Touchscreen, public i2c::I2CDevice
 | 
			
		||||
 | 
			
		||||
  void set_interrupt_pin(InternalGPIOPin *pin) { this->interrupt_pin_ = pin; }
 | 
			
		||||
  void set_reset_pin(GPIOPin *pin) { this->reset_pin_ = pin; }
 | 
			
		||||
  void set_skip_probe(bool skip_probe) { this->skip_probe_ = skip_probe; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  void continue_setup_();
 | 
			
		||||
@@ -54,7 +53,6 @@ class CST816Touchscreen : public touchscreen::Touchscreen, public i2c::I2CDevice
 | 
			
		||||
  InternalGPIOPin *interrupt_pin_{};
 | 
			
		||||
  GPIOPin *reset_pin_{};
 | 
			
		||||
  uint8_t chip_id_{};
 | 
			
		||||
  bool skip_probe_{};  // if set, do not expect to be able to probe the controller on the i2c bus.
 | 
			
		||||
  std::vector<CST816ButtonListener *> button_listeners_;
 | 
			
		||||
  bool button_touched_{};
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -18,7 +18,7 @@ from esphome.const import (
 | 
			
		||||
    CONF_TIME_ID,
 | 
			
		||||
    CONF_TRIGGER_ID,
 | 
			
		||||
    CONF_TYPE,
 | 
			
		||||
    CONF_WEB_SERVER,
 | 
			
		||||
    CONF_WEB_SERVER_ID,
 | 
			
		||||
    CONF_YEAR,
 | 
			
		||||
)
 | 
			
		||||
from esphome.core import CORE, coroutine_with_priority
 | 
			
		||||
@@ -26,6 +26,7 @@ from esphome.cpp_generator import MockObjClass
 | 
			
		||||
from esphome.cpp_helpers import setup_entity
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@rfdarter", "@jesserockz"]
 | 
			
		||||
DEPENDENCIES = ["time"]
 | 
			
		||||
 | 
			
		||||
IS_PLATFORM_COMPONENT = True
 | 
			
		||||
 | 
			
		||||
@@ -61,28 +62,20 @@ DATETIME_MODES = [
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _validate_time_present(config):
 | 
			
		||||
    config = config.copy()
 | 
			
		||||
    if CONF_ON_TIME in config and CONF_TIME_ID not in config:
 | 
			
		||||
        time_id = cv.use_id(time.RealTimeClock)(None)
 | 
			
		||||
        config[CONF_TIME_ID] = time_id
 | 
			
		||||
    return config
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
_DATETIME_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(
 | 
			
		||||
    web_server.WEBSERVER_SORTING_SCHEMA,
 | 
			
		||||
    cv.MQTT_COMMAND_COMPONENT_SCHEMA,
 | 
			
		||||
    cv.Schema(
 | 
			
		||||
_DATETIME_SCHEMA = (
 | 
			
		||||
    cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
 | 
			
		||||
    .extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA)
 | 
			
		||||
    .extend(
 | 
			
		||||
        {
 | 
			
		||||
            cv.Optional(CONF_ON_VALUE): automation.validate_automation(
 | 
			
		||||
                {
 | 
			
		||||
                    cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DateTimeStateTrigger),
 | 
			
		||||
                }
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_TIME_ID): cv.use_id(time.RealTimeClock),
 | 
			
		||||
            cv.GenerateID(CONF_TIME_ID): cv.use_id(time.RealTimeClock),
 | 
			
		||||
        }
 | 
			
		||||
    ),
 | 
			
		||||
).add_extra(_validate_time_present)
 | 
			
		||||
    )
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def date_schema(class_: MockObjClass) -> cv.Schema:
 | 
			
		||||
@@ -138,15 +131,15 @@ async def setup_datetime_core_(var, config):
 | 
			
		||||
    if (mqtt_id := config.get(CONF_MQTT_ID)) is not None:
 | 
			
		||||
        mqtt_ = cg.new_Pvariable(mqtt_id, var)
 | 
			
		||||
        await mqtt.register_mqtt_component(mqtt_, config)
 | 
			
		||||
    if web_server_config := config.get(CONF_WEB_SERVER):
 | 
			
		||||
        await web_server.add_entity_config(var, web_server_config)
 | 
			
		||||
    if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None:
 | 
			
		||||
        web_server_ = await cg.get_variable(webserver_id)
 | 
			
		||||
        web_server.add_entity_to_sorting_list(web_server_, var, config)
 | 
			
		||||
    for conf in config.get(CONF_ON_VALUE, []):
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
			
		||||
        await automation.build_automation(trigger, [(cg.ESPTime, "x")], conf)
 | 
			
		||||
 | 
			
		||||
    if CONF_TIME_ID in config:
 | 
			
		||||
        rtc = await cg.get_variable(config[CONF_TIME_ID])
 | 
			
		||||
        cg.add(var.set_rtc(rtc))
 | 
			
		||||
    rtc = await cg.get_variable(config[CONF_TIME_ID])
 | 
			
		||||
    cg.add(var.set_rtc(rtc))
 | 
			
		||||
 | 
			
		||||
    for conf in config.get(CONF_ON_TIME, []):
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID])
 | 
			
		||||
 
 | 
			
		||||
@@ -4,9 +4,8 @@
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/entity_base.h"
 | 
			
		||||
#include "esphome/core/time.h"
 | 
			
		||||
#ifdef USE_TIME
 | 
			
		||||
 | 
			
		||||
#include "esphome/components/time/real_time_clock.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace datetime {
 | 
			
		||||
@@ -20,29 +19,23 @@ class DateTimeBase : public EntityBase {
 | 
			
		||||
 | 
			
		||||
  void add_on_state_callback(std::function<void()> &&callback) { this->state_callback_.add(std::move(callback)); }
 | 
			
		||||
 | 
			
		||||
#ifdef USE_TIME
 | 
			
		||||
  void set_rtc(time::RealTimeClock *rtc) { this->rtc_ = rtc; }
 | 
			
		||||
  time::RealTimeClock *get_rtc() const { return this->rtc_; }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  CallbackManager<void()> state_callback_;
 | 
			
		||||
 | 
			
		||||
#ifdef USE_TIME
 | 
			
		||||
  time::RealTimeClock *rtc_;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  bool has_state_{false};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#ifdef USE_TIME
 | 
			
		||||
class DateTimeStateTrigger : public Trigger<ESPTime> {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit DateTimeStateTrigger(DateTimeBase *parent) {
 | 
			
		||||
    parent->add_on_state_callback([this, parent]() { this->trigger(parent->state_as_esptime()); });
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
}  // namespace datetime
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -192,7 +192,6 @@ void DateTimeEntityRestoreState::apply(DateTimeEntity *time) {
 | 
			
		||||
  time->publish_state();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef USE_TIME
 | 
			
		||||
static const int MAX_TIMESTAMP_DRIFT = 900;  // how far can the clock drift before we consider
 | 
			
		||||
                                             // there has been a drastic time synchronization
 | 
			
		||||
 | 
			
		||||
@@ -246,7 +245,6 @@ bool OnDateTimeTrigger::matches_(const ESPTime &time) const {
 | 
			
		||||
         time.day_of_month == this->parent_->day && time.hour == this->parent_->hour &&
 | 
			
		||||
         time.minute == this->parent_->minute && time.second == this->parent_->second;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
}  // namespace datetime
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -134,7 +134,6 @@ template<typename... Ts> class DateTimeSetAction : public Action<Ts...>, public
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#ifdef USE_TIME
 | 
			
		||||
class OnDateTimeTrigger : public Trigger<>, public Component, public Parented<DateTimeEntity> {
 | 
			
		||||
 public:
 | 
			
		||||
  void loop() override;
 | 
			
		||||
@@ -144,7 +143,6 @@ class OnDateTimeTrigger : public Trigger<>, public Component, public Parented<Da
 | 
			
		||||
 | 
			
		||||
  optional<ESPTime> last_check_;
 | 
			
		||||
};
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
}  // namespace datetime
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -94,7 +94,6 @@ void TimeEntityRestoreState::apply(TimeEntity *time) {
 | 
			
		||||
  time->publish_state();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef USE_TIME
 | 
			
		||||
static const int MAX_TIMESTAMP_DRIFT = 900;  // how far can the clock drift before we consider
 | 
			
		||||
                                             // there has been a drastic time synchronization
 | 
			
		||||
 | 
			
		||||
@@ -146,7 +145,6 @@ bool OnTimeTrigger::matches_(const ESPTime &time) const {
 | 
			
		||||
  return time.is_valid() && time.hour == this->parent_->hour && time.minute == this->parent_->minute &&
 | 
			
		||||
         time.second == this->parent_->second;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
}  // namespace datetime
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -113,7 +113,6 @@ template<typename... Ts> class TimeSetAction : public Action<Ts...>, public Pare
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#ifdef USE_TIME
 | 
			
		||||
class OnTimeTrigger : public Trigger<>, public Component, public Parented<TimeEntity> {
 | 
			
		||||
 public:
 | 
			
		||||
  void loop() override;
 | 
			
		||||
@@ -123,7 +122,6 @@ class OnTimeTrigger : public Trigger<>, public Component, public Parented<TimeEn
 | 
			
		||||
 | 
			
		||||
  optional<ESPTime> last_check_;
 | 
			
		||||
};
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
}  // namespace datetime
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,6 @@ from esphome.const import (
 | 
			
		||||
    CONF_COMPONENTS,
 | 
			
		||||
    CONF_ESPHOME,
 | 
			
		||||
    CONF_FRAMEWORK,
 | 
			
		||||
    CONF_IGNORE_EFUSE_CUSTOM_MAC,
 | 
			
		||||
    CONF_IGNORE_EFUSE_MAC_CRC,
 | 
			
		||||
    CONF_NAME,
 | 
			
		||||
    CONF_PATH,
 | 
			
		||||
@@ -53,7 +52,6 @@ from .const import (  # noqa
 | 
			
		||||
    KEY_SDKCONFIG_OPTIONS,
 | 
			
		||||
    KEY_SUBMODULES,
 | 
			
		||||
    KEY_VARIANT,
 | 
			
		||||
    VARIANT_ESP32,
 | 
			
		||||
    VARIANT_FRIENDLY,
 | 
			
		||||
    VARIANTS,
 | 
			
		||||
)
 | 
			
		||||
@@ -241,7 +239,7 @@ ARDUINO_PLATFORM_VERSION = cv.Version(5, 4, 0)
 | 
			
		||||
# The default/recommended esp-idf framework version
 | 
			
		||||
#  - https://github.com/espressif/esp-idf/releases
 | 
			
		||||
#  - https://api.registry.platformio.org/v3/packages/platformio/tool/framework-espidf
 | 
			
		||||
RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION = cv.Version(4, 4, 8)
 | 
			
		||||
RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION = cv.Version(4, 4, 7)
 | 
			
		||||
# The platformio/espressif32 version to use for esp-idf frameworks
 | 
			
		||||
#  - https://github.com/platformio/platform-espressif32/releases
 | 
			
		||||
#  - https://api.registry.platformio.org/v3/packages/platformio/platform/espressif32
 | 
			
		||||
@@ -377,15 +375,6 @@ def final_validate(config):
 | 
			
		||||
            f"Please specify {CONF_FLASH_SIZE} within esp32 configuration only"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    if (
 | 
			
		||||
        config[CONF_VARIANT] != VARIANT_ESP32
 | 
			
		||||
        and CONF_ADVANCED in (conf_fw := config[CONF_FRAMEWORK])
 | 
			
		||||
        and CONF_IGNORE_EFUSE_MAC_CRC in conf_fw[CONF_ADVANCED]
 | 
			
		||||
    ):
 | 
			
		||||
        raise cv.Invalid(
 | 
			
		||||
            f"{CONF_IGNORE_EFUSE_MAC_CRC} is not supported on {config[CONF_VARIANT]}"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    return config
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -412,10 +401,7 @@ ESP_IDF_FRAMEWORK_SCHEMA = cv.All(
 | 
			
		||||
            },
 | 
			
		||||
            cv.Optional(CONF_ADVANCED, default={}): cv.Schema(
 | 
			
		||||
                {
 | 
			
		||||
                    cv.Optional(
 | 
			
		||||
                        CONF_IGNORE_EFUSE_CUSTOM_MAC, default=False
 | 
			
		||||
                    ): cv.boolean,
 | 
			
		||||
                    cv.Optional(CONF_IGNORE_EFUSE_MAC_CRC): cv.boolean,
 | 
			
		||||
                    cv.Optional(CONF_IGNORE_EFUSE_MAC_CRC, default=False): cv.boolean,
 | 
			
		||||
                }
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_COMPONENTS, default=[]): cv.ensure_list(
 | 
			
		||||
@@ -540,10 +526,8 @@ async def to_code(config):
 | 
			
		||||
        for name, value in conf[CONF_SDKCONFIG_OPTIONS].items():
 | 
			
		||||
            add_idf_sdkconfig_option(name, RawSdkconfigValue(value))
 | 
			
		||||
 | 
			
		||||
        if conf[CONF_ADVANCED][CONF_IGNORE_EFUSE_CUSTOM_MAC]:
 | 
			
		||||
            cg.add_define("USE_ESP32_IGNORE_EFUSE_CUSTOM_MAC")
 | 
			
		||||
        if conf[CONF_ADVANCED].get(CONF_IGNORE_EFUSE_MAC_CRC):
 | 
			
		||||
            add_idf_sdkconfig_option("CONFIG_ESP_MAC_IGNORE_MAC_CRC_ERROR", True)
 | 
			
		||||
        if conf[CONF_ADVANCED][CONF_IGNORE_EFUSE_MAC_CRC]:
 | 
			
		||||
            cg.add_define("USE_ESP32_IGNORE_EFUSE_MAC_CRC")
 | 
			
		||||
            if (framework_ver.major, framework_ver.minor) >= (4, 4):
 | 
			
		||||
                add_idf_sdkconfig_option(
 | 
			
		||||
                    "CONFIG_ESP_PHY_CALIBRATION_AND_DATA_STORAGE", False
 | 
			
		||||
 
 | 
			
		||||
@@ -31,13 +31,6 @@ ESPBTUUID ESPBTUUID::from_raw(const uint8_t *data) {
 | 
			
		||||
  memcpy(ret.uuid_.uuid.uuid128, data, ESP_UUID_LEN_128);
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
ESPBTUUID ESPBTUUID::from_raw_reversed(const uint8_t *data) {
 | 
			
		||||
  ESPBTUUID ret;
 | 
			
		||||
  ret.uuid_.len = ESP_UUID_LEN_128;
 | 
			
		||||
  for (int i = 0; i < ESP_UUID_LEN_128; i++)
 | 
			
		||||
    ret.uuid_.uuid.uuid128[ESP_UUID_LEN_128 - 1 - i] = data[i];
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
ESPBTUUID ESPBTUUID::from_raw(const std::string &data) {
 | 
			
		||||
  ESPBTUUID ret;
 | 
			
		||||
  if (data.length() == 4) {
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,6 @@ class ESPBTUUID {
 | 
			
		||||
  static ESPBTUUID from_uint32(uint32_t uuid);
 | 
			
		||||
 | 
			
		||||
  static ESPBTUUID from_raw(const uint8_t *data);
 | 
			
		||||
  static ESPBTUUID from_raw_reversed(const uint8_t *data);
 | 
			
		||||
 | 
			
		||||
  static ESPBTUUID from_raw(const std::string &data);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -462,16 +462,14 @@ void ESPBTDevice::parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_e
 | 
			
		||||
    ESP_LOGVV(TAG, "  Service UUID: %s", uuid.to_string().c_str());
 | 
			
		||||
  }
 | 
			
		||||
  for (auto &data : this->manufacturer_datas_) {
 | 
			
		||||
    auto ibeacon = ESPBLEiBeacon::from_manufacturer_data(data);
 | 
			
		||||
    if (ibeacon.has_value()) {
 | 
			
		||||
      ESP_LOGVV(TAG, "  Manufacturer iBeacon:");
 | 
			
		||||
      ESP_LOGVV(TAG, "    UUID: %s", ibeacon.value().get_uuid().to_string().c_str());
 | 
			
		||||
      ESP_LOGVV(TAG, "    Major: %u", ibeacon.value().get_major());
 | 
			
		||||
      ESP_LOGVV(TAG, "    Minor: %u", ibeacon.value().get_minor());
 | 
			
		||||
      ESP_LOGVV(TAG, "    TXPower: %d", ibeacon.value().get_signal_power());
 | 
			
		||||
    } else {
 | 
			
		||||
      ESP_LOGVV(TAG, "  Manufacturer ID: %s, data: %s", data.uuid.to_string().c_str(),
 | 
			
		||||
                format_hex_pretty(data.data).c_str());
 | 
			
		||||
    ESP_LOGVV(TAG, "  Manufacturer data: %s", format_hex_pretty(data.data).c_str());
 | 
			
		||||
    if (this->get_ibeacon().has_value()) {
 | 
			
		||||
      auto ibeacon = this->get_ibeacon().value();
 | 
			
		||||
      ESP_LOGVV(TAG, "    iBeacon data:");
 | 
			
		||||
      ESP_LOGVV(TAG, "      UUID: %s", ibeacon.get_uuid().to_string().c_str());
 | 
			
		||||
      ESP_LOGVV(TAG, "      Major: %u", ibeacon.get_major());
 | 
			
		||||
      ESP_LOGVV(TAG, "      Minor: %u", ibeacon.get_minor());
 | 
			
		||||
      ESP_LOGVV(TAG, "      TXPower: %d", ibeacon.get_signal_power());
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  for (auto &data : this->service_datas_) {
 | 
			
		||||
@@ -480,7 +478,7 @@ void ESPBTDevice::parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_e
 | 
			
		||||
    ESP_LOGVV(TAG, "    Data: %s", format_hex_pretty(data.data).c_str());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ESP_LOGVV(TAG, "  Adv data: %s", format_hex_pretty(param.ble_adv, param.adv_data_len + param.scan_rsp_len).c_str());
 | 
			
		||||
  ESP_LOGVV(TAG, "Adv data: %s", format_hex_pretty(param.ble_adv, param.adv_data_len + param.scan_rsp_len).c_str());
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
void ESPBTDevice::parse_adv_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m) {
 | 
			
		||||
 
 | 
			
		||||
@@ -44,10 +44,10 @@ class ESPBLEiBeacon {
 | 
			
		||||
  ESPBLEiBeacon(const uint8_t *data);
 | 
			
		||||
  static optional<ESPBLEiBeacon> from_manufacturer_data(const ServiceData &data);
 | 
			
		||||
 | 
			
		||||
  uint16_t get_major() { return byteswap(this->beacon_data_.major); }
 | 
			
		||||
  uint16_t get_minor() { return byteswap(this->beacon_data_.minor); }
 | 
			
		||||
  uint16_t get_major() { return ((this->beacon_data_.major & 0xFF) << 8) | (this->beacon_data_.major >> 8); }
 | 
			
		||||
  uint16_t get_minor() { return ((this->beacon_data_.minor & 0xFF) << 8) | (this->beacon_data_.minor >> 8); }
 | 
			
		||||
  int8_t get_signal_power() { return this->beacon_data_.signal_power; }
 | 
			
		||||
  ESPBTUUID get_uuid() { return ESPBTUUID::from_raw_reversed(this->beacon_data_.proximity_uuid); }
 | 
			
		||||
  ESPBTUUID get_uuid() { return ESPBTUUID::from_raw(this->beacon_data_.proximity_uuid); }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  struct {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,7 @@
 | 
			
		||||
from esphome import automation
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
from esphome.components import binary_sensor, esp32_ble_server, output
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.const import CONF_ID, CONF_ON_STATE, CONF_TRIGGER_ID
 | 
			
		||||
from esphome.const import CONF_ID
 | 
			
		||||
 | 
			
		||||
AUTO_LOAD = ["esp32_ble_server"]
 | 
			
		||||
CODEOWNERS = ["@jesserockz"]
 | 
			
		||||
@@ -12,36 +11,13 @@ CONF_AUTHORIZED_DURATION = "authorized_duration"
 | 
			
		||||
CONF_AUTHORIZER = "authorizer"
 | 
			
		||||
CONF_BLE_SERVER_ID = "ble_server_id"
 | 
			
		||||
CONF_IDENTIFY_DURATION = "identify_duration"
 | 
			
		||||
CONF_ON_PROVISIONED = "on_provisioned"
 | 
			
		||||
CONF_ON_PROVISIONING = "on_provisioning"
 | 
			
		||||
CONF_ON_START = "on_start"
 | 
			
		||||
CONF_ON_STOP = "on_stop"
 | 
			
		||||
CONF_STATUS_INDICATOR = "status_indicator"
 | 
			
		||||
CONF_WIFI_TIMEOUT = "wifi_timeout"
 | 
			
		||||
 | 
			
		||||
improv_ns = cg.esphome_ns.namespace("improv")
 | 
			
		||||
Error = improv_ns.enum("Error")
 | 
			
		||||
State = improv_ns.enum("State")
 | 
			
		||||
 | 
			
		||||
esp32_improv_ns = cg.esphome_ns.namespace("esp32_improv")
 | 
			
		||||
ESP32ImprovComponent = esp32_improv_ns.class_(
 | 
			
		||||
    "ESP32ImprovComponent", cg.Component, esp32_ble_server.BLEServiceComponent
 | 
			
		||||
)
 | 
			
		||||
ESP32ImprovProvisionedTrigger = esp32_improv_ns.class_(
 | 
			
		||||
    "ESP32ImprovProvisionedTrigger", automation.Trigger.template()
 | 
			
		||||
)
 | 
			
		||||
ESP32ImprovProvisioningTrigger = esp32_improv_ns.class_(
 | 
			
		||||
    "ESP32ImprovProvisioningTrigger", automation.Trigger.template()
 | 
			
		||||
)
 | 
			
		||||
ESP32ImprovStartTrigger = esp32_improv_ns.class_(
 | 
			
		||||
    "ESP32ImprovStartTrigger", automation.Trigger.template()
 | 
			
		||||
)
 | 
			
		||||
ESP32ImprovStateTrigger = esp32_improv_ns.class_(
 | 
			
		||||
    "ESP32ImprovStateTrigger", automation.Trigger.template()
 | 
			
		||||
)
 | 
			
		||||
ESP32ImprovStoppedTrigger = esp32_improv_ns.class_(
 | 
			
		||||
    "ESP32ImprovStoppedTrigger", automation.Trigger.template()
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
@@ -61,37 +37,6 @@ CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
        cv.Optional(
 | 
			
		||||
            CONF_WIFI_TIMEOUT, default="1min"
 | 
			
		||||
        ): cv.positive_time_period_milliseconds,
 | 
			
		||||
        cv.Optional(CONF_ON_PROVISIONED): automation.validate_automation(
 | 
			
		||||
            {
 | 
			
		||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
 | 
			
		||||
                    ESP32ImprovProvisionedTrigger
 | 
			
		||||
                ),
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_ON_PROVISIONING): automation.validate_automation(
 | 
			
		||||
            {
 | 
			
		||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
 | 
			
		||||
                    ESP32ImprovProvisioningTrigger
 | 
			
		||||
                ),
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_ON_START): automation.validate_automation(
 | 
			
		||||
            {
 | 
			
		||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ESP32ImprovStartTrigger),
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_ON_STATE): automation.validate_automation(
 | 
			
		||||
            {
 | 
			
		||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ESP32ImprovStateTrigger),
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_ON_STOP): automation.validate_automation(
 | 
			
		||||
            {
 | 
			
		||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
 | 
			
		||||
                    ESP32ImprovStoppedTrigger
 | 
			
		||||
                ),
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
    }
 | 
			
		||||
).extend(cv.COMPONENT_SCHEMA)
 | 
			
		||||
 | 
			
		||||
@@ -118,29 +63,3 @@ async def to_code(config):
 | 
			
		||||
    if CONF_STATUS_INDICATOR in config:
 | 
			
		||||
        status_indicator = await cg.get_variable(config[CONF_STATUS_INDICATOR])
 | 
			
		||||
        cg.add(var.set_status_indicator(status_indicator))
 | 
			
		||||
 | 
			
		||||
    use_state_callback = False
 | 
			
		||||
    for conf in config.get(CONF_ON_PROVISIONED, []):
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
			
		||||
        await automation.build_automation(trigger, [], conf)
 | 
			
		||||
        use_state_callback = True
 | 
			
		||||
    for conf in config.get(CONF_ON_PROVISIONING, []):
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
			
		||||
        await automation.build_automation(trigger, [], conf)
 | 
			
		||||
        use_state_callback = True
 | 
			
		||||
    for conf in config.get(CONF_ON_START, []):
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
			
		||||
        await automation.build_automation(trigger, [], conf)
 | 
			
		||||
        use_state_callback = True
 | 
			
		||||
    for conf in config.get(CONF_ON_STATE, []):
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
			
		||||
        await automation.build_automation(
 | 
			
		||||
            trigger, [(State, "state"), (Error, "error")], conf
 | 
			
		||||
        )
 | 
			
		||||
        use_state_callback = True
 | 
			
		||||
    for conf in config.get(CONF_ON_STOP, []):
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
			
		||||
        await automation.build_automation(trigger, [], conf)
 | 
			
		||||
        use_state_callback = True
 | 
			
		||||
    if use_state_callback:
 | 
			
		||||
        cg.add_define("USE_ESP32_IMPROV_STATE_CALLBACK")
 | 
			
		||||
 
 | 
			
		||||
@@ -1,72 +0,0 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
#ifdef USE_ESP32_IMPROV_STATE_CALLBACK
 | 
			
		||||
#include "esp32_improv_component.h"
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/automation.h"
 | 
			
		||||
 | 
			
		||||
#include <improv.h>
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace esp32_improv {
 | 
			
		||||
 | 
			
		||||
class ESP32ImprovProvisionedTrigger : public Trigger<> {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit ESP32ImprovProvisionedTrigger(ESP32ImprovComponent *parent) {
 | 
			
		||||
    parent->add_on_state_callback([this, parent](improv::State state, improv::Error error) {
 | 
			
		||||
      if (state == improv::STATE_PROVISIONED && !parent->is_failed()) {
 | 
			
		||||
        trigger();
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ESP32ImprovProvisioningTrigger : public Trigger<> {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit ESP32ImprovProvisioningTrigger(ESP32ImprovComponent *parent) {
 | 
			
		||||
    parent->add_on_state_callback([this, parent](improv::State state, improv::Error error) {
 | 
			
		||||
      if (state == improv::STATE_PROVISIONING && !parent->is_failed()) {
 | 
			
		||||
        trigger();
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ESP32ImprovStartTrigger : public Trigger<> {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit ESP32ImprovStartTrigger(ESP32ImprovComponent *parent) {
 | 
			
		||||
    parent->add_on_state_callback([this, parent](improv::State state, improv::Error error) {
 | 
			
		||||
      if ((state == improv::STATE_AUTHORIZED || state == improv::STATE_AWAITING_AUTHORIZATION) &&
 | 
			
		||||
          !parent->is_failed()) {
 | 
			
		||||
        trigger();
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ESP32ImprovStateTrigger : public Trigger<improv::State, improv::Error> {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit ESP32ImprovStateTrigger(ESP32ImprovComponent *parent) {
 | 
			
		||||
    parent->add_on_state_callback([this, parent](improv::State state, improv::Error error) {
 | 
			
		||||
      if (!parent->is_failed()) {
 | 
			
		||||
        trigger(state, error);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ESP32ImprovStoppedTrigger : public Trigger<> {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit ESP32ImprovStoppedTrigger(ESP32ImprovComponent *parent) {
 | 
			
		||||
    parent->add_on_state_callback([this, parent](improv::State state, improv::Error error) {
 | 
			
		||||
      if (state == improv::STATE_STOPPED && !parent->is_failed()) {
 | 
			
		||||
        trigger();
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace esp32_improv
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
@@ -68,12 +68,7 @@ void ESP32ImprovComponent::setup_characteristics() {
 | 
			
		||||
 | 
			
		||||
void ESP32ImprovComponent::loop() {
 | 
			
		||||
  if (!global_ble_server->is_running()) {
 | 
			
		||||
    if (this->state_ != improv::STATE_STOPPED) {
 | 
			
		||||
      this->state_ = improv::STATE_STOPPED;
 | 
			
		||||
#ifdef USE_ESP32_IMPROV_STATE_CALLBACK
 | 
			
		||||
      this->state_callback_.call(this->state_, this->error_state_);
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
    this->state_ = improv::STATE_STOPPED;
 | 
			
		||||
    this->incoming_data_.clear();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
@@ -222,9 +217,6 @@ void ESP32ImprovComponent::set_state_(improv::State state) {
 | 
			
		||||
  service_data[7] = 0x00;  // Reserved
 | 
			
		||||
 | 
			
		||||
  esp32_ble::global_ble->advertising_set_service_data(service_data);
 | 
			
		||||
#ifdef USE_ESP32_IMPROV_STATE_CALLBACK
 | 
			
		||||
  this->state_callback_.call(this->state_, this->error_state_);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ESP32ImprovComponent::set_error_(improv::Error error) {
 | 
			
		||||
@@ -278,7 +270,7 @@ void ESP32ImprovComponent::dump_config() {
 | 
			
		||||
void ESP32ImprovComponent::process_incoming_data_() {
 | 
			
		||||
  uint8_t length = this->incoming_data_[1];
 | 
			
		||||
 | 
			
		||||
  ESP_LOGV(TAG, "Processing bytes - %s", format_hex_pretty(this->incoming_data_).c_str());
 | 
			
		||||
  ESP_LOGD(TAG, "Processing bytes - %s", format_hex_pretty(this->incoming_data_).c_str());
 | 
			
		||||
  if (this->incoming_data_.size() - 3 == length) {
 | 
			
		||||
    this->set_error_(improv::ERROR_NONE);
 | 
			
		||||
    improv::ImprovCommand command = improv::parse_improv_data(this->incoming_data_);
 | 
			
		||||
@@ -303,7 +295,7 @@ void ESP32ImprovComponent::process_incoming_data_() {
 | 
			
		||||
        wifi::global_wifi_component->set_sta(sta);
 | 
			
		||||
        wifi::global_wifi_component->start_connecting(sta, false);
 | 
			
		||||
        this->set_state_(improv::STATE_PROVISIONING);
 | 
			
		||||
        ESP_LOGD(TAG, "Received Improv Wi-Fi settings ssid=%s, password=" LOG_SECRET("%s"), command.ssid.c_str(),
 | 
			
		||||
        ESP_LOGD(TAG, "Received Improv wifi settings ssid=%s, password=" LOG_SECRET("%s"), command.ssid.c_str(),
 | 
			
		||||
                 command.password.c_str());
 | 
			
		||||
 | 
			
		||||
        auto f = std::bind(&ESP32ImprovComponent::on_wifi_connect_timeout_, this);
 | 
			
		||||
@@ -321,7 +313,7 @@ void ESP32ImprovComponent::process_incoming_data_() {
 | 
			
		||||
        this->incoming_data_.clear();
 | 
			
		||||
    }
 | 
			
		||||
  } else if (this->incoming_data_.size() - 2 > length) {
 | 
			
		||||
    ESP_LOGV(TAG, "Too much data received or data malformed; resetting buffer...");
 | 
			
		||||
    ESP_LOGV(TAG, "Too much data came in, or malformed resetting buffer...");
 | 
			
		||||
    this->incoming_data_.clear();
 | 
			
		||||
  } else {
 | 
			
		||||
    ESP_LOGV(TAG, "Waiting for split data packets...");
 | 
			
		||||
@@ -335,7 +327,7 @@ void ESP32ImprovComponent::on_wifi_connect_timeout_() {
 | 
			
		||||
  if (this->authorizer_ != nullptr)
 | 
			
		||||
    this->authorized_start_ = millis();
 | 
			
		||||
#endif
 | 
			
		||||
  ESP_LOGW(TAG, "Timed out while connecting to Wi-Fi network");
 | 
			
		||||
  ESP_LOGW(TAG, "Timed out trying to connect to given WiFi network");
 | 
			
		||||
  wifi::global_wifi_component->clear_sta();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,10 +9,6 @@
 | 
			
		||||
#include "esphome/components/esp32_ble_server/ble_server.h"
 | 
			
		||||
#include "esphome/components/wifi/wifi_component.h"
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32_IMPROV_STATE_CALLBACK
 | 
			
		||||
#include "esphome/core/automation.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
#include "esphome/components/binary_sensor/binary_sensor.h"
 | 
			
		||||
#endif
 | 
			
		||||
@@ -46,11 +42,6 @@ class ESP32ImprovComponent : public Component, public BLEServiceComponent {
 | 
			
		||||
  void stop() override;
 | 
			
		||||
  bool is_active() const { return this->state_ != improv::STATE_STOPPED; }
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32_IMPROV_STATE_CALLBACK
 | 
			
		||||
  void add_on_state_callback(std::function<void(improv::State, improv::Error)> &&callback) {
 | 
			
		||||
    this->state_callback_.add(std::move(callback));
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
  void set_authorizer(binary_sensor::BinarySensor *authorizer) { this->authorizer_ = authorizer; }
 | 
			
		||||
#endif
 | 
			
		||||
@@ -63,9 +54,6 @@ class ESP32ImprovComponent : public Component, public BLEServiceComponent {
 | 
			
		||||
  void set_wifi_timeout(uint32_t wifi_timeout) { this->wifi_timeout_ = wifi_timeout; }
 | 
			
		||||
  uint32_t get_wifi_timeout() const { return this->wifi_timeout_; }
 | 
			
		||||
 | 
			
		||||
  improv::State get_improv_state() const { return this->state_; }
 | 
			
		||||
  improv::Error get_improv_error_state() const { return this->error_state_; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool should_start_{false};
 | 
			
		||||
  bool setup_complete_{false};
 | 
			
		||||
@@ -96,9 +84,6 @@ class ESP32ImprovComponent : public Component, public BLEServiceComponent {
 | 
			
		||||
 | 
			
		||||
  improv::State state_{improv::STATE_STOPPED};
 | 
			
		||||
  improv::Error error_state_{improv::ERROR_NONE};
 | 
			
		||||
#ifdef USE_ESP32_IMPROV_STATE_CALLBACK
 | 
			
		||||
  CallbackManager<void(improv::State, improv::Error)> state_callback_{};
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  bool status_indicator_state_{false};
 | 
			
		||||
  void set_status_indicator_state_(bool state);
 | 
			
		||||
 
 | 
			
		||||
@@ -59,7 +59,6 @@ ETHERNET_TYPES = {
 | 
			
		||||
    "KSZ8081": EthernetType.ETHERNET_TYPE_KSZ8081,
 | 
			
		||||
    "KSZ8081RNA": EthernetType.ETHERNET_TYPE_KSZ8081RNA,
 | 
			
		||||
    "W5500": EthernetType.ETHERNET_TYPE_W5500,
 | 
			
		||||
    "OPENETH": EthernetType.ETHERNET_TYPE_OPENETH,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SPI_ETHERNET_TYPES = ["W5500"]
 | 
			
		||||
@@ -172,7 +171,6 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
            "KSZ8081": RMII_SCHEMA,
 | 
			
		||||
            "KSZ8081RNA": RMII_SCHEMA,
 | 
			
		||||
            "W5500": SPI_SCHEMA,
 | 
			
		||||
            "OPENETH": BASE_SCHEMA,
 | 
			
		||||
        },
 | 
			
		||||
        upper=True,
 | 
			
		||||
    ),
 | 
			
		||||
@@ -242,9 +240,6 @@ async def to_code(config):
 | 
			
		||||
        if CORE.using_esp_idf:
 | 
			
		||||
            add_idf_sdkconfig_option("CONFIG_ETH_USE_SPI_ETHERNET", True)
 | 
			
		||||
            add_idf_sdkconfig_option("CONFIG_ETH_SPI_ETHERNET_W5500", True)
 | 
			
		||||
    elif config[CONF_TYPE] == "OPENETH":
 | 
			
		||||
        cg.add_define("USE_ETHERNET_OPENETH")
 | 
			
		||||
        add_idf_sdkconfig_option("CONFIG_ETH_USE_OPENETH", True)
 | 
			
		||||
    else:
 | 
			
		||||
        cg.add(var.set_phy_addr(config[CONF_PHY_ADDR]))
 | 
			
		||||
        cg.add(var.set_mdc_pin(config[CONF_MDC_PIN]))
 | 
			
		||||
 
 | 
			
		||||
@@ -120,8 +120,6 @@ void EthernetComponent::setup() {
 | 
			
		||||
  phy_config.reset_gpio_num = this->reset_pin_;
 | 
			
		||||
 | 
			
		||||
  esp_eth_mac_t *mac = esp_eth_mac_new_w5500(&w5500_config, &mac_config);
 | 
			
		||||
#elif defined(USE_ETHERNET_OPENETH)
 | 
			
		||||
  esp_eth_mac_t *mac = esp_eth_mac_new_openeth(&mac_config);
 | 
			
		||||
#else
 | 
			
		||||
  phy_config.phy_addr = this->phy_addr_;
 | 
			
		||||
  phy_config.reset_gpio_num = this->power_pin_;
 | 
			
		||||
@@ -145,13 +143,6 @@ void EthernetComponent::setup() {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  switch (this->type_) {
 | 
			
		||||
#ifdef USE_ETHERNET_OPENETH
 | 
			
		||||
    case ETHERNET_TYPE_OPENETH: {
 | 
			
		||||
      phy_config.autonego_timeout_ms = 1000;
 | 
			
		||||
      this->phy_ = esp_eth_phy_new_dp83848(&phy_config);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
#if CONFIG_ETH_USE_ESP32_EMAC
 | 
			
		||||
    case ETHERNET_TYPE_LAN8720: {
 | 
			
		||||
      this->phy_ = esp_eth_phy_new_lan87xx(&phy_config);
 | 
			
		||||
@@ -311,10 +302,6 @@ void EthernetComponent::dump_config() {
 | 
			
		||||
      eth_type = "W5500";
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case ETHERNET_TYPE_OPENETH:
 | 
			
		||||
      eth_type = "OPENETH";
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
      eth_type = "Unknown";
 | 
			
		||||
      break;
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,6 @@ enum EthernetType {
 | 
			
		||||
  ETHERNET_TYPE_KSZ8081,
 | 
			
		||||
  ETHERNET_TYPE_KSZ8081RNA,
 | 
			
		||||
  ETHERNET_TYPE_W5500,
 | 
			
		||||
  ETHERNET_TYPE_OPENETH,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ManualIP {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
from esphome import automation
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
from esphome.components import mqtt, web_server
 | 
			
		||||
from esphome.components import mqtt
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_DEVICE_CLASS,
 | 
			
		||||
@@ -11,7 +11,6 @@ from esphome.const import (
 | 
			
		||||
    CONF_MQTT_ID,
 | 
			
		||||
    CONF_ON_EVENT,
 | 
			
		||||
    CONF_TRIGGER_ID,
 | 
			
		||||
    CONF_WEB_SERVER,
 | 
			
		||||
    DEVICE_CLASS_BUTTON,
 | 
			
		||||
    DEVICE_CLASS_DOORBELL,
 | 
			
		||||
    DEVICE_CLASS_EMPTY,
 | 
			
		||||
@@ -41,21 +40,17 @@ EventTrigger = event_ns.class_("EventTrigger", automation.Trigger.template())
 | 
			
		||||
 | 
			
		||||
validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
 | 
			
		||||
 | 
			
		||||
EVENT_SCHEMA = (
 | 
			
		||||
    cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
 | 
			
		||||
    .extend(cv.MQTT_COMPONENT_SCHEMA)
 | 
			
		||||
    .extend(
 | 
			
		||||
        {
 | 
			
		||||
            cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTEventComponent),
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(Event),
 | 
			
		||||
            cv.Optional(CONF_DEVICE_CLASS): validate_device_class,
 | 
			
		||||
            cv.Optional(CONF_ON_EVENT): automation.validate_automation(
 | 
			
		||||
                {
 | 
			
		||||
                    cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(EventTrigger),
 | 
			
		||||
                }
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
EVENT_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
 | 
			
		||||
    {
 | 
			
		||||
        cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTEventComponent),
 | 
			
		||||
        cv.GenerateID(): cv.declare_id(Event),
 | 
			
		||||
        cv.Optional(CONF_DEVICE_CLASS): validate_device_class,
 | 
			
		||||
        cv.Optional(CONF_ON_EVENT): automation.validate_automation(
 | 
			
		||||
            {
 | 
			
		||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(EventTrigger),
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
    }
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
_UNDEF = object()
 | 
			
		||||
@@ -102,9 +97,6 @@ async def setup_event_core_(var, config, *, event_types: list[str]):
 | 
			
		||||
        mqtt_ = cg.new_Pvariable(mqtt_id, var)
 | 
			
		||||
        await mqtt.register_mqtt_component(mqtt_, config)
 | 
			
		||||
 | 
			
		||||
    if web_server_config := config.get(CONF_WEB_SERVER):
 | 
			
		||||
        await web_server.add_entity_config(var, web_server_config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def register_event(var, config, *, event_types: list[str]):
 | 
			
		||||
    if not CORE.has_id(config[CONF_ID]):
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,7 @@ from esphome.const import (
 | 
			
		||||
    CONF_SPEED_LEVEL_STATE_TOPIC,
 | 
			
		||||
    CONF_SPEED_STATE_TOPIC,
 | 
			
		||||
    CONF_TRIGGER_ID,
 | 
			
		||||
    CONF_WEB_SERVER,
 | 
			
		||||
    CONF_WEB_SERVER_ID,
 | 
			
		||||
)
 | 
			
		||||
from esphome.core import CORE, coroutine_with_priority
 | 
			
		||||
from esphome.cpp_helpers import setup_entity
 | 
			
		||||
@@ -218,8 +218,9 @@ async def setup_fan_core_(var, config):
 | 
			
		||||
        if (speed_command_topic := config.get(CONF_SPEED_COMMAND_TOPIC)) is not None:
 | 
			
		||||
            cg.add(mqtt_.set_custom_speed_command_topic(speed_command_topic))
 | 
			
		||||
 | 
			
		||||
    if web_server_config := config.get(CONF_WEB_SERVER):
 | 
			
		||||
        await web_server.add_entity_config(var, web_server_config)
 | 
			
		||||
    if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None:
 | 
			
		||||
        web_server_ = await cg.get_variable(webserver_id)
 | 
			
		||||
        web_server.add_entity_to_sorting_list(web_server_, var, config)
 | 
			
		||||
 | 
			
		||||
    for conf in config.get(CONF_ON_STATE, []):
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,67 +0,0 @@
 | 
			
		||||
#include "gp2y1010au0f.h"
 | 
			
		||||
#include "esphome/core/hal.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
#include <cinttypes>
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace gp2y1010au0f {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "gp2y1010au0f";
 | 
			
		||||
static const float MIN_VOLTAGE = 0.0f;
 | 
			
		||||
static const float MAX_VOLTAGE = 4.0f;
 | 
			
		||||
 | 
			
		||||
void GP2Y1010AU0FSensor::dump_config() {
 | 
			
		||||
  LOG_SENSOR("", "Sharp GP2Y1010AU0F PM2.5 Sensor", this);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Sampling duration: %" PRId32 " ms", this->sample_duration_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  ADC voltage multiplier: %.3f", this->voltage_multiplier_);
 | 
			
		||||
  LOG_UPDATE_INTERVAL(this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GP2Y1010AU0FSensor::update() {
 | 
			
		||||
  is_sampling_ = true;
 | 
			
		||||
 | 
			
		||||
  this->set_timeout("read", this->sample_duration_, [this]() {
 | 
			
		||||
    this->is_sampling_ = false;
 | 
			
		||||
    if (this->num_samples_ == 0)
 | 
			
		||||
      return;
 | 
			
		||||
 | 
			
		||||
    float mean = this->sample_sum_ / float(this->num_samples_);
 | 
			
		||||
    ESP_LOGD(TAG, "ADC read voltage: %.3f V (mean from %" PRId32 " samples)", mean, this->num_samples_);
 | 
			
		||||
 | 
			
		||||
    // PM2.5 calculation
 | 
			
		||||
    // ref: https://www.howmuchsnow.com/arduino/airquality/
 | 
			
		||||
    int16_t pm_2_5_value = 170 * mean;
 | 
			
		||||
    this->publish_state(pm_2_5_value);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  // reset readings
 | 
			
		||||
  this->num_samples_ = 0;
 | 
			
		||||
  this->sample_sum_ = 0.0f;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GP2Y1010AU0FSensor::loop() {
 | 
			
		||||
  if (!this->is_sampling_)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  // enable the internal IR LED
 | 
			
		||||
  this->led_output_->turn_on();
 | 
			
		||||
  // wait for the sensor to stabilize
 | 
			
		||||
  delayMicroseconds(this->sample_wait_before_);
 | 
			
		||||
  // perform a single sample
 | 
			
		||||
  float read_voltage = this->source_->sample();
 | 
			
		||||
  // disable the internal IR LED
 | 
			
		||||
  this->led_output_->turn_off();
 | 
			
		||||
 | 
			
		||||
  if (std::isnan(read_voltage))
 | 
			
		||||
    return;
 | 
			
		||||
  read_voltage = read_voltage * this->voltage_multiplier_ - this->voltage_offset_;
 | 
			
		||||
  if (read_voltage < MIN_VOLTAGE || read_voltage > MAX_VOLTAGE)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  this->num_samples_++;
 | 
			
		||||
  this->sample_sum_ += read_voltage;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace gp2y1010au0f
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -1,52 +0,0 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/components/sensor/sensor.h"
 | 
			
		||||
#include "esphome/components/voltage_sampler/voltage_sampler.h"
 | 
			
		||||
#include "esphome/components/output/binary_output.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace gp2y1010au0f {
 | 
			
		||||
 | 
			
		||||
class GP2Y1010AU0FSensor : public sensor::Sensor, public PollingComponent {
 | 
			
		||||
 public:
 | 
			
		||||
  void update() override;
 | 
			
		||||
  void loop() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
  float get_setup_priority() const override {
 | 
			
		||||
    // after the base sensor has been initialized
 | 
			
		||||
    return setup_priority::DATA - 1.0f;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void set_adc_source(voltage_sampler::VoltageSampler *source) { source_ = source; }
 | 
			
		||||
  void set_voltage_refs(float offset, float multiplier) {
 | 
			
		||||
    this->voltage_offset_ = offset;
 | 
			
		||||
    this->voltage_multiplier_ = multiplier;
 | 
			
		||||
  }
 | 
			
		||||
  void set_led_output(output::BinaryOutput *output) { led_output_ = output; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  // duration in ms of the sampling phase
 | 
			
		||||
  uint32_t sample_duration_ = 100;
 | 
			
		||||
  // duration in us of the wait before sampling
 | 
			
		||||
  // ref: https://global.sharp/products/device/lineup/data/pdf/datasheet/gp2y1010au_appl_e.pdf
 | 
			
		||||
  uint32_t sample_wait_before_ = 280;
 | 
			
		||||
  // duration in us of the wait after sampling
 | 
			
		||||
  // it seems no need to delay on purpose since one ADC sampling takes longer than that (300-400 us on ESP8266)
 | 
			
		||||
  // uint32_t sample_wait_after_ = 40;
 | 
			
		||||
  // the sampling source to read voltage from
 | 
			
		||||
  voltage_sampler::VoltageSampler *source_;
 | 
			
		||||
  // ADC voltage reading offset
 | 
			
		||||
  float voltage_offset_ = 0.0f;
 | 
			
		||||
  // ADC voltage reading multiplier
 | 
			
		||||
  float voltage_multiplier_ = 1.0f;
 | 
			
		||||
  // the binary output to control the sampling LED
 | 
			
		||||
  output::BinaryOutput *led_output_;
 | 
			
		||||
 | 
			
		||||
  float sample_sum_ = 0.0f;
 | 
			
		||||
  uint32_t num_samples_ = 0;
 | 
			
		||||
  bool is_sampling_ = false;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace gp2y1010au0f
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -1,61 +0,0 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import sensor, voltage_sampler, output
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_SENSOR,
 | 
			
		||||
    CONF_OUTPUT,
 | 
			
		||||
    DEVICE_CLASS_PM25,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    UNIT_MICROGRAMS_PER_CUBIC_METER,
 | 
			
		||||
    ICON_CHEMICAL_WEAPON,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["output"]
 | 
			
		||||
AUTO_LOAD = ["voltage_sampler"]
 | 
			
		||||
CODEOWNERS = ["@zry98"]
 | 
			
		||||
 | 
			
		||||
CONF_ADC_VOLTAGE_OFFSET = "adc_voltage_offset"
 | 
			
		||||
CONF_ADC_VOLTAGE_MULTIPLIER = "adc_voltage_multiplier"
 | 
			
		||||
 | 
			
		||||
gp2y1010au0f_ns = cg.esphome_ns.namespace("gp2y1010au0f")
 | 
			
		||||
GP2Y1010AU0FSensor = gp2y1010au0f_ns.class_(
 | 
			
		||||
    "GP2Y1010AU0FSensor", sensor.Sensor, cg.PollingComponent
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = (
 | 
			
		||||
    sensor.sensor_schema(
 | 
			
		||||
        GP2Y1010AU0FSensor,
 | 
			
		||||
        unit_of_measurement=UNIT_MICROGRAMS_PER_CUBIC_METER,
 | 
			
		||||
        accuracy_decimals=0,
 | 
			
		||||
        device_class=DEVICE_CLASS_PM25,
 | 
			
		||||
        state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
        icon=ICON_CHEMICAL_WEAPON,
 | 
			
		||||
    )
 | 
			
		||||
    .extend(
 | 
			
		||||
        {
 | 
			
		||||
            cv.Required(CONF_SENSOR): cv.use_id(voltage_sampler.VoltageSampler),
 | 
			
		||||
            cv.Optional(CONF_ADC_VOLTAGE_OFFSET, default=0.0): cv.float_,
 | 
			
		||||
            cv.Optional(CONF_ADC_VOLTAGE_MULTIPLIER, default=1.0): cv.float_,
 | 
			
		||||
            cv.Required(CONF_OUTPUT): cv.use_id(output.BinaryOutput),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
    .extend(cv.polling_component_schema("60s"))
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = await sensor.new_sensor(config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
 | 
			
		||||
    # the ADC sensor to read voltage from
 | 
			
		||||
    adc_sensor = await cg.get_variable(config[CONF_SENSOR])
 | 
			
		||||
    cg.add(var.set_adc_source(adc_sensor))
 | 
			
		||||
    cg.add(
 | 
			
		||||
        var.set_voltage_refs(
 | 
			
		||||
            config[CONF_ADC_VOLTAGE_OFFSET], config[CONF_ADC_VOLTAGE_MULTIPLIER]
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    # the binary output to control the module's internal IR LED
 | 
			
		||||
    led_output = await cg.get_variable(config[CONF_OUTPUT])
 | 
			
		||||
    cg.add(var.set_led_output(led_output))
 | 
			
		||||
@@ -1,38 +0,0 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include "esphome/core/hal.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace gpio_expander {
 | 
			
		||||
 | 
			
		||||
/// @brief A class to cache the read state of a GPIO expander.
 | 
			
		||||
template<typename T, T N> class CachedGpioExpander {
 | 
			
		||||
 public:
 | 
			
		||||
  bool digital_read(T pin) {
 | 
			
		||||
    if (!this->read_cache_invalidated_[pin]) {
 | 
			
		||||
      this->read_cache_invalidated_[pin] = true;
 | 
			
		||||
      return this->digital_read_cache(pin);
 | 
			
		||||
    }
 | 
			
		||||
    return this->digital_read_hw(pin);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void digital_write(T pin, bool value) { this->digital_write_hw(pin, value); }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  virtual bool digital_read_hw(T pin) = 0;
 | 
			
		||||
  virtual bool digital_read_cache(T pin) = 0;
 | 
			
		||||
  virtual void digital_write_hw(T pin, bool value) = 0;
 | 
			
		||||
 | 
			
		||||
  void reset_pin_cache_() {
 | 
			
		||||
    for (T i = 0; i < N; i++) {
 | 
			
		||||
      this->read_cache_invalidated_[i] = false;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  std::array<bool, N> read_cache_invalidated_{};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace gpio_expander
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -1,88 +0,0 @@
 | 
			
		||||
#include "grove_gas_mc_v2.h"
 | 
			
		||||
#include "esphome/core/hal.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace grove_gas_mc_v2 {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "grove_gas_mc_v2";
 | 
			
		||||
 | 
			
		||||
// I2C Commands for Grove Gas Multichannel V2 Sensor
 | 
			
		||||
// Taken from:
 | 
			
		||||
// https://github.com/Seeed-Studio/Seeed_Arduino_MultiGas/blob/master/src/Multichannel_Gas_GroveGasMultichannelV2.h
 | 
			
		||||
static const uint8_t GROVE_GAS_MC_V2_HEAT_ON = 0xFE;
 | 
			
		||||
static const uint8_t GROVE_GAS_MC_V2_HEAT_OFF = 0xFF;
 | 
			
		||||
static const uint8_t GROVE_GAS_MC_V2_READ_GM102B = 0x01;
 | 
			
		||||
static const uint8_t GROVE_GAS_MC_V2_READ_GM302B = 0x03;
 | 
			
		||||
static const uint8_t GROVE_GAS_MC_V2_READ_GM502B = 0x05;
 | 
			
		||||
static const uint8_t GROVE_GAS_MC_V2_READ_GM702B = 0x07;
 | 
			
		||||
 | 
			
		||||
bool GroveGasMultichannelV2Component::read_sensor_(uint8_t address, sensor::Sensor *sensor) {
 | 
			
		||||
  if (sensor == nullptr) {
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
  uint32_t value = 0;
 | 
			
		||||
  if (!this->read_bytes(address, (uint8_t *) &value, 4)) {
 | 
			
		||||
    ESP_LOGW(TAG, "Reading Grove Gas Sensor data failed!");
 | 
			
		||||
    this->error_code_ = COMMUNICATION_FAILED;
 | 
			
		||||
    this->status_set_warning();
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  sensor->publish_state(value);
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GroveGasMultichannelV2Component::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up Grove Multichannel Gas Sensor V2...");
 | 
			
		||||
 | 
			
		||||
  // Before reading sensor values, must preheat sensor
 | 
			
		||||
  if (!(this->write_bytes(GROVE_GAS_MC_V2_HEAT_ON, {}))) {
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    this->error_code_ = APP_START_FAILED;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GroveGasMultichannelV2Component::update() {
 | 
			
		||||
  // Read from each of the gas sensors
 | 
			
		||||
  if (!this->read_sensor_(GROVE_GAS_MC_V2_READ_GM102B, this->nitrogen_dioxide_sensor_))
 | 
			
		||||
    return;
 | 
			
		||||
  if (!this->read_sensor_(GROVE_GAS_MC_V2_READ_GM302B, this->ethanol_sensor_))
 | 
			
		||||
    return;
 | 
			
		||||
  if (!this->read_sensor_(GROVE_GAS_MC_V2_READ_GM502B, this->tvoc_sensor_))
 | 
			
		||||
    return;
 | 
			
		||||
  if (!this->read_sensor_(GROVE_GAS_MC_V2_READ_GM702B, this->carbon_monoxide_sensor_))
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  this->status_clear_warning();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GroveGasMultichannelV2Component::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Grove Multichannel Gas Sensor V2");
 | 
			
		||||
  LOG_I2C_DEVICE(this)
 | 
			
		||||
  LOG_UPDATE_INTERVAL(this)
 | 
			
		||||
  LOG_SENSOR("  ", "Nitrogen Dioxide", this->nitrogen_dioxide_sensor_)
 | 
			
		||||
  LOG_SENSOR("  ", "Ethanol", this->ethanol_sensor_)
 | 
			
		||||
  LOG_SENSOR("  ", "Carbon Monoxide", this->carbon_monoxide_sensor_)
 | 
			
		||||
  LOG_SENSOR("  ", "TVOC", this->tvoc_sensor_)
 | 
			
		||||
 | 
			
		||||
  if (this->is_failed()) {
 | 
			
		||||
    switch (this->error_code_) {
 | 
			
		||||
      case COMMUNICATION_FAILED:
 | 
			
		||||
        ESP_LOGW(TAG, "Communication failed! Is the sensor connected?");
 | 
			
		||||
        break;
 | 
			
		||||
      case APP_INVALID:
 | 
			
		||||
        ESP_LOGW(TAG, "Sensor reported invalid APP installed.");
 | 
			
		||||
        break;
 | 
			
		||||
      case APP_START_FAILED:
 | 
			
		||||
        ESP_LOGW(TAG, "Sensor reported APP start failed.");
 | 
			
		||||
        break;
 | 
			
		||||
      case UNKNOWN:
 | 
			
		||||
      default:
 | 
			
		||||
        ESP_LOGW(TAG, "Unknown setup error!");
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace grove_gas_mc_v2
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -1,39 +0,0 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/components/i2c/i2c.h"
 | 
			
		||||
#include "esphome/components/sensor/sensor.h"
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/preferences.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace grove_gas_mc_v2 {
 | 
			
		||||
 | 
			
		||||
class GroveGasMultichannelV2Component : public PollingComponent, public i2c::I2CDevice {
 | 
			
		||||
  SUB_SENSOR(tvoc)
 | 
			
		||||
  SUB_SENSOR(carbon_monoxide)
 | 
			
		||||
  SUB_SENSOR(nitrogen_dioxide)
 | 
			
		||||
  SUB_SENSOR(ethanol)
 | 
			
		||||
 | 
			
		||||
 public:
 | 
			
		||||
  /// Setup the sensor and test for a connection.
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  /// Schedule temperature+pressure readings.
 | 
			
		||||
  void update() override;
 | 
			
		||||
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
 | 
			
		||||
  float get_setup_priority() const override { return setup_priority::DATA; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  enum ErrorCode {
 | 
			
		||||
    UNKNOWN,
 | 
			
		||||
    COMMUNICATION_FAILED,
 | 
			
		||||
    APP_INVALID,
 | 
			
		||||
    APP_START_FAILED,
 | 
			
		||||
  } error_code_{UNKNOWN};
 | 
			
		||||
 | 
			
		||||
  bool read_sensor_(uint8_t address, sensor::Sensor *sensor);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace grove_gas_mc_v2
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -1,77 +0,0 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
from esphome.components import i2c, sensor
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_CARBON_MONOXIDE,
 | 
			
		||||
    CONF_ETHANOL,
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_NITROGEN_DIOXIDE,
 | 
			
		||||
    CONF_TVOC,
 | 
			
		||||
    DEVICE_CLASS_CARBON_MONOXIDE,
 | 
			
		||||
    DEVICE_CLASS_NITROGEN_DIOXIDE,
 | 
			
		||||
    DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS,
 | 
			
		||||
    ICON_AIR_FILTER,
 | 
			
		||||
    ICON_FLASK_ROUND_BOTTOM,
 | 
			
		||||
    ICON_GAS_CYLINDER,
 | 
			
		||||
    ICON_MOLECULE_CO,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    UNIT_MICROGRAMS_PER_CUBIC_METER,
 | 
			
		||||
    UNIT_PARTS_PER_MILLION,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@YorkshireIoT"]
 | 
			
		||||
DEPENDENCIES = ["i2c"]
 | 
			
		||||
 | 
			
		||||
grove_gas_mc_v2_ns = cg.esphome_ns.namespace("grove_gas_mc_v2")
 | 
			
		||||
 | 
			
		||||
GroveGasMultichannelV2Component = grove_gas_mc_v2_ns.class_(
 | 
			
		||||
    "GroveGasMultichannelV2Component", cg.PollingComponent, i2c.I2CDevice
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = (
 | 
			
		||||
    cv.Schema(
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(GroveGasMultichannelV2Component),
 | 
			
		||||
            cv.Optional(CONF_TVOC): sensor.sensor_schema(
 | 
			
		||||
                unit_of_measurement=UNIT_MICROGRAMS_PER_CUBIC_METER,
 | 
			
		||||
                icon=ICON_AIR_FILTER,
 | 
			
		||||
                accuracy_decimals=0,
 | 
			
		||||
                device_class=DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS,
 | 
			
		||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_CARBON_MONOXIDE): sensor.sensor_schema(
 | 
			
		||||
                unit_of_measurement=UNIT_PARTS_PER_MILLION,
 | 
			
		||||
                icon=ICON_MOLECULE_CO,
 | 
			
		||||
                accuracy_decimals=0,
 | 
			
		||||
                device_class=DEVICE_CLASS_CARBON_MONOXIDE,
 | 
			
		||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_NITROGEN_DIOXIDE): sensor.sensor_schema(
 | 
			
		||||
                unit_of_measurement=UNIT_MICROGRAMS_PER_CUBIC_METER,
 | 
			
		||||
                icon=ICON_GAS_CYLINDER,
 | 
			
		||||
                accuracy_decimals=0,
 | 
			
		||||
                device_class=DEVICE_CLASS_NITROGEN_DIOXIDE,
 | 
			
		||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_ETHANOL): sensor.sensor_schema(
 | 
			
		||||
                unit_of_measurement=UNIT_PARTS_PER_MILLION,
 | 
			
		||||
                icon=ICON_FLASK_ROUND_BOTTOM,
 | 
			
		||||
                accuracy_decimals=0,
 | 
			
		||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
    .extend(cv.polling_component_schema("60s"))
 | 
			
		||||
    .extend(i2c.i2c_device_schema(0x08))
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await i2c.register_i2c_device(var, config)
 | 
			
		||||
 | 
			
		||||
    for key in [CONF_TVOC, CONF_CARBON_MONOXIDE, CONF_NITROGEN_DIOXIDE, CONF_ETHANOL]:
 | 
			
		||||
        if sensor_config := config.get(key):
 | 
			
		||||
            sensor_ = await sensor.new_sensor(sensor_config)
 | 
			
		||||
            cg.add(getattr(var, f"set_{key}_sensor")(sensor_))
 | 
			
		||||
@@ -114,6 +114,7 @@ SUPPORTED_CLIMATE_PRESETS_SMARTAIR2_OPTIONS = {
 | 
			
		||||
SUPPORTED_CLIMATE_PRESETS_HON_OPTIONS = {
 | 
			
		||||
    "AWAY": ClimatePreset.CLIMATE_PRESET_AWAY,
 | 
			
		||||
    "BOOST": ClimatePreset.CLIMATE_PRESET_BOOST,
 | 
			
		||||
    "ECO": ClimatePreset.CLIMATE_PRESET_ECO,
 | 
			
		||||
    "SLEEP": ClimatePreset.CLIMATE_PRESET_SLEEP,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -239,9 +240,7 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
                    ): cv.ensure_list(
 | 
			
		||||
                        cv.enum(SUPPORTED_HON_CONTROL_METHODS, upper=True)
 | 
			
		||||
                    ),
 | 
			
		||||
                    cv.Optional(CONF_BEEPER): cv.invalid(
 | 
			
		||||
                        f"The {CONF_BEEPER} option is deprecated, use beeper_on/beeper_off actions or beeper switch for a haier platform instead"
 | 
			
		||||
                    ),
 | 
			
		||||
                    cv.Optional(CONF_BEEPER, default=True): cv.boolean,
 | 
			
		||||
                    cv.Optional(
 | 
			
		||||
                        CONF_CONTROL_PACKET_SIZE, default=PROTOCOL_CONTROL_PACKET_SIZE
 | 
			
		||||
                    ): cv.int_range(min=PROTOCOL_CONTROL_PACKET_SIZE, max=50),
 | 
			
		||||
@@ -255,7 +254,7 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
                    ): cv.int_range(min=PROTOCOL_STATUS_MESSAGE_HEADER_SIZE),
 | 
			
		||||
                    cv.Optional(
 | 
			
		||||
                        CONF_SUPPORTED_PRESETS,
 | 
			
		||||
                        default=["BOOST", "SLEEP"],  # No AWAY by default
 | 
			
		||||
                        default=["BOOST", "ECO", "SLEEP"],  # No AWAY by default
 | 
			
		||||
                    ): cv.ensure_list(
 | 
			
		||||
                        cv.enum(SUPPORTED_CLIMATE_PRESETS_HON_OPTIONS, upper=True)
 | 
			
		||||
                    ),
 | 
			
		||||
 
 | 
			
		||||
@@ -52,6 +52,8 @@ bool check_timeout(std::chrono::steady_clock::time_point now, std::chrono::stead
 | 
			
		||||
HaierClimateBase::HaierClimateBase()
 | 
			
		||||
    : haier_protocol_(*this),
 | 
			
		||||
      protocol_phase_(ProtocolPhases::SENDING_INIT_1),
 | 
			
		||||
      display_status_(true),
 | 
			
		||||
      health_mode_(false),
 | 
			
		||||
      force_send_control_(false),
 | 
			
		||||
      forced_request_status_(false),
 | 
			
		||||
      reset_protocol_request_(false),
 | 
			
		||||
@@ -125,34 +127,21 @@ haier_protocol::HaierMessage HaierClimateBase::get_wifi_signal_message_() {
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void HaierClimateBase::save_settings() {
 | 
			
		||||
  HaierBaseSettings settings{this->get_health_mode(), this->get_display_state()};
 | 
			
		||||
  if (!this->base_rtc_.save(&settings)) {
 | 
			
		||||
    ESP_LOGW(TAG, "Failed to save settings");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool HaierClimateBase::get_display_state() const {
 | 
			
		||||
  return (this->display_status_ == SwitchState::ON) || (this->display_status_ == SwitchState::PENDING_ON);
 | 
			
		||||
}
 | 
			
		||||
bool HaierClimateBase::get_display_state() const { return this->display_status_; }
 | 
			
		||||
 | 
			
		||||
void HaierClimateBase::set_display_state(bool state) {
 | 
			
		||||
  if (state != this->get_display_state()) {
 | 
			
		||||
    this->display_status_ = state ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF;
 | 
			
		||||
  if (this->display_status_ != state) {
 | 
			
		||||
    this->display_status_ = state;
 | 
			
		||||
    this->force_send_control_ = true;
 | 
			
		||||
    this->save_settings();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool HaierClimateBase::get_health_mode() const {
 | 
			
		||||
  return (this->health_mode_ == SwitchState::ON) || (this->health_mode_ == SwitchState::PENDING_ON);
 | 
			
		||||
}
 | 
			
		||||
bool HaierClimateBase::get_health_mode() const { return this->health_mode_; }
 | 
			
		||||
 | 
			
		||||
void HaierClimateBase::set_health_mode(bool state) {
 | 
			
		||||
  if (state != this->get_health_mode()) {
 | 
			
		||||
    this->health_mode_ = state ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF;
 | 
			
		||||
  if (this->health_mode_ != state) {
 | 
			
		||||
    this->health_mode_ = state;
 | 
			
		||||
    this->force_send_control_ = true;
 | 
			
		||||
    this->save_settings();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -298,14 +287,6 @@ void HaierClimateBase::loop() {
 | 
			
		||||
  }
 | 
			
		||||
  this->process_phase(now);
 | 
			
		||||
  this->haier_protocol_.loop();
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
  if ((this->display_switch_ != nullptr) && (this->display_switch_->state != this->get_display_state())) {
 | 
			
		||||
    this->display_switch_->publish_state(this->get_display_state());
 | 
			
		||||
  }
 | 
			
		||||
  if ((this->health_mode_switch_ != nullptr) && (this->health_mode_switch_->state != this->get_health_mode())) {
 | 
			
		||||
    this->health_mode_switch_->publish_state(this->get_health_mode());
 | 
			
		||||
  }
 | 
			
		||||
#endif  // USE_SWITCH
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HaierClimateBase::process_protocol_reset() {
 | 
			
		||||
@@ -348,26 +329,6 @@ bool HaierClimateBase::prepare_pending_action() {
 | 
			
		||||
 | 
			
		||||
ClimateTraits HaierClimateBase::traits() { return traits_; }
 | 
			
		||||
 | 
			
		||||
void HaierClimateBase::initialization() {
 | 
			
		||||
  constexpr uint32_t restore_settings_version = 0xA77D21EF;
 | 
			
		||||
  this->base_rtc_ =
 | 
			
		||||
      global_preferences->make_preference<HaierBaseSettings>(this->get_object_id_hash() ^ restore_settings_version);
 | 
			
		||||
  HaierBaseSettings recovered;
 | 
			
		||||
  if (!this->base_rtc_.load(&recovered)) {
 | 
			
		||||
    recovered = {false, true};
 | 
			
		||||
  }
 | 
			
		||||
  this->display_status_ = recovered.display_state ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF;
 | 
			
		||||
  this->health_mode_ = recovered.health_mode ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF;
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
  if (this->display_switch_ != nullptr) {
 | 
			
		||||
    this->display_switch_->publish_state(this->get_display_state());
 | 
			
		||||
  }
 | 
			
		||||
  if (this->health_mode_switch_ != nullptr) {
 | 
			
		||||
    this->health_mode_switch_->publish_state(this->get_health_mode());
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HaierClimateBase::control(const ClimateCall &call) {
 | 
			
		||||
  ESP_LOGD("Control", "Control call");
 | 
			
		||||
  if (!this->valid_connection()) {
 | 
			
		||||
@@ -392,22 +353,6 @@ void HaierClimateBase::control(const ClimateCall &call) {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
void HaierClimateBase::set_display_switch(switch_::Switch *sw) {
 | 
			
		||||
  this->display_switch_ = sw;
 | 
			
		||||
  if ((this->display_switch_ != nullptr) && (this->valid_connection())) {
 | 
			
		||||
    this->display_switch_->publish_state(this->get_display_state());
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HaierClimateBase::set_health_mode_switch(switch_::Switch *sw) {
 | 
			
		||||
  this->health_mode_switch_ = sw;
 | 
			
		||||
  if ((this->health_mode_switch_ != nullptr) && (this->valid_connection())) {
 | 
			
		||||
    this->health_mode_switch_->publish_state(this->get_health_mode());
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void HaierClimateBase::HvacSettings::reset() {
 | 
			
		||||
  this->valid = false;
 | 
			
		||||
  this->mode.reset();
 | 
			
		||||
 
 | 
			
		||||
@@ -8,10 +8,6 @@
 | 
			
		||||
// HaierProtocol
 | 
			
		||||
#include <protocol/haier_protocol.h>
 | 
			
		||||
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
#include "esphome/components/switch/switch.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace haier {
 | 
			
		||||
 | 
			
		||||
@@ -24,24 +20,10 @@ enum class ActionRequest : uint8_t {
 | 
			
		||||
  START_STERI_CLEAN = 5,  // only hOn
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct HaierBaseSettings {
 | 
			
		||||
  bool health_mode;
 | 
			
		||||
  bool display_state;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class HaierClimateBase : public esphome::Component,
 | 
			
		||||
                         public esphome::climate::Climate,
 | 
			
		||||
                         public esphome::uart::UARTDevice,
 | 
			
		||||
                         public haier_protocol::ProtocolStream {
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
 public:
 | 
			
		||||
  void set_display_switch(switch_::Switch *sw);
 | 
			
		||||
  void set_health_mode_switch(switch_::Switch *sw);
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  switch_::Switch *display_switch_{nullptr};
 | 
			
		||||
  switch_::Switch *health_mode_switch_{nullptr};
 | 
			
		||||
#endif
 | 
			
		||||
 public:
 | 
			
		||||
  HaierClimateBase();
 | 
			
		||||
  HaierClimateBase(const HaierClimateBase &) = delete;
 | 
			
		||||
@@ -100,8 +82,7 @@ class HaierClimateBase : public esphome::Component,
 | 
			
		||||
  virtual void process_phase(std::chrono::steady_clock::time_point now) = 0;
 | 
			
		||||
  virtual haier_protocol::HaierMessage get_control_message() = 0;          // NOLINT(readability-identifier-naming)
 | 
			
		||||
  virtual haier_protocol::HaierMessage get_power_message(bool state) = 0;  // NOLINT(readability-identifier-naming)
 | 
			
		||||
  virtual void save_settings();
 | 
			
		||||
  virtual void initialization();
 | 
			
		||||
  virtual void initialization(){};
 | 
			
		||||
  virtual bool prepare_pending_action();
 | 
			
		||||
  virtual void process_protocol_reset();
 | 
			
		||||
  esphome::climate::ClimateTraits traits() override;
 | 
			
		||||
@@ -146,19 +127,13 @@ class HaierClimateBase : public esphome::Component,
 | 
			
		||||
    ActionRequest action;
 | 
			
		||||
    esphome::optional<haier_protocol::HaierMessage> message;
 | 
			
		||||
  };
 | 
			
		||||
  enum class SwitchState {
 | 
			
		||||
    OFF = 0b00,
 | 
			
		||||
    ON = 0b01,
 | 
			
		||||
    PENDING_OFF = 0b10,
 | 
			
		||||
    PENDING_ON = 0b11,
 | 
			
		||||
  };
 | 
			
		||||
  haier_protocol::ProtocolHandler haier_protocol_;
 | 
			
		||||
  ProtocolPhases protocol_phase_;
 | 
			
		||||
  esphome::optional<PendingAction> action_request_;
 | 
			
		||||
  uint8_t fan_mode_speed_;
 | 
			
		||||
  uint8_t other_modes_fan_speed_;
 | 
			
		||||
  SwitchState display_status_{SwitchState::ON};
 | 
			
		||||
  SwitchState health_mode_{SwitchState::OFF};
 | 
			
		||||
  bool display_status_;
 | 
			
		||||
  bool health_mode_;
 | 
			
		||||
  bool force_send_control_;
 | 
			
		||||
  bool forced_request_status_;
 | 
			
		||||
  bool reset_protocol_request_;
 | 
			
		||||
@@ -173,7 +148,6 @@ class HaierClimateBase : public esphome::Component,
 | 
			
		||||
  std::chrono::steady_clock::time_point last_status_request_;          // To request AC status
 | 
			
		||||
  std::chrono::steady_clock::time_point last_signal_request_;          // To send WiFI signal level
 | 
			
		||||
  CallbackManager<void(const char *, size_t)> status_message_callback_{};
 | 
			
		||||
  ESPPreferenceObject base_rtc_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class StatusMessageTrigger : public Trigger<const char *, size_t> {
 | 
			
		||||
 
 | 
			
		||||
@@ -31,32 +31,9 @@ HonClimate::HonClimate()
 | 
			
		||||
 | 
			
		||||
HonClimate::~HonClimate() {}
 | 
			
		||||
 | 
			
		||||
void HonClimate::set_beeper_state(bool state) {
 | 
			
		||||
  if (state != this->settings_.beeper_state) {
 | 
			
		||||
    this->settings_.beeper_state = state;
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
    this->beeper_switch_->publish_state(state);
 | 
			
		||||
#endif
 | 
			
		||||
    this->hon_rtc_.save(&this->settings_);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void HonClimate::set_beeper_state(bool state) { this->beeper_status_ = state; }
 | 
			
		||||
 | 
			
		||||
bool HonClimate::get_beeper_state() const { return this->settings_.beeper_state; }
 | 
			
		||||
 | 
			
		||||
void HonClimate::set_quiet_mode_state(bool state) {
 | 
			
		||||
  if (state != this->get_quiet_mode_state()) {
 | 
			
		||||
    this->quiet_mode_state_ = state ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF;
 | 
			
		||||
    this->settings_.quiet_mode_state = state;
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
    this->quiet_mode_switch_->publish_state(state);
 | 
			
		||||
#endif
 | 
			
		||||
    this->hon_rtc_.save(&this->settings_);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool HonClimate::get_quiet_mode_state() const {
 | 
			
		||||
  return (this->quiet_mode_state_ == SwitchState::ON) || (this->quiet_mode_state_ == SwitchState::PENDING_ON);
 | 
			
		||||
}
 | 
			
		||||
bool HonClimate::get_beeper_state() const { return this->beeper_status_; }
 | 
			
		||||
 | 
			
		||||
esphome::optional<hon_protocol::VerticalSwingMode> HonClimate::get_vertical_airflow() const {
 | 
			
		||||
  return this->current_vertical_swing_;
 | 
			
		||||
@@ -497,19 +474,16 @@ haier_protocol::HaierMessage HonClimate::get_power_message(bool state) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HonClimate::initialization() {
 | 
			
		||||
  HaierClimateBase::initialization();
 | 
			
		||||
  constexpr uint32_t restore_settings_version = 0x57EB59DDUL;
 | 
			
		||||
  this->hon_rtc_ =
 | 
			
		||||
      global_preferences->make_preference<HonSettings>(this->get_object_id_hash() ^ restore_settings_version);
 | 
			
		||||
  constexpr uint32_t restore_settings_version = 0xE834D8DCUL;
 | 
			
		||||
  this->rtc_ = global_preferences->make_preference<HonSettings>(this->get_object_id_hash() ^ restore_settings_version);
 | 
			
		||||
  HonSettings recovered;
 | 
			
		||||
  if (this->hon_rtc_.load(&recovered)) {
 | 
			
		||||
  if (this->rtc_.load(&recovered)) {
 | 
			
		||||
    this->settings_ = recovered;
 | 
			
		||||
  } else {
 | 
			
		||||
    this->settings_ = {hon_protocol::VerticalSwingMode::CENTER, hon_protocol::HorizontalSwingMode::CENTER, true, false};
 | 
			
		||||
    this->settings_ = {hon_protocol::VerticalSwingMode::CENTER, hon_protocol::HorizontalSwingMode::CENTER};
 | 
			
		||||
  }
 | 
			
		||||
  this->current_vertical_swing_ = this->settings_.last_vertiacal_swing;
 | 
			
		||||
  this->current_horizontal_swing_ = this->settings_.last_horizontal_swing;
 | 
			
		||||
  this->quiet_mode_state_ = this->settings_.quiet_mode_state ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
haier_protocol::HaierMessage HonClimate::get_control_message() {
 | 
			
		||||
@@ -545,7 +519,8 @@ haier_protocol::HaierMessage HonClimate::get_control_message() {
 | 
			
		||||
          out_data->ac_power = 1;
 | 
			
		||||
          out_data->ac_mode = (uint8_t) hon_protocol::ConditioningMode::FAN;
 | 
			
		||||
          out_data->fan_mode = this->fan_mode_speed_;  // Auto doesn't work in fan only mode
 | 
			
		||||
          // Disabling boost for Fan only
 | 
			
		||||
          // Disabling boost and eco mode for Fan only
 | 
			
		||||
          out_data->quiet_mode = 0;
 | 
			
		||||
          out_data->fast_mode = 0;
 | 
			
		||||
          break;
 | 
			
		||||
        case CLIMATE_MODE_COOL:
 | 
			
		||||
@@ -607,34 +582,47 @@ haier_protocol::HaierMessage HonClimate::get_control_message() {
 | 
			
		||||
    }
 | 
			
		||||
    if (out_data->ac_power == 0) {
 | 
			
		||||
      // If AC is off - no presets allowed
 | 
			
		||||
      out_data->quiet_mode = 0;
 | 
			
		||||
      out_data->fast_mode = 0;
 | 
			
		||||
      out_data->sleep_mode = 0;
 | 
			
		||||
    } else if (climate_control.preset.has_value()) {
 | 
			
		||||
      switch (climate_control.preset.value()) {
 | 
			
		||||
        case CLIMATE_PRESET_NONE:
 | 
			
		||||
          out_data->quiet_mode = 0;
 | 
			
		||||
          out_data->fast_mode = 0;
 | 
			
		||||
          out_data->sleep_mode = 0;
 | 
			
		||||
          out_data->ten_degree = 0;
 | 
			
		||||
          break;
 | 
			
		||||
        case CLIMATE_PRESET_ECO:
 | 
			
		||||
          // Eco is not supported in Fan only mode
 | 
			
		||||
          out_data->quiet_mode = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 1 : 0;
 | 
			
		||||
          out_data->fast_mode = 0;
 | 
			
		||||
          out_data->sleep_mode = 0;
 | 
			
		||||
          out_data->ten_degree = 0;
 | 
			
		||||
          break;
 | 
			
		||||
        case CLIMATE_PRESET_BOOST:
 | 
			
		||||
          out_data->quiet_mode = 0;
 | 
			
		||||
          // Boost is not supported in Fan only mode
 | 
			
		||||
          out_data->fast_mode = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 1 : 0;
 | 
			
		||||
          out_data->sleep_mode = 0;
 | 
			
		||||
          out_data->ten_degree = 0;
 | 
			
		||||
          break;
 | 
			
		||||
        case CLIMATE_PRESET_AWAY:
 | 
			
		||||
          out_data->quiet_mode = 0;
 | 
			
		||||
          out_data->fast_mode = 0;
 | 
			
		||||
          out_data->sleep_mode = 0;
 | 
			
		||||
          // 10 degrees allowed only in heat mode
 | 
			
		||||
          out_data->ten_degree = (this->mode == CLIMATE_MODE_HEAT) ? 1 : 0;
 | 
			
		||||
          break;
 | 
			
		||||
        case CLIMATE_PRESET_SLEEP:
 | 
			
		||||
          out_data->quiet_mode = 0;
 | 
			
		||||
          out_data->fast_mode = 0;
 | 
			
		||||
          out_data->sleep_mode = 1;
 | 
			
		||||
          out_data->ten_degree = 0;
 | 
			
		||||
          break;
 | 
			
		||||
        default:
 | 
			
		||||
          ESP_LOGE("Control", "Unsupported preset");
 | 
			
		||||
          out_data->quiet_mode = 0;
 | 
			
		||||
          out_data->fast_mode = 0;
 | 
			
		||||
          out_data->sleep_mode = 0;
 | 
			
		||||
          out_data->ten_degree = 0;
 | 
			
		||||
@@ -650,23 +638,10 @@ haier_protocol::HaierMessage HonClimate::get_control_message() {
 | 
			
		||||
    out_data->horizontal_swing_mode = (uint8_t) this->pending_horizontal_direction_.value();
 | 
			
		||||
    this->pending_horizontal_direction_.reset();
 | 
			
		||||
  }
 | 
			
		||||
  {
 | 
			
		||||
    // Quiet mode
 | 
			
		||||
    if ((out_data->ac_power == 0) || (out_data->ac_mode == (uint8_t) hon_protocol::ConditioningMode::FAN)) {
 | 
			
		||||
      // If AC is off or in fan only mode - no quiet mode allowed
 | 
			
		||||
      out_data->quiet_mode = 0;
 | 
			
		||||
    } else {
 | 
			
		||||
      out_data->quiet_mode = this->get_quiet_mode_state() ? 1 : 0;
 | 
			
		||||
    }
 | 
			
		||||
    // Clean quiet mode state pending flag
 | 
			
		||||
    this->quiet_mode_state_ = (SwitchState) ((uint8_t) this->quiet_mode_state_ & 0b01);
 | 
			
		||||
  }
 | 
			
		||||
  out_data->beeper_status = ((!this->get_beeper_state()) || (!has_hvac_settings)) ? 1 : 0;
 | 
			
		||||
  out_data->beeper_status = ((!this->beeper_status_) || (!has_hvac_settings)) ? 1 : 0;
 | 
			
		||||
  control_out_buffer[4] = 0;  // This byte should be cleared before setting values
 | 
			
		||||
  out_data->display_status = this->get_display_state() ? 1 : 0;
 | 
			
		||||
  this->display_status_ = (SwitchState) ((uint8_t) this->display_status_ & 0b01);
 | 
			
		||||
  out_data->health_mode = this->get_health_mode() ? 1 : 0;
 | 
			
		||||
  this->health_mode_ = (SwitchState) ((uint8_t) this->health_mode_ & 0b01);
 | 
			
		||||
  out_data->display_status = this->display_status_ ? 1 : 0;
 | 
			
		||||
  out_data->health_mode = this->health_mode_ ? 1 : 0;
 | 
			
		||||
  return haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
 | 
			
		||||
                                      (uint16_t) hon_protocol::SubcommandsControl::SET_GROUP_PARAMETERS,
 | 
			
		||||
                                      control_out_buffer, this->real_control_packet_size_);
 | 
			
		||||
@@ -790,22 +765,6 @@ void HonClimate::update_sub_text_sensor_(SubTextSensorType type, const std::stri
 | 
			
		||||
}
 | 
			
		||||
#endif  // USE_TEXT_SENSOR
 | 
			
		||||
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
void HonClimate::set_beeper_switch(switch_::Switch *sw) {
 | 
			
		||||
  this->beeper_switch_ = sw;
 | 
			
		||||
  if (this->beeper_switch_ != nullptr) {
 | 
			
		||||
    this->beeper_switch_->publish_state(this->get_beeper_state());
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HonClimate::set_quiet_mode_switch(switch_::Switch *sw) {
 | 
			
		||||
  this->quiet_mode_switch_ = sw;
 | 
			
		||||
  if (this->quiet_mode_switch_ != nullptr) {
 | 
			
		||||
    this->quiet_mode_switch_->publish_state(this->settings_.quiet_mode_state);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
#endif  // USE_SWITCH
 | 
			
		||||
 | 
			
		||||
haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t *packet_buffer, uint8_t size) {
 | 
			
		||||
  size_t expected_size =
 | 
			
		||||
      2 + this->status_message_header_size_ + this->real_control_packet_size_ + this->real_sensors_packet_size_;
 | 
			
		||||
@@ -868,7 +827,9 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t *
 | 
			
		||||
  {
 | 
			
		||||
    // Extra modes/presets
 | 
			
		||||
    optional<ClimatePreset> old_preset = this->preset;
 | 
			
		||||
    if (packet.control.fast_mode != 0) {
 | 
			
		||||
    if (packet.control.quiet_mode != 0) {
 | 
			
		||||
      this->preset = CLIMATE_PRESET_ECO;
 | 
			
		||||
    } else if (packet.control.fast_mode != 0) {
 | 
			
		||||
      this->preset = CLIMATE_PRESET_BOOST;
 | 
			
		||||
    } else if (packet.control.sleep_mode != 0) {
 | 
			
		||||
      this->preset = CLIMATE_PRESET_SLEEP;
 | 
			
		||||
@@ -922,26 +883,28 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t *
 | 
			
		||||
    }
 | 
			
		||||
    should_publish = should_publish || (!old_fan_mode.has_value()) || (old_fan_mode.value() != fan_mode.value());
 | 
			
		||||
  }
 | 
			
		||||
  // Display status
 | 
			
		||||
  // should be before "Climate mode" because it is changing this->mode
 | 
			
		||||
  if (packet.control.ac_power != 0) {
 | 
			
		||||
    // if AC is off display status always ON so process it only when AC is on
 | 
			
		||||
    bool disp_status = packet.control.display_status != 0;
 | 
			
		||||
    if (disp_status != this->get_display_state()) {
 | 
			
		||||
      // Do something only if display status changed
 | 
			
		||||
      if (this->mode == CLIMATE_MODE_OFF) {
 | 
			
		||||
        // AC just turned on from remote need to turn off display
 | 
			
		||||
        this->force_send_control_ = true;
 | 
			
		||||
      } else if ((((uint8_t) this->health_mode_) & 0b10) == 0) {
 | 
			
		||||
        this->display_status_ = disp_status ? SwitchState::ON : SwitchState::OFF;
 | 
			
		||||
  {
 | 
			
		||||
    // Display status
 | 
			
		||||
    // should be before "Climate mode" because it is changing this->mode
 | 
			
		||||
    if (packet.control.ac_power != 0) {
 | 
			
		||||
      // if AC is off display status always ON so process it only when AC is on
 | 
			
		||||
      bool disp_status = packet.control.display_status != 0;
 | 
			
		||||
      if (disp_status != this->display_status_) {
 | 
			
		||||
        // Do something only if display status changed
 | 
			
		||||
        if (this->mode == CLIMATE_MODE_OFF) {
 | 
			
		||||
          // AC just turned on from remote need to turn off display
 | 
			
		||||
          this->force_send_control_ = true;
 | 
			
		||||
        } else {
 | 
			
		||||
          this->display_status_ = disp_status;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  // Health mode
 | 
			
		||||
  if ((((uint8_t) this->health_mode_) & 0b10) == 0) {
 | 
			
		||||
    bool old_health_mode = this->get_health_mode();
 | 
			
		||||
    this->health_mode_ = packet.control.health_mode == 1 ? SwitchState::ON : SwitchState::OFF;
 | 
			
		||||
    should_publish = should_publish || (old_health_mode != this->get_health_mode());
 | 
			
		||||
  {
 | 
			
		||||
    // Health mode
 | 
			
		||||
    bool old_health_mode = this->health_mode_;
 | 
			
		||||
    this->health_mode_ = packet.control.health_mode == 1;
 | 
			
		||||
    should_publish = should_publish || (old_health_mode != this->health_mode_);
 | 
			
		||||
  }
 | 
			
		||||
  {
 | 
			
		||||
    CleaningState new_cleaning;
 | 
			
		||||
@@ -995,36 +958,17 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t *
 | 
			
		||||
    }
 | 
			
		||||
    should_publish = should_publish || (old_mode != this->mode);
 | 
			
		||||
  }
 | 
			
		||||
  {
 | 
			
		||||
    // Quiet mode, should be after climate mode
 | 
			
		||||
    if ((this->mode != CLIMATE_MODE_FAN_ONLY) && (this->mode != CLIMATE_MODE_OFF) &&
 | 
			
		||||
        ((((uint8_t) this->quiet_mode_state_) & 0b10) == 0)) {
 | 
			
		||||
      // In proper mode and not in pending state
 | 
			
		||||
      bool new_quiet_mode = packet.control.quiet_mode != 0;
 | 
			
		||||
      if (new_quiet_mode != this->get_quiet_mode_state()) {
 | 
			
		||||
        this->quiet_mode_state_ = new_quiet_mode ? SwitchState::ON : SwitchState::OFF;
 | 
			
		||||
        this->settings_.quiet_mode_state = new_quiet_mode;
 | 
			
		||||
        this->hon_rtc_.save(&this->settings_);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  {
 | 
			
		||||
    // Swing mode
 | 
			
		||||
    ClimateSwingMode old_swing_mode = this->swing_mode;
 | 
			
		||||
    const std::set<ClimateSwingMode> &swing_modes = traits_.get_supported_swing_modes();
 | 
			
		||||
    bool vertical_swing_supported = swing_modes.find(CLIMATE_SWING_VERTICAL) != swing_modes.end();
 | 
			
		||||
    bool horizontal_swing_supported = swing_modes.find(CLIMATE_SWING_HORIZONTAL) != swing_modes.end();
 | 
			
		||||
    if (horizontal_swing_supported &&
 | 
			
		||||
        (packet.control.horizontal_swing_mode == (uint8_t) hon_protocol::HorizontalSwingMode::AUTO)) {
 | 
			
		||||
      if (vertical_swing_supported &&
 | 
			
		||||
          (packet.control.vertical_swing_mode == (uint8_t) hon_protocol::VerticalSwingMode::AUTO)) {
 | 
			
		||||
    if (packet.control.horizontal_swing_mode == (uint8_t) hon_protocol::HorizontalSwingMode::AUTO) {
 | 
			
		||||
      if (packet.control.vertical_swing_mode == (uint8_t) hon_protocol::VerticalSwingMode::AUTO) {
 | 
			
		||||
        this->swing_mode = CLIMATE_SWING_BOTH;
 | 
			
		||||
      } else {
 | 
			
		||||
        this->swing_mode = CLIMATE_SWING_HORIZONTAL;
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      if (vertical_swing_supported &&
 | 
			
		||||
          (packet.control.vertical_swing_mode == (uint8_t) hon_protocol::VerticalSwingMode::AUTO)) {
 | 
			
		||||
      if (packet.control.vertical_swing_mode == (uint8_t) hon_protocol::VerticalSwingMode::AUTO) {
 | 
			
		||||
        this->swing_mode = CLIMATE_SWING_VERTICAL;
 | 
			
		||||
      } else {
 | 
			
		||||
        this->swing_mode = CLIMATE_SWING_OFF;
 | 
			
		||||
@@ -1041,7 +985,7 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t *
 | 
			
		||||
    if (save_settings) {
 | 
			
		||||
      this->settings_.last_vertiacal_swing = this->current_vertical_swing_.value();
 | 
			
		||||
      this->settings_.last_horizontal_swing = this->current_horizontal_swing_.value();
 | 
			
		||||
      this->hon_rtc_.save(&this->settings_);
 | 
			
		||||
      this->rtc_.save(&this->settings_);
 | 
			
		||||
    }
 | 
			
		||||
    should_publish = should_publish || (old_swing_mode != this->swing_mode);
 | 
			
		||||
  }
 | 
			
		||||
@@ -1073,7 +1017,7 @@ void HonClimate::fill_control_messages_queue_() {
 | 
			
		||||
        haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
 | 
			
		||||
                                     (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER +
 | 
			
		||||
                                         (uint8_t) hon_protocol::DataParameters::BEEPER_STATUS,
 | 
			
		||||
                                     this->get_beeper_state() ? ZERO_BUF : ONE_BUF, 2));
 | 
			
		||||
                                     this->beeper_status_ ? ZERO_BUF : ONE_BUF, 2));
 | 
			
		||||
  }
 | 
			
		||||
  // Health mode
 | 
			
		||||
  {
 | 
			
		||||
@@ -1081,16 +1025,13 @@ void HonClimate::fill_control_messages_queue_() {
 | 
			
		||||
        haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
 | 
			
		||||
                                     (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER +
 | 
			
		||||
                                         (uint8_t) hon_protocol::DataParameters::HEALTH_MODE,
 | 
			
		||||
                                     this->get_health_mode() ? ONE_BUF : ZERO_BUF, 2));
 | 
			
		||||
    this->health_mode_ = (SwitchState) ((uint8_t) this->health_mode_ & 0b01);
 | 
			
		||||
                                     this->health_mode_ ? ONE_BUF : ZERO_BUF, 2));
 | 
			
		||||
  }
 | 
			
		||||
  // Climate mode
 | 
			
		||||
  ClimateMode climate_mode = this->mode;
 | 
			
		||||
  bool new_power = this->mode != CLIMATE_MODE_OFF;
 | 
			
		||||
  uint8_t fan_mode_buf[] = {0x00, 0xFF};
 | 
			
		||||
  uint8_t quiet_mode_buf[] = {0x00, 0xFF};
 | 
			
		||||
  if (climate_control.mode.has_value()) {
 | 
			
		||||
    climate_mode = climate_control.mode.value();
 | 
			
		||||
    uint8_t buffer[2] = {0x00, 0x00};
 | 
			
		||||
    switch (climate_control.mode.value()) {
 | 
			
		||||
      case CLIMATE_MODE_OFF:
 | 
			
		||||
@@ -1135,6 +1076,8 @@ void HonClimate::fill_control_messages_queue_() {
 | 
			
		||||
                                             (uint8_t) hon_protocol::DataParameters::AC_MODE,
 | 
			
		||||
                                         buffer, 2));
 | 
			
		||||
        fan_mode_buf[1] = this->other_modes_fan_speed_;  // Auto doesn't work in fan only mode
 | 
			
		||||
        // Disabling eco mode for Fan only
 | 
			
		||||
        quiet_mode_buf[1] = 0;
 | 
			
		||||
        break;
 | 
			
		||||
      case CLIMATE_MODE_COOL:
 | 
			
		||||
        new_power = true;
 | 
			
		||||
@@ -1165,20 +1108,30 @@ void HonClimate::fill_control_messages_queue_() {
 | 
			
		||||
    uint8_t away_mode_buf[] = {0x00, 0xFF};
 | 
			
		||||
    if (!new_power) {
 | 
			
		||||
      // If AC is off - no presets allowed
 | 
			
		||||
      quiet_mode_buf[1] = 0x00;
 | 
			
		||||
      fast_mode_buf[1] = 0x00;
 | 
			
		||||
      away_mode_buf[1] = 0x00;
 | 
			
		||||
    } else if (climate_control.preset.has_value()) {
 | 
			
		||||
      switch (climate_control.preset.value()) {
 | 
			
		||||
        case CLIMATE_PRESET_NONE:
 | 
			
		||||
          quiet_mode_buf[1] = 0x00;
 | 
			
		||||
          fast_mode_buf[1] = 0x00;
 | 
			
		||||
          away_mode_buf[1] = 0x00;
 | 
			
		||||
          break;
 | 
			
		||||
        case CLIMATE_PRESET_ECO:
 | 
			
		||||
          // Eco is not supported in Fan only mode
 | 
			
		||||
          quiet_mode_buf[1] = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 0x01 : 0x00;
 | 
			
		||||
          fast_mode_buf[1] = 0x00;
 | 
			
		||||
          away_mode_buf[1] = 0x00;
 | 
			
		||||
          break;
 | 
			
		||||
        case CLIMATE_PRESET_BOOST:
 | 
			
		||||
          quiet_mode_buf[1] = 0x00;
 | 
			
		||||
          // Boost is not supported in Fan only mode
 | 
			
		||||
          fast_mode_buf[1] = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 0x01 : 0x00;
 | 
			
		||||
          away_mode_buf[1] = 0x00;
 | 
			
		||||
          break;
 | 
			
		||||
        case CLIMATE_PRESET_AWAY:
 | 
			
		||||
          quiet_mode_buf[1] = 0x00;
 | 
			
		||||
          fast_mode_buf[1] = 0x00;
 | 
			
		||||
          away_mode_buf[1] = (this->mode == CLIMATE_MODE_HEAT) ? 0x01 : 0x00;
 | 
			
		||||
          break;
 | 
			
		||||
@@ -1187,18 +1140,8 @@ void HonClimate::fill_control_messages_queue_() {
 | 
			
		||||
          break;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    {
 | 
			
		||||
      // Quiet mode
 | 
			
		||||
      if (new_power && (climate_mode != CLIMATE_MODE_FAN_ONLY) && this->get_quiet_mode_state()) {
 | 
			
		||||
        quiet_mode_buf[1] = 0x01;
 | 
			
		||||
      } else {
 | 
			
		||||
        quiet_mode_buf[1] = 0x00;
 | 
			
		||||
      }
 | 
			
		||||
      // Clean quiet mode state pending flag
 | 
			
		||||
      this->quiet_mode_state_ = (SwitchState) ((uint8_t) this->quiet_mode_state_ & 0b01);
 | 
			
		||||
    }
 | 
			
		||||
    auto presets = this->traits_.get_supported_presets();
 | 
			
		||||
    if (quiet_mode_buf[1] != 0xFF) {
 | 
			
		||||
    if ((quiet_mode_buf[1] != 0xFF) && ((presets.find(climate::ClimatePreset::CLIMATE_PRESET_ECO) != presets.end()))) {
 | 
			
		||||
      this->control_messages_queue_.push(
 | 
			
		||||
          haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
 | 
			
		||||
                                       (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER +
 | 
			
		||||
 
 | 
			
		||||
@@ -10,9 +10,6 @@
 | 
			
		||||
#ifdef USE_TEXT_SENSOR
 | 
			
		||||
#include "esphome/components/text_sensor/text_sensor.h"
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
#include "esphome/components/switch/switch.h"
 | 
			
		||||
#endif
 | 
			
		||||
#include "esphome/core/automation.h"
 | 
			
		||||
#include "haier_base.h"
 | 
			
		||||
#include "hon_packet.h"
 | 
			
		||||
@@ -31,8 +28,6 @@ enum class HonControlMethod { MONITOR_ONLY = 0, SET_GROUP_PARAMETERS, SET_SINGLE
 | 
			
		||||
struct HonSettings {
 | 
			
		||||
  hon_protocol::VerticalSwingMode last_vertiacal_swing;
 | 
			
		||||
  hon_protocol::HorizontalSwingMode last_horizontal_swing;
 | 
			
		||||
  bool beeper_state;
 | 
			
		||||
  bool quiet_mode_state;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class HonClimate : public HaierClimateBase {
 | 
			
		||||
@@ -91,15 +86,6 @@ class HonClimate : public HaierClimateBase {
 | 
			
		||||
 protected:
 | 
			
		||||
  void update_sub_text_sensor_(SubTextSensorType type, const std::string &value);
 | 
			
		||||
  text_sensor::TextSensor *sub_text_sensors_[(size_t) SubTextSensorType::SUB_TEXT_SENSOR_TYPE_COUNT]{nullptr};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
 public:
 | 
			
		||||
  void set_beeper_switch(switch_::Switch *sw);
 | 
			
		||||
  void set_quiet_mode_switch(switch_::Switch *sw);
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  switch_::Switch *beeper_switch_{nullptr};
 | 
			
		||||
  switch_::Switch *quiet_mode_switch_{nullptr};
 | 
			
		||||
#endif
 | 
			
		||||
 public:
 | 
			
		||||
  HonClimate();
 | 
			
		||||
@@ -109,8 +95,6 @@ class HonClimate : public HaierClimateBase {
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
  void set_beeper_state(bool state);
 | 
			
		||||
  bool get_beeper_state() const;
 | 
			
		||||
  void set_quiet_mode_state(bool state);
 | 
			
		||||
  bool get_quiet_mode_state() const;
 | 
			
		||||
  esphome::optional<hon_protocol::VerticalSwingMode> get_vertical_airflow() const;
 | 
			
		||||
  void set_vertical_airflow(hon_protocol::VerticalSwingMode direction);
 | 
			
		||||
  esphome::optional<hon_protocol::HorizontalSwingMode> get_horizontal_airflow() const;
 | 
			
		||||
@@ -169,6 +153,7 @@ class HonClimate : public HaierClimateBase {
 | 
			
		||||
    bool functions_[5];
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  bool beeper_status_;
 | 
			
		||||
  CleaningState cleaning_status_;
 | 
			
		||||
  bool got_valid_outdoor_temp_;
 | 
			
		||||
  esphome::optional<hon_protocol::VerticalSwingMode> pending_vertical_direction_{};
 | 
			
		||||
@@ -190,8 +175,7 @@ class HonClimate : public HaierClimateBase {
 | 
			
		||||
  esphome::optional<hon_protocol::VerticalSwingMode> current_vertical_swing_{};
 | 
			
		||||
  esphome::optional<hon_protocol::HorizontalSwingMode> current_horizontal_swing_{};
 | 
			
		||||
  HonSettings settings_;
 | 
			
		||||
  ESPPreferenceObject hon_rtc_;
 | 
			
		||||
  SwitchState quiet_mode_state_{SwitchState::OFF};
 | 
			
		||||
  ESPPreferenceObject rtc_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class HaierAlarmStartTrigger : public Trigger<uint8_t, const char *> {
 | 
			
		||||
 
 | 
			
		||||
@@ -376,10 +376,8 @@ haier_protocol::HaierMessage Smartair2Climate::get_control_message() {
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  out_data->display_status = this->get_display_state() ? 0 : 1;
 | 
			
		||||
  this->display_status_ = (SwitchState) ((uint8_t) this->display_status_ & 0b01);
 | 
			
		||||
  out_data->health_mode = this->get_health_mode() ? 1 : 0;
 | 
			
		||||
  this->health_mode_ = (SwitchState) ((uint8_t) this->health_mode_ & 0b01);
 | 
			
		||||
  out_data->display_status = this->display_status_ ? 0 : 1;
 | 
			
		||||
  out_data->health_mode = this->health_mode_ ? 1 : 0;
 | 
			
		||||
  return haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL, 0x4D5F, control_out_buffer,
 | 
			
		||||
                                      sizeof(smartair2_protocol::HaierPacketControl));
 | 
			
		||||
}
 | 
			
		||||
@@ -448,26 +446,28 @@ haier_protocol::HandlerError Smartair2Climate::process_status_message_(const uin
 | 
			
		||||
    }
 | 
			
		||||
    should_publish = should_publish || (!old_fan_mode.has_value()) || (old_fan_mode.value() != fan_mode.value());
 | 
			
		||||
  }
 | 
			
		||||
  // Display status
 | 
			
		||||
  // should be before "Climate mode" because it is changing this->mode
 | 
			
		||||
  if (packet.control.ac_power != 0) {
 | 
			
		||||
    // if AC is off display status always ON so process it only when AC is on
 | 
			
		||||
    bool disp_status = packet.control.display_status == 0;
 | 
			
		||||
    if (disp_status != this->get_display_state()) {
 | 
			
		||||
      // Do something only if display status changed
 | 
			
		||||
      if (this->mode == CLIMATE_MODE_OFF) {
 | 
			
		||||
        // AC just turned on from remote need to turn off display
 | 
			
		||||
        this->force_send_control_ = true;
 | 
			
		||||
      } else if ((((uint8_t) this->health_mode_) & 0b10) == 0) {
 | 
			
		||||
        this->display_status_ = disp_status ? SwitchState::ON : SwitchState::OFF;
 | 
			
		||||
  {
 | 
			
		||||
    // Display status
 | 
			
		||||
    // should be before "Climate mode" because it is changing this->mode
 | 
			
		||||
    if (packet.control.ac_power != 0) {
 | 
			
		||||
      // if AC is off display status always ON so process it only when AC is on
 | 
			
		||||
      bool disp_status = packet.control.display_status == 0;
 | 
			
		||||
      if (disp_status != this->display_status_) {
 | 
			
		||||
        // Do something only if display status changed
 | 
			
		||||
        if (this->mode == CLIMATE_MODE_OFF) {
 | 
			
		||||
          // AC just turned on from remote need to turn off display
 | 
			
		||||
          this->force_send_control_ = true;
 | 
			
		||||
        } else {
 | 
			
		||||
          this->display_status_ = disp_status;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  // Health mode
 | 
			
		||||
  if ((((uint8_t) this->health_mode_) & 0b10) == 0) {
 | 
			
		||||
    bool old_health_mode = this->get_health_mode();
 | 
			
		||||
    this->health_mode_ = packet.control.health_mode == 1 ? SwitchState::ON : SwitchState::OFF;
 | 
			
		||||
    should_publish = should_publish || (old_health_mode != this->get_health_mode());
 | 
			
		||||
  {
 | 
			
		||||
    // Health mode
 | 
			
		||||
    bool old_health_mode = this->health_mode_;
 | 
			
		||||
    this->health_mode_ = packet.control.health_mode == 1;
 | 
			
		||||
    should_publish = should_publish || (old_health_mode != this->health_mode_);
 | 
			
		||||
  }
 | 
			
		||||
  {
 | 
			
		||||
    // Climate mode
 | 
			
		||||
 
 | 
			
		||||
@@ -1,91 +0,0 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
import esphome.final_validate as fv
 | 
			
		||||
from esphome.components import switch
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_BEEPER,
 | 
			
		||||
    CONF_DISPLAY,
 | 
			
		||||
    ENTITY_CATEGORY_CONFIG,
 | 
			
		||||
)
 | 
			
		||||
from ..climate import (
 | 
			
		||||
    CONF_HAIER_ID,
 | 
			
		||||
    CONF_PROTOCOL,
 | 
			
		||||
    HaierClimateBase,
 | 
			
		||||
    haier_ns,
 | 
			
		||||
    PROTOCOL_HON,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@paveldn"]
 | 
			
		||||
BeeperSwitch = haier_ns.class_("BeeperSwitch", switch.Switch)
 | 
			
		||||
HealthModeSwitch = haier_ns.class_("HealthModeSwitch", switch.Switch)
 | 
			
		||||
DisplaySwitch = haier_ns.class_("DisplaySwitch", switch.Switch)
 | 
			
		||||
QuietModeSwitch = haier_ns.class_("QuietModeSwitch", switch.Switch)
 | 
			
		||||
 | 
			
		||||
# Haier switches
 | 
			
		||||
CONF_HEALTH_MODE = "health_mode"
 | 
			
		||||
CONF_QUIET_MODE = "quiet_mode"
 | 
			
		||||
 | 
			
		||||
# Additional icons
 | 
			
		||||
ICON_LEAF = "mdi:leaf"
 | 
			
		||||
ICON_LED_ON = "mdi:led-on"
 | 
			
		||||
ICON_VOLUME_HIGH = "mdi:volume-high"
 | 
			
		||||
ICON_VOLUME_OFF = "mdi:volume-off"
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(CONF_HAIER_ID): cv.use_id(HaierClimateBase),
 | 
			
		||||
        cv.Optional(CONF_DISPLAY): switch.switch_schema(
 | 
			
		||||
            DisplaySwitch,
 | 
			
		||||
            icon=ICON_LED_ON,
 | 
			
		||||
            entity_category=ENTITY_CATEGORY_CONFIG,
 | 
			
		||||
            default_restore_mode="DISABLED",
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_HEALTH_MODE): switch.switch_schema(
 | 
			
		||||
            HealthModeSwitch,
 | 
			
		||||
            icon=ICON_LEAF,
 | 
			
		||||
            default_restore_mode="DISABLED",
 | 
			
		||||
        ),
 | 
			
		||||
        # Beeper switch is only supported for HonClimate
 | 
			
		||||
        cv.Optional(CONF_BEEPER): switch.switch_schema(
 | 
			
		||||
            BeeperSwitch,
 | 
			
		||||
            icon=ICON_VOLUME_HIGH,
 | 
			
		||||
            entity_category=ENTITY_CATEGORY_CONFIG,
 | 
			
		||||
            default_restore_mode="DISABLED",
 | 
			
		||||
        ),
 | 
			
		||||
        # Quiet mode is only supported for HonClimate
 | 
			
		||||
        cv.Optional(CONF_QUIET_MODE): switch.switch_schema(
 | 
			
		||||
            QuietModeSwitch,
 | 
			
		||||
            icon=ICON_VOLUME_OFF,
 | 
			
		||||
            entity_category=ENTITY_CATEGORY_CONFIG,
 | 
			
		||||
            default_restore_mode="DISABLED",
 | 
			
		||||
        ),
 | 
			
		||||
    }
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _final_validate(config):
 | 
			
		||||
    full_config = fv.full_config.get()
 | 
			
		||||
    for switch_type in [CONF_BEEPER, CONF_QUIET_MODE]:
 | 
			
		||||
        # Check switches that are only supported for HonClimate
 | 
			
		||||
        if config.get(switch_type):
 | 
			
		||||
            climate_path = full_config.get_path_for_id(config[CONF_HAIER_ID])[:-1]
 | 
			
		||||
            climate_conf = full_config.get_config_for_path(climate_path)
 | 
			
		||||
            protocol_type = climate_conf.get(CONF_PROTOCOL)
 | 
			
		||||
            if protocol_type.casefold() != PROTOCOL_HON.casefold():
 | 
			
		||||
                raise cv.Invalid(
 | 
			
		||||
                    f"{switch_type} switch is only supported for hon climate"
 | 
			
		||||
                )
 | 
			
		||||
    return config
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
FINAL_VALIDATE_SCHEMA = _final_validate
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    parent = await cg.get_variable(config[CONF_HAIER_ID])
 | 
			
		||||
 | 
			
		||||
    for switch_type in [CONF_DISPLAY, CONF_HEALTH_MODE, CONF_BEEPER, CONF_QUIET_MODE]:
 | 
			
		||||
        if conf := config.get(switch_type):
 | 
			
		||||
            sw_var = await switch.new_switch(conf)
 | 
			
		||||
            await cg.register_parented(sw_var, parent)
 | 
			
		||||
            cg.add(getattr(parent, f"set_{switch_type}_switch")(sw_var))
 | 
			
		||||
@@ -1,14 +0,0 @@
 | 
			
		||||
#include "beeper.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace haier {
 | 
			
		||||
 | 
			
		||||
void BeeperSwitch::write_state(bool state) {
 | 
			
		||||
  if (this->parent_->get_beeper_state() != state) {
 | 
			
		||||
    this->parent_->set_beeper_state(state);
 | 
			
		||||
  }
 | 
			
		||||
  this->publish_state(state);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace haier
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -1,18 +0,0 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/components/switch/switch.h"
 | 
			
		||||
#include "../hon_climate.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace haier {
 | 
			
		||||
 | 
			
		||||
class BeeperSwitch : public switch_::Switch, public Parented<HonClimate> {
 | 
			
		||||
 public:
 | 
			
		||||
  BeeperSwitch() = default;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  void write_state(bool state) override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace haier
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -1,14 +0,0 @@
 | 
			
		||||
#include "display.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace haier {
 | 
			
		||||
 | 
			
		||||
void DisplaySwitch::write_state(bool state) {
 | 
			
		||||
  if (this->parent_->get_display_state() != state) {
 | 
			
		||||
    this->parent_->set_display_state(state);
 | 
			
		||||
  }
 | 
			
		||||
  this->publish_state(state);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace haier
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -1,18 +0,0 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/components/switch/switch.h"
 | 
			
		||||
#include "../haier_base.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace haier {
 | 
			
		||||
 | 
			
		||||
class DisplaySwitch : public switch_::Switch, public Parented<HaierClimateBase> {
 | 
			
		||||
 public:
 | 
			
		||||
  DisplaySwitch() = default;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  void write_state(bool state) override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace haier
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -1,14 +0,0 @@
 | 
			
		||||
#include "health_mode.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace haier {
 | 
			
		||||
 | 
			
		||||
void HealthModeSwitch::write_state(bool state) {
 | 
			
		||||
  if (this->parent_->get_health_mode() != state) {
 | 
			
		||||
    this->parent_->set_health_mode(state);
 | 
			
		||||
  }
 | 
			
		||||
  this->publish_state(state);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace haier
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -1,18 +0,0 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/components/switch/switch.h"
 | 
			
		||||
#include "../haier_base.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace haier {
 | 
			
		||||
 | 
			
		||||
class HealthModeSwitch : public switch_::Switch, public Parented<HaierClimateBase> {
 | 
			
		||||
 public:
 | 
			
		||||
  HealthModeSwitch() = default;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  void write_state(bool state) override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace haier
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -1,14 +0,0 @@
 | 
			
		||||
#include "quiet_mode.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace haier {
 | 
			
		||||
 | 
			
		||||
void QuietModeSwitch::write_state(bool state) {
 | 
			
		||||
  if (this->parent_->get_quiet_mode_state() != state) {
 | 
			
		||||
    this->parent_->set_quiet_mode_state(state);
 | 
			
		||||
  }
 | 
			
		||||
  this->publish_state(state);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace haier
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -1,18 +0,0 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/components/switch/switch.h"
 | 
			
		||||
#include "../hon_climate.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace haier {
 | 
			
		||||
 | 
			
		||||
class QuietModeSwitch : public switch_::Switch, public Parented<HonClimate> {
 | 
			
		||||
 public:
 | 
			
		||||
  QuietModeSwitch() = default;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  void write_state(bool state) override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace haier
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
#include <cstdio>
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include "hmac_md5.h"
 | 
			
		||||
#ifdef USE_MD5
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
@@ -55,4 +54,3 @@ bool HmacMD5::equals_hex(const char *expected) { return this->ohash_.equals_hex(
 | 
			
		||||
 | 
			
		||||
}  // namespace hmac_md5
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#ifdef USE_MD5
 | 
			
		||||
#include "esphome/components/md5/md5.h"
 | 
			
		||||
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
@@ -46,4 +46,3 @@ class HmacMD5 {
 | 
			
		||||
 | 
			
		||||
}  // namespace hmac_md5
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user