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
switchfield is the one of the Zigbee Endpoints of the device. - The
switch.typeis the type of device you want to expose the Zigbee Endpoint as. - The
switch.object_idis the field you want to define to override some configuration. - The
lightis referenced by theswitch.object_id. - The
lightis what gets used as the MQTT path. In this example it’d be something likehomeassistant/light/0x12345678/light/config. - The fields inside of the
lightobject are used as the Home Assistant MQTT discovery payload. - No clue why they set the
nametonullor thevalue_templatetonull.
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!