How to make VZM30-SN report as a fan (or at least a switch) to Home Assistant from Zigbee2MQTT?

Okay, I managed to kind of figure it out. The documentation for Zigbee2MQTT could really use some actual explanation about what the fields of these objects are, but near as I can tell, with the example as given:

devices:
  "0x12345678":
    friendly_name: my_switch
    homeassistant:
      switch:
        type: light
        object_id: light
      light:
        name: null
        value_template: null
        state_value_template: "{{ value_json.state }}"
  • The switch field is the one of the Zigbee Endpoints of the device.
  • The switch.type is the type of device you want to expose the Zigbee Endpoint as.
  • The switch.object_id is the field you want to define to override some configuration.
  • The light is referenced by the switch.object_id.
  • The light is what gets used as the MQTT path. In this example it’d be something like homeassistant/light/0x12345678/light/config.
  • The fields inside of the light object are used as the Home Assistant MQTT discovery payload.
  • No clue why they set the name to null or the value_template to null.

So it seems to be mapping from the switch Endpoint to a light Endpoint. It’s coincidence that they decided to call the light’s object_id they wanted to expose light. It’s not that the light field they defined has an actual meaning in the type of entity exposed over MQTT. So the way this works seems to be that you have to define an object that has the same name as one of the Zigbee Endpoints, and its type is what type you’re exposing that Endpoint to be in Home Assistant. So if you want a light to show up as a fan, you would have a light object with a type field that has the value fan. I assume if you wanted a switch to show up as a cover, you would have a switch object with a type field that has the value cover. It’s confusing without any documentation, but I can understand how that schema came about.

Regardless, after a lot of struggling, the configuration for the fan looks like:

devices:
  "0x12345678":
    friendly_name: Bathroom Exhaust Fan Switch
    homeassistant:
      fan:
        state_value_template: "{{ value_json.state }}"
      light:
        object_id: fan
        type: fan

This generates the correct Home Assistant MQTT discovery configuration of homeassistant/fan/0x12345678/fan/config, and it shows up in Home Assistant as a fan, not a light! So everything works like expected!

For anyone still a little confused, I’m doing a similar thing and using the object_id that happens to be the same as the type, but that’s not necessary. It would also work as:

devices:
  "0x12345678":
    friendly_name: Bathroom Exhaust Fan Switch
    homeassistant:
      this_is_a_fan:
        state_value_template: "{{ value_json.state }}"
      light:
        object_id: this_is_a_fan
        type: fan

The only difference is what the MQTT discovery path would look like. It would look like homeassistant/fan/0x12345678/this_is_a_fan/config,

Hopefully this helps someone else in the future!