Skip to content

Developers: Plugin Settings GUI

oznu edited this page Apr 14, 2020 · 78 revisions

Plugin authors can publish a config.schema.json which defines the config their plugin requires in the JSON Schema format (v6, v4 or v3). Homebridge Config UI X will use this file to generate a user-interfaces so users can set up a plugin without having to manually manipulate the config.json.

Enabling Support For Your Plugin

To add support for a GUI settings page for your plugin, all you need to do is define and publish the config.schema.json as part of your npm package. Homebridge Config UI X will detect this file and show the Settings button on the plugin page:

image

The config schema supports both Platform and Accessory plugin types.

Form Generator Playground

The forms are generated using Angular JSON Schema Form, you can test your schema and form layout using the demonstration playground. This JSON Schema Tool can also help you create your schema and then you can modify to your liking.

Basic Structure

{
  "pluginAlias": "Camera-ffmpeg",
  "pluginType": "platform",
  "singular": false,
  "headerDisplay": "Optional content to display above the plugin config. Supports markdown.",
  "footerDisplay": "Optional content to display below the plugin config. Supports markdown.",
  "schema": {
    "type": "object",
    "properties": {
      "name": {
        "title": "Name",
        "type": "string",
        "required": true
      }
    }
  },
  "form": null,
  "display": null
}
  • pluginAlias: The plugin identifier.
  • pluginType: The type of plugin, valid values are platform or accessory.
  • singular: If set to true the UI will not allow the user to add more than one config block. This is usually used for platform plugins where only a single config block should be present.
  • headerDisplay and footerDisplay: See Below for details.
  • form and display: These attributes are optional and can be used to further customise how the interface is presented to the user, see the playground for more examples.

You do not need to include the platform or accessory attribute in your schema object. This will be automatically added based on the pluginType.

Default Values

Setting default values is a great way to ensure your users get up and running as smoothly as possible - to do this just add the default attribute with the desired value:

{
  "pluginAlias": "daikin-esp8266-platform",
  "pluginType": "platform",
  "singular": true,
  "schema": {
    "type": "object",
    "properties": {
      "name": {
        "title": "Name",
        "type": "string",
        "default": "Daikin AC",
        "required": true
      }
    }
  }
}

Element Types

Checkboxes

Checkboxes return true or false and can be implemented using the JSON Schema boolean type.

{
  "pluginAlias": "config",
  "pluginType": "platform",
  "schema": {
    "type": "object",
    "properties": {
      "sudo": {
        "title": "Use Sudo",
        "type": "boolean"
      }
    }
  }
}

Arrays

Arrays of any size are supported using the JSON Schema array type. Arrays of objects are also supported, see the complex example below.

{
  "pluginAlias": "UniFi Occupancy Sensor",
  "pluginType": "accessory",
  "schema": {
    "type": "object",
    "properties": {
      "watch": {
        "title": "Watched Devices",
        "type": "array",
        "items": {
          "title": "MAC Address",
          "type": "string"
        }
      }
    }
  }
}

Dropdown Select Boxes

Dropdown select boxes can be implemented using the JSON Schema oneOf attribute.

{
  "pluginAlias": "config",
  "pluginType": "platform",
  "schema": {
    "type": "object",
    "properties": {
      "auth": {
        "title": "Auth Mode",
        "type": "string",
        "default": "form",
        "oneOf": [
          { "title": "Form", "enum": ["form"] },
          { "title": "Basic Auth", "enum": ["basic"] },
          { "title": "None", "enum": ["none"] }
        ],
        "required": true
      }
    }
  }
}

Typeahead Data Lists

Datalists suggest values to the user in a similar way to drop down boxes, but still allowing the user to enter their own value if they like.

{
  "pluginAlias": "config",
  "pluginType": "platform",
  "schema": {
    "type": "object",
    "properties": {
        "vcodec": {
        "title": "Video Codec",
        "type": "string",
        "placeholder": "libx264",
        "description": "The ffmpeg video processing codec to use.",
        "typeahead": {
          "source": [
            "libx264",
            "copy",
            "h264_omx",
            "h264",
            "h264_mmal"
          ]
        }
      }
    }
  }
}

Help Text

Help text can be added below each input using the description attribute.

{
  "pluginAlias": "Camera-ffmpeg",
  "pluginType": "platform",
  "schema": {
    "type": "object",
    "properties": {
      "name": {
        "title": "Name",
        "type": "string",
        "required": true,
        "description": "The name of the plugin"
      }
    }
  }
}

Validators

String Length

The min and max string length can be set using the minLength and maxLength JSON Schema attributes. Both are optional.

{
  "pluginAlias": "Camera-ffmpeg",
  "pluginType": "platform",
  "schema": {
    "type": "object",
    "properties": {
      "name": {
        "title": "Name",
        "type": "string",
        "minLength": 4,
        "maxLength": 10
      }
    }
  }
}

Number Range

Numeric input can be validated using the minimum and maximum JSON Schema attributes. Providing both a min and max value will display a range slider to the user.

{
  "pluginAlias": "Camera-ffmpeg",
  "pluginType": "platform",
  "schema": {
    "type": "object",
    "properties": {
      "timeout": {
        "title": "Timeout",
        "type": "integer",
        "minimum": 15,
        "maximum": 3000
      }
    }
  }
}

Pattern

You can validate the user's input using a regex in the JSON Schema pattern attribute.

{
  "pluginAlias": "UniFi Occupancy Sensor",
  "pluginType": "accessory",
  "schema": {
    "type": "object",
    "properties": {
      "mac": {
        "title": "MAC Address",
        "type": "string",
        "pattern": "^([A-Fa-f0-9]{2}:){5}[A-Fa-f0-9]{2}$"
      }
    }
  }
}

Format

You can also use the built-in format types to validate the data:

  • date-time: Date representation, as defined by RFC 3339, section 5.6.
  • email: Internet email address, see RFC 5322, section 3.4.1.
  • hostname: Internet hostname, see RFC 1034, section 3.1.
  • ipv4: IPv4 address, according to dotted-quad ABNF syntax as defined in RFC 2673, section 3.2.
  • ipv6: IPv6 address, as defined in RFC 2373, section 2.2.
  • uri: A universal resource identifier (URI), according to RFC3986.

Example:

{
  "pluginAlias": "UniFi Occupancy Sensor",
  "pluginType": "accessory",
  "schema": {
    "type": "object",
    "properties": {
      "host": {
        "title": "IP Address / Hostname",
        "type": "string",
        "required": true,
        "format": "hostname"
      }
    }
  }
}

Header and Footer Display

Plugin authors can display additional content in the user interface above and below the config form using the headerDisplay and footerDisplay attributes. These displays support markdown and plain text.

Things to keep in mind when creating displays:

  • Keep it short. Screen real estate is limited, rather than displaying your entire setup guide it might be better to provide a link to your wiki instead.
  • Remote images are supported when using the full image URI.
    • Images may only be loaded from https://raw.githubusercontent.com.
  • Absolutely no HTML tags. GitHub flavoured markdown supports some HTML tags such as a and img. We do not, these tags will not be rendered in the interface.
  • Custom JavaScript is not supported anywhere.

Simple Example

This example shows the simplest config.schema.json example.

{
  "pluginAlias": "BelkinWeMo",
  "pluginType": "platform",
  "schema": {
    "type": "object",
    "properties": {
      "name": {
        "title": "Name",
        "type": "string",
        "default": "WeMo Platform",
        "required": true
      }
    }
  }
}

Complex Example

This example shows the config schema for KhaosT/homebridge-camera-ffmpeg. The user interface will allow users to add to the array of cameras.

{
  "pluginAlias": "Camera-ffmpeg",
  "pluginType": "platform",
  "headerDisplay": "The **ffmpeg** binary must be installed on your system for this plugin to work.",
  "footerDisplay": "For help please see the [wiki](https://github.com/KhaosT/homebridge-camera-ffmpeg/wiki).",
  "schema": {
    "name": {
      "title": "Name",
      "type": "string",
      "default": "Camera ffmpeg",
      "required": true
    },
    "cameras": {
      "type": "array",
      "items": {
        "title": "Camera Config",
        "type": "object",
        "properties": {
          "source": {
            "title": "Source",
            "type": "string",
            "default": "-re -i rtsp://myfancy_rtsp_stream",
            "required": true
          },
          "stillImageSource": {
            "title": "Still Image Source",
            "type": "string",
            "default": "-i http://faster_still_image_grab_url/this_is_optional.jpg"
          },
          "maxStreams": {
            "title": "Maximum Number of Streams",
            "type": "integer",
            "default": 2,
            "minimum": 1,
            "description": "The maximum number of streams that will be generated for this camera"
          },
          "maxWidth": {
            "title": "Maximum Width",
            "type": "integer",
            "default": 1280,
            "minimum": 1,
            "description": "The maximum width reported to HomeKit"
          },
          "maxHeight": {
            "title": "Maximum Height",
            "type": "integer",
            "default": 720,
            "minimum": 1,
            "description": "The maximum height reported to HomeKit"
          },
          "maxFPS": {
            "title": "Maximum Height",
            "type": "integer",
            "default": 10,
            "minimum": 1,
            "description": "The maximum frame rate of the stream"
          },
          "maxBitrate": {
            "title": "Maximum Height",
            "type": "integer",
            "default": 300,
            "minimum": 1,
            "description": "The maximum bit rate of the stream"
          },
          "vcodec": {
            "title": "Video Codec",
            "type": "string",
            "default": "libx264"
          },
          "packetSize": {
            "title": "Packet Size",
            "type": "number",
            "default": 1316,
            "multipleOf": 188.0
          },
          "audio": {
            "title": "Enable Audio?",
            "type": "boolean",
            "default": false
          },
          "debug": {
            "title": "Enable Debug Mode?",
            "type": "boolean",
            "default": false
          }
        }
      }
    }
  }
}

Limitations and Workarounds

User-Defined Keys

A small number of existing plugin configs may not work with the automatically generated forms. Specifically, the JSON Schema patternProperties attribute is not supported. The patternProperties attribute would be used when the plugin requires user-defined keys. Since this is not supported plugin authors should swap such config blocks to use arrays instead.

Here is an example Homebridge config block that we can't generate a form for because it depends on user-defined key names:

"platforms": [
  {
    "platform": "Some Platform",
    "users": {
      "user-one": "password",
      "user-two": "password"
    }
  }
]

This can be fixed by changing the config to use an array instead of an object:

"platforms": [
  {
    "platform": "Some Platform",
    "users": [
      { "key": "user-one", "value": "password" },
      { "key": "user-two", "value": "password" }
    ]
  }
]

The config.schema.json file would then look like this:

{
  "pluginAlias": "Some Platform",
  "pluginType": "platform",
  "schema": {
    "type": "object",
    "properties": {
      "users": {
        "title": "Users",
        "type": "array",
        "items": {
          "title": "User",
          "type": "object",
          "properties": {
            "key": {
              "title": "Username",
              "type": "string",
              "required": true
            },
            "value": {
              "title": "Password",
              "type": "string",
              "required": true
            }
          }
        }
      }
    }
  }
}

If the plugin requires the config block to be put back into the original object format they can easily transform the array at runtime:

const users = {};
config.users.forEach(x => users[x.key] = x.value);

Advanced Requirements

If you have more complex requirements than what the standard config.schema.json syntax can support; for example, an OAUTH2 workflow, or exchanging username and password for a token, please reach out to me (@oznu) and we can work something out.

Examples of advanced implementations:

  • homebridge-ring - supports exchanging Ring credentials for a refreshToken directly from the user interface (with 2FA support).
  • homebridge-honeywell-home - supports an OAUTH2 workflow to retrieve a token.
  • homebridge-gsh - supports an OAUTH2 workflow to get an access token.

Plugins Using This

These are examples of plugins that currently implement the Plugin Settings GUI using the config.schema.json:

homebridge-zp screenshot (dark mode theme):

image

homebridge-config-ui-x screenshot:

image

homebridge-alexa screenshot:

image

Clone this wiki locally