# # solis-modbus-inv.yaml -- Ginlong Solis INV Inverter modbus sensors include file # # ! This file is to be included as a package in an ESPhome definition ! # ! This file only works with Solis 'INV' 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: 3051 # = 3052 - 1 value_type: S_WORD unit_of_measurement: '%' entity_category: config icon: mdi:percent skip_updates: 10 mode: box min_value: 0 max_value: 110 multiply: 100 select: - platform: modbus_controller name: Working Mode disabled_by_default: true # intentionally disabled, remove if you know what you are doing address: 3040 # = 3041 - 1 value_type: U_WORD entity_category: config icon: mdi:cog-outline skip_updates: 10 optionsmap: "No response mode": 0 "Volt-Watt default": 1 "Volt-VAr": 2 "Fixed power factor": 3 "Fix reactive power": 4 "Power-PF": 5 "Rule21Volt-Watt": 6 binary_sensor: - platform: modbus_controller modbus_controller_id: modbus_master name: Working status Normal register_type: read address: 3071 # = 3072 - 1 bitmask: 0x0001 - platform: modbus_controller modbus_controller_id: modbus_master name: Working status Initializing register_type: read address: 3071 # = 3072 - 1 bitmask: 0x0002 - platform: modbus_controller modbus_controller_id: modbus_master name: Working status Grid off device_class: problem register_type: read address: 3071 # = 3072 - 1 bitmask: 0x0004 - platform: modbus_controller modbus_controller_id: modbus_master name: Working status Fault to stop device_class: problem register_type: read address: 3071 # = 3072 - 1 bitmask: 0x0008 - platform: modbus_controller modbus_controller_id: modbus_master name: Working status Standby register_type: read address: 3071 # = 3072 - 1 bitmask: 0x0010 - platform: modbus_controller modbus_controller_id: modbus_master name: Working status Derating register_type: read address: 3071 # = 3072 - 1 bitmask: 0x0020 - platform: modbus_controller modbus_controller_id: modbus_master name: Working status Limitating register_type: read address: 3071 # = 3072 - 1 bitmask: 0x0040 - platform: modbus_controller modbus_controller_id: modbus_master name: Working status Grid surge device_class: problem register_type: read address: 3071 # = 3072 - 1 bitmask: 0x0100 - platform: modbus_controller modbus_controller_id: modbus_master name: Working status Fan fault device_class: problem register_type: read address: 3071 # = 3072 - 1 bitmask: 0x0200 sensor: - platform: modbus_controller modbus_controller_id: modbus_master name: Active Power device_class: power state_class: measurement unit_of_measurement: W register_type: read address: 3004 # = 3005 - 1 value_type: U_DWORD on_value: then: - script.execute: modbus_activity - 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: 3006 # = 3007 - 1 value_type: U_DWORD - 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: 3008 # = 3009 - 1 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: 3010 # = 3011 - 1 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: 3012 # = 3013 - 1 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: 3014 # = 3015 - 1 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: 3015 # = 3016 - 1 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: 3021 # = 3022 - 1 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: 3022 # = 3023 - 1 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: 3023 # = 3024 - 1 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: 3024 # = 3025 - 1 value_type: U_WORD accuracy_decimals: 1 filters: - multiply: 0.1 - 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: 3035 # = 3036 - 1 value_type: U_WORD accuracy_decimals: 1 filters: - multiply: 0.1 - 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: 3041 # = 3042 - 1 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: 3042 # = 3043 - 1 value_type: U_WORD accuracy_decimals: 2 filters: - multiply: 0.01 text_sensor: - platform: modbus_controller modbus_controller_id: modbus_master name: Product model register_type: read address: 2999 # = 3000 - 1 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: 3000 # = 3001 - 1 response_size: 2 force_new_range: true # workaround for bug concatening 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: LCD software version register_type: read address: 3001 # = 3002 - 1 response_size: 2 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: 3053 # = 3054 - 1 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: 3060 # = 3061 - 1 register_count: 4 raw_encode: HEXBYTES entity_category: diagnostic icon: mdi:numeric skip_updates: 10 lambda: |- char serialnumber[2 * 8 + 1]; for (int j = 0; j < 8; j++) { unsigned int r = data[item->offset + j + (j + 1) % 2 - j % 2]; sprintf(serialnumber + (j * 2), "%02x", (r & 0x0F) << 4 | (r & 0xF0) >> 4); } serialnumber[2 * 8] = 0; return std::string(serialnumber); - platform: modbus_controller modbus_controller_id: modbus_master id: inverter_rtc register_type: holding address: 2999 # = 3000 - 1 register_count: 6 raw_encode: HEXBYTES entity_category: diagnostic icon: mdi:clock-digital skip_updates: 10 lambda: |- time_t i_t; struct tm i_tm = { 0 }; i_tm.tm_year = static_cast((static_cast(data[item->offset + 0]) << 8) | data[item->offset + 1]) + 2000 - 1900; i_tm.tm_mon = static_cast((static_cast(data[item->offset + 2]) << 8) | data[item->offset + 3]) - 1; i_tm.tm_mday = static_cast((static_cast(data[item->offset + 4]) << 8) | data[item->offset + 5]); i_tm.tm_hour = static_cast((static_cast(data[item->offset + 6]) << 8) | data[item->offset + 7]); i_tm.tm_min = static_cast((static_cast(data[item->offset + 8]) << 8) | data[item->offset + 9]); i_tm.tm_sec = static_cast((static_cast(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, item->start_address, 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 {}; - 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