ZigbeeMultistate

About

The ZigbeeMultistate class provides multistate input and output endpoints for Zigbee networks. This endpoint implements the Zigbee Home Automation (HA) standard for multistate signal processing and control. Multistate Input (MI) is meant to be used for sensors that provide discrete state values, such as fan speed settings, HVAC modes, or security states to be sent to the coordinator. Multistate Output (MO) is meant to be used for actuators that require discrete state control, such as fan controllers, HVAC systems, or security devices to be controlled by the coordinator.

Note

HomeAssistant ZHA does not support multistate input and output clusters yet.

Common API

Constructor

ZigbeeMultistate

Creates a new Zigbee multistate endpoint.

ZigbeeMultistate(uint8_t endpoint);
  • endpoint - Endpoint number (1-254)

Multistate Application Types

The ZigbeeMultistate class supports predefined application types with specific state configurations:

// Application Type 0: Fan states (Off, On, Auto)
#define ZB_MULTISTATE_APPLICATION_TYPE_0_INDEX         0x0000
#define ZB_MULTISTATE_APPLICATION_TYPE_0_NUM_STATES    3
#define ZB_MULTISTATE_APPLICATION_TYPE_0_STATE_NAMES   (const char* const[]){"Off", "On", "Auto"}

// Application Type 1: Fan speed states (Off, Low, Medium, High)
#define ZB_MULTISTATE_APPLICATION_TYPE_1_INDEX         0x0001
#define ZB_MULTISTATE_APPLICATION_TYPE_1_NUM_STATES    4
#define ZB_MULTISTATE_APPLICATION_TYPE_1_STATE_NAMES   (const char* const[]){"Off", "Low", "Medium", "High"}

// Application Type 2: HVAC modes (Auto, Heat, Cool, Off, Emergency Heat, Fan Only, Max Heat)
#define ZB_MULTISTATE_APPLICATION_TYPE_2_INDEX         0x0002
#define ZB_MULTISTATE_APPLICATION_TYPE_2_NUM_STATES    7
#define ZB_MULTISTATE_APPLICATION_TYPE_2_STATE_NAMES   (const char* const[]){"Auto", "Heat", "Cool", "Off", "Emergency Heat", "Fan Only", "Max Heat"}

// Application Type 3: Occupancy states (Occupied, Unoccupied, Standby, Bypass)
#define ZB_MULTISTATE_APPLICATION_TYPE_3_INDEX         0x0003
#define ZB_MULTISTATE_APPLICATION_TYPE_3_NUM_STATES    4
#define ZB_MULTISTATE_APPLICATION_TYPE_3_STATE_NAMES   (const char* const[]){"Occupied", "Unoccupied", "Standby", "Bypass"}

// Application Type 4: Control states (Inactive, Active, Hold)
#define ZB_MULTISTATE_APPLICATION_TYPE_4_INDEX         0x0004
#define ZB_MULTISTATE_APPLICATION_TYPE_4_NUM_STATES    3
#define ZB_MULTISTATE_APPLICATION_TYPE_4_STATE_NAMES   (const char* const[]){"Inactive", "Active", "Hold"}

// Application Type 5: Water system states (Auto, Warm-up, Water Flush, Autocalibration, Shutdown Open, Shutdown Closed, Low Limit, Test and Balance)
#define ZB_MULTISTATE_APPLICATION_TYPE_5_INDEX         0x0005
#define ZB_MULTISTATE_APPLICATION_TYPE_5_NUM_STATES    8
#define ZB_MULTISTATE_APPLICATION_TYPE_5_STATE_NAMES   (const char* const[]){"Auto", "Warm-up", "Water Flush", "Autocalibration", "Shutdown Open", "Shutdown Closed", "Low Limit", "Test and Balance"}

// Application Type 6: HVAC system states (Off, Auto, Heat Cool, Heat Only, Cool Only, Fan Only)
#define ZB_MULTISTATE_APPLICATION_TYPE_6_INDEX         0x0006
#define ZB_MULTISTATE_APPLICATION_TYPE_6_NUM_STATES    6
#define ZB_MULTISTATE_APPLICATION_TYPE_6_STATE_NAMES   (const char* const[]){"Off", "Auto", "Heat Cool", "Heat Only", "Cool Only", "Fan Only"}

// Application Type 7: Light states (High, Normal, Low)
#define ZB_MULTISTATE_APPLICATION_TYPE_7_INDEX         0x0007
#define ZB_MULTISTATE_APPLICATION_TYPE_7_NUM_STATES    3
#define ZB_MULTISTATE_APPLICATION_TYPE_7_STATE_NAMES   (const char* const[]){"High", "Normal", "Low"}

// Application Type 8: Occupancy control states (Occupied, Unoccupied, Startup, Shutdown)
#define ZB_MULTISTATE_APPLICATION_TYPE_8_INDEX         0x0008
#define ZB_MULTISTATE_APPLICATION_TYPE_8_NUM_STATES    4
#define ZB_MULTISTATE_APPLICATION_TYPE_8_STATE_NAMES   (const char* const[]){"Occupied", "Unoccupied", "Startup", "Shutdown"}

// Application Type 9: Time states (Night, Day, Hold)
#define ZB_MULTISTATE_APPLICATION_TYPE_9_INDEX         0x0009
#define ZB_MULTISTATE_APPLICATION_TYPE_9_NUM_STATES    3
#define ZB_MULTISTATE_APPLICATION_TYPE_9_STATE_NAMES   (const char* const[]){"Night", "Day", "Hold"}

// Application Type 10: HVAC control states (Off, Cool, Heat, Auto, Emergency Heat)
#define ZB_MULTISTATE_APPLICATION_TYPE_10_INDEX         0x000A
#define ZB_MULTISTATE_APPLICATION_TYPE_10_NUM_STATES    5
#define ZB_MULTISTATE_APPLICATION_TYPE_10_STATE_NAMES   (const char* const[]){"Off", "Cool", "Heat", "Auto", "Emergency Heat"}

// Application Type 11: Water control states (Shutdown Closed, Shutdown Open, Satisfied, Mixing, Cooling, Heating, Suppl Heat)
#define ZB_MULTISTATE_APPLICATION_TYPE_11_INDEX         0x000B
#define ZB_MULTISTATE_APPLICATION_TYPE_11_NUM_STATES    7
#define ZB_MULTISTATE_APPLICATION_TYPE_11_STATE_NAMES   (const char* const[]){"Shutdown Closed", "Shutdown Open", "Satisfied", "Mixing", "Cooling", "Heating", "Suppl Heat"}

// Custom application type for user-defined states
#define ZB_MULTISTATE_APPLICATION_TYPE_OTHER_INDEX      0xFFFF

Cluster Management

addMultistateInput

Adds multistate input cluster to the endpoint.

bool addMultistateInput();

This function will return true if successful, false otherwise.

addMultistateOutput

Adds multistate output cluster to the endpoint.

bool addMultistateOutput();

This function will return true if successful, false otherwise.

Multistate Input API

Configuration Methods

setMultistateInputApplication

Sets the application type for the multistate input.

bool setMultistateInputApplication(uint32_t application_type);
  • application_type - Application type constant (see Multistate Application Types above)

This function will return true if successful, false otherwise.

setMultistateInputDescription

Sets a custom description for the multistate input.

bool setMultistateInputDescription(const char *description);
  • description - Description string

This function will return true if successful, false otherwise.

setMultistateInputStates

Sets the number of states for the multistate input.

bool setMultistateInputStates(uint16_t number_of_states);
  • number_of_states - Number of discrete states (1-65535)

This function will return true if successful, false otherwise.

Value Control

setMultistateInput

Sets the multistate input value.

bool setMultistateInput(uint16_t state);
  • state - State index (0 to number_of_states-1)

This function will return true if successful, false otherwise.

getMultistateInput

Gets the current multistate input value.

uint16_t getMultistateInput();

This function returns the current multistate input state.

Reporting Methods

reportMultistateInput

Manually reports the current multistate input value.

bool reportMultistateInput();

This function will return true if successful, false otherwise.

Multistate Output API

Configuration Methods

setMultistateOutputApplication

Sets the application type for the multistate output.

bool setMultistateOutputApplication(uint32_t application_type);
  • application_type - Application type constant (see Multistate Application Types above)

This function will return true if successful, false otherwise.

setMultistateOutputDescription

Sets a custom description for the multistate output.

bool setMultistateOutputDescription(const char *description);
  • description - Description string

This function will return true if successful, false otherwise.

setMultistateOutputStates

Sets the number of states for the multistate output.

bool setMultistateOutputStates(uint16_t number_of_states);
  • number_of_states - Number of discrete states (1-65535)

This function will return true if successful, false otherwise.

Value Control

setMultistateOutput

Sets the multistate output value.

bool setMultistateOutput(uint16_t state);
  • state - State index (0 to number_of_states-1)

This function will return true if successful, false otherwise.

getMultistateOutput

Gets the current multistate output value.

uint16_t getMultistateOutput();

This function returns the current multistate output state.

State Information

getMultistateInputStateNamesLength

Gets the number of state names for multistate input.

uint16_t getMultistateInputStateNamesLength();

This function returns the number of state names configured for multistate input.

getMultistateOutputStateNamesLength

Gets the number of state names for multistate output.

uint16_t getMultistateOutputStateNamesLength();

This function returns the number of state names configured for multistate output.

Reporting Methods

reportMultistateOutput

Manually reports the current multistate output value.

bool reportMultistateOutput();

This function will return true if successful, false otherwise.

Event Handling

onMultistateOutputChange

Sets a callback function to be called when the multistate output value changes.

void onMultistateOutputChange(void (*callback)(uint16_t state));
  • callback - Function to call when multistate output changes, receives the new state value

Example

Multistate Input/Output

// Copyright 2025 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @brief This example demonstrates Zigbee multistate input / output device.
 *
 * The example demonstrates how to use Zigbee library to create a router multistate device.
 * In the example, we have two multistate devices:
 * - zbMultistateDevice: uses defined application states from Zigbee specification
 * - zbMultistateDeviceCustom: uses custom application states (user defined)
 *
 * Proper Zigbee mode must be selected in Tools->Zigbee mode
 * and also the correct partition scheme must be selected in Tools->Partition Scheme.
 *
 * Please check the README.md for instructions and more detailed description.
 *
 * NOTE: HomeAssistant ZHA does not support multistate input and output clusters yet.
 *
 * Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
 */

#ifndef ZIGBEE_MODE_ZCZR
#error "Zigbee coordinator/router device mode is not selected in Tools->Zigbee mode"
#endif

#include "Zigbee.h"

/* Zigbee multistate device configuration */
#define MULTISTATE_DEVICE_ENDPOINT_NUMBER 1

uint8_t button = BOOT_PIN;

// zbMultistateDevice will use defined application states
ZigbeeMultistate zbMultistateDevice = ZigbeeMultistate(MULTISTATE_DEVICE_ENDPOINT_NUMBER);

// zbMultistateDeviceCustom will use custom application states (user defined)
ZigbeeMultistate zbMultistateDeviceCustom = ZigbeeMultistate(MULTISTATE_DEVICE_ENDPOINT_NUMBER + 1);

const char *multistate_custom_state_names[6] = {"Off", "On", "UltraSlow", "Slow", "Fast", "SuperFast"};

void onStateChange(uint16_t state) {
  // print the state
  Serial.printf("Received state change: %d\r\n", state);
  // print the state name using the stored state names
  const char *const *state_names = ZB_MULTISTATE_APPLICATION_TYPE_7_STATE_NAMES;
  if (state_names && state < zbMultistateDevice.getMultistateOutputStateNamesLength()) {
    Serial.printf("State name: %s\r\n", state_names[state]);
  }
  // print state index of possible options
  Serial.printf("State index: %d / %d\r\n", state, zbMultistateDevice.getMultistateOutputStateNamesLength() - 1);
}

void onStateChangeCustom(uint16_t state) {
  // print the state
  Serial.printf("Received state change: %d\r\n", state);
  // print the state name using the stored state names
  if (state < zbMultistateDeviceCustom.getMultistateOutputStateNamesLength()) {
    Serial.printf("State name: %s\r\n", multistate_custom_state_names[state]);
  }
  // print state index of possible options
  Serial.printf("State index: %d / %d\r\n", state, zbMultistateDeviceCustom.getMultistateOutputStateNamesLength() - 1);

  Serial.print("Changing to fan mode to: ");
  switch (state) {
    case 0:  Serial.println("Off"); break;
    case 1:  Serial.println("On"); break;
    case 2:  Serial.println("UltraSlow"); break;
    case 3:  Serial.println("Slow"); break;
    case 4:  Serial.println("Fast"); break;
    case 5:  Serial.println("SuperFast"); break;
    default: Serial.println("Invalid state"); break;
  }
}

void setup() {
  log_d("Starting serial");
  Serial.begin(115200);

  // Init button switch
  log_d("Init button switch");
  pinMode(button, INPUT_PULLUP);

  // Optional: set Zigbee device name and model
  log_d("Set Zigbee device name and model");
  zbMultistateDevice.setManufacturerAndModel("Espressif", "ZigbeeMultistateDevice");

  // Set up analog input
  log_d("Add Multistate Input");
  zbMultistateDevice.addMultistateInput();
  log_d("Set Multistate Input Application");
  zbMultistateDevice.setMultistateInputApplication(ZB_MULTISTATE_APPLICATION_TYPE_0_INDEX);
  log_d("Set Multistate Input Description");
  zbMultistateDevice.setMultistateInputDescription("Fan (on/off/auto)");
  zbMultistateDevice.setMultistateInputStates(ZB_MULTISTATE_APPLICATION_TYPE_0_NUM_STATES);

  // Set up analog output
  log_d("Add Multistate Output");
  zbMultistateDevice.addMultistateOutput();
  log_d("Set Multistate Output Application");
  zbMultistateDevice.setMultistateOutputApplication(ZB_MULTISTATE_APPLICATION_TYPE_7_INDEX);
  log_d("Set Multistate Output Description");
  zbMultistateDevice.setMultistateOutputDescription("Light (high/normal/low)");
  zbMultistateDevice.setMultistateOutputStates(ZB_MULTISTATE_APPLICATION_TYPE_7_NUM_STATES);

  // Set up custom output
  log_d("Add Multistate Output");
  zbMultistateDeviceCustom.addMultistateOutput();
  log_d("Set Multistate Output Application");
  zbMultistateDeviceCustom.setMultistateOutputApplication(ZB_MULTISTATE_APPLICATION_TYPE_OTHER_INDEX);
  log_d("Set Multistate Output Description");
  zbMultistateDeviceCustom.setMultistateOutputDescription("Fan (on/off/slow/medium/fast)");
  zbMultistateDeviceCustom.setMultistateOutputStates(5);

  // Set callback function for multistate output change
  log_d("Set callback function for multistate output change");
  zbMultistateDevice.onMultistateOutputChange(onStateChange);
  zbMultistateDeviceCustom.onMultistateOutputChange(onStateChangeCustom);

  // Add endpoints to Zigbee Core
  log_d("Add endpoints to Zigbee Core");
  Zigbee.addEndpoint(&zbMultistateDevice);
  Zigbee.addEndpoint(&zbMultistateDeviceCustom);

  Serial.println("Starting Zigbee...");
  // When all EPs are registered, start Zigbee in Router Device mode
  if (!Zigbee.begin(ZIGBEE_ROUTER)) {
    Serial.println("Zigbee failed to start!");
    Serial.println("Rebooting...");
    ESP.restart();
  } else {
    Serial.println("Zigbee started successfully!");
  }
  Serial.println("Connecting to network");
  while (!Zigbee.connected()) {
    Serial.print(".");
    delay(100);
  }
  Serial.println("Connected");
}

void loop() {
  // Checking button for factory reset and reporting
  if (digitalRead(button) == LOW) {  // Push button pressed
    // Key debounce handling
    delay(100);
    int startTime = millis();
    while (digitalRead(button) == LOW) {
      delay(50);
      if ((millis() - startTime) > 3000) {
        // If key pressed for more than 3secs, factory reset Zigbee and reboot
        Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
        delay(1000);
        Zigbee.factoryReset();
      }
    }
    // For demonstration purposes, increment the multistate output/input value by 1
    if (zbMultistateDevice.getMultistateOutput() < zbMultistateDevice.getMultistateOutputStateNamesLength() - 1) {
      zbMultistateDevice.setMultistateOutput(zbMultistateDevice.getMultistateOutput() + 1);
      zbMultistateDevice.reportMultistateOutput();
      zbMultistateDevice.setMultistateInput(zbMultistateDevice.getMultistateOutput());
      zbMultistateDevice.reportMultistateInput();
    } else {
      zbMultistateDevice.setMultistateOutput(0);
      zbMultistateDevice.reportMultistateOutput();
      zbMultistateDevice.setMultistateInput(zbMultistateDevice.getMultistateOutput());
      zbMultistateDevice.reportMultistateInput();
    }

    if (zbMultistateDeviceCustom.getMultistateOutput() < zbMultistateDeviceCustom.getMultistateOutputStateNamesLength() - 1) {
      zbMultistateDeviceCustom.setMultistateOutput(zbMultistateDeviceCustom.getMultistateOutput() + 1);
      zbMultistateDeviceCustom.reportMultistateOutput();
    } else {
      zbMultistateDeviceCustom.setMultistateOutput(0);
      zbMultistateDeviceCustom.reportMultistateOutput();
    }
  }
  delay(100);
}