diff --git a/esphome/components/pwrman_charger/__init__.py b/esphome/components/pwrman_charger/__init__.py new file mode 100644 index 0000000..89aba6d --- /dev/null +++ b/esphome/components/pwrman_charger/__init__.py @@ -0,0 +1,37 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import i2c +from esphome.const import CONF_ID, CONF_MAX_CURRENT + +CODEOWNERS = ["@mikesmitty"] +DEPENDENCIES = ["i2c"] +MULTI_CONF = True + +CONF_PORT_NUMBER = "port_number" +CONF_PWRMAN_CHARGER_ID = "pwrman_charger_id" + +pwrman_charger_ns = cg.esphome_ns.namespace("pwrman_charger") +PwrmanCharger = pwrman_charger_ns.class_("PwrmanCharger", cg.Component, i2c.I2CDevice) + +PWRMAN_CHARGER_SCHEMA = cv.Schema( + { + cv.GenerateID(CONF_PWRMAN_CHARGER_ID): cv.use_id(PwrmanCharger), + } +) + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(PwrmanCharger), + # FIXME: add max_current + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(i2c.i2c_device_schema(0x4D)) +) + + +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) diff --git a/esphome/components/pwrman_charger/binary_sensor/__init__.py b/esphome/components/pwrman_charger/binary_sensor/__init__.py new file mode 100644 index 0000000..7f50294 --- /dev/null +++ b/esphome/components/pwrman_charger/binary_sensor/__init__.py @@ -0,0 +1,80 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import binary_sensor +from esphome.const import CONF_ID, ENTITY_CATEGORY_DIAGNOSTIC, ENTITY_CATEGORY_NONE +from .. import ( + CONF_PWRMAN_CHARGER_ID, + PWRMAN_CHARGER_SCHEMA, + pwrman_charger_ns, +) + +CODEOWNERS = ["@mikesmitty"] +DEPENDENCIES = ["pwrman_charger"] + +CONF_CABLE_5A_CAPABLE = "cable_5a_capable" +CONF_OTW_THRESHOLD_1 = "otw_threshold_1" +CONF_OTW_THRESHOLD_2 = "otw_threshold_2" +CONF_PPS_MODE = "pps_mode" +CONF_SINK_ATTACHED = "sink_attached" + +ICON_BATTERY_ARROW_DOWN_OUTLINE = "mdi:battery-arrow-down-outline" +ICON_NUMERIC_5_BOX_MULTIPLE = "mdi:numeric-5-box-multiple" +ICON_POWER_SETTINGS = "mdi:power-settings" +ICON_THERMOMETER_ALERT = "mdi:thermometer-alert" +ICON_THERMOMETER_HIGH = "mdi:thermometer-high" +ICON_USB_C_PORT = "mdi:usb-c-port" + +PwrmanChargerBinarySensor = pwrman_charger_ns.class_( + "PwrmanChargerBinarySensor", cg.PollingComponent +) + +CONFIG_SCHEMA = PWRMAN_CHARGER_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(PwrmanChargerBinarySensor), + cv.Optional(CONF_CABLE_5A_CAPABLE): binary_sensor.binary_sensor_schema( + icon=ICON_NUMERIC_5_BOX_MULTIPLE, + ), + cv.Optional(CONF_OTW_THRESHOLD_1): binary_sensor.binary_sensor_schema( + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + icon=ICON_THERMOMETER_HIGH, + ), + cv.Optional(CONF_OTW_THRESHOLD_2): binary_sensor.binary_sensor_schema( + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + icon=ICON_THERMOMETER_ALERT, + ), + cv.Optional(CONF_PPS_MODE): binary_sensor.binary_sensor_schema( + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + icon=ICON_POWER_SETTINGS, + ), + cv.Optional(CONF_SINK_ATTACHED): binary_sensor.binary_sensor_schema( + entity_category=ENTITY_CATEGORY_NONE, + icon=ICON_USB_C_PORT, + ), + } +).extend(cv.polling_component_schema("60s")) + + +async def to_code(config): + paren = await cg.get_variable(config[CONF_PWRMAN_CHARGER_ID]) + var = cg.new_Pvariable(config[CONF_ID], paren) + await cg.register_component(var, config) + + if cable_5a_capable_config := config.get(CONF_CABLE_5A_CAPABLE): + sens = await binary_sensor.new_binary_sensor(cable_5a_capable_config) + cg.add(var.set_cable_5a_capable_binary_sensor(sens)) + + if otw_threshold_1_config := config.get(CONF_OTW_THRESHOLD_1): + sens = await binary_sensor.new_binary_sensor(otw_threshold_1_config) + cg.add(var.set_otw_threshold_1_binary_sensor(sens)) + + if otw_threshold_2_config := config.get(CONF_OTW_THRESHOLD_2): + sens = await binary_sensor.new_binary_sensor(otw_threshold_2_config) + cg.add(var.set_otw_threshold_2_binary_sensor(sens)) + + if pps_mode_config := config.get(CONF_PPS_MODE): + sens = await binary_sensor.new_binary_sensor(pps_mode_config) + cg.add(var.set_pps_mode_binary_sensor(sens)) + + if sink_attached_config := config.get(CONF_SINK_ATTACHED): + sens = await binary_sensor.new_binary_sensor(sink_attached_config) + cg.add(var.set_sink_attached_binary_sensor(sens)) diff --git a/esphome/components/pwrman_charger/binary_sensor/pwrman_charger_binary_sensor.cpp b/esphome/components/pwrman_charger/binary_sensor/pwrman_charger_binary_sensor.cpp new file mode 100644 index 0000000..79d5fdd --- /dev/null +++ b/esphome/components/pwrman_charger/binary_sensor/pwrman_charger_binary_sensor.cpp @@ -0,0 +1,54 @@ +#include "pwrman_charger_binary_sensor.h" + +#include "esphome/components/binary_sensor/binary_sensor.h" +#include "esphome/core/component.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace pwrman_charger { + +static const char *TAG = "pwrman_charger.binary_sensor"; + +float PwrmanChargerBinarySensor::get_setup_priority() const { return setup_priority::DATA; } + +void PwrmanChargerBinarySensor::setup() { ESP_LOGCONFIG(TAG, "Setting up PwrmanChargerBinarySensor..."); } + +void PwrmanChargerBinarySensor::update() { + uint8_t fault_state = 0; + this->parent_->read_register(PWRMAN_CHARGER_REGISTER_FAULT, &fault_state, 1); + + if (this->cable_5a_capable_binary_sensor_ != nullptr) { + this->parent_->read_register(PWRMAN_CHARGER_REGISTER_CABLE_5A_FLAG, this->resp_bytes_, 1); + this->cable_5a_capable_binary_sensor_->publish_state(this->resp_bytes_[0]); + } + + if (this->otw_threshold_1_binary_sensor_ != nullptr) { + this->otw_threshold_1_binary_sensor_->publish_state(CHECK_BIT(fault_state, 1)); + } + + if (this->otw_threshold_2_binary_sensor_ != nullptr) { + this->otw_threshold_2_binary_sensor_->publish_state(CHECK_BIT(fault_state, 2)); + } + + if (this->pps_mode_binary_sensor_ != nullptr) { + this->parent_->read_register(PWRMAN_CHARGER_REGISTER_CUR_PDO_MIN_VOLT, this->resp_bytes_, 1); + this->pps_mode_binary_sensor_->publish_state(this->resp_bytes_[0] != 0); + } + + if (this->sink_attached_binary_sensor_ != nullptr) { + this->parent_->read_register(PWRMAN_CHARGER_REGISTER_SINK_ATTACHED, this->resp_bytes_, 1); + this->sink_attached_binary_sensor_->publish_state(this->resp_bytes_[0]); + } +} + +void PwrmanChargerBinarySensor::dump_config() { + ESP_LOGCONFIG(TAG, "PwrmanCharger Sensor:"); + LOG_BINARY_SENSOR(" ", "Cable5ACapableBinarySensor", this->cable_5a_capable_binary_sensor_); + LOG_BINARY_SENSOR(" ", "OtwThreshold1BinarySensor", this->otw_threshold_1_binary_sensor_); + LOG_BINARY_SENSOR(" ", "OtwThreshold2BinarySensor", this->otw_threshold_2_binary_sensor_); + LOG_BINARY_SENSOR(" ", "PpsModeBinarySensor", this->pps_mode_binary_sensor_); + LOG_BINARY_SENSOR(" ", "SinkAttachedBinarySensor", this->sink_attached_binary_sensor_); +} + +} // namespace pwrman_charger +} // namespace esphome diff --git a/esphome/components/pwrman_charger/binary_sensor/pwrman_charger_binary_sensor.h b/esphome/components/pwrman_charger/binary_sensor/pwrman_charger_binary_sensor.h new file mode 100644 index 0000000..d9d6cbf --- /dev/null +++ b/esphome/components/pwrman_charger/binary_sensor/pwrman_charger_binary_sensor.h @@ -0,0 +1,66 @@ +#pragma once + +#include "../pwrman_charger.h" +#include "esphome/components/binary_sensor/binary_sensor.h" +#include "esphome/core/defines.h" +#include "esphome/core/component.h" + +namespace esphome { +namespace pwrman_charger { + +/// This class includes i2c support for the Power Manifold Charger Module. +/// This hot-swappable module can be remotely managed and monitored via +/// I2C and is capable of up to 100W charging. This class is for the +/// binary sensor configuration. +class PwrmanChargerBinarySensor : public PollingComponent { + public: + PwrmanChargerBinarySensor(PwrmanCharger *parent) : parent_(parent) {} + + /** Sets the binary sensor indicating if a 5A-capable cable is attached. */ + void set_cable_5a_capable_binary_sensor(binary_sensor::BinarySensor *binary_sensor) { + this->cable_5a_capable_binary_sensor_ = binary_sensor; + } + + /** Sets the binary sensor indicating if the otw threshold 1 has been reached. */ + void set_otw_threshold_1_binary_sensor(binary_sensor::BinarySensor *binary_sensor) { + this->otw_threshold_1_binary_sensor_ = binary_sensor; + } + + /** Sets the binary sensor indicating if the otw threshold 2 has been reached. */ + void set_otw_threshold_2_binary_sensor(binary_sensor::BinarySensor *binary_sensor) { + this->otw_threshold_2_binary_sensor_ = binary_sensor; + } + + /** Sets the binary sensor indicating if the selected PDO uses PPS. */ + void set_pps_mode_binary_sensor(binary_sensor::BinarySensor *binary_sensor) { + this->pps_mode_binary_sensor_ = binary_sensor; + } + + /** Sets the binary sensor indicating if a sink is attached. */ + void set_sink_attached_binary_sensor(binary_sensor::BinarySensor *binary_sensor) { + this->sink_attached_binary_sensor_ = binary_sensor; + } + + /** Used by ESPHome framework. */ + void dump_config() override; + /** Used by ESPHome framework. */ + void update() override; + /** Used by ESPHome framework. */ + void setup() override; + /** Used by ESPHome framework. */ + float get_setup_priority() const override; + + protected: + PwrmanCharger *parent_; + + uint8_t resp_bytes_[1] = {0}; + + binary_sensor::BinarySensor *cable_5a_capable_binary_sensor_{nullptr}; + binary_sensor::BinarySensor *otw_threshold_1_binary_sensor_{nullptr}; + binary_sensor::BinarySensor *otw_threshold_2_binary_sensor_{nullptr}; + binary_sensor::BinarySensor *pps_mode_binary_sensor_{nullptr}; + binary_sensor::BinarySensor *sink_attached_binary_sensor_{nullptr}; +}; + +} // namespace pwrman_charger +} // namespace esphome diff --git a/esphome/components/pwrman_charger/button/__init__.py b/esphome/components/pwrman_charger/button/__init__.py new file mode 100644 index 0000000..8fb29b1 --- /dev/null +++ b/esphome/components/pwrman_charger/button/__init__.py @@ -0,0 +1,45 @@ +import esphome.codegen as cg +from esphome.components import button +import esphome.config_validation as cv +from esphome.const import ( + DEVICE_CLASS_RESTART, + ENTITY_CATEGORY_DIAGNOSTIC, + ENTITY_CATEGORY_CONFIG, + ICON_RESTART, +) +from .. import CONF_PWRMAN_CHARGER_ID, PwrmanCharger, pwrman_charger_ns + +HardResetButton = pwrman_charger_ns.class_("HardResetButton", button.Button) +SrcCapButton = pwrman_charger_ns.class_("SrcCapButton", button.Button) + +CONF_SEND_HARD_RESET = "send_hard_reset" +CONF_SEND_SRC_CAP = "send_src_cap" + +ICON_DATABASE_EXPORT = "mdi:database-export" + +CONFIG_SCHEMA = { + cv.GenerateID(CONF_PWRMAN_CHARGER_ID): cv.use_id(PwrmanCharger), + cv.Optional(CONF_SEND_HARD_RESET): button.button_schema( + HardResetButton, + device_class=DEVICE_CLASS_RESTART, + entity_category=ENTITY_CATEGORY_CONFIG, + icon=ICON_RESTART, + ), + cv.Optional(CONF_SEND_SRC_CAP): button.button_schema( + SrcCapButton, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + icon=ICON_DATABASE_EXPORT, + ), +} + + +async def to_code(config): + paren = await cg.get_variable(config[CONF_PWRMAN_CHARGER_ID]) + if hard_reset_config := config.get(CONF_SEND_HARD_RESET): + b = await button.new_button(hard_reset_config) + await cg.register_parented(b, config[CONF_PWRMAN_CHARGER_ID]) + cg.add(paren.set_hard_reset_button(b)) + if src_cap_config := config.get(CONF_SEND_SRC_CAP): + b = await button.new_button(src_cap_config) + await cg.register_parented(b, config[CONF_PWRMAN_CHARGER_ID]) + cg.add(paren.set_src_cap_button(b)) diff --git a/esphome/components/pwrman_charger/button/hard_reset_button.cpp b/esphome/components/pwrman_charger/button/hard_reset_button.cpp new file mode 100644 index 0000000..0809d17 --- /dev/null +++ b/esphome/components/pwrman_charger/button/hard_reset_button.cpp @@ -0,0 +1,9 @@ +#include "hard_reset_button.h" + +namespace esphome { +namespace pwrman_charger { + +void HardResetButton::press_action() { this->parent_->send_hard_reset(); } + +} // namespace pwrman_charger +} // namespace esphome \ No newline at end of file diff --git a/esphome/components/pwrman_charger/button/hard_reset_button.h b/esphome/components/pwrman_charger/button/hard_reset_button.h new file mode 100644 index 0000000..ed1eb8b --- /dev/null +++ b/esphome/components/pwrman_charger/button/hard_reset_button.h @@ -0,0 +1,18 @@ +#pragma once + +#include "esphome/components/button/button.h" +#include "../pwrman_charger.h" + +namespace esphome { +namespace pwrman_charger { + +class HardResetButton : public button::Button, public Parented { + public: + HardResetButton() = default; + + protected: + void press_action() override; +}; + +} // namespace pwrman_charger +} // namespace esphome \ No newline at end of file diff --git a/esphome/components/pwrman_charger/button/src_cap_button.cpp b/esphome/components/pwrman_charger/button/src_cap_button.cpp new file mode 100644 index 0000000..9655d64 --- /dev/null +++ b/esphome/components/pwrman_charger/button/src_cap_button.cpp @@ -0,0 +1,9 @@ +#include "src_cap_button.h" + +namespace esphome { +namespace pwrman_charger { + +void SrcCapButton::press_action() { this->parent_->send_src_cap(); } + +} // namespace pwrman_charger +} // namespace esphome \ No newline at end of file diff --git a/esphome/components/pwrman_charger/button/src_cap_button.h b/esphome/components/pwrman_charger/button/src_cap_button.h new file mode 100644 index 0000000..67c8c05 --- /dev/null +++ b/esphome/components/pwrman_charger/button/src_cap_button.h @@ -0,0 +1,18 @@ +#pragma once + +#include "esphome/components/button/button.h" +#include "../pwrman_charger.h" + +namespace esphome { +namespace pwrman_charger { + +class SrcCapButton : public button::Button, public Parented { + public: + SrcCapButton() = default; + + protected: + void press_action() override; +}; + +} // namespace pwrman_charger +} // namespace esphome \ No newline at end of file diff --git a/esphome/components/pwrman_charger/light/__init__.py b/esphome/components/pwrman_charger/light/__init__.py new file mode 100644 index 0000000..97acf76 --- /dev/null +++ b/esphome/components/pwrman_charger/light/__init__.py @@ -0,0 +1,29 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import light +from esphome.const import CONF_OUTPUT_ID +from .. import CONF_PWRMAN_CHARGER_ID, PWRMAN_CHARGER_SCHEMA, pwrman_charger_ns + +PwrmanChargerLightOutput = pwrman_charger_ns.class_( + "PwrmanChargerLight", light.LightOutput, cg.Component +) + +CONFIG_SCHEMA = ( + light.RGB_LIGHT_SCHEMA.extend( + { + cv.GenerateID(CONF_PWRMAN_CHARGER_ID): cv.declare_id( + PwrmanChargerLightOutput + ), + } + ) + .extend(PWRMAN_CHARGER_SCHEMA) + .extend(cv.COMPONENT_SCHEMA) +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_PWRMAN_CHARGER_ID]) + await light.register_light(var, config) + + # out = await cg.get_variable(config[CONF_OUTPUT]) + # cg.add(var.set_output(out)) diff --git a/esphome/components/pwrman_charger/light/pwrman_charger_light.cpp b/esphome/components/pwrman_charger/light/pwrman_charger_light.cpp new file mode 100644 index 0000000..e69de29 diff --git a/esphome/components/pwrman_charger/light/pwrman_charger_light.h b/esphome/components/pwrman_charger/light/pwrman_charger_light.h new file mode 100644 index 0000000..e69de29 diff --git a/esphome/components/pwrman_charger/pwrman_charger.cpp b/esphome/components/pwrman_charger/pwrman_charger.cpp new file mode 100644 index 0000000..8cde4f3 --- /dev/null +++ b/esphome/components/pwrman_charger/pwrman_charger.cpp @@ -0,0 +1,47 @@ +#include "pwrman_charger.h" + +#include "esphome/components/i2c/i2c.h" +#include "esphome/core/component.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace pwrman_charger { + +static const char *TAG = "pwrman_charger.component"; + +static const uint8_t PWRMAN_CHARGER_CHIP_ID = 0x42; // Pwrman Charger device id + +float PwrmanCharger::get_setup_priority() const { return setup_priority::DATA; } + +void PwrmanCharger::setup() { + ESP_LOGCONFIG(TAG, "Setting up Pwrman Charger..."); + + // Make sure we're talking to the right chip + uint8_t chip_id = reg(PWRMAN_CHARGER_REGISTER_DEV_ID).get(); + if (chip_id != PWRMAN_CHARGER_DEV_ID) { + ESP_LOGE(TAG, "Wrong chip ID %02X", chip_id); + this->mark_failed(); + return; + } +} + +void PwrmanCharger::send_hard_reset() { + const uint8_t data[1] = {1}; + this->write_register(PWRMAN_CHARGER_REGISTER_SEND_HARD_RESET, data, 1); +} + +void PwrmanCharger::send_src_cap() { + const uint8_t data[1] = {1}; + this->write_register(PWRMAN_CHARGER_REGISTER_SEND_SRC_CAP, data, 1); +} + +void PwrmanCharger::dump_config() { + ESP_LOGCONFIG(TAG, "Pwrman Charger component:"); + LOG_I2C_DEVICE(this); + if (this->is_failed()) { + ESP_LOGE(TAG, "Communication with Pwrman Charger failed!"); + } +} + +} // namespace pwrman_charger +} // namespace esphome \ No newline at end of file diff --git a/esphome/components/pwrman_charger/pwrman_charger.h b/esphome/components/pwrman_charger/pwrman_charger.h new file mode 100644 index 0000000..2e96721 --- /dev/null +++ b/esphome/components/pwrman_charger/pwrman_charger.h @@ -0,0 +1,106 @@ +#pragma once +#include "esphome/core/component.h" +#include "esphome/core/defines.h" +#ifdef USE_BUTTON +#include "esphome/components/button/button.h" +#endif +#ifdef USE_SWITCH +#include "esphome/components/switch/switch.h" +#endif +#include "esphome/components/i2c/i2c.h" + +namespace esphome { +namespace pwrman_charger { + +#define CHECK_BIT(var, pos) (((var) >> (pos)) & 1) + +enum PwrmanChargerPixelEffect : uint8_t { + PWRMAN_CHARGER_PIXEL_EFFECT_SOLID, + PWRMAN_CHARGER_PIXEL_EFFECT_PULSE, + PWRMAN_CHARGER_PIXEL_EFFECT_BLINK, + PWRMAN_CHARGER_PIXEL_EFFECT_RAINBOW, +}; + +enum PwrmanChargerPdoType : uint8_t { + PWRMAN_CHARGER_PDO_TYPE_FIXED = 0x00, + PWRMAN_CHARGER_PDO_TYPE_PPS = 0x01, +}; + +static const uint8_t PWRMAN_CHARGER_DEV_ID = 0x42; + +// Read-write registers +static const uint8_t PWRMAN_CHARGER_REGISTER_PIXEL_EFFECT = 0x00; +static const uint8_t PWRMAN_CHARGER_REGISTER_PIXEL_R = 0x01; +static const uint8_t PWRMAN_CHARGER_REGISTER_PIXEL_G = 0x02; +static const uint8_t PWRMAN_CHARGER_REGISTER_PIXEL_B = 0x03; +static const uint8_t PWRMAN_CHARGER_REGISTER_PIXEL_W = 0x04; +static const uint8_t PWRMAN_CHARGER_REGISTER_ENABLE = 0x05; +static const uint8_t PWRMAN_CHARGER_REGISTER_MAX_CURRENT = 0x06; +static const uint8_t PWRMAN_CHARGER_REGISTER_ENABLE_12V_PDO = 0x07; +static const uint8_t PWRMAN_CHARGER_REGISTER_SEND_SRC_CAP = 0x08; +static const uint8_t PWRMAN_CHARGER_REGISTER_SEND_HARD_RESET = 0x09; + +// Read-only registers +static const uint8_t PWRMAN_CHARGER_REGISTER_DEV_ID = 0x10; +static const uint8_t PWRMAN_CHARGER_REGISTER_IOUT = 0x11; +static const uint8_t PWRMAN_CHARGER_REGISTER_VOUT = 0x13; +static const uint8_t PWRMAN_CHARGER_REGISTER_VIN = 0x15; +static const uint8_t PWRMAN_CHARGER_REGISTER_CUR_PDO_NUM = 0x17; +static const uint8_t PWRMAN_CHARGER_REGISTER_CUR_PDO_MIN_VOLT = 0x18; +static const uint8_t PWRMAN_CHARGER_REGISTER_CUR_PDO_VOLT = 0x19; +static const uint8_t PWRMAN_CHARGER_REGISTER_CUR_PDO_AMP = 0x1A; +static const uint8_t PWRMAN_CHARGER_REGISTER_FAULT = 0x1B; +static const uint8_t PWRMAN_CHARGER_REGISTER_SINK_ATTACHED = 0x1C; +static const uint8_t PWRMAN_CHARGER_REGISTER_CONTRACT_POWER = 0x1D; +static const uint8_t PWRMAN_CHARGER_REGISTER_MISMATCH_FLAG = 0x1E; +static const uint8_t PWRMAN_CHARGER_REGISTER_GIVEBACK_FLAG = 0x1F; +static const uint8_t PWRMAN_CHARGER_REGISTER_CABLE_5A_FLAG = 0x20; +static const uint8_t PWRMAN_CHARGER_REGISTER_MAX_REQ_CUR = 0x21; +static const uint8_t PWRMAN_CHARGER_REGISTER_OTP_ID = 0x22; +static const uint8_t PWRMAN_CHARGER_REGISTER_OTP_SW_REV = 0x23; + +/// This class includes i2c support for the Power Manifold Charger Module. +/// This hot-swappable module can be remotely managed and monitored via +/// I2C and is capable of up to 100W charging. This class is for the +/// central configuration. +class PwrmanCharger : public i2c::I2CDevice, public PollingComponent { +#ifdef USE_SWITCH + protected: + switch_::Switch *enable_12v_switch_{nullptr}; + switch_::Switch *power_switch_{nullptr}; + + public: + void set_enable_12v_switch(switch_::Switch *s) { this->enable_12v_switch_ = s; } + void set_power_switch(switch_::Switch *s) { this->power_switch_ = s; } +#endif + + /** Sends a command to the USB controller to perform a hard reset of the port. */ + void send_hard_reset(); + + /** Sends a command to the USB controller to send a SRC_CAP message to the sink. */ + void send_src_cap(); + + /** Sets the button for sending a hard reset message to the sink. */ + void set_hard_reset_button(button::Button *button) { this->hard_reset_button_ = button; } + + /** Sets the button for sending a SRC_CAP message to the sink. */ + void set_src_cap_button(button::Button *button) { this->src_cap_button_ = button; } + + /** Used by ESPHome framework. */ + void setup() override; + /** Used by ESPHome framework. */ + void dump_config() override; + /** Used by ESPHome framework. */ + float get_setup_priority() const override; + + protected: + i2c::ErrorCode last_error_; + + const uint8_t *pixel_effect_; + + button::Button *hard_reset_button_{nullptr}; + button::Button *src_cap_button_{nullptr}; +}; + +} // namespace pwrman_charger +} // namespace esphome \ No newline at end of file diff --git a/esphome/components/pwrman_charger/sensor/__init__.py b/esphome/components/pwrman_charger/sensor/__init__.py new file mode 100644 index 0000000..baf478d --- /dev/null +++ b/esphome/components/pwrman_charger/sensor/__init__.py @@ -0,0 +1,123 @@ +import esphome.codegen as cg +from esphome.components import sensor +import esphome.config_validation as cv +from esphome.const import ( + CONF_ID, + DEVICE_CLASS_CURRENT, + DEVICE_CLASS_POWER, + DEVICE_CLASS_VOLTAGE, + ENTITY_CATEGORY_NONE, + ENTITY_CATEGORY_DIAGNOSTIC, + ICON_FLASH, + STATE_CLASS_MEASUREMENT, + UNIT_AMPERE, + UNIT_VOLT, + UNIT_WATT, +) +from .. import ( + CONF_PWRMAN_CHARGER_ID, + PWRMAN_CHARGER_SCHEMA, + pwrman_charger_ns, +) + +CODEOWNERS = ["@mikesmitty"] +DEPENDENCIES = ["pwrman_charger"] + +CONF_CONTRACT_POWER = "contract_power" +CONF_INPUT_VOLTAGE = "input_voltage" +CONF_OUTPUT_CURRENT = "output_current" +CONF_OUTPUT_VOLTAGE = "output_voltage" +CONF_PDO_MAX_CURRENT = "pdo_max_current" +CONF_PDO_MIN_VOLTAGE = "pdo_min_voltage" +CONF_PDO_VOLTAGE = "pdo_voltage" + +ICON_CURRENT_DC = "mdi:current-dc" +ICON_NUMERIC = "mdi:numeric" +ICON_POWER_PLUG = "mdi:power-plug" + +PwrmanChargerSensor = pwrman_charger_ns.class_( + "PwrmanChargerSensor", cg.PollingComponent +) + +CONFIG_SCHEMA = PWRMAN_CHARGER_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(PwrmanChargerSensor), + cv.Optional(CONF_CONTRACT_POWER): sensor.sensor_schema( + device_class=DEVICE_CLASS_POWER, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + state_class=STATE_CLASS_MEASUREMENT, + unit_of_measurement=UNIT_WATT, + icon=ICON_POWER_PLUG, + ), + cv.Optional(CONF_OUTPUT_CURRENT): sensor.sensor_schema( + device_class=DEVICE_CLASS_CURRENT, + entity_category=ENTITY_CATEGORY_NONE, + state_class=STATE_CLASS_MEASUREMENT, + unit_of_measurement=UNIT_AMPERE, + icon=ICON_CURRENT_DC, + ), + cv.Optional(CONF_INPUT_VOLTAGE): sensor.sensor_schema( + device_class=DEVICE_CLASS_VOLTAGE, + entity_category=ENTITY_CATEGORY_NONE, + state_class=STATE_CLASS_MEASUREMENT, + unit_of_measurement=UNIT_VOLT, + icon=ICON_FLASH, + ), + cv.Optional(CONF_OUTPUT_VOLTAGE): sensor.sensor_schema( + device_class=DEVICE_CLASS_VOLTAGE, + entity_category=ENTITY_CATEGORY_NONE, + state_class=STATE_CLASS_MEASUREMENT, + unit_of_measurement=UNIT_VOLT, + icon=ICON_FLASH, + ), + cv.Optional(CONF_PDO_MAX_CURRENT): sensor.sensor_schema( + device_class=DEVICE_CLASS_CURRENT, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + state_class=STATE_CLASS_MEASUREMENT, + unit_of_measurement=UNIT_AMPERE, + icon=ICON_CURRENT_DC, + ), + cv.Optional(CONF_PDO_MIN_VOLTAGE): sensor.sensor_schema( + device_class=DEVICE_CLASS_VOLTAGE, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + state_class=STATE_CLASS_MEASUREMENT, + unit_of_measurement=UNIT_VOLT, + icon=ICON_FLASH, + ), + cv.Optional(CONF_PDO_VOLTAGE): sensor.sensor_schema( + device_class=DEVICE_CLASS_VOLTAGE, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + state_class=STATE_CLASS_MEASUREMENT, + unit_of_measurement=UNIT_VOLT, + icon=ICON_FLASH, + ), + } +).extend(cv.polling_component_schema("60s")) + + +async def to_code(config): + paren = await cg.get_variable(config[CONF_PWRMAN_CHARGER_ID]) + var = cg.new_Pvariable(config[CONF_ID], paren) + await cg.register_component(var, config) + + if contract_power_config := config.get(CONF_CONTRACT_POWER): + sens = await sensor.new_sensor(contract_power_config) + cg.add(var.set_contract_power_sensor(sens)) + if input_voltage_config := config.get(CONF_INPUT_VOLTAGE): + sens = await sensor.new_sensor(input_voltage_config) + cg.add(var.set_input_voltage_sensor(sens)) + if output_current_config := config.get(CONF_OUTPUT_CURRENT): + sens = await sensor.new_sensor(output_current_config) + cg.add(var.set_output_current_sensor(sens)) + if output_voltage_config := config.get(CONF_OUTPUT_VOLTAGE): + sens = await sensor.new_sensor(output_voltage_config) + cg.add(var.set_output_voltage_sensor(sens)) + if pdo_max_current_config := config.get(CONF_PDO_MAX_CURRENT): + sens = await sensor.new_sensor(pdo_max_current_config) + cg.add(var.set_pdo_max_current_sensor(sens)) + if pdo_min_voltage_config := config.get(CONF_PDO_MIN_VOLTAGE): + sens = await sensor.new_sensor(pdo_min_voltage_config) + cg.add(var.set_pdo_min_voltage_sensor(sens)) + if pdo_voltage_config := config.get(CONF_PDO_VOLTAGE): + sens = await sensor.new_sensor(pdo_voltage_config) + cg.add(var.set_pdo_voltage_sensor(sens)) diff --git a/esphome/components/pwrman_charger/sensor/pwrman_charger_sensor.cpp b/esphome/components/pwrman_charger/sensor/pwrman_charger_sensor.cpp new file mode 100644 index 0000000..893aa8d --- /dev/null +++ b/esphome/components/pwrman_charger/sensor/pwrman_charger_sensor.cpp @@ -0,0 +1,91 @@ +#include "pwrman_charger_sensor.h" + +#include "esphome/components/i2c/i2c.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/core/component.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace pwrman_charger { + +static const char *TAG = "pwrman_charger.sensor"; + +float PwrmanChargerSensor::get_setup_priority() const { return setup_priority::DATA; } + +void PwrmanChargerSensor::setup() { ESP_LOGCONFIG(TAG, "Setting up PwrmanChargerSensor..."); } + +void PwrmanChargerSensor::update() { + // Get 8-bit value from CONTRACT_POWER register and convert from 0.5W units to W + if (this->contract_power_sensor_ != nullptr) { + this->parent_->read_register(PWRMAN_CHARGER_REGISTER_CONTRACT_POWER, this->resp_bytes_, 1); + this->contract_power_sensor_->publish_state(this->resp_bytes_[0] * 0.5); + } + + // Get 16-bit value from IOUT register and convert from 10mA units to A + if (this->output_current_sensor_ != nullptr) { + this->parent_->read_register(PWRMAN_CHARGER_REGISTER_IOUT, this->resp_bytes_, 2); + this->output_current_sensor_->publish_state((this->resp_bytes_[0] << 8 | this->resp_bytes_[1]) * 0.01); + } + + // Get 16-bit value from VOUT register and convert from 10mV units to V + if (this->output_voltage_sensor_ != nullptr) { + this->parent_->read_register(PWRMAN_CHARGER_REGISTER_VOUT, this->resp_bytes_, 2); + this->output_voltage_sensor_->publish_state((this->resp_bytes_[0] << 8 | this->resp_bytes_[1]) * 0.01); + } + + // Get 16-bit value from VIN register and convert from 10mV units to V + if (this->input_voltage_sensor_ != nullptr) { + this->parent_->read_register(PWRMAN_CHARGER_REGISTER_VIN, this->resp_bytes_, 2); + this->input_voltage_sensor_->publish_state((this->resp_bytes_[0] << 8 | this->resp_bytes_[1]) * 0.01); + } + + // Check if the selected PDO has changed + this->parent_->read_register(PWRMAN_CHARGER_REGISTER_CUR_PDO_NUM, this->resp_bytes_, 1); + uint8_t selected_pdo = this->resp_bytes_[0]; + + if (selected_pdo != this->selected_pdo_) { + this->selected_pdo_ = selected_pdo; + + if (selected_pdo == 0) { + if (this->pdo_max_current_sensor_ != nullptr) { + this->pdo_max_current_sensor_->publish_state(NAN); + } + if (this->pdo_min_voltage_sensor_ != nullptr) { + this->pdo_min_voltage_sensor_->publish_state(NAN); + } + if (this->pdo_voltage_sensor_ != nullptr) { + this->pdo_voltage_sensor_->publish_state(NAN); + } + } + + // Get current PDO's max current in 50mA units + if (this->pdo_max_current_sensor_ != nullptr && selected_pdo != 0) { + this->parent_->read_register(PWRMAN_CHARGER_REGISTER_CUR_PDO_AMP, this->resp_bytes_, 1); + this->pdo_max_current_sensor_->publish_state(this->resp_bytes_[0] * 0.05); + } + // Get current PDO's min voltage in 100mV units (PPS only) + if (this->pdo_min_voltage_sensor_ != nullptr && selected_pdo != 0) { + this->parent_->read_register(PWRMAN_CHARGER_REGISTER_CUR_PDO_MIN_VOLT, this->resp_bytes_, 1); + this->pdo_min_voltage_sensor_->publish_state(this->resp_bytes_[0] * 0.1); + } + // Get current PDO's voltage in 100mV units + if (this->pdo_voltage_sensor_ != nullptr && selected_pdo != 0) { + this->parent_->read_register(PWRMAN_CHARGER_REGISTER_CUR_PDO_VOLT, this->resp_bytes_, 1); + this->pdo_voltage_sensor_->publish_state(this->resp_bytes_[0] * 0.1); + } + } +} + +void PwrmanChargerSensor::dump_config() { + ESP_LOGCONFIG(TAG, "pwrman_charger Sensor:"); + LOG_SENSOR(" ", "ContractPowerSensor", this->contract_power_sensor_); + LOG_SENSOR(" ", "InputVoltageSensor", this->input_voltage_sensor_); + LOG_SENSOR(" ", "OutputCurrentSensor", this->output_current_sensor_); + LOG_SENSOR(" ", "OutputVoltageSensor", this->output_voltage_sensor_); + LOG_SENSOR(" ", "PdoMaxCurrentSensor", this->pdo_max_current_sensor_); + LOG_SENSOR(" ", "PdoMinVoltageSensor", this->pdo_min_voltage_sensor_); + LOG_SENSOR(" ", "PdoVoltageSensor", this->pdo_voltage_sensor_); +} + +} // namespace pwrman_charger +} // namespace esphome diff --git a/esphome/components/pwrman_charger/sensor/pwrman_charger_sensor.h b/esphome/components/pwrman_charger/sensor/pwrman_charger_sensor.h new file mode 100644 index 0000000..15b8185 --- /dev/null +++ b/esphome/components/pwrman_charger/sensor/pwrman_charger_sensor.h @@ -0,0 +1,66 @@ +#pragma once + +#include "../pwrman_charger.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/core/defines.h" +#include "esphome/core/component.h" + +namespace esphome { +namespace pwrman_charger { + +/// This class includes i2c support for the Power Manifold Charger module. +/// The device has 7 configurable PDOs, 2 GPIOs with several functions +/// and is capable of up to 100W charging. This class is for the +/// PwrmanCharger configuration. +class PwrmanChargerSensor : public PollingComponent { + public: + PwrmanChargerSensor(PwrmanCharger *parent) : parent_(parent) {} + + /** Sets the sensor that will report the contract power negotiated with the sink. */ + void set_contract_power_sensor(sensor::Sensor *sensor) { this->contract_power_sensor_ = sensor; } + + /** Sets the sensor that will report the input voltage to the charger module. */ + void set_input_voltage_sensor(sensor::Sensor *sensor) { this->input_voltage_sensor_ = sensor; } + + /** Sets the sensor that will report the USB output current from the module. */ + void set_output_current_sensor(sensor::Sensor *sensor) { this->output_current_sensor_ = sensor; } + + /** Sets the sensor that will report the USB output voltage from the module. */ + void set_output_voltage_sensor(sensor::Sensor *sensor) { this->output_voltage_sensor_ = sensor; } + + /** Sets the sensor that will report the selected PDO's configured max current. */ + void set_pdo_max_current_sensor(sensor::Sensor *sensor) { this->pdo_max_current_sensor_ = sensor; } + + /** Sets the sensor that will report the selected PDO's configured minimum voltage. */ + void set_pdo_min_voltage_sensor(sensor::Sensor *sensor) { this->pdo_min_voltage_sensor_ = sensor; } + + /** Sets the sensor that will report the selected PDO's configured voltage. */ + void set_pdo_voltage_sensor(sensor::Sensor *sensor) { this->pdo_voltage_sensor_ = sensor; } + + /** Used by ESPHome framework. */ + void dump_config() override; + /** Used by ESPHome framework. */ + void setup() override; + /** Used by ESPHome framework. */ + void update() override; + /** Used by ESPHome framework. */ + float get_setup_priority() const override; + + protected: + PwrmanCharger *parent_; + + float selected_pdo_; + + uint8_t resp_bytes_[2] = {0, 0}; + + sensor::Sensor *contract_power_sensor_{nullptr}; + sensor::Sensor *input_voltage_sensor_{nullptr}; + sensor::Sensor *output_current_sensor_{nullptr}; + sensor::Sensor *output_voltage_sensor_{nullptr}; + sensor::Sensor *pdo_max_current_sensor_{nullptr}; + sensor::Sensor *pdo_min_voltage_sensor_{nullptr}; + sensor::Sensor *pdo_voltage_sensor_{nullptr}; +}; + +} // namespace pwrman_charger +} // namespace esphome \ No newline at end of file diff --git a/esphome/components/pwrman_charger/switch/__init__.py b/esphome/components/pwrman_charger/switch/__init__.py new file mode 100644 index 0000000..10e606c --- /dev/null +++ b/esphome/components/pwrman_charger/switch/__init__.py @@ -0,0 +1,55 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import switch +from esphome.const import ( + CONF_POWER, + DEVICE_CLASS_OUTLET, + DEVICE_CLASS_SWITCH, + ENTITY_CATEGORY_CONFIG, +) +from .. import ( + CONF_PWRMAN_CHARGER_ID, + PWRMAN_CHARGER_SCHEMA, + pwrman_charger_ns, +) + +CODEOWNERS = ["@mikesmitty"] +DEPENDENCIES = ["pwrman_charger"] + +CONF_ENABLE_12V = "enable_12v" + +PwrmanChargerSwitch = pwrman_charger_ns.class_( + "PwrmanChargerSwitch", switch.Switch, cg.Component +) +PwrmanCharger12vSwitch = pwrman_charger_ns.class_( + "PwrmanCharger12vSwitch", switch.Switch, cg.Component +) + +CONFIG_SCHEMA = PWRMAN_CHARGER_SCHEMA.extend( + { + cv.Optional(CONF_POWER): switch.switch_schema( + PwrmanChargerSwitch, + block_inverted=True, + device_class=DEVICE_CLASS_OUTLET, + entity_category=ENTITY_CATEGORY_CONFIG, + ), + cv.Optional(CONF_ENABLE_12V): switch.switch_schema( + PwrmanCharger12vSwitch, + block_inverted=True, + device_class=DEVICE_CLASS_SWITCH, + entity_category=ENTITY_CATEGORY_CONFIG, + ), + } +).extend(cv.COMPONENT_SCHEMA) + + +async def to_code(config): + pwrman_charger_component = await cg.get_variable(config[CONF_PWRMAN_CHARGER_ID]) + if power_config := config.get(CONF_POWER): + s = await switch.new_switch(power_config) + await cg.register_parented(s, config[CONF_PWRMAN_CHARGER_ID]) + cg.add(pwrman_charger_component.set_power_switch(s)) + if enable_12v_config := config.get(CONF_ENABLE_12V): + s = await switch.new_switch(enable_12v_config) + await cg.register_parented(s, config[CONF_PWRMAN_CHARGER_ID]) + cg.add(pwrman_charger_component.set_enable_12v_switch(s)) diff --git a/esphome/components/pwrman_charger/switch/pwrman_charger_12v_switch.cpp b/esphome/components/pwrman_charger/switch/pwrman_charger_12v_switch.cpp new file mode 100644 index 0000000..1a75221 --- /dev/null +++ b/esphome/components/pwrman_charger/switch/pwrman_charger_12v_switch.cpp @@ -0,0 +1,52 @@ +#include "pwrman_charger_12v_switch.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace pwrman_charger { + +static const char *const TAG = "pwrman_charger.switch"; + +float PwrmanCharger12vSwitch::get_setup_priority() const { return setup_priority::DATA; } +void PwrmanCharger12vSwitch::setup() { + ESP_LOGCONFIG(TAG, "Setting up PwrmanCharger 12v Switch..."); + + if (this->get_initial_state_with_restore_mode().has_value()) { + ESP_LOGCONFIG(TAG, "Restoring initial state: %d", this->get_initial_state_with_restore_mode().value()); // FIXME + this->enabled_ = this->get_initial_state_with_restore_mode().value(); + } else { + this->parent_->read_register(PWRMAN_CHARGER_REGISTER_ENABLE_12V_PDO, &this->enabled_, 1); + } + if (this->enabled_) { + this->turn_on(); + } else { + this->turn_off(); + } +} +void PwrmanCharger12vSwitch::dump_config() { + ESP_LOGCONFIG(TAG, "CANARY: PwrmanCharger12vSwitch"); // FIXME + LOG_SWITCH(TAG, "PwrmanCharger 12v Switch", this); +} +void PwrmanCharger12vSwitch::update() { + this->parent_->read_register(PWRMAN_CHARGER_REGISTER_ENABLE_12V_PDO, &this->enabled_, 1); + if (this->enabled_) { + this->turn_on(); + } else { + this->turn_off(); + } + /* + uint8_t enabled; + this->parent_->read_register(PWRMAN_CHARGER_REGISTER_ENABLE, &enabled, 1); + if (enabled != this->enabled_) { + this->enabled_ = enabled; + this->publish_state(enabled); + } + */ +} +void PwrmanCharger12vSwitch::write_state(bool state) { + this->enabled_ = static_cast(state); + this->parent_->write_register(PWRMAN_CHARGER_REGISTER_ENABLE, &this->enabled_, 1); + this->publish_state(state); +} + +} // namespace pwrman_charger +} // namespace esphome \ No newline at end of file diff --git a/esphome/components/pwrman_charger/switch/pwrman_charger_12v_switch.h b/esphome/components/pwrman_charger/switch/pwrman_charger_12v_switch.h new file mode 100644 index 0000000..748812a --- /dev/null +++ b/esphome/components/pwrman_charger/switch/pwrman_charger_12v_switch.h @@ -0,0 +1,31 @@ +#pragma once + +#include "../pwrman_charger.h" +#include "esphome/core/component.h" +#include "esphome/components/switch/switch.h" + +namespace esphome { +namespace pwrman_charger { + +class PwrmanCharger12vSwitch : public switch_::Switch, public PollingComponent { + public: + void set_parent(PwrmanCharger *parent) { this->parent_ = parent; } + + /** Used by ESPHome framework. */ + float get_setup_priority() const override; + /** Used by ESPHome framework. */ + void setup() override; + /** Used by ESPHome framework. */ + void update() override; + /** Used by ESPHome framework. */ + void dump_config() override; + + protected: + PwrmanCharger *parent_; + uint8_t enabled_; + + void write_state(bool state) override; +}; + +} // namespace pwrman_charger +} // namespace esphome \ No newline at end of file diff --git a/esphome/components/pwrman_charger/switch/pwrman_charger_switch.cpp b/esphome/components/pwrman_charger/switch/pwrman_charger_switch.cpp new file mode 100644 index 0000000..4abc005 --- /dev/null +++ b/esphome/components/pwrman_charger/switch/pwrman_charger_switch.cpp @@ -0,0 +1,52 @@ +#include "pwrman_charger_switch.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace pwrman_charger { + +static const char *const TAG = "pwrman_charger.switch"; + +float PwrmanChargerSwitch::get_setup_priority() const { return setup_priority::DATA; } +void PwrmanChargerSwitch::setup() { + ESP_LOGCONFIG(TAG, "Setting up PwrmanCharger Switch..."); + + if (this->get_initial_state_with_restore_mode().has_value()) { + ESP_LOGCONFIG(TAG, "Restoring initial state: %d", this->get_initial_state_with_restore_mode().value()); // FIXME + this->enabled_ = this->get_initial_state_with_restore_mode().value(); + } else { + this->parent_->read_register(PWRMAN_CHARGER_REGISTER_ENABLE, &this->enabled_, 1); + } + if (this->enabled_) { + this->turn_on(); + } else { + this->turn_off(); + } +} +void PwrmanChargerSwitch::dump_config() { + ESP_LOGCONFIG(TAG, "CANARY: PwrmanChargerSwitch"); // FIXME + LOG_SWITCH(TAG, "PwrmanCharger Switch", this); +} +void PwrmanChargerSwitch::update() { + this->parent_->read_register(PWRMAN_CHARGER_REGISTER_ENABLE, &this->enabled_, 1); + if (this->enabled_) { + this->turn_on(); + } else { + this->turn_off(); + } + /* + uint8_t enabled; + this->parent_->read_register(PWRMAN_CHARGER_REGISTER_ENABLE, &enabled, 1); + if (enabled != this->enabled_) { + this->enabled_ = enabled; + this->publish_state(enabled); + } + */ +} +void PwrmanChargerSwitch::write_state(bool state) { + this->enabled_ = static_cast(state); + this->parent_->write_register(PWRMAN_CHARGER_REGISTER_ENABLE, &this->enabled_, 1); + this->publish_state(state); +} + +} // namespace pwrman_charger +} // namespace esphome \ No newline at end of file diff --git a/esphome/components/pwrman_charger/switch/pwrman_charger_switch.h b/esphome/components/pwrman_charger/switch/pwrman_charger_switch.h new file mode 100644 index 0000000..653dba3 --- /dev/null +++ b/esphome/components/pwrman_charger/switch/pwrman_charger_switch.h @@ -0,0 +1,31 @@ +#pragma once + +#include "../pwrman_charger.h" +#include "esphome/core/component.h" +#include "esphome/components/switch/switch.h" + +namespace esphome { +namespace pwrman_charger { + +class PwrmanChargerSwitch : public switch_::Switch, public PollingComponent { + public: + void set_parent(PwrmanCharger *parent) { this->parent_ = parent; } + + /** Used by ESPHome framework. */ + float get_setup_priority() const override; + /** Used by ESPHome framework. */ + void setup() override; + /** Used by ESPHome framework. */ + void update() override; + /** Used by ESPHome framework. */ + void dump_config() override; + + protected: + PwrmanCharger *parent_; + uint8_t enabled_; + + void write_state(bool state) override; +}; + +} // namespace pwrman_charger +} // namespace esphome \ No newline at end of file