dxs238xw — ESPHome component for DDS238-4W / DTS238-7W energy meters
Local control of Hiking / ENGELEC WiFi smart energy meters sold under the WISEN
brand — no cloud, no internet.
| Model | Type |
|---|---|
| DDS238-4W | single phase, DIN rail, 5(60) A |
| DTS238-7W | three phase, DIN rail |
These meters ship with a TYWE3S / ESP8285 WiFi module that can be reflashed with
ESPHome, or replaced with any ESP8266/ESP32 board. The measuring board speaks the
HEKR protocol over UART at 9600 8N1.
[!WARNING]
These meters are wired to mains voltage. Disconnect the installation before
opening the meter, and never work on it while it is energised.
Installation
external_components:
- source: github://kaboom748/dxs238xw
components: [dxs238xw]
refresh: 1d
uart:
id: uart_bus
tx_pin: GPIO1
rx_pin: GPIO3
baud_rate: 9600
logger:
baud_rate: 0 # the log would collide with the meter on the same pins
dxs238xw:
id: meter
sensor:
- platform: dxs238xw
active_power_total:
name: "Active Power"
total_energy:
name: "Total Energy"
binary_sensor:
- platform: dxs238xw
meter_state:
name: "Meter State"
switch:
- platform: dxs238xw
meter_state:
name: "Relay"
restore_from_flash: true is required for the kWh price, the starting index, the
delay and the energy purchase to survive a reboot.
A complete configuration exposing all 57 entities is in example/smartmeter.yaml.
Full reference documentation: docs/dxs238xw.mdx.
What makes this component different
It never blocks
The state machine runs inside loop() and always returns immediately. No
while(true) + yield(), no waiting in setup(), no recursion. On an ESP8266 that
also runs WiFi and the API, a blocking serial read is what causes disconnections
and Component took a long time warnings.
It matches the datasheet
Checked against the DDS238-4W Technical Datasheet (ENGELEC) and the DTS238-7
WIFI User Manual:
| Point | Datasheet | This component |
|---|---|---|
| Current protection | 65 A default, settable 1-63 A (1-80 A on the 80 A variant) | 1-80 A |
| Delay | “time delay control on/off”, 1 min to 24 h | 1-1440 min |
| Total energy | “negative energy accumulated into positive energy” → total = |import| + |export| |
total_increasing |
| Export | “the reverse active energy, such as solar power generation” | positive accumulator |
| Voltage limits | “high limit must be bigger than low limit” | cross-validated |
Sign correction is exact
The meter encodes negative (export) values as offset binary, with an offset of
1,000,000 raw units — the same for current and power. Correcting the sign
after scaling means computing 1000.0f - 1000.5f, and the float spacing at that
magnitude is 6e-5, which destroys the precision of the small result (-0.50006
instead of -0.5). This component corrects on the raw integer:
1000500 - 1000000 = 500, exact.
It does not fight the meter’s firmware
When the delay timer elapses, the meter’s own firmware drives the relay — on this
hardware it switches it on. Sending a command at that moment means fighting the
meter. Verified on a DTS238-7W: the meter also never clears its armed flag, so
delay_state stays set forever with remaining = 0.
Consequences, all handled:
- ESPHome sends nothing when the timer elapses (
delay_end_action: none). - The
delay_stateswitch reports whether a delay is running
(delay_state && remaining > 0), not the raw flag, so it returns to off at0m. warning_off_by_end_delayfires on the transition to zero, not on the steady
state — otherwise a manual switch-off months later would still be blamed on the
delay.
delay_end_action |
Effect |
|---|---|
none (default) |
ESPHome observes. The meter decides. |
disarm |
disarms the timer, without touching the relay |
force_off |
forces the relay off — discouraged |
Known hardware limitation
The manual is explicit: “the meter do not have time clock internal, the time
control is decide to cloud serve”. Scheduling (fixed-time on/off) depends on
the WISEN cloud and is not reachable from any local component. Only the delay
(a relative timer) is local, and it is implemented here. Use Home Assistant
automations or ESPHome’s time component for scheduling.
Entities
57 entities across 6 platforms: 32 sensor, 8 binary_sensor, 4 text_sensor,
9 number, 3 switch, 1 button, plus the meter_state_on / meter_state_off /
meter_state_toggle / hex_message actions. See docs/dxs238xw.mdx.
Testing without hardware
tools/meter_sim.py simulates a HEKR meter on a virtual serial port:
# 1. create a pair of linked virtual serial ports
socat -d -d pty,raw,echo=0,link=/tmp/meter pty,raw,echo=0,link=/tmp/esp
# 2. run the simulator on one end
python tools/meter_sim.py /tmp/meter
# 3. point an ESPHome `host` platform build at the other end: /tmp/esp
Quality checks
| Check | Result |
|---|---|
Google Test unit tests (tests/components/dxs238xw/) |
11/11 |
esphome config, esp8266-ard / esp32-ard / esp32-idf / rp2040-ard |
4/4 |
ESPHome ci-custom.py |
0 |
ruff check / ruff format |
0 |
clang-format (ESPHome config) |
0 |
-Wall -Wextra -Wpedantic, 3 log levels |
0 warnings |
The layout mirrors the official ESPHome repository (esphome/components/…,
tests/components/…), so it also works directly as a github:// source.
Credits
The HEKR protocol was reverse engineered by rodgon81, whose dxs238xw
component (rodgon81/esphome on GitHub) is the origin of this work — see ESPHome
pull request #3750. This component keeps his protocol map and frame layout, and
departs from his implementation on the points described above.
Also relevant: ESPHome pull request #10207 by Gaudi111.
License
Same model as ESPHome: the C++ / runtime code (.cpp, .h) is GPLv3, the
Python code and everything else is MIT. See the LICENSE file.