Decoding Power Bar with Energy Monitoring

Thank you! I assume only 2 of the 4 channels are required right? Just considering getting something smaller

This is the circuit I used for level shifting in my original mod. Resistor values are not too important, as long as they are not too far off. (YMMV)

image

For my initial proof of concept, I used a 74LS00 just to boost the output to 5V…

I updated mine to use external component. Create the folders and files under esphome

__init__.py

# just required so Python treats the folder as a package

sensor.py

import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor
from esphome.const import (
    UNIT_WATT, ICON_FLASH, DEVICE_CLASS_POWER,
    UNIT_VOLT, DEVICE_CLASS_VOLTAGE,
    UNIT_AMPERE, DEVICE_CLASS_CURRENT,
)

wion_ns = cg.esphome_ns.namespace("wion_power_monitor")
WionPowerMonitor = wion_ns.class_("WionPowerMonitor", cg.PollingComponent)

CONFIG_SCHEMA = cv.Schema({
    cv.GenerateID(): cv.declare_id(WionPowerMonitor),

    cv.Optional("power"): sensor.sensor_schema(
        unit_of_measurement=UNIT_WATT,
        icon=ICON_FLASH,
        accuracy_decimals=1,
        device_class=DEVICE_CLASS_POWER,
    ),
    cv.Optional("voltage"): sensor.sensor_schema(
        unit_of_measurement=UNIT_VOLT,
        accuracy_decimals=1,
        device_class=DEVICE_CLASS_VOLTAGE,
    ),
    cv.Optional("current"): sensor.sensor_schema(
        unit_of_measurement=UNIT_AMPERE,
        accuracy_decimals=1,
        device_class=DEVICE_CLASS_CURRENT,
    ),
    cv.Optional("power_factor"): sensor.sensor_schema(
        accuracy_decimals=1,
    ),
})

async def to_code(config):
    var = cg.new_Pvariable(config[cv.CONF_ID])
    await cg.register_component(var, config)

    if "power" in config:
        sens = await sensor.new_sensor(config["power"])
        cg.add(var.set_power_sensor(sens))
    if "voltage" in config:
        sens = await sensor.new_sensor(config["voltage"])
        cg.add(var.set_voltage_sensor(sens))
    if "current" in config:
        sens = await sensor.new_sensor(config["current"])
        cg.add(var.set_current_sensor(sens))
    if "power_factor" in config:
        sens = await sensor.new_sensor(config["power_factor"])
        cg.add(var.set_powerf_sensor(sens))

wion_power_monitor.cpp

#include "wion_power_monitor.h"
#include "esphome/core/log.h"

namespace esphome {
namespace wion_power_monitor {

void WionPowerMonitor::setup() {
  pinMode(0, OUTPUT);
  pinMode(12, INPUT);
  digitalWrite(0, HIGH);
}

void WionPowerMonitor::update() {
  const int numBlocks = 4;
  const int bitsPerBlock = 32;
  int data[numBlocks];

  for (int block = 0; block < numBlocks; block++) {
    for (int bitIdx = bitsPerBlock - 1; bitIdx >= 0; bitIdx--) {
      digitalWrite(0, LOW);
      delayMicroseconds(35);
      bitWrite(data[block], bitIdx, digitalRead(12));
      digitalWrite(0, HIGH);
      delayMicroseconds(35);
    }
  }

  for (int block = 0; block < numBlocks; block++) {
    for (int bit = 24; bit < 32; bit++) {
      bitWrite(data[block], bit, 0);
    }
  }

  ESP_LOGD(TAG, "raw: %#08X; %#08X; %#08X; %#08X", data[0], data[1], data[2], data[3]);

  float power = data[0];
  float voltage = data[1];
  float powerF = data[2];
  float current = data[3];

  power = power / 3;  // calibrated against Sonoff S31

  if (power < 1 || power > 2000) {
    power = 0;
  }

  if (data[0] != 0xFFFFFF && current < 3000) {
    if (power_sensor) power_sensor->publish_state(power);
    if (voltage_sensor) voltage_sensor->publish_state(voltage);
    if (powerf_sensor) powerf_sensor->publish_state(powerF);
    if (current_sensor) current_sensor->publish_state(current);
  }
}

void WionPowerMonitor::dump_config() {
  ESP_LOGCONFIG(TAG, "Wion Power Monitor:");
  LOG_SENSOR("  ", "Power", this->power_sensor);
  LOG_SENSOR("  ", "Voltage", this->voltage_sensor);
  LOG_SENSOR("  ", "PowerF", this->powerf_sensor);
  LOG_SENSOR("  ", "Current", this->current_sensor);
}

}  // namespace wion_power_monitor
}  // namespace esphome

wion_power_monitor.h

#pragma once

#include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h"

namespace esphome {
namespace wion_power_monitor {

static const char *const TAG = "wion_power_monitor";

class WionPowerMonitor : public PollingComponent {
 public:
  WionPowerMonitor() : PollingComponent(15000) {}

  // setters called from sensor.py
  void set_power_sensor(sensor::Sensor *s) { power_sensor = s; }
  void set_voltage_sensor(sensor::Sensor *s) { voltage_sensor = s; }
  void set_current_sensor(sensor::Sensor *s) { current_sensor = s; }
  void set_powerf_sensor(sensor::Sensor *s) { powerf_sensor = s; }

  float get_setup_priority() const override { return esphome::setup_priority::BUS; }

  void setup() override;
  void update() override;
  void dump_config() override;

 protected:
  sensor::Sensor *power_sensor{nullptr};
  sensor::Sensor *voltage_sensor{nullptr};
  sensor::Sensor *current_sensor{nullptr};
  sensor::Sensor *powerf_sensor{nullptr};
};

}  // namespace wion_power_monitor
}  // namespace esphome

esphome YAML

esphome:
  name: "wion-plug"
  friendly_name: WION-Plug

esp8266:
  board: esp01_1m

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "key"

ota:
  - platform: esphome
    password: "password"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Wion-Plug Fallback Hotspot"
    password: "password"

switch:
  - platform: gpio
    name: " Plug"
    pin: GPIO15
    id: relay1

binary_sensor:
  - platform: gpio
    pin:
      number: GPIO13
    id: button
    on_press:
     - switch.toggle: relay1

  - platform: status
    name: Plug

light:
  - platform: binary
    name: "LED"
    output: light_output
    restore_mode: ALWAYS_OFF

output:
  - id: light_output
    platform: gpio
    pin: GPIO14
  - id: wifi_output
    platform: gpio
    pin: GPIO2
    inverted: true

interval:
  - interval: 5s
    then:
      if:
        condition:
          wifi.connected:
        then:
          - output.turn_on: wifi_output
        else:
          - output.turn_off: wifi_output
  
external_components:
  - source:
      type: local
      path: ./external_components

sensor:
  - platform: wion_power_monitor
    power:
      name: "Power"
      
captive_portal:

I am only using the power sensor, but the rest are in the code

hi, I did a complete reverse engineering of this plug, I posted the code here GitHub - Taxom/WiOn-50055-ESPHome: ESPHome replacement firmware for the Woods WiOn 50055 Wi-Fi outlet, retaining the stock power-monitoring hardware. · GitHub