diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 886b03f..b4691af 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -77,11 +77,15 @@ jobs: env: # Cache the ESP-IDF install rather than re-downloading it every run. ESPHOME_ESP_IDF_PREFIX: ~/.esphome-idf + # 2026.10 builds with a precompiled header by default. clang-tidy replays the + # build's flags, but clang can't read GCC's PCH and rejects the build's, so build + # without one. + ESPHOME_PCH_ENABLE: "0" strategy: fail-fast: false matrix: include: - - device: pool # esp32s3 / esp-idf: pool_controller, pzem6l24 + - device: pool # esp32s3 / esp-idf: pool_controller platform: esp32 - device: water-heater # esp32 / esp-idf: high_temp_water_heater platform: esp32 diff --git a/README.md b/README.md index 0eba1f8..688af1b 100644 --- a/README.md +++ b/README.md @@ -34,9 +34,6 @@ Schedule-based control for a pool's primary circulation pump, an auxiliary clean ### Power Cycle Pool Light A light component for LED pool lights that advance to the next color each time power is briefly cut, exposing each color as a selectable "effect" in Home Assistant. Built for my [TREO LED Pool Lights](https://www.srsmith.com/en-us/products/pool-lighting/treo-led-pool-light/), but the colors, reset sequence, and timings are configurable so it should work with Moov, Pentair IntelliBrite/GloBrite/MicroBrite, J&J ColorSplash, Jandy WaterColors, and similar lights. Details: [components/power_cycle_pool_light/README.md](./components/power_cycle_pool_light/README.md). -### PZEM-6L24 Three-Phase Energy Monitor -A native ESPHome component for the PZEM-6L24 three-phase AC energy meter (voltage/current/power/energy per phase over Modbus RTU). Used by my [Pool](./docs/pool/README.md) controller to monitor the pump and cleaner. Details: [components/pzem6l24/README.md](./components/pzem6l24/README.md). - ### Bed Sensor Detects per-side bed occupancy using resistive pressure sensors multiplexed onto a single ADC input. Used by [Master Bed](./docs/master-bed/README.md). Details: [components/bed_sensor/README.md](./components/bed_sensor/README.md). @@ -64,7 +61,7 @@ A [M5Stack ATOMIC RS485 Base](https://shop.m5stack.com/products/atomic-rs485-bas A [NodeMCU](https://www.amazon.com/gp/product/B010N1SPRK) under the mattress that detects per-side occupancy using a pair of Force Sensitive Resistors and the custom [bed_sensor](./components/bed_sensor/README.md) component. Full write-up: [docs/master-bed](./docs/master-bed/README.md). ### [Pool](./devices/pool.yaml) -A [Waveshare ESP32-S3 7" Touch LCD B](https://www.waveshare.com/product/esp32-s3-lcd-7b.htm) running the custom [Pool Controller](./components/pool_controller/README.md) and [PZEM-6L24](./components/pzem6l24/README.md) components to manage pump scheduling, water chemistry, and (eventually) heat/fill/drain. Full write-up: [docs/pool](./docs/pool/README.md). +A [Waveshare ESP32-S3 7" Touch LCD B](https://www.waveshare.com/product/esp32-s3-lcd-7b.htm) running the custom [Pool Controller](./components/pool_controller/README.md) component to manage pump scheduling, water chemistry, and (eventually) heat/fill/drain. Full write-up: [docs/pool](./docs/pool/README.md). ### [Water Heater](./devices/water-heater.yaml) A [M5Stack ATOMIC RS485 Base](https://shop.m5stack.com/products/atomic-rs485-base) with an ESP32 connected to a Rheem hybrid water heater managed through the [ESPHome Econet](https://github.com/esphome-econet/esphome-econet) component. The custom [High Temp Water Heater](./components/high_temp_water_heater/README.md) component sits in front of it so I can set any target temperature I want (e.g. 140°F during peak electric rate hours) while the physical water heater setting itself stays fixed at 150°F — the physical control panel only lets you adjust it to values above 140°F. diff --git a/components/pzem6l24/README.md b/components/pzem6l24/README.md deleted file mode 100644 index 24c599c..0000000 --- a/components/pzem6l24/README.md +++ /dev/null @@ -1,156 +0,0 @@ -# PZEM-6L24 Three-Phase Energy Monitor - -## Overview -The PZEM-6L24 is a three-phase AC energy monitor from Peacefair. It is similar to the single-phase PZEM-004T V3 (which ESPHome supports natively via the `pzemac` component) but measures all three phases simultaneously. It communicates over RS-485 using Modbus RTU and reports voltage, current, active power, reactive power, apparent power, power factor, and energy for each phase individually as well as combined totals. This is a native ESPHome component with no external library dependencies. - -## Hardware Setup -The PZEM-6L24 exposes two physical interfaces and you can use either one: - -### Option 1 — Direct UART (TTL header on the PCB) -The board has a 4-pin header labeled **GND / TX / RX / +5V**. This is the simplest connection when the ESP is close to the meter. - -``` -PZEM-6L24 GND ──── ESP GND -PZEM-6L24 TX ──── ESP RX pin -PZEM-6L24 RX ──── ESP TX pin -``` - -> **Note:** The TX and RX signal lines operate at **3.3 V** logic, so they connect directly to any ESP32 or ESP8266 GPIO without a level shifter. The PZEM-6L24 is self-powered from its AC inputs, so the +5V pin on the UART header is not needed. - -### Option 2 — RS-485 (screw terminals on the meter) -Use a MAX485 or MAX3485 module when the ESP is further away from the meter, when the meter is on a shared RS-485 bus with multiple devices, or when you want proper differential signalling for noise immunity. - -``` -PZEM-6L24 A+ ──── MAX485 A -PZEM-6L24 B- ──── MAX485 B -MAX485 VCC ──── 5 V supply -MAX485 GND ──── GND -MAX485 DI ──── ESP TX pin -MAX485 RO ──── ESP RX pin -MAX485 DE ──┐ -MAX485 RE ──┘── GND (tie low for permanent receive mode) -``` - -If you need software control of the DE/RE direction pin instead of tying it low, set `flow_control_pin` on the `modbus` component. - ---- - -The default baud rate is **9600 8N1** for both connection methods. When using RS-485 with multiple devices on the same bus, each device must have a unique Modbus address (1–247). Use the DIP switches on the meter for hardware addressing or configure a software address through the device's settings before integrating it into ESPHome. - -## Example YAML Configuration -```yaml -external_components: - - source: - type: local - path: components - components: [pzem6l24] - -uart: - id: modbus_uart - tx_pin: GPIO17 - rx_pin: GPIO16 - baud_rate: 9600 - -modbus: - uart_id: modbus_uart - -sensor: - - platform: pzem6l24 - id: pzem - address: 0x01 - update_interval: 10s - - # Per-phase voltage - voltage_a: - name: "Voltage Phase A" - voltage_b: - name: "Voltage Phase B" - voltage_c: - name: "Voltage Phase C" - - # Per-phase current - current_a: - name: "Current Phase A" - current_b: - name: "Current Phase B" - current_c: - name: "Current Phase C" - - # Per-phase active power - active_power_a: - name: "Active Power Phase A" - active_power_b: - name: "Active Power Phase B" - active_power_c: - name: "Active Power Phase C" - - # Per-phase power factor - power_factor_a: - name: "Power Factor Phase A" - power_factor_b: - name: "Power Factor Phase B" - power_factor_c: - name: "Power Factor Phase C" - - # Per-phase energy - active_energy_a: - name: "Energy Phase A" - active_energy_b: - name: "Energy Phase B" - active_energy_c: - name: "Energy Phase C" - - # Combined totals - frequency: - name: "Grid Frequency" - total_active_power: - name: "Total Active Power" - total_active_energy: - name: "Total Active Energy" - -button: - - platform: template - name: "Reset Energy Counters" - on_press: - - pzem6l24.reset_energy: - id: pzem - phase: all -``` - -## Configuration Variables -* **id** (Optional, string): Manually specify the component ID used for code generation and for referencing the device in actions. -* **address** (Optional, int, default: `0x01`): Modbus slave address of the PZEM-6L24 device (1–247). Each device on the same RS-485 bus must have a unique address. -* **update_interval** (Optional, Time, default: `60s`): How often the component polls the device for new measurements. - -### Per-Phase Sensors (phases A, B, and C) -Each per-phase key follows the pattern `_a`, `_b`, `_c`. All are optional. - -* **voltage_a / voltage_b / voltage_c** (Optional): Line voltage for the phase in volts (V), resolution 0.1 V. -* **current_a / current_b / current_c** (Optional): Line current for the phase in amperes (A), resolution 0.01 A. -* **active_power_a / active_power_b / active_power_c** (Optional): Active (real) power for the phase in watts (W), resolution 0.1 W. -* **reactive_power_a / reactive_power_b / reactive_power_c** (Optional): Reactive power for the phase in VAR, resolution 0.1 VAR. -* **apparent_power_a / apparent_power_b / apparent_power_c** (Optional): Apparent power for the phase in VA, resolution 0.1 VA. -* **power_factor_a / power_factor_b / power_factor_c** (Optional): Power factor for the phase (0.00–1.00), resolution 0.01. -* **active_energy_a / active_energy_b / active_energy_c** (Optional): Accumulated active energy for the phase in kWh, resolution 0.1 kWh. -* **reactive_energy_a / reactive_energy_b / reactive_energy_c** (Optional): Accumulated reactive energy for the phase in kVARh, resolution 0.1 kVARh. -* **apparent_energy_a / apparent_energy_b / apparent_energy_c** (Optional): Accumulated apparent energy for the phase in kVAh, resolution 0.1 kVAh. - -### Combined Sensors -* **frequency** (Optional): Grid frequency in hertz (Hz), resolution 0.01 Hz. Derived from phase A (all phases share the same grid frequency). -* **total_active_power** (Optional): Sum of active power across all three phases in watts (W), resolution 0.1 W. -* **total_reactive_power** (Optional): Sum of reactive power across all three phases in VAR, resolution 0.1 VAR. -* **total_apparent_power** (Optional): Sum of apparent power across all three phases in VA, resolution 0.1 VA. -* **total_power_factor** (Optional): Combined power factor across all three phases (0.00–1.00), resolution 0.01. -* **total_active_energy** (Optional): Total accumulated active energy across all three phases in kWh, resolution 0.1 kWh. -* **total_reactive_energy** (Optional): Total accumulated reactive energy across all three phases in kVARh, resolution 0.1 kVARh. -* **total_apparent_energy** (Optional): Total accumulated apparent energy across all three phases in kVAh, resolution 0.1 kVAh. - -### Actions -* **pzem6l24.reset_energy**: Resets the energy counters on the device. - * **id** (Required): The ID of the PZEM-6L24 component to reset. - * **phase** (Optional, default: `all`): Which energy counter(s) to reset. Options: - * `all` — Reset all counters (phases A, B, C, and the combined total) - * `a` — Reset phase A only - * `b` — Reset phase B only - * `c` — Reset phase C only - * `combined` — Reset the combined total only diff --git a/components/pzem6l24/__init__.py b/components/pzem6l24/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/components/pzem6l24/pzem6l24.cpp b/components/pzem6l24/pzem6l24.cpp deleted file mode 100644 index 05e8087..0000000 --- a/components/pzem6l24/pzem6l24.cpp +++ /dev/null @@ -1,323 +0,0 @@ -#include "pzem6l24.h" -#include "esphome/core/log.h" - -namespace esphome::pzem6l24 { - -static const char *const TAG = "pzem6l24"; - -// Modbus function code to read input registers -static const uint8_t PZEM_CMD_READ_IN_REGISTERS = 0x04; -// Reset energy function code (PZEM-6L24 specific, non-standard Modbus) -static const uint8_t PZEM_CMD_RESET_ENERGY = 0x42; -// Number of input registers to read (0x0000 – 0x003F inclusive) -static const uint8_t PZEM_REGISTER_COUNT = 64; // 64 × 16-bit registers = 128 bytes - -// ----------------------------------------------------------------------- -// Register map (input registers, starting address 0x0000): -// -// The PZEM-6L24 returns all register bytes in little-endian order, -// i.e. the low byte of each 16-bit register is transmitted first. -// 32-bit quantities occupy two consecutive registers with the low -// word at the lower address. -// -// Byte offset = register_address × 2 -// -// 0x0000 (byte 0) – Voltage A (uint16, ×0.1 V) -// 0x0001 (byte 2) – Voltage B (uint16, ×0.1 V) -// 0x0002 (byte 4) – Voltage C (uint16, ×0.1 V) -// 0x0003 (byte 6) – Current A (uint16, ×0.01 A) -// 0x0004 (byte 8) – Current B (uint16, ×0.01 A) -// 0x0005 (byte 10) – Current C (uint16, ×0.01 A) -// 0x0006 (byte 12) – Frequency A (uint16, ×0.01 Hz) -// 0x0007 (byte 14) – Frequency B (uint16, ×0.01 Hz) -// 0x0008 (byte 16) – Frequency C (uint16, ×0.01 Hz) -// 0x0009 (byte 18) – Voltage angle B (uint16, ×0.01 °) -// 0x000A (byte 20) – Voltage angle C (uint16, ×0.01 °) -// 0x000B (byte 22) – Current angle A (uint16, ×0.01 °) -// 0x000C (byte 24) – Current angle B (uint16, ×0.01 °) -// 0x000D (byte 26) – Current angle C (uint16, ×0.01 °) -// 0x000E (byte 28) – Active power A (int32 lo-word, ×0.1 W) -// 0x000F (byte 30) – Active power A (int32 hi-word) -// 0x0010 (byte 32) – Active power B (int32 lo-word, ×0.1 W) -// 0x0011 (byte 34) – Active power B (int32 hi-word) -// 0x0012 (byte 36) – Active power C (int32 lo-word, ×0.1 W) -// 0x0013 (byte 38) – Active power C (int32 hi-word) -// 0x0014 (byte 40) – Reactive power A (int32 lo-word, ×0.1 var) -// 0x0015 (byte 42) – Reactive power A (int32 hi-word) -// 0x0016 (byte 44) – Reactive power B (int32 lo-word, ×0.1 var) -// 0x0017 (byte 46) – Reactive power B (int32 hi-word) -// 0x0018 (byte 48) – Reactive power C (int32 lo-word, ×0.1 var) -// 0x0019 (byte 50) – Reactive power C (int32 hi-word) -// 0x001A (byte 52) – Apparent power A (int32 lo-word, ×0.1 VA) -// 0x001B (byte 54) – Apparent power A (int32 hi-word) -// 0x001C (byte 56) – Apparent power B (int32 lo-word, ×0.1 VA) -// 0x001D (byte 58) – Apparent power B (int32 hi-word) -// 0x001E (byte 60) – Apparent power C (int32 lo-word, ×0.1 VA) -// 0x001F (byte 62) – Apparent power C (int32 hi-word) -// 0x0020 (byte 64) – Total active pwr (int32 lo-word, ×0.1 W) -// 0x0021 (byte 66) – Total active pwr (int32 hi-word) -// 0x0022 (byte 68) – Total reactive pwr (int32 lo-word, ×0.1 var) -// 0x0023 (byte 70) – Total reactive pwr (int32 hi-word) -// 0x0024 (byte 72) – Total apparent pwr (int32 lo-word, ×0.1 VA) -// 0x0025 (byte 74) – Total apparent pwr (int32 hi-word) -// 0x0026 (byte 76) – Power factor A/B: hi-byte = A (×0.01), lo-byte = B (×0.01) -// 0x0027 (byte 78) – Power factor C/tot:hi-byte = C (×0.01), lo-byte = total (×0.01) -// 0x0028 (byte 80) – Active energy A (uint32 lo-word, ×0.1 kWh) -// 0x0029 (byte 82) – Active energy A (uint32 hi-word) -// 0x002A (byte 84) – Active energy B (uint32 lo-word, ×0.1 kWh) -// 0x002B (byte 86) – Active energy B (uint32 hi-word) -// 0x002C (byte 88) – Active energy C (uint32 lo-word, ×0.1 kWh) -// 0x002D (byte 90) – Active energy C (uint32 hi-word) -// 0x002E (byte 92) – Reactive energy A (uint32 lo-word, ×0.1 kvarh) -// 0x002F (byte 94) – Reactive energy A (uint32 hi-word) -// 0x0030 (byte 96) – Reactive energy B (uint32 lo-word, ×0.1 kvarh) -// 0x0031 (byte 98) – Reactive energy B (uint32 hi-word) -// 0x0032 (byte 100)– Reactive energy C (uint32 lo-word, ×0.1 kvarh) -// 0x0033 (byte 102)– Reactive energy C (uint32 hi-word) -// 0x0034 (byte 104)– Apparent energy A (uint32 lo-word, ×0.1 kVAh) -// 0x0035 (byte 106)– Apparent energy A (uint32 hi-word) -// 0x0036 (byte 108)– Apparent energy B (uint32 lo-word, ×0.1 kVAh) -// 0x0037 (byte 110)– Apparent energy B (uint32 hi-word) -// 0x0038 (byte 112)– Apparent energy C (uint32 lo-word, ×0.1 kVAh) -// 0x0039 (byte 114)– Apparent energy C (uint32 hi-word) -// 0x003A (byte 116)– Total active nrg (uint32 lo-word, ×0.1 kWh) -// 0x003B (byte 118)– Total active nrg (uint32 hi-word) -// 0x003C (byte 120)– Total reactive nrg (uint32 lo-word, ×0.1 kvarh) -// 0x003D (byte 122)– Total reactive nrg (uint32 hi-word) -// 0x003E (byte 124)– Total apparent nrg (uint32 lo-word, ×0.1 kVAh) -// 0x003F (byte 126)– Total apparent nrg (uint32 hi-word) -// ----------------------------------------------------------------------- - -void PZEM6L24::on_modbus_data(const std::vector &data) { - if (data.size() < 128) { - ESP_LOGW(TAG, "Invalid data size for PZEM-6L24: expected 128 bytes, got %zu", data.size()); - return; - } - - // Helper: decode a little-endian 16-bit value at byte offset i. - auto get_u16 = [&](size_t i) -> uint16_t { return (uint16_t(data[i + 1]) << 8) | uint16_t(data[i]); }; - - // Helper: decode a little-endian unsigned 32-bit value at byte offset i - // (low word at i, high word at i+2). - auto get_u32 = [&](size_t i) -> uint32_t { - return (uint32_t(data[i + 3]) << 24) | (uint32_t(data[i + 2]) << 16) | (uint32_t(data[i + 1]) << 8) | - uint32_t(data[i]); - }; - - // Helper: decode a little-endian signed 32-bit value at byte offset i. - auto get_i32 = [&](size_t i) -> int32_t { return static_cast(get_u32(i)); }; - - // --- Voltages (×0.1 V) --- - float voltage_a = get_u16(0) * 0.1f; - float voltage_b = get_u16(2) * 0.1f; - float voltage_c = get_u16(4) * 0.1f; - - // --- Currents (×0.01 A) --- - float current_a = get_u16(6) * 0.01f; - float current_b = get_u16(8) * 0.01f; - float current_c = get_u16(10) * 0.01f; - - // --- Frequency (×0.01 Hz) — all three phases share the same grid frequency; - // we report phase A as the representative value. --- - float frequency = get_u16(12) * 0.01f; - - // --- Active powers (×0.1 W, signed) --- - float active_power_a = get_i32(28) * 0.1f; - float active_power_b = get_i32(32) * 0.1f; - float active_power_c = get_i32(36) * 0.1f; - - // --- Reactive powers (×0.1 var, signed) --- - float reactive_power_a = get_i32(40) * 0.1f; - float reactive_power_b = get_i32(44) * 0.1f; - float reactive_power_c = get_i32(48) * 0.1f; - - // --- Apparent powers (×0.1 VA, signed) --- - float apparent_power_a = get_i32(52) * 0.1f; - float apparent_power_b = get_i32(56) * 0.1f; - float apparent_power_c = get_i32(60) * 0.1f; - - // --- Combined powers (×0.1 W/var/VA) --- - float total_active_power = get_i32(64) * 0.1f; - float total_reactive_power = get_i32(68) * 0.1f; - float total_apparent_power = get_i32(72) * 0.1f; - - // --- Power factors (×0.01, packed as two uint8 values per register): - // Register 0x0026: hi-byte = phase A, lo-byte = phase B - // Register 0x0027: hi-byte = phase C, lo-byte = combined --- - float power_factor_a = data[77] * 0.01f; - float power_factor_b = data[76] * 0.01f; - float power_factor_c = data[79] * 0.01f; - float total_power_factor = data[78] * 0.01f; - - // --- Active energies (×0.1 kWh, unsigned) --- - float active_energy_a = get_u32(80) * 0.1f; - float active_energy_b = get_u32(84) * 0.1f; - float active_energy_c = get_u32(88) * 0.1f; - - // --- Reactive energies (×0.1 kvarh, unsigned) --- - float reactive_energy_a = get_u32(92) * 0.1f; - float reactive_energy_b = get_u32(96) * 0.1f; - float reactive_energy_c = get_u32(100) * 0.1f; - - // --- Apparent energies (×0.1 kVAh, unsigned) --- - float apparent_energy_a = get_u32(104) * 0.1f; - float apparent_energy_b = get_u32(108) * 0.1f; - float apparent_energy_c = get_u32(112) * 0.1f; - - // --- Combined energies --- - float total_active_energy = get_u32(116) * 0.1f; - float total_reactive_energy = get_u32(120) * 0.1f; - float total_apparent_energy = get_u32(124) * 0.1f; - - ESP_LOGD(TAG, "PZEM-6L24 A: V=%.1f V, I=%.2f A, P=%.1f W, Q=%.1f var, S=%.1f VA, PF=%.2f, E=%.1f kWh", voltage_a, - current_a, active_power_a, reactive_power_a, apparent_power_a, power_factor_a, active_energy_a); - ESP_LOGD(TAG, "PZEM-6L24 B: V=%.1f V, I=%.2f A, P=%.1f W, Q=%.1f var, S=%.1f VA, PF=%.2f, E=%.1f kWh", voltage_b, - current_b, active_power_b, reactive_power_b, apparent_power_b, power_factor_b, active_energy_b); - ESP_LOGD(TAG, "PZEM-6L24 C: V=%.1f V, I=%.2f A, P=%.1f W, Q=%.1f var, S=%.1f VA, PF=%.2f, E=%.1f kWh", voltage_c, - current_c, active_power_c, reactive_power_c, apparent_power_c, power_factor_c, active_energy_c); - ESP_LOGD(TAG, "PZEM-6L24 Total: P=%.1f W, Q=%.1f var, S=%.1f VA, PF=%.2f, E=%.1f kWh, F=%.2f Hz", total_active_power, - total_reactive_power, total_apparent_power, total_power_factor, total_active_energy, frequency); - - // Publish per-phase voltages - if (this->voltage_a_ != nullptr) - this->voltage_a_->publish_state(voltage_a); - if (this->voltage_b_ != nullptr) - this->voltage_b_->publish_state(voltage_b); - if (this->voltage_c_ != nullptr) - this->voltage_c_->publish_state(voltage_c); - - // Publish per-phase currents - if (this->current_a_ != nullptr) - this->current_a_->publish_state(current_a); - if (this->current_b_ != nullptr) - this->current_b_->publish_state(current_b); - if (this->current_c_ != nullptr) - this->current_c_->publish_state(current_c); - - // Publish per-phase active powers - if (this->active_power_a_ != nullptr) - this->active_power_a_->publish_state(active_power_a); - if (this->active_power_b_ != nullptr) - this->active_power_b_->publish_state(active_power_b); - if (this->active_power_c_ != nullptr) - this->active_power_c_->publish_state(active_power_c); - - // Publish per-phase reactive powers - if (this->reactive_power_a_ != nullptr) - this->reactive_power_a_->publish_state(reactive_power_a); - if (this->reactive_power_b_ != nullptr) - this->reactive_power_b_->publish_state(reactive_power_b); - if (this->reactive_power_c_ != nullptr) - this->reactive_power_c_->publish_state(reactive_power_c); - - // Publish per-phase apparent powers - if (this->apparent_power_a_ != nullptr) - this->apparent_power_a_->publish_state(apparent_power_a); - if (this->apparent_power_b_ != nullptr) - this->apparent_power_b_->publish_state(apparent_power_b); - if (this->apparent_power_c_ != nullptr) - this->apparent_power_c_->publish_state(apparent_power_c); - - // Publish per-phase power factors - if (this->power_factor_a_ != nullptr) - this->power_factor_a_->publish_state(power_factor_a); - if (this->power_factor_b_ != nullptr) - this->power_factor_b_->publish_state(power_factor_b); - if (this->power_factor_c_ != nullptr) - this->power_factor_c_->publish_state(power_factor_c); - - // Publish per-phase active energies - if (this->active_energy_a_ != nullptr) - this->active_energy_a_->publish_state(active_energy_a); - if (this->active_energy_b_ != nullptr) - this->active_energy_b_->publish_state(active_energy_b); - if (this->active_energy_c_ != nullptr) - this->active_energy_c_->publish_state(active_energy_c); - - // Publish per-phase reactive energies - if (this->reactive_energy_a_ != nullptr) - this->reactive_energy_a_->publish_state(reactive_energy_a); - if (this->reactive_energy_b_ != nullptr) - this->reactive_energy_b_->publish_state(reactive_energy_b); - if (this->reactive_energy_c_ != nullptr) - this->reactive_energy_c_->publish_state(reactive_energy_c); - - // Publish per-phase apparent energies - if (this->apparent_energy_a_ != nullptr) - this->apparent_energy_a_->publish_state(apparent_energy_a); - if (this->apparent_energy_b_ != nullptr) - this->apparent_energy_b_->publish_state(apparent_energy_b); - if (this->apparent_energy_c_ != nullptr) - this->apparent_energy_c_->publish_state(apparent_energy_c); - - // Publish combined sensors - if (this->frequency_ != nullptr) - this->frequency_->publish_state(frequency); - if (this->total_active_power_ != nullptr) - this->total_active_power_->publish_state(total_active_power); - if (this->total_reactive_power_ != nullptr) - this->total_reactive_power_->publish_state(total_reactive_power); - if (this->total_apparent_power_ != nullptr) - this->total_apparent_power_->publish_state(total_apparent_power); - if (this->total_power_factor_ != nullptr) - this->total_power_factor_->publish_state(total_power_factor); - if (this->total_active_energy_ != nullptr) - this->total_active_energy_->publish_state(total_active_energy); - if (this->total_reactive_energy_ != nullptr) - this->total_reactive_energy_->publish_state(total_reactive_energy); - if (this->total_apparent_energy_ != nullptr) - this->total_apparent_energy_->publish_state(total_apparent_energy); -} - -void PZEM6L24::update() { this->send(PZEM_CMD_READ_IN_REGISTERS, 0x0000, PZEM_REGISTER_COUNT); } - -void PZEM6L24::dump_config() { - ESP_LOGCONFIG(TAG, - "PZEM-6L24:\n" - " Address: 0x%02X", - this->address_); - LOG_SENSOR(" ", "Voltage A", this->voltage_a_); - LOG_SENSOR(" ", "Voltage B", this->voltage_b_); - LOG_SENSOR(" ", "Voltage C", this->voltage_c_); - LOG_SENSOR(" ", "Current A", this->current_a_); - LOG_SENSOR(" ", "Current B", this->current_b_); - LOG_SENSOR(" ", "Current C", this->current_c_); - LOG_SENSOR(" ", "Active Power A", this->active_power_a_); - LOG_SENSOR(" ", "Active Power B", this->active_power_b_); - LOG_SENSOR(" ", "Active Power C", this->active_power_c_); - LOG_SENSOR(" ", "Reactive Power A", this->reactive_power_a_); - LOG_SENSOR(" ", "Reactive Power B", this->reactive_power_b_); - LOG_SENSOR(" ", "Reactive Power C", this->reactive_power_c_); - LOG_SENSOR(" ", "Apparent Power A", this->apparent_power_a_); - LOG_SENSOR(" ", "Apparent Power B", this->apparent_power_b_); - LOG_SENSOR(" ", "Apparent Power C", this->apparent_power_c_); - LOG_SENSOR(" ", "Power Factor A", this->power_factor_a_); - LOG_SENSOR(" ", "Power Factor B", this->power_factor_b_); - LOG_SENSOR(" ", "Power Factor C", this->power_factor_c_); - LOG_SENSOR(" ", "Active Energy A", this->active_energy_a_); - LOG_SENSOR(" ", "Active Energy B", this->active_energy_b_); - LOG_SENSOR(" ", "Active Energy C", this->active_energy_c_); - LOG_SENSOR(" ", "Reactive Energy A", this->reactive_energy_a_); - LOG_SENSOR(" ", "Reactive Energy B", this->reactive_energy_b_); - LOG_SENSOR(" ", "Reactive Energy C", this->reactive_energy_c_); - LOG_SENSOR(" ", "Apparent Energy A", this->apparent_energy_a_); - LOG_SENSOR(" ", "Apparent Energy B", this->apparent_energy_b_); - LOG_SENSOR(" ", "Apparent Energy C", this->apparent_energy_c_); - LOG_SENSOR(" ", "Frequency", this->frequency_); - LOG_SENSOR(" ", "Total Active Power", this->total_active_power_); - LOG_SENSOR(" ", "Total Reactive Power", this->total_reactive_power_); - LOG_SENSOR(" ", "Total Apparent Power", this->total_apparent_power_); - LOG_SENSOR(" ", "Total Power Factor", this->total_power_factor_); - LOG_SENSOR(" ", "Total Active Energy", this->total_active_energy_); - LOG_SENSOR(" ", "Total Reactive Energy", this->total_reactive_energy_); - LOG_SENSOR(" ", "Total Apparent Energy", this->total_apparent_energy_); -} - -void PZEM6L24::reset_energy_(ResetPhase phase_option) { - // The PZEM-6L24 reset command uses function code 0x42 with two extra - // bytes: a reserved 0x00 byte and the phase selection byte. - // The ESPHome modbus send_raw() appends the CRC automatically. - this->send_raw({this->address_, PZEM_CMD_RESET_ENERGY, 0x00, static_cast(phase_option)}); -} - -} // namespace esphome::pzem6l24 diff --git a/components/pzem6l24/pzem6l24.h b/components/pzem6l24/pzem6l24.h deleted file mode 100644 index 4eb83a4..0000000 --- a/components/pzem6l24/pzem6l24.h +++ /dev/null @@ -1,168 +0,0 @@ -#pragma once - -#include "esphome/core/automation.h" -#include "esphome/core/component.h" -#include "esphome/components/sensor/sensor.h" -#include "esphome/components/modbus/modbus.h" - -#include - -namespace esphome::pzem6l24 { - -template class ResetEnergyAction; - -// Options for the reset_energy action -enum ResetPhase : uint8_t { - RESET_PHASE_A = 0x00, // Reset phase A energy only - RESET_PHASE_B = 0x01, // Reset phase B energy only - RESET_PHASE_C = 0x02, // Reset phase C energy only - RESET_PHASE_COMBINED = 0x03, // Reset combined (sum) energy only - RESET_PHASE_ALL = 0x0F, // Reset all energy counters -}; - -// Temporary: 2026.8.0 moved the legacy on_modbus_data()/on_modbus_error() hooks off -// ModbusClientDevice onto the deprecated ModbusDevice shim (removal 2027.2.0). This component is -// superseded by the rewrite in esphome/esphome#17849; the base class is only pinned here so it still -// builds while that PR is in review. -class PZEM6L24 final : public PollingComponent, public modbus::ModbusDevice { - public: - // Per-phase voltage setters - void set_voltage_sensor_a(sensor::Sensor *voltage_a) { this->voltage_a_ = voltage_a; } - void set_voltage_sensor_b(sensor::Sensor *voltage_b) { this->voltage_b_ = voltage_b; } - void set_voltage_sensor_c(sensor::Sensor *voltage_c) { this->voltage_c_ = voltage_c; } - - // Per-phase current setters - void set_current_sensor_a(sensor::Sensor *current_a) { this->current_a_ = current_a; } - void set_current_sensor_b(sensor::Sensor *current_b) { this->current_b_ = current_b; } - void set_current_sensor_c(sensor::Sensor *current_c) { this->current_c_ = current_c; } - - // Per-phase active power setters - void set_active_power_sensor_a(sensor::Sensor *active_power_a) { this->active_power_a_ = active_power_a; } - void set_active_power_sensor_b(sensor::Sensor *active_power_b) { this->active_power_b_ = active_power_b; } - void set_active_power_sensor_c(sensor::Sensor *active_power_c) { this->active_power_c_ = active_power_c; } - - // Per-phase reactive power setters - void set_reactive_power_sensor_a(sensor::Sensor *reactive_power_a) { this->reactive_power_a_ = reactive_power_a; } - void set_reactive_power_sensor_b(sensor::Sensor *reactive_power_b) { this->reactive_power_b_ = reactive_power_b; } - void set_reactive_power_sensor_c(sensor::Sensor *reactive_power_c) { this->reactive_power_c_ = reactive_power_c; } - - // Per-phase apparent power setters - void set_apparent_power_sensor_a(sensor::Sensor *apparent_power_a) { this->apparent_power_a_ = apparent_power_a; } - void set_apparent_power_sensor_b(sensor::Sensor *apparent_power_b) { this->apparent_power_b_ = apparent_power_b; } - void set_apparent_power_sensor_c(sensor::Sensor *apparent_power_c) { this->apparent_power_c_ = apparent_power_c; } - - // Per-phase power factor setters - void set_power_factor_sensor_a(sensor::Sensor *power_factor_a) { this->power_factor_a_ = power_factor_a; } - void set_power_factor_sensor_b(sensor::Sensor *power_factor_b) { this->power_factor_b_ = power_factor_b; } - void set_power_factor_sensor_c(sensor::Sensor *power_factor_c) { this->power_factor_c_ = power_factor_c; } - - // Per-phase active energy setters - void set_active_energy_sensor_a(sensor::Sensor *active_energy_a) { this->active_energy_a_ = active_energy_a; } - void set_active_energy_sensor_b(sensor::Sensor *active_energy_b) { this->active_energy_b_ = active_energy_b; } - void set_active_energy_sensor_c(sensor::Sensor *active_energy_c) { this->active_energy_c_ = active_energy_c; } - - // Per-phase reactive energy setters - void set_reactive_energy_sensor_a(sensor::Sensor *reactive_energy_a) { this->reactive_energy_a_ = reactive_energy_a; } - void set_reactive_energy_sensor_b(sensor::Sensor *reactive_energy_b) { this->reactive_energy_b_ = reactive_energy_b; } - void set_reactive_energy_sensor_c(sensor::Sensor *reactive_energy_c) { this->reactive_energy_c_ = reactive_energy_c; } - - // Per-phase apparent energy setters - void set_apparent_energy_sensor_a(sensor::Sensor *apparent_energy_a) { this->apparent_energy_a_ = apparent_energy_a; } - void set_apparent_energy_sensor_b(sensor::Sensor *apparent_energy_b) { this->apparent_energy_b_ = apparent_energy_b; } - void set_apparent_energy_sensor_c(sensor::Sensor *apparent_energy_c) { this->apparent_energy_c_ = apparent_energy_c; } - - // Combined sensor setters - void set_frequency_sensor(sensor::Sensor *frequency) { this->frequency_ = frequency; } - void set_total_active_power_sensor(sensor::Sensor *total_active_power) { - this->total_active_power_ = total_active_power; - } - void set_total_reactive_power_sensor(sensor::Sensor *total_reactive_power) { - this->total_reactive_power_ = total_reactive_power; - } - void set_total_apparent_power_sensor(sensor::Sensor *total_apparent_power) { - this->total_apparent_power_ = total_apparent_power; - } - void set_total_power_factor_sensor(sensor::Sensor *total_power_factor) { - this->total_power_factor_ = total_power_factor; - } - void set_total_active_energy_sensor(sensor::Sensor *total_active_energy) { - this->total_active_energy_ = total_active_energy; - } - void set_total_reactive_energy_sensor(sensor::Sensor *total_reactive_energy) { - this->total_reactive_energy_ = total_reactive_energy; - } - void set_total_apparent_energy_sensor(sensor::Sensor *total_apparent_energy) { - this->total_apparent_energy_ = total_apparent_energy; - } - - void update() override; - void on_modbus_data(const std::vector &data) override; - void dump_config() override; - - protected: - template friend class ResetEnergyAction; - - // Per-phase sensors - sensor::Sensor *voltage_a_{nullptr}; - sensor::Sensor *voltage_b_{nullptr}; - sensor::Sensor *voltage_c_{nullptr}; - - sensor::Sensor *current_a_{nullptr}; - sensor::Sensor *current_b_{nullptr}; - sensor::Sensor *current_c_{nullptr}; - - sensor::Sensor *active_power_a_{nullptr}; - sensor::Sensor *active_power_b_{nullptr}; - sensor::Sensor *active_power_c_{nullptr}; - - sensor::Sensor *reactive_power_a_{nullptr}; - sensor::Sensor *reactive_power_b_{nullptr}; - sensor::Sensor *reactive_power_c_{nullptr}; - - sensor::Sensor *apparent_power_a_{nullptr}; - sensor::Sensor *apparent_power_b_{nullptr}; - sensor::Sensor *apparent_power_c_{nullptr}; - - sensor::Sensor *power_factor_a_{nullptr}; - sensor::Sensor *power_factor_b_{nullptr}; - sensor::Sensor *power_factor_c_{nullptr}; - - sensor::Sensor *active_energy_a_{nullptr}; - sensor::Sensor *active_energy_b_{nullptr}; - sensor::Sensor *active_energy_c_{nullptr}; - - sensor::Sensor *reactive_energy_a_{nullptr}; - sensor::Sensor *reactive_energy_b_{nullptr}; - sensor::Sensor *reactive_energy_c_{nullptr}; - - sensor::Sensor *apparent_energy_a_{nullptr}; - sensor::Sensor *apparent_energy_b_{nullptr}; - sensor::Sensor *apparent_energy_c_{nullptr}; - - // Combined sensors - sensor::Sensor *frequency_{nullptr}; - sensor::Sensor *total_active_power_{nullptr}; - sensor::Sensor *total_reactive_power_{nullptr}; - sensor::Sensor *total_apparent_power_{nullptr}; - sensor::Sensor *total_power_factor_{nullptr}; - sensor::Sensor *total_active_energy_{nullptr}; - sensor::Sensor *total_reactive_energy_{nullptr}; - sensor::Sensor *total_apparent_energy_{nullptr}; - - void reset_energy_(ResetPhase phase_option); -}; - -template class ResetEnergyAction final : public Action { - public: - explicit ResetEnergyAction(PZEM6L24 *parent) : parent_(parent) {} - - void set_phase(ResetPhase phase) { this->phase_ = phase; } - - void play(const Ts &...x) override { this->parent_->reset_energy_(this->phase_); } - - protected: - PZEM6L24 *parent_; - ResetPhase phase_{RESET_PHASE_ALL}; -}; - -} // namespace esphome::pzem6l24 diff --git a/components/pzem6l24/sensor.py b/components/pzem6l24/sensor.py deleted file mode 100644 index eb069f9..0000000 --- a/components/pzem6l24/sensor.py +++ /dev/null @@ -1,366 +0,0 @@ -from esphome import automation -from esphome.automation import maybe_simple_id -import esphome.codegen as cg -from esphome.components import modbus, sensor -import esphome.config_validation as cv -from esphome.const import ( - CONF_FREQUENCY, - CONF_ID, - DEVICE_CLASS_APPARENT_POWER, - DEVICE_CLASS_CURRENT, - DEVICE_CLASS_ENERGY, - DEVICE_CLASS_FREQUENCY, - DEVICE_CLASS_POWER, - DEVICE_CLASS_POWER_FACTOR, - DEVICE_CLASS_REACTIVE_ENERGY, - DEVICE_CLASS_REACTIVE_POWER, - DEVICE_CLASS_VOLTAGE, - ICON_CURRENT_AC, - STATE_CLASS_MEASUREMENT, - STATE_CLASS_TOTAL_INCREASING, - UNIT_AMPERE, - UNIT_HERTZ, - UNIT_KILOVOLT_AMPS_HOURS, - UNIT_KILOVOLT_AMPS_REACTIVE_HOURS, - UNIT_KILOWATT_HOURS, - UNIT_VOLT, - UNIT_VOLT_AMPS, - UNIT_VOLT_AMPS_REACTIVE, - UNIT_WATT, -) -from esphome.types import ConfigType - -AUTO_LOAD = ["modbus"] - -pzem6l24_ns = cg.esphome_ns.namespace("pzem6l24") -PZEM6L24 = pzem6l24_ns.class_("PZEM6L24", cg.PollingComponent, modbus.ModbusDevice) -ResetEnergyAction = pzem6l24_ns.class_("ResetEnergyAction", automation.Action) - -ResetPhase = pzem6l24_ns.enum("ResetPhase") -RESET_PHASE_OPTIONS = { - "all": ResetPhase.RESET_PHASE_ALL, - "a": ResetPhase.RESET_PHASE_A, - "b": ResetPhase.RESET_PHASE_B, - "c": ResetPhase.RESET_PHASE_C, - "combined": ResetPhase.RESET_PHASE_COMBINED, -} - -# Per-phase config keys -CONF_VOLTAGE_A = "voltage_a" -CONF_VOLTAGE_B = "voltage_b" -CONF_VOLTAGE_C = "voltage_c" -CONF_CURRENT_A = "current_a" -CONF_CURRENT_B = "current_b" -CONF_CURRENT_C = "current_c" -CONF_ACTIVE_POWER_A = "active_power_a" -CONF_ACTIVE_POWER_B = "active_power_b" -CONF_ACTIVE_POWER_C = "active_power_c" -CONF_REACTIVE_POWER_A = "reactive_power_a" -CONF_REACTIVE_POWER_B = "reactive_power_b" -CONF_REACTIVE_POWER_C = "reactive_power_c" -CONF_APPARENT_POWER_A = "apparent_power_a" -CONF_APPARENT_POWER_B = "apparent_power_b" -CONF_APPARENT_POWER_C = "apparent_power_c" -CONF_POWER_FACTOR_A = "power_factor_a" -CONF_POWER_FACTOR_B = "power_factor_b" -CONF_POWER_FACTOR_C = "power_factor_c" -CONF_ACTIVE_ENERGY_A = "active_energy_a" -CONF_ACTIVE_ENERGY_B = "active_energy_b" -CONF_ACTIVE_ENERGY_C = "active_energy_c" -CONF_REACTIVE_ENERGY_A = "reactive_energy_a" -CONF_REACTIVE_ENERGY_B = "reactive_energy_b" -CONF_REACTIVE_ENERGY_C = "reactive_energy_c" -CONF_APPARENT_ENERGY_A = "apparent_energy_a" -CONF_APPARENT_ENERGY_B = "apparent_energy_b" -CONF_APPARENT_ENERGY_C = "apparent_energy_c" -# Combined config keys -CONF_TOTAL_ACTIVE_POWER = "total_active_power" -CONF_TOTAL_REACTIVE_POWER = "total_reactive_power" -CONF_TOTAL_APPARENT_POWER = "total_apparent_power" -CONF_TOTAL_POWER_FACTOR = "total_power_factor" -CONF_TOTAL_ACTIVE_ENERGY = "total_active_energy" -CONF_TOTAL_REACTIVE_ENERGY = "total_reactive_energy" -CONF_TOTAL_APPARENT_ENERGY = "total_apparent_energy" -CONF_PHASE = "phase" - - -def _voltage_schema(): - return sensor.sensor_schema( - unit_of_measurement=UNIT_VOLT, - accuracy_decimals=1, - device_class=DEVICE_CLASS_VOLTAGE, - state_class=STATE_CLASS_MEASUREMENT, - ) - - -def _current_schema(): - return sensor.sensor_schema( - unit_of_measurement=UNIT_AMPERE, - accuracy_decimals=2, - device_class=DEVICE_CLASS_CURRENT, - state_class=STATE_CLASS_MEASUREMENT, - ) - - -def _active_power_schema(): - return sensor.sensor_schema( - unit_of_measurement=UNIT_WATT, - accuracy_decimals=1, - device_class=DEVICE_CLASS_POWER, - state_class=STATE_CLASS_MEASUREMENT, - ) - - -def _reactive_power_schema(): - return sensor.sensor_schema( - unit_of_measurement=UNIT_VOLT_AMPS_REACTIVE, - accuracy_decimals=1, - device_class=DEVICE_CLASS_REACTIVE_POWER, - state_class=STATE_CLASS_MEASUREMENT, - ) - - -def _apparent_power_schema(): - return sensor.sensor_schema( - unit_of_measurement=UNIT_VOLT_AMPS, - accuracy_decimals=1, - device_class=DEVICE_CLASS_APPARENT_POWER, - state_class=STATE_CLASS_MEASUREMENT, - ) - - -def _power_factor_schema(): - return sensor.sensor_schema( - accuracy_decimals=2, - device_class=DEVICE_CLASS_POWER_FACTOR, - state_class=STATE_CLASS_MEASUREMENT, - ) - - -def _active_energy_schema(): - return sensor.sensor_schema( - unit_of_measurement=UNIT_KILOWATT_HOURS, - accuracy_decimals=1, - device_class=DEVICE_CLASS_ENERGY, - state_class=STATE_CLASS_TOTAL_INCREASING, - ) - - -def _reactive_energy_schema(): - return sensor.sensor_schema( - unit_of_measurement=UNIT_KILOVOLT_AMPS_REACTIVE_HOURS, - accuracy_decimals=1, - device_class=DEVICE_CLASS_REACTIVE_ENERGY, - state_class=STATE_CLASS_TOTAL_INCREASING, - ) - - -def _apparent_energy_schema(): - return sensor.sensor_schema( - unit_of_measurement=UNIT_KILOVOLT_AMPS_HOURS, - accuracy_decimals=1, - state_class=STATE_CLASS_TOTAL_INCREASING, - ) - - -CONFIG_SCHEMA = ( - cv.Schema( - { - cv.GenerateID(): cv.declare_id(PZEM6L24), - # Per-phase voltage - cv.Optional(CONF_VOLTAGE_A): _voltage_schema(), - cv.Optional(CONF_VOLTAGE_B): _voltage_schema(), - cv.Optional(CONF_VOLTAGE_C): _voltage_schema(), - # Per-phase current - cv.Optional(CONF_CURRENT_A): _current_schema(), - cv.Optional(CONF_CURRENT_B): _current_schema(), - cv.Optional(CONF_CURRENT_C): _current_schema(), - # Per-phase active power - cv.Optional(CONF_ACTIVE_POWER_A): _active_power_schema(), - cv.Optional(CONF_ACTIVE_POWER_B): _active_power_schema(), - cv.Optional(CONF_ACTIVE_POWER_C): _active_power_schema(), - # Per-phase reactive power - cv.Optional(CONF_REACTIVE_POWER_A): _reactive_power_schema(), - cv.Optional(CONF_REACTIVE_POWER_B): _reactive_power_schema(), - cv.Optional(CONF_REACTIVE_POWER_C): _reactive_power_schema(), - # Per-phase apparent power - cv.Optional(CONF_APPARENT_POWER_A): _apparent_power_schema(), - cv.Optional(CONF_APPARENT_POWER_B): _apparent_power_schema(), - cv.Optional(CONF_APPARENT_POWER_C): _apparent_power_schema(), - # Per-phase power factor - cv.Optional(CONF_POWER_FACTOR_A): _power_factor_schema(), - cv.Optional(CONF_POWER_FACTOR_B): _power_factor_schema(), - cv.Optional(CONF_POWER_FACTOR_C): _power_factor_schema(), - # Per-phase active energy - cv.Optional(CONF_ACTIVE_ENERGY_A): _active_energy_schema(), - cv.Optional(CONF_ACTIVE_ENERGY_B): _active_energy_schema(), - cv.Optional(CONF_ACTIVE_ENERGY_C): _active_energy_schema(), - # Per-phase reactive energy - cv.Optional(CONF_REACTIVE_ENERGY_A): _reactive_energy_schema(), - cv.Optional(CONF_REACTIVE_ENERGY_B): _reactive_energy_schema(), - cv.Optional(CONF_REACTIVE_ENERGY_C): _reactive_energy_schema(), - # Per-phase apparent energy - cv.Optional(CONF_APPARENT_ENERGY_A): _apparent_energy_schema(), - cv.Optional(CONF_APPARENT_ENERGY_B): _apparent_energy_schema(), - cv.Optional(CONF_APPARENT_ENERGY_C): _apparent_energy_schema(), - # Combined sensors - cv.Optional(CONF_FREQUENCY): sensor.sensor_schema( - unit_of_measurement=UNIT_HERTZ, - icon=ICON_CURRENT_AC, - accuracy_decimals=2, - device_class=DEVICE_CLASS_FREQUENCY, - state_class=STATE_CLASS_MEASUREMENT, - ), - cv.Optional(CONF_TOTAL_ACTIVE_POWER): _active_power_schema(), - cv.Optional(CONF_TOTAL_REACTIVE_POWER): _reactive_power_schema(), - cv.Optional(CONF_TOTAL_APPARENT_POWER): _apparent_power_schema(), - cv.Optional(CONF_TOTAL_POWER_FACTOR): _power_factor_schema(), - cv.Optional(CONF_TOTAL_ACTIVE_ENERGY): _active_energy_schema(), - cv.Optional(CONF_TOTAL_REACTIVE_ENERGY): _reactive_energy_schema(), - cv.Optional(CONF_TOTAL_APPARENT_ENERGY): _apparent_energy_schema(), - } - ) - .extend(cv.polling_component_schema("60s")) - .extend(modbus.modbus_device_schema(0x01)) -) - - -@automation.register_action( - "pzem6l24.reset_energy", - ResetEnergyAction, - maybe_simple_id( - { - cv.Required(CONF_ID): cv.use_id(PZEM6L24), - cv.Optional(CONF_PHASE, default="all"): cv.enum( - RESET_PHASE_OPTIONS, lower=True - ), - } - ), - synchronous=True, -) -async def reset_energy_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) - cg.add(var.set_phase(config[CONF_PHASE])) - return var - - -async def _register_sensor(var, config, conf_key, setter_name): - if (conf := config.get(conf_key)) is not None: - sens = await sensor.new_sensor(conf) - cg.add(getattr(var, setter_name)(sens)) - - -def _final_validate(config: ConfigType) -> ConfigType: - return modbus.final_validate_modbus_device("pzem6l24", role="client")(config) - - -FINAL_VALIDATE_SCHEMA = _final_validate - - -async def to_code(config): - var = cg.new_Pvariable(config[CONF_ID]) - await cg.register_component(var, config) - await modbus.register_modbus_client_device(var, config) - - # Per-phase voltage - await _register_sensor(var, config, CONF_VOLTAGE_A, "set_voltage_sensor_a") - await _register_sensor(var, config, CONF_VOLTAGE_B, "set_voltage_sensor_b") - await _register_sensor(var, config, CONF_VOLTAGE_C, "set_voltage_sensor_c") - # Per-phase current - await _register_sensor(var, config, CONF_CURRENT_A, "set_current_sensor_a") - await _register_sensor(var, config, CONF_CURRENT_B, "set_current_sensor_b") - await _register_sensor(var, config, CONF_CURRENT_C, "set_current_sensor_c") - # Per-phase active power - await _register_sensor( - var, config, CONF_ACTIVE_POWER_A, "set_active_power_sensor_a" - ) - await _register_sensor( - var, config, CONF_ACTIVE_POWER_B, "set_active_power_sensor_b" - ) - await _register_sensor( - var, config, CONF_ACTIVE_POWER_C, "set_active_power_sensor_c" - ) - # Per-phase reactive power - await _register_sensor( - var, config, CONF_REACTIVE_POWER_A, "set_reactive_power_sensor_a" - ) - await _register_sensor( - var, config, CONF_REACTIVE_POWER_B, "set_reactive_power_sensor_b" - ) - await _register_sensor( - var, config, CONF_REACTIVE_POWER_C, "set_reactive_power_sensor_c" - ) - # Per-phase apparent power - await _register_sensor( - var, config, CONF_APPARENT_POWER_A, "set_apparent_power_sensor_a" - ) - await _register_sensor( - var, config, CONF_APPARENT_POWER_B, "set_apparent_power_sensor_b" - ) - await _register_sensor( - var, config, CONF_APPARENT_POWER_C, "set_apparent_power_sensor_c" - ) - # Per-phase power factor - await _register_sensor( - var, config, CONF_POWER_FACTOR_A, "set_power_factor_sensor_a" - ) - await _register_sensor( - var, config, CONF_POWER_FACTOR_B, "set_power_factor_sensor_b" - ) - await _register_sensor( - var, config, CONF_POWER_FACTOR_C, "set_power_factor_sensor_c" - ) - # Per-phase active energy - await _register_sensor( - var, config, CONF_ACTIVE_ENERGY_A, "set_active_energy_sensor_a" - ) - await _register_sensor( - var, config, CONF_ACTIVE_ENERGY_B, "set_active_energy_sensor_b" - ) - await _register_sensor( - var, config, CONF_ACTIVE_ENERGY_C, "set_active_energy_sensor_c" - ) - # Per-phase reactive energy - await _register_sensor( - var, config, CONF_REACTIVE_ENERGY_A, "set_reactive_energy_sensor_a" - ) - await _register_sensor( - var, config, CONF_REACTIVE_ENERGY_B, "set_reactive_energy_sensor_b" - ) - await _register_sensor( - var, config, CONF_REACTIVE_ENERGY_C, "set_reactive_energy_sensor_c" - ) - # Per-phase apparent energy - await _register_sensor( - var, config, CONF_APPARENT_ENERGY_A, "set_apparent_energy_sensor_a" - ) - await _register_sensor( - var, config, CONF_APPARENT_ENERGY_B, "set_apparent_energy_sensor_b" - ) - await _register_sensor( - var, config, CONF_APPARENT_ENERGY_C, "set_apparent_energy_sensor_c" - ) - # Combined sensors - await _register_sensor(var, config, CONF_FREQUENCY, "set_frequency_sensor") - await _register_sensor( - var, config, CONF_TOTAL_ACTIVE_POWER, "set_total_active_power_sensor" - ) - await _register_sensor( - var, config, CONF_TOTAL_REACTIVE_POWER, "set_total_reactive_power_sensor" - ) - await _register_sensor( - var, config, CONF_TOTAL_APPARENT_POWER, "set_total_apparent_power_sensor" - ) - await _register_sensor( - var, config, CONF_TOTAL_POWER_FACTOR, "set_total_power_factor_sensor" - ) - await _register_sensor( - var, config, CONF_TOTAL_ACTIVE_ENERGY, "set_total_active_energy_sensor" - ) - await _register_sensor( - var, config, CONF_TOTAL_REACTIVE_ENERGY, "set_total_reactive_energy_sensor" - ) - await _register_sensor( - var, config, CONF_TOTAL_APPARENT_ENERGY, "set_total_apparent_energy_sensor" - ) diff --git a/devices/basement-bath-shower-light-heat.yaml b/devices/basement-bath-shower-light-heat.yaml index 609690e..c367da0 100644 --- a/devices/basement-bath-shower-light-heat.yaml +++ b/devices/basement-bath-shower-light-heat.yaml @@ -3,7 +3,6 @@ substitutions: device_name: Basement Bathroom Shower Light and Heater board: esp01_1m api_key: !secret basement_bathroom_shower_light_heat_key - pwd: !secret basement_bathroom_shower_light_heat_pwd packages: device: !include ./packages/device_esp8266_wifi.yaml diff --git a/devices/basement-fridge.yaml b/devices/basement-fridge.yaml index 5da17f3..2344842 100644 --- a/devices/basement-fridge.yaml +++ b/devices/basement-fridge.yaml @@ -2,7 +2,6 @@ substitutions: device_id: basement-fridge device_name: Basement Fridge api_key: !secret basement_fridge_key - pwd: !secret basement_fridge_pwd packages: device: !include ./packages/topgreener_smart_plug.yaml diff --git a/devices/basement-tv.yaml b/devices/basement-tv.yaml index 2a2a873..61f6291 100644 --- a/devices/basement-tv.yaml +++ b/devices/basement-tv.yaml @@ -2,7 +2,6 @@ substitutions: device_id: basement-tv device_name: Basement TV api_key: !secret basement_tv_key - pwd: !secret basement_tv_pwd packages: device: !include ./packages/topgreener_smart_plug.yaml diff --git a/devices/bt-proxy-basement.yaml b/devices/bt-proxy-basement.yaml index f4e0e16..ae805e6 100644 --- a/devices/bt-proxy-basement.yaml +++ b/devices/bt-proxy-basement.yaml @@ -2,7 +2,6 @@ substitutions: device_id: bt-proxy-basement device_name: Basement Bluetooth Proxy api_key: !secret bt_proxy_basement_key - pwd: !secret bt_proxy_basement_pwd packages: device: !include ./packages/device_esp32_wifi.yaml diff --git a/devices/coffee-maker.yaml b/devices/coffee-maker.yaml index 060dd6f..d9322a8 100644 --- a/devices/coffee-maker.yaml +++ b/devices/coffee-maker.yaml @@ -3,7 +3,6 @@ substitutions: device_name: Coffee Maker board: nodemcuv2 api_key: !secret coffee_maker_key - pwd: !secret coffee_maker_pwd packages: device: !include ./packages/device_esp8266_wifi.yaml diff --git a/devices/emporia-vue2.yaml b/devices/emporia-vue2.yaml index 98ccfeb..2ede29f 100644 --- a/devices/emporia-vue2.yaml +++ b/devices/emporia-vue2.yaml @@ -3,14 +3,13 @@ substitutions: device_name: Emporia Vue2 Power Monitor board: esp32dev api_key: !secret emporia_vue2_key - pwd: !secret emporia_vue2_pwd packages: device: !include ./packages/device_esp32_wifi.yaml api: - services: - - service: play_rtttl + actions: + - action: play_rtttl variables: song_str: string then: diff --git a/devices/family-room-tv.yaml b/devices/family-room-tv.yaml index 889505f..903bab7 100644 --- a/devices/family-room-tv.yaml +++ b/devices/family-room-tv.yaml @@ -2,7 +2,6 @@ substitutions: device_id: family-room-tv device_name: Family Room TV api_key: !secret family_room_tv_key - pwd: !secret family_room_tv_pwd packages: device: !include ./packages/topgreener_smart_plug.yaml diff --git a/devices/fire-pit-fountain.yaml b/devices/fire-pit-fountain.yaml index 41cafdb..1415101 100644 --- a/devices/fire-pit-fountain.yaml +++ b/devices/fire-pit-fountain.yaml @@ -3,7 +3,6 @@ substitutions: device_name: Fire Pit and Fountain board: d1_mini api_key: !secret fire_pit_fountain_key - pwd: !secret fire_pit_fountain_pwd packages: device: !include ./packages/device_esp8266_wifi.yaml diff --git a/devices/garage-fridge.yaml b/devices/garage-fridge.yaml index 6bc0149..19c3812 100644 --- a/devices/garage-fridge.yaml +++ b/devices/garage-fridge.yaml @@ -2,7 +2,6 @@ substitutions: device_id: garage-fridge device_name: Garage Fridge api_key: !secret garage_fridge_key - pwd: !secret garage_fridge_pwd log_baud_rate: '0' packages: diff --git a/devices/garage-lights.yaml b/devices/garage-lights.yaml index b4465a3..0458da5 100644 --- a/devices/garage-lights.yaml +++ b/devices/garage-lights.yaml @@ -2,7 +2,6 @@ substitutions: device_id: garage-lights device_name: Garage Lights api_key: !secret garage_lights_key - pwd: !secret garage_lights_pwd light_wattage: '378' packages: diff --git a/devices/hvac.yaml b/devices/hvac.yaml index b2a6124..c8a0c45 100644 --- a/devices/hvac.yaml +++ b/devices/hvac.yaml @@ -2,7 +2,6 @@ substitutions: device_id: hvac device_name: HVAC api_key: !secret hvac_key - pwd: !secret hvac_pwd zone1name: Main Floor zone2name: Upstairs zone3name: Basement diff --git a/devices/kitchen-fridge.yaml b/devices/kitchen-fridge.yaml index ffc932b..3241931 100644 --- a/devices/kitchen-fridge.yaml +++ b/devices/kitchen-fridge.yaml @@ -2,7 +2,6 @@ substitutions: device_id: kitchen-fridge device_name: Kitchen Fridge api_key: !secret kitchen_fridge_key - pwd: !secret kitchen_fridge_pwd packages: device: !include ./packages/topgreener_smart_plug.yaml diff --git a/devices/main-garage-door.yaml b/devices/main-garage-door.yaml index 95d9c55..1b65d02 100644 --- a/devices/main-garage-door.yaml +++ b/devices/main-garage-door.yaml @@ -3,7 +3,6 @@ substitutions: device_name: Main Garage Door board: d1_mini api_key: !secret main_garage_door_key - pwd: !secret main_garage_door_pwd flash_write_interval: 5s packages: diff --git a/devices/master-bed.yaml b/devices/master-bed.yaml index b86519a..b4fbbc4 100644 --- a/devices/master-bed.yaml +++ b/devices/master-bed.yaml @@ -3,7 +3,6 @@ substitutions: device_name: Master Bed board: nodemcuv2 api_key: !secret master_bed_key - pwd: !secret master_bed_pwd packages: device: !include ./packages/device_esp8266_wifi.yaml diff --git a/devices/network-equipment.yaml b/devices/network-equipment.yaml index 0888763..3f11300 100644 --- a/devices/network-equipment.yaml +++ b/devices/network-equipment.yaml @@ -2,7 +2,6 @@ substitutions: device_id: network-equipment device_name: Network Equipment api_key: !secret network_equipment_key - pwd: !secret network_equipment_pwd packages: device: !include ./packages/topgreener_smart_plug.yaml diff --git a/devices/packages/wifi/wifi.yaml b/devices/packages/wifi/wifi.yaml index bb7036a..424880c 100644 --- a/devices/packages/wifi/wifi.yaml +++ b/devices/packages/wifi/wifi.yaml @@ -8,7 +8,3 @@ wifi: fast_connect: true power_save_mode: none min_auth_mode: WPA2 - ap: - ssid: ${device_id} - -captive_portal: diff --git a/devices/pool-lights.yaml b/devices/pool-lights.yaml index bf38833..18cc24d 100644 --- a/devices/pool-lights.yaml +++ b/devices/pool-lights.yaml @@ -3,7 +3,6 @@ substitutions: device_name: Pool Lights board: esp01_1m api_key: !secret pool_lights_key - pwd: !secret pool_lights_pwd packages: device: !include ./packages/device_esp8266_wifi.yaml diff --git a/devices/pool.yaml b/devices/pool.yaml index 011a6c1..67054aa 100644 --- a/devices/pool.yaml +++ b/devices/pool.yaml @@ -4,7 +4,6 @@ substitutions: device_id: pool device_name: Pool api_key: !secret pool_key - pwd: !secret pool_pwd esp32_variant: esp32s3 log_level: DEBUG @@ -16,16 +15,6 @@ external_components: type: local path: ../components components: [pool_controller] - # Pinned to the last pzem6l24 commit before upstream switched reset_energy to - # automation.register_apply_action (esphome/esphome#19469, merged 2026-09-23). - # That helper landed after the 2026.9 branch cut, so the PR head no longer - # validates against any released ESPHome. Unpin once a release carries it. - - source: - type: git - url: https://github.com/esphome/esphome.git - ref: 36d39416f0a7cdabb536d5a7434222de462c3e89 - components: [pzem6l24] - refresh: never # esphome: # on_boot: diff --git a/devices/second-garage-door.yaml b/devices/second-garage-door.yaml index c74054b..8505408 100644 --- a/devices/second-garage-door.yaml +++ b/devices/second-garage-door.yaml @@ -3,7 +3,6 @@ substitutions: device_name: Second Garage Door board: d1_mini api_key: !secret second_garage_door_key - pwd: !secret second_garage_door_pwd id_prefix: second_garage_door uart_tx_pin: D1 uart_rx_pin: D2 @@ -27,6 +26,7 @@ button: ota: - id: !remove ${id_prefix}_ratgdo_ota + - id: !remove ${id_prefix}_ratgdo_ota_ws text_sensor: - id: !remove ${id_prefix}_firmware_version diff --git a/devices/sump-pump.yaml b/devices/sump-pump.yaml index 3cabcd6..c0ca788 100644 --- a/devices/sump-pump.yaml +++ b/devices/sump-pump.yaml @@ -2,7 +2,6 @@ substitutions: device_id: sump-pump device_name: Sump Pump api_key: !secret sump_pump_key - pwd: !secret sump_pump_pwd packages: device: !include ./packages/topgreener_smart_plug.yaml diff --git a/devices/washing-machine.yaml b/devices/washing-machine.yaml index e9e426b..63fc081 100644 --- a/devices/washing-machine.yaml +++ b/devices/washing-machine.yaml @@ -2,7 +2,6 @@ substitutions: device_id: washing-machine device_name: Washing Machine api_key: !secret washing_machine_key - pwd: !secret washing_machine_pwd packages: device: !include ./packages/topgreener_smart_plug.yaml diff --git a/devices/water-heater.yaml b/devices/water-heater.yaml index e026ffd..71fdea3 100644 --- a/devices/water-heater.yaml +++ b/devices/water-heater.yaml @@ -2,7 +2,6 @@ substitutions: device_id: water-heater device_name: Water Heater api_key: !secret water_heater_key - pwd: !secret water_heater_pwd external_components: - source: diff --git a/docs/pool/README.md b/docs/pool/README.md index b733065..ccada7d 100644 --- a/docs/pool/README.md +++ b/docs/pool/README.md @@ -4,7 +4,7 @@ Work in progress — this device is actively being expanded. The sections marked `TODO` below are placeholders waiting on details, photos, and schematics. ## Overview -The pool is controlled by a [Waveshare ESP32-S3 7" Touch LCD B](https://www.waveshare.com/product/esp32-s3-lcd-7b.htm) running a touchscreen UI (LVGL) plus the custom [Pool Controller](../../components/pool_controller/README.md) and [PZEM-6L24](../../components/pzem6l24/README.md) components. Configuration lives in [pool.yaml](../../devices/pool.yaml). +The pool is controlled by a [Waveshare ESP32-S3 7" Touch LCD B](https://www.waveshare.com/product/esp32-s3-lcd-7b.htm) running a touchscreen UI (LVGL) plus the custom [Pool Controller](../../components/pool_controller/README.md) component. Configuration lives in [pool.yaml](../../devices/pool.yaml). ## Hardware - Controller board: Waveshare ESP32-S3 7" Touch LCD B (`esp32s3`), MIPI RGB display + GT911 touch