ESPHome Modbus send custom value for switch

Hi there,
I have a Growatt Mic600 Inverter and I can read and control a lot of it over Modbus. I’m also able to switch off the inverter with a switch, but I can’t turn it back on. From Growatt cloud website, it’s possible to shut off and on, so I think ESPHome is messing up the register value.
Is it possible to send custom values for “on”, as example 01 or 0101 instead of 1?
Here’s my relevant config: (could post full yaml, but all other values work)

switch:
  - platform: modbus_controller
    name: "${friendly_name} OnOff"
    skip_updates: 5
    use_write_multiple: true
    register_type: holding
    address: 0
    entity_category: config
    icon: "mdi:toggle-switch"

Setup from ESPHome

esphome:
  name: shine_x_1
esp8266:
  board: d1_mini
uart:
  id: mod_bus
  tx_pin: 1
  rx_pin: 3
  baud_rate: 115200
  
modbus:
  id: modbus1
  uart_id: mod_bus
  
modbus_controller:
  - id: growatt
# the Modbus device addr
    address: 0x1
    modbus_id: modbus1
    setup_priority: -10  
    update_interval: 5s

and this is my Device:


I hope someone can help fixing it, because I want to turn the inverter off and on, based on battery state/voltage.

I don’t see even off write in your yaml clips.
I doubt that Growatt implemented on/off register with values like 0101…

It’s a switch, so it’s either on, or off. :sweat_smile:
It’s possible to switch off and on within the Growatt Cloud, this is communication to esp07 and send command over USB port. All other commands, like set max % power, set inverter model (2kw instead of 600w :face_with_hand_over_mouth:), read values and so on working without problems.

If you look into different PDFs, it could be they have 0/1, 00/01, 0000/0001 or 0000/0101. I just wanna try which value could work because more then me can confirm, the switch is only possible to switch off and the cloud is able to switch on again.

I’m not beginner with modbus devices. But newer tried to use “switch” to write register. Hell knows how it’s implemented…
Do you have some “pdf” for your protocol?
Can you show yaml for one of your holding register write commands, that works?

Sure and thanks for the help!

Page 9 is adress 0: on/off and 3: Max output power % which is working as

number:
    - platform: modbus_controller
      name: "${friendly_name} Max Output Power %"
      id: poweroutput
      address: 3
      value_type: U_WORD
      min_value: 0
      max_value: 100
      entity_category: config
      step: 1

Also working write:


select:
  - platform: modbus_controller
    id: powermode
    name: "${friendly_name} Power Mode"
    address: 121
    value_type: U_WORD
    optionsmap:
      "600": 6
      "750": 7
      "800": 8
      "900": 9
      "1000": 10
      "1100": 11
      "1200": 12
      "1300": 13
      "1400": 14
      "1500": 15
      "1600": 16
      "1700": 17
      "1800": 18
      "1900": 19
      "2000": 20
    entity_category: config

and from the switch, “turn off” is working too. I also tried “coil” instead of holding. :smiley:
But if you know a bit about modbus, to you might know how to log when I set the register over cloud interface? (Not sure if I can turn on standby on mobile app.)

You are a genius! You gave me exactly the right input to solve 90% of my problem! The problem is the switch and holding register.
I created a new select drop-down, mapped to on:1, off:0 and that works!


select:
  - platform: modbus_controller
    id: onoffsel1
    name: "${friendly_name} OnOffSel"
    address: 0
    value_type: U_WORD
    entity_category: config
    icon: "mdi:toggle-switch"
    optionsmap:
      "Off": 0
      "On": 1

Now I have to adjust my automatition to select the right value and it should work (hopfully):

alias: Growatt Einschalten
description: Einschalten über 3.2V
trigger:
  - platform: numeric_state
    entity_id:
      - sensor.jk_bms_min_cell_voltage
    above: 3.2
action:
  - service: switch.turn_on
    target:
      entity_id:
        - switch.growatt_inverter_onoff
    data: {}
mode: single

Tell that to my daughter… :sweat_smile:
So what’s your opinion, why switch didn’t work?

I would need to dig into the Modbus components to find out. Maybe it’s the difference between writing to coil or holding register vs value type u_word. The actual value 0/1 is the same in both, so it has to be the way it’s written.
Modbus select is
U_WORD: unsigned 16 bit integer from 1 register = 16bit
And switch holding
holding: Holding Registers - Holding registers are the most universal 16-bit register. They may be read and/or written. Modbus Function Code 3 (Read Holding Registers) will be used.

I’ll create an issue on GitHub tomorrow and explain the problem there too. Maybe one can explain the difference in writing.

Esphome documentation for writing registers is really awful, I wouldn’t be surprised if somebody wrote to (for example calibration) register when intended to read them.

Anyway, this should do it if component implementations are getting too weird…
custom_command: [ 0x01, 0x06, 0x00, 0x00, 0x00, 0x01]

Not that reading is so clear either…
For example, You want read holding register:

sensor:
- platform: modbus_controller
  modbus_controller_id: modbus_device
  name: "Battery Capacity"
  register_type: holding
  address: 0x9001

Ok.
What about if you want read input register?

with the help of Github, I was able to figure it out. If someone comes along this topic: modbus_controller switch write "on" does not work · Issue #5888 · esphome/issues · GitHub

To switch the MIC600 on and off by switch, use this:

switch:
  - platform: modbus_controller
    name: "${friendly_name} OnOff"
    skip_updates: 5
    use_write_multiple: false
    register_type: holding
    address: 0
    bitmask: 1
    entity_category: config
    icon: "mdi:toggle-switch"

Here is a full Yaml to set up ESPHome for Growatt Mic600TL-X

substitutions:
  device_description: Growatt Inverter
  friendly_name:  Growatt Inverter
  name: Shine X 1
  
esphome:
  name: shine_x_1

esp8266:
  board: d1_mini # This is a Wemos, plugged in over USB, not the original WiFi-X Stick!

# Enable logging
logger:
  baud_rate: 0
# Enable Home Assistant API
api:
  encryption:
    key: !secret api_key


ota:
  password: !secret ota_passwd

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  power_save_mode: none
  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "${name} Fallback Hotspot"
    password: !secret fallback_password

captive_portal:

time:
  - platform: homeassistant
    id: homeassistant_time

uart:
  id: mod_bus
  tx_pin: 1
  rx_pin: 3
  baud_rate: 115200
#  debug:
  
modbus:
  id: modbus1
  uart_id: mod_bus
  
modbus_controller:
  - id: growatt
# the Modbus device addr
    address: 0x1
    modbus_id: modbus1
    setup_priority: -10  
    update_interval: 5s

number:
    - platform: modbus_controller
      name: "${friendly_name} Max Output Power %" # to reduce output power in combination with a smart meter
      id: poweroutput
      address: 3
      value_type: U_WORD
      min_value: 0
      max_value: 100
      step: 1
 
    - platform: template
      name: "Power Output"
      id: "Power_Output"
      mode: slider
      optimistic: true
      min_value: 0
      initial_value: 100
      max_value: 100
      step: 1

select:
  - platform: modbus_controller
    id: powermode
    name: "${friendly_name} Power Mode" # Use with care! It's illegal to set above 800W in Germany. ;)
    address: 121
    value_type: U_WORD
    optionsmap:
      "600": 6
      "750": 7
      "800": 8
      "900": 9
      "1000": 10
      "1100": 11
      "1200": 12
      "1300": 13
      "1400": 14
      "1500": 15
      "1600": 16
      "1700": 17
      "1800": 18
      "1900": 19
      "2000": 20
  - platform: modbus_controller
    id: onoffsel
    name: "${friendly_name} OnOffSel" # Same as on/off switch
    address: 0
    value_type: U_WORD
    icon: "mdi:toggle-switch"
    optionsmap:
      "Off": 0
      "On": 1

text_sensor:
  - platform: modbus_controller
    name: "${friendly_name} Firmware Version"
    address: 9
    register_count: 3
    register_type: holding
    #internal: true
    entity_category: diagnostic

  - platform: template
    name: "${friendly_name} Status"
    icon: mdi:eye
    entity_category: diagnostic
    lambda: |-
      if (id(status).state == 1) {
        return {"Normal"};
      } else if (id(status).state == 0)  {
        return {"Waiting"};
      } else {
        return {"Fault!"};
      }

switch:
  - platform: modbus_controller
    name: "${friendly_name} OnOff"
    skip_updates: 5
    use_write_multiple: false
    register_type: holding
    address: 0
    bitmask: 1
    icon: "mdi:toggle-switch"

sensor:
  - platform: modbus_controller
    address: 0
    register_type: "read"
    internal: true
    id: status
  - platform: wifi_signal
    name: "${friendly_name} WiFi Signal"
    update_interval: 60s
  - platform: modbus_controller
    name: "${friendly_name} DcPower"
    address: 5
    register_type: "read"
    unit_of_measurement: W
    device_class: power
    icon: mdi:flash
    value_type: U_DWORD
    accuracy_decimals: 1
    filters:
    - multiply: 0.1
        
  - platform: modbus_controller
    name: "${friendly_name} DcVoltage"
    address: 3
    register_type: "read"
    unit_of_measurement: V
    device_class: voltage
    icon: mdi:flash
    value_type: U_WORD
    accuracy_decimals: 1
    filters:
    - multiply: 0.1
    
  - platform: modbus_controller
    name: "${friendly_name} DcInputCurrent"
    address: 4
    register_type: "read"
    unit_of_measurement: A
    device_class: current
    icon: mdi:flash
    value_type: U_WORD
    accuracy_decimals: 1
    filters:
    - multiply: 0.1
    
  - platform: modbus_controller
    name: "${friendly_name} AcFrequency"
    address: 37
    register_type: "read"
    unit_of_measurement: Hz
    icon: mdi:flash
    value_type: U_WORD
    accuracy_decimals: 1
    filters:
    - multiply: 0.01
  
  - platform: modbus_controller
    name: "${friendly_name} AcVoltage"
    address: 38
    register_type: "read"
    unit_of_measurement: V
    device_class: voltage
    icon: mdi:flash
    value_type: U_WORD
    accuracy_decimals: 1
    filters:
    - multiply: 0.1
  
  - platform: modbus_controller
    name: "${friendly_name} AcOutputCurrent"
    address: 39
    register_type: "read"
    unit_of_measurement: A
    device_class: current
    icon: mdi:flash
    value_type: U_WORD
    accuracy_decimals: 1
    filters:
    - multiply: 0.1
    
  - platform: modbus_controller
    name: "${friendly_name} AcPower"
    address: 40
    register_type: "read"
    unit_of_measurement: W
    device_class: power
    icon: mdi:flash
    value_type: U_DWORD
    accuracy_decimals: 1
    filters:
    - multiply: 0.1
    
  - platform: modbus_controller
    name: "${friendly_name} EnergyToday"
    address: 53
    register_type: "read"
    unit_of_measurement: kWh
    device_class: energy
    icon: mdi:flash
    value_type: U_DWORD
    accuracy_decimals: 1
    filters:
    - multiply: 0.1
    
  - platform: modbus_controller
    name: "${friendly_name} EnergyTotal"
    address: 55
    register_type: "read"
    unit_of_measurement: kWh
    state_class: total_increasing
    device_class: energy
    icon: mdi:flash
    value_type: U_DWORD
    accuracy_decimals: 1
    filters:
    - multiply: 0.1

  - platform: modbus_controller
    name: "${friendly_name} Temperature"
    address: 93
    register_type: "read"
    unit_of_measurement: C
    device_class: temperature
    icon: mdi:thermometer
    value_type: U_WORD
    accuracy_decimals: 1
    filters:
    - multiply: 0.1
1 Like

why quotes ?

Hi,

My 2 cents on the matter as I ran into the same issue I solved it with the following code, however I like the solution from MoritzS as well:

select: 
 - platform: modbus_controller
    name: "Inverter"
    icon: mdi:battery-charging-100
    address: 00
    value_type: U_WORD
    optionsmap:
      "Off": 0
      "On": 1

If you check the full yaml, you’ll see switch with bit mask 0 and select with on/off. :face_with_hand_over_mouth: I have both in my code, both work.
But actually I think, using the switch is better then select because it’s a more default behavior.