Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PI18 protocol support #62

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on: # yamllint disable-line rule:truthy
push:
branches:
- main
- pi18
pull_request:
schedule:
- cron: 0 12 * * *
Expand Down Expand Up @@ -36,16 +37,10 @@ jobs:
- name: Write secrets.yaml
shell: bash
run: 'echo -e "wifi_ssid: ssid\nwifi_password: password\nmqtt_host: host\nmqtt_username: username\nmqtt_password: password" > secrets.yaml'
- name: Write tests/secrets.yaml
shell: bash
run: 'echo -e "wifi_ssid: ssid\nwifi_password: password\nmqtt_host: host\nmqtt_username: username\nmqtt_password: password" > tests/secrets.yaml'
- run: |
esphome config esp32-example.yaml
- run: |
esphome config esp8266-example.yaml
esphome config tests/esp8266-test-ping.yaml
esphome config tests/esp8266-test-pong.yaml
esphome config tests/esp8266-test-protocols.yaml

esphome-compile:
runs-on: ubuntu-latest
Expand Down
32 changes: 32 additions & 0 deletions components/pipsolar/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.const import CONF_ID
from esphome.components import uart

DEPENDENCIES = ["uart"]
CODEOWNERS = ["@andreashergert1984"]
AUTO_LOAD = ["binary_sensor", "text_sensor", "sensor", "switch", "output"]
MULTI_CONF = True

CONF_PIPSOLAR_ID = "pipsolar_id"

pipsolar_ns = cg.esphome_ns.namespace("pipsolar")
PipsolarComponent = pipsolar_ns.class_("Pipsolar", cg.Component)

PIPSOLAR_COMPONENT_SCHEMA = cv.Schema(
{
cv.Required(CONF_PIPSOLAR_ID): cv.use_id(PipsolarComponent),
}
)

CONFIG_SCHEMA = cv.All(
cv.Schema({cv.GenerateID(): cv.declare_id(PipsolarComponent)})
.extend(cv.polling_component_schema("1s"))
.extend(uart.UART_DEVICE_SCHEMA)
)


def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config)
yield uart.register_uart_device(var, config)
168 changes: 168 additions & 0 deletions components/pipsolar/binary_sensor/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import binary_sensor

from .. import PIPSOLAR_COMPONENT_SCHEMA, CONF_PIPSOLAR_ID

DEPENDENCIES = ["uart"]

CONF_SETTING_VALUE_CONFIGURATION_STATE = "setting_value_configuration_state"
CONF_LOAD_CONNECTION = "load_connection"

CONF_ADD_SBU_PRIORITY_VERSION = "add_sbu_priority_version"
CONF_CONFIGURATION_STATUS = "configuration_status"
CONF_SCC_FIRMWARE_VERSION = "scc_firmware_version"
CONF_LOAD_STATUS = "load_status"
CONF_BATTERY_VOLTAGE_TO_STEADY_WHILE_CHARGING = (
"battery_voltage_to_steady_while_charging"
)
CONF_CHARGING_STATUS = "charging_status"
CONF_SCC_CHARGING_STATUS = "scc_charging_status"
CONF_AC_CHARGING_STATUS = "ac_charging_status"
CONF_CHARGING_TO_FLOATING_MODE = "charging_to_floating_mode"
CONF_SWITCH_ON = "switch_on"
CONF_DUSTPROOF_INSTALLED = "dustproof_installed"

CONF_SILENCE_BUZZER_OPEN_BUZZER = "silence_buzzer_open_buzzer"
CONF_OVERLOAD_BYPASS_FUNCTION = "overload_bypass_function"
CONF_LCD_ESCAPE_TO_DEFAULT = "lcd_escape_to_default"
CONF_OVERLOAD_RESTART_FUNCTION = "overload_restart_function"
CONF_OVER_TEMPERATURE_RESTART_FUNCTION = "over_temperature_restart_function"
CONF_BACKLIGHT_ON = "backlight_on"
CONF_ALARM_ON_WHEN_PRIMARY_SOURCE_INTERRUPT = "alarm_on_when_primary_source_interrupt"
CONF_FAULT_CODE_RECORD = "fault_code_record"
CONF_POWER_SAVING = "power_saving"

CONF_WARNING_LINE_FAIL = "warning_line_fail"
CONF_WARNING_OUTPUT_CIRCUIT_SHORT = "warning_output_circuit_short"
CONF_WARNING_OVER_TEMPERATURE = "warning_over_temperature"
CONF_WARNING_OVER_TEMPERATURE = "warning_over_temperature"
CONF_WARNING_FAN_LOCK = "warning_fan_lock"
CONF_WARNING_BATTERY_VOLTAGE_HIGH = "warning_battery_voltage_high"
CONF_WARNING_BATTERY_LOW_ALARM = "warning_battery_low_alarm"
CONF_WARNING_BATTERY_UNDER_SHUTDOWN = "warning_battery_under_shutdown"
CONF_WARNING_OVER_LOAD = "warning_over_load"
CONF_WARNING_EEPROM_FAILED = "warning_eeprom_failed"
CONF_WARNING_POWER_LIMIT = "warning_power_limit"
CONF_WARNING_PV1_VOLTAGE_HIGH = "warning_pv1_voltage_high"
CONF_WARNING_PV2_VOLTAGE_HIGH = "warning_pv2_voltage_high"
CONF_WARNING_MPPT1_OVERLOAD = "warning_mppt1_overload"
CONF_WARNING_MPPT2_OVERLOAD = "warning_mppt2_overload"
CONF_SCC1_BATTERY_TOO_LOW_TO_CHARGE = "scc1_battery_too_low_to_charge"
CONF_SCC2_BATTERY_TOO_LOW_TO_CHARGE = "scc2_battery_too_low_to_charge"

CONF_WARNINGS_PRESENT = "warnings_present"
CONF_FAULTS_PRESENT = "faults_present"
CONF_WARNING_POWER_LOSS = "warning_power_loss"
CONF_FAULT_INVERTER_FAULT = "fault_inverter_fault"
CONF_FAULT_BUS_OVER = "fault_bus_over"
CONF_FAULT_BUS_UNDER = "fault_bus_under"
CONF_FAULT_BUS_SOFT_FAIL = "fault_bus_soft_fail"
CONF_WARNING_LINE_FAIL = "warning_line_fail"
CONF_FAULT_OPVSHORT = "fault_opvshort"
CONF_FAULT_INVERTER_VOLTAGE_TOO_LOW = "fault_inverter_voltage_too_low"
CONF_FAULT_INVERTER_VOLTAGE_TOO_HIGH = "fault_inverter_voltage_too_high"

CONF_WARNING_BATTERY_DERATING = "warning_battery_derating"


CONF_FAULT_INVERTER_OVER_CURRENT = "fault_inverter_over_current"
CONF_FAULT_INVERTER_SOFT_FAILED = "fault_inverter_soft_failed"
CONF_FAULT_SELF_TEST_FAILED = "fault_self_test_failed"
CONF_FAULT_OP_DC_VOLTAGE_OVER = "fault_op_dc_voltage_over"
CONF_FAULT_BATTERY_OPEN = "fault_battery_open"
CONF_FAULT_CURRENT_SENSOR_FAILED = "fault_current_sensor_failed"
CONF_FAULT_BATTERY_SHORT = "fault_battery_short"

CONF_WARNING_PV_VOLTAGE_HIGH = "warning_pv_voltage_high"
CONF_FAULT_MPPT_OVERLOAD = "fault_mppt_overload"
CONF_WARNING_MPPT_OVERLOAD = "warning_mppt_overload"
CONF_WARNING_BATTERY_TOO_LOW_TO_CHARGE = "warning_battery_too_low_to_charge"
CONF_FAULT_DC_DC_OVER_CURRENT = "fault_dc_dc_over_current"
# CONF_FAULT_CODE = "fault_code"
CONF_WARNUNG_LOW_PV_ENERGY = "warnung_low_pv_energy"
CONF_WARNING_HIGH_AC_INPUT_DURING_BUS_SOFT_START = (
"warning_high_ac_input_during_bus_soft_start"
)
CONF_WARNING_BATTERY_EQUALIZATION = "warning_battery_equalization"

TYPES = [
CONF_SETTING_VALUE_CONFIGURATION_STATE,
CONF_LOAD_CONNECTION,
CONF_ADD_SBU_PRIORITY_VERSION,
CONF_CONFIGURATION_STATUS,
CONF_SCC_FIRMWARE_VERSION,
CONF_LOAD_STATUS,
CONF_BATTERY_VOLTAGE_TO_STEADY_WHILE_CHARGING,
CONF_CHARGING_STATUS,
CONF_SCC_CHARGING_STATUS,
CONF_AC_CHARGING_STATUS,
CONF_CHARGING_TO_FLOATING_MODE,
CONF_SWITCH_ON,
CONF_DUSTPROOF_INSTALLED,
CONF_SILENCE_BUZZER_OPEN_BUZZER,
CONF_OVERLOAD_BYPASS_FUNCTION,
CONF_LCD_ESCAPE_TO_DEFAULT,
CONF_OVERLOAD_RESTART_FUNCTION,
CONF_OVER_TEMPERATURE_RESTART_FUNCTION,
CONF_BACKLIGHT_ON,
CONF_ALARM_ON_WHEN_PRIMARY_SOURCE_INTERRUPT,
CONF_FAULT_CODE_RECORD,
CONF_POWER_SAVING,
CONF_WARNINGS_PRESENT,
CONF_FAULTS_PRESENT,
CONF_WARNING_POWER_LOSS,
CONF_FAULT_INVERTER_FAULT,
CONF_FAULT_BUS_OVER,
CONF_FAULT_BUS_UNDER,
CONF_FAULT_BUS_SOFT_FAIL,
CONF_WARNING_LINE_FAIL,
CONF_FAULT_OPVSHORT,
CONF_FAULT_INVERTER_VOLTAGE_TOO_LOW,
CONF_FAULT_INVERTER_VOLTAGE_TOO_HIGH,
CONF_WARNING_OVER_TEMPERATURE,
CONF_WARNING_FAN_LOCK,
CONF_WARNING_BATTERY_VOLTAGE_HIGH,
CONF_WARNING_BATTERY_LOW_ALARM,
CONF_WARNING_BATTERY_UNDER_SHUTDOWN,
CONF_WARNING_BATTERY_DERATING,
CONF_WARNING_OVER_LOAD,
CONF_WARNING_EEPROM_FAILED,
CONF_FAULT_INVERTER_OVER_CURRENT,
CONF_FAULT_INVERTER_SOFT_FAILED,
CONF_FAULT_SELF_TEST_FAILED,
CONF_FAULT_OP_DC_VOLTAGE_OVER,
CONF_FAULT_BATTERY_OPEN,
CONF_FAULT_CURRENT_SENSOR_FAILED,
CONF_FAULT_BATTERY_SHORT,
CONF_WARNING_POWER_LIMIT,
CONF_WARNING_PV_VOLTAGE_HIGH,
CONF_FAULT_MPPT_OVERLOAD,
CONF_WARNING_MPPT_OVERLOAD,
CONF_WARNING_BATTERY_TOO_LOW_TO_CHARGE,
CONF_FAULT_DC_DC_OVER_CURRENT,
# CONF_FAULT_CODE,
CONF_WARNUNG_LOW_PV_ENERGY,
CONF_WARNING_HIGH_AC_INPUT_DURING_BUS_SOFT_START,
CONF_WARNING_BATTERY_EQUALIZATION,
CONF_WARNING_OUTPUT_CIRCUIT_SHORT,
CONF_WARNING_PV1_VOLTAGE_HIGH,
CONF_WARNING_PV2_VOLTAGE_HIGH,
CONF_WARNING_MPPT1_OVERLOAD,
CONF_WARNING_MPPT2_OVERLOAD,
CONF_SCC1_BATTERY_TOO_LOW_TO_CHARGE,
CONF_SCC2_BATTERY_TOO_LOW_TO_CHARGE,
]

CONFIG_SCHEMA = PIPSOLAR_COMPONENT_SCHEMA.extend(
{cv.Optional(type): binary_sensor.binary_sensor_schema() for type in TYPES}
)


async def to_code(config):
paren = await cg.get_variable(config[CONF_PIPSOLAR_ID])
for type in TYPES:
if type in config:
conf = config[type]
var = await binary_sensor.new_binary_sensor(conf)
cg.add(getattr(paren, f"set_{type}")(var))
114 changes: 114 additions & 0 deletions components/pipsolar/output/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
from esphome.components import output
from esphome.const import CONF_ID, CONF_VALUE
from .. import PIPSOLAR_COMPONENT_SCHEMA, CONF_PIPSOLAR_ID, pipsolar_ns

DEPENDENCIES = ["pipsolar"]

PipsolarOutput = pipsolar_ns.class_("PipsolarOutput", output.FloatOutput)
SetOutputAction = pipsolar_ns.class_("SetOutputAction", automation.Action)

CONF_POSSIBLE_VALUES = "possible_values"

# 3.11 PCVV<nn.n><cr>: Setting battery C.V. (constant voltage) charging voltage 48.0V ~ 58.4V for 48V unit
# battery_bulk_voltage;
# battery_recharge_voltage; 12V unit: 11V/11.3V/11.5V/11.8V/12V/12.3V/12.5V/12.8V
# 24V unit: 22V/22.5V/23V/23.5V/24V/24.5V/25V/25.5V
# 48V unit: 44V/45V/46V/47V/48V/49V/50V/51V
# battery_under_voltage; 40.0V ~ 48.0V for 48V unit
# battery_float_voltage; 48.0V ~ 58.4V for 48V unit
# battery_type; 00 for AGM, 01 for Flooded battery
# current_max_ac_charging_current;
# output_source_priority; 00 / 01 / 02
# charger_source_priority; For HS: 00 for utility first, 01 for solar first, 02 for solar and utility, 03 for only solar charging
# For MS/MSX: 00 for utility first, 01 for solar first, 03 for only solar charging
# battery_redischarge_voltage; 12V unit: 00.0V12V/12.3V/12.5V/12.8V/13V/13.3V/13.5V/13.8V/14V/14.3V/14.5
# 24V unit: 00.0V/24V/24.5V/25V/25.5V/26V/26.5V/27V/27.5V/28V/28.5V/29V
# 48V unit: 00.0V48V/49V/50V/51V/52V/53V/54V/55V/56V/57V/58V

CONF_CURRENT_MAX_AC_CHARGING_CURRENT = "current_max_ac_charging_current"
CONF_CURRENT_MAX_CHARGING_CURRENT = "current_max_charging_current"

# CONF_BATTERY_RECHARGE_VOLTAGE = "battery_recharge_voltage"
# CONF_BATTERY_UNDER_VOLTAGE = "battery_under_voltage"
# CONF_BATTERY_FLOAT_VOLTAGE = "battery_float_voltage"
# CONF_BATTERY_TYPE = "battery_type"
#
# CONF_OUTPUT_SOURCE_PRIORITY = "output_source_priority"
# CONF_CHARGER_SOURCE_PRIORITY = "charger_source_priority"
# CONF_BATTERY_REDISCHARGE_VOLTAGE = "battery_redischarge_voltage"

TYPES = {
CONF_CURRENT_MAX_AC_CHARGING_CURRENT: (
[2, 10, 20, 30, 40, 50, 60, 70, 80, 90],
"^S013MUCHGC0,%02d",
),
CONF_CURRENT_MAX_CHARGING_CURRENT: (
[10, 20, 30, 40, 50, 60, 70, 80, 90],
"^S013MCHGC0,%02d",
),
# CONF_BATTERY_RECHARGE_VOLTAGE: (
# [440, 450, 460, 470, 480, 490, 500, 510],
# "PBCV%02.1f",
# ),
# CONF_BATTERY_REDISCHARGE_VOLTAGE: (
# [0, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58],
# "PBDV%02.1f",
# ),
# CONF_BATTERY_UNDER_VOLTAGE: (
# [40.0, 40.1, 42, 43, 44, 45, 46, 47, 48.0],
# "PSDV%02.1f",
# ),
# CONF_BATTERY_FLOAT_VOLTAGE: ([48.0, 49.0, 50.0, 51.0], "PBFT%02.1f"),
# CONF_BATTERY_TYPE: ([0, 1, 2], "PBT%02.0f"),
# CONF_OUTPUT_SOURCE_PRIORITY: ([0, 1, 2], "POP%02.0f"),
# CONF_CHARGER_SOURCE_PRIORITY: ([0, 1, 2, 3], "PCP%02.0f"),
}

CONFIG_SCHEMA = PIPSOLAR_COMPONENT_SCHEMA.extend(
{
cv.Optional(type): output.FLOAT_OUTPUT_SCHEMA.extend(
{
cv.Required(CONF_ID): cv.declare_id(PipsolarOutput),
cv.Optional(CONF_POSSIBLE_VALUES, default=values): cv.All(
cv.ensure_list(cv.positive_float), cv.Length(min=1)
),
}
)
for type, (values, _) in TYPES.items()
}
)


async def to_code(config):
paren = await cg.get_variable(config[CONF_PIPSOLAR_ID])

for type, (_, command) in TYPES.items():
if type in config:
conf = config[type]
var = cg.new_Pvariable(conf[CONF_ID])
await output.register_output(var, conf)
cg.add(var.set_parent(paren))
cg.add(var.set_set_command(command))
if (CONF_POSSIBLE_VALUES) in conf:
cg.add(var.set_possible_values(conf[CONF_POSSIBLE_VALUES]))


@automation.register_action(
"output.pipsolar.set_level",
SetOutputAction,
cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(CONF_ID),
cv.Required(CONF_VALUE): cv.templatable(cv.positive_float),
}
),
)
def output_pipsolar_set_level_to_code(config, action_id, template_arg, args):
paren = yield cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren)
template_ = yield cg.templatable(config[CONF_VALUE], args, float)
cg.add(var.set_level(template_))
yield var
22 changes: 22 additions & 0 deletions components/pipsolar/output/pipsolar_output.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#include "pipsolar_output.h"
#include "esphome/core/log.h"
#include "esphome/core/helpers.h"

namespace esphome {
namespace pipsolar {

static const char *const TAG = "pipsolar.output";

void PipsolarOutput::write_state(float state) {
char tmp[10];
sprintf(tmp, this->set_command_.c_str(), state);

if (std::find(this->possible_values_.begin(), this->possible_values_.end(), state) != this->possible_values_.end()) {
ESP_LOGD(TAG, "Will write: %s out of value %f / %02.0f", tmp, state, state);
this->parent_->switch_command(std::string(tmp));
} else {
ESP_LOGD(TAG, "Will not write: %s as it is not in list of allowed values", tmp);
}
}
} // namespace pipsolar
} // namespace esphome
Loading
Loading