diff --git a/src/growatt.h b/src/growatt.h index 6a34297..e8561bd 100644 --- a/src/growatt.h +++ b/src/growatt.h @@ -21,18 +21,18 @@ typedef struct __attribute__((aligned(MAX_METRIC_LENGTH * 2))) { uint8_t address; char human_name[MAX_METRIC_LENGTH]; char metric_name[MAX_METRIC_LENGTH]; - enum { REGISTER_SINGLE, REGISTER_DOUBLE } register_size; - double scale; char device_class[MAX_METRIC_LENGTH]; char unit[MAX_METRIC_LENGTH]; + enum { REGISTER_SINGLE, REGISTER_DOUBLE } register_size; + double scale; } REGISTER; const REGISTER holding_registers[] = { // NOLINTBEGIN(readability-magic-numbers) - {34, "max charging current", "settings_max_charging_amps", REGISTER_SINGLE, 1}, - {35, "bulk charging voltage", "settings_bulk_charging_volts", REGISTER_SINGLE, 0.1}, - {36, "float charging voltage", "settings_float_charging_volts", REGISTER_SINGLE, 0.1}, - {37, "battery voltage switch to utility", "settings_switch_to_utility_volts", REGISTER_SINGLE, 0.1}, + {34, "max charging current", "settings_max_charging_amps", "current", "A", REGISTER_SINGLE, 1}, + {35, "bulk charging voltage", "settings_bulk_charging_volts", "voltage", "V", REGISTER_SINGLE, 0.1}, + {36, "float charging voltage", "settings_float_charging_volts", "voltage", "V", REGISTER_SINGLE, 0.1}, + {37, "battery voltage switch to utility", "settings_switch_to_utility_volts", "voltage", "V", REGISTER_SINGLE, 0.1}, // {76, "rated active power", "rated_active_power_watts", REGISTER_DOUBLE, 0.1}, // XXX: not needed // {78, "rated apparant power", "rated_apparant_power_va", REGISTER_DOUBLE, 0.1}, // XXX: not needed // NOLINTEND(readability-magic-numbers) @@ -40,48 +40,48 @@ const REGISTER holding_registers[] = { const REGISTER input_registers[] = { // NOLINTBEGIN(readability-magic-numbers) - {0, "system status", "system_status", REGISTER_SINGLE, 1}, - {1, "PV1 voltage", "pv1_volts", REGISTER_SINGLE, 0.1}, - {3, "PV1 power", "pv1_watts", REGISTER_DOUBLE, 0.1}, - {7, "buck1 current", "buck1_amps", REGISTER_SINGLE, 0.1}, - // {8, "buck2 current", "buck2_amps", REGISTER_SINGLE, 0.1}, // XXX: always zero - {9, "inverter active power", "inverter_active_power_watts", REGISTER_DOUBLE, 0.1}, - {11, "inverter apparant power", "inverter_apparant_power_va", REGISTER_DOUBLE, 0.1}, - {13, "grid charging power", "grid_charging_watts", REGISTER_DOUBLE, 0.1}, - {17, "battery voltage", "battery_volts", REGISTER_SINGLE, 0.01}, - {18, "battery SOC", "battery_soc", REGISTER_SINGLE, 1}, + {0, "system status", "system_status", "None", "", REGISTER_SINGLE, 1}, + {1, "PV1 voltage", "pv1_volts", "voltage", "V", REGISTER_SINGLE, 0.1}, + {3, "PV1 power", "pv1_watts", "power", "W", REGISTER_DOUBLE, 0.1}, + {7, "buck1 current", "buck1_amps", "current", "A", REGISTER_SINGLE, 0.1}, + // {8, "buck2 current", "buck2_amps", "current", "A", REGISTER_SINGLE, 0.1}, // XXX: always zero + {9, "inverter active power", "inverter_active_power_watts", "power", "W", REGISTER_DOUBLE, 0.1}, + {11, "inverter apparant power", "inverter_apparant_power_va", "apparent_power", "VA", REGISTER_DOUBLE, 0.1}, + {13, "grid charging power", "grid_charging_watts", "power", "W", REGISTER_DOUBLE, 0.1}, + {17, "battery voltage", "battery_volts", "voltage", "V", REGISTER_SINGLE, 0.01}, + {18, "battery SOC", "battery_soc", "battery", "%", REGISTER_SINGLE, 1}, // {19, "bus voltage", "bus_volts", REGISTER_SINGLE, 0.1}, // irrelevant - {20, "grid voltage", "grid_volts", REGISTER_SINGLE, 0.1}, - {21, "grid frequency", "grid_hz", REGISTER_SINGLE, 0.01}, + {20, "grid voltage", "grid_volts", "voltage", "V", REGISTER_SINGLE, 0.1}, + {21, "grid frequency", "grid_hz", "frequency", "Hz", REGISTER_SINGLE, 0.01}, // {24, "output DC voltage", "output_dc_volts", REGISTER_SINGLE, 0.1}, // XXX: always zero - {25, "inverter temperature", "temperature_inverter_celsius", REGISTER_SINGLE, 0.1}, - {26, "DC-DC temperature", "temperature_dcdc_celsius", REGISTER_SINGLE, 0.1}, - {27, "inverter load percent", "inverter_load_percent", REGISTER_SINGLE, 0.1}, + {25, "inverter temperature", "temperature_inverter_celsius", "temperature", "°C", REGISTER_SINGLE, 0.1}, + {26, "DC-DC temperature", "temperature_dcdc_celsius", "temperature", "°C", REGISTER_SINGLE, 0.1}, + {27, "inverter load percent", "inverter_load_percent", "None", "%", REGISTER_SINGLE, 0.1}, // {30, "work time total", "work_time_total_seconds", REGISTER_DOUBLE, 0.5}, // XXX: always zero - {32, "buck1 temperature", "temperature_buck1_celsius", REGISTER_SINGLE, 0.1, "temperature", "0C"}, + {32, "buck1 temperature", "temperature_buck1_celsius", "temperature", "°C", REGISTER_SINGLE, 0.1}, // {33, "buck2 temperature", "temperature_buck2_celsius", REGISTER_SINGLE, 0.1}, // irrelevant - {34, "output current", "output_amps", REGISTER_SINGLE, 0.1}, - {35, "inverter current", "inverter_amps", REGISTER_SINGLE, 0.1}, - {40, "fault bit", "fault_bit", REGISTER_SINGLE, 1}, - {41, "warning bit", "warning_bit", REGISTER_SINGLE, 1}, + {34, "output current", "output_amps", "current", "A", REGISTER_SINGLE, 0.1}, + {35, "inverter current", "inverter_amps", "current", "A", REGISTER_SINGLE, 0.1}, + {40, "fault bit", "fault_bit", "None", "", REGISTER_SINGLE, 1}, + {41, "warning bit", "warning_bit", "None", "", REGISTER_SINGLE, 1}, // {42, "fault value", "fault_value", REGISTER_SINGLE, 1}, // XXX: always zero // {43, "warning value", "warning_value", REGISTER_SINGLE, 1}, // XXX: always zero // {45, "product check step", "product_check_step", REGISTER_SINGLE, 1}, // irrelevant // {46, "production line mode", "production_line_mode", REGISTER_SINGLE, 1}, // XXX: always zero // {47, "constant power OK flag", "constant_power_ok_flag", REGISTER_SINGLE, 1}, // XXX: always zero - {48, "PV energy today", "energy_pv_today_kwh", REGISTER_DOUBLE, 0.1, "energy", "kWh"}, - {50, "PV energy total", "energy_pv_total_kwh", REGISTER_DOUBLE, 0.1}, - {56, "grid energy today", "energy_grid_today_kwh", REGISTER_DOUBLE, 0.1}, - {58, "grid energy total", "energy_grid_total_kwh", REGISTER_DOUBLE, 0.1}, - {60, "battery discharging energy today", "battery_discharging_today_kwh", REGISTER_DOUBLE, 0.1}, - {64, "grid discharging energy today", "grid_discharging_today_kwh", REGISTER_DOUBLE, 0.1}, - {66, "grid discharging energy total", "grid_discharging_total_kwh", REGISTER_DOUBLE, 0.1}, - {68, "grid charging current", "grid_charging_amps", REGISTER_SINGLE, 0.1}, - {69, "inverter discharging power", "inverter_discharging_watts", REGISTER_DOUBLE, 0.1}, - {73, "battery discharging power", "battery_discharging_watts", REGISTER_DOUBLE, 0.1}, - {77, "battery net power (signed)", "battery_net_watts", REGISTER_DOUBLE, -0.1}, + {48, "PV energy today", "energy_pv_today_kwh", "energy", "kWh", REGISTER_DOUBLE, 0.1}, + {50, "PV energy total", "energy_pv_total_kwh", "energy", "kWh", REGISTER_DOUBLE, 0.1}, + {56, "grid energy today", "energy_grid_today_kwh", "energy", "kWh", REGISTER_DOUBLE, 0.1}, + {58, "grid energy total", "energy_grid_total_kwh", "energy", "kWh", REGISTER_DOUBLE, 0.1}, + {60, "battery discharging energy today", "battery_discharging_today_kwh", "energy", "kWh", REGISTER_DOUBLE, 0.1}, + {64, "grid discharging energy today", "grid_discharging_today_kwh", "energy", "kWh", REGISTER_DOUBLE, 0.1}, + {66, "grid discharging energy total", "grid_discharging_total_kwh", "energy", "kWh", REGISTER_DOUBLE, 0.1}, + {68, "grid charging current", "grid_charging_amps", "current", "A", REGISTER_SINGLE, 0.1}, + {69, "inverter discharging power", "inverter_discharging_watts", "power", "W", REGISTER_DOUBLE, 0.1}, + {73, "battery discharging power", "battery_discharging_watts", "power", "W", REGISTER_DOUBLE, 0.1}, + {77, "battery net power (signed)", "battery_net_watts", "power", "W", REGISTER_DOUBLE, -0.1}, // {81, "fan speed MPPT", "fan_speed_mppt", REGISTER_SINGLE, 1}, // XXX: always zero - {82, "fan speed inverter", "fan_speed_inverter", REGISTER_SINGLE, 1}, + {82, "fan speed inverter", "fan_speed_inverter", "wind_speed", "%", REGISTER_SINGLE, 1}, // {180, "solar charger status", "solar_status", REGISTER_SINGLE, 1}, // XXX: always zero // NOLINTEND(readability-magic-numbers) }; diff --git a/src/mqtt.h b/src/mqtt.h index 8f4631f..f48832d 100644 --- a/src/mqtt.h +++ b/src/mqtt.h @@ -5,6 +5,7 @@ #include #include // sleep() +#include "growatt.h" #include "log.h" #include "modbus.h" @@ -84,70 +85,23 @@ int start_mqtt_thread(void *config_ptr) { LOG(LOG_INFO, "Connected to the MQTT broker"); - // TODO: char payload[MQTT_METRIC_PAYLOAD_SIZE]; - char metric_id[MQTT_METRIC_ID_SIZE]; char unique_id[MQTT_METRIC_ID_SIZE]; char topic[MQTT_METRIC_ID_SIZE * 2]; - sprintf(metric_id, "battery_volts"); - sprintf(unique_id, "growatt_%s", metric_id); - sprintf(payload, - "{\"device_class\":\"voltage\",\"state_topic\":\"%s\",\"unit_of_measurement\":\"V\",\"value_template\":\"{{" - "value_json.%s}}\",\"name\":\"Battery " - "voltage\",\"unique_id\":\"%s\",\"device\":{\"identifiers\":[\"1\"],\"name\":\"Growatt\",\"manufacturer\":" - "\"Growatt\"}}", - TOPIC_STATE, metric_id, unique_id); - - sprintf(topic, "homeassistant/sensor/%s/config", unique_id); - mosquitto_publish(client, NULL, topic, (int)strlen(payload), payload, 0, true); - - sprintf(metric_id, "pv1_watts"); - sprintf(unique_id, "growatt_%s", metric_id); - sprintf(payload, - "{\"device_class\":\"power\",\"state_topic\":\"%s\",\"unit_of_measurement\":\"W\",\"value_template\":\"{{" - "value_json.%s}}\",\"name\":\"PV1 " - "power\",\"unique_id\":\"%s\",\"device\":{\"identifiers\":[\"1\"],\"name\":\"Growatt\",\"manufacturer\":" - "\"Growatt\"}}", - TOPIC_STATE, metric_id, unique_id); - - sprintf(topic, "homeassistant/sensor/%s/config", unique_id); - mosquitto_publish(client, NULL, topic, (int)strlen(payload), payload, 0, true); - - sprintf(metric_id, "pv1_volts"); - sprintf(unique_id, "growatt_%s", metric_id); - sprintf(payload, - "{\"device_class\":\"voltage\",\"state_topic\":\"%s\",\"unit_of_measurement\":\"V\",\"value_template\":\"{{" - "value_json.%s}}\",\"name\":\"PV1 " - "voltage\",\"unique_id\":\"%s\",\"device\":{\"identifiers\":[\"1\"],\"name\":\"Growatt\",\"manufacturer\":" - "\"Growatt\"}}", - TOPIC_STATE, metric_id, unique_id); - - sprintf(topic, "homeassistant/sensor/%s/config", unique_id); - mosquitto_publish(client, NULL, topic, (int)strlen(payload), payload, 0, false); - - sprintf(metric_id, "energy_pv_today_kwh"); - sprintf(unique_id, "growatt_%s", metric_id); // should be on Energy Dashboard because of "device_class: energy" - sprintf(payload, - "{\"device_class\":\"energy\",\"state_topic\":\"%s\",\"unit_of_measurement\":\"kWh\",\"value_template\":\"{{" - "value_json.%s}}\",\"name\":\"PV production " - "today\",\"unique_id\":\"%s\",\"device\":{\"identifiers\":[\"1\"],\"name\":\"Growatt\",\"manufacturer\":" - "\"Growatt\"}}", - TOPIC_STATE, metric_id, unique_id); - - sprintf(metric_id, "energy_pv_total_kwh"); - sprintf(unique_id, "growatt_%s", - metric_id); // should be on Energy Dashboard because of "device_class: energy" and state_class - sprintf(payload, - "{\"device_class\":\"energy\",\"state_class\":\"total_increasing\",\"state_topic\":\"%s\",\"unit_of_" - "measurement\":\"kWh\",\"value_template\":\"{{" - "value_json.%s}}\",\"name\":\"PV production " - "total\",\"unique_id\":\"%s\",\"device\":{\"identifiers\":[\"1\"],\"name\":\"Growatt\",\"manufacturer\":" - "\"Growatt\"}}", - TOPIC_STATE, metric_id, unique_id); - - sprintf(topic, "homeassistant/sensor/%s/config", unique_id); - mosquitto_publish(client, NULL, topic, (int)strlen(payload), payload, 0, false); + for (size_t index = 0; index < COUNT(input_registers); index++) { + const REGISTER reg = input_registers[index]; + + sprintf(unique_id, "growatt_%s", reg.metric_name); + sprintf(payload, + "{\"device_class\":\"%s\",\"state_topic\":\"%s\",\"unit_of_measurement\":\"%s\"," + "\"value_template\":\"{{value_json.%s}}\",\"name\":\"%s\",\"unique_id\":\"%s\"," + "\"device\":{\"identifiers\":[\"1\"],\"name\":\"Growatt\",\"manufacturer\":\"Growatt\"}}", + reg.device_class, TOPIC_STATE, reg.unit, reg.metric_name, reg.human_name, unique_id); + + sprintf(topic, "homeassistant/sensor/%s/config", unique_id); + mosquitto_publish(client, NULL, topic, (int)strlen(payload), payload, 0, true); + } char metrics[RESPONSE_SIZE] = {0}; char buffer[RESPONSE_SIZE] = {0};