Plugin Config Schema
By adding a config schema to your plugin, users will be able to configure your plugin in the Homebridge UI without having to manually edit the Homebridge config.json
file.
Plugin authors should publish a config.schema.json
file which defines the config their plugin requires in the JSON Schema format (v6, v4 or v3).
- Enabling Support For Your Plugin
- Form Generator Playground
- Basic Structure
- Default Values
- Placeholder Values
- Element Types
- Help Text
- Validators
- Conditional Display
- Header and Footer Display
- Simple Example
- Complex Example
- Limitations and Workarounds
- Advanced Requirements
- Plugins Using This
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:
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 areplatform
oraccessory
.singular
: If set totrue
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
andfooterDisplay
: See Below for details.form
anddisplay
: 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
}
}
}
}
Placeholder Values
Placeholder values should be used for optional fields that have a default value defined in your plugins code. The place holder value will not be saved to the config.json
unless modified by the user.
{
"pluginAlias": "daikin-esp8266-platform",
"pluginType": "platform",
"singular": true,
"schema": {
"type": "object",
"properties": {
"minTemp": {
"title": "Minimum Temperature",
"type": "number",
"placeholder": "18"
}
}
}
}
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"
}
}
}
}
"sudo": true
Checkboxes can also build a string of arrays from a pre-defined list. The below example will generate an array named disabled_modes
with the values Off
, Home
, Night
, or Away
depending on which checkboxes are selected.
{
"pluginAlias": "config",
"pluginType": "platform",
"schema": {
"type": "object",
"properties": {
"disabled_modes": {
"title": "Disabled Modes",
"type": "array",
"uniqueItems": true,
"items": {
"title": "Mode",
"type": "string",
"enum": [
"Off",
"Home",
"Night",
"Away"
]
}
}
}
}
"disabled_modes": ["Off", "Night"]
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"
}
}
}
}
Conditional Display
You can set rules for when a field should be displayed or hidden. For example, you might only show some fields, based on the value of another.
"condition": {
"functionBody": "return model.blah === 'foo';"
}
functionBody
is called with model
and arrayIndices
as arguments and should return a boolean
value.
model
- the current object representation of the current plugin config.arrayIndices
- if the condition is being called inside an array, this is the array index of the current item.
Full Example:
{
"pluginAlias": "BelkinWeMo",
"pluginType": "platform",
"schema": {
"type": "object",
"properties": {
"name": {
"title": "Name",
"type": "string",
"default": "WeMo Platform",
"required": true
},
"showOption": {
"title": "Should Show Other Option",
"type": "boolean"
},
"thatOtherOption": {
"title": "This option is hidden unless `showOption` is true",
"type": "string",
"condition": {
"functionBody": "return model.showOption === true;"
}
}
}
}
}
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
.
- Images may only be loaded from
- Absolutely no HTML tags. GitHub flavoured markdown supports some HTML tags such as
a
andimg
. 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 Sunoo/homebridge-camera-ffmpeg. The user interface will allow users to add to the array of cameras, and includes a custom layout:
{
"pluginAlias": "Camera-ffmpeg",
"pluginType": "platform",
"singular": true,
"headerDisplay": "Cameras are exposed to HomeKit as separate accessories and each needs to be manually paired.\n\n1. Open the Home <img src='https://user-images.githubusercontent.com/3979615/78010622-4ea1d380-738e-11ea-8a17-e6a465eeec35.png' height='16.42px'> app on your device.\n2. Tap the Home tab, then tap <img src='https://user-images.githubusercontent.com/3979615/78010869-9aed1380-738e-11ea-9644-9f46b3633026.png' height='16.42px'>.\n3. Tap *Add Accessory*, and select *I Don't Have a Code or Cannot Scan*.\n4. Enter the Homebridge PIN, this can be found under the QR code in Homebridge UI or your Homebridge logs, alternatively you can select *Use Camera* and scan the QR code again.\n\nFor help and examples of common configurations please read the [wiki](https://github.com/KhaosT/homebridge-camera-ffmpeg/wiki).",
"footerDisplay": "The **ffmpeg** binary must be installed on your system for this plugin to work.",
"schema": {
"name": {
"title": "Name",
"type": "string"
},
"videoProcessor": {
"title": "Video Processor",
"type": "string"
},
"interfaceName": {
"title": "Interface Name",
"type": "string"
},
"cameras": {
"type": "array",
"items": {
"title": "Cameras",
"type": "object",
"properties": {
"name": {
"title": "Name",
"type": "string",
"placeholder": "Enter camera name...",
"required": true
},
"uploader": {
"type": "boolean"
},
"manufacturer": {
"type": "string"
},
"model": {
"type": "string"
},
"serialNumber": {
"type": "string"
},
"firmwareRevision": {
"type": "string"
},
"motion": {
"title": "Enable IOS 13 Motion Notifications",
"type": "boolean"
},
"videoConfig": {
"title": "Video Configuration",
"type": "object",
"properties": {
"source": {
"title": "Source",
"type": "string",
"placeholder": "-re -i rtsp://myfancy_rtsp_stream",
"required": true
},
"stillImageSource": {
"title": "Still Image Source",
"type": "string"
},
"maxStreams": {
"title": "Maximum Number of Streams",
"type": "integer",
"placeholder": 2,
"minimum": 1,
"description": "The maximum number of streams that will be generated for this camera"
},
"maxWidth": {
"title": "Maximum Width",
"type": "integer",
"placeholder": 1280,
"minimum": 1,
"description": "The maximum width reported to HomeKit"
},
"maxHeight": {
"title": "Maximum Height",
"type": "integer",
"placeholder": 720,
"minimum": 1,
"description": "The maximum height reported to HomeKit"
},
"maxFPS": {
"title": "Maximum FPS",
"type": "integer",
"placeholder": 10,
"minimum": 1,
"description": "The maximum frame rate of the stream"
},
"maxBitrate": {
"title": "Maximum Bitrate",
"type": "integer",
"placeholder": 300,
"minimum": 1,
"description": "The maximum bit rate of the stream"
},
"preserveRatio": {
"title": "Preserve Ratio",
"type": "string",
"description": "Can be set to either 'W' or 'H' with respective obvious meanings.",
"typeahead": {
"source": [
"W",
"H"
]
}
},
"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"
]
}
},
"packetSize": {
"title": "Packet Size",
"type": "number",
"placeholder": 1316,
"multipleOf": 188.0
},
"videoFilter": {
"title": "Allows a custom video filter to be passed to FFmpeg via -vf",
"type": "string",
"placeholder": "scale=1280:720"
},
"additionalCommandline": {
"title": "Additional of extra command line options",
"type": "string",
"description": "Additional of extra command line options to use with FFmpeg, for example '-loglevel verbose'"
},
"mapvideo": {
"type": "string",
"title": "Map Video",
"placeholder": "0:0",
"description": " Select the stream used for video"
},
"mapaudio": {
"type": "string",
"title": "Map Audio",
"placeholder": "0:1",
"description": " Select the stream used for audio"
},
"audio": {
"title": "Enable Audio (requires ffmpeg with libfdk-aac)",
"type": "boolean"
},
"vflip": {
"title": "Flip Stream Vertically",
"type": "boolean"
},
"hflip": {
"title": "Flip Stream Horizontally",
"type": "boolean"
},
"debug": {
"title": "Enable Debug Mode",
"type": "boolean"
}
}
}
}
}
}
},
"layout": [
{
"key": "cameras",
"type": "array",
"orderable": false,
"buttonText": "Add Camera",
"items": [
"cameras[].name",
"cameras[].videoConfig.source",
"cameras[].videoConfig.stillImageSource",
"cameras[].videoConfig.vcodec",
"cameras[].videoConfig.audio",
"cameras[].videoConfig.debug",
{
"key": "cameras[].videoConfig",
"type": "section",
"title": "Advanced Settings",
"expandable": true,
"expanded": false,
"items": [
"cameras[].videoConfig.maxStreams",
"cameras[].videoConfig.maxWidth",
"cameras[].videoConfig.maxHeight",
"cameras[].videoConfig.maxFPS",
"cameras[].videoConfig.maxBitrate",
"cameras[].videoConfig.preserveRatio",
"cameras[].videoConfig.packetSize",
"cameras[].videoConfig.videoFilter",
"cameras[].videoConfig.additionalCommandline",
"cameras[].videoConfig.mapvideo",
"cameras[].videoConfig.mapaudio",
"cameras[].videoConfig.vflip",
"cameras[].videoConfig.hflip",
"cameras[].motion"
]
}
]
}
]
}
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
Looking at example of existing schemas is a great way to learn.
These are examples of plugins that currently implement the Plugin Settings GUI using the config.schema.json
:
- homebridge-433-arduino
- homebridge-aladdin-connect-garage-door
- homebridge-alexa
- homebridge-apple-tv-remote
- homebridge-automation-chromecast
- homebridge-automower
- homebridge-blink
- homebridge-bravia
- homebridge-button-platform
- homebridge-camera-ffmpeg
- homebridge-canary
- homebridge-comelit-hub
- homebridge-config-ui-x
- homebridge-connex
- homebridge-chamberlain
- homebridge-daikin-esp8266
- homebridge-denon-tv
- homebridge-dummy
- homebridge-dummy-lock
- homebridge-dummy-thermostat
- homebridge-dyson-pure-cool
- homebridge-esp-irrigation-controller
- homebridge-esp-pir
- homebridge-esp8266-fan
- homebridge-ecoplug
- homebridge-eveatmo
- homebridge-g-on-alice
- homebridge-gogogate2
- homebridge-gsh
- homebridge-harmony
- homebridge-homeconnect
- homebridge-homeqtt-alarm
- homebridge-honeywell-home
- homebridge-honeywell-leak
- homebridge-hue
- homebridge-landroid
- homebridge-lgwebos-tv
- homebridge-luxtronik2
- homebridge-meross
- homebridge-mi-hygrothermograph
- homebridge-mqttthing
- homebridge-music
- homebridge-mysmartblinds-bridge
- homebridge-neato
- homebridge-nest
- homebridge-onkyo
- homebridge-openwebif-tv
- homebridge-otgw
- homebridge-p1
- homebridge-pihole
- homebridge-platform-maxcube
- homebridge-platform-wemo
- homebridge-ring
- homebridge-rpi
- homebridge-sengled
- homebridge-smartglass
- homebridge-smartthings-v2
- homebridge-sonos
- homebridge-sunricher-wifi
- homebridge-tesla
- homebridge-tion
- homebridge-tydom
- homebridge-ueboom
- homebridge-unifi-occupancy-sensor
- homebridge-videodoorbell
- homebridge-webos-tv
- homebridge-weather-plus
- homebridge-ws
- homebridge-xbox-tv
- homebridge-yalesmarthomealarm
- homebridge-zigbee
- homebridge-zp
homebridge-zp screenshot (dark mode theme):
homebridge-config-ui-x screenshot:
homebridge-alexa screenshot: