Homebridge MQTT-Thing

repository·master·Indexed 19 days ago

https://github.com/arachnetech/homebridge-mqttthing

A Homebridge plugin that integrates various HomeKit accessory types using the MQTT protocol. It allows users to bridge MQTT-based devices into HomeKit by defining control (setXXX) and status (getXXX) topics. Supported accessories include sensors (air pressure, quality, CO2, CO, contact, humidity, etc.), controls (fans, garage door openers, light bulbs, locks, switches, thermostats), and other devices like air purifiers and doorbells. Requires Node.js 16 or later since version 1.1.45.

Tokens
37.4K
Snippets
51
Records
102
Agent score
66%

What's inside homebridge-mqttthing

  1. Understand MQTT topic patterns in Homebridge MQTT-Thing

    master

    Homebridge MQTT-Thing uses two distinct patterns for MQTT topics to manage device interaction:

    1. Control topics (setXXX): These are published by the plugin to command a device (e.g., sending a command to turn a light on).
    2. Status/notification topics (getXXX): These are published by the device to inform the plugin of state changes (e.g., a sensor reporting motion or a device confirming a command was executed).

    When configuring your accessories, ensure you distinguish between topics the plugin needs to listen to and topics the plugin needs to publish to.

  2. How Codecs work in Homebridge MQTT-Thing

    master

    A Codec is a Node.js module used to transform incoming (MQTT to Homebridge) and outgoing (Homebridge to MQTT) data. Unlike apply functions, Codecs are defined in separate JavaScript files and referenced in your configuration via the codec setting.

    Key Behaviors

    • Encoding: Transforms data before it is published to MQTT.
    • Decoding: Transforms data received from MQTT before it is processed by MQTT-Thing.
    • Scope: A codec can process all properties or target specific properties. It can also suppress messages or generate new ones.
    • Lifecycle: A codec is loaded once. To maintain accessory-specific state, you must store it within the init() function to ensure it is unique to the instance created during initialization.
    // Example of a minimal no-op codec structure
    function init() {
        function encode( message ) {
            return message; // no-op
        }
    
        function decode( message ) {
            return message; // no-op
        }
    
        return {
            encode,
            decode
        };
    }
    
    module.exports = {
        init
    };
  3. Group multiple services using custom accessories

    master

    You can group services provided by multiple accessories into a single HomeKit accessory by creating an accessory with the type set to custom. This accessory must contain a services array where each element defines an individual service.

    Key Rules for Custom Accessories:

    • Inheritance: Settings defined at the top-level custom accessory (e.g., integerValue, url, logMqtt) apply to all services within the services array.
    • Overriding: Settings specified within an individual service object will override the defaults provided at the custom accessory level.
    • Limitations:
      • Custom accessories are intended for simple services only. Do not use them to replicate complex accessories like 'weather station' which already bundle multiple services.
      • Custom accessories cannot be configured via the Config UI X interface; they must be configured manually in the Homebridge config.json.
    {
        "accessory": "mqttthing",
        "type": "custom",
        "name": "Composite",
        "url": "mqttserver",
        "logMqtt": true,
        "integerValue": true,
        "services": [
            {
                "type": "switch",
                "name": "Switch 1",
                "topics": {
                    "getOn": "home/get/switch1/POWER",
                    "setOn": "home/set/switch1/POWER"
                }
            },
            {
                "type": "switch",
                "name": "Switch 2",
                "topics": {
                    "getOn": "home/get/switch2/POWER",
                    "setOn": "home/set/switch2/POWER"
                }
            },
            {
                "type": "motionSensor",
                "name": "My PIR",
                "topics": {
                    "getMotionDetected": "home/get/pir/STATUS",
                    "getStatusActive": "home/get/pir/ACTIVE",
                    "getStatusFault": "home/get/pir/FAULT",
                    "getStatusLowBattery": "home/get/pir/BATLOW"
                }
            }
        ]
    }
  4. Understand MQTT topic patterns in MQTT-Thing

    master

    MQTT-Thing uses two distinct categories of MQTT topics to facilitate communication between Homebridge and your devices:

    1. Control topics (setXXX): These are published by MQTT-Thing to command a device to change its state (e.g., turning a light on or off).
    2. Status/notification topics (getXXX): These are published by the device to notify MQTT-Thing of state changes or events (e.g., a sensor detecting motion or a device confirming a command was executed).

    For detailed configuration schemas and codec information, refer to docs/Configuration.md and docs/Codecs.md.

  5. Install Homebridge MQTT-Thing

    master

    You can install the plugin via NPM or through the Homebridge Config UI X interface. If installing via NPM, it should be installed globally.

    Requirements:

    • Node.js 16 or later (required since version 1.1.45).

    Note: If you are looking for a simpler integration specifically for Zigbee devices exposed via Zigbee2MQTT, consider using the z2m plugin instead.

    npm install -g homebridge-mqttthing
  6. Configure Homebridge MQTT-Thing

    master

    The plugin is configured within your Homebridge config.json file. For a more user-friendly experience, most configuration settings can be managed via the Homebridge Config UI X interface.

    Note: When reviewing documentation, values enclosed in <> (e.g., <example_topic>) are descriptive placeholders and should not be copied directly into your configuration. Use actual topic names or values instead.

  7. Use Apply Functions for Custom Payload Transformation

    master

    If an MQTT message is not a simple value, you can use a JavaScript apply function to decode or transform it. This is done directly in the configuration file (not supported in config-ui-x).

    Replace the topic string with an object containing:

    • topic: The actual MQTT topic string.
    • apply: A complete JavaScript function body that returns the processed value. It accepts message (the original payload) and an optional state object.

    state.global can be used to share data across all topics.

    Examples

    Decoding JSON:

    "getCurrentTemperature": {
        "topic": "outdoor",
        "apply": "return JSON.parse(message).temperature.toFixed(1);"
    }

    Scaling values (e.g., 0-100 to 0-255):

    "getBrightness": {
        "topic": "test/lightbulb/getBrightness",
        "apply": "return Math.round( message / 2.55 );"
    }
    {
      "topics": {
        "getCurrentTemperature": {
          "topic": "outdoor",
          "apply": "return JSON.parse(message).temperature.toFixed(1);"
        }
      }
    }
  8. Create a custom codec in MQTT-Thing

    master

    A codec is a standalone JavaScript file that allows you to apply custom logic to accessories, such as encoding/decoding messages or running arbitrary background tasks.

    To create a custom codec:

    1. Define an init( params ) function. The params object provides access to config (the accessory configuration) and publish (a function to send MQTT messages).
    2. Use init to declare local state that persists for the life of the accessory.
    3. Return an object containing a properties map. Each property in the map can define its own decode and encode functions to handle incoming and outgoing MQTT messages.

    For a minimal starting point, you can use the no-op implementation found in test/empty-codec.js in the repository.

    /**
     * Example: toggle.js
     * Toggles switch state whenever any message is received.
     */
    'use strict'
    
    module.exports = {
        init: function() {
            let state = false; // Local state declared within init()
            return {
                properties: {
                    on: {
                        decode: function() {
                            state = ! state;
                            return state;
                        },
                        encode: function( msg ) {
                            state = msg;
                            return msg;
                        }
                    }
                }
            };
        }
    };
  9. Enable History Service (Eve App)

    master

    For specific sensor types, you can enable a History Service powered by fakegato-history. This allows viewing historical data in the Eve App (Note: Home.app does not support this).

    Supported Types: Temperature, Humidity, Air Pressure, Air Quality, Motion, Contact, Outlet (power consumption), and Switch.

    Configuration:

    • history: Set to true to enable.
    • historyOptions: An object to fine-tune behavior:
      • size: Max data points (default: 4032).
      • noAutoTimer: Disable averaging/repeating 10min timer.
      • noAutoRepeat: Disable repetition of last value if no data received in 10min.
      • mergeInterval: Merge events close in time (minutes) (Motion sensors only).
      • persistencePath: Directory to store history data.

    Warning: Avoid using / in Information Service characteristics (like serialNumber) as it may break history data. Ensure serialNumber is unique if controlling multiple accessories of the same type to prevent Eve.app from merging histories.

  10. Configure a Temperature Sensor accessory

    master

    The temperatureSensor type reports temperature in degrees Celsius (up to 1 decimal place).

    Key configuration options:

    • minTemperature / maxTemperature: Overrides the default HomeKit range. If not specified, the minimum is set to -100°C for compatibility.
    • history: Set to true to enable the History service for the Eve App.

    Available status topics include getStatusActive, getStatusFault, getStatusTampered, and getStatusLowBattery.

    {
        "accessory": "mqttthing",
        "type": "temperatureSensor",
        "name": "<name of sensor>",
        "url": "<url of MQTT server (optional)",
        "username": "<username for MQTT (optional)",
        "password": "<password for MQTT (optional)",
        "caption": "<label (optional)",
        "topics":
        {
            "getCurrentTemperature":        "<topic used to provide 'current temperature'>",
            "getStatusActive":              "<topic used to provide 'active' status (optional)",
            "getStatusFault":               "<topic used to provide 'fault' status (optional)",
            "getStatusTampered":            "<topic used to provide 'tampered' status (optional)",
            "getStatusLowBattery":          "<topic used to provide 'low battery' status (optional)"
        },
        "history": "<true to enable History service for Eve App (optional)>",
        "minTemperature": minimum_target_temperature,
        "maxTemperature": maximum_taret_temperature
    }
  11. Configure a Television accessory

    master

    To represent a television in HomeKit, use the television type. You can define multiple input sources (e.g., HDMI1, HDMI2, Live TV) using the inputs array. If inputs is provided, you can use setActiveInput and getActiveInput topics to manage them. Remote control commands can be sent via the setRemoteKey topic, which can be triggered from the iOS Control Center.

    Use integerValue: true if your MQTT device uses 1|0 instead of true|false for on/off states. You can also customize the onValue and offValue strings.

    {
        "accessory": "mqttthing",
        "type": "television",
        "name": "<name of TV>",
        "url": "<url of MQTT server (optional)",
        "username": "<username for MQTT (optional)",
        "password": "<password for MQTT (optional)",
        "caption": "<label (optional)",
        "topics":
        {
            "setActive":            "<topic to set the status>",
            "getActive":            "<topic to get the status>",
            "setActiveInput":       "<topic to set the active input source (optional)",
            "getActiveInput":       "<topic to get the active input source (optional)",
            "setRemoteKey":         "<topic for publishing remote key actions (optional)"
        },
        "inputs": [
            {
                "name":     "<name for first input source>",
                "value":    "<MQTT value for first input source>"
            },
            {
                "name":     "<name for second input source>",
                "value":    "<MQTT value for second input source>"
            }
        ],
        "integerValue":     "<true to use 1|0 instead of true|false default onValue and offValue>",
        "onValue":          "<value representing on (optional)",
        "offValue":         "<value representing off (optional)"
    }