# # solis-modbus-esinv.yaml -- Ginlong Solis ESINV Inverter modbus sensors include file # # ! This file is to be included as a package in an ESPhome definition ! # ! This file only works with Solis 'ESINV' type inverters ! # # (C) 2023 Hajo Noerenberg # # http://www.noerenberg.de/ # https://github.com/hn/ginlong-solis # # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License version 3.0 as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # number: - platform: modbus_controller modbus_controller_id: modbus_master name: Limit output power disabled_by_default: true # intentionally disabled, remove if you know what you are doing register_type: holding address: 43052 value_type: U_WORD unit_of_measurement: '%' entity_category: config icon: mdi:percent skip_updates: 10 mode: box min_value: 0 max_value: 110 multiply: 100 - platform: modbus_controller modbus_controller_id: modbus_master name: Set backflow power disabled_by_default: true # intentionally disabled, remove if you know what you are doing register_type: holding address: 43074 value_type: U_WORD unit_of_measurement: 'W' entity_category: config icon: mdi:transmission-tower-import skip_updates: 10 mode: box min_value: 0 multiply: 0.01 binary_sensor: - platform: modbus_controller modbus_controller_id: modbus_master name: Operation status Normal Operation device_class: problem register_type: read address: 33121 bitmask: 0x0001 # bit 00 filters: - invert: - platform: modbus_controller modbus_controller_id: modbus_master name: Operation status Initializing register_type: read address: 33121 bitmask: 0x0002 # bit 01 - platform: modbus_controller modbus_controller_id: modbus_master name: Operation status Controlled turning OFF device_class: problem register_type: read address: 33121 bitmask: 0x0004 # bit 02 - platform: modbus_controller modbus_controller_id: modbus_master name: Operation status Fault leads to turning OFF device_class: problem register_type: read address: 33121 bitmask: 0x0008 # bit 03 - platform: modbus_controller modbus_controller_id: modbus_master name: Operation status Stand-by register_type: read address: 33121 bitmask: 0x0010 # bit 04 - platform: modbus_controller modbus_controller_id: modbus_master name: Operation status Limited Operation device_class: problem register_type: read address: 33121 bitmask: 0x0020 # bit 05 - platform: modbus_controller modbus_controller_id: modbus_master name: Operation status Limited Operation external device_class: problem register_type: read address: 33121 bitmask: 0x0040 # bit 06 - platform: modbus_controller modbus_controller_id: modbus_master name: Operation status Backup overload device_class: problem register_type: read address: 33121 bitmask: 0x0080 # bit 07 - platform: modbus_controller modbus_controller_id: modbus_master name: Operation status Load fault device_class: problem register_type: read address: 33121 bitmask: 0x0100 # bit 08 filters: - invert: # Modbus returns 0 for load fault, 1 for OK - platform: modbus_controller modbus_controller_id: modbus_master name: Operation status Grid fault device_class: problem register_type: read address: 33121 bitmask: 0x0200 # bit 09 filters: - invert: # Modbus returns 0 for grid fault, 1 for OK - platform: modbus_controller modbus_controller_id: modbus_master name: Operation status Battery fault device_class: problem register_type: read address: 33121 bitmask: 0x0400 # bit 10 filters: - invert: # Modbus returns 0 for battery fault, 1 for OK - platform: modbus_controller modbus_controller_id: modbus_master name: Operation status Grid Surge Warn device_class: problem register_type: read address: 33121 bitmask: 0x1000 # bit 12 - platform: modbus_controller modbus_controller_id: modbus_master name: Operation status Fan Fault Warn device_class: problem register_type: read address: 33121 bitmask: 0x2000 # bit 13 - platform: modbus_controller modbus_controller_id: modbus_master name: Battery current direction id: battery_current_direction device_class: battery_charging # on means charging, off means not charging register_type: read address: 33135 filters: - invert: # modbus returns 0 for charging, 1 for discharging sensor: - platform: modbus_controller modbus_controller_id: modbus_master name: Total energy device_class: energy state_class: total_increasing unit_of_measurement: kWh register_type: read address: 33029 value_type: U_DWORD force_new_range: true # workaround to seperate reg 3006: "skip_updates applies for all components in the same range" skip_updates: 10 - platform: modbus_controller modbus_controller_id: modbus_master name: Energy this month device_class: energy state_class: total_increasing unit_of_measurement: kWh register_type: read address: 33031 value_type: U_DWORD skip_updates: 10 - platform: modbus_controller modbus_controller_id: modbus_master name: Energy last month device_class: energy state_class: total_increasing unit_of_measurement: kWh register_type: read address: 33033 value_type: U_DWORD skip_updates: 10 - platform: modbus_controller modbus_controller_id: modbus_master name: Energy today device_class: energy state_class: total_increasing unit_of_measurement: kWh register_type: read address: 33035 value_type: U_WORD accuracy_decimals: 1 filters: - multiply: 0.1 skip_updates: 10 - platform: modbus_controller modbus_controller_id: modbus_master name: Energy last day device_class: energy state_class: total_increasing unit_of_measurement: kWh register_type: read address: 33036 value_type: U_WORD accuracy_decimals: 1 filters: - multiply: 0.1 skip_updates: 10 - platform: modbus_controller modbus_controller_id: modbus_master name: DC voltage 1 device_class: voltage state_class: measurement unit_of_measurement: V register_type: read address: 33049 value_type: U_WORD accuracy_decimals: 1 filters: - multiply: 0.1 - platform: modbus_controller modbus_controller_id: modbus_master name: DC current 1 device_class: current state_class: measurement unit_of_measurement: A register_type: read address: 33050 value_type: U_WORD accuracy_decimals: 1 filters: - multiply: 0.1 - platform: modbus_controller modbus_controller_id: modbus_master name: DC voltage 2 device_class: voltage state_class: measurement unit_of_measurement: V register_type: read address: 33051 value_type: U_WORD accuracy_decimals: 1 filters: - multiply: 0.1 - platform: modbus_controller modbus_controller_id: modbus_master name: DC current 2 device_class: current state_class: measurement unit_of_measurement: A register_type: read address: 33052 value_type: U_WORD accuracy_decimals: 1 filters: - multiply: 0.1 - platform: modbus_controller modbus_controller_id: modbus_master name: Total DC output power device_class: power state_class: measurement unit_of_measurement: W register_type: read address: 33057 value_type: U_DWORD on_value: then: - script.execute: modbus_activity - platform: modbus_controller modbus_controller_id: modbus_master name: AC voltage device_class: voltage state_class: measurement unit_of_measurement: V register_type: read address: 33073 value_type: U_WORD accuracy_decimals: 1 filters: - multiply: 0.1 - platform: modbus_controller modbus_controller_id: modbus_master name: Active power device_class: power state_class: measurement disabled_by_default: true # intentionally disabled to save storage space unit_of_measurement: W register_type: read address: 33079 value_type: S_DWORD - platform: modbus_controller modbus_controller_id: modbus_master name: Reactive power device_class: reactive_power state_class: measurement disabled_by_default: true # intentionally disabled to save storage space unit_of_measurement: var register_type: read address: 33081 value_type: S_DWORD - platform: modbus_controller modbus_controller_id: modbus_master name: Apparent power device_class: apparent_power state_class: measurement disabled_by_default: true # intentionally disabled to save storage space unit_of_measurement: VA register_type: read address: 33083 value_type: S_DWORD - platform: modbus_controller modbus_controller_id: modbus_master name: Inverter temperature device_class: temperature state_class: measurement unit_of_measurement: °C register_type: read address: 33093 value_type: U_WORD accuracy_decimals: 1 filters: - multiply: 0.1 - platform: modbus_controller modbus_controller_id: modbus_master name: Grid frequency device_class: frequency state_class: measurement unit_of_measurement: Hz register_type: read address: 33094 value_type: U_WORD accuracy_decimals: 2 filters: - multiply: 0.01 - platform: modbus_controller modbus_controller_id: modbus_master name: Battery voltage device_class: voltage state_class: measurement disabled_by_default: true # intentionally disabled to save storage space unit_of_measurement: V register_type: read address: 33133 value_type: U_WORD accuracy_decimals: 2 filters: - multiply: 0.1 - platform: modbus_controller modbus_controller_id: modbus_master name: Battery current device_class: current state_class: measurement disabled_by_default: true # intentionally disabled to save storage space unit_of_measurement: A register_type: read address: 33134 value_type: S_WORD accuracy_decimals: 2 filters: - multiply: 0.1 - platform: modbus_controller modbus_controller_id: modbus_master name: Battery state of charge (SoC) device_class: battery state_class: measurement unit_of_measurement: "%" register_type: read address: 33139 value_type: U_WORD skip_updates: 10 - platform: modbus_controller modbus_controller_id: modbus_master name: Battery state of health (SoH) device_class: battery state_class: measurement unit_of_measurement: "%" register_type: read address: 33140 value_type: U_WORD icon: mdi:battery-heart-variant skip_updates: 10 - platform: modbus_controller modbus_controller_id: modbus_master name: Battery charge current limitation device_class: current state_class: measurement disabled_by_default: true # intentionally disabled to save storage space unit_of_measurement: A register_type: read address: 33143 value_type: U_WORD accuracy_decimals: 2 filters: - multiply: 0.1 - platform: modbus_controller modbus_controller_id: modbus_master name: Battery discharge current limitation device_class: current state_class: measurement disabled_by_default: true # intentionally disabled to save storage space unit_of_measurement: A register_type: read address: 33144 value_type: U_WORD accuracy_decimals: 2 filters: - multiply: 0.1 - platform: modbus_controller modbus_controller_id: modbus_master name: Household load power device_class: power state_class: measurement unit_of_measurement: W register_type: read address: 33147 value_type: U_WORD accuracy_decimals: 0 - platform: modbus_controller modbus_controller_id: modbus_master name: Backup load power device_class: power state_class: measurement disabled_by_default: true # Only relevant if backup connected unit_of_measurement: W register_type: read address: 33148 value_type: U_WORD accuracy_decimals: 0 - platform: modbus_controller modbus_controller_id: modbus_master name: Battery power device_class: power state_class: measurement unit_of_measurement: W register_type: read address: 33149 value_type: S_DWORD accuracy_decimals: 0 lambda: |- if (id(battery_current_direction).state) { return x; } else { return -x; } - platform: modbus_controller modbus_controller_id: modbus_master name: Total battery charge device_class: energy state_class: total_increasing unit_of_measurement: kWh register_type: read address: 33161 value_type: U_DWORD skip_updates: 10 - platform: modbus_controller modbus_controller_id: modbus_master name: Total battery discharge device_class: energy state_class: total_increasing unit_of_measurement: kWh register_type: read address: 33165 value_type: U_DWORD skip_updates: 10 # The inverter clock is stored in six consecutive Modbus U16 input registers: # INV: # - 33022: Year # - 33023: Month # - 33034: Day # - 33025: Hour # - 33026: Minute # - 33027: Second # To set the clock, write to 43000 in the same format, see https://solis-exporter.readthedocs.io/en/latest/packet_log/ # These may also start at 3073, see https://diysolarforum.com/resources/solis-grid-tied-inverters-2018-rs485-modbus-communication-protocol.272/download - platform: modbus_controller modbus_controller_id: modbus_master id: inverter_clock name: "Inverter clock (internal)" address: 33022 register_count: 6 register_type: read value_type: U_WORD # irrelevant because of the register_count override but meh skip_updates: 10 # Doesn't change frequently, update every 10 updates internal: true entity_category: diagnostic lambda: |- time_t i_t; struct tm i_tm = { 0 }; // Inverter doesn't have a century byte. If it's still in use in 75 // years, you'll want to change this. i_tm.tm_year = ((data[item->offset+ 0] << 8) | (data[item->offset+ 1])) + 2000 - 1900; i_tm.tm_mon = ((data[item->offset+ 2] << 8) | (data[item->offset+ 3])) - 1; i_tm.tm_mday = (data[item->offset+ 4] << 8) | (data[item->offset+ 5]); i_tm.tm_hour = (data[item->offset+ 6] << 8) | (data[item->offset+ 7]); i_tm.tm_min = (data[item->offset+ 8] << 8) | (data[item->offset+ 9]); i_tm.tm_sec = (data[item->offset+10] << 8) | (data[item->offset+11]); i_tm.tm_isdst = -1; i_t = mktime(&i_tm); ESP_LOGI("SyncInverterRTC", "Inverter RTC is %02d:%02d:%02d %04d-%02d-%02d", i_tm.tm_hour, i_tm.tm_min, i_tm.tm_sec, i_tm.tm_year + 1900, i_tm.tm_mon + 1, i_tm.tm_mday); auto s_espt = id(network_time).now(); if (s_espt.is_valid()) { double deviation = difftime(i_t, s_espt.timestamp); if (abs(deviation) > 42) { std::vector rtc_data = {(uint16_t) (s_espt.year % 100), s_espt.month, s_espt.day_of_month, s_espt.hour, s_espt.minute, s_espt.second}; auto set_rtc_command = modbus_controller::ModbusCommandItem::create_write_multiple_command(modbus_master, 43000, item->register_count, rtc_data); modbus_master->queue_command(set_rtc_command); ESP_LOGI("SyncInverterRTC", "Inverter RTC deviation=%.fs, updated RTC to %02d:%02d:%02d %04d-%02d-%02d", deviation, s_espt.hour, s_espt.minute, s_espt.second, s_espt.year, s_espt.month, s_espt.day_of_month); } } else { ESP_LOGI("SyncInverterRTC", "Inverter RTC not checked because NTP is not yet synchronised"); } return {}; text_sensor: - platform: modbus_controller modbus_controller_id: modbus_master name: Product model register_type: read address: 33000 response_size: 2 raw_encode: HEXBYTES entity_category: diagnostic icon: mdi:factory skip_updates: 10 - platform: modbus_controller modbus_controller_id: modbus_master name: DSP software version register_type: read address: 33001 response_size: 2 force_new_range: true # workaround for bug concatenating model, dsp and lcd raw_encode: HEXBYTES entity_category: diagnostic icon: mdi:chip skip_updates: 10 - platform: modbus_controller modbus_controller_id: modbus_master name: Country standard code register_type: read address: 33092 response_size: 2 raw_encode: HEXBYTES entity_category: diagnostic icon: mdi:earth skip_updates: 10 lambda: |- uint16_t value = modbus::helpers::word_from_hex_str(x, 0); switch (value) { /* Solis Spec Appendix 3 */ case 0x003: return std::string("VDE0126"); case 0x009: return std::string("MEX-CFE"); case 0x00B: return std::string("VDE4105"); } return x; - platform: modbus_controller modbus_controller_id: modbus_master name: Inverter SN register_type: read address: 33004 register_count: 4 raw_encode: HEXBYTES entity_category: diagnostic icon: mdi:numeric skip_updates: 10 lambda: |- char serialnumber[4 * 4 + 1]; for (int j = 0; j < 4; j++) { unsigned int r = esphome::modbus::helpers::word_from_hex_str(x, j * 2);; sprintf(serialnumber + (j * 4), "%02x%02x", (r & 0x0F) << 4 | (r & 0xF0) >> 4, (r & 0x0F00) >> 4 | (r & 0xF000) >> 12); } serialnumber[4 * 4] = 0; return std::string(serialnumber); - platform: modbus_controller modbus_controller_id: modbus_master name: Inverter type definition register_type: read address: 35000 response_size: 2 raw_encode: HEXBYTES entity_category: diagnostic icon: mdi:chip skip_updates: 10