diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/404.html b/404.html new file mode 100644 index 0000000..caec9fa --- /dev/null +++ b/404.html @@ -0,0 +1,101 @@ + + + + + + + + + The Homie convention + + + + + + + + + + + + + +
+
+ +
+ + + +
+
+ homieiot/convention +
+
+
+ + + +
+ + +
+

Page Not Found

+
+

Page not found. Go back home.

+ + + + + + + + + diff --git a/categories/index.xml b/categories/index.xml new file mode 100644 index 0000000..ac1d9d2 --- /dev/null +++ b/categories/index.xml @@ -0,0 +1,11 @@ + + + + Categories on The Homie convention + https://homieiot.github.io/categories/ + Recent content in Categories on The Homie convention + Hugo + en-us + + + diff --git a/extensions/index.html b/extensions/index.html new file mode 100644 index 0000000..be920eb --- /dev/null +++ b/extensions/index.html @@ -0,0 +1,184 @@ + + + + + + + + + The Homie convention + + + + + + + + + + + + + +
+
+ +
+ + + +
+
+ homieiot/convention +
+
+
+ + + +
+ +
+
+
+

+ Homie Extensions +

+
Homie can be extended
+

The Homie convention defines a standardized way of how devices and services announce themselves and defines the structure and content of messages and their relation.

+

It does not define domain types, like a lightbulb and required properties for a Home Automation domain. +It also not defines procedures like Over-The-Air updates and metrics like cpu usage or memory consumption.

+

That is what extensions are for.

+

License

+

Every extension must be published using a license. +The license can be chosen freely, even proprietary licenses are possible. +The recommended license is the CCA 4.0, since this is the license Homie itself uses.

+

Extension Identifier

+

Every extension is identified by a unique ID and will be linked from this section. +The ID consists of the reverse domain name and a freely chosen suffix. +For example, an organization example.org wanting to add a feature our-feature would choose the extension ID org.example.our-feature. +The proper term homie is reserved and must not be used as the suffix or as part of the domain name.

+

If a device decides to implement an extension, a new entry in the $extensions list of that device is required. +The new extensions entry has to be formated in the following way: +extension ID:extension version:[homie versions] +where extension ID is the extension ID and extension version the version of the extension. +An extension might be designed to support different versions of the Homie convention. +This is reflected by the homie versions part, which is a semicolon (;) separated list of all supported Homie versions.

+

For example the Meta extension with the extension ID eu.epnw.meta and version 1.1.0 supports Homie 3.0.1 and 4.x. +The resulting $extensions entry is eu.epnw.meta:1.1.0:[3.0.1;4.x]. +The Legacy Stats extension with the extension ID org.homie.legacy-stats and version 0.1.1 supports Homie 4.x, so the $extensions entry is org.homie.legacy-stats:0.1.1:[4.x]. +Now, if the device super-car implements both extensions it publishes

+
homie/super-car/$extensions  "eu.epnw.meta:1.1.0:[3.0.1;4.x],org.homie.legacy-stats:0.1.1:[4.x]"
+

Extension Datatypes

+

An extension may define new datatypes and formats for them.

+

New Attributes

+

An extension may add new attributes to devices, nodes and properties. +The attributes MUST start with a $. Attributes are always retained. +An attribute may have no value, but instead act as a root for more nested attributes. +This is necessary to distinguish a nesting attribute from a node (if the nesting attribute is added to a device) or from a property (if its added to a node). +In the following example $certifications is the nesting attribute, which serves as root for the nested $european-union and $usa attributes.

+
homie/super-car/engine/$certifications/$european-union  "Euro 6b"
+homie/super-car/engine/$certifications/$usa  "Tier 3"
+

Nested attributes may start with $ but don’t have to. +This means, that in the above example using homie/super-car/engine/$certifications/usa would have been a valid topic, too. +An extension document may decide which extension attributes are required and which are optional. +If they are optional, default values may be given. Additionally, given examples for each attribute are recommended.

+

Create your own extension

+

To create an own extension,

+
    +
  • fork the Homie repository,
  • +
  • create a new branch,
  • +
  • write your extension in documents/extensions/your-extension-id.md based on the extension template,
  • +
  • and create a pull request.
  • +
+

Homie Extensions

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ExtensionExtension IdentifierDescriptionDocument
Legacy Statsorg.homie.legacy-statsThis extension adds the stats functionality of Homie 3.0.1 to Homie 4.0GitHub
Legacy Firmwareorg.homie.legacy-firmwareThis extension adds the firmware, mac and localip device attributes of Homie 3.0.1 to Homie 4.0GitHub
Metaeu.epnw.metaThis extension defines how to add metadata and tags to devices, nodes and propertiesGitHub
+ +
+
+
+ + + + + + + + diff --git a/fonts/FontAwesome.otf b/fonts/FontAwesome.otf new file mode 100644 index 0000000..401ec0f Binary files /dev/null and b/fonts/FontAwesome.otf differ diff --git a/fonts/fa-brands-400.eot b/fonts/fa-brands-400.eot new file mode 100644 index 0000000..1a675a4 Binary files /dev/null and b/fonts/fa-brands-400.eot differ diff --git a/fonts/fa-brands-400.svg b/fonts/fa-brands-400.svg new file mode 100644 index 0000000..5b60e84 --- /dev/null +++ b/fonts/fa-brands-400.svg @@ -0,0 +1,1184 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fonts/fa-brands-400.ttf b/fonts/fa-brands-400.ttf new file mode 100644 index 0000000..953d567 Binary files /dev/null and b/fonts/fa-brands-400.ttf differ diff --git a/fonts/fa-brands-400.woff b/fonts/fa-brands-400.woff new file mode 100644 index 0000000..1ae5263 Binary files /dev/null and b/fonts/fa-brands-400.woff differ diff --git a/fonts/fa-brands-400.woff2 b/fonts/fa-brands-400.woff2 new file mode 100644 index 0000000..4a07e40 Binary files /dev/null and b/fonts/fa-brands-400.woff2 differ diff --git a/fonts/fa-regular-400.eot b/fonts/fa-regular-400.eot new file mode 100644 index 0000000..db3ed40 Binary files /dev/null and b/fonts/fa-regular-400.eot differ diff --git a/fonts/fa-regular-400.svg b/fonts/fa-regular-400.svg new file mode 100644 index 0000000..cf3d065 --- /dev/null +++ b/fonts/fa-regular-400.svg @@ -0,0 +1,467 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fonts/fa-regular-400.ttf b/fonts/fa-regular-400.ttf new file mode 100644 index 0000000..235101c Binary files /dev/null and b/fonts/fa-regular-400.ttf differ diff --git a/fonts/fa-regular-400.woff b/fonts/fa-regular-400.woff new file mode 100644 index 0000000..9058e29 Binary files /dev/null and b/fonts/fa-regular-400.woff differ diff --git a/fonts/fa-regular-400.woff2 b/fonts/fa-regular-400.woff2 new file mode 100644 index 0000000..1489f64 Binary files /dev/null and b/fonts/fa-regular-400.woff2 differ diff --git a/fonts/fa-solid-900.eot b/fonts/fa-solid-900.eot new file mode 100644 index 0000000..cb8d3f0 Binary files /dev/null and b/fonts/fa-solid-900.eot differ diff --git a/fonts/fa-solid-900.svg b/fonts/fa-solid-900.svg new file mode 100644 index 0000000..bd7565a --- /dev/null +++ b/fonts/fa-solid-900.svg @@ -0,0 +1,2618 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fonts/fa-solid-900.ttf b/fonts/fa-solid-900.ttf new file mode 100644 index 0000000..7c92e98 Binary files /dev/null and b/fonts/fa-solid-900.ttf differ diff --git a/fonts/fa-solid-900.woff b/fonts/fa-solid-900.woff new file mode 100644 index 0000000..b7d52cf Binary files /dev/null and b/fonts/fa-solid-900.woff differ diff --git a/fonts/fa-solid-900.woff2 b/fonts/fa-solid-900.woff2 new file mode 100644 index 0000000..59d92b2 Binary files /dev/null and b/fonts/fa-solid-900.woff2 differ diff --git a/fonts/fontawesome-webfont.eot b/fonts/fontawesome-webfont.eot new file mode 100644 index 0000000..e9f60ca Binary files /dev/null and b/fonts/fontawesome-webfont.eot differ diff --git a/fonts/fontawesome-webfont.svg b/fonts/fontawesome-webfont.svg new file mode 100644 index 0000000..855c845 --- /dev/null +++ b/fonts/fontawesome-webfont.svg @@ -0,0 +1,2671 @@ + + + + +Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 + By ,,, +Copyright Dave Gandy 2016. All rights reserved. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fonts/fontawesome-webfont.ttf b/fonts/fontawesome-webfont.ttf new file mode 100644 index 0000000..35acda2 Binary files /dev/null and b/fonts/fontawesome-webfont.ttf differ diff --git a/fonts/fontawesome-webfont.woff b/fonts/fontawesome-webfont.woff new file mode 100644 index 0000000..400014a Binary files /dev/null and b/fonts/fontawesome-webfont.woff differ diff --git a/fonts/fontawesome-webfont.woff2 b/fonts/fontawesome-webfont.woff2 new file mode 100644 index 0000000..4d13fc6 Binary files /dev/null and b/fonts/fontawesome-webfont.woff2 differ diff --git a/get_involved/index.html b/get_involved/index.html new file mode 100644 index 0000000..0f115d7 --- /dev/null +++ b/get_involved/index.html @@ -0,0 +1,167 @@ + + + + + + + + + The Homie convention + + + + + + + + + + + + + +
+
+ +
+ + + +
+
+ homieiot/convention +
+
+
+ + + +
+ +
+
+
+

+ How to get involved? +

+
If you are missing a feature in the convention, something is unclear and requires better documentation or you have a better solution for a current feature: Get Involved!
+
+ +
Propose feature
+ +
+ Open a new GitHub Issue to start a discussion about a new feature. +Open a GitHub Pull Request to provide a change to the convention and have it discussed. +
+ + +
+ +
+ +
+ +
Feedback
+ +
+

The discussion period for a new feature lasts for a maximum of 3 weeks. Everyone involved can apply for a longer time frame if required. 3 weeks no progress means “Rejected” automatically.

+

Active maintainers are required to response within 3 weeks to a feature request.

+ +
+ +
+ +
+ +
+
Consensus
+
+ All active maintainers MUST agree on a new feature for it to be added.
An exception is the trivial case (spelling error, …) in which a single maintainer is sufficient. +
+ +
+ +
+ +
+ +
Merge feature
+ +
+

A new feature or specification change must be categorized and the commit message must be prefixed with:

+
    +
  • BREAKING: The commit message for breaking commits (MAJOR bumped)
  • +
  • feature: The commit message for new features commits (MINOR bumped)
  • +
  • fix: The commit message for fixes (PATCH bumped)
  • +
+
+ +
+ + +
+
+
+ + + + + + + + diff --git a/img/Homie-logo.svg b/img/Homie-logo.svg new file mode 100644 index 0000000..a518498 --- /dev/null +++ b/img/Homie-logo.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/img/homie-logo.png b/img/homie-logo.png new file mode 100644 index 0000000..625f9cf Binary files /dev/null and b/img/homie-logo.png differ diff --git a/img/works-with-homie-2x.png b/img/works-with-homie-2x.png new file mode 100644 index 0000000..3f3bd2a Binary files /dev/null and b/img/works-with-homie-2x.png differ diff --git a/img/works-with-homie.png b/img/works-with-homie.png new file mode 100644 index 0000000..348f744 Binary files /dev/null and b/img/works-with-homie.png differ diff --git a/img/works-with-homie.svg b/img/works-with-homie.svg new file mode 100644 index 0000000..a8174a0 --- /dev/null +++ b/img/works-with-homie.svg @@ -0,0 +1,19 @@ + + + + Works with + Created with Sketch. + + + + + + + + + + \ No newline at end of file diff --git a/implementations/index.html b/implementations/index.html new file mode 100644 index 0000000..993adc8 --- /dev/null +++ b/implementations/index.html @@ -0,0 +1,500 @@ + + + + + + + + + The Homie convention + + + + + + + + + + + + + +
+
+ +
+ + + +
+
+ homieiot/convention +
+
+
+ + + +
+ +
+
+
+

+ Implementations +

+
The Homie convention differentiates between Devices and Controllers.
+
+ + + + + + + + + + + + + +
Device
+ An instance of a physical piece of hardware is called a device. For example, a car, an Arduino/ESP8266 or a coffee machine. It publishes Nodes and Properties to the MQTT broker. +
+ +
+ + +
+ + + + + + + + + + + + + + +
Controller
+ A controller does not announce anything to the MQTT broker, but discovers and interacts with Devices. There can be more than one Controller interacting with the different devices on the same broker. +
+ +
+ + + +
+

Device

+

Find software libraries and full firmware projects for Devices in this section.

+

Libraries

+

A library can be included in your own project to act as a Homie device.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameLanguageHomie VersionWebsite/DownloadDescription
homie-cppC++3.0GitHubHomie C++ header only library
homie-pythonPython2.1GitHubHomie 3.0 as PR
MicrohomieµPython4.0.0GitHubMicroPython implementation
homie-ESP32C++2.0.1GitHubAn esp-idf component for the Homie convention.
kotlin-homieiotkotlin3.0.1GitHubKotlin implementation for the Homie convention
node-red-homieNode-Red2.1GitHubunmaintained
HomieV3Python3.0.1GitHubEasily build Homie 3.0 devices
HomieV4Python4.0.0GitHubEasily build Homie 4.0 devices
homie_dartDart4.0.0Pub.devImplementation for dart (and flutter)
LeifHomieLibC++3.0.1GitHubFor ESP8266/ESP32 with Arduino
node-red-contrib-homie-conventionNode-RED4.0.0GitHub
npm
Node-RED
Homie node with auto discovery for node red forum discussion
homie-for-esp32C++3.0.1GitHubFor ESP32 with Arduino. Limitations: Broadcast is not supported.
homie-deviceRust4.0.0crates.ioRust library for building Homie 4.0 devices
homie-mqttRuby4.0.0GitHubRuby library for building Homie 4.0 devices
+

Firmware

+

A full featured firmware, ready to be flashed on a supported device type.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameLanguageHomie VersionWebsite/DownloadDescription
homie-esp8266C++2.0.1GitHub
homie-esp8266C++3.0.1GitHub
ESPEasyC++4.0.0GitHubEasy to use Firmware for ESP8266/ESP32. Choose dev builds like ESP_Easy_mega-yyyymmdd_dev_ESP8266_4M1M.bin or build your own
+

Software projects

+

A software application that speaks MQTT/Homie and acts as a Homie Device.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameLanguageHomie VersionWebsite/DownloadDescription
miflora-mqtt-daemonPython3.0GitHubA Linux daemon to fetch and publish data from Mi Flora plant sensors
MBMDGo4.0GitHubA Linux daemon to fetch and publish data from ModBus devices like power meters and grid inverters
Somecomfort-HomePython4.0GitHubHomie implementation for Honeywell Total Comfort Thermostats using somecomefort
ISY-Home-BridgePython4.0GitHubHomie implementation for Universal Devices ISY994 controller
mijia-homieRust4.0GitHubA Linux daemon to fetch and publish data from Xiaomi Mijia v2 Bluetooth temperature and humidity sensors.
+

Controller

+

Find software libraries, administrative tools and home automation projects for Controllers in this section.

+

Libraries

+

A library can be included in your own project to act as a Controller for Homie devices.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameLanguageHomie VersionWebsite/Download
Homie-DeviceNode JS3.0npmjs
homie-cppC++3.0GitHub
homie-controllerRust4.0.0crates.io
+

Administration and utilities

+

Administrative tools usually allow you to detect, list and interact with Homie devices.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameHomieDownloadImplementation notes
Hodmin2.0Website
Homie-ota3.0 + Custom OTAGitHubOTA Server for devices running the homie-esp8266 firmware
HoDD3.0.1 / 4.0.0GitHubBrowser based device discovery
FlutterHomie3.0.1GitHubA Flutter App used to discover and manage Homie devices.
homie-influx4.0.0GitHubA service to record property value changes to an InfluxDB database, with Homie metadata.
+

Home automation

+

Home automation software in this list allows you to detect and interact with Homie devices +and integrate them into your automation plan.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameVersionHomieDownloadImplementation notes
openHAB2.4+3.0WebsiteNo node instances
HomeAssistantWIP2.0WebsiteModule need to be installed into HA installation
Node-RED0.20+4.0.0Node-RED
node-red-contrib-homie-convention
install node-red-contrib-homie-convention node with auto discovery forum discussion
+

Works with Homie

+

We recommendent all projects which are implementing Homie to refer to this convention using our label. +Please refer on websites directly to these labels to use always current version.

+

SVG version

+

works with MQTT Homie

+

HTML snippet:

+
+ +
+
<a href="https://homieiot.github.io/">
+  <object type="image/svg+xml" data="https://homieiot.github.io/img/works-with-homie.svg">
+    works with MQTT Homie
+  </object>
+</a>
+
+
+ +
+ +

PNG version

+

works with MQTT Homie

+

HTML snippet:

+
+ +
+
<a href="https://homieiot.github.io/">
+  <img src="https://homieiot.github.io/img/works-with-homie.png" alt="works with MQTT Homie">
+</a>
+
+
+ +
+ + +
+
+
+ + + + + + + + diff --git a/index.html b/index.html new file mode 100644 index 0000000..b9e6536 --- /dev/null +++ b/index.html @@ -0,0 +1,210 @@ + + + + + + + + + + The Homie convention + + + + + + + + + + + + + +
+
+ +
+ + + +
+
+ homieiot/convention +
+
+
+ + + +
+ +
+
+
+ +
+

+ Homie +

+
An MQTT Convention for IoT/M2M
+
+
+

Overview

+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FeatureTCPMQTTHomie
Stream based communication
Message based communication
Publish/Subscribe
Defined topic structure
Self–describing topics
Multiple Payload types
+
+

MQTT supports easy and unrestricted message-based communication.

+

However, MQTT doesn’t define the structure and content of these messages and their relation. An IoT device publishes data and provides interaction possibilities but a controlling entity will need to be specifically configured to be able to interface with the device.

+

The Homie convention defines a standardized way of how IoT devices and services announce themselves and their data on the MQTT broker.

+

It is thereby a crucial aspect on top of the MQTT protocol for automatic discovery, configuration and usage of devices and services.

+
+ +
+

Example

+

Homies topic layout follows the pattern “homie/device/node/property”.

+

In this example our device has a thermostat functionality where the target +temperature can be read from and written to. The thermostat function +is modelled as node, the temperature is a property of that node.

+

The following topics are published as retained messages to the broker:

+
+ +
+
  homie / device123 / $homie → 3.0
+  homie / device123 / $name → My device
+  homie / device123 / $state → ready
+  homie / device123 / $nodes → mythermostat
+
+  homie / device123 / mythermostat / $name → My thermostat
+  homie / device123 / mythermostat / $properties → temperature
+
+  homie / device123 / mythermostat / temperature → 22 
+  homie / device123 / mythermostat / temperature / $name → Temperature
+  homie / device123 / mythermostat / temperature / $unit → °C
+  homie / device123 / mythermostat / temperature / $datatype → integer
+  homie / device123 / mythermostat / temperature / $settable → true
+
+ +
+ +
+ +

Any Homie compliant controller can now find “My device” and will find out +about “My thermostat” with “Temperature in °C” and that it needs to publish +to “…/temperature/set” to change the temperature.

+

If we got your attention, head over to the specification +and let all your questions being answered.

+ +
+
+
+ + + + + + + + diff --git a/index.xml b/index.xml new file mode 100644 index 0000000..b442f2c --- /dev/null +++ b/index.xml @@ -0,0 +1,186 @@ + + + + Homie on The Homie convention + https://homieiot.github.io/ + Recent content in Homie on The Homie convention + Hugo + en-us + + + + https://homieiot.github.io/offline.html + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/offline.html + Page Not Found You are offline and could not find page :/ Go back + + + + https://homieiot.github.io/specification/spec-core-develop-changes/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/specification/spec-core-develop-changes/ + Commit title Date Hash chore(convention): minor cleanup and consistency updates 2024-07-06 14:23:54 +0200 0d7baf5 Trigger website deployment on convention changes (#293) 2024-07-04 10:25:15 +0200 ce4f54a fix(logging): one level up in doc structure 2024-07-03 14:54:46 +0200 ce5e371 chore(headings): move some headings 2024-06-30 11:05:43 +0200 63f21bd Correct topic pattern for empty string payload (#288) 2024-02-17 18:20:16 +0200 133c6b8 homie5(alerts): replace &lsquo;alert&rsquo; state with alert-topic (#283) 2023-11-15 23:38:52 +0100 eb0b437 fix(target): equality of received commands (#277) 2023-11-13 11:23:55 +0100 3715512 homie5(type): add type field for device and node (#282) 2023-11-05 12:12:29 +0100 11f0378 chore(state): add some more details around state management (#272) 2023-11-05 10:59:14 +0100 b314544 fix(broadcast): broadcasted messages should be non-retained (#280) 2023-11-05 10:48:22 +0100 ed50bc6 feat(unit): add &lsquo;rpm&rsquo; (revolutions per minute) (#279) 2023-11-05 10:47:22 +0100 1331fcc Added information about unpublishing/removing a device (#278) 2023-11-05 10:45:02 +0100 250d31a fix(log): clarify all messages must be non-retained (#276) 2023-07-26 13:52:23 +0200 c8201c8 feat(color): allow to set a preferred format (#275) 2023-07-06 11:07:52 +0200 6eb4639 feat(json-type): add a JSON data type (#273) 2023-06-25 20:01:12 +0200 566a2d6 feat(log): change &ldquo;alert&rdquo; state, add logging (#262) 2023-06-25 15:13:31 +0200 adc710e feat(property): add an optional $target property (#263) 2023-06-25 14:29:22 +0200 611ec27 clarify(enum): clarify that whitesapce is significant (#268) 2023-06-25 10:00:24 +0200 4bb1287 chore(id): simplify ID checks (#261) 2023-06-24 12:08:34 +0200 da23e1f change(description): change arrays to objects (#270) 2023-06-24 12:08:17 +0200 015b328 feat(format): add color format &lsquo;xyz&rsquo; (#274) 2023-06-24 12:07:55 +0200 1c15005 fix(validation): specify float/int validation order (#269) 2023-06-18 20:06:37 +0200 13e241e fix(hierarchy): root device should not have root (#271) 2023-06-18 20:05:06 +0200 d6b4ad8 chore(properties): align the 3 property tables in the spec (#267) 2023-06-16 11:28:17 +0200 35837c2 chore(format): reformat table (flip-axis) for readability (#266) 2023-06-16 09:28:16 +0200 dbff282 fix(example): no description update in &lsquo;ready&rsquo; state (#265) 2023-06-15 09:32:00 +0200 06446c0 fix(format): properly format the integer format template (#264) 2023-06-15 00:23:36 +0200 c1a58ce chore(spec): spelling corrections (#260) 2023-06-15 00:13:04 +0200 9fa98ce fix(base-topic): some occurences of homie/5/ were missing (#259) 2023-03-19 13:58:20 +0100 6afa235 feat(formats): add step size to integer/float formats (#257) 2023-03-17 13:45:02 +0100 953a1e2 feat(units): add airquality units; ppm (co2 / co) 2023-03-12 13:20:46 +0100 da5f1b4 feat(units): add windspeed units; m/s + knots 2023-03-07 11:10:07 +0100 ce7a3ee fix(utf8): more explicit encoding details 2023-03-07 11:01:06 +0100 edf94b1 change(description-doc): relax language about omitting defaults (#258) 2023-03-17 13:42:05 +0100 67e0ad7 feat(units): add recommended units (#254) 2023-03-04 15:29:08 +0100 2fe6f7a change(QoS) clarify QoS settings and switch to 2 (#253) 2023-01-26 10:32:56 +0100 063578d refactor(mqtt) relocate all info wrt retained and qos (#252) 2023-01-26 10:30:50 +0100 c3f03bf fix(strings) use 0x00 instead of 0x00 (#251) 2023-01-19 07:46:58 +0100 158b873 fix(version) device example missing required attribute (#250) 2023-01-18 23:20:50 +0100 d59d163 feat(version) add version to description document 2023-01-18 00:12:25 +0100 4accccd feat(versioning) add improved versioning and compatibility 2023-01-17 23:29:52 +0100 4d10316 fix(property) handling empty-string values (#239) 2023-01-17 23:39:16 +0100 734d6ff change(formats) float-colors, improved numbers, add booleans 2023-01-17 07:46:27 +0100 7e50cb1 feat(units) add kW, kWh and m3 as units 2023-01-17 06:50:42 +0100 0f8439f readability(retained) reorder more logically (#245) 2023-01-17 09:11:35 +0100 1f73c11 fix(device) be more specific about empty attributes (#242) 2023-01-16 23:24:07 +0100 f974349 describe parent-child hierarchy and handling (#240) 2023-01-16 23:23:29 +0100 11dc08a fix(percent) drop the percent type (#243) 2023-01-16 23:21:58 +0100 0155f38 drop node. + + + + https://homieiot.github.io/specification/spec-core-develop-diff/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/specification/spec-core-develop-diff/ + v4.0.0 develop n3version: v4.0.0n3version: develop 4releasedate: 27. August 20194releasedate: 06. July 2024 n18A topic level ID MAY contain lowercase letters from `a` to `z`, numbers from `0` to `9` as well as the hyphen character (`-`).n18A topic level ID MAY ONLY contain lowercase letters from `a` to `z`, numbers from `0` to `9` as well as the hyphen character (`-`). n20A topic level ID MUST NOT start or end with a hyphen (`-`). + + + + https://homieiot.github.io/specification/spec-core-develop/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/specification/spec-core-develop/ + MQTT Restrictions Homie communicates through MQTT and is hence based on the basic principles of MQTT topic publication and subscription. Topic IDs An MQTT topic consists of one or more topic levels, separated by the slash character (/). A topic level ID MAY ONLY contain lowercase letters from a to z, numbers from 0 to 9 as well as the hyphen character (-). The special character $ is used and reserved for Homie attributes. + + + + https://homieiot.github.io/specification/spec-core-v1_5_0/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/specification/spec-core-v1_5_0/ + Background An instance of a physical piece of hardware (an Arduino, an ESP8266&hellip;) is called a device. A device has device properties, like the current local IP, the Wi-Fi signal, etc. A device can expose multiple nodes. For example, a weather device might expose a temperature node and an humidity node. A node can have multiple node properties. The temperature node might for example expose a temperature property containing the actual temperature, and an unit property. + + + + https://homieiot.github.io/specification/spec-core-v2_0_0-changes/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/specification/spec-core-v2_0_0-changes/ + Commit title Date Hash Add MIT LICENSE - closes #21 2017-04-28 10:06:43 +0200 3bcbb01 :memo: Add link to hodmin cli-client (#19) 2017-04-17 22:15:19 +0200 f137694 :memo: Fix typo in README.md (#20) 2017-04-17 22:14:44 +0200 ec0b666 :memo: Add note that the v2 might still change before final release 2017-02-13 10:12:11 +0100 279f55d :art: Fix backticks inside table 2016-11-16 19:52:15 +0100 bff4047 :sparkles: Add format of $mac 2016-11-16 19:51:26 +0100 c9b402c :sparkles: Say $online must be sent last 2016-11-16 12:33:46 +0100 23f4f8f :sparkles: Add MAC address 2016-11-16 12:25:31 +0100 b4ce39d :bug: Remove old $ota left-over 2016-11-16 12:21:42 +0100 724dfc5 :sparkles: Add firmware checksum 2016-11-16 12:19:50 +0100 284a1f9 :art: Create a stats device property - fix #8 2016-10-26 16:40:44 +0200 2e28318 :fire: Remove $ota - now implementation specific 2016-10-24 16:46:27 +0200 9f8a584 :sparkles: Make $signal specify an interval too - fix #8 2016-10-24 15:58:56 +0200 b5f27f0 :docs: Add reference to homie-python (#9) 2016-10-17 22:37:35 +0200 c181ec0 Center title 2016-10-12 19:46:47 +0200 860b26a Add ID format section and cosmetic tweaks 2016-10-11 13:13:07 +0200 7f5e582 Make banner transparent 2016-10-10 10:23:02 +0200 b2a1931 Replace devices base topic by homie 2016-10-03 09:59:09 +0200 b0ec78f Add broadcast channel 2016-09-07 19:36:51 +0200 cd84f93 Add range properties 2016-08-27 11:17:48 +0200 2d5e7fe Remove useless $nodes 2016-08-19 14:52:04 +0200 3bba8d3 Add $type and $properties to node 2016-08-19 14:48:23 +0200 d92ceb9 Remove old $reset property 2016-08-16 18:47:49 +0200 675f0aa Cosmetic changes and typo fixes 2016-08-16 00:31:00 +0200 d5f4ad1 Allow implementation-specific properties 2016-08-15 00:39:32 +0200 62bc55d Fix typos 2016-08-13 11:31:40 +0200 14f9d81 First v2 changes 2016-08-13 11:26:27 +0200 658f2b3 + + + + https://homieiot.github.io/specification/spec-core-v2_0_0-diff/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/specification/spec-core-v2_0_0-diff/ + v1.5.0 v2.0.0 n3version: v1.5.0n3version: v2.0.0 4releasedate: 19. June 20164releasedate: 28. April 2017 n13An instance of a physical piece of hardware (an Arduino, an ESP8266...) is called a **device**. A device has **device properties**, like the current local IP, the Wi-Fi signal, etc. A device can expose multiple **nodes**. For example, a weather device might expose a temperature node and an humidity node. A node can have multiple **node properties**. The temperature node might for example expose a temperature property containing the actual temperature, and an unit property. + + + + https://homieiot.github.io/specification/spec-core-v2_0_0/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/specification/spec-core-v2_0_0/ + Background An instance of a physical piece of hardware (an Arduino, an ESP8266&hellip;) is called a device. A device has device properties, like the current local IP, the Wi-Fi signal, etc. A device can expose multiple nodes. For example, a weather device might expose a temperature node and an humidity node. A node can have multiple node properties. The temperature node might for example expose a degrees property containing the actual temperature, and an unit property. + + + + https://homieiot.github.io/specification/spec-core-v2_0_1-changes/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/specification/spec-core-v2_0_1-changes/ + Commit title Date Hash Add Node JS Implementation 2018-03-24 12:57:05 +1100 b1f7ec0 2.0.1 Added $nodes to a device 2018-03-24 08:27:05 +1100 775ee43 + + + + https://homieiot.github.io/specification/spec-core-v2_0_1-diff/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/specification/spec-core-v2_0_1-diff/ + v2.0.0 v2.0.1 n3version: v2.0.0n3version: v2.0.1 4releasedate: 28. April 20174releasedate: 24. March 2018 tt133 &lt;/tr&gt; 134 &lt;tr&gt; 135 &lt;td&gt;$nodes&lt;/td&gt; 136 &lt;td&gt;Device → Controller&lt;/td&gt; 137 &lt;td&gt;Nodes the device exposes, with format &lt;code&gt;id&lt;/code&gt; separated by a &lt;code&gt;,&lt;/code&gt; if there are multiple nodes.&lt;/td&gt; 138 &lt;td&gt;Yes&lt;/td&gt; 139 &lt;td&gt;Yes&lt;/td&gt; + + + + https://homieiot.github.io/specification/spec-core-v2_0_1/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/specification/spec-core-v2_0_1/ + Background An instance of a physical piece of hardware (an Arduino, an ESP8266&hellip;) is called a device. A device has device properties, like the current local IP, the Wi-Fi signal, etc. A device can expose multiple nodes. For example, a weather device might expose a temperature node and an humidity node. A node can have multiple node properties. The temperature node might for example expose a degrees property containing the actual temperature, and an unit property. + + + + https://homieiot.github.io/specification/spec-core-v3_0_0-changes/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/specification/spec-core-v3_0_0-changes/ + Commit title Date Hash Merge pull request #71 from homieiot/rolling-releases 2018-03-25 08:55:18 +1100 96c3086 Add info regarding rolling releases 2018-03-24 15:18:37 +0100 c5ad0b2 Add NodeJS implementation 2018-03-24 11:25:36 +1100 110b68f Small Readme version fix 2018-03-21 09:00:35 +1100 508f6ae Fix Versioning. 2018-03-16 07:55:10 +1100 09c84e4 Merge pull request #67 from marvinroger/v2 2018-03-12 12:01:13 +1100 0b90e74 Update Version 2018-03-12 12:00:15 +1100 be4d750 Merge branch &lsquo;redesign&rsquo; into v2 2018-03-12 11:59:28 +1100 e25c5dc added micropython-homie 2018-03-12 11:54:43 +1100 00285cb Merge pull request #64 from microhomie/v2 2018-02-17 21:12:45 +1100 0a9e88b Remove WIP state 2018-02-12 10:49:02 +0100 85d6a1f Add MicroPython implementation 2018-02-10 11:56:40 +0100 d7392c0 Merge pull request #63 from nerdfirefighter/redesign 2018-02-03 18:44:59 +1100 b071d29 Typo Fix 2018-02-02 20:46:05 +1100 25b727f Merge pull request #1 from marvinroger/redesign 2018-02-02 20:43:27 +1100 71b15b9 Update Branch 2018-01-24 12:08:45 +1100 755b994 Merge pull request #60 from nerdfirefighter/v2 2018-01-24 11:50:03 +1100 0482588 Update README. + + + + https://homieiot.github.io/specification/spec-core-v3_0_0-diff/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/specification/spec-core-v3_0_0-diff/ + v2.0.1 v3.0.0 n3version: v2.0.1n3version: v3.0.0 4releasedate: 24. March 20184releasedate: 25. March 2018 n11## Backgroundn11## Table of Contents n13An instance of a physical piece of hardware (an Arduino, an ESP8266...) is called a **device**. A device has **device properties**, like the current local IP, the Wi-Fi signal, etc. A device can expose multiple **nodes**. For example, a weather device might expose a `temperature` node and an `humidity` node. A node can have multiple **node properties**. + + + + https://homieiot.github.io/specification/spec-core-v3_0_0/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/specification/spec-core-v3_0_0/ + Table of Contents Motivation MQTT restrictions Topic IDs Payload QoS and retained messages Topology Base topic Devices Device attributes Device behavior Device statistics Nodes Node attributes Properties Property attributes Arrays Broadcast channel Motivation The Homie convention strives to be a communication definition on top of MQTT between IoT devices and controlling entities. MQTT is a machine-to-machine (M2M)/&ldquo;Internet of Things&rdquo; connectivity protocol. It was designed as an extremely lightweight publish/subscribe messaging transport. + + + + https://homieiot.github.io/specification/spec-core-v3_0_1-changes/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/specification/spec-core-v3_0_1-changes/ + Commit title Date Hash Stateful attribute for properties (#108) 2018-10-27 15:22:20 +0200 730357e Remove regex requirement (#88) 2018-04-29 14:32:39 +0200 081214d Use &ldquo;true&rdquo; instead of &ldquo;on&rdquo; (#89) 2018-04-27 14:51:40 +0200 5cbe5ee Add FAQ, discuss getter topic (#87) 2018-04-27 14:29:27 +0200 fb8d153 Add my ESP32 IDF implementation (#73) 2018-04-23 15:25:24 -0700 7d9f236 + + + + https://homieiot.github.io/specification/spec-core-v3_0_1-diff/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/specification/spec-core-v3_0_1-diff/ + v3.0.0 v3.0.1 n3version: v3.0.0n3version: v3.0.1 4releasedate: 25. March 20184releasedate: 27. October 2018 nn30* [FAQ and Rationale](#faq) 31 nn97 98Properties can be **retained**. 99A property is retained by default. A non-retained property would be useful for momentary events (door bell pressed). 100 101A combination of those flags compiles into this list: 102 103* **retained + non-settable**: The node publishes a property state (temperature sensor) 104* **retained + settable**: The node publishes a property state, and can receive commands for the property (by controller or other party) (lamp power) 105* **non-retained + non-settable**: The node publishes momentary events (door bell pressed) 106* **non-retained + settable**: The node publishes momentary events, and can receive commands for the property (by controller or other party) (brew coffee) nn446 &lt;/tr&gt; 447 &lt;tr&gt; 448 &lt;td&gt;$retained&lt;/td&gt; 449 &lt;td&gt;Device → Controller&lt;/td&gt; 450 &lt;td&gt;Specifies whether the property is retained (&lt;code&gt;true&lt;/code&gt;) or non-retained (&lt;code&gt;false&lt;/code&gt;). + + + + https://homieiot.github.io/specification/spec-core-v3_0_1/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/specification/spec-core-v3_0_1/ + Table of Contents Motivation MQTT restrictions Topic IDs Payload QoS and retained messages Topology Base topic Devices Device attributes Device behavior Device statistics Nodes Node attributes Properties Property attributes Arrays Broadcast channel FAQ and Rationale Motivation The Homie convention strives to be a communication definition on top of MQTT between IoT devices and controlling entities. MQTT is a machine-to-machine (M2M)/&ldquo;Internet of Things&rdquo; connectivity protocol. It was designed as an extremely lightweight publish/subscribe messaging transport. + + + + https://homieiot.github.io/specification/spec-core-v4_0_0-changes/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/specification/spec-core-v4_0_0-changes/ + Commit title Date Hash Remove array brackets in super-car example (#177) 2019-08-27 14:01:18 +0200 12453d8 Corections to meta extension (#173) 2019-07-18 00:07:23 +0200 2c1999e Fixed typos and broken links, removed whitespace in homie_legacy_stat… (#172) 2019-07-17 21:13:33 +0200 608541a Extension Convention (#171) 2019-07-16 22:55:32 +0200 eaf3edc remove references to deprecated arrays (#169) 2019-07-03 19:56:14 +0800 9ac3433 Point Last Will section to $state (#157) 2019-03-07 17:30:43 +0900 a3ea000 Remove timings paragraph. Rename behaviour to lifecycle. + + + + https://homieiot.github.io/specification/spec-core-v4_0_0-diff/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/specification/spec-core-v4_0_0-diff/ + v3.0.1 v4.0.0 n2source: README.mdn2source: convention.md 3version: v3.0.13version: v4.0.0 4releasedate: 27. October 20184releasedate: 27. August 2019 n11## Table of Contentsn 12 13* [Motivation](#motivation) 14* [MQTT restrictions](#mqtt-restrictions) 15 * [Topic IDs](#topic-ids) 16 * [Payload](#payload) 17 * [QoS and retained messages](#qos-and-retained-messages) 18* [Topology](#topology) 19 * [Base topic](#base-topic) 20 * [Devices](#devices) 21 * [Device attributes](#device-attributes) 22 * [Device behavior](#device-behavior) 23 * [Device statistics](#device-statistics) 24 * [Nodes](#nodes) 25 * [Node attributes](#node-attributes) 26 * [Properties](#properties) 27 * [Property attributes](#property-attributes) 28 * [Arrays](#arrays) 29 * [Broadcast channel](#broadcast-channel) 30* [FAQ and Rationale](#faq) 31 32 33## Motivation 34 35The Homie convention strives to be a **communication definition on top of MQTT** between IoT devices and controlling entities. + + + + https://homieiot.github.io/specification/spec-core-v4_0_0/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/specification/spec-core-v4_0_0/ + MQTT Restrictions Homie communicates through MQTT and is hence based on the basic principles of MQTT topic publication and subscription. Topic IDs An MQTT topic consists of one or more topic levels, separated by the slash character (/). A topic level ID MAY contain lowercase letters from a to z, numbers from 0 to 9 as well as the hyphen character (-). A topic level ID MUST NOT start or end with a hyphen (-). + + + Homie Extensions + https://homieiot.github.io/extensions/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/extensions/ + The Homie convention defines a standardized way of how devices and services announce themselves and defines the structure and content of messages and their relation. It does not define domain types, like a lightbulb and required properties for a Home Automation domain. It also not defines procedures like Over-The-Air updates and metrics like cpu usage or memory consumption. That is what extensions are for. License Every extension must be published using a license. + + + How to get involved? + https://homieiot.github.io/get_involved/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/get_involved/ + Propose feature Open a new GitHub Issue to start a discussion about a new feature. Open a GitHub Pull Request to provide a change to the convention and have it discussed. Raise an Issue Open a Pull-Request Feedback The discussion period for a new feature lasts for a maximum of 3 weeks. Everyone involved can apply for a longer time frame if required. 3 weeks no progress means &ldquo;Rejected&rdquo; automatically. + + + Implementations + https://homieiot.github.io/implementations/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/implementations/ + Device An instance of a physical piece of hardware is called a device. For example, a car, an Arduino/ESP8266 or a coffee machine. It publishes Nodes and Properties to the MQTT broker. Controller A controller does not announce anything to the MQTT broker, but discovers and interacts with Devices. There can be more than one Controller interacting with the different devices on the same broker. Device Find software libraries and full firmware projects for Devices in this section. + + + License + https://homieiot.github.io/license/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/license/ + By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution 4.0 International Public License (&ldquo;Public License&rdquo;). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. + + + Tools + https://homieiot.github.io/tools/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/tools/ + Online verificator for Homie 4 Paste published Homie topics into the text field and let the online verificator tell you what Homie devices, nodes and properties it has recognised. Instructions: Connect your Homie device Use for example "mosquitto_sub -v -t homie/#" to create a list of all published homie topics and their payloads. Paste the list into the textfield below 1 2 homie/device123/$homie 4.0 homie/device123/$name My device homie/device123/$state ready homie/device123/$extensions homie/device123/$nodes mythermostat homie/device123/mythermostat/$name My thermostat homie/device123/mythermostat/$properties temperature homie/device123/mythermostat/temperature 22 homie/device123/mythermostat/temperature/$name Temperature homie/device123/mythermostat/temperature/$unit °C homie/device123/mythermostat/temperature/$datatype integer Validation result: Not yet validated Validate + + + diff --git a/js/main.min.js b/js/main.min.js new file mode 100644 index 0000000..4bb4f95 --- /dev/null +++ b/js/main.min.js @@ -0,0 +1,4 @@ +"use strict";document.addEventListener("DOMContentLoaded",()=>{var e=document.querySelector(".progress");e&&document.addEventListener("scroll",function(){const t="scrollTop",n="scrollHeight";var s=(document.documentElement[t]||document.body[t])/((document.documentElement[n]||document.body[n])-document.documentElement.clientHeight)*100;e.style.setProperty("--scroll",s+"%")}),document.dispatchEvent(new Event("MainContentChanged"))});const deviceMeta={allowed:new Set(["$homie","$name","$state","$nodes","$extensions"]),deprecated:new Set(["$mac","$localip","$fw/name","$fw/version","$stats","$implementation"]),required:new Set(["$homie","$name","$state","$nodes"])},nodeMeta={allowed:new Set(["$name","$type","$properties","$array"]),deprecated:new Set,required:new Set(["$name","$properties"])},propertyMeta={allowed:new Set(["$name","$settable","$retained","$unit","$datatype","$format","$$value"]),deprecated:new Set([]),required:new Set(["$name"])},valueRestrictions=new Map([["$name",/^.+$/],["$homie",/^[0-9.]+$/],["$settable",/^true$|^false$/],["$retained",/^true$|^false$/],["$state",/^init$|^ready$|^disconnected$|^sleeping$|^lost$|^alert$/],["$nodes",/^[a-z0-9_\-,]+$/i],["$properties",/^[a-z0-9_\-,]+$/i],["$datatype",/^\b(integer|float|boolean|string|enum|color)\b$/]]);function topic_vec_to_object_tree(e,t,n,s,o){for(var i,r=e,c=t,a=1;a2&&i.length<6)topic_vec_to_object_tree(s,o,i,c,n);else return{line:n,text:"Does not describe a device!"};return null}function checkAttributes(e,t,n,s,o,i,a){for(let[r,c]of Object.entries(n))if(r.startsWith("$")){if(s.deprecated.has(r)){i.push({line:a[r],text:e+" '"+t+"' has the deprecated attribute '"+r+"' set! Please check with the newest version of the convention."});continue}if(!s.allowed.has(r)){o.push({line:a[r],text:e+" '"+t+"' doesn't allow '"+r+"' to be set! Must be one of "+Array.from(s.allowed).join(", ")});continue}if(typeof c!="string"){o.push({line:a[r],text:"The key '"+t+"/"+r+"' should not contain children and does not conform to the convention!"});continue}const n=valueRestrictions.get(r);if(n&&!c.match(n)){o.push({line:a[r],text:"The value '"+c+"' of '"+t+"/"+r+"' does not conform to the convention!"});continue}}const r=new Set(Object.keys(n));for(let n of s.required)r.has(n)||o.push({line:-1,text:e+" '"+t+"' requires '"+n+"' to be set!"})}function addLineNumbers(){const t=document.getElementById("homieinput");for(var s=t.offsetHeight,o=parseInt(document.defaultView.getComputedStyle(t,null).getPropertyValue("line-height")),i=s/o,n="",e=0;e";document.getElementById("linenumbers").innerHTML=n}document.addEventListener("MainContentChanged",()=>{const e=document.getElementById("homieinput");if(!e)return;const t=document.getElementById("homiebasetopic");if(!t)return;e.addEventListener("focus",()=>e.innerText=e.innerText),e.addEventListener("blur",()=>e.innerText=e.innerText),e.addEventListener("change",addLineNumbers),addLineNumbers(),e.innerText=e.innerText,t.addEventListener("change",()=>e.innerText=e.innerText)}),window.homieverificator=()=>{const u=document.getElementById("homiebasetopic").value.split("/")[0],c=document.getElementById("homieinput"),n=c.innerText.split(` +`),s=document.getElementById("homieoutput"),l=document.getElementById("validationresult");for(var t,r,e=[],o=[],d={},i={},a=0;a0?u:"homie",a,d,i);t&&e.push(t)}r=/^[a-z0-9]+[a-z0-9-]*$/i;for(let[t,n]of Object.entries(d)){r.test(t)||e.push({line:-1,text:"Device ''"+t+"' id does not conform to topic id restriction!"}),checkAttributes("Device",t,n,deviceMeta,e,o,i[t]),(n.$extensions||"").trim().length!=0&&o.push({line:i[t].$extensions,text:"Device ''"+t+"' has extensions enabled, validation might be inaccurate."});let s=new Set((n.$nodes||"").split(",").map(e=>e.trim()));for(let[a,c]of Object.entries(n)){if(a.startsWith("$"))continue;r.test(a)||e.push({line:-1,text:"Node '"+a+"' id does not conform to topic id restriction!"}),s.has(a)||e.push({line:-1,text:"Node '"+a+"' is not published by its device!"}),s.delete(a),checkAttributes("Node",a,c,nodeMeta,e,o,i[t][a]);let l=new Set((c.$properties||"").split(",").map(e=>e.trim()));for(let[n,s]of Object.entries(c)){if(n.startsWith("$"))continue;if(r.test(n)||e.push({line:-1,text:"Property '"+n+"' of node '"+t+"/"+a+"' id does not conform to topic id restriction!"}),l.has(n)||e.push({line:-1,text:"Property '"+n+"' of node '"+t+"/"+a+"' is not published by node!"}),l.delete(n),checkAttributes("Property",n,s,propertyMeta,e,o,i[t][a][n]),s.$$value){const r=i[t][a][n].value,o=s.$$value,c=s.$format;switch(s.$datatype){case"integer":o.match(/^[\d]+$/)||e.push({line:r,text:"Property '"+n+"' value of type integer contains invalid data: '"+o+"'!"});break;case"float":o.match(/^[+-]?([0-9]*[.])?[0-9]+$/)||e.push({line:r,text:"Property '"+n+"' value of type float contains invalid data: '"+o+"'!"});break;case"boolean":o.match(/^\b(true|false)\b$/)||e.push({line:r,text:"Property '"+n+"' value of type boolean contains invalid data: '"+o+"'!"});break;case"enum":if(!c){e.push({line:r,text:"Property '"+n+"' is of type enum but $format is not set!"});continue}c.split(",").indexOf(o)==-1&&e.push({line:r,text:"Property '"+n+"' value of type enum contains invalid data: '"+o+"'. Must be one of '"+c+"'!"});break;case"color":if(!c||!c.match(/^\b(rgb|hsv)\b/)){e.push({line:r,text:"Property '"+n+"' is of type color but $format is not 'rgb' or 'hsv'!"});continue}o.match(/[\d]{3},[\d]{3},[\d]{3}/)||e.push({line:r,text:"Property '"+n+"' value of type color must be in the format xxx,xxx,xxx!"});break;default:case"string":break}}}for(let n of l)e.push({line:-1,text:"Property '"+n+"' of node '"+t+"/"+a+"' is not defined!"})}for(let n of s)e.push({line:-1,text:"Node '"+n+"' of device '"+t+"' is not defined!"})}if(s.innerHTML="",o.length&&(t="",o.forEach(e=>{const s=Number.isInteger(e.line)?[e.line]:Object.values(e.line);for(const o of s)t+="",o>=0&&(n[o]=""+n[o]+"")}),t+="
LineWarning message
"+(o<0?"n/a":o+1)+""+e.text+"
",s.innerHTML=s.innerHTML+t,c.innerHTML=n.join(` +`)),e.length)t="",e.forEach(e=>{const s=Number.isInteger(e.line)?[e.line]:Object.values(e.line);for(const o of s)t+="",o>=0&&(n[o]=""+n[o]+"")}),t+="
LineError message
"+(o<0?"n/a":o+1)+""+e.text+"
",s.innerHTML=s.innerHTML+t,c.innerHTML=n.join(` +`);else{t="
Devices:
    ";for(let[n,e]of Object.entries(d)){if(!e.$name)continue;t+="
  • "+e.$name+" with Nodes:
      ";for(let[s,n]of Object.entries(e)){if(!n.$name)continue;t+="
    • "+n.$name+" with Properties:
        ";for(let[s,e]of Object.entries(n)){if(!e.$name)continue;t+="
      • "+e.$name+"
      • "}t+="
    • "}t+="
  • "}t+="
",s.innerHTML=s.innerHTML+t}e.length!=0?l.innerHTML="Validation failed":o.length!=0?l.innerHTML="Validation successful with warnings":l.innerHTML="Validation successful"} \ No newline at end of file diff --git a/js/sw.js b/js/sw.js new file mode 100644 index 0000000..12166a6 --- /dev/null +++ b/js/sw.js @@ -0,0 +1,33 @@ +self.addEventListener('install', function (event) { + event.waitUntil( + caches.open('v2').then(function (cache) { + return cache.addAll([ + '/scss/all.min.css', + '/offline.html' + ]).catch(function (error) { + console.log("Cache failed", error) + }); + }) + ); +}); +self.addEventListener('fetch', function (event) { + event.respondWith( + // Try the cache + caches.open('v2').then(function (cache) { + return cache.match(event.request).then(function (response) { + if (!response) + // Fall back to network + return response || fetch(event.request).then(function (response) { + cache.put(event.request, response.clone()); + return response; + }); + }).catch(function () { + // If both fail, show a generic fallback: + return caches.match('/offline.html'); + // However, in reality you'd have many different + // fallbacks, depending on URL & headers. + // Eg, a fallback silhouette image for avatars. + }) + }) + ); + }); diff --git a/license/index.html b/license/index.html new file mode 100644 index 0000000..60fd276 --- /dev/null +++ b/license/index.html @@ -0,0 +1,218 @@ + + + + + + + + + The Homie convention + + + + + + + + + + + + + +
+
+ +
+ + + +
+
+ homieiot/convention +
+
+
+ + + +
+ +
+
+
+

+ License +

+
Creative Commons Attribution 4.0 International Public License
+

By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution 4.0 International Public License (“Public License”). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions.

+

Section 1 – Definitions.

+

a. Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image.

+

b. Adapter’s License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License.

+

c. Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights.

+

d. Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements.

+

e. Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material.

+

f. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License.

+

g. Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license.

+

h. Licensor means the individual(s) or entity(ies) granting rights under this Public License.

+

i. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them.

+

j. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world.

+

k. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning.

+

Section 2 – Scope.

+

a. License grant.

+
    +
  1. +

    Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to:

    +

    A. reproduce and Share the Licensed Material, in whole or in part; and

    +

    B. produce, reproduce, and Share Adapted Material.

    +
  2. +
  3. +

    Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions.

    +
  4. +
  5. +

    Term. The term of this Public License is specified in Section 6(a).

    +
  6. +
  7. +

    Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material.

    +
  8. +
  9. +

    Downstream recipients.

    +

    A. Offer from the Licensor – Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License.

    +

    B. No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material.

    +
  10. +
  11. +

    No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i).

    +
  12. +
+

b. Other rights.

+
    +
  1. +

    Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise.

    +
  2. +
  3. +

    Patent and trademark rights are not licensed under this Public License.

    +
  4. +
  5. +

    To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties.

    +
  6. +
+

Section 3 – License Conditions.

+

Your exercise of the Licensed Rights is expressly made subject to the following conditions.

+

a. Attribution.

+
    +
  1. +

    If You Share the Licensed Material (including in modified form), You must:

    +

    A. retain the following if it is supplied by the Licensor with the Licensed Material:

    +

    i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated);

    +

    ii. a copyright notice;

    +

    iii. a notice that refers to this Public License;

    +

    iv. a notice that refers to the disclaimer of warranties;

    +

    v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable;

    +

    B. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and

    +

    C. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License.

    +
  2. +
  3. +

    You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information.

    +
  4. +
  5. +

    If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable.

    +
  6. +
  7. +

    If You Share Adapted Material You produce, the Adapter’s License You apply must not prevent recipients of the Adapted Material from complying with this Public License.

    +
  8. +
+

Section 4 – Sui Generis Database Rights.

+

Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material:

+

a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database;

+

b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and

+

c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database.

+

For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights.

+

Section 5 – Disclaimer of Warranties and Limitation of Liability.

+

a. Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You.

+

b. To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You.

+

c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability.

+

Section 6 – Term and Termination.

+

a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically.

+

b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates:

+
    +
  1. +

    automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or

    +
  2. +
  3. +

    upon express reinstatement by the Licensor.

    +
  4. +
+

For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License.

+

c. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License.

+

d. Sections 1, 5, 6, 7, and 8 survive termination of this Public License.

+

Section 7 – Other Terms and Conditions.

+

a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed.

+

b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License.

+

Section 8 – Interpretation.

+

a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License.

+

b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions.

+

c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor.

+

d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority.

+
+

Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.” Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at creativecommons.org/policies, Creative Commons does not authorize the use of the trademark “Creative Commons” or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses.

+

Creative Commons may be contacted at creativecommons.org

+
+ +
+
+
+ + + +
+ +
+ + + + diff --git a/offline.html b/offline.html new file mode 100644 index 0000000..01c3f92 --- /dev/null +++ b/offline.html @@ -0,0 +1,110 @@ + + + + + + + + + The Homie convention + + + + + + + + + + + + + +
+
+ +
+ + + +
+
+ homieiot/convention +
+
+
+ + + +
+ +
+
+
+

+ +

+
+
+

Page Not Found

+
+

You are offline and could not find page :/ Go back home.

+ +
+
+
+ + + +
+ +
+ + + + diff --git a/scss/all.min.css b/scss/all.min.css new file mode 100644 index 0000000..5710fae --- /dev/null +++ b/scss/all.min.css @@ -0,0 +1,12 @@ +@charset "UTF-8";/*!* Font Awesome Free 5.5.0 by @fontawesome - https://fontawesome.com +* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)*/.fa,.fas,.far,.fal,.fab{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:inline-block;font-style:normal;font-variant:normal;text-rendering:auto;line-height:1}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-.0667em}.fa-xs{font-size:.75em}.fa-sm{font-size:.875em}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:2.5em;padding-left:0}.fa-ul>li{position:relative}.fa-li{left:-2em;position:absolute;text-align:center;width:2em;line-height:inherit}.fa-border{border:solid .08em #eee;border-radius:.1em;padding:.2em .25em .15em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left,.fas.fa-pull-left,.far.fa-pull-left,.fal.fa-pull-left,.fab.fa-pull-left{margin-right:.3em}.fa.fa-pull-right,.fas.fa-pull-right,.far.fa-pull-right,.fal.fa-pull-right,.fab.fa-pull-right{margin-left:.3em}.fa-spin{animation:fa-spin 2s infinite linear}.fa-pulse{animation:fa-spin 1s infinite steps(8)}@keyframes fa-spin{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";transform:scale(-1,1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";transform:scale(1,-1)}.fa-flip-horizontal.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";transform:scale(-1,-1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2.5em}.fa-stack-1x,.fa-stack-2x{left:0;position:absolute;text-align:center;width:100%}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-500px:before{content:"\f26e"}.fa-accessible-icon:before{content:"\f368"}.fa-accusoft:before{content:"\f369"}.fa-acquisitions-incorporated:before{content:"\f6af"}.fa-ad:before{content:"\f641"}.fa-address-book:before{content:"\f2b9"}.fa-address-card:before{content:"\f2bb"}.fa-adjust:before{content:"\f042"}.fa-adn:before{content:"\f170"}.fa-adversal:before{content:"\f36a"}.fa-affiliatetheme:before{content:"\f36b"}.fa-air-freshener:before{content:"\f5d0"}.fa-algolia:before{content:"\f36c"}.fa-align-center:before{content:"\f037"}.fa-align-justify:before{content:"\f039"}.fa-align-left:before{content:"\f036"}.fa-align-right:before{content:"\f038"}.fa-alipay:before{content:"\f642"}.fa-allergies:before{content:"\f461"}.fa-amazon:before{content:"\f270"}.fa-amazon-pay:before{content:"\f42c"}.fa-ambulance:before{content:"\f0f9"}.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-amilia:before{content:"\f36d"}.fa-anchor:before{content:"\f13d"}.fa-android:before{content:"\f17b"}.fa-angellist:before{content:"\f209"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-down:before{content:"\f107"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angry:before{content:"\f556"}.fa-angrycreative:before{content:"\f36e"}.fa-angular:before{content:"\f420"}.fa-ankh:before{content:"\f644"}.fa-app-store:before{content:"\f36f"}.fa-app-store-ios:before{content:"\f370"}.fa-apper:before{content:"\f371"}.fa-apple:before{content:"\f179"}.fa-apple-alt:before{content:"\f5d1"}.fa-apple-pay:before{content:"\f415"}.fa-archive:before{content:"\f187"}.fa-archway:before{content:"\f557"}.fa-arrow-alt-circle-down:before{content:"\f358"}.fa-arrow-alt-circle-left:before{content:"\f359"}.fa-arrow-alt-circle-right:before{content:"\f35a"}.fa-arrow-alt-circle-up:before{content:"\f35b"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-down:before{content:"\f063"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrows-alt:before{content:"\f0b2"}.fa-arrows-alt-h:before,.icon-hori-ver-arrow:before{content:"\f337"}.fa-arrows-alt-v:before{content:"\f338"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asterisk:before{content:"\f069"}.fa-asymmetrik:before{content:"\f372"}.fa-at:before{content:"\f1fa"}.fa-atlas:before{content:"\f558"}.fa-atom:before{content:"\f5d2"}.fa-audible:before{content:"\f373"}.fa-audio-description:before{content:"\f29e"}.fa-autoprefixer:before{content:"\f41c"}.fa-avianex:before{content:"\f374"}.fa-aviato:before{content:"\f421"}.fa-award:before{content:"\f559"}.fa-aws:before{content:"\f375"}.fa-backspace:before{content:"\f55a"}.fa-backward:before{content:"\f04a"}.fa-balance-scale:before{content:"\f24e"}.fa-ban:before{content:"\f05e"}.fa-band-aid:before{content:"\f462"}.fa-bandcamp:before{content:"\f2d5"}.fa-barcode:before{content:"\f02a"}.fa-bars:before{content:"\f0c9"}.fa-baseball-ball:before{content:"\f433"}.fa-basketball-ball:before{content:"\f434"}.fa-bath:before{content:"\f2cd"}.fa-battery-empty:before{content:"\f244"}.fa-battery-full:before{content:"\f240"}.fa-battery-half:before{content:"\f242"}.fa-battery-quarter:before{content:"\f243"}.fa-battery-three-quarters:before{content:"\f241"}.fa-bed:before{content:"\f236"}.fa-beer:before{content:"\f0fc"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-bell:before{content:"\f0f3"}.fa-bell-slash:before{content:"\f1f6"}.fa-bezier-curve:before{content:"\f55b"}.fa-bible:before{content:"\f647"}.fa-bicycle:before{content:"\f206"}.fa-bimobject:before{content:"\f378"}.fa-binoculars:before{content:"\f1e5"}.fa-birthday-cake:before{content:"\f1fd"}.fa-bitbucket:before{content:"\f171"}.fa-bitcoin:before{content:"\f379"}.fa-bity:before{content:"\f37a"}.fa-black-tie:before{content:"\f27e"}.fa-blackberry:before{content:"\f37b"}.fa-blender:before{content:"\f517"}.fa-blender-phone:before{content:"\f6b6"}.fa-blind:before{content:"\f29d"}.fa-blogger:before{content:"\f37c"}.fa-blogger-b:before{content:"\f37d"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-bold:before{content:"\f032"}.fa-bolt:before{content:"\f0e7"}.fa-bomb:before{content:"\f1e2"}.fa-bone:before{content:"\f5d7"}.fa-bong:before{content:"\f55c"}.fa-book:before{content:"\f02d"}.fa-book-dead:before{content:"\f6b7"}.fa-book-open:before{content:"\f518"}.fa-book-reader:before{content:"\f5da"}.fa-bookmark:before{content:"\f02e"}.fa-bowling-ball:before{content:"\f436"}.fa-box:before{content:"\f466"}.fa-box-open:before{content:"\f49e"}.fa-boxes:before{content:"\f468"}.fa-braille:before{content:"\f2a1"}.fa-brain:before{content:"\f5dc"}.fa-briefcase:before{content:"\f0b1"}.fa-briefcase-medical:before{content:"\f469"}.fa-broadcast-tower:before{content:"\f519"}.fa-broom:before{content:"\f51a"}.fa-brush:before{content:"\f55d"}.fa-btc:before{content:"\f15a"}.fa-bug:before{content:"\f188"}.fa-building:before{content:"\f1ad"}.fa-bullhorn:before{content:"\f0a1"}.fa-bullseye:before{content:"\f140"}.fa-burn:before{content:"\f46a"}.fa-buromobelexperte:before{content:"\f37f"}.fa-bus:before{content:"\f207"}.fa-bus-alt:before{content:"\f55e"}.fa-business-time:before{content:"\f64a"}.fa-buysellads:before{content:"\f20d"}.fa-calculator:before{content:"\f1ec"}.fa-calendar:before{content:"\f133"}.fa-calendar-alt:before{content:"\f073"}.fa-calendar-check:before{content:"\f274"}.fa-calendar-minus:before{content:"\f272"}.fa-calendar-plus:before{content:"\f271"}.fa-calendar-times:before{content:"\f273"}.fa-camera:before{content:"\f030"}.fa-camera-retro:before{content:"\f083"}.fa-campground:before{content:"\f6bb"}.fa-cannabis:before{content:"\f55f"}.fa-capsules:before{content:"\f46b"}.fa-car:before{content:"\f1b9"}.fa-car-alt:before{content:"\f5de"}.fa-car-battery:before{content:"\f5df"}.fa-car-crash:before{content:"\f5e1"}.fa-car-side:before{content:"\f5e4"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-caret-square-down:before{content:"\f150"}.fa-caret-square-left:before{content:"\f191"}.fa-caret-square-right:before{content:"\f152"}.fa-caret-square-up:before{content:"\f151"}.fa-caret-up:before{content:"\f0d8"}.fa-cart-arrow-down:before{content:"\f218"}.fa-cart-plus:before{content:"\f217"}.fa-cat:before{content:"\f6be"}.fa-cc-amazon-pay:before{content:"\f42d"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-apple-pay:before{content:"\f416"}.fa-cc-diners-club:before{content:"\f24c"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-cc-visa:before{content:"\f1f0"}.fa-centercode:before{content:"\f380"}.fa-certificate:before{content:"\f0a3"}.fa-chair:before{content:"\f6c0"}.fa-chalkboard:before{content:"\f51b"}.fa-chalkboard-teacher:before{content:"\f51c"}.fa-charging-station:before{content:"\f5e7"}.fa-chart-area:before{content:"\f1fe"}.fa-chart-bar:before{content:"\f080"}.fa-chart-line:before{content:"\f201"}.fa-chart-pie:before{content:"\f200"}.fa-check:before{content:"\f00c"}.fa-check-circle:before{content:"\f058"}.fa-check-double:before{content:"\f560"}.fa-check-square:before{content:"\f14a"}.fa-chess:before{content:"\f439"}.fa-chess-bishop:before{content:"\f43a"}.fa-chess-board:before{content:"\f43c"}.fa-chess-king:before{content:"\f43f"}.fa-chess-knight:before{content:"\f441"}.fa-chess-pawn:before{content:"\f443"}.fa-chess-queen:before{content:"\f445"}.fa-chess-rook:before{content:"\f447"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-down:before{content:"\f078"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-chevron-up:before{content:"\f077"}.fa-child:before{content:"\f1ae"}.fa-chrome:before{content:"\f268"}.fa-church:before{content:"\f51d"}.fa-circle:before{content:"\f111"}.fa-circle-notch:before{content:"\f1ce"}.fa-city:before{content:"\f64f"}.fa-clipboard:before{content:"\f328"}.fa-clipboard-check:before{content:"\f46c"}.fa-clipboard-list:before{content:"\f46d"}.fa-clock:before{content:"\f017"}.fa-clone:before{content:"\f24d"}.fa-closed-captioning:before{content:"\f20a"}.fa-cloud:before{content:"\f0c2"}.fa-cloud-download-alt:before{content:"\f381"}.fa-cloud-meatball:before{content:"\f73b"}.fa-cloud-moon:before{content:"\f6c3"}.fa-cloud-moon-rain:before{content:"\f73c"}.fa-cloud-rain:before{content:"\f73d"}.fa-cloud-showers-heavy:before{content:"\f740"}.fa-cloud-sun:before{content:"\f6c4"}.fa-cloud-sun-rain:before{content:"\f743"}.fa-cloud-upload-alt:before{content:"\f382"}.fa-cloudscale:before{content:"\f383"}.fa-cloudsmith:before{content:"\f384"}.fa-cloudversify:before{content:"\f385"}.fa-cocktail:before{content:"\f561"}.fa-code:before{content:"\f121"}.fa-code-branch:before{content:"\f126"}.fa-codepen:before{content:"\f1cb"}.fa-codiepie:before{content:"\f284"}.fa-coffee:before{content:"\f0f4"}.fa-cog:before{content:"\f013"}.fa-cogs:before{content:"\f085"}.fa-coins:before{content:"\f51e"}.fa-columns:before{content:"\f0db"}.fa-comment:before{content:"\f075"}.fa-comment-alt:before{content:"\f27a"}.fa-comment-dollar:before{content:"\f651"}.fa-comment-dots:before{content:"\f4ad"}.fa-comment-slash:before{content:"\f4b3"}.fa-comments:before{content:"\f086"}.fa-comments-dollar:before{content:"\f653"}.fa-compact-disc:before{content:"\f51f"}.fa-compass:before{content:"\f14e"}.fa-compress:before{content:"\f066"}.fa-concierge-bell:before{content:"\f562"}.fa-connectdevelop:before{content:"\f20e"}.fa-contao:before{content:"\f26d"}.fa-cookie:before{content:"\f563"}.fa-cookie-bite:before{content:"\f564"}.fa-copy:before{content:"\f0c5"}.fa-copyright:before{content:"\f1f9"}.fa-couch:before{content:"\f4b8"}.fa-cpanel:before{content:"\f388"}.fa-creative-commons:before{content:"\f25e"}.fa-creative-commons-by:before{content:"\f4e7"}.fa-creative-commons-nc:before{content:"\f4e8"}.fa-creative-commons-nc-eu:before{content:"\f4e9"}.fa-creative-commons-nc-jp:before{content:"\f4ea"}.fa-creative-commons-nd:before{content:"\f4eb"}.fa-creative-commons-pd:before{content:"\f4ec"}.fa-creative-commons-pd-alt:before{content:"\f4ed"}.fa-creative-commons-remix:before{content:"\f4ee"}.fa-creative-commons-sa:before{content:"\f4ef"}.fa-creative-commons-sampling:before{content:"\f4f0"}.fa-creative-commons-sampling-plus:before{content:"\f4f1"}.fa-creative-commons-share:before{content:"\f4f2"}.fa-creative-commons-zero:before{content:"\f4f3"}.fa-credit-card:before{content:"\f09d"}.fa-critical-role:before{content:"\f6c9"}.fa-crop:before{content:"\f125"}.fa-crop-alt:before{content:"\f565"}.fa-cross:before{content:"\f654"}.fa-crosshairs:before{content:"\f05b"}.fa-crow:before{content:"\f520"}.fa-crown:before{content:"\f521"}.fa-css3:before{content:"\f13c"}.fa-css3-alt:before{content:"\f38b"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-cut:before{content:"\f0c4"}.fa-cuttlefish:before{content:"\f38c"}.fa-d-and-d:before{content:"\f38d"}.fa-d-and-d-beyond:before{content:"\f6ca"}.fa-dashcube:before{content:"\f210"}.fa-database:before{content:"\f1c0"}.fa-deaf:before{content:"\f2a4"}.fa-delicious:before{content:"\f1a5"}.fa-democrat:before{content:"\f747"}.fa-deploydog:before{content:"\f38e"}.fa-deskpro:before{content:"\f38f"}.fa-desktop:before{content:"\f108"}.fa-dev:before{content:"\f6cc"}.fa-deviantart:before{content:"\f1bd"}.fa-dharmachakra:before{content:"\f655"}.fa-diagnoses:before{content:"\f470"}.fa-dice:before{content:"\f522"}.fa-dice-d20:before{content:"\f6cf"}.fa-dice-d6:before{content:"\f6d1"}.fa-dice-five:before{content:"\f523"}.fa-dice-four:before{content:"\f524"}.fa-dice-one:before{content:"\f525"}.fa-dice-six:before{content:"\f526"}.fa-dice-three:before{content:"\f527"}.fa-dice-two:before{content:"\f528"}.fa-digg:before{content:"\f1a6"}.fa-digital-ocean:before{content:"\f391"}.fa-digital-tachograph:before{content:"\f566"}.fa-directions:before{content:"\f5eb"}.fa-discord:before{content:"\f392"}.fa-discourse:before{content:"\f393"}.fa-divide:before{content:"\f529"}.fa-dizzy:before{content:"\f567"}.fa-dna:before{content:"\f471"}.fa-dochub:before{content:"\f394"}.fa-docker:before{content:"\f395"}.fa-dog:before{content:"\f6d3"}.fa-dollar-sign:before{content:"\f155"}.fa-dolly:before{content:"\f472"}.fa-dolly-flatbed:before{content:"\f474"}.fa-donate:before{content:"\f4b9"}.fa-door-closed:before{content:"\f52a"}.fa-door-open:before{content:"\f52b"}.fa-dot-circle:before{content:"\f192"}.fa-dove:before{content:"\f4ba"}.fa-download:before{content:"\f019"}.fa-draft2digital:before{content:"\f396"}.fa-drafting-compass:before{content:"\f568"}.fa-dragon:before{content:"\f6d5"}.fa-draw-polygon:before{content:"\f5ee"}.fa-dribbble:before{content:"\f17d"}.fa-dribbble-square:before{content:"\f397"}.fa-dropbox:before{content:"\f16b"}.fa-drum:before{content:"\f569"}.fa-drum-steelpan:before{content:"\f56a"}.fa-drumstick-bite:before{content:"\f6d7"}.fa-drupal:before{content:"\f1a9"}.fa-dumbbell:before{content:"\f44b"}.fa-dungeon:before{content:"\f6d9"}.fa-dyalog:before{content:"\f399"}.fa-earlybirds:before{content:"\f39a"}.fa-ebay:before{content:"\f4f4"}.fa-edge:before{content:"\f282"}.fa-edit:before{content:"\f044"}.fa-eject:before{content:"\f052"}.fa-elementor:before{content:"\f430"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-ello:before{content:"\f5f1"}.fa-ember:before{content:"\f423"}.fa-empire:before{content:"\f1d1"}.fa-envelope:before{content:"\f0e0"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-text:before{content:"\f658"}.fa-envelope-square:before{content:"\f199"}.fa-envira:before{content:"\f299"}.fa-equals:before{content:"\f52c"}.fa-eraser:before{content:"\f12d"}.fa-erlang:before{content:"\f39d"}.fa-ethereum:before{content:"\f42e"}.fa-etsy:before{content:"\f2d7"}.fa-euro-sign:before{content:"\f153"}.fa-exchange-alt:before{content:"\f362"}.fa-exclamation:before{content:"\f12a"}.fa-exclamation-circle:before{content:"\f06a"}.fa-exclamation-triangle:before{content:"\f071"}.fa-expand:before{content:"\f065"}.fa-expand-arrows-alt:before{content:"\f31e"}.fa-expeditedssl:before{content:"\f23e"}.fa-external-link-alt:before{content:"\f35d"}.fa-external-link-square-alt:before{content:"\f360"}.fa-eye:before{content:"\f06e"}.fa-eye-dropper:before{content:"\f1fb"}.fa-eye-slash:before{content:"\f070"}.fa-facebook:before{content:"\f09a"}.fa-facebook-f:before{content:"\f39e"}.fa-facebook-messenger:before{content:"\f39f"}.fa-facebook-square:before{content:"\f082"}.fa-fantasy-flight-games:before{content:"\f6dc"}.fa-fast-backward:before{content:"\f049"}.fa-fast-forward:before{content:"\f050"}.fa-fax:before{content:"\f1ac"}.fa-feather:before{content:"\f52d"}.fa-feather-alt:before{content:"\f56b"}.fa-female:before{content:"\f182"}.fa-fighter-jet:before{content:"\f0fb"}.fa-file:before{content:"\f15b"}.fa-file-alt:before{content:"\f15c"}.fa-file-archive:before{content:"\f1c6"}.fa-file-audio:before{content:"\f1c7"}.fa-file-code:before{content:"\f1c9"}.fa-file-contract:before{content:"\f56c"}.fa-file-csv:before{content:"\f6dd"}.fa-file-download:before{content:"\f56d"}.fa-file-excel:before{content:"\f1c3"}.fa-file-export:before{content:"\f56e"}.fa-file-image:before{content:"\f1c5"}.fa-file-import:before{content:"\f56f"}.fa-file-invoice:before{content:"\f570"}.fa-file-invoice-dollar:before{content:"\f571"}.fa-file-medical:before{content:"\f477"}.fa-file-medical-alt:before{content:"\f478"}.fa-file-pdf:before{content:"\f1c1"}.fa-file-powerpoint:before{content:"\f1c4"}.fa-file-prescription:before{content:"\f572"}.fa-file-signature:before{content:"\f573"}.fa-file-upload:before{content:"\f574"}.fa-file-video:before{content:"\f1c8"}.fa-file-word:before{content:"\f1c2"}.fa-fill:before{content:"\f575"}.fa-fill-drip:before{content:"\f576"}.fa-film:before{content:"\f008"}.fa-filter:before{content:"\f0b0"}.fa-fingerprint:before{content:"\f577"}.fa-fire:before{content:"\f06d"}.fa-fire-extinguisher:before{content:"\f134"}.fa-firefox:before{content:"\f269"}.fa-first-aid:before{content:"\f479"}.fa-first-order:before{content:"\f2b0"}.fa-first-order-alt:before{content:"\f50a"}.fa-firstdraft:before{content:"\f3a1"}.fa-fish:before{content:"\f578"}.fa-fist-raised:before{content:"\f6de"}.fa-flag:before{content:"\f024"}.fa-flag-checkered:before{content:"\f11e"}.fa-flag-usa:before{content:"\f74d"}.fa-flask:before{content:"\f0c3"}.fa-flickr:before{content:"\f16e"}.fa-flipboard:before{content:"\f44d"}.fa-flushed:before{content:"\f579"}.fa-fly:before{content:"\f417"}.fa-folder:before{content:"\f07b"}.fa-folder-minus:before{content:"\f65d"}.fa-folder-open:before{content:"\f07c"}.fa-folder-plus:before{content:"\f65e"}.fa-font:before{content:"\f031"}.fa-font-awesome:before{content:"\f2b4"}.fa-font-awesome-alt:before{content:"\f35c"}.fa-font-awesome-flag:before{content:"\f425"}.fa-font-awesome-logo-full:before{content:"\f4e6"}.fa-fonticons:before{content:"\f280"}.fa-fonticons-fi:before{content:"\f3a2"}.fa-football-ball:before{content:"\f44e"}.fa-fort-awesome:before{content:"\f286"}.fa-fort-awesome-alt:before{content:"\f3a3"}.fa-forumbee:before{content:"\f211"}.fa-forward:before{content:"\f04e"}.fa-foursquare:before{content:"\f180"}.fa-free-code-camp:before{content:"\f2c5"}.fa-freebsd:before{content:"\f3a4"}.fa-frog:before{content:"\f52e"}.fa-frown:before{content:"\f119"}.fa-frown-open:before{content:"\f57a"}.fa-fulcrum:before{content:"\f50b"}.fa-funnel-dollar:before{content:"\f662"}.fa-futbol:before{content:"\f1e3"}.fa-galactic-republic:before{content:"\f50c"}.fa-galactic-senate:before{content:"\f50d"}.fa-gamepad:before{content:"\f11b"}.fa-gas-pump:before{content:"\f52f"}.fa-gavel:before{content:"\f0e3"}.fa-gem:before{content:"\f3a5"}.fa-genderless:before{content:"\f22d"}.fa-get-pocket:before{content:"\f265"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-ghost:before{content:"\f6e2"}.fa-gift:before{content:"\f06b"}.fa-git:before{content:"\f1d3"}.fa-git-square:before{content:"\f1d2"}.fa-github:before{content:"\f09b"}.fa-github-alt:before{content:"\f113"}.fa-github-square:before{content:"\f092"}.fa-gitkraken:before{content:"\f3a6"}.fa-gitlab:before{content:"\f296"}.fa-gitter:before{content:"\f426"}.fa-glass-martini:before{content:"\f000"}.fa-glass-martini-alt:before{content:"\f57b"}.fa-glasses:before{content:"\f530"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-globe:before{content:"\f0ac"}.fa-globe-africa:before{content:"\f57c"}.fa-globe-americas:before{content:"\f57d"}.fa-globe-asia:before{content:"\f57e"}.fa-gofore:before{content:"\f3a7"}.fa-golf-ball:before{content:"\f450"}.fa-goodreads:before{content:"\f3a8"}.fa-goodreads-g:before{content:"\f3a9"}.fa-google:before{content:"\f1a0"}.fa-google-drive:before{content:"\f3aa"}.fa-google-play:before{content:"\f3ab"}.fa-google-plus:before{content:"\f2b3"}.fa-google-plus-g:before{content:"\f0d5"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-wallet:before{content:"\f1ee"}.fa-gopuram:before{content:"\f664"}.fa-graduation-cap:before{content:"\f19d"}.fa-gratipay:before{content:"\f184"}.fa-grav:before{content:"\f2d6"}.fa-greater-than:before{content:"\f531"}.fa-greater-than-equal:before{content:"\f532"}.fa-grimace:before{content:"\f57f"}.fa-grin:before{content:"\f580"}.fa-grin-alt:before{content:"\f581"}.fa-grin-beam:before{content:"\f582"}.fa-grin-beam-sweat:before{content:"\f583"}.fa-grin-hearts:before{content:"\f584"}.fa-grin-squint:before{content:"\f585"}.fa-grin-squint-tears:before{content:"\f586"}.fa-grin-stars:before{content:"\f587"}.fa-grin-tears:before{content:"\f588"}.fa-grin-tongue:before{content:"\f589"}.fa-grin-tongue-squint:before{content:"\f58a"}.fa-grin-tongue-wink:before{content:"\f58b"}.fa-grin-wink:before{content:"\f58c"}.fa-grip-horizontal:before{content:"\f58d"}.fa-grip-vertical:before{content:"\f58e"}.fa-gripfire:before{content:"\f3ac"}.fa-grunt:before{content:"\f3ad"}.fa-gulp:before{content:"\f3ae"}.fa-h-square:before{content:"\f0fd"}.fa-hacker-news:before{content:"\f1d4"}.fa-hacker-news-square:before{content:"\f3af"}.fa-hackerrank:before{content:"\f5f7"}.fa-hammer:before{content:"\f6e3"}.fa-hamsa:before{content:"\f665"}.fa-hand-holding:before{content:"\f4bd"}.fa-hand-holding-heart:before{content:"\f4be"}.fa-hand-holding-usd:before{content:"\f4c0"}.fa-hand-lizard:before{content:"\f258"}.fa-hand-paper:before{content:"\f256"}.fa-hand-peace:before{content:"\f25b"}.fa-hand-point-down:before{content:"\f0a7"}.fa-hand-point-left:before{content:"\f0a5"}.fa-hand-point-right:before{content:"\f0a4"}.fa-hand-point-up:before{content:"\f0a6"}.fa-hand-pointer:before{content:"\f25a"}.fa-hand-rock:before{content:"\f255"}.fa-hand-scissors:before{content:"\f257"}.fa-hand-spock:before{content:"\f259"}.fa-hands:before{content:"\f4c2"}.fa-hands-helping:before{content:"\f4c4"}.fa-handshake:before{content:"\f2b5"}.fa-hanukiah:before{content:"\f6e6"}.fa-hashtag:before{content:"\f292"}.fa-hat-wizard:before{content:"\f6e8"}.fa-haykal:before{content:"\f666"}.fa-hdd:before{content:"\f0a0"}.fa-heading:before{content:"\f1dc"}.fa-headphones:before{content:"\f025"}.fa-headphones-alt:before{content:"\f58f"}.fa-headset:before{content:"\f590"}.fa-heart:before{content:"\f004"}.fa-heartbeat:before{content:"\f21e"}.fa-helicopter:before{content:"\f533"}.fa-highlighter:before{content:"\f591"}.fa-hiking:before{content:"\f6ec"}.fa-hippo:before{content:"\f6ed"}.fa-hips:before{content:"\f452"}.fa-hire-a-helper:before{content:"\f3b0"}.fa-history:before{content:"\f1da"}.fa-hockey-puck:before{content:"\f453"}.fa-home:before{content:"\f015"}.fa-hooli:before{content:"\f427"}.fa-hornbill:before{content:"\f592"}.fa-horse:before{content:"\f6f0"}.fa-hospital:before{content:"\f0f8"}.fa-hospital-alt:before{content:"\f47d"}.fa-hospital-symbol:before{content:"\f47e"}.fa-hot-tub:before{content:"\f593"}.fa-hotel:before{content:"\f594"}.fa-hotjar:before{content:"\f3b1"}.fa-hourglass:before{content:"\f254"}.fa-hourglass-end:before{content:"\f253"}.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-start:before{content:"\f251"}.fa-house-damage:before{content:"\f6f1"}.fa-houzz:before{content:"\f27c"}.fa-hryvnia:before{content:"\f6f2"}.fa-html5:before{content:"\f13b"}.fa-hubspot:before{content:"\f3b2"}.fa-i-cursor:before{content:"\f246"}.fa-id-badge:before{content:"\f2c1"}.fa-id-card:before{content:"\f2c2"}.fa-id-card-alt:before{content:"\f47f"}.fa-image:before{content:"\f03e"}.fa-images:before{content:"\f302"}.fa-imdb:before{content:"\f2d8"}.fa-inbox:before{content:"\f01c"}.fa-indent:before{content:"\f03c"}.fa-industry:before{content:"\f275"}.fa-infinity:before{content:"\f534"}.fa-info:before{content:"\f129"}.fa-info-circle:before{content:"\f05a"}.fa-instagram:before{content:"\f16d"}.fa-internet-explorer:before{content:"\f26b"}.fa-ioxhost:before{content:"\f208"}.fa-italic:before{content:"\f033"}.fa-itunes:before{content:"\f3b4"}.fa-itunes-note:before{content:"\f3b5"}.fa-java:before{content:"\f4e4"}.fa-jedi:before{content:"\f669"}.fa-jedi-order:before{content:"\f50e"}.fa-jenkins:before{content:"\f3b6"}.fa-joget:before{content:"\f3b7"}.fa-joint:before{content:"\f595"}.fa-joomla:before{content:"\f1aa"}.fa-journal-whills:before{content:"\f66a"}.fa-js:before{content:"\f3b8"}.fa-js-square:before{content:"\f3b9"}.fa-jsfiddle:before{content:"\f1cc"}.fa-kaaba:before{content:"\f66b"}.fa-kaggle:before{content:"\f5fa"}.fa-key:before{content:"\f084"}.fa-keybase:before{content:"\f4f5"}.fa-keyboard:before{content:"\f11c"}.fa-keycdn:before{content:"\f3ba"}.fa-khanda:before{content:"\f66d"}.fa-kickstarter:before{content:"\f3bb"}.fa-kickstarter-k:before{content:"\f3bc"}.fa-kiss:before{content:"\f596"}.fa-kiss-beam:before{content:"\f597"}.fa-kiss-wink-heart:before{content:"\f598"}.fa-kiwi-bird:before{content:"\f535"}.fa-korvue:before{content:"\f42f"}.fa-landmark:before{content:"\f66f"}.fa-language:before{content:"\f1ab"}.fa-laptop:before{content:"\f109"}.fa-laptop-code:before{content:"\f5fc"}.fa-laravel:before{content:"\f3bd"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-laugh:before{content:"\f599"}.fa-laugh-beam:before{content:"\f59a"}.fa-laugh-squint:before{content:"\f59b"}.fa-laugh-wink:before{content:"\f59c"}.fa-layer-group:before{content:"\f5fd"}.fa-leaf:before{content:"\f06c"}.fa-leanpub:before{content:"\f212"}.fa-lemon:before{content:"\f094"}.fa-less:before{content:"\f41d"}.fa-less-than:before{content:"\f536"}.fa-less-than-equal:before{content:"\f537"}.fa-level-down-alt:before{content:"\f3be"}.fa-level-up-alt:before{content:"\f3bf"}.fa-life-ring:before{content:"\f1cd"}.fa-lightbulb:before{content:"\f0eb"}.fa-line:before{content:"\f3c0"}.fa-link:before{content:"\f0c1"}.fa-linkedin:before{content:"\f08c"}.fa-linkedin-in:before{content:"\f0e1"}.fa-linode:before{content:"\f2b8"}.fa-linux:before{content:"\f17c"}.fa-lira-sign:before{content:"\f195"}.fa-list:before{content:"\f03a"}.fa-list-alt:before{content:"\f022"}.fa-list-ol:before{content:"\f0cb"}.fa-list-ul:before{content:"\f0ca"}.fa-location-arrow:before{content:"\f124"}.fa-lock:before{content:"\f023"}.fa-lock-open:before{content:"\f3c1"}.fa-long-arrow-alt-down:before{content:"\f309"}.fa-long-arrow-alt-left:before{content:"\f30a"}.fa-long-arrow-alt-right:before{content:"\f30b"}.fa-long-arrow-alt-up:before{content:"\f30c"}.fa-low-vision:before{content:"\f2a8"}.fa-luggage-cart:before{content:"\f59d"}.fa-lyft:before{content:"\f3c3"}.fa-magento:before{content:"\f3c4"}.fa-magic:before{content:"\f0d0"}.fa-magnet:before{content:"\f076"}.fa-mail-bulk:before{content:"\f674"}.fa-mailchimp:before{content:"\f59e"}.fa-male:before{content:"\f183"}.fa-mandalorian:before{content:"\f50f"}.fa-map:before{content:"\f279"}.fa-map-marked:before{content:"\f59f"}.fa-map-marked-alt:before{content:"\f5a0"}.fa-map-marker:before{content:"\f041"}.fa-map-marker-alt:before{content:"\f3c5"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-markdown:before{content:"\f60f"}.fa-marker:before{content:"\f5a1"}.fa-mars:before{content:"\f222"}.fa-mars-double:before{content:"\f227"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mask:before{content:"\f6fa"}.fa-mastodon:before{content:"\f4f6"}.fa-maxcdn:before{content:"\f136"}.fa-medal:before{content:"\f5a2"}.fa-medapps:before{content:"\f3c6"}.fa-medium:before{content:"\f23a"}.fa-medium-m:before{content:"\f3c7"}.fa-medkit:before{content:"\f0fa"}.fa-medrt:before{content:"\f3c8"}.fa-meetup:before{content:"\f2e0"}.fa-megaport:before{content:"\f5a3"}.fa-meh:before{content:"\f11a"}.fa-meh-blank:before{content:"\f5a4"}.fa-meh-rolling-eyes:before{content:"\f5a5"}.fa-memory:before{content:"\f538"}.fa-menorah:before{content:"\f676"}.fa-mercury:before{content:"\f223"}.fa-meteor:before{content:"\f753"}.fa-microchip:before{content:"\f2db"}.fa-microphone:before{content:"\f130"}.fa-microphone-alt:before{content:"\f3c9"}.fa-microphone-alt-slash:before{content:"\f539"}.fa-microphone-slash:before{content:"\f131"}.fa-microscope:before{content:"\f610"}.fa-microsoft:before{content:"\f3ca"}.fa-minus:before{content:"\f068"}.fa-minus-circle:before{content:"\f056"}.fa-minus-square:before{content:"\f146"}.fa-mix:before{content:"\f3cb"}.fa-mixcloud:before{content:"\f289"}.fa-mizuni:before{content:"\f3cc"}.fa-mobile:before{content:"\f10b"}.fa-mobile-alt:before{content:"\f3cd"}.fa-modx:before{content:"\f285"}.fa-monero:before{content:"\f3d0"}.fa-money-bill:before{content:"\f0d6"}.fa-money-bill-alt:before{content:"\f3d1"}.fa-money-bill-wave:before{content:"\f53a"}.fa-money-bill-wave-alt:before{content:"\f53b"}.fa-money-check:before{content:"\f53c"}.fa-money-check-alt:before{content:"\f53d"}.fa-monument:before{content:"\f5a6"}.fa-moon:before{content:"\f186"}.fa-mortar-pestle:before{content:"\f5a7"}.fa-mosque:before{content:"\f678"}.fa-motorcycle:before{content:"\f21c"}.fa-mountain:before{content:"\f6fc"}.fa-mouse-pointer:before{content:"\f245"}.fa-music:before{content:"\f001"}.fa-napster:before{content:"\f3d2"}.fa-neos:before{content:"\f612"}.fa-network-wired:before{content:"\f6ff"}.fa-neuter:before{content:"\f22c"}.fa-newspaper:before{content:"\f1ea"}.fa-nimblr:before{content:"\f5a8"}.fa-nintendo-switch:before{content:"\f418"}.fa-node:before{content:"\f419"}.fa-node-js:before{content:"\f3d3"}.fa-not-equal:before{content:"\f53e"}.fa-notes-medical:before{content:"\f481"}.fa-npm:before{content:"\f3d4"}.fa-ns8:before{content:"\f3d5"}.fa-nutritionix:before{content:"\f3d6"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-oil-can:before{content:"\f613"}.fa-old-republic:before{content:"\f510"}.fa-om:before{content:"\f679"}.fa-opencart:before{content:"\f23d"}.fa-openid:before{content:"\f19b"}.fa-opera:before{content:"\f26a"}.fa-optin-monster:before{content:"\f23c"}.fa-osi:before{content:"\f41a"}.fa-otter:before{content:"\f700"}.fa-outdent:before{content:"\f03b"}.fa-page4:before{content:"\f3d7"}.fa-pagelines:before{content:"\f18c"}.fa-paint-brush:before{content:"\f1fc"}.fa-paint-roller:before{content:"\f5aa"}.fa-palette:before{content:"\f53f"}.fa-palfed:before{content:"\f3d8"}.fa-pallet:before{content:"\f482"}.fa-paper-plane:before{content:"\f1d8"}.fa-paperclip:before{content:"\f0c6"}.fa-parachute-box:before{content:"\f4cd"}.fa-paragraph:before{content:"\f1dd"}.fa-parking:before{content:"\f540"}.fa-passport:before{content:"\f5ab"}.fa-pastafarianism:before{content:"\f67b"}.fa-paste:before{content:"\f0ea"}.fa-patreon:before{content:"\f3d9"}.fa-pause:before{content:"\f04c"}.fa-pause-circle:before{content:"\f28b"}.fa-paw:before{content:"\f1b0"}.fa-paypal:before{content:"\f1ed"}.fa-peace:before{content:"\f67c"}.fa-pen:before{content:"\f304"}.fa-pen-alt:before{content:"\f305"}.fa-pen-fancy:before{content:"\f5ac"}.fa-pen-nib:before{content:"\f5ad"}.fa-pen-square:before{content:"\f14b"}.fa-pencil-alt:before{content:"\f303"}.fa-pencil-ruler:before{content:"\f5ae"}.fa-penny-arcade:before{content:"\f704"}.fa-people-carry:before{content:"\f4ce"}.fa-percent:before{content:"\f295"}.fa-percentage:before{content:"\f541"}.fa-periscope:before{content:"\f3da"}.fa-person-booth:before{content:"\f756"}.fa-phabricator:before{content:"\f3db"}.fa-phoenix-framework:before{content:"\f3dc"}.fa-phoenix-squadron:before{content:"\f511"}.fa-phone:before{content:"\f095"}.fa-phone-slash:before{content:"\f3dd"}.fa-phone-square:before{content:"\f098"}.fa-phone-volume:before{content:"\f2a0"}.fa-php:before{content:"\f457"}.fa-pied-piper:before{content:"\f2ae"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-pied-piper-hat:before{content:"\f4e5"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-piggy-bank:before{content:"\f4d3"}.fa-pills:before{content:"\f484"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-p:before{content:"\f231"}.fa-pinterest-square:before{content:"\f0d3"}.fa-place-of-worship:before{content:"\f67f"}.fa-plane:before{content:"\f072"}.fa-plane-arrival:before{content:"\f5af"}.fa-plane-departure:before{content:"\f5b0"}.fa-play:before{content:"\f04b"}.fa-play-circle:before{content:"\f144"}.fa-playstation:before{content:"\f3df"}.fa-plug:before{content:"\f1e6"}.fa-plus:before{content:"\f067"}.fa-plus-circle:before{content:"\f055"}.fa-plus-square:before{content:"\f0fe"}.fa-podcast:before{content:"\f2ce"}.fa-poll:before{content:"\f681"}.fa-poll-h:before{content:"\f682"}.fa-poo:before{content:"\f2fe"}.fa-poo-storm:before{content:"\f75a"}.fa-poop:before{content:"\f619"}.fa-portrait:before{content:"\f3e0"}.fa-pound-sign:before{content:"\f154"}.fa-power-off:before{content:"\f011"}.fa-pray:before{content:"\f683"}.fa-praying-hands:before{content:"\f684"}.fa-prescription:before{content:"\f5b1"}.fa-prescription-bottle:before{content:"\f485"}.fa-prescription-bottle-alt:before{content:"\f486"}.fa-print:before{content:"\f02f"}.fa-procedures:before{content:"\f487"}.fa-product-hunt:before{content:"\f288"}.fa-project-diagram:before{content:"\f542"}.fa-pushed:before{content:"\f3e1"}.fa-puzzle-piece:before{content:"\f12e"}.fa-python:before{content:"\f3e2"}.fa-qq:before{content:"\f1d6"}.fa-qrcode:before{content:"\f029"}.fa-question:before{content:"\f128"}.fa-question-circle:before{content:"\f059"}.fa-quidditch:before{content:"\f458"}.fa-quinscape:before{content:"\f459"}.fa-quora:before{content:"\f2c4"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-quran:before{content:"\f687"}.fa-r-project:before{content:"\f4f7"}.fa-rainbow:before{content:"\f75b"}.fa-random:before{content:"\f074"}.fa-ravelry:before{content:"\f2d9"}.fa-react:before{content:"\f41b"}.fa-reacteurope:before{content:"\f75d"}.fa-readme:before{content:"\f4d5"}.fa-rebel:before{content:"\f1d0"}.fa-receipt:before{content:"\f543"}.fa-recycle:before{content:"\f1b8"}.fa-red-river:before{content:"\f3e3"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-alien:before{content:"\f281"}.fa-reddit-square:before{content:"\f1a2"}.fa-redo:before{content:"\f01e"}.fa-redo-alt:before{content:"\f2f9"}.fa-registered:before{content:"\f25d"}.fa-renren:before{content:"\f18b"}.fa-reply:before{content:"\f3e5"}.fa-reply-all:before{content:"\f122"}.fa-replyd:before{content:"\f3e6"}.fa-republican:before{content:"\f75e"}.fa-researchgate:before{content:"\f4f8"}.fa-resolving:before{content:"\f3e7"}.fa-retweet:before{content:"\f079"}.fa-rev:before{content:"\f5b2"}.fa-ribbon:before{content:"\f4d6"}.fa-ring:before{content:"\f70b"}.fa-road:before{content:"\f018"}.fa-robot:before{content:"\f544"}.fa-rocket:before{content:"\f135"}.fa-rocketchat:before{content:"\f3e8"}.fa-rockrms:before{content:"\f3e9"}.fa-route:before{content:"\f4d7"}.fa-rss:before{content:"\f09e"}.fa-rss-square:before{content:"\f143"}.fa-ruble-sign:before{content:"\f158"}.fa-ruler:before{content:"\f545"}.fa-ruler-combined:before{content:"\f546"}.fa-ruler-horizontal:before{content:"\f547"}.fa-ruler-vertical:before{content:"\f548"}.fa-running:before{content:"\f70c"}.fa-rupee-sign:before{content:"\f156"}.fa-sad-cry:before{content:"\f5b3"}.fa-sad-tear:before{content:"\f5b4"}.fa-safari:before{content:"\f267"}.fa-sass:before{content:"\f41e"}.fa-save:before{content:"\f0c7"}.fa-schlix:before{content:"\f3ea"}.fa-school:before{content:"\f549"}.fa-screwdriver:before{content:"\f54a"}.fa-scribd:before{content:"\f28a"}.fa-scroll:before{content:"\f70e"}.fa-search:before{content:"\f002"}.fa-search-dollar:before{content:"\f688"}.fa-search-location:before{content:"\f689"}.fa-search-minus:before{content:"\f010"}.fa-search-plus:before{content:"\f00e"}.fa-searchengin:before{content:"\f3eb"}.fa-seedling:before{content:"\f4d8"}.fa-sellcast:before{content:"\f2da"}.fa-sellsy:before{content:"\f213"}.fa-server:before{content:"\f233"}.fa-servicestack:before{content:"\f3ec"}.fa-shapes:before{content:"\f61f"}.fa-share:before{content:"\f064"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-share-square:before{content:"\f14d"}.fa-shekel-sign:before{content:"\f20b"}.fa-shield-alt:before{content:"\f3ed"}.fa-ship:before{content:"\f21a"}.fa-shipping-fast:before{content:"\f48b"}.fa-shirtsinbulk:before{content:"\f214"}.fa-shoe-prints:before{content:"\f54b"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-shopping-cart:before{content:"\f07a"}.fa-shopware:before{content:"\f5b5"}.fa-shower:before{content:"\f2cc"}.fa-shuttle-van:before{content:"\f5b6"}.fa-sign:before{content:"\f4d9"}.fa-sign-in-alt:before{content:"\f2f6"}.fa-sign-language:before{content:"\f2a7"}.fa-sign-out-alt:before{content:"\f2f5"}.fa-signal:before{content:"\f012"}.fa-signature:before{content:"\f5b7"}.fa-simplybuilt:before{content:"\f215"}.fa-sistrix:before{content:"\f3ee"}.fa-sitemap:before{content:"\f0e8"}.fa-sith:before{content:"\f512"}.fa-skull:before{content:"\f54c"}.fa-skull-crossbones:before{content:"\f714"}.fa-skyatlas:before{content:"\f216"}.fa-skype:before{content:"\f17e"}.fa-slack:before{content:"\f198"}.fa-slack-hash:before{content:"\f3ef"}.fa-slash:before{content:"\f715"}.fa-sliders-h:before{content:"\f1de"}.fa-slideshare:before{content:"\f1e7"}.fa-smile:before{content:"\f118"}.fa-smile-beam:before{content:"\f5b8"}.fa-smile-wink:before{content:"\f4da"}.fa-smog:before{content:"\f75f"}.fa-smoking:before{content:"\f48d"}.fa-smoking-ban:before{content:"\f54d"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-snowflake:before{content:"\f2dc"}.fa-socks:before{content:"\f696"}.fa-solar-panel:before{content:"\f5ba"}.fa-sort:before{content:"\f0dc"}.fa-sort-alpha-down:before{content:"\f15d"}.fa-sort-alpha-up:before{content:"\f15e"}.fa-sort-amount-down:before{content:"\f160"}.fa-sort-amount-up:before{content:"\f161"}.fa-sort-down:before{content:"\f0dd"}.fa-sort-numeric-down:before{content:"\f162"}.fa-sort-numeric-up:before{content:"\f163"}.fa-sort-up:before{content:"\f0de"}.fa-soundcloud:before{content:"\f1be"}.fa-spa:before{content:"\f5bb"}.fa-space-shuttle:before{content:"\f197"}.fa-speakap:before{content:"\f3f3"}.fa-spider:before{content:"\f717"}.fa-spinner:before{content:"\f110"}.fa-splotch:before{content:"\f5bc"}.fa-spotify:before{content:"\f1bc"}.fa-spray-can:before{content:"\f5bd"}.fa-square:before{content:"\f0c8"}.fa-square-full:before{content:"\f45c"}.fa-square-root-alt:before{content:"\f698"}.fa-squarespace:before{content:"\f5be"}.fa-stack-exchange:before{content:"\f18d"}.fa-stack-overflow:before{content:"\f16c"}.fa-stamp:before{content:"\f5bf"}.fa-star:before{content:"\f005"}.fa-star-and-crescent:before{content:"\f699"}.fa-star-half:before{content:"\f089"}.fa-star-half-alt:before{content:"\f5c0"}.fa-star-of-david:before{content:"\f69a"}.fa-star-of-life:before{content:"\f621"}.fa-staylinked:before{content:"\f3f5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-steam-symbol:before{content:"\f3f6"}.fa-step-backward:before{content:"\f048"}.fa-step-forward:before{content:"\f051"}.fa-stethoscope:before{content:"\f0f1"}.fa-sticker-mule:before{content:"\f3f7"}.fa-sticky-note:before{content:"\f249"}.fa-stop:before{content:"\f04d"}.fa-stop-circle:before{content:"\f28d"}.fa-stopwatch:before{content:"\f2f2"}.fa-store:before{content:"\f54e"}.fa-store-alt:before{content:"\f54f"}.fa-strava:before{content:"\f428"}.fa-stream:before{content:"\f550"}.fa-street-view:before{content:"\f21d"}.fa-strikethrough:before{content:"\f0cc"}.fa-stripe:before{content:"\f429"}.fa-stripe-s:before{content:"\f42a"}.fa-stroopwafel:before{content:"\f551"}.fa-studiovinari:before{content:"\f3f8"}.fa-stumbleupon:before{content:"\f1a4"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-subscript:before{content:"\f12c"}.fa-subway:before{content:"\f239"}.fa-suitcase:before{content:"\f0f2"}.fa-suitcase-rolling:before{content:"\f5c1"}.fa-sun:before{content:"\f185"}.fa-superpowers:before{content:"\f2dd"}.fa-superscript:before{content:"\f12b"}.fa-supple:before{content:"\f3f9"}.fa-surprise:before{content:"\f5c2"}.fa-swatchbook:before{content:"\f5c3"}.fa-swimmer:before{content:"\f5c4"}.fa-swimming-pool:before{content:"\f5c5"}.fa-synagogue:before{content:"\f69b"}.fa-sync:before{content:"\f021"}.fa-sync-alt:before{content:"\f2f1"}.fa-syringe:before{content:"\f48e"}.fa-table:before{content:"\f0ce"}.fa-table-tennis:before{content:"\f45d"}.fa-tablet:before{content:"\f10a"}.fa-tablet-alt:before{content:"\f3fa"}.fa-tablets:before{content:"\f490"}.fa-tachometer-alt:before{content:"\f3fd"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-tape:before{content:"\f4db"}.fa-tasks:before{content:"\f0ae"}.fa-taxi:before{content:"\f1ba"}.fa-teamspeak:before{content:"\f4f9"}.fa-teeth:before{content:"\f62e"}.fa-teeth-open:before{content:"\f62f"}.fa-telegram:before{content:"\f2c6"}.fa-telegram-plane:before{content:"\f3fe"}.fa-temperature-high:before{content:"\f769"}.fa-temperature-low:before{content:"\f76b"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-terminal:before{content:"\f120"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-th:before{content:"\f00a"}.fa-th-large:before{content:"\f009"}.fa-th-list:before{content:"\f00b"}.fa-the-red-yeti:before{content:"\f69d"}.fa-theater-masks:before{content:"\f630"}.fa-themeco:before{content:"\f5c6"}.fa-themeisle:before{content:"\f2b2"}.fa-thermometer:before{content:"\f491"}.fa-thermometer-empty:before{content:"\f2cb"}.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-think-peaks:before{content:"\f731"}.fa-thumbs-down:before{content:"\f165"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbtack:before{content:"\f08d"}.fa-ticket-alt:before{content:"\f3ff"}.fa-times:before{content:"\f00d"}.fa-times-circle:before{content:"\f057"}.fa-tint:before{content:"\f043"}.fa-tint-slash:before{content:"\f5c7"}.fa-tired:before{content:"\f5c8"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-toilet-paper:before{content:"\f71e"}.fa-toolbox:before{content:"\f552"}.fa-tooth:before{content:"\f5c9"}.fa-torah:before{content:"\f6a0"}.fa-torii-gate:before{content:"\f6a1"}.fa-tractor:before{content:"\f722"}.fa-trade-federation:before{content:"\f513"}.fa-trademark:before{content:"\f25c"}.fa-traffic-light:before{content:"\f637"}.fa-train:before{content:"\f238"}.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-trash:before{content:"\f1f8"}.fa-trash-alt:before{content:"\f2ed"}.fa-tree:before{content:"\f1bb"}.fa-trello:before{content:"\f181"}.fa-tripadvisor:before{content:"\f262"}.fa-trophy:before{content:"\f091"}.fa-truck:before{content:"\f0d1"}.fa-truck-loading:before{content:"\f4de"}.fa-truck-monster:before{content:"\f63b"}.fa-truck-moving:before{content:"\f4df"}.fa-truck-pickup:before{content:"\f63c"}.fa-tshirt:before{content:"\f553"}.fa-tty:before{content:"\f1e4"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-tv:before{content:"\f26c"}.fa-twitch:before{content:"\f1e8"}.fa-twitter:before{content:"\f099"}.fa-twitter-square:before{content:"\f081"}.fa-typo3:before{content:"\f42b"}.fa-uber:before{content:"\f402"}.fa-uikit:before{content:"\f403"}.fa-umbrella:before{content:"\f0e9"}.fa-umbrella-beach:before{content:"\f5ca"}.fa-underline:before{content:"\f0cd"}.fa-undo:before{content:"\f0e2"}.fa-undo-alt:before{content:"\f2ea"}.fa-uniregistry:before{content:"\f404"}.fa-universal-access:before{content:"\f29a"}.fa-university:before{content:"\f19c"}.fa-unlink:before{content:"\f127"}.fa-unlock:before{content:"\f09c"}.fa-unlock-alt:before{content:"\f13e"}.fa-untappd:before{content:"\f405"}.fa-upload:before{content:"\f093"}.fa-usb:before{content:"\f287"}.fa-user:before{content:"\f007"}.fa-user-alt:before{content:"\f406"}.fa-user-alt-slash:before{content:"\f4fa"}.fa-user-astronaut:before{content:"\f4fb"}.fa-user-check:before{content:"\f4fc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-clock:before{content:"\f4fd"}.fa-user-cog:before{content:"\f4fe"}.fa-user-edit:before{content:"\f4ff"}.fa-user-friends:before{content:"\f500"}.fa-user-graduate:before{content:"\f501"}.fa-user-injured:before{content:"\f728"}.fa-user-lock:before{content:"\f502"}.fa-user-md:before{content:"\f0f0"}.fa-user-minus:before{content:"\f503"}.fa-user-ninja:before{content:"\f504"}.fa-user-plus:before{content:"\f234"}.fa-user-secret:before{content:"\f21b"}.fa-user-shield:before{content:"\f505"}.fa-user-slash:before{content:"\f506"}.fa-user-tag:before{content:"\f507"}.fa-user-tie:before{content:"\f508"}.fa-user-times:before{content:"\f235"}.fa-users:before{content:"\f0c0"}.fa-users-cog:before{content:"\f509"}.fa-ussunnah:before{content:"\f407"}.fa-utensil-spoon:before{content:"\f2e5"}.fa-utensils:before{content:"\f2e7"}.fa-vaadin:before{content:"\f408"}.fa-vector-square:before{content:"\f5cb"}.fa-venus:before{content:"\f221"}.fa-venus-double:before{content:"\f226"}.fa-venus-mars:before{content:"\f228"}.fa-viacoin:before{content:"\f237"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-vial:before{content:"\f492"}.fa-vials:before{content:"\f493"}.fa-viber:before{content:"\f409"}.fa-video:before{content:"\f03d"}.fa-video-slash:before{content:"\f4e2"}.fa-vihara:before{content:"\f6a7"}.fa-vimeo:before{content:"\f40a"}.fa-vimeo-square:before{content:"\f194"}.fa-vimeo-v:before{content:"\f27d"}.fa-vine:before{content:"\f1ca"}.fa-vk:before{content:"\f189"}.fa-vnv:before{content:"\f40b"}.fa-volleyball-ball:before{content:"\f45f"}.fa-volume-down:before{content:"\f027"}.fa-volume-mute:before{content:"\f6a9"}.fa-volume-off:before{content:"\f026"}.fa-volume-up:before{content:"\f028"}.fa-vote-yea:before{content:"\f772"}.fa-vr-cardboard:before{content:"\f729"}.fa-vuejs:before{content:"\f41f"}.fa-walking:before{content:"\f554"}.fa-wallet:before{content:"\f555"}.fa-warehouse:before{content:"\f494"}.fa-water:before{content:"\f773"}.fa-weebly:before{content:"\f5cc"}.fa-weibo:before{content:"\f18a"}.fa-weight:before{content:"\f496"}.fa-weight-hanging:before{content:"\f5cd"}.fa-weixin:before{content:"\f1d7"}.fa-whatsapp:before{content:"\f232"}.fa-whatsapp-square:before{content:"\f40c"}.fa-wheelchair:before{content:"\f193"}.fa-whmcs:before{content:"\f40d"}.fa-wifi:before{content:"\f1eb"}.fa-wikipedia-w:before{content:"\f266"}.fa-wind:before{content:"\f72e"}.fa-window-close:before{content:"\f410"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-windows:before{content:"\f17a"}.fa-wine-bottle:before{content:"\f72f"}.fa-wine-glass:before{content:"\f4e3"}.fa-wine-glass-alt:before{content:"\f5ce"}.fa-wix:before{content:"\f5cf"}.fa-wizards-of-the-coast:before{content:"\f730"}.fa-wolf-pack-battalion:before{content:"\f514"}.fa-won-sign:before{content:"\f159"}.fa-wordpress:before{content:"\f19a"}.fa-wordpress-simple:before{content:"\f411"}.fa-wpbeginner:before{content:"\f297"}.fa-wpexplorer:before{content:"\f2de"}.fa-wpforms:before{content:"\f298"}.fa-wpressr:before{content:"\f3e4"}.fa-wrench:before{content:"\f0ad"}.fa-x-ray:before{content:"\f497"}.fa-xbox:before{content:"\f412"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-y-combinator:before{content:"\f23b"}.fa-yahoo:before{content:"\f19e"}.fa-yandex:before{content:"\f413"}.fa-yandex-international:before{content:"\f414"}.fa-yelp:before{content:"\f1e9"}.fa-yen-sign:before{content:"\f157"}.fa-yin-yang:before{content:"\f6ad"}.fa-yoast:before{content:"\f2b1"}.fa-youtube:before{content:"\f167"}.fa-youtube-square:before{content:"\f431"}.fa-zhihu:before{content:"\f63f"}.sr-only{border:0;clip:rect(0,0,0,0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.sr-only-focusable:active,.sr-only-focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}/*!* Font Awesome Free 5.5.0 by @fontawesome - https://fontawesome.com +* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)*/@font-face{font-family:'font awesome 5 free';font-style:normal;font-weight:900;src:url(../fonts/fa-solid-900.eot);src:url(../fonts/fa-solid-900.eot?#iefix)format("embedded-opentype"),url(../fonts/fa-solid-900.woff2)format("woff2"),url(../fonts/fa-solid-900.woff)format("woff"),url(../fonts/fa-solid-900.ttf)format("truetype"),url(../fonts/fa-solid-900.svg#fontawesome)format("svg")}.fa,.fas{font-family:'font awesome 5 free';font-weight:900}/*!* Font Awesome Free 5.5.0 by @fontawesome - https://fontawesome.com +* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)*/@font-face{font-family:'font awesome 5 free';font-style:normal;font-weight:400;src:url(../fonts/fa-regular-400.eot);src:url(../fonts/fa-regular-400.eot?#iefix)format("embedded-opentype"),url(../fonts/fa-regular-400.woff2)format("woff2"),url(../fonts/fa-regular-400.woff)format("woff"),url(../fonts/fa-regular-400.ttf)format("truetype"),url(../fonts/fa-regular-400.svg#fontawesome)format("svg")}.far{font-family:'font awesome 5 free';font-weight:400}/*!* Font Awesome Free 5.5.0 by @fontawesome - https://fontawesome.com +* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)*/@font-face{font-family:'font awesome 5 brands';font-style:normal;font-weight:400;src:url(../fonts/fa-brands-400.eot);src:url(../fonts/fa-brands-400.eot?#iefix)format("embedded-opentype"),url(../fonts/fa-brands-400.woff2)format("woff2"),url(../fonts/fa-brands-400.woff)format("woff"),url(../fonts/fa-brands-400.ttf)format("truetype"),url(../fonts/fa-brands-400.svg#fontawesome)format("svg")}.fab{font-family:'font awesome 5 brands'}/*!* Bootstrap v4.1.3 (https://getbootstrap.com/) +* Copyright 2011-2018 The Bootstrap Authors +* Copyright 2011-2018 Twitter, Inc. +* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)*/:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#007bff;--secondary:#6c757d;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-family-monospace:SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace}*,*::before,*::after{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:transparent}@-ms-viewport{width: device-width; }article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,sans-serif,apple color emoji,segoe ui emoji,segoe ui symbol,noto color emoji;font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[title],abbr[data-original-title]{text-decoration:underline;text-decoration:underline dotted;cursor:help;border-bottom:0}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul,dl{margin-top:0;margin-bottom:1rem}ol ol,ul ul,ol ul,ul ol{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}dfn{font-style:italic}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent;-webkit-text-decoration-skip:objects}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):hover,a:not([href]):not([tabindex]):focus{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}pre,code,kbd,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,liberation mono,courier new,monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}input,button,select,optgroup,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}button,html [type=button],[type=reset],[type=submit]{-webkit-appearance:button}button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{padding:0;border-style:none}input[type=radio],input[type=checkbox]{box-sizing:border-box;padding:0}input[type=date],input[type=time],input[type=datetime-local],input[type=month]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{margin-bottom:.5rem;font-family:inherit;font-weight:500;line-height:1.2;color:inherit}h1,.h1{font-size:2.5rem}h2,.h2{font-size:2rem}h3,.h3{font-size:1.75rem}h4,.h4{font-size:1.5rem}h5,.h5{font-size:1.25rem}h6,.h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem;font-weight:300;line-height:1.2}.display-2{font-size:5.5rem;font-weight:300;line-height:1.2}.display-3{font-size:4.5rem;font-weight:300;line-height:1.2}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}small,.small{font-size:80%;font-weight:400}mark,.mark{padding:.2em;background-color:#fcf8e3}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote-footer{display:block;font-size:80%;color:#6c757d}.blockquote-footer::before{content:"\2014 \00A0"}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#6c757d}code{font-size:87.5%;color:#e83e8c;word-break:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:#212529}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media(min-width:576px){.container{max-width:540px}}@media(min-width:768px){.container{max-width:720px}}@media(min-width:992px){.container{max-width:960px}}@media(min-width:1200px){.container{max-width:1140px}}.container-fluid{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{display:flex;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col-xl,.col-xl-auto,.col-xl-12,.col-xl-11,.col-xl-10,.col-xl-9,.col-xl-8,.col-xl-7,.col-xl-6,.col-xl-5,.col-xl-4,.col-xl-3,.col-xl-2,.col-xl-1,.col-lg,.col-lg-auto,.col-lg-12,.col-lg-11,.col-lg-10,.col-lg-9,.col-lg-8,.col-lg-7,.col-lg-6,.col-lg-5,.col-lg-4,.col-lg-3,.col-lg-2,.col-lg-1,.col-md,.col-md-auto,.col-md-12,.col-md-11,.col-md-10,.col-md-9,.col-md-8,.col-md-7,.col-md-6,.col-md-5,.col-md-4,.col-md-3,.col-md-2,.col-md-1,.col-sm,.col-sm-auto,.col-sm-12,.col-sm-11,.col-sm-10,.col-sm-9,.col-sm-8,.col-sm-7,.col-sm-6,.col-sm-5,.col-sm-4,.col-sm-3,.col-sm-2,.col-sm-1,.col,.col-auto,.col-12,.col-11,.col-10,.col-9,.col-8,.col-7,.col-6,.col-5,.col-4,.col-3,.col-2,.col-1{position:relative;width:100%;min-height:1px;padding-right:15px;padding-left:15px}.col{flex-basis:0;flex-grow:1;max-width:100%}.col-auto{flex:none;width:auto;max-width:none}.col-1{flex:0 0 8.33333333%;max-width:8.33333333%}.col-2{flex:0 0 16.66666667%;max-width:16.66666667%}.col-3{flex:0 0 25%;max-width:25%}.col-4{flex:0 0 33.33333333%;max-width:33.33333333%}.col-5{flex:0 0 41.66666667%;max-width:41.66666667%}.col-6{flex:0 0 50%;max-width:50%}.col-7{flex:0 0 58.33333333%;max-width:58.33333333%}.col-8{flex:0 0 66.66666667%;max-width:66.66666667%}.col-9{flex:0 0 75%;max-width:75%}.col-10{flex:0 0 83.33333333%;max-width:83.33333333%}.col-11{flex:0 0 91.66666667%;max-width:91.66666667%}.col-12{flex:0 0 100%;max-width:100%}.order-first{order:-1}.order-last{order:13}.order-0{order:0}.order-1{order:1}.order-2{order:2}.order-3{order:3}.order-4{order:4}.order-5{order:5}.order-6{order:6}.order-7{order:7}.order-8{order:8}.order-9{order:9}.order-10{order:10}.order-11{order:11}.order-12{order:12}.offset-1{margin-left:8.33333333%}.offset-2{margin-left:16.66666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.33333333%}.offset-5{margin-left:41.66666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.33333333%}.offset-8{margin-left:66.66666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.33333333%}.offset-11{margin-left:91.66666667%}@media(min-width:576px){.col-sm{flex-basis:0;flex-grow:1;max-width:100%}.col-sm-auto{flex:none;width:auto;max-width:none}.col-sm-1{flex:0 0 8.33333333%;max-width:8.33333333%}.col-sm-2{flex:0 0 16.66666667%;max-width:16.66666667%}.col-sm-3{flex:0 0 25%;max-width:25%}.col-sm-4{flex:0 0 33.33333333%;max-width:33.33333333%}.col-sm-5{flex:0 0 41.66666667%;max-width:41.66666667%}.col-sm-6{flex:0 0 50%;max-width:50%}.col-sm-7{flex:0 0 58.33333333%;max-width:58.33333333%}.col-sm-8{flex:0 0 66.66666667%;max-width:66.66666667%}.col-sm-9{flex:0 0 75%;max-width:75%}.col-sm-10{flex:0 0 83.33333333%;max-width:83.33333333%}.col-sm-11{flex:0 0 91.66666667%;max-width:91.66666667%}.col-sm-12{flex:0 0 100%;max-width:100%}.order-sm-first{order:-1}.order-sm-last{order:13}.order-sm-0{order:0}.order-sm-1{order:1}.order-sm-2{order:2}.order-sm-3{order:3}.order-sm-4{order:4}.order-sm-5{order:5}.order-sm-6{order:6}.order-sm-7{order:7}.order-sm-8{order:8}.order-sm-9{order:9}.order-sm-10{order:10}.order-sm-11{order:11}.order-sm-12{order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.33333333%}.offset-sm-2{margin-left:16.66666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.33333333%}.offset-sm-5{margin-left:41.66666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.33333333%}.offset-sm-8{margin-left:66.66666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.33333333%}.offset-sm-11{margin-left:91.66666667%}}@media(min-width:768px){.col-md{flex-basis:0;flex-grow:1;max-width:100%}.col-md-auto{flex:none;width:auto;max-width:none}.col-md-1{flex:0 0 8.33333333%;max-width:8.33333333%}.col-md-2{flex:0 0 16.66666667%;max-width:16.66666667%}.col-md-3{flex:0 0 25%;max-width:25%}.col-md-4{flex:0 0 33.33333333%;max-width:33.33333333%}.col-md-5{flex:0 0 41.66666667%;max-width:41.66666667%}.col-md-6{flex:0 0 50%;max-width:50%}.col-md-7{flex:0 0 58.33333333%;max-width:58.33333333%}.col-md-8{flex:0 0 66.66666667%;max-width:66.66666667%}.col-md-9{flex:0 0 75%;max-width:75%}.col-md-10{flex:0 0 83.33333333%;max-width:83.33333333%}.col-md-11{flex:0 0 91.66666667%;max-width:91.66666667%}.col-md-12{flex:0 0 100%;max-width:100%}.order-md-first{order:-1}.order-md-last{order:13}.order-md-0{order:0}.order-md-1{order:1}.order-md-2{order:2}.order-md-3{order:3}.order-md-4{order:4}.order-md-5{order:5}.order-md-6{order:6}.order-md-7{order:7}.order-md-8{order:8}.order-md-9{order:9}.order-md-10{order:10}.order-md-11{order:11}.order-md-12{order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.33333333%}.offset-md-2{margin-left:16.66666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.33333333%}.offset-md-5{margin-left:41.66666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.33333333%}.offset-md-8{margin-left:66.66666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.33333333%}.offset-md-11{margin-left:91.66666667%}}@media(min-width:992px){.col-lg{flex-basis:0;flex-grow:1;max-width:100%}.col-lg-auto{flex:none;width:auto;max-width:none}.col-lg-1{flex:0 0 8.33333333%;max-width:8.33333333%}.col-lg-2{flex:0 0 16.66666667%;max-width:16.66666667%}.col-lg-3{flex:0 0 25%;max-width:25%}.col-lg-4{flex:0 0 33.33333333%;max-width:33.33333333%}.col-lg-5{flex:0 0 41.66666667%;max-width:41.66666667%}.col-lg-6{flex:0 0 50%;max-width:50%}.col-lg-7{flex:0 0 58.33333333%;max-width:58.33333333%}.col-lg-8{flex:0 0 66.66666667%;max-width:66.66666667%}.col-lg-9{flex:0 0 75%;max-width:75%}.col-lg-10{flex:0 0 83.33333333%;max-width:83.33333333%}.col-lg-11{flex:0 0 91.66666667%;max-width:91.66666667%}.col-lg-12{flex:0 0 100%;max-width:100%}.order-lg-first{order:-1}.order-lg-last{order:13}.order-lg-0{order:0}.order-lg-1{order:1}.order-lg-2{order:2}.order-lg-3{order:3}.order-lg-4{order:4}.order-lg-5{order:5}.order-lg-6{order:6}.order-lg-7{order:7}.order-lg-8{order:8}.order-lg-9{order:9}.order-lg-10{order:10}.order-lg-11{order:11}.order-lg-12{order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.33333333%}.offset-lg-2{margin-left:16.66666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.33333333%}.offset-lg-5{margin-left:41.66666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.33333333%}.offset-lg-8{margin-left:66.66666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.33333333%}.offset-lg-11{margin-left:91.66666667%}}@media(min-width:1200px){.col-xl{flex-basis:0;flex-grow:1;max-width:100%}.col-xl-auto{flex:none;width:auto;max-width:none}.col-xl-1{flex:0 0 8.33333333%;max-width:8.33333333%}.col-xl-2{flex:0 0 16.66666667%;max-width:16.66666667%}.col-xl-3{flex:0 0 25%;max-width:25%}.col-xl-4{flex:0 0 33.33333333%;max-width:33.33333333%}.col-xl-5{flex:0 0 41.66666667%;max-width:41.66666667%}.col-xl-6{flex:0 0 50%;max-width:50%}.col-xl-7{flex:0 0 58.33333333%;max-width:58.33333333%}.col-xl-8{flex:0 0 66.66666667%;max-width:66.66666667%}.col-xl-9{flex:0 0 75%;max-width:75%}.col-xl-10{flex:0 0 83.33333333%;max-width:83.33333333%}.col-xl-11{flex:0 0 91.66666667%;max-width:91.66666667%}.col-xl-12{flex:0 0 100%;max-width:100%}.order-xl-first{order:-1}.order-xl-last{order:13}.order-xl-0{order:0}.order-xl-1{order:1}.order-xl-2{order:2}.order-xl-3{order:3}.order-xl-4{order:4}.order-xl-5{order:5}.order-xl-6{order:6}.order-xl-7{order:7}.order-xl-8{order:8}.order-xl-9{order:9}.order-xl-10{order:10}.order-xl-11{order:11}.order-xl-12{order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.33333333%}.offset-xl-2{margin-left:16.66666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.33333333%}.offset-xl-5{margin-left:41.66666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.33333333%}.offset-xl-8{margin-left:66.66666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.33333333%}.offset-xl-11{margin-left:91.66666667%}}.table{width:100%;margin-bottom:1rem;background-color:transparent}.table th,.table td{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody{border-top:2px solid #dee2e6}.table .table{background-color:#fff}.table-sm th,.table-sm td{padding:.3rem}.table-bordered{border:1px solid #dee2e6}.table-bordered th,.table-bordered td{border:1px solid #dee2e6}.table-bordered thead th,.table-bordered thead td{border-bottom-width:2px}.table-borderless th,.table-borderless td,.table-borderless thead th,.table-borderless tbody+tbody{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,5%)}.table-hover tbody tr:hover{background-color:rgba(0,0,0,.075)}.table-primary,.table-primary>th,.table-primary>td{background-color:#b8daff}.table-hover .table-primary:hover{background-color:#9fcdff}.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#9fcdff}.table-secondary,.table-secondary>th,.table-secondary>td{background-color:#d6d8db}.table-hover .table-secondary:hover{background-color:#c8cbcf}.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#c8cbcf}.table-success,.table-success>th,.table-success>td{background-color:#c3e6cb}.table-hover .table-success:hover{background-color:#b1dfbb}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#b1dfbb}.table-info,.table-info>th,.table-info>td{background-color:#bee5eb}.table-hover .table-info:hover{background-color:#abdde5}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#abdde5}.table-warning,.table-warning>th,.table-warning>td{background-color:#ffeeba}.table-hover .table-warning:hover{background-color:#ffe8a1}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#ffe8a1}.table-danger,.table-danger>th,.table-danger>td{background-color:#f5c6cb}.table-hover .table-danger:hover{background-color:#f1b0b7}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f1b0b7}.table-light,.table-light>th,.table-light>td{background-color:#fdfdfe}.table-hover .table-light:hover{background-color:#ececf6}.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ececf6}.table-dark,.table-dark>th,.table-dark>td{background-color:#c6c8ca}.table-hover .table-dark:hover{background-color:#b9bbbe}.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b9bbbe}.table-active,.table-active>th,.table-active>td{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,.075)}.table .thead-dark th{color:#fff;background-color:#212529;border-color:#32383e}.table .thead-light th{color:#495057;background-color:#e9ecef;border-color:#dee2e6}.table-dark{color:#fff;background-color:#212529}.table-dark th,.table-dark td,.table-dark thead th{border-color:#32383e}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,5%)}.table-dark.table-hover tbody tr:hover{background-color:rgba(255,255,255,.075)}@media(max-width:575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-sm>.table-bordered{border:0}}@media(max-width:767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-md>.table-bordered{border:0}}@media(max-width:991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-lg>.table-bordered{border:0}}@media(max-width:1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(2.25rem + 2px);padding:.375rem .75rem;font-size:1rem;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media screen and (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:focus{color:#495057;background-color:#fff;border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}select.form-control:focus::-ms-value{color:#495057;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding-top:.375rem;padding-bottom:.375rem;margin-bottom:0;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-sm,.form-control-plaintext.form-control-lg{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.8125rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.form-control-lg{height:calc(2.875rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}select.form-control[size],select.form-control[multiple]{height:auto}textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:flex;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label{color:#6c757d}.form-check-label{margin-bottom:0}.form-check-inline{display:inline-flex;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#28a745}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(40,167,69,.9);border-radius:.25rem}.was-validated .form-control:valid,.form-control.is-valid,.was-validated .custom-select:valid,.custom-select.is-valid{border-color:#28a745}.was-validated .form-control:valid:focus,.form-control.is-valid:focus,.was-validated .custom-select:valid:focus,.custom-select.is-valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.was-validated .form-control:valid~.valid-feedback,.was-validated .form-control:valid~.valid-tooltip,.form-control.is-valid~.valid-feedback,.form-control.is-valid~.valid-tooltip,.was-validated .custom-select:valid~.valid-feedback,.was-validated .custom-select:valid~.valid-tooltip,.custom-select.is-valid~.valid-feedback,.custom-select.is-valid~.valid-tooltip{display:block}.was-validated .form-control-file:valid~.valid-feedback,.was-validated .form-control-file:valid~.valid-tooltip,.form-control-file.is-valid~.valid-feedback,.form-control-file.is-valid~.valid-tooltip{display:block}.was-validated .form-check-input:valid~.form-check-label,.form-check-input.is-valid~.form-check-label{color:#28a745}.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip,.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip{display:block}.was-validated .custom-control-input:valid~.custom-control-label,.custom-control-input.is-valid~.custom-control-label{color:#28a745}.was-validated .custom-control-input:valid~.custom-control-label::before,.custom-control-input.is-valid~.custom-control-label::before{background-color:#71dd8a}.was-validated .custom-control-input:valid~.valid-feedback,.was-validated .custom-control-input:valid~.valid-tooltip,.custom-control-input.is-valid~.valid-feedback,.custom-control-input.is-valid~.valid-tooltip{display:block}.was-validated .custom-control-input:valid:checked~.custom-control-label::before,.custom-control-input.is-valid:checked~.custom-control-label::before{background-color:#34ce57}.was-validated .custom-control-input:valid:focus~.custom-control-label::before,.custom-control-input.is-valid:focus~.custom-control-label::before{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(40,167,69,.25)}.was-validated .custom-file-input:valid~.custom-file-label,.custom-file-input.is-valid~.custom-file-label{border-color:#28a745}.was-validated .custom-file-input:valid~.custom-file-label::after,.custom-file-input.is-valid~.custom-file-label::after{border-color:inherit}.was-validated .custom-file-input:valid~.valid-feedback,.was-validated .custom-file-input:valid~.valid-tooltip,.custom-file-input.is-valid~.valid-feedback,.custom-file-input.is-valid~.valid-tooltip{display:block}.was-validated .custom-file-input:valid:focus~.custom-file-label,.custom-file-input.is-valid:focus~.custom-file-label{box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.was-validated .form-control:invalid,.form-control.is-invalid,.was-validated .custom-select:invalid,.custom-select.is-invalid{border-color:#dc3545}.was-validated .form-control:invalid:focus,.form-control.is-invalid:focus,.was-validated .custom-select:invalid:focus,.custom-select.is-invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.was-validated .form-control:invalid~.invalid-feedback,.was-validated .form-control:invalid~.invalid-tooltip,.form-control.is-invalid~.invalid-feedback,.form-control.is-invalid~.invalid-tooltip,.was-validated .custom-select:invalid~.invalid-feedback,.was-validated .custom-select:invalid~.invalid-tooltip,.custom-select.is-invalid~.invalid-feedback,.custom-select.is-invalid~.invalid-tooltip{display:block}.was-validated .form-control-file:invalid~.invalid-feedback,.was-validated .form-control-file:invalid~.invalid-tooltip,.form-control-file.is-invalid~.invalid-feedback,.form-control-file.is-invalid~.invalid-tooltip{display:block}.was-validated .form-check-input:invalid~.form-check-label,.form-check-input.is-invalid~.form-check-label{color:#dc3545}.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip,.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip{display:block}.was-validated .custom-control-input:invalid~.custom-control-label,.custom-control-input.is-invalid~.custom-control-label{color:#dc3545}.was-validated .custom-control-input:invalid~.custom-control-label::before,.custom-control-input.is-invalid~.custom-control-label::before{background-color:#efa2a9}.was-validated .custom-control-input:invalid~.invalid-feedback,.was-validated .custom-control-input:invalid~.invalid-tooltip,.custom-control-input.is-invalid~.invalid-feedback,.custom-control-input.is-invalid~.invalid-tooltip{display:block}.was-validated .custom-control-input:invalid:checked~.custom-control-label::before,.custom-control-input.is-invalid:checked~.custom-control-label::before{background-color:#e4606d}.was-validated .custom-control-input:invalid:focus~.custom-control-label::before,.custom-control-input.is-invalid:focus~.custom-control-label::before{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(220,53,69,.25)}.was-validated .custom-file-input:invalid~.custom-file-label,.custom-file-input.is-invalid~.custom-file-label{border-color:#dc3545}.was-validated .custom-file-input:invalid~.custom-file-label::after,.custom-file-input.is-invalid~.custom-file-label::after{border-color:inherit}.was-validated .custom-file-input:invalid~.invalid-feedback,.was-validated .custom-file-input:invalid~.invalid-tooltip,.custom-file-input.is-invalid~.invalid-feedback,.custom-file-input.is-invalid~.invalid-tooltip{display:block}.was-validated .custom-file-input:invalid:focus~.custom-file-label,.custom-file-input.is-invalid:focus~.custom-file-label{box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-inline{display:flex;flex-flow:row wrap;align-items:center}.form-inline .form-check{width:100%}@media(min-width:576px){.form-inline label{display:flex;align-items:center;justify-content:center;margin-bottom:0}.form-inline .form-group{display:flex;flex:none;flex-flow:row wrap;align-items:center;margin-bottom:0}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .input-group,.form-inline .custom-select{width:auto}.form-inline .form-check{display:flex;align-items:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{align-items:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;text-align:center;white-space:nowrap;vertical-align:middle;user-select:none;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media screen and (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover,.btn:focus{text-decoration:none}.btn:focus,.btn.focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.btn.disabled,.btn:disabled{opacity:.65}.btn:not(:disabled):not(.disabled){cursor:pointer}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:hover{color:#fff;background-color:#0069d9;border-color:#0062cc}.btn-primary:focus,.btn-primary.focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:not(:disabled):not(.disabled):active,.btn-primary:not(:disabled):not(.disabled).active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0062cc;border-color:#005cbf}.btn-primary:not(:disabled):not(.disabled):active:focus,.btn-primary:not(:disabled):not(.disabled).active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5a6268;border-color:#545b62}.btn-secondary:focus,.btn-secondary.focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:not(:disabled):not(.disabled):active,.btn-secondary:not(:disabled):not(.disabled).active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#545b62;border-color:#4e555b}.btn-secondary:not(:disabled):not(.disabled):active:focus,.btn-secondary:not(:disabled):not(.disabled).active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-success{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:hover{color:#fff;background-color:#218838;border-color:#1e7e34}.btn-success:focus,.btn-success.focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:not(:disabled):not(.disabled):active,.btn-success:not(:disabled):not(.disabled).active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#1e7e34;border-color:#1c7430}.btn-success:not(:disabled):not(.disabled):active:focus,.btn-success:not(:disabled):not(.disabled).active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-info{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:hover{color:#fff;background-color:#138496;border-color:#117a8b}.btn-info:focus,.btn-info.focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:not(:disabled):not(.disabled):active,.btn-info:not(:disabled):not(.disabled).active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#117a8b;border-color:#10707f}.btn-info:not(:disabled):not(.disabled):active:focus,.btn-info:not(:disabled):not(.disabled).active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-warning{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#212529;background-color:#e0a800;border-color:#d39e00}.btn-warning:focus,.btn-warning.focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:not(:disabled):not(.disabled):active,.btn-warning:not(:disabled):not(.disabled).active,.show>.btn-warning.dropdown-toggle{color:#212529;background-color:#d39e00;border-color:#c69500}.btn-warning:not(:disabled):not(.disabled):active:focus,.btn-warning:not(:disabled):not(.disabled).active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#c82333;border-color:#bd2130}.btn-danger:focus,.btn-danger.focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:not(:disabled):not(.disabled):active,.btn-danger:not(:disabled):not(.disabled).active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#bd2130;border-color:#b21f2d}.btn-danger:not(:disabled):not(.disabled):active:focus,.btn-danger:not(:disabled):not(.disabled).active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-light{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#212529;background-color:#e2e6ea;border-color:#dae0e5}.btn-light:focus,.btn-light.focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-light.disabled,.btn-light:disabled{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:not(:disabled):not(.disabled):active,.btn-light:not(:disabled):not(.disabled).active,.show>.btn-light.dropdown-toggle{color:#212529;background-color:#dae0e5;border-color:#d3d9df}.btn-light:not(:disabled):not(.disabled):active:focus,.btn-light:not(:disabled):not(.disabled).active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-dark:focus,.btn-dark.focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:not(:disabled):not(.disabled):active,.btn-dark:not(:disabled):not(.disabled).active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-dark:not(:disabled):not(.disabled):active:focus,.btn-dark:not(:disabled):not(.disabled).active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-primary{color:#007bff;background-color:transparent;background-image:none;border-color:#007bff}.btn-outline-primary:hover{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary:focus,.btn-outline-primary.focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#007bff;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled):active,.btn-outline-primary:not(:disabled):not(.disabled).active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-secondary{color:#6c757d;background-color:transparent;background-image:none;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary:focus,.btn-outline-secondary.focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled):active,.btn-outline-secondary:not(:disabled):not(.disabled).active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-success{color:#28a745;background-color:transparent;background-image:none;border-color:#28a745}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success:focus,.btn-outline-success.focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled):active,.btn-outline-success:not(:disabled):not(.disabled).active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success:not(:disabled):not(.disabled):active:focus,.btn-outline-success:not(:disabled):not(.disabled).active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-info{color:#17a2b8;background-color:transparent;background-image:none;border-color:#17a2b8}.btn-outline-info:hover{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info:focus,.btn-outline-info.focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#17a2b8;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled):active,.btn-outline-info:not(:disabled):not(.disabled).active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info:not(:disabled):not(.disabled):active:focus,.btn-outline-info:not(:disabled):not(.disabled).active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-warning{color:#ffc107;background-color:transparent;background-image:none;border-color:#ffc107}.btn-outline-warning:hover{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning:focus,.btn-outline-warning.focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled):active,.btn-outline-warning:not(:disabled):not(.disabled).active,.show>.btn-outline-warning.dropdown-toggle{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-danger{color:#dc3545;background-color:transparent;background-image:none;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger:focus,.btn-outline-danger.focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled):active,.btn-outline-danger:not(:disabled):not(.disabled).active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-light{color:#f8f9fa;background-color:transparent;background-image:none;border-color:#f8f9fa}.btn-outline-light:hover{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:focus,.btn-outline-light.focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled):active,.btn-outline-light:not(:disabled):not(.disabled).active,.show>.btn-outline-light.dropdown-toggle{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:not(:disabled):not(.disabled):active:focus,.btn-outline-light:not(:disabled):not(.disabled).active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-dark{color:#343a40;background-color:transparent;background-image:none;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark:focus,.btn-outline-dark.focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled):active,.btn-outline-dark:not(:disabled):not(.disabled).active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-link{font-weight:400;color:#007bff;background-color:transparent}.btn-link:hover{color:#0056b3;text-decoration:underline;background-color:transparent;border-color:transparent}.btn-link:focus,.btn-link.focus{text-decoration:underline;border-color:transparent;box-shadow:none}.btn-link:disabled,.btn-link.disabled{color:#6c757d;pointer-events:none}.btn-lg,.btn-group-lg>.btn{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.btn-sm,.btn-group-sm>.btn{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=submit].btn-block,input[type=reset].btn-block,input[type=button].btn-block{width:100%}.fade{transition:opacity .15s linear}@media screen and (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;transition:height .35s ease}@media screen and (prefers-reduced-motion:reduce){.collapsing{transition:none}}.dropup,.dropright,.dropdown,.dropleft{position:relative}.dropdown-toggle::after{display:inline-block;width:0;height:0;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu-right{right:0;left:auto}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;width:0;height:0;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle::after{display:inline-block;width:0;height:0;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-toggle::after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle::after{display:inline-block;width:0;height:0;margin-left:.255em;vertical-align:.255em;content:""}.dropleft .dropdown-toggle::after{display:none}.dropleft .dropdown-toggle::before{display:inline-block;width:0;height:0;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty::after{margin-left:0}.dropleft .dropdown-toggle::before{vertical-align:0}.dropdown-menu[x-placement^=top],.dropdown-menu[x-placement^=right],.dropdown-menu[x-placement^=bottom],.dropdown-menu[x-placement^=left]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #e9ecef}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:hover,.dropdown-item:focus{color:#16181b;text-decoration:none;background-color:#f8f9fa}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#007bff}.dropdown-item.disabled,.dropdown-item:disabled{color:#6c757d;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1.5rem;color:#212529}.btn-group,.btn-group-vertical{position:relative;display:inline-flex;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;flex:initial}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover{z-index:1}.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn.active{z-index:1}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group,.btn-group-vertical .btn+.btn,.btn-group-vertical .btn+.btn-group,.btn-group-vertical .btn-group+.btn,.btn-group-vertical .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{display:flex;flex-wrap:wrap;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:not(:last-child):not(.dropdown-toggle),.btn-group>.btn-group:not(:last-child)>.btn{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:not(:first-child),.btn-group>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after,.dropright .dropdown-toggle-split::after{margin-left:0}.dropleft .dropdown-toggle-split::before{margin-right:0}.btn-sm+.dropdown-toggle-split,.btn-group-sm>.btn+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-lg+.dropdown-toggle-split,.btn-group-lg>.btn+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{flex-direction:column;align-items:flex-start;justify-content:center}.btn-group-vertical .btn,.btn-group-vertical .btn-group{width:100%}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle),.btn-group-vertical>.btn-group:not(:last-child)>.btn{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:not(:first-child),.btn-group-vertical>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn input[type=radio],.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio],.btn-group-toggle>.btn-group>.btn input[type=checkbox]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:flex;flex-wrap:wrap;align-items:stretch;width:100%}.input-group>.form-control,.input-group>.custom-select,.input-group>.custom-file{position:relative;flex:auto;width:1%;margin-bottom:0}.input-group>.form-control+.form-control,.input-group>.form-control+.custom-select,.input-group>.form-control+.custom-file,.input-group>.custom-select+.form-control,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.custom-file,.input-group>.custom-file+.form-control,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.custom-file{margin-left:-1px}.input-group>.form-control:focus,.input-group>.custom-select:focus,.input-group>.custom-file .custom-file-input:focus~.custom-file-label{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.form-control:not(:last-child),.input-group>.custom-select:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.form-control:not(:first-child),.input-group>.custom-select:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:flex;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label::after{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-prepend,.input-group-append{display:flex}.input-group-prepend .btn,.input-group-append .btn{position:relative;z-index:2}.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.input-group-text,.input-group-append .input-group-text+.btn{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:flex;align-items:center;padding:.375rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-text input[type=radio],.input-group-text input[type=checkbox]{margin-top:0}.input-group-lg>.form-control,.input-group-lg>.input-group-prepend>.input-group-text,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-append>.btn{height:calc(2.875rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.input-group-sm>.form-control,.input-group-sm>.input-group-prepend>.input-group-text,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-append>.btn{height:calc(1.8125rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text,.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;display:block;min-height:1.5rem;padding-left:1.5rem}.custom-control-inline{display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;z-index:-1;opacity:0}.custom-control-input:checked~.custom-control-label::before{color:#fff;background-color:#007bff}.custom-control-input:focus~.custom-control-label::before{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-control-input:active~.custom-control-label::before{color:#fff;background-color:#b3d7ff}.custom-control-input:disabled~.custom-control-label{color:#6c757d}.custom-control-input:disabled~.custom-control-label::before{background-color:#e9ecef}.custom-control-label{position:relative;margin-bottom:0}.custom-control-label::before{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;pointer-events:none;content:"";user-select:none;background-color:#dee2e6}.custom-control-label::after{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:"";background-repeat:no-repeat;background-position:50%;background-size:50% 50%}.custom-checkbox .custom-control-label::before{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-label::before{background-color:#007bff}.custom-checkbox .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::before{background-color:#007bff}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::after{background-image:url(data:image/svg+xml;charset=utf8;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0IDQiPjxwYXRoIHN0cm9rZT0iI2ZmZiIgZD0iTTAgMmg0Ii8+PC9zdmc+)}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-radio .custom-control-label::before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label::before{background-color:#007bff}.custom-radio .custom-control-input:checked~.custom-control-label::after{background-image:url(data:image/svg+xml;charset=utf8;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9Ii00IC00IDggOCI+PGNpcmNsZSByPSIzIiBmaWxsPSIjZmZmIi8+PC9zdmc+)}.custom-radio .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-select{display:inline-block;width:100%;height:calc(2.25rem + 2px);padding:.375rem 1.75rem .375rem .75rem;line-height:1.5;color:#495057;vertical-align:middle;background:#fff url(data:image/svg+xml;charset=utf8;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0IDUiPjxwYXRoIGZpbGw9IiMzNDNhNDAiIGQ9Ik0yIDAgMCAyaDR6bTAgNUwwIDNoNHoiLz48L3N2Zz4=)no-repeat right .75rem center;background-size:8px 10px;border:1px solid #ced4da;border-radius:.25rem;appearance:none}.custom-select:focus{border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(128,189,255,.5)}.custom-select:focus::-ms-value{color:#495057;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#6c757d;background-color:#e9ecef}.custom-select::-ms-expand{opacity:0}.custom-select-sm{height:calc(1.8125rem + 2px);padding-top:.375rem;padding-bottom:.375rem;font-size:75%}.custom-select-lg{height:calc(2.875rem + 2px);padding-top:.375rem;padding-bottom:.375rem;font-size:125%}.custom-file{position:relative;display:inline-block;width:100%;height:calc(2.25rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(2.25rem + 2px);margin:0;opacity:0}.custom-file-input:focus~.custom-file-label{border-color:#80bdff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-file-input:focus~.custom-file-label::after{border-color:#80bdff}.custom-file-input:disabled~.custom-file-label{background-color:#e9ecef}.custom-file-input:lang(en)~.custom-file-label::after{content:"Browse"}.custom-file-label{position:absolute;top:0;right:0;left:0;z-index:1;height:calc(2.25rem + 2px);padding:.375rem .75rem;line-height:1.5;color:#495057;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label::after{position:absolute;top:0;right:0;bottom:0;z-index:3;display:block;height:2.25rem;padding:.375rem .75rem;line-height:1.5;color:#495057;content:"Browse";background-color:#e9ecef;border-left:1px solid #ced4da;border-radius:0 .25rem .25rem 0}.custom-range{width:100%;padding-left:0;background-color:transparent;appearance:none}.custom-range:focus{outline:none}.custom-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media screen and (prefers-reduced-motion:reduce){.custom-range::-webkit-slider-thumb{transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#b3d7ff}.custom-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media screen and (prefers-reduced-motion:reduce){.custom-range::-moz-range-thumb{transition:none}}.custom-range::-moz-range-thumb:active{background-color:#b3d7ff}.custom-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:.2rem;margin-left:.2rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media screen and (prefers-reduced-motion:reduce){.custom-range::-ms-thumb{transition:none}}.custom-range::-ms-thumb:active{background-color:#b3d7ff}.custom-range::-ms-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:.5rem}.custom-range::-ms-fill-lower{background-color:#dee2e6;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px;background-color:#dee2e6;border-radius:1rem}.custom-control-label::before,.custom-file-label,.custom-select{transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media screen and (prefers-reduced-motion:reduce){.custom-control-label::before,.custom-file-label,.custom-select{transition:none}}.nav{display:flex;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:hover,.nav-link:focus{text-decoration:none}.nav-link.disabled{color:#6c757d}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:hover,.nav-tabs .nav-link:focus{border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-link.active,.nav-tabs .nav-item.show .nav-link{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#007bff}.nav-fill .nav-item{flex:auto;text-align:center}.nav-justified .nav-item{flex-basis:0;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;padding:.5rem 1rem}.navbar>.container,.navbar>.container-fluid{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;line-height:inherit;white-space:nowrap}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}.navbar-nav{display:flex;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{flex-basis:100%;flex-grow:1;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:hover,.navbar-toggler:focus{text-decoration:none}.navbar-toggler:not(:disabled):not(.disabled){cursor:pointer}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:no-repeat 50%;background-size:100% 100%}@media(max-width:575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{padding-right:0;padding-left:0}}@media(min-width:576px){.navbar-expand-sm{flex-flow:row nowrap;justify-content:flex-start}.navbar-expand-sm .navbar-nav{flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media(max-width:767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{padding-right:0;padding-left:0}}@media(min-width:768px){.navbar-expand-md{flex-flow:row nowrap;justify-content:flex-start}.navbar-expand-md .navbar-nav{flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media(max-width:991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{padding-right:0;padding-left:0}}@media(min-width:992px){.navbar-expand-lg{flex-flow:row nowrap;justify-content:flex-start}.navbar-expand-lg .navbar-nav{flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media(max-width:1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{padding-right:0;padding-left:0}}@media(min-width:1200px){.navbar-expand-xl{flex-flow:row nowrap;justify-content:flex-start}.navbar-expand-xl .navbar-nav{flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{flex-flow:row nowrap;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid{flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:hover,.navbar-light .navbar-brand:focus{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:hover,.navbar-light .navbar-nav .nav-link:focus{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .show>.nav-link,.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .nav-link.active{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a{color:rgba(0,0,0,.9)}.navbar-light .navbar-text a:hover,.navbar-light .navbar-text a:focus{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:hover,.navbar-dark .navbar-brand:focus{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.5)}.navbar-dark .navbar-nav .nav-link:hover,.navbar-dark .navbar-nav .nav-link:focus{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .show>.nav-link,.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .nav-link.active{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.5);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E")}.navbar-dark .navbar-text{color:rgba(255,255,255,.5)}.navbar-dark .navbar-text a{color:#fff}.navbar-dark .navbar-text a:hover,.navbar-dark .navbar-text a:focus{color:#fff}.card,main pre{position:relative;display:flex;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr,main pre>hr{margin-right:0;margin-left:0}.card>.list-group:first-child .list-group-item:first-child,main pre>.list-group:first-child .list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card>.list-group:last-child .list-group-item:last-child,main pre>.list-group:last-child .list-group-item:last-child{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.card-body{flex:auto;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:.75rem 1.25rem;margin-bottom:0;background-color:rgba(0,0,0,3%);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px)calc(.25rem - 1px)0 0}.card-header+.list-group .list-group-item:first-child{border-top:0}.card-footer{padding:.75rem 1.25rem;background-color:rgba(0,0,0,3%);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px)calc(.25rem - 1px)}.card-header-tabs{margin-right:-.625rem;margin-bottom:-.75rem;margin-left:-.625rem;border-bottom:0}.card-header-pills{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem}.card-img{width:100%;border-radius:calc(.25rem - 1px)}.card-img-top{width:100%;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img-bottom{width:100%;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-deck{display:flex;flex-direction:column}.card-deck .card,.card-deck main pre,main .card-deck pre{margin-bottom:15px}@media(min-width:576px){.card-deck{flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card,.card-deck main pre,main .card-deck pre{display:flex;flex:1 0;flex-direction:column;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group{display:flex;flex-direction:column}.card-group>.card,main .card-group>pre{margin-bottom:15px}@media(min-width:576px){.card-group{flex-flow:row wrap}.card-group>.card,main .card-group>pre{flex:1 0;margin-bottom:0}.card-group>.card+.card,main .card-group>pre+.card,main .card-group>.card+pre,main .card-group>pre+pre{margin-left:0;border-left:0}.card-group>.card:first-child,main .card-group>pre:first-child{border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:first-child .card-img-top,main .card-group>pre:first-child .card-img-top,.card-group>.card:first-child .card-header,main .card-group>pre:first-child .card-header{border-top-right-radius:0}.card-group>.card:first-child .card-img-bottom,main .card-group>pre:first-child .card-img-bottom,.card-group>.card:first-child .card-footer,main .card-group>pre:first-child .card-footer{border-bottom-right-radius:0}.card-group>.card:last-child,main .card-group>pre:last-child{border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:last-child .card-img-top,main .card-group>pre:last-child .card-img-top,.card-group>.card:last-child .card-header,main .card-group>pre:last-child .card-header{border-top-left-radius:0}.card-group>.card:last-child .card-img-bottom,main .card-group>pre:last-child .card-img-bottom,.card-group>.card:last-child .card-footer,main .card-group>pre:last-child .card-footer{border-bottom-left-radius:0}.card-group>.card:only-child,main .card-group>pre:only-child{border-radius:.25rem}.card-group>.card:only-child .card-img-top,main .card-group>pre:only-child .card-img-top,.card-group>.card:only-child .card-header,main .card-group>pre:only-child .card-header{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card-group>.card:only-child .card-img-bottom,main .card-group>pre:only-child .card-img-bottom,.card-group>.card:only-child .card-footer,main .card-group>pre:only-child .card-footer{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.card-group>.card:not(:first-child):not(:last-child):not(:only-child),main .card-group>pre:not(:first-child):not(:last-child):not(:only-child){border-radius:0}.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-img-top,main .card-group>pre:not(:first-child):not(:last-child):not(:only-child) .card-img-top,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-img-bottom,main .card-group>pre:not(:first-child):not(:last-child):not(:only-child) .card-img-bottom,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-header,main .card-group>pre:not(:first-child):not(:last-child):not(:only-child) .card-header,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-footer,main .card-group>pre:not(:first-child):not(:last-child):not(:only-child) .card-footer{border-radius:0}}.card-columns .card,.card-columns main pre,main .card-columns pre{margin-bottom:.75rem}@media(min-width:576px){.card-columns{column-count:3;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card,.card-columns main pre,main .card-columns pre{display:inline-block;width:100%}}.accordion .card:not(:first-of-type):not(:last-of-type),.accordion main pre:not(:first-of-type):not(:last-of-type),main .accordion pre:not(:first-of-type):not(:last-of-type){border-bottom:0;border-radius:0}.accordion .card:not(:first-of-type) .card-header:first-child,.accordion main pre:not(:first-of-type) .card-header:first-child,main .accordion pre:not(:first-of-type) .card-header:first-child{border-radius:0}.accordion .card:first-of-type,.accordion main pre:first-of-type,main .accordion pre:first-of-type{border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion .card:last-of-type,.accordion main pre:last-of-type,main .accordion pre:last-of-type{border-top-left-radius:0;border-top-right-radius:0}.breadcrumb{display:flex;flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{display:inline-block;padding-right:.5rem;color:#6c757d;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#6c757d}.pagination{display:flex;padding-left:0;list-style:none;border-radius:.25rem}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#007bff;background-color:#fff;border:1px solid #dee2e6}.page-link:hover{z-index:2;color:#0056b3;text-decoration:none;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:2;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.page-link:not(:disabled):not(.disabled){cursor:pointer}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.page-item.active .page-link{z-index:1;color:#fff;background-color:#007bff;border-color:#007bff}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;cursor:auto;background-color:#fff;border-color:#dee2e6}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#007bff}.badge-primary[href]:hover,.badge-primary[href]:focus{color:#fff;text-decoration:none;background-color:#0062cc}.badge-secondary{color:#fff;background-color:#6c757d}.badge-secondary[href]:hover,.badge-secondary[href]:focus{color:#fff;text-decoration:none;background-color:#545b62}.badge-success{color:#fff;background-color:#28a745}.badge-success[href]:hover,.badge-success[href]:focus{color:#fff;text-decoration:none;background-color:#1e7e34}.badge-info{color:#fff;background-color:#17a2b8}.badge-info[href]:hover,.badge-info[href]:focus{color:#fff;text-decoration:none;background-color:#117a8b}.badge-warning{color:#212529;background-color:#ffc107}.badge-warning[href]:hover,.badge-warning[href]:focus{color:#212529;text-decoration:none;background-color:#d39e00}.badge-danger{color:#fff;background-color:#dc3545}.badge-danger[href]:hover,.badge-danger[href]:focus{color:#fff;text-decoration:none;background-color:#bd2130}.badge-light{color:#212529;background-color:#f8f9fa}.badge-light[href]:hover,.badge-light[href]:focus{color:#212529;text-decoration:none;background-color:#dae0e5}.badge-dark{color:#fff;background-color:#343a40}.badge-dark[href]:hover,.badge-dark[href]:focus{color:#fff;text-decoration:none;background-color:#1d2124}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:.3rem}@media(min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:4rem}.alert-dismissible .close{position:absolute;top:0;right:0;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#004085;background-color:#cce5ff;border-color:#b8daff}.alert-primary hr{border-top-color:#9fcdff}.alert-primary .alert-link{color:#002752}.alert-secondary{color:#383d41;background-color:#e2e3e5;border-color:#d6d8db}.alert-secondary hr{border-top-color:#c8cbcf}.alert-secondary .alert-link{color:#202326}.alert-success{color:#155724;background-color:#d4edda;border-color:#c3e6cb}.alert-success hr{border-top-color:#b1dfbb}.alert-success .alert-link{color:#0b2e13}.alert-info{color:#0c5460;background-color:#d1ecf1;border-color:#bee5eb}.alert-info hr{border-top-color:#abdde5}.alert-info .alert-link{color:#062c33}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.alert-warning hr{border-top-color:#ffe8a1}.alert-warning .alert-link{color:#533f03}.alert-danger{color:#721c24;background-color:#f8d7da;border-color:#f5c6cb}.alert-danger hr{border-top-color:#f1b0b7}.alert-danger .alert-link{color:#491217}.alert-light{color:#818182;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686868}.alert-dark{color:#1b1e21;background-color:#d6d8d9;border-color:#c6c8ca}.alert-dark hr{border-top-color:#b9bbbe}.alert-dark .alert-link{color:#040505}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{display:flex;height:1rem;overflow:hidden;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:flex;flex-direction:column;justify-content:center;color:#fff;text-align:center;white-space:nowrap;background-color:#007bff;transition:width .6s ease}@media screen and (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{animation:progress-bar-stripes 1s linear infinite}.media{display:flex;align-items:flex-start}.media-body{flex:1}.list-group{display:flex;flex-direction:column;padding-left:0;margin-bottom:0}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:hover,.list-group-item-action:focus{color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;margin-bottom:-1px;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.list-group-item:hover,.list-group-item:focus{z-index:1;text-decoration:none}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#007bff;border-color:#007bff}.list-group-flush .list-group-item{border-right:0;border-left:0;border-radius:0}.list-group-flush:first-child .list-group-item:first-child{border-top:0}.list-group-flush:last-child .list-group-item:last-child{border-bottom:0}.list-group-item-primary{color:#004085;background-color:#b8daff}.list-group-item-primary.list-group-item-action:hover,.list-group-item-primary.list-group-item-action:focus{color:#004085;background-color:#9fcdff}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#004085;border-color:#004085}.list-group-item-secondary{color:#383d41;background-color:#d6d8db}.list-group-item-secondary.list-group-item-action:hover,.list-group-item-secondary.list-group-item-action:focus{color:#383d41;background-color:#c8cbcf}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#383d41;border-color:#383d41}.list-group-item-success{color:#155724;background-color:#c3e6cb}.list-group-item-success.list-group-item-action:hover,.list-group-item-success.list-group-item-action:focus{color:#155724;background-color:#b1dfbb}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#155724;border-color:#155724}.list-group-item-info{color:#0c5460;background-color:#bee5eb}.list-group-item-info.list-group-item-action:hover,.list-group-item-info.list-group-item-action:focus{color:#0c5460;background-color:#abdde5}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#0c5460;border-color:#0c5460}.list-group-item-warning{color:#856404;background-color:#ffeeba}.list-group-item-warning.list-group-item-action:hover,.list-group-item-warning.list-group-item-action:focus{color:#856404;background-color:#ffe8a1}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#856404;border-color:#856404}.list-group-item-danger{color:#721c24;background-color:#f5c6cb}.list-group-item-danger.list-group-item-action:hover,.list-group-item-danger.list-group-item-action:focus{color:#721c24;background-color:#f1b0b7}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#721c24;border-color:#721c24}.list-group-item-light{color:#818182;background-color:#fdfdfe}.list-group-item-light.list-group-item-action:hover,.list-group-item-light.list-group-item-action:focus{color:#818182;background-color:#ececf6}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#818182;border-color:#818182}.list-group-item-dark{color:#1b1e21;background-color:#c6c8ca}.list-group-item-dark.list-group-item-action:hover,.list-group-item-dark.list-group-item-action:focus{color:#1b1e21;background-color:#b9bbbe}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1b1e21;border-color:#1b1e21}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:not(:disabled):not(.disabled){cursor:pointer}.close:not(:disabled):not(.disabled):hover,.close:not(:disabled):not(.disabled):focus{color:#000;text-decoration:none;opacity:.75}button.close{padding:0;background-color:transparent;border:0;-webkit-appearance:none}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:transform .3s ease-out;transform:translate(0,-25%)}@media screen and (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{transform:translate(0,0)}.modal-dialog-centered{display:flex;align-items:center;min-height:calc(100% - (.5rem * 2))}.modal-dialog-centered::before{display:block;height:calc(100vh - (.5rem * 2));content:""}.modal-content{position:relative;display:flex;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:flex;align-items:flex-start;justify-content:space-between;padding:1rem;border-bottom:1px solid #e9ecef;border-top-left-radius:.3rem;border-top-right-radius:.3rem}.modal-header .close{padding:1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;flex:auto;padding:1rem}.modal-footer{display:flex;align-items:center;justify-content:flex-end;padding:1rem;border-top:1px solid #e9ecef}.modal-footer>:not(:first-child){margin-left:.25rem}.modal-footer>:not(:last-child){margin-right:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media(min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-centered{min-height:calc(100% - (1.75rem * 2))}.modal-dialog-centered::before{height:calc(100vh - (1.75rem * 2))}.modal-sm{max-width:300px}}@media(min-width:992px){.modal-lg{max-width:800px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,sans-serif,apple color emoji,segoe ui emoji,segoe ui symbol,noto color emoji;font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-top,.bs-tooltip-auto[x-placement^=top]{padding:.4rem 0}.bs-tooltip-top .arrow,.bs-tooltip-auto[x-placement^=top] .arrow{bottom:0}.bs-tooltip-top .arrow::before,.bs-tooltip-auto[x-placement^=top] .arrow::before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-right,.bs-tooltip-auto[x-placement^=right]{padding:0 .4rem}.bs-tooltip-right .arrow,.bs-tooltip-auto[x-placement^=right] .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-right .arrow::before,.bs-tooltip-auto[x-placement^=right] .arrow::before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-bottom,.bs-tooltip-auto[x-placement^=bottom]{padding:.4rem 0}.bs-tooltip-bottom .arrow,.bs-tooltip-auto[x-placement^=bottom] .arrow{top:0}.bs-tooltip-bottom .arrow::before,.bs-tooltip-auto[x-placement^=bottom] .arrow::before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-left,.bs-tooltip-auto[x-placement^=left]{padding:0 .4rem}.bs-tooltip-left .arrow,.bs-tooltip-auto[x-placement^=left] .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-left .arrow::before,.bs-tooltip-auto[x-placement^=left] .arrow::before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1060;display:block;max-width:276px;font-family:-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,sans-serif,apple color emoji,segoe ui emoji,segoe ui symbol,noto color emoji;font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .arrow{position:absolute;display:block;width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow::before,.popover .arrow::after{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-top,.bs-popover-auto[x-placement^=top]{margin-bottom:.5rem}.bs-popover-top .arrow,.bs-popover-auto[x-placement^=top] .arrow{bottom:calc((.5rem + 1px) * -1)}.bs-popover-top .arrow::before,.bs-popover-auto[x-placement^=top] .arrow::before,.bs-popover-top .arrow::after,.bs-popover-auto[x-placement^=top] .arrow::after{border-width:.5rem .5rem 0}.bs-popover-top .arrow::before,.bs-popover-auto[x-placement^=top] .arrow::before{bottom:0;border-top-color:rgba(0,0,0,.25)}.bs-popover-top .arrow::after,.bs-popover-auto[x-placement^=top] .arrow::after{bottom:1px;border-top-color:#fff}.bs-popover-right,.bs-popover-auto[x-placement^=right]{margin-left:.5rem}.bs-popover-right .arrow,.bs-popover-auto[x-placement^=right] .arrow{left:calc((.5rem + 1px) * -1);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-right .arrow::before,.bs-popover-auto[x-placement^=right] .arrow::before,.bs-popover-right .arrow::after,.bs-popover-auto[x-placement^=right] .arrow::after{border-width:.5rem .5rem .5rem 0}.bs-popover-right .arrow::before,.bs-popover-auto[x-placement^=right] .arrow::before{left:0;border-right-color:rgba(0,0,0,.25)}.bs-popover-right .arrow::after,.bs-popover-auto[x-placement^=right] .arrow::after{left:1px;border-right-color:#fff}.bs-popover-bottom,.bs-popover-auto[x-placement^=bottom]{margin-top:.5rem}.bs-popover-bottom .arrow,.bs-popover-auto[x-placement^=bottom] .arrow{top:calc((.5rem + 1px) * -1)}.bs-popover-bottom .arrow::before,.bs-popover-auto[x-placement^=bottom] .arrow::before,.bs-popover-bottom .arrow::after,.bs-popover-auto[x-placement^=bottom] .arrow::after{border-width:0 .5rem .5rem}.bs-popover-bottom .arrow::before,.bs-popover-auto[x-placement^=bottom] .arrow::before{top:0;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-bottom .arrow::after,.bs-popover-auto[x-placement^=bottom] .arrow::after{top:1px;border-bottom-color:#fff}.bs-popover-bottom .popover-header::before,.bs-popover-auto[x-placement^=bottom] .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-left,.bs-popover-auto[x-placement^=left]{margin-right:.5rem}.bs-popover-left .arrow,.bs-popover-auto[x-placement^=left] .arrow{right:calc((.5rem + 1px) * -1);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-left .arrow::before,.bs-popover-auto[x-placement^=left] .arrow::before,.bs-popover-left .arrow::after,.bs-popover-auto[x-placement^=left] .arrow::after{border-width:.5rem 0 .5rem .5rem}.bs-popover-left .arrow::before,.bs-popover-auto[x-placement^=left] .arrow::before{right:0;border-left-color:rgba(0,0,0,.25)}.bs-popover-left .arrow::after,.bs-popover-auto[x-placement^=left] .arrow::after{right:1px;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;color:inherit;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .75rem;color:#212529}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-item{position:relative;display:none;align-items:center;width:100%;backface-visibility:hidden;perspective:1000px}.carousel-item.active,.carousel-item-next,.carousel-item-prev{display:block;transition:transform .6s ease}@media screen and (prefers-reduced-motion:reduce){.carousel-item.active,.carousel-item-next,.carousel-item-prev{transition:none}}.carousel-item-next,.carousel-item-prev{position:absolute;top:0}.carousel-item-next.carousel-item-left,.carousel-item-prev.carousel-item-right{transform:translateX(0)}@supports(transform-style:preserve-3d){.carousel-item-next.carousel-item-left,.carousel-item-prev.carousel-item-right{transform:translate3d(0,0,0)}}.carousel-item-next,.active.carousel-item-right{transform:translateX(100%)}@supports(transform-style:preserve-3d){.carousel-item-next,.active.carousel-item-right{transform:translate3d(100%,0,0)}}.carousel-item-prev,.active.carousel-item-left{transform:translateX(-100%)}@supports(transform-style:preserve-3d){.carousel-item-prev,.active.carousel-item-left{transform:translate3d(-100%,0,0)}}.carousel-fade .carousel-item{opacity:0;transition-duration:.6s;transition-property:opacity}.carousel-fade .carousel-item.active,.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right{opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{opacity:0}.carousel-fade .carousel-item-next,.carousel-fade .carousel-item-prev,.carousel-fade .carousel-item.active,.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-prev{transform:translateX(0)}@supports(transform-style:preserve-3d){.carousel-fade .carousel-item-next,.carousel-fade .carousel-item-prev,.carousel-fade .carousel-item.active,.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-prev{transform:translate3d(0,0,0)}}.carousel-control-prev,.carousel-control-next{position:absolute;top:0;bottom:0;display:flex;align-items:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:.5}.carousel-control-prev:hover,.carousel-control-prev:focus,.carousel-control-next:hover,.carousel-control-next:focus{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-prev-icon,.carousel-control-next-icon{display:inline-block;width:20px;height:20px;background:no-repeat 50%;background-size:100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3E%3C/svg%3E")}.carousel-control-next-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3E%3C/svg%3E")}.carousel-indicators{position:absolute;right:0;bottom:10px;left:0;z-index:15;display:flex;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{position:relative;flex:initial;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:rgba(255,255,255,.5)}.carousel-indicators li::before{position:absolute;top:-10px;left:0;display:inline-block;width:100%;height:10px;content:""}.carousel-indicators li::after{position:absolute;bottom:-10px;left:0;display:inline-block;width:100%;height:10px;content:""}.carousel-indicators .active{background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#007bff!important}a.bg-primary:hover,a.bg-primary:focus,button.bg-primary:hover,button.bg-primary:focus{background-color:#0062cc!important}.bg-secondary{background-color:#6c757d!important}a.bg-secondary:hover,a.bg-secondary:focus,button.bg-secondary:hover,button.bg-secondary:focus{background-color:#545b62!important}.bg-success{background-color:#28a745!important}a.bg-success:hover,a.bg-success:focus,button.bg-success:hover,button.bg-success:focus{background-color:#1e7e34!important}.bg-info{background-color:#17a2b8!important}a.bg-info:hover,a.bg-info:focus,button.bg-info:hover,button.bg-info:focus{background-color:#117a8b!important}.bg-warning{background-color:#ffc107!important}a.bg-warning:hover,a.bg-warning:focus,button.bg-warning:hover,button.bg-warning:focus{background-color:#d39e00!important}.bg-danger{background-color:#dc3545!important}a.bg-danger:hover,a.bg-danger:focus,button.bg-danger:hover,button.bg-danger:focus{background-color:#bd2130!important}.bg-light{background-color:#f8f9fa!important}a.bg-light:hover,a.bg-light:focus,button.bg-light:hover,button.bg-light:focus{background-color:#dae0e5!important}.bg-dark{background-color:#343a40!important}a.bg-dark:hover,a.bg-dark:focus,button.bg-dark:hover,button.bg-dark:focus{background-color:#1d2124!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.border{border:1px solid #dee2e6!important}.border-top{border-top:1px solid #dee2e6!important}.border-right{border-right:1px solid #dee2e6!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-left{border-left:1px solid #dee2e6!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#007bff!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#28a745!important}.border-info{border-color:#17a2b8!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#343a40!important}.border-white{border-color:#fff!important}.rounded{border-radius:.25rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-right{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-left{border-top-left-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-circle{border-radius:50%!important}.rounded-0{border-radius:0!important}.clearfix::after{display:block;clear:both;content:""}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:flex!important}.d-inline-flex{display:inline-flex!important}@media(min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:flex!important}.d-sm-inline-flex{display:inline-flex!important}}@media(min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:flex!important}.d-md-inline-flex{display:inline-flex!important}}@media(min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:flex!important}.d-lg-inline-flex{display:inline-flex!important}}@media(min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:flex!important}.d-xl-inline-flex{display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:flex!important}.d-print-inline-flex{display:inline-flex!important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive::before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9::before{padding-top:42.85714286%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-4by3::before{padding-top:75%}.embed-responsive-1by1::before{padding-top:100%}.flex-row{flex-direction:row!important}.flex-column{flex-direction:column!important}.flex-row-reverse{flex-direction:row-reverse!important}.flex-column-reverse{flex-direction:column-reverse!important}.flex-wrap{flex-wrap:wrap!important}.flex-nowrap{flex-wrap:nowrap!important}.flex-wrap-reverse{flex-wrap:wrap-reverse!important}.flex-fill{flex:auto!important}.flex-grow-0{flex-grow:0!important}.flex-grow-1{flex-grow:1!important}.flex-shrink-0{flex-shrink:0!important}.flex-shrink-1{flex-shrink:1!important}.justify-content-start{justify-content:flex-start!important}.justify-content-end{justify-content:flex-end!important}.justify-content-center{justify-content:center!important}.justify-content-between{justify-content:space-between!important}.justify-content-around{justify-content:space-around!important}.align-items-start{align-items:flex-start!important}.align-items-end{align-items:flex-end!important}.align-items-center{align-items:center!important}.align-items-baseline{align-items:baseline!important}.align-items-stretch{align-items:stretch!important}.align-content-start{align-content:flex-start!important}.align-content-end{align-content:flex-end!important}.align-content-center{align-content:center!important}.align-content-between{align-content:space-between!important}.align-content-around{align-content:space-around!important}.align-content-stretch{align-content:stretch!important}.align-self-auto{align-self:auto!important}.align-self-start{align-self:flex-start!important}.align-self-end{align-self:flex-end!important}.align-self-center{align-self:center!important}.align-self-baseline{align-self:baseline!important}.align-self-stretch{align-self:stretch!important}@media(min-width:576px){.flex-sm-row{flex-direction:row!important}.flex-sm-column{flex-direction:column!important}.flex-sm-row-reverse{flex-direction:row-reverse!important}.flex-sm-column-reverse{flex-direction:column-reverse!important}.flex-sm-wrap{flex-wrap:wrap!important}.flex-sm-nowrap{flex-wrap:nowrap!important}.flex-sm-wrap-reverse{flex-wrap:wrap-reverse!important}.flex-sm-fill{flex:auto!important}.flex-sm-grow-0{flex-grow:0!important}.flex-sm-grow-1{flex-grow:1!important}.flex-sm-shrink-0{flex-shrink:0!important}.flex-sm-shrink-1{flex-shrink:1!important}.justify-content-sm-start{justify-content:flex-start!important}.justify-content-sm-end{justify-content:flex-end!important}.justify-content-sm-center{justify-content:center!important}.justify-content-sm-between{justify-content:space-between!important}.justify-content-sm-around{justify-content:space-around!important}.align-items-sm-start{align-items:flex-start!important}.align-items-sm-end{align-items:flex-end!important}.align-items-sm-center{align-items:center!important}.align-items-sm-baseline{align-items:baseline!important}.align-items-sm-stretch{align-items:stretch!important}.align-content-sm-start{align-content:flex-start!important}.align-content-sm-end{align-content:flex-end!important}.align-content-sm-center{align-content:center!important}.align-content-sm-between{align-content:space-between!important}.align-content-sm-around{align-content:space-around!important}.align-content-sm-stretch{align-content:stretch!important}.align-self-sm-auto{align-self:auto!important}.align-self-sm-start{align-self:flex-start!important}.align-self-sm-end{align-self:flex-end!important}.align-self-sm-center{align-self:center!important}.align-self-sm-baseline{align-self:baseline!important}.align-self-sm-stretch{align-self:stretch!important}}@media(min-width:768px){.flex-md-row{flex-direction:row!important}.flex-md-column{flex-direction:column!important}.flex-md-row-reverse{flex-direction:row-reverse!important}.flex-md-column-reverse{flex-direction:column-reverse!important}.flex-md-wrap{flex-wrap:wrap!important}.flex-md-nowrap{flex-wrap:nowrap!important}.flex-md-wrap-reverse{flex-wrap:wrap-reverse!important}.flex-md-fill{flex:auto!important}.flex-md-grow-0{flex-grow:0!important}.flex-md-grow-1{flex-grow:1!important}.flex-md-shrink-0{flex-shrink:0!important}.flex-md-shrink-1{flex-shrink:1!important}.justify-content-md-start{justify-content:flex-start!important}.justify-content-md-end{justify-content:flex-end!important}.justify-content-md-center{justify-content:center!important}.justify-content-md-between{justify-content:space-between!important}.justify-content-md-around{justify-content:space-around!important}.align-items-md-start{align-items:flex-start!important}.align-items-md-end{align-items:flex-end!important}.align-items-md-center{align-items:center!important}.align-items-md-baseline{align-items:baseline!important}.align-items-md-stretch{align-items:stretch!important}.align-content-md-start{align-content:flex-start!important}.align-content-md-end{align-content:flex-end!important}.align-content-md-center{align-content:center!important}.align-content-md-between{align-content:space-between!important}.align-content-md-around{align-content:space-around!important}.align-content-md-stretch{align-content:stretch!important}.align-self-md-auto{align-self:auto!important}.align-self-md-start{align-self:flex-start!important}.align-self-md-end{align-self:flex-end!important}.align-self-md-center{align-self:center!important}.align-self-md-baseline{align-self:baseline!important}.align-self-md-stretch{align-self:stretch!important}}@media(min-width:992px){.flex-lg-row{flex-direction:row!important}.flex-lg-column{flex-direction:column!important}.flex-lg-row-reverse{flex-direction:row-reverse!important}.flex-lg-column-reverse{flex-direction:column-reverse!important}.flex-lg-wrap{flex-wrap:wrap!important}.flex-lg-nowrap{flex-wrap:nowrap!important}.flex-lg-wrap-reverse{flex-wrap:wrap-reverse!important}.flex-lg-fill{flex:auto!important}.flex-lg-grow-0{flex-grow:0!important}.flex-lg-grow-1{flex-grow:1!important}.flex-lg-shrink-0{flex-shrink:0!important}.flex-lg-shrink-1{flex-shrink:1!important}.justify-content-lg-start{justify-content:flex-start!important}.justify-content-lg-end{justify-content:flex-end!important}.justify-content-lg-center{justify-content:center!important}.justify-content-lg-between{justify-content:space-between!important}.justify-content-lg-around{justify-content:space-around!important}.align-items-lg-start{align-items:flex-start!important}.align-items-lg-end{align-items:flex-end!important}.align-items-lg-center{align-items:center!important}.align-items-lg-baseline{align-items:baseline!important}.align-items-lg-stretch{align-items:stretch!important}.align-content-lg-start{align-content:flex-start!important}.align-content-lg-end{align-content:flex-end!important}.align-content-lg-center{align-content:center!important}.align-content-lg-between{align-content:space-between!important}.align-content-lg-around{align-content:space-around!important}.align-content-lg-stretch{align-content:stretch!important}.align-self-lg-auto{align-self:auto!important}.align-self-lg-start{align-self:flex-start!important}.align-self-lg-end{align-self:flex-end!important}.align-self-lg-center{align-self:center!important}.align-self-lg-baseline{align-self:baseline!important}.align-self-lg-stretch{align-self:stretch!important}}@media(min-width:1200px){.flex-xl-row{flex-direction:row!important}.flex-xl-column{flex-direction:column!important}.flex-xl-row-reverse{flex-direction:row-reverse!important}.flex-xl-column-reverse{flex-direction:column-reverse!important}.flex-xl-wrap{flex-wrap:wrap!important}.flex-xl-nowrap{flex-wrap:nowrap!important}.flex-xl-wrap-reverse{flex-wrap:wrap-reverse!important}.flex-xl-fill{flex:auto!important}.flex-xl-grow-0{flex-grow:0!important}.flex-xl-grow-1{flex-grow:1!important}.flex-xl-shrink-0{flex-shrink:0!important}.flex-xl-shrink-1{flex-shrink:1!important}.justify-content-xl-start{justify-content:flex-start!important}.justify-content-xl-end{justify-content:flex-end!important}.justify-content-xl-center{justify-content:center!important}.justify-content-xl-between{justify-content:space-between!important}.justify-content-xl-around{justify-content:space-around!important}.align-items-xl-start{align-items:flex-start!important}.align-items-xl-end{align-items:flex-end!important}.align-items-xl-center{align-items:center!important}.align-items-xl-baseline{align-items:baseline!important}.align-items-xl-stretch{align-items:stretch!important}.align-content-xl-start{align-content:flex-start!important}.align-content-xl-end{align-content:flex-end!important}.align-content-xl-center{align-content:center!important}.align-content-xl-between{align-content:space-between!important}.align-content-xl-around{align-content:space-around!important}.align-content-xl-stretch{align-content:stretch!important}.align-self-xl-auto{align-self:auto!important}.align-self-xl-start{align-self:flex-start!important}.align-self-xl-end{align-self:flex-end!important}.align-self-xl-center{align-self:center!important}.align-self-xl-baseline{align-self:baseline!important}.align-self-xl-stretch{align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media(min-width:576px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media(min-width:768px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media(min-width:992px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media(min-width:1200px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:sticky!important}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}@supports(position:sticky){.sticky-top{position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media(min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media(min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media(min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media(min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.text-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,liberation mono,courier new,monospace}.text-justify{text-align:justify!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media(min-width:576px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media(min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media(min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media(min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:700!important}.font-italic{font-style:italic!important}.text-white{color:#fff!important}.text-primary{color:#007bff!important}a.text-primary:hover,a.text-primary:focus{color:#0062cc!important}.text-secondary{color:#6c757d!important}a.text-secondary:hover,a.text-secondary:focus{color:#545b62!important}.text-success{color:#28a745!important}a.text-success:hover,a.text-success:focus{color:#1e7e34!important}.text-info{color:#17a2b8!important}a.text-info:hover,a.text-info:focus{color:#117a8b!important}.text-warning{color:#ffc107!important}a.text-warning:hover,a.text-warning:focus{color:#d39e00!important}.text-danger{color:#dc3545!important}a.text-danger:hover,a.text-danger:focus{color:#bd2130!important}.text-light{color:#f8f9fa!important}a.text-light:hover,a.text-light:focus{color:#dae0e5!important}.text-dark{color:#343a40!important}a.text-dark:hover,a.text-dark:focus{color:#1d2124!important}.text-body{color:#212529!important}.text-muted{color:#6c757d!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:rgba(255,255,255,.5)!important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,*::before,*::after{text-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]::after{content:" (" attr(title)")"}pre{white-space:pre-wrap!important}pre,blockquote{border:1px solid #adb5bd;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}body{min-width:992px!important}.container{min-width:992px!important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered th,.table-bordered td{border:1px solid #dee2e6!important}.table-dark{color:inherit}.table-dark th,.table-dark td,.table-dark thead th,.table-dark tbody+tbody{border-color:#dee2e6}.table .thead-dark th{color:inherit;border-color:#dee2e6}}/*!* animate.css -http://daneden.me/animate +* Version - 3.7.0 +* Licensed under the MIT license - http://opensource.org/licenses/MIT +* +* Copyright (c) 2018 Daniel Eden*/@-webkit-keyframes bounce{from,20%,53%,80%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1);-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}40%,43%{-webkit-animation-timing-function:cubic-bezier(.755,.05,.855,.06);animation-timing-function:cubic-bezier(.755,.05,.855,.06);-webkit-transform:translate3d(0,-30px,0);transform:translate3d(0,-30px,0)}70%{-webkit-animation-timing-function:cubic-bezier(.755,.05,.855,.06);animation-timing-function:cubic-bezier(.755,.05,.855,.06);-webkit-transform:translate3d(0,-15px,0);transform:translate3d(0,-15px,0)}90%{-webkit-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0)}}@keyframes bounce{from,20%,53%,80%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1);-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}40%,43%{-webkit-animation-timing-function:cubic-bezier(.755,.05,.855,.06);animation-timing-function:cubic-bezier(.755,.05,.855,.06);-webkit-transform:translate3d(0,-30px,0);transform:translate3d(0,-30px,0)}70%{-webkit-animation-timing-function:cubic-bezier(.755,.05,.855,.06);animation-timing-function:cubic-bezier(.755,.05,.855,.06);-webkit-transform:translate3d(0,-15px,0);transform:translate3d(0,-15px,0)}90%{-webkit-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0)}}.bounce{-webkit-animation-name:bounce;animation-name:bounce;-webkit-transform-origin:center bottom;transform-origin:center bottom}@-webkit-keyframes flash{from,50%,to{opacity:1}25%,75%{opacity:0}}@keyframes flash{from,50%,to{opacity:1}25%,75%{opacity:0}}.flash{-webkit-animation-name:flash;animation-name:flash}@-webkit-keyframes pulse{from{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}50%{-webkit-transform:scale3d(1.05,1.05,1.05);transform:scale3d(1.05,1.05,1.05)}to{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}@keyframes pulse{from{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}50%{-webkit-transform:scale3d(1.05,1.05,1.05);transform:scale3d(1.05,1.05,1.05)}to{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}.pulse{-webkit-animation-name:pulse;animation-name:pulse}@-webkit-keyframes rubberBand{from{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}30%{-webkit-transform:scale3d(1.25,.75,1);transform:scale3d(1.25,.75,1)}40%{-webkit-transform:scale3d(.75,1.25,1);transform:scale3d(.75,1.25,1)}50%{-webkit-transform:scale3d(1.15,.85,1);transform:scale3d(1.15,.85,1)}65%{-webkit-transform:scale3d(.95,1.05,1);transform:scale3d(.95,1.05,1)}75%{-webkit-transform:scale3d(1.05,.95,1);transform:scale3d(1.05,.95,1)}to{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}@keyframes rubberBand{from{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}30%{-webkit-transform:scale3d(1.25,.75,1);transform:scale3d(1.25,.75,1)}40%{-webkit-transform:scale3d(.75,1.25,1);transform:scale3d(.75,1.25,1)}50%{-webkit-transform:scale3d(1.15,.85,1);transform:scale3d(1.15,.85,1)}65%{-webkit-transform:scale3d(.95,1.05,1);transform:scale3d(.95,1.05,1)}75%{-webkit-transform:scale3d(1.05,.95,1);transform:scale3d(1.05,.95,1)}to{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}.rubberBand{-webkit-animation-name:rubberBand;animation-name:rubberBand}@-webkit-keyframes shake{from,to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}10%,30%,50%,70%,90%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}20%,40%,60%,80%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}}@keyframes shake{from,to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}10%,30%,50%,70%,90%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}20%,40%,60%,80%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}}.shake{-webkit-animation-name:shake;animation-name:shake}@-webkit-keyframes headShake{0%{-webkit-transform:translateX(0);transform:translateX(0)}6.5%{-webkit-transform:translateX(-6px)rotateY(-9deg);transform:translateX(-6px)rotateY(-9deg)}18.5%{-webkit-transform:translateX(5px)rotateY(7deg);transform:translateX(5px)rotateY(7deg)}31.5%{-webkit-transform:translateX(-3px)rotateY(-5deg);transform:translateX(-3px)rotateY(-5deg)}43.5%{-webkit-transform:translateX(2px)rotateY(3deg);transform:translateX(2px)rotateY(3deg)}50%{-webkit-transform:translateX(0);transform:translateX(0)}}@keyframes headShake{0%{-webkit-transform:translateX(0);transform:translateX(0)}6.5%{-webkit-transform:translateX(-6px)rotateY(-9deg);transform:translateX(-6px)rotateY(-9deg)}18.5%{-webkit-transform:translateX(5px)rotateY(7deg);transform:translateX(5px)rotateY(7deg)}31.5%{-webkit-transform:translateX(-3px)rotateY(-5deg);transform:translateX(-3px)rotateY(-5deg)}43.5%{-webkit-transform:translateX(2px)rotateY(3deg);transform:translateX(2px)rotateY(3deg)}50%{-webkit-transform:translateX(0);transform:translateX(0)}}.headShake{-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;-webkit-animation-name:headShake;animation-name:headShake}@-webkit-keyframes swing{20%{-webkit-transform:rotate3d(0,0,1,15deg);transform:rotate3d(0,0,1,15deg)}40%{-webkit-transform:rotate3d(0,0,1,-10deg);transform:rotate3d(0,0,1,-10deg)}60%{-webkit-transform:rotate3d(0,0,1,5deg);transform:rotate3d(0,0,1,5deg)}80%{-webkit-transform:rotate3d(0,0,1,-5deg);transform:rotate3d(0,0,1,-5deg)}to{-webkit-transform:rotate3d(0,0,1,0);transform:rotate3d(0,0,1,0)}}@keyframes swing{20%{-webkit-transform:rotate3d(0,0,1,15deg);transform:rotate3d(0,0,1,15deg)}40%{-webkit-transform:rotate3d(0,0,1,-10deg);transform:rotate3d(0,0,1,-10deg)}60%{-webkit-transform:rotate3d(0,0,1,5deg);transform:rotate3d(0,0,1,5deg)}80%{-webkit-transform:rotate3d(0,0,1,-5deg);transform:rotate3d(0,0,1,-5deg)}to{-webkit-transform:rotate3d(0,0,1,0);transform:rotate3d(0,0,1,0)}}.swing{-webkit-transform-origin:top center;transform-origin:top center;-webkit-animation-name:swing;animation-name:swing}@-webkit-keyframes tada{from{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}10%,20%{-webkit-transform:scale3d(.9,.9,.9)rotate3d(0,0,1,-3deg);transform:scale3d(.9,.9,.9)rotate3d(0,0,1,-3deg)}30%,50%,70%,90%{-webkit-transform:scale3d(1.1,1.1,1.1)rotate3d(0,0,1,3deg);transform:scale3d(1.1,1.1,1.1)rotate3d(0,0,1,3deg)}40%,60%,80%{-webkit-transform:scale3d(1.1,1.1,1.1)rotate3d(0,0,1,-3deg);transform:scale3d(1.1,1.1,1.1)rotate3d(0,0,1,-3deg)}to{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}@keyframes tada{from{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}10%,20%{-webkit-transform:scale3d(.9,.9,.9)rotate3d(0,0,1,-3deg);transform:scale3d(.9,.9,.9)rotate3d(0,0,1,-3deg)}30%,50%,70%,90%{-webkit-transform:scale3d(1.1,1.1,1.1)rotate3d(0,0,1,3deg);transform:scale3d(1.1,1.1,1.1)rotate3d(0,0,1,3deg)}40%,60%,80%{-webkit-transform:scale3d(1.1,1.1,1.1)rotate3d(0,0,1,-3deg);transform:scale3d(1.1,1.1,1.1)rotate3d(0,0,1,-3deg)}to{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}.tada{-webkit-animation-name:tada;animation-name:tada}@-webkit-keyframes wobble{from{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}15%{-webkit-transform:translate3d(-25%,0,0)rotate3d(0,0,1,-5deg);transform:translate3d(-25%,0,0)rotate3d(0,0,1,-5deg)}30%{-webkit-transform:translate3d(20%,0,0)rotate3d(0,0,1,3deg);transform:translate3d(20%,0,0)rotate3d(0,0,1,3deg)}45%{-webkit-transform:translate3d(-15%,0,0)rotate3d(0,0,1,-3deg);transform:translate3d(-15%,0,0)rotate3d(0,0,1,-3deg)}60%{-webkit-transform:translate3d(10%,0,0)rotate3d(0,0,1,2deg);transform:translate3d(10%,0,0)rotate3d(0,0,1,2deg)}75%{-webkit-transform:translate3d(-5%,0,0)rotate3d(0,0,1,-1deg);transform:translate3d(-5%,0,0)rotate3d(0,0,1,-1deg)}to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}@keyframes wobble{from{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}15%{-webkit-transform:translate3d(-25%,0,0)rotate3d(0,0,1,-5deg);transform:translate3d(-25%,0,0)rotate3d(0,0,1,-5deg)}30%{-webkit-transform:translate3d(20%,0,0)rotate3d(0,0,1,3deg);transform:translate3d(20%,0,0)rotate3d(0,0,1,3deg)}45%{-webkit-transform:translate3d(-15%,0,0)rotate3d(0,0,1,-3deg);transform:translate3d(-15%,0,0)rotate3d(0,0,1,-3deg)}60%{-webkit-transform:translate3d(10%,0,0)rotate3d(0,0,1,2deg);transform:translate3d(10%,0,0)rotate3d(0,0,1,2deg)}75%{-webkit-transform:translate3d(-5%,0,0)rotate3d(0,0,1,-1deg);transform:translate3d(-5%,0,0)rotate3d(0,0,1,-1deg)}to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.wobble{-webkit-animation-name:wobble;animation-name:wobble}@-webkit-keyframes jello{from,11.1%,to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}22.2%{-webkit-transform:skewX(-12.5deg)skewY(-12.5deg);transform:skewX(-12.5deg)skewY(-12.5deg)}33.3%{-webkit-transform:skewX(6.25deg)skewY(6.25deg);transform:skewX(6.25deg)skewY(6.25deg)}44.4%{-webkit-transform:skewX(-3.125deg)skewY(-3.125deg);transform:skewX(-3.125deg)skewY(-3.125deg)}55.5%{-webkit-transform:skewX(1.5625deg)skewY(1.5625deg);transform:skewX(1.5625deg)skewY(1.5625deg)}66.6%{-webkit-transform:skewX(-.78125deg)skewY(-.78125deg);transform:skewX(-.78125deg)skewY(-.78125deg)}77.7%{-webkit-transform:skewX(.390625deg)skewY(.390625deg);transform:skewX(.390625deg)skewY(.390625deg)}88.8%{-webkit-transform:skewX(-.1953125deg)skewY(-.1953125deg);transform:skewX(-.1953125deg)skewY(-.1953125deg)}}@keyframes jello{from,11.1%,to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}22.2%{-webkit-transform:skewX(-12.5deg)skewY(-12.5deg);transform:skewX(-12.5deg)skewY(-12.5deg)}33.3%{-webkit-transform:skewX(6.25deg)skewY(6.25deg);transform:skewX(6.25deg)skewY(6.25deg)}44.4%{-webkit-transform:skewX(-3.125deg)skewY(-3.125deg);transform:skewX(-3.125deg)skewY(-3.125deg)}55.5%{-webkit-transform:skewX(1.5625deg)skewY(1.5625deg);transform:skewX(1.5625deg)skewY(1.5625deg)}66.6%{-webkit-transform:skewX(-.78125deg)skewY(-.78125deg);transform:skewX(-.78125deg)skewY(-.78125deg)}77.7%{-webkit-transform:skewX(.390625deg)skewY(.390625deg);transform:skewX(.390625deg)skewY(.390625deg)}88.8%{-webkit-transform:skewX(-.1953125deg)skewY(-.1953125deg);transform:skewX(-.1953125deg)skewY(-.1953125deg)}}.jello{-webkit-animation-name:jello;animation-name:jello;-webkit-transform-origin:center;transform-origin:center}@-webkit-keyframes heartBeat{0%{-webkit-transform:scale(1);transform:scale(1)}14%{-webkit-transform:scale(1.3);transform:scale(1.3)}28%{-webkit-transform:scale(1);transform:scale(1)}42%{-webkit-transform:scale(1.3);transform:scale(1.3)}70%{-webkit-transform:scale(1);transform:scale(1)}}@keyframes heartBeat{0%{-webkit-transform:scale(1);transform:scale(1)}14%{-webkit-transform:scale(1.3);transform:scale(1.3)}28%{-webkit-transform:scale(1);transform:scale(1)}42%{-webkit-transform:scale(1.3);transform:scale(1.3)}70%{-webkit-transform:scale(1);transform:scale(1)}}.heartBeat{-webkit-animation-name:heartBeat;animation-name:heartBeat;-webkit-animation-duration:1.3s;animation-duration:1.3s;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}@-webkit-keyframes bounceIn{from,20%,40%,60%,80%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}20%{-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}40%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}60%{opacity:1;-webkit-transform:scale3d(1.03,1.03,1.03);transform:scale3d(1.03,1.03,1.03)}80%{-webkit-transform:scale3d(.97,.97,.97);transform:scale3d(.97,.97,.97)}to{opacity:1;-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}@keyframes bounceIn{from,20%,40%,60%,80%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}20%{-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}40%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}60%{opacity:1;-webkit-transform:scale3d(1.03,1.03,1.03);transform:scale3d(1.03,1.03,1.03)}80%{-webkit-transform:scale3d(.97,.97,.97);transform:scale3d(.97,.97,.97)}to{opacity:1;-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}.bounceIn{-webkit-animation-duration:.75s;animation-duration:.75s;-webkit-animation-name:bounceIn;animation-name:bounceIn}@-webkit-keyframes bounceInDown{from,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,-3000px,0);transform:translate3d(0,-3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,25px,0);transform:translate3d(0,25px,0)}75%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}90%{-webkit-transform:translate3d(0,5px,0);transform:translate3d(0,5px,0)}to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}@keyframes bounceInDown{from,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,-3000px,0);transform:translate3d(0,-3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,25px,0);transform:translate3d(0,25px,0)}75%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}90%{-webkit-transform:translate3d(0,5px,0);transform:translate3d(0,5px,0)}to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.bounceInDown{-webkit-animation-name:bounceInDown;animation-name:bounceInDown}@-webkit-keyframes bounceInLeft{from,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(-3000px,0,0);transform:translate3d(-3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(25px,0,0);transform:translate3d(25px,0,0)}75%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}90%{-webkit-transform:translate3d(5px,0,0);transform:translate3d(5px,0,0)}to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}@keyframes bounceInLeft{from,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(-3000px,0,0);transform:translate3d(-3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(25px,0,0);transform:translate3d(25px,0,0)}75%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}90%{-webkit-transform:translate3d(5px,0,0);transform:translate3d(5px,0,0)}to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.bounceInLeft{-webkit-animation-name:bounceInLeft;animation-name:bounceInLeft}@-webkit-keyframes bounceInRight{from,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}from{opacity:0;-webkit-transform:translate3d(3000px,0,0);transform:translate3d(3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(-25px,0,0);transform:translate3d(-25px,0,0)}75%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}90%{-webkit-transform:translate3d(-5px,0,0);transform:translate3d(-5px,0,0)}to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}@keyframes bounceInRight{from,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}from{opacity:0;-webkit-transform:translate3d(3000px,0,0);transform:translate3d(3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(-25px,0,0);transform:translate3d(-25px,0,0)}75%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}90%{-webkit-transform:translate3d(-5px,0,0);transform:translate3d(-5px,0,0)}to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.bounceInRight{-webkit-animation-name:bounceInRight;animation-name:bounceInRight}@-webkit-keyframes bounceInUp{from,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}from{opacity:0;-webkit-transform:translate3d(0,3000px,0);transform:translate3d(0,3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}75%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}90%{-webkit-transform:translate3d(0,-5px,0);transform:translate3d(0,-5px,0)}to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}@keyframes bounceInUp{from,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}from{opacity:0;-webkit-transform:translate3d(0,3000px,0);transform:translate3d(0,3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}75%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}90%{-webkit-transform:translate3d(0,-5px,0);transform:translate3d(0,-5px,0)}to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.bounceInUp{-webkit-animation-name:bounceInUp;animation-name:bounceInUp}@-webkit-keyframes bounceOut{20%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}50%,55%{opacity:1;-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}to{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}}@keyframes bounceOut{20%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}50%,55%{opacity:1;-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}to{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}}.bounceOut{-webkit-animation-duration:.75s;animation-duration:.75s;-webkit-animation-name:bounceOut;animation-name:bounceOut}@-webkit-keyframes bounceOutDown{20%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}to{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}@keyframes bounceOutDown{20%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}to{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}.bounceOutDown{-webkit-animation-name:bounceOutDown;animation-name:bounceOutDown}@-webkit-keyframes bounceOutLeft{20%{opacity:1;-webkit-transform:translate3d(20px,0,0);transform:translate3d(20px,0,0)}to{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}@keyframes bounceOutLeft{20%{opacity:1;-webkit-transform:translate3d(20px,0,0);transform:translate3d(20px,0,0)}to{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}.bounceOutLeft{-webkit-animation-name:bounceOutLeft;animation-name:bounceOutLeft}@-webkit-keyframes bounceOutRight{20%{opacity:1;-webkit-transform:translate3d(-20px,0,0);transform:translate3d(-20px,0,0)}to{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}@keyframes bounceOutRight{20%{opacity:1;-webkit-transform:translate3d(-20px,0,0);transform:translate3d(-20px,0,0)}to{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}.bounceOutRight{-webkit-animation-name:bounceOutRight;animation-name:bounceOutRight}@-webkit-keyframes bounceOutUp{20%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,20px,0);transform:translate3d(0,20px,0)}to{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}@keyframes bounceOutUp{20%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,20px,0);transform:translate3d(0,20px,0)}to{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}.bounceOutUp{-webkit-animation-name:bounceOutUp;animation-name:bounceOutUp}@-webkit-keyframes fadeIn{from{opacity:0}to{opacity:1}}@keyframes fadeIn{from{opacity:0}to{opacity:1}}.fadeIn{-webkit-animation-name:fadeIn;animation-name:fadeIn}@-webkit-keyframes fadeInDown{from{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}to{opacity:1;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}@keyframes fadeInDown{from{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}to{opacity:1;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.fadeInDown{-webkit-animation-name:fadeInDown;animation-name:fadeInDown}@-webkit-keyframes fadeInDownBig{from{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}to{opacity:1;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}@keyframes fadeInDownBig{from{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}to{opacity:1;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.fadeInDownBig{-webkit-animation-name:fadeInDownBig;animation-name:fadeInDownBig}@-webkit-keyframes fadeInLeft{from{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}to{opacity:1;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}@keyframes fadeInLeft{from{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}to{opacity:1;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.fadeInLeft{-webkit-animation-name:fadeInLeft;animation-name:fadeInLeft}@-webkit-keyframes fadeInLeftBig{from{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}to{opacity:1;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}@keyframes fadeInLeftBig{from{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}to{opacity:1;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.fadeInLeftBig{-webkit-animation-name:fadeInLeftBig;animation-name:fadeInLeftBig}@-webkit-keyframes fadeInRight{from{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}to{opacity:1;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}@keyframes fadeInRight{from{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}to{opacity:1;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.fadeInRight{-webkit-animation-name:fadeInRight;animation-name:fadeInRight}@-webkit-keyframes fadeInRightBig{from{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}to{opacity:1;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}@keyframes fadeInRightBig{from{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}to{opacity:1;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.fadeInRightBig{-webkit-animation-name:fadeInRightBig;animation-name:fadeInRightBig}@-webkit-keyframes fadeInUp{from{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}to{opacity:1;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}@keyframes fadeInUp{from{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}to{opacity:1;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.fadeInUp{-webkit-animation-name:fadeInUp;animation-name:fadeInUp}@-webkit-keyframes fadeInUpBig{from{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}to{opacity:1;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}@keyframes fadeInUpBig{from{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}to{opacity:1;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.fadeInUpBig{-webkit-animation-name:fadeInUpBig;animation-name:fadeInUpBig}@-webkit-keyframes fadeOut{from{opacity:1}to{opacity:0}}@keyframes fadeOut{from{opacity:1}to{opacity:0}}.fadeOut{-webkit-animation-name:fadeOut;animation-name:fadeOut}@-webkit-keyframes fadeOutDown{from{opacity:1}to{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}}@keyframes fadeOutDown{from{opacity:1}to{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}}.fadeOutDown{-webkit-animation-name:fadeOutDown;animation-name:fadeOutDown}@-webkit-keyframes fadeOutDownBig{from{opacity:1}to{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}@keyframes fadeOutDownBig{from{opacity:1}to{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}.fadeOutDownBig{-webkit-animation-name:fadeOutDownBig;animation-name:fadeOutDownBig}@-webkit-keyframes fadeOutLeft{from{opacity:1}to{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}}@keyframes fadeOutLeft{from{opacity:1}to{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}}.fadeOutLeft{-webkit-animation-name:fadeOutLeft;animation-name:fadeOutLeft}@-webkit-keyframes fadeOutLeftBig{from{opacity:1}to{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}@keyframes fadeOutLeftBig{from{opacity:1}to{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}.fadeOutLeftBig{-webkit-animation-name:fadeOutLeftBig;animation-name:fadeOutLeftBig}@-webkit-keyframes fadeOutRight{from{opacity:1}to{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}}@keyframes fadeOutRight{from{opacity:1}to{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}}.fadeOutRight{-webkit-animation-name:fadeOutRight;animation-name:fadeOutRight}@-webkit-keyframes fadeOutRightBig{from{opacity:1}to{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}@keyframes fadeOutRightBig{from{opacity:1}to{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}.fadeOutRightBig{-webkit-animation-name:fadeOutRightBig;animation-name:fadeOutRightBig}@-webkit-keyframes fadeOutUp{from{opacity:1}to{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}}@keyframes fadeOutUp{from{opacity:1}to{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}}.fadeOutUp{-webkit-animation-name:fadeOutUp;animation-name:fadeOutUp}@-webkit-keyframes fadeOutUpBig{from{opacity:1}to{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}@keyframes fadeOutUpBig{from{opacity:1}to{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}.fadeOutUpBig{-webkit-animation-name:fadeOutUpBig;animation-name:fadeOutUpBig}@-webkit-keyframes flip{from{-webkit-transform:perspective(400px)scale3d(1,1,1)translate3d(0,0,0)rotate3d(0,1,0,-360deg);transform:perspective(400px)scale3d(1,1,1)translate3d(0,0,0)rotate3d(0,1,0,-360deg);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}40%{-webkit-transform:perspective(400px)scale3d(1,1,1)translate3d(0,0,150px)rotate3d(0,1,0,-190deg);transform:perspective(400px)scale3d(1,1,1)translate3d(0,0,150px)rotate3d(0,1,0,-190deg);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}50%{-webkit-transform:perspective(400px)scale3d(1,1,1)translate3d(0,0,150px)rotate3d(0,1,0,-170deg);transform:perspective(400px)scale3d(1,1,1)translate3d(0,0,150px)rotate3d(0,1,0,-170deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}80%{-webkit-transform:perspective(400px)scale3d(.95,.95,.95)translate3d(0,0,0)rotate3d(0,1,0,0);transform:perspective(400px)scale3d(.95,.95,.95)translate3d(0,0,0)rotate3d(0,1,0,0);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}to{-webkit-transform:perspective(400px)scale3d(1,1,1)translate3d(0,0,0)rotate3d(0,1,0,0);transform:perspective(400px)scale3d(1,1,1)translate3d(0,0,0)rotate3d(0,1,0,0);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}}@keyframes flip{from{-webkit-transform:perspective(400px)scale3d(1,1,1)translate3d(0,0,0)rotate3d(0,1,0,-360deg);transform:perspective(400px)scale3d(1,1,1)translate3d(0,0,0)rotate3d(0,1,0,-360deg);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}40%{-webkit-transform:perspective(400px)scale3d(1,1,1)translate3d(0,0,150px)rotate3d(0,1,0,-190deg);transform:perspective(400px)scale3d(1,1,1)translate3d(0,0,150px)rotate3d(0,1,0,-190deg);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}50%{-webkit-transform:perspective(400px)scale3d(1,1,1)translate3d(0,0,150px)rotate3d(0,1,0,-170deg);transform:perspective(400px)scale3d(1,1,1)translate3d(0,0,150px)rotate3d(0,1,0,-170deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}80%{-webkit-transform:perspective(400px)scale3d(.95,.95,.95)translate3d(0,0,0)rotate3d(0,1,0,0);transform:perspective(400px)scale3d(.95,.95,.95)translate3d(0,0,0)rotate3d(0,1,0,0);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}to{-webkit-transform:perspective(400px)scale3d(1,1,1)translate3d(0,0,0)rotate3d(0,1,0,0);transform:perspective(400px)scale3d(1,1,1)translate3d(0,0,0)rotate3d(0,1,0,0);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}}.animated.flip{-webkit-backface-visibility:visible;backface-visibility:visible;-webkit-animation-name:flip;animation-name:flip}@-webkit-keyframes flipInX{from{-webkit-transform:perspective(400px)rotate3d(1,0,0,90deg);transform:perspective(400px)rotate3d(1,0,0,90deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in;opacity:0}40%{-webkit-transform:perspective(400px)rotate3d(1,0,0,-20deg);transform:perspective(400px)rotate3d(1,0,0,-20deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}60%{-webkit-transform:perspective(400px)rotate3d(1,0,0,10deg);transform:perspective(400px)rotate3d(1,0,0,10deg);opacity:1}80%{-webkit-transform:perspective(400px)rotate3d(1,0,0,-5deg);transform:perspective(400px)rotate3d(1,0,0,-5deg)}to{-webkit-transform:perspective(400px);transform:perspective(400px)}}@keyframes flipInX{from{-webkit-transform:perspective(400px)rotate3d(1,0,0,90deg);transform:perspective(400px)rotate3d(1,0,0,90deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in;opacity:0}40%{-webkit-transform:perspective(400px)rotate3d(1,0,0,-20deg);transform:perspective(400px)rotate3d(1,0,0,-20deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}60%{-webkit-transform:perspective(400px)rotate3d(1,0,0,10deg);transform:perspective(400px)rotate3d(1,0,0,10deg);opacity:1}80%{-webkit-transform:perspective(400px)rotate3d(1,0,0,-5deg);transform:perspective(400px)rotate3d(1,0,0,-5deg)}to{-webkit-transform:perspective(400px);transform:perspective(400px)}}.flipInX{-webkit-backface-visibility:visible!important;backface-visibility:visible!important;-webkit-animation-name:flipInX;animation-name:flipInX}@-webkit-keyframes flipInY{from{-webkit-transform:perspective(400px)rotate3d(0,1,0,90deg);transform:perspective(400px)rotate3d(0,1,0,90deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in;opacity:0}40%{-webkit-transform:perspective(400px)rotate3d(0,1,0,-20deg);transform:perspective(400px)rotate3d(0,1,0,-20deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}60%{-webkit-transform:perspective(400px)rotate3d(0,1,0,10deg);transform:perspective(400px)rotate3d(0,1,0,10deg);opacity:1}80%{-webkit-transform:perspective(400px)rotate3d(0,1,0,-5deg);transform:perspective(400px)rotate3d(0,1,0,-5deg)}to{-webkit-transform:perspective(400px);transform:perspective(400px)}}@keyframes flipInY{from{-webkit-transform:perspective(400px)rotate3d(0,1,0,90deg);transform:perspective(400px)rotate3d(0,1,0,90deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in;opacity:0}40%{-webkit-transform:perspective(400px)rotate3d(0,1,0,-20deg);transform:perspective(400px)rotate3d(0,1,0,-20deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}60%{-webkit-transform:perspective(400px)rotate3d(0,1,0,10deg);transform:perspective(400px)rotate3d(0,1,0,10deg);opacity:1}80%{-webkit-transform:perspective(400px)rotate3d(0,1,0,-5deg);transform:perspective(400px)rotate3d(0,1,0,-5deg)}to{-webkit-transform:perspective(400px);transform:perspective(400px)}}.flipInY{-webkit-backface-visibility:visible!important;backface-visibility:visible!important;-webkit-animation-name:flipInY;animation-name:flipInY}@-webkit-keyframes flipOutX{from{-webkit-transform:perspective(400px);transform:perspective(400px)}30%{-webkit-transform:perspective(400px)rotate3d(1,0,0,-20deg);transform:perspective(400px)rotate3d(1,0,0,-20deg);opacity:1}to{-webkit-transform:perspective(400px)rotate3d(1,0,0,90deg);transform:perspective(400px)rotate3d(1,0,0,90deg);opacity:0}}@keyframes flipOutX{from{-webkit-transform:perspective(400px);transform:perspective(400px)}30%{-webkit-transform:perspective(400px)rotate3d(1,0,0,-20deg);transform:perspective(400px)rotate3d(1,0,0,-20deg);opacity:1}to{-webkit-transform:perspective(400px)rotate3d(1,0,0,90deg);transform:perspective(400px)rotate3d(1,0,0,90deg);opacity:0}}.flipOutX{-webkit-animation-duration:.75s;animation-duration:.75s;-webkit-animation-name:flipOutX;animation-name:flipOutX;-webkit-backface-visibility:visible!important;backface-visibility:visible!important}@-webkit-keyframes flipOutY{from{-webkit-transform:perspective(400px);transform:perspective(400px)}30%{-webkit-transform:perspective(400px)rotate3d(0,1,0,-15deg);transform:perspective(400px)rotate3d(0,1,0,-15deg);opacity:1}to{-webkit-transform:perspective(400px)rotate3d(0,1,0,90deg);transform:perspective(400px)rotate3d(0,1,0,90deg);opacity:0}}@keyframes flipOutY{from{-webkit-transform:perspective(400px);transform:perspective(400px)}30%{-webkit-transform:perspective(400px)rotate3d(0,1,0,-15deg);transform:perspective(400px)rotate3d(0,1,0,-15deg);opacity:1}to{-webkit-transform:perspective(400px)rotate3d(0,1,0,90deg);transform:perspective(400px)rotate3d(0,1,0,90deg);opacity:0}}.flipOutY{-webkit-animation-duration:.75s;animation-duration:.75s;-webkit-backface-visibility:visible!important;backface-visibility:visible!important;-webkit-animation-name:flipOutY;animation-name:flipOutY}@-webkit-keyframes lightSpeedIn{from{-webkit-transform:translate3d(100%,0,0)skewX(-30deg);transform:translate3d(100%,0,0)skewX(-30deg);opacity:0}60%{-webkit-transform:skewX(20deg);transform:skewX(20deg);opacity:1}80%{-webkit-transform:skewX(-5deg);transform:skewX(-5deg)}to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}@keyframes lightSpeedIn{from{-webkit-transform:translate3d(100%,0,0)skewX(-30deg);transform:translate3d(100%,0,0)skewX(-30deg);opacity:0}60%{-webkit-transform:skewX(20deg);transform:skewX(20deg);opacity:1}80%{-webkit-transform:skewX(-5deg);transform:skewX(-5deg)}to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.lightSpeedIn{-webkit-animation-name:lightSpeedIn;animation-name:lightSpeedIn;-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}@-webkit-keyframes lightSpeedOut{from{opacity:1}to{-webkit-transform:translate3d(100%,0,0)skewX(30deg);transform:translate3d(100%,0,0)skewX(30deg);opacity:0}}@keyframes lightSpeedOut{from{opacity:1}to{-webkit-transform:translate3d(100%,0,0)skewX(30deg);transform:translate3d(100%,0,0)skewX(30deg);opacity:0}}.lightSpeedOut{-webkit-animation-name:lightSpeedOut;animation-name:lightSpeedOut;-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}@-webkit-keyframes rotateIn{from{-webkit-transform-origin:center;transform-origin:center;-webkit-transform:rotate3d(0,0,1,-200deg);transform:rotate3d(0,0,1,-200deg);opacity:0}to{-webkit-transform-origin:center;transform-origin:center;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}@keyframes rotateIn{from{-webkit-transform-origin:center;transform-origin:center;-webkit-transform:rotate3d(0,0,1,-200deg);transform:rotate3d(0,0,1,-200deg);opacity:0}to{-webkit-transform-origin:center;transform-origin:center;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}.rotateIn{-webkit-animation-name:rotateIn;animation-name:rotateIn}@-webkit-keyframes rotateInDownLeft{from{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);opacity:0}to{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}@keyframes rotateInDownLeft{from{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);opacity:0}to{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}.rotateInDownLeft{-webkit-animation-name:rotateInDownLeft;animation-name:rotateInDownLeft}@-webkit-keyframes rotateInDownRight{from{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);opacity:0}to{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}@keyframes rotateInDownRight{from{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);opacity:0}to{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}.rotateInDownRight{-webkit-animation-name:rotateInDownRight;animation-name:rotateInDownRight}@-webkit-keyframes rotateInUpLeft{from{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);opacity:0}to{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}@keyframes rotateInUpLeft{from{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);opacity:0}to{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}.rotateInUpLeft{-webkit-animation-name:rotateInUpLeft;animation-name:rotateInUpLeft}@-webkit-keyframes rotateInUpRight{from{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,-90deg);transform:rotate3d(0,0,1,-90deg);opacity:0}to{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}@keyframes rotateInUpRight{from{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,-90deg);transform:rotate3d(0,0,1,-90deg);opacity:0}to{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}.rotateInUpRight{-webkit-animation-name:rotateInUpRight;animation-name:rotateInUpRight}@-webkit-keyframes rotateOut{from{-webkit-transform-origin:center;transform-origin:center;opacity:1}to{-webkit-transform-origin:center;transform-origin:center;-webkit-transform:rotate3d(0,0,1,200deg);transform:rotate3d(0,0,1,200deg);opacity:0}}@keyframes rotateOut{from{-webkit-transform-origin:center;transform-origin:center;opacity:1}to{-webkit-transform-origin:center;transform-origin:center;-webkit-transform:rotate3d(0,0,1,200deg);transform:rotate3d(0,0,1,200deg);opacity:0}}.rotateOut{-webkit-animation-name:rotateOut;animation-name:rotateOut}@-webkit-keyframes rotateOutDownLeft{from{-webkit-transform-origin:left bottom;transform-origin:left bottom;opacity:1}to{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);opacity:0}}@keyframes rotateOutDownLeft{from{-webkit-transform-origin:left bottom;transform-origin:left bottom;opacity:1}to{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);opacity:0}}.rotateOutDownLeft{-webkit-animation-name:rotateOutDownLeft;animation-name:rotateOutDownLeft}@-webkit-keyframes rotateOutDownRight{from{-webkit-transform-origin:right bottom;transform-origin:right bottom;opacity:1}to{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);opacity:0}}@keyframes rotateOutDownRight{from{-webkit-transform-origin:right bottom;transform-origin:right bottom;opacity:1}to{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);opacity:0}}.rotateOutDownRight{-webkit-animation-name:rotateOutDownRight;animation-name:rotateOutDownRight}@-webkit-keyframes rotateOutUpLeft{from{-webkit-transform-origin:left bottom;transform-origin:left bottom;opacity:1}to{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);opacity:0}}@keyframes rotateOutUpLeft{from{-webkit-transform-origin:left bottom;transform-origin:left bottom;opacity:1}to{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);opacity:0}}.rotateOutUpLeft{-webkit-animation-name:rotateOutUpLeft;animation-name:rotateOutUpLeft}@-webkit-keyframes rotateOutUpRight{from{-webkit-transform-origin:right bottom;transform-origin:right bottom;opacity:1}to{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,90deg);transform:rotate3d(0,0,1,90deg);opacity:0}}@keyframes rotateOutUpRight{from{-webkit-transform-origin:right bottom;transform-origin:right bottom;opacity:1}to{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,90deg);transform:rotate3d(0,0,1,90deg);opacity:0}}.rotateOutUpRight{-webkit-animation-name:rotateOutUpRight;animation-name:rotateOutUpRight}@-webkit-keyframes hinge{0%{-webkit-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}20%,60%{-webkit-transform:rotate3d(0,0,1,80deg);transform:rotate3d(0,0,1,80deg);-webkit-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}40%,80%{-webkit-transform:rotate3d(0,0,1,60deg);transform:rotate3d(0,0,1,60deg);-webkit-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;opacity:1}to{-webkit-transform:translate3d(0,700px,0);transform:translate3d(0,700px,0);opacity:0}}@keyframes hinge{0%{-webkit-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}20%,60%{-webkit-transform:rotate3d(0,0,1,80deg);transform:rotate3d(0,0,1,80deg);-webkit-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}40%,80%{-webkit-transform:rotate3d(0,0,1,60deg);transform:rotate3d(0,0,1,60deg);-webkit-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;opacity:1}to{-webkit-transform:translate3d(0,700px,0);transform:translate3d(0,700px,0);opacity:0}}.hinge{-webkit-animation-duration:2s;animation-duration:2s;-webkit-animation-name:hinge;animation-name:hinge}@-webkit-keyframes jackInTheBox{from{opacity:0;-webkit-transform:scale(.1)rotate(30deg);transform:scale(.1)rotate(30deg);-webkit-transform-origin:center bottom;transform-origin:center bottom}50%{-webkit-transform:rotate(-10deg);transform:rotate(-10deg)}70%{-webkit-transform:rotate(3deg);transform:rotate(3deg)}to{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@keyframes jackInTheBox{from{opacity:0;-webkit-transform:scale(.1)rotate(30deg);transform:scale(.1)rotate(30deg);-webkit-transform-origin:center bottom;transform-origin:center bottom}50%{-webkit-transform:rotate(-10deg);transform:rotate(-10deg)}70%{-webkit-transform:rotate(3deg);transform:rotate(3deg)}to{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}.jackInTheBox{-webkit-animation-name:jackInTheBox;animation-name:jackInTheBox}@-webkit-keyframes rollIn{from{opacity:0;-webkit-transform:translate3d(-100%,0,0)rotate3d(0,0,1,-120deg);transform:translate3d(-100%,0,0)rotate3d(0,0,1,-120deg)}to{opacity:1;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}@keyframes rollIn{from{opacity:0;-webkit-transform:translate3d(-100%,0,0)rotate3d(0,0,1,-120deg);transform:translate3d(-100%,0,0)rotate3d(0,0,1,-120deg)}to{opacity:1;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.rollIn{-webkit-animation-name:rollIn;animation-name:rollIn}@-webkit-keyframes rollOut{from{opacity:1}to{opacity:0;-webkit-transform:translate3d(100%,0,0)rotate3d(0,0,1,120deg);transform:translate3d(100%,0,0)rotate3d(0,0,1,120deg)}}@keyframes rollOut{from{opacity:1}to{opacity:0;-webkit-transform:translate3d(100%,0,0)rotate3d(0,0,1,120deg);transform:translate3d(100%,0,0)rotate3d(0,0,1,120deg)}}.rollOut{-webkit-animation-name:rollOut;animation-name:rollOut}@-webkit-keyframes zoomIn{from{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}50%{opacity:1}}@keyframes zoomIn{from{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}50%{opacity:1}}.zoomIn{-webkit-animation-name:zoomIn;animation-name:zoomIn}@-webkit-keyframes zoomInDown{from{opacity:0;-webkit-transform:scale3d(.1,.1,.1)translate3d(0,-1000px,0);transform:scale3d(.1,.1,.1)translate3d(0,-1000px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475)translate3d(0,60px,0);transform:scale3d(.475,.475,.475)translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}@keyframes zoomInDown{from{opacity:0;-webkit-transform:scale3d(.1,.1,.1)translate3d(0,-1000px,0);transform:scale3d(.1,.1,.1)translate3d(0,-1000px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475)translate3d(0,60px,0);transform:scale3d(.475,.475,.475)translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}.zoomInDown{-webkit-animation-name:zoomInDown;animation-name:zoomInDown}@-webkit-keyframes zoomInLeft{from{opacity:0;-webkit-transform:scale3d(.1,.1,.1)translate3d(-1000px,0,0);transform:scale3d(.1,.1,.1)translate3d(-1000px,0,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475)translate3d(10px,0,0);transform:scale3d(.475,.475,.475)translate3d(10px,0,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}@keyframes zoomInLeft{from{opacity:0;-webkit-transform:scale3d(.1,.1,.1)translate3d(-1000px,0,0);transform:scale3d(.1,.1,.1)translate3d(-1000px,0,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475)translate3d(10px,0,0);transform:scale3d(.475,.475,.475)translate3d(10px,0,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}.zoomInLeft{-webkit-animation-name:zoomInLeft;animation-name:zoomInLeft}@-webkit-keyframes zoomInRight{from{opacity:0;-webkit-transform:scale3d(.1,.1,.1)translate3d(1000px,0,0);transform:scale3d(.1,.1,.1)translate3d(1000px,0,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475)translate3d(-10px,0,0);transform:scale3d(.475,.475,.475)translate3d(-10px,0,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}@keyframes zoomInRight{from{opacity:0;-webkit-transform:scale3d(.1,.1,.1)translate3d(1000px,0,0);transform:scale3d(.1,.1,.1)translate3d(1000px,0,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475)translate3d(-10px,0,0);transform:scale3d(.475,.475,.475)translate3d(-10px,0,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}.zoomInRight{-webkit-animation-name:zoomInRight;animation-name:zoomInRight}@-webkit-keyframes zoomInUp{from{opacity:0;-webkit-transform:scale3d(.1,.1,.1)translate3d(0,1000px,0);transform:scale3d(.1,.1,.1)translate3d(0,1000px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475)translate3d(0,-60px,0);transform:scale3d(.475,.475,.475)translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}@keyframes zoomInUp{from{opacity:0;-webkit-transform:scale3d(.1,.1,.1)translate3d(0,1000px,0);transform:scale3d(.1,.1,.1)translate3d(0,1000px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475)translate3d(0,-60px,0);transform:scale3d(.475,.475,.475)translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}.zoomInUp{-webkit-animation-name:zoomInUp;animation-name:zoomInUp}@-webkit-keyframes zoomOut{from{opacity:1}50%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}to{opacity:0}}@keyframes zoomOut{from{opacity:1}50%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}to{opacity:0}}.zoomOut{-webkit-animation-name:zoomOut;animation-name:zoomOut}@-webkit-keyframes zoomOutDown{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475)translate3d(0,-60px,0);transform:scale3d(.475,.475,.475)translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}to{opacity:0;-webkit-transform:scale3d(.1,.1,.1)translate3d(0,2000px,0);transform:scale3d(.1,.1,.1)translate3d(0,2000px,0);-webkit-transform-origin:center bottom;transform-origin:center bottom;-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}@keyframes zoomOutDown{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475)translate3d(0,-60px,0);transform:scale3d(.475,.475,.475)translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}to{opacity:0;-webkit-transform:scale3d(.1,.1,.1)translate3d(0,2000px,0);transform:scale3d(.1,.1,.1)translate3d(0,2000px,0);-webkit-transform-origin:center bottom;transform-origin:center bottom;-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}.zoomOutDown{-webkit-animation-name:zoomOutDown;animation-name:zoomOutDown}@-webkit-keyframes zoomOutLeft{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475)translate3d(42px,0,0);transform:scale3d(.475,.475,.475)translate3d(42px,0,0)}to{opacity:0;-webkit-transform:scale(.1)translate3d(-2000px,0,0);transform:scale(.1)translate3d(-2000px,0,0);-webkit-transform-origin:left center;transform-origin:left center}}@keyframes zoomOutLeft{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475)translate3d(42px,0,0);transform:scale3d(.475,.475,.475)translate3d(42px,0,0)}to{opacity:0;-webkit-transform:scale(.1)translate3d(-2000px,0,0);transform:scale(.1)translate3d(-2000px,0,0);-webkit-transform-origin:left center;transform-origin:left center}}.zoomOutLeft{-webkit-animation-name:zoomOutLeft;animation-name:zoomOutLeft}@-webkit-keyframes zoomOutRight{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475)translate3d(-42px,0,0);transform:scale3d(.475,.475,.475)translate3d(-42px,0,0)}to{opacity:0;-webkit-transform:scale(.1)translate3d(2000px,0,0);transform:scale(.1)translate3d(2000px,0,0);-webkit-transform-origin:right center;transform-origin:right center}}@keyframes zoomOutRight{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475)translate3d(-42px,0,0);transform:scale3d(.475,.475,.475)translate3d(-42px,0,0)}to{opacity:0;-webkit-transform:scale(.1)translate3d(2000px,0,0);transform:scale(.1)translate3d(2000px,0,0);-webkit-transform-origin:right center;transform-origin:right center}}.zoomOutRight{-webkit-animation-name:zoomOutRight;animation-name:zoomOutRight}@-webkit-keyframes zoomOutUp{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475)translate3d(0,60px,0);transform:scale3d(.475,.475,.475)translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}to{opacity:0;-webkit-transform:scale3d(.1,.1,.1)translate3d(0,-2000px,0);transform:scale3d(.1,.1,.1)translate3d(0,-2000px,0);-webkit-transform-origin:center bottom;transform-origin:center bottom;-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}@keyframes zoomOutUp{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475)translate3d(0,60px,0);transform:scale3d(.475,.475,.475)translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}to{opacity:0;-webkit-transform:scale3d(.1,.1,.1)translate3d(0,-2000px,0);transform:scale3d(.1,.1,.1)translate3d(0,-2000px,0);-webkit-transform-origin:center bottom;transform-origin:center bottom;-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}.zoomOutUp{-webkit-animation-name:zoomOutUp;animation-name:zoomOutUp}@-webkit-keyframes slideInDown{from{-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0);visibility:visible}to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}@keyframes slideInDown{from{-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0);visibility:visible}to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.slideInDown{-webkit-animation-name:slideInDown;animation-name:slideInDown}@-webkit-keyframes slideInLeft{from{-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0);visibility:visible}to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}@keyframes slideInLeft{from{-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0);visibility:visible}to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.slideInLeft{-webkit-animation-name:slideInLeft;animation-name:slideInLeft}@-webkit-keyframes slideInRight{from{-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0);visibility:visible}to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}@keyframes slideInRight{from{-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0);visibility:visible}to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.slideInRight{-webkit-animation-name:slideInRight;animation-name:slideInRight}@-webkit-keyframes slideInUp{from{-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0);visibility:visible}to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}@keyframes slideInUp{from{-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0);visibility:visible}to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.slideInUp{-webkit-animation-name:slideInUp;animation-name:slideInUp}@-webkit-keyframes slideOutDown{from{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}to{visibility:hidden;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}}@keyframes slideOutDown{from{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}to{visibility:hidden;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}}.slideOutDown{-webkit-animation-name:slideOutDown;animation-name:slideOutDown}@-webkit-keyframes slideOutLeft{from{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}to{visibility:hidden;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}}@keyframes slideOutLeft{from{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}to{visibility:hidden;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}}.slideOutLeft{-webkit-animation-name:slideOutLeft;animation-name:slideOutLeft}@-webkit-keyframes slideOutRight{from{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}to{visibility:hidden;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}}@keyframes slideOutRight{from{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}to{visibility:hidden;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}}.slideOutRight{-webkit-animation-name:slideOutRight;animation-name:slideOutRight}@-webkit-keyframes slideOutUp{from{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}to{visibility:hidden;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}}@keyframes slideOutUp{from{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}to{visibility:hidden;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}}.slideOutUp{-webkit-animation-name:slideOutUp;animation-name:slideOutUp}.animated{-webkit-animation-duration:1s;animation-duration:1s;-webkit-animation-fill-mode:both;animation-fill-mode:both}.animated.infinite{-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite}.animated.delay-1s{-webkit-animation-delay:1s;animation-delay:1s}.animated.delay-2s{-webkit-animation-delay:2s;animation-delay:2s}.animated.delay-3s{-webkit-animation-delay:3s;animation-delay:3s}.animated.delay-4s{-webkit-animation-delay:4s;animation-delay:4s}.animated.delay-5s{-webkit-animation-delay:5s;animation-delay:5s}.animated.fast{-webkit-animation-duration:800ms;animation-duration:800ms}.animated.faster{-webkit-animation-duration:500ms;animation-duration:500ms}.animated.slow{-webkit-animation-duration:2s;animation-duration:2s}.animated.slower{-webkit-animation-duration:3s;animation-duration:3s}@media(prefers-reduced-motion){.animated{-webkit-animation:unset!important;animation:unset!important;-webkit-transition:none!important;transition:none!important}}html{overflow-y:scroll}.btn-main.active{box-shadow:inset 0 3px 8px rgba(0,0,0,.125);background-color:#e6e6e6;color:#333}.btn-main:hover:not(.active){color:#007bff}.btn-main{background:linear-gradient(to bottom,#fff,#f2f2f2);background-repeat:repeat-x;border-left:1px solid rgba(255,255,255,.75);border-right:1px solid rgba(0,0,0,.1);border:1px solid #d4d4d4;border-radius:4px;box-shadow:0 1px 4px rgba(0,0,0,.065);padding:10px;text-decoration:none;text-shadow:0 1px 0 #fff;text-align:center;color:#555;flex-grow:1!important;max-width:160px}@media(max-width:850px){.btn-main{max-width:inherit}}@media(max-width:650px){.btn-group{flex-direction:column}}.btn-group{padding-left:10px;padding-right:10px;width:100%;justify-content:center}.container{max-width:900px}table{box-shadow:0 2px 2px rgba(0,0,0,.14),0 1px 5px rgba(0,0,0,.12),0 3px 1px -2px rgba(0,0,0,.2);width:100%;border-radius:.2rem;overflow:auto}table+*{margin-top:1.5em}table th,table td{text-align:left}[dir=rtl] table th,[dir=rtl] table td{text-align:right}table th,table>tbody:only-child>tr:first-child td{padding:10px;color:#fff;vertical-align:top}table td{padding:10px 15px;border-top:.1rem solid rgba(0,0,0,7%);vertical-align:top}table tr:first-child td{border-top:0}table a{word-break:normal}table>thead>tr,table>tbody:only-child>tr:first-child{background-color:rgba(0,0,0,.54)}hr+hr{display:none}main{counter-reset:h2counter}h1.title{text-align:center;margin-top:40px;font-size:5em;font-weight:bolder}main h2{page-break-after:avoid;font-size:1.2em;counter-reset:h3counter}main h3{page-break-after:avoid;font-size:1.2em;counter-reset:h4counter}main h4{page-break-after:avoid;font-size:1.2em}main h2:before{content:counter(h2counter)".  ";counter-increment:h2counter}main h3:before{content:counter(h2counter)"." counter(h3counter)".  ";counter-increment:h3counter}main h4:before{content:counter(h2counter)"." counter(h3counter)"." counter(h4counter)".  ";counter-increment:h4counter}main p,main ul,main pre{page-break-inside:avoid;padding-left:40px}main table{page-break-inside:avoid;margin-left:40px;width:calc(100% - 40px)}table.diff td{padding:0;margin:0}main pre{margin-left:40px;padding:5px;transition:box-shadow 2s cubic-bezier(.165,.84,.44,1)}main pre:hover,main pre:focus{box-shadow:0 8px 17px rgba(0,0,0,.2),0 6px 20px rgba(0,0,0,.15)}@media(max-width:490px){.icon-hori-ver-arrow:before{content:"\f338"!important}}.md-grid{max-width:900px;margin-right:auto;margin-left:auto}.md-sidebar{display:none}@media only screen and (min-width:1500px){.md-sidebar{display:block;position:absolute;top:4.8rem;bottom:0;overflow-y:auto;overflow-x:hidden;margin-left:900px;padding-right:1rem}}.md-source:hover{opacity:.7}a.md-source:hover{text-decoration:none}.md-source{display:block;padding-right:1.2rem;font-size:1.3rem;line-height:1.2;white-space:nowrap;transition:opacity .25s;padding-bottom:1em}@media(min-width:1400px){.md-source{position:absolute;right:0;z-index:10}}.md-source__icon{display:inline-block;width:4.8rem;content:"";vertical-align:middle}.md-source__icon svg{width:2.4rem;margin-left:1.2rem}.md-source__icon+.md-source__repository{margin-left:-4.4rem;padding-left:4rem}.md-source__repository{display:inline-block;max-width:100%;margin-left:1.2rem;font-weight:700;text-overflow:ellipsis;vertical-align:middle;overflow:hidden}.md-source::after{display:inline-block;content:"";vertical-align:middle}[data-md-state=done] .md-source__facts{animation:md-source__facts--done .25s ease-in}.md-source__facts{font-size:1.1rem;font-weight:700;list-style-type:none;opacity:.75;margin:0;padding:0;overflow:hidden;text-align:start}.progress{background:linear-gradient(to right,#F9EC31 var(--scroll),transparent 0);background-repeat:no-repeat;position:fixed;width:100%;height:4px;top:0;z-index:1}@media print{body{display:table}header{display:table-header-group}#mainContent{display:table-row-group}footer{display:table-footer-group}table,h2+p,h3+p,h4+p,h5+p,code{page-break-inside:avoid}}*::-webkit-scrollbar{width:12px}*::-webkit-scrollbar-track{border-radius:8px;background-color:#e7e7e7;border:1px solid #cacaca;box-shadow:inset 0 0 6px rgba(0,0,0,.3)}*::-webkit-scrollbar-thumb{border-radius:8px;background-color:#363636} \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 0000000..82322e9 --- /dev/null +++ b/sitemap.xml @@ -0,0 +1,63 @@ + + + + https://homieiot.github.io/offline.html + + https://homieiot.github.io/specification/ + + https://homieiot.github.io/specification/spec-core-develop-changes/ + + https://homieiot.github.io/specification/spec-core-develop-diff/ + + https://homieiot.github.io/specification/spec-core-develop/ + + https://homieiot.github.io/specification/spec-core-v1_5_0/ + + https://homieiot.github.io/specification/spec-core-v2_0_0-changes/ + + https://homieiot.github.io/specification/spec-core-v2_0_0-diff/ + + https://homieiot.github.io/specification/spec-core-v2_0_0/ + + https://homieiot.github.io/specification/spec-core-v2_0_1-changes/ + + https://homieiot.github.io/specification/spec-core-v2_0_1-diff/ + + https://homieiot.github.io/specification/spec-core-v2_0_1/ + + https://homieiot.github.io/specification/spec-core-v3_0_0-changes/ + + https://homieiot.github.io/specification/spec-core-v3_0_0-diff/ + + https://homieiot.github.io/specification/spec-core-v3_0_0/ + + https://homieiot.github.io/specification/spec-core-v3_0_1-changes/ + + https://homieiot.github.io/specification/spec-core-v3_0_1-diff/ + + https://homieiot.github.io/specification/spec-core-v3_0_1/ + + https://homieiot.github.io/specification/spec-core-v4_0_0-changes/ + + https://homieiot.github.io/specification/spec-core-v4_0_0-diff/ + + https://homieiot.github.io/specification/spec-core-v4_0_0/ + + https://homieiot.github.io/categories/ + + https://homieiot.github.io/ + + https://homieiot.github.io/extensions/ + + https://homieiot.github.io/get_involved/ + + https://homieiot.github.io/implementations/ + + https://homieiot.github.io/license/ + + https://homieiot.github.io/tags/ + + https://homieiot.github.io/tools/ + + diff --git a/specification/index.html b/specification/index.html new file mode 100644 index 0000000..873c30d --- /dev/null +++ b/specification/index.html @@ -0,0 +1,1126 @@ + + + + + + + + + The Homie convention + + + + + + + + + + + + + +
+
+ +
+ + + +
+
+ homieiot/convention +
+
+
+ + + +
+ +
+ + + + + + + + + + +
+
+
+

Homie: An MQTT Convention for IoT/M2M

+
+
+
+ +
+ + + +
+ + License: CCA 4.0
+ Version: + + + + [develop] + + + + [v1.5.0] + + + + [v2.0.0] + + + + [v2.0.1] + + + + [v3.0.0] + + + + [v3.0.1] + + + + [v4.0.0] + +
+ + Changes: [Diff to previous]
+ + Release date: 06. July 2024 +
+
+ +
+ Frequently asked questions + +

How do I query/request a property?

+

You don’t. +The MQTT protocol does not implement the request-reply but rather the publish-subscribe messaging pattern. +The Homie convention follows the publish-subscribe principle by publishing data as retained messages on a regular basis. +You might want to rethink the design of your application - in most scenarios a regularly updated information is sufficient.

+

Workaround: You are free to implement your own ideas on top of the basic structure of the Homie convention. +You could either implement a get getter topic and its logic to trigger a value update, or you may exploit the concept of Homie properties and define a settable property to trigger a value update.

+ + +
+ +
+

License

+

By exercising the Licensed Rights (defined on https://creativecommons.org/licenses/by/4.0/), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions.

+
+ +
+

Table of Contents

+
+ + +

+

MQTT Restrictions

+

Homie communicates through MQTT and is hence based on the basic principles of MQTT topic publication and subscription.

+

Topic IDs

+

An MQTT topic consists of one or more topic levels, separated by the slash character (/). +A topic level ID MAY ONLY contain lowercase letters from a to z, numbers from 0 to 9 as well as the hyphen character (-).

+

The special character $ is used and reserved for Homie attributes.

+

QoS and retained messages

+

The recommended QoS level is Exactly once (QoS 2) (except for non-retained, see below).

+
    +
  • All messages MUST be sent as retained, UNLESS stated otherwise.
  • +
  • Controllers setting values for device properties publish to the Property set topic with non-retained messages only.
  • +
  • Controllers setting values for non-retained device properties should publish to the Property /set topic with a QoS of At most once (QoS 0).
  • +
  • Devices publishing values for their non-retained properties must use non-retained messages, with a QoS of At most once (QoS 0).
  • +
+

For QoS details see the explanation.

+

Last Will

+

Homie requires the last will (LWT) to set the homie / 5 / [device ID] / $state attribute to the value lost, see Device Lifecycle. +MQTT only allows one last will message per connection, but since a device can have children, the LWT message MUST be set on the +root device (the device at the root of the parent-child tree).

+

Empty string values

+

MQTT will treat an empty string payload as a “delete” instruction for the topic, therefor an +empty string value is represented by a 1-character string containing a single byte value 0 (Hex: 0x00, Dec: 0).

+

The empty string (passed as an MQTT payload) can only occur in 3 places;

+
    +
  • homie / 5 / [device ID] / [node ID] / [property ID]; reported property values (for string types)
  • +
  • homie / 5 / [device ID] / [node ID] / [property ID] / set; the topic to set properties (of string types)
  • +
  • homie / 5 / [device ID] / [node ID] / [property ID] / $target; the target property value (for string types)
  • +
+

This convention specifies no way to represent an actual value of a 1-character string with a single byte 0. If a device +needs this, then it should provide an escape mechanism on the application level.

+

Payloads

+
    +
  • Every MQTT message payload MUST be sent as a UTF-8 encoded string
  • +
  • The message MUST NOT include the UTF-8 BOM
  • +
  • The value published as payload MUST be valid for the respective property/attribute type as per the list below
  • +
+

String

+
    +
  • String types are limited to 268,435,456 characters
  • +
  • An empty string ("") is a valid payload
  • +
+

Integer

+
    +
  • Integer types are string literal representations of 64-bit signed whole numbers
  • +
  • Integers range from -9,223,372,036,854,775,808 (-263) to 9,223,372,036,854,775,807 (263-1)
  • +
  • The payload may only contain whole numbers and the negation character “-”. No other characters including spaces (" “) are permitted
  • +
  • A string with just a negation sign (”-") is not a valid payload
  • +
  • An empty string ("") is not a valid payload
  • +
+

Float

+
    +
  • Float types are string literal representations of 64-bit signed floating point numbers
  • +
  • Floats range from +/-(2^-1074) to +/-((2 - 2^-52) * 2^1023)
  • +
  • The payload may only contain whole numbers, the negation character “-”, the exponent character “e” or “E” and the decimal separator “.”, no other characters, including spaces (" “) are permitted
  • +
  • The dot character (”.") is the decimal separator (used if necessary) and may only have a single instance present in the payload
  • +
  • Representations of numeric concepts such as “NaN” (Not a Number) and “Infinity” are not a valid payload
  • +
  • A string with just a negation sign ("-") is not a valid payload
  • +
  • An empty string ("") is not a valid payload
  • +
+

Boolean

+
    +
  • Booleans must be converted to the string literals “true” or “false”
  • +
  • Representation is case sensitive, e.g. “TRUE” or “FALSE” are not valid payloads.
  • +
  • An empty string ("") is not a valid payload
  • +
+

Enum

+
    +
  • Enum payloads must be one of the values specified in the format definition of the property
  • +
  • Enum payloads are case sensitive, e.g. “Car” will not match a format definition of “car”
  • +
  • Leading- and trailing-whitespace is significant, e.g. “Car” will not match " Car".
  • +
  • An empty string ("") is not a valid payload
  • +
+

Color

+
    +
  • Color payload validity varies depending on the property format definition of either “rgb”, “hsv”, or “xyz”
  • +
  • All payload types contain comma-separated data of differing restricted ranges. The first being the type, followed by numbers. The numbers must conform to the float format
  • +
  • The encoded string may only contain the float numbers and the comma character “,”, no other characters are permitted, including spaces (" “)
  • +
  • Payloads for type “rgb” contain 3 comma-separated values of floats (r, g, b) with a valid range between 0 and 255 (inclusive). e.g. "rgb,100,100,100"
  • +
  • Payloads for type “hsv” contain 3 comma-separated values of floats. The first number (h) has a range of 0 to 360 (inclusive), and the second and third numbers (s and v) have a range of 0 to 100 (inclusive). e.g. "hsv,300,50,75"
  • +
  • Payloads for type “xyz” contain 2 comma separated values of floats (x, y) with a valid range between 0 and 1 (inclusive). The “z” value can be calculated via z=1-x-y and is therefore not transmitted. (see CIE_1931_color_space). e.g. "xyz,0.25,0.34"
  • +
  • An empty string (”") is not a valid payload
  • +
+

DateTime

+ +

Duration

+
    +
  • Duration payloads must use the ISO 8601 duration format
  • +
  • The format is PTxHxMxS, where: +P: Indicates a period/duration (required). +T: Indicates a time (required). +xH: Hours, where x represents the number of hours (optional). +xM: Minutes, where x represents the number of minutes (optional). +xS: Seconds, where x represents the number of seconds (optional).
  • +
  • Examples: PT12H5M46S (12 hours, 5 minutes, 46 seconds), PT5M (5 minutes)
  • +
  • An empty string ("") is not a valid payload
  • +
+

JSON

+
    +
  • Contains a JSON string for transporting complex data formats that cannot be exposed as single value attributes.
  • +
  • The payload MUST be either a JSON-Array or JSON-Object type, for other types the standard Homie types should be used.
  • +
+

Base Topic

+

The root topic in this convention is "homie/5/". +If this root topic does not suit your needs (in case of, e.g., a public broker or because of branding), +you can change the first segment, but the "/5/" segment must be retained. This allows controllers +to subscribe to only the devices they are compatible with.

+

Auto-Discovery

+

Homie 5 controllers must by default perform auto-discovery on the wildcard topic "+/5/+/$state". +Controllers are free to restrict discovery to a specific root topic, configurable by the user. +A zero length payload published on the $state topic indicates a device removal, see device lifecycle.

+

Topology and structure

+

Devices: +An instance of a physical piece of hardware is called a device. +For example, a car, an Arduino/ESP8266, or a coffee machine. +Within the convention devices can be modelled to have children. For example, bridge +devices; a zwave bridge device (the parent) exposes many child devices (the +zwave devices). There is no depth limit set on additionally nested children.

+

Nodes: +A device can expose multiple nodes. +Nodes are independent or logically separable parts of a device. +For example, a car might expose a wheels node, an engine node, and a lights node.

+

Properties: +A node can have multiple properties. +Properties represent basic characteristics of the node/device, often given as numbers or finite states. +For example, the wheels node might expose an angle property. +The engine node might expose a speed, direction, and temperature property. +The lights node might expose an intensity and a color property.

+

Attributes: +Devices, nodes and properties have specific attributes characterizing them. +Attributes are represented by a topic identifier starting with $. +The precise definition of attributes is important for the automatic discovery of devices following the Homie convention.

+

Examples: A device might have an IP attribute, a node will have a name attribute, and a property will have a unit attribute.

+

Devices

+
    +
  • homie / 5 / [device ID]: this is the base topic of a device. +Each device must have a unique device ID that adheres to the ID format.
  • +
+

Device Attributes

+

The following topic structure will be used to expose the device attributes:

+
    +
  • homie / 5 / [device ID] / [$device-attribute]:
  • +
+

Devices have the following attributes:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
AttributeRequiredDescription
$stateyesReflects the current state of the device. See Device Lifecycle
$descriptionyesThe description document (JSON), describing the device, nodes, and properties of this device. Important: this value may only change when the device $state is either init, disconnected, or lost.
$lognoA topic that allows devices to log messages. See Logging
+

The JSON description document is a JSON object with the following fields;

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeRequiredDefaultNullableDescription
homiestringyesnoThe implemented Homie convention version, without the “patch” level. So the format is "5.x", where the 'x' is the minor version.
versionintegeryesnoThe version of the description document. Whenever the document changes, a new higher version must be assigned. This does not need to be sequential, eg. a timestamp could be used.
nodesobjectno{}noThe Nodes the device exposes. An object containing the Nodes, indexed by their ID. Defaults to an empty object.
namestringyesnoFriendly name of the device.
typestringnonoType of Device. Please ensure proper namespacing to prevent naming collisions.
childrenarray-stringsno[]noArray of ID’s of child devices. Defaults to an empty array.
rootstringyes/nonoID of the root parent device. Required if the device is NOT the root device, MUST be omitted otherwise.
parentstringyes/nosame as rootnoID of the parent device. Required if the parent is NOT the root device. Defaults to the value of the root property.
extensionsarray-stringsno[]noArray of supported extensions. Defaults to an empty array.
+

For example, a device with an ID of super-car that comprises of a wheels, engine, and a lights node would send:

+
homie/5/super-car/$state  "init"
+homie/5/super-car/$description  following JSON document;
+
      {
+        "homie": "5.0",
+        "name": "Supercar",
+        "version": 7,
+        "nodes": { 
+          "wheels": { ... },
+          "engine": { ... },
+          "lights": { ... }
+        }
+      }
+

Device hierarchy

+

Devices can be organized in parent-child relationships. These are expressed via the device +attributes root, parent, and children. In any parent-child tree, there is only one +“root” device, which is the top-level device that has no parent, but only children.

+

Example: a ZWave bridge (id = "bridge"), which exposes a ZWave device with a dual-relay (id = "dualrelay"), +which respectively control Light1 (id = "light1") and Light2 (id = "light2"). So there are 4 devices in total. +Then these are the attribute values:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
idchildrenrootparent
Zwave bridge“bridge”[“dualrelay”]
Zwave relay“dualrelay”[“light1”, “light2”]“bridge”
First light“light1”“bridge”“dualrelay”
Second light“light2”“bridge”“dualrelay”
+

To monitor the state of child devices in this tree 2 topic subscriptions are needed. The $state attribute of the device itself, as well as the $state attribute of its root device. +Because if the root device loses its connection to the MQTT server, the last will (LWT), will set its $state attribute to "lost", but it will not update the child-device states. Hence the need for 2 topic subscriptions.

+

The state of any device should be determined as follows:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
has a root setroot statedevice state
non.a.device state is the $state attribute of the device itself
yesnot "lost"device state is the $state attribute of the device itself
yes"lost"device state is "lost" ($state attribute of the root device)
+

Device Lifecycle

+

The $state device attribute represents the current state of the device. A device exists once a valid value is set in the $state attribute. It doesn’t mean the device is complete and valid (yet), but it does mean it exists.

+

There are 5 possible state values:

+
    +
  • init: this is the state the device is in when it is connected to the MQTT broker, but has not yet sent all Homie messages and is not yet ready to operate. +This state is optional and may be sent if the device takes a long time to initialize, but wishes to announce to consumers that it is coming online. +A device may fall back into this state to do some reconfiguration.
  • +
  • ready: this is the state the device is in when it is connected to the MQTT broker and has sent all Homie messages describing the device attributes, nodes, properties, and their values. The device has subscribed to all appropriate /set topics and is ready to receive messages.
  • +
  • disconnected: this is the state the device is in when it is cleanly disconnected from the MQTT broker. +You must send this message before cleanly disconnecting.
  • +
  • sleeping: this is the state the device is in when the device is sleeping. +You have to send this message before sleeping.
  • +
  • lost: this is the state the device is in when the device has been “badly” disconnected. Important: If a root-device $state is "lost" then the state of every child device in its tree is also "lost". +You must define this message as the last will (LWT) for root devices.
  • +
+

In order to permanently remove a device the following steps should be performed in order:

+
    +
  1. remove the retained $state attribute from the broker by publishing a zero length payload message to its topic. The device will cease to exist.
  2. +
  3. any other retained attributes or property values should be cleared via the same method afterwards.
  4. +
+

Nodes

+
    +
  • homie / 5 / [device ID] / [node ID]: this is the base topic of a node. +Each node must have a unique node ID on a per-device basis which adheres to the ID format.
  • +
+

Node Attributes

+

There are no node attributes in MQTT topics for this level.

+

The Node object itself is described in the homie / 5 / [device ID] / $description JSON document. The Node object has the following fields:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeRequiredDefaultNullableDescription
namestringyesnoFriendly name of the Node.
typestringnonoType of Node. Please ensure proper namespacing to prevent naming collisions.
propertiesobjectno{}noThe Properties the Node exposes. An object containing the Properties, indexed by their ID. Defaults to an empty object.
+

For example, our engine node would look like this:

+
      ...
+      "engine": {
+        "name": "Car engine",
+        "properties": {
+          "speed": { ... },
+          "direction": { ... },
+          "temperature": { ... }
+        }
+      }
+      ...
+

Properties

+
    +
  • homie / 5 / [device ID] / [node ID] / [property ID]: this is the base topic of a property. +Each property must have a unique property ID on a per-node basis which adheres to the ID format.
  • +
+

Property Attributes

+ + + + + + + + + + + + + + + + + + + + +
AttributeRequiredDescription
yesA property value (e.g. a sensor reading) is directly published to the property topic, e.g.: homie/5/super-car/engine/temperature → "21.5"
$targetnoDescribes an intended state change. The $target attribute must either be used for every value update (including the initial one), or it must never be used.
+

The Property object itself is described in the homie / 5 / device ID / $description JSON document. The Property object has the following fields:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeRequiredDefaultNullableDescription
namestringyesnoFriendly name of the Property.
datatypestringyesnoThe data type. See Payloads. Any of the following values: "integer", "float", "boolean", "string", "enum", "color", "datetime", "duration", "json".
formatstringsee formatssee formatsnoSpecifies restrictions or options for the given data type.
settablebooleannofalsenoWhether the Property is settable.
retainedbooleannotruenoWhether the Property is retained.
unitstringnonoUnit of this property. See units.
+

For example, our temperature property would look like this in the device/node description document:

+
      ...
+      "temperature": {
+        "name": "Engine temperature",
+        "unit": "°C",
+        "datatype": "float",
+        "format": "-20:120"
+      }
+      ...
+

And the following MQTT topic with the reported property value:

+
homie/5/super-car/engine/temperature  "21.5"
+

Settable and retained properties

+

Properties can be settable and/or retained. For example, you don’t want your temperature +property to be settable in case of a temperature sensor (like the car example), but it should be +settable in the case of a thermostat setpoint.

+

A property is retained by default. A non-retained property would be useful for momentary events +(e.g. doorbell pressed). See also QoS settings.

+

A combination of the settable and retained flags compiles into this list:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
retainedsettabledescription
yesyesThe node publishes a property state and can receive commands for the property (by a controller or other party) (lamp power)
yesno(default) The node publishes a property state (temperature sensor)
noyesThe node publishes momentary events and can receive commands for the property from a controller (brew coffee)
nonoThe node publishes momentary events (doorbell pressed)
+

Formats

+

The format attribute specifies restrictions or options for the given data type. User interfaces can derive hints from +the formats for displaying values.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeRequiredDefaultDescription
floatno:[min]:[max][:step] where min and max are the respective minimum and maximum (inclusive) allowed values, both represented in the format for float types. Eg. 10.123:15.123. If the minimum and/or maximum are missing from the format, then they are open-ended, so 0: allows a value >= 0.
The optional step determines the step size, eg. 2:6:2 will allow values 2, 4, and 6. It must be greater than 0. The base for calculating a proper value based on step should be min, max, or the current property value (in that order). The implementation should round property values to the nearest step (which can be outside the min/max range). The min/max validation must be done after rounding.
integerno:[min]:[max][:step] where min and max are the respective minimum and maximum (inclusive) allowed values, both represented in the format for integer types. Eg. 5:35. If the minimum and/or maximum are missing from the format, then they are open-ended, so :10 allows a value <= 10.
The optional step determines the step size, eg. 2:6:2 will allow values 2, 4, and 6. It must be greater than 0. The base for calculating a proper value based on step should be min, max, or the current property value (in that order). The implementation should round property values to the nearest step (which can be outside the min/max range). The min/max validation must be done after rounding.
enumyesA comma-separated list of non-quoted values. Eg. value1,value2,value3. Leading- and trailing whitespace is significant. Individual values can not be an empty string, hence at least 1 value must be specified in the format.
coloryesA comma-separated list of color formats supported; rgb, hsv, and/or xyz. The formats should be listed in order of preference (most preferred first, least preferred last). See the color type for the resulting value formats. E.g. a device supporting RGB and HSV, where RGB is preferred, would have its format set to "rgb,hsv".
booleannofalse,trueIdentical to an enum with 2 entries. The first represents the false value and the second is the true value. Eg. close,open or off,on. If provided, then both entries must be specified. Important: the format does NOT specify valid payloads, they are descriptions of the valid payloads false and true.
jsonno{"anyOf": [{"type": "array"},{"type": "object"}]}A JSONschema definition, which is added as a string (escaped), NOT as a nested json-object. See JSON considerations, for some ideas wrt compatibility. If a client fails to parse/compile the JSONschema, then it should ignore the given schema and fall back to the default schema.
+

Units

+

Recommended unit strings:

+
    +
  • °C: Degree Celsius (see ‘Degree’ for encoding)
  • +
  • °F: Degree Fahrenheit (see ‘Degree’ for encoding)
  • +
  • °: Degree + +
  • +
  • L: Liter
  • +
  • gal: Gallon
  • +
  • V: Volts
  • +
  • W: Watt
  • +
  • kW: Kilowatt
  • +
  • kWh: Kilowatt-hour
  • +
  • A: Ampere
  • +
  • Hz: Hertz
  • +
  • rpm: Revolutions per minute
  • +
  • %: Percent
  • +
  • m: Meter
  • +
  • : Cubic meter + +
  • +
  • ft: Feet
  • +
  • m/s: Meters per Second
  • +
  • kn: Knots
  • +
  • Pa: Pascal
  • +
  • psi: PSI
  • +
  • ppm: Parts Per Million
  • +
  • s: Seconds
  • +
  • min: Minutes
  • +
  • h: Hours
  • +
  • lx: Lux
  • +
  • K: Kelvin
  • +
  • MK⁻¹: Mired +
      +
    • Character ‘⁻’ is Unicode: U+207B, Hex: 0xe2 0x81 0xbb, Dec: 226 129 187
    • +
    • Character ‘¹’ is Unicode: U+00B9, Hex: 0xc2 0xb9, Dec: 194 185
    • +
    +
  • +
  • #: Count or Amount
  • +
+

The non-ASCII characters are specified as Unicode codepoints and the UTF-8 byte sequence that represents them. Since the same characters can be created in many visually similar ways it is important to stick to the exact byte sequences to enable proper interoperability.

+

You are not limited to the recommended values, although they are the only well known ones that will have to be recognized by any Homie consumer.

+

Target attribute

+

The $target attribute for properties allows a device to communicate an intended state change of a property. This serves 2 main +purposes;

+
    +
  1. closing the control loop for a controller setting a value (if the property is settable).
  2. +
  3. feedback in case a change is not instantaneous (e.g. a light that slowly dimms over a longer period, or a +motorized valve that takes several minutes to fully open)
  4. +
+

If implemented, then a device must first update the $target attribute, then start the transition (with +optional state-value updates during the transition), and when done update the property value to match the +$target value (functional equivalent, not necessarily a byte-by-byte equality).

+

If a new target is received (and accepted) from a controller by publishing to the property’s set topic, then the exact value received must be published to the $target topic (byte-by-byte equality). To allow for closing the control loop.

+

Notes:

+
    +
  • a controller can only assume that the command it send to the set topic was received and accepted. Not necessarily that it will ever reach the target state, since if another controller updates the property again, it might never reach the target state.
  • +
  • The same goes for possible conversions (colors), rounding (number formats), etc. it will be very hard to check functional equivalence, since the value published may have a different format. So a controller should NOT implement a retry loop checking the final value. At best they should implement retries until the value set is being accepted.
  • +
  • Homie devices representing remote hardware (typically when bridging) should NOT set the $target attribute upon receiving a change from the hardware device. This is only allowed if the hardware explicitly distinguishes between current value and target value. This is to prevent a loop; e.g. a homie controller sets 100% as target, software instructs hardware to change, intermediate updates received from hardware; 20%, 40%, etc, should NOT overwrite the $target value, since that still is 100.
  • +
+

Property command topic

+
    +
  • homie / 5 / [device ID] / [node ID] / [property ID] / set: The device must subscribe to this topic if the property is settable (in the case of actuators for example).
  • +
+

A Homie controller publishes to the set command topic with non-retained messages only. See retained messages.

+

The assigned and processed payload must be reflected by the Homie device in the property topic homie / 5 / [device ID] / [node ID] / [property ID] or target attribute homie / 5 / [device ID] / [node ID] / [property ID] / $target as soon as possible. +This property state update not only informs other devices about the change but closes the control loop for the commanding controller, important for deterministic interaction with the client device.

+

To give an example: A kitchen-light device exposing the light node with a settable power property subscribes to the topic homie/5/kitchen-light/light/power/set for commands:

+
homie/5/kitchen-light/light/power/set  "true"
+

In response, the device will turn on the light and upon success update its power property state accordingly:

+
homie/5/kitchen-light/light/power  "true"
+

If the light were a dimmable light with a brightness property (0-100%), and it would be set to slowly dim over 5 seconds, then the $target attribute can be used (assuming once per second updates);

+
homie/5/kitchen-light/light/brightness/set  100
+homie/5/kitchen-light/light/brightness/$target  100
+homie/5/kitchen-light/light/brightness  20  (after 1 second)
+homie/5/kitchen-light/light/brightness  40  (after 2 seconds)
+homie/5/kitchen-light/light/brightness  60  (after 3 seconds)
+homie/5/kitchen-light/light/brightness  80  (after 4 seconds)
+homie/5/kitchen-light/light/brightness  100  (after 5 seconds)
+

Alert topic

+

Devices can raise alerts. Alerts are user facing messages that have an ID, they can be set and removed. +The alert topic is defined as;

+
    +
  • homie / 5 / [device ID] / $alert / [alert ID] → “alert message”
  • +
+

A device can raise a message on a specific ID. Once the alert is no longer usefull or has been resolved, it can be removed by deleting the topic. Alerts must be send as retained messages. The alert ID must have a valid ID format, where topic ID’s starting with $ are reserved for Homie usage.

+

Examples;

+
/homie/5/mydevid/$alert/childlost = "Sensor xyz in livingroom hasn't reported updates for 3 hours"
+/homie/5/mydevid/$alert/battery = "Battery is low, at 8%"
+

In the examples above, once the situation is resolved (the sensor comes back to live, or the batteries are replaced), the device will delete the topics again, indicating the alerts have been handled.

+

Broadcast Topic

+

Homie defines a broadcast topic, so a controller can broadcast a message to all Homie devices:

+
    +
  • homie / 5 / $broadcast / [subtopic]: where subtopic can be any topic with single or multiple levels. Each segement must adhere to the ID format.
  • +
+

The messages SHOULD be non-retained.

+

For example, you might want to broadcast an alert event with the alert reason as the payload. +Devices are then free to react or not. +In our case, every buzzer of your home automation system would start buzzing.

+
homie/5/$broadcast/alert  "Intruder detected"
+homie/5/$broadcast/security/alert  "Intruder detected"
+

Logging

+

Since devices may be resource constraint they might not have logging capabilities. Homie provides a specific +topic where devices can send log messages. The topic is defined as;

+
    +
  • homie / 5 / [device ID] / $log / [level]
  • +
+

The topic-value is the logged message, no sub-topics are allowed. +All log messages send should be non-retained. +The level is set according to the following table:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
leveldescription
debugdetailed information for troubleshooting purposes
infoinformational message, device is working as expected
warnsomething potentially harmful happened
erroran error happened, the device will continue to operate but functionality might be impaired
fatala non-recoverable error occured, operation of the device is likely suspended/stopped
+
homie/5/my-device/$log/warn  "battery low"
+homie/5/my-device/$log/error  "sensor value is out of range"
+

Note that MQTT is not meant to be a logging solution, and hence it should be used with care. The implementation should +try and limit the traffic on the MQTT bus. If devices implement messages and levels that can be “noisy”, then the +device should provide a configuration option to turn them off, to limit the bandwidth consumed.

+

Extensions

+

This convention only covers the discoverability of devices and their capabilities. +The aim is to have standardized MQTT topics for all kinds of complex scenarios. +A Homie device may therefore support extensions, defined in separate documents. +Every extension is identified by a unique ID.

+

The ID consists of the reverse domain name and a freely chosen suffix. +The proper term homie is reserved and must not be used as a suffix or as part of the domain name.

+

For example, an organization example.org wanting to add a feature our-feature would choose the extension ID org.example.our-feature.

+

Every extension must be published using a license. +The license can be chosen freely, even proprietary licenses are possible. +The recommended license is the CCA 4.0 since this is the license Homie itself uses.

+

Implementation notes

+

Device initialization

+

Some devices require knowledge of their settable retained properties to function properly. +The homie convention does not specify how to initialize/recover them e.g. after a power cycle. +A few common approaches are:

+
    +
  • A device can simply load default values from some configuration file.
  • +
  • A device can restore its previous state from some local storage. This is the recommended way.
  • +
  • A device may try to restore its state using MQTT. This can be done by subscribing to the respective channels. +The controller could set all properties of a device once it becomes ready. +An alternative way is to recover the state from other MQTT channels that are external to the Homie specification.
  • +
  • If a property is not critical for correctly functioning, there is no need to recover it.
  • +
+

Device reconfiguration

+

If a device wishes to modify any of its nodes or properties, it can

+
    +
  • disconnect and reconnect with other values, or
  • +
  • set $state=init and then modify any of the attributes.
  • +
+

Devices can remove old properties and nodes by deleting the respective MQTT topics by publishing an empty message +to those topics (an actual empty string on MQTT level, so NOT the escaped 0x00 byte, see also empty string values).

+

When adding many child devices, implementations should take care of not publishing too many parent-updates, since every controller would have to parse the description again and again.

+

Adding children

+

The recommended way to add child device is as follows:

+
    +
  1. first publish any child-devices, as any other device +
      +
    1. set child-device state to "init"
    2. +
    3. publish child-device details (including parent details in root and parent fields)
    4. +
    5. set child-device state to "ready"
    6. +
    +
  2. +
  3. update the parent device, as any other change +
      +
    1. set parent state to "init"
    2. +
    3. update parent description (add any child IDs to its children array)
    4. +
    5. set parent state to "ready"
    6. +
    +
  4. +
+

Be aware that due to MQTT message ordering the consistency at any stage in this process cannot be guaranteed.

+

Removing children

+

The recommended way to remove child device is as follows:

+
    +
  1. update the parent device +
      +
    1. set parent state to "init"
    2. +
    3. update parent description (remove any child IDs from its children array)
    4. +
    5. set parent state to "ready"
    6. +
    +
  2. +
  3. clear any child-device(s) topics, starting with the $state topic
  4. +
+

Be aware that due to MQTT message ordering the consistency at any stage in this process cannot be guaranteed.

+

Versioning

+

Some considerations related to versioning in this specification;

+
    +
  • compatibility is assumed to be major version only, so version 5 for this spec.
  • +
  • the base topic includes the major version. This allows controllers to only subscribe to devices they are +compatible with.
  • +
+

Backward compatibility

+
    +
  • backward compatibility: a v5 controller controlling a v5 device with a smaller minor version. Eg. a v5.3 +controller sending commands to a v5.0 device.
  • +
  • Controllers should be aware of unsupported features in older major or minor versions they subscribe to because the spec for that version is known.
  • +
+

Forward compatibility

+
    +
  • forward compatibility: a v5 controller controlling a v5 device with a higher minor version. Eg. a v5.0 +controller sending commands to a v5.2 device.
  • +
  • Controllers should ignore unknown fields, properties, attributes, etc. within an object (device, node, or property), but keep the object itself.
  • +
  • Controllers should ignore the entire object (device, node, or property) if in a known field, property, or attribute an illegal value is encountered. For example; +
      +
    • illegal characters in a topic or name
    • +
    • unknown data type
    • +
    • unknown/illegal format
    • +
    • required element missing
    • +
    +
  • +
+

JSON considerations

+

Validation of JSON payloads is hard. The most common approach to validate JSON data is to use JSONschema. +Unfortunately JSONschema is not a standard, it is a long list of mostly incompatible drafts of a potential standard. And as such one +has to take into account the potential differences in implementations. This is about the JSONschema specifics itself as well as its reliance on RegEx engines for string validations, which are also known to be riddled with incompatibilities (typically language/platform specific).

+

The most popular JSONschema versions over time tend to be draft 4, draft 7 and the latest (at the time of writing) 2020-12.

+

General recommendations;

+
    +
  • If possible use a library that implements the latest JSONschema version available
  • +
  • When writing schema’s make sure they are compatible with the popular versions mentioned above
  • +
  • Try to avoid RegEx’es, if you have to use them, then; +
      +
    • restrict them to character classes and modifiers ("+", "-", "*", "?")
    • +
    • do not use back-tracking and OR ("|") constructs (the OR construct can typically be handled on the JSONschema level using an anyOf construct)
    • +
    +
  • +
  • If a device fails to parse the JSONschema, or a RegEx, then by default it should skip validation and assume the payload is valid.
  • +
+

QoS choices explained

+

The nature of the Homie convention makes it safe about duplicate messages, so QoS levels for reliability At least once (QoS 1) +and Exactly once (QoS 2) should both be fine. The recommended level is Exactly once (QoS 2), since a resend on QoS 1 might have a different order, and hence is slightly less reliable, in case another device sends a new message that lands in between the ‘send’ and ‘resend’ of the first message. However, the probability of that happening is most likely negligible.

+

Keep in mind that if you change the QoS level to At least once (QoS 1), then it should be done so for the entire Homie network. +Because the MQTT order will not hold if the QoS levels of messages are different. That said; anyone who accepts the lesser reliability of +At least once (QoS 1), will most likely also not care about the potential ordering issue of mixed QoS levels.

+

For non-retained properties the QoS level is At most once (QoS 0) to ensure that events don’t arrive late or multiple times. Because the events and commands are time-sensitive. With At most once (QoS 0) messages will not be queued by the broker for delivery if the subscriber (a device or controller) is currently disconnected. Which effectively translates to “either you get it now, or you don’t get it at all”.

+ +
+ +
+
+ + + +
+ +
+ + + + diff --git a/specification/index.xml b/specification/index.xml new file mode 100644 index 0000000..7535012 --- /dev/null +++ b/specification/index.xml @@ -0,0 +1,144 @@ + + + + The Homie convention + https://homieiot.github.io/specification/ + Recent content on The Homie convention + Hugo + en-us + + + + https://homieiot.github.io/specification/spec-core-develop-changes/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/specification/spec-core-develop-changes/ + Commit title Date Hash chore(convention): minor cleanup and consistency updates 2024-07-06 14:23:54 +0200 0d7baf5 Trigger website deployment on convention changes (#293) 2024-07-04 10:25:15 +0200 ce4f54a fix(logging): one level up in doc structure 2024-07-03 14:54:46 +0200 ce5e371 chore(headings): move some headings 2024-06-30 11:05:43 +0200 63f21bd Correct topic pattern for empty string payload (#288) 2024-02-17 18:20:16 +0200 133c6b8 homie5(alerts): replace &lsquo;alert&rsquo; state with alert-topic (#283) 2023-11-15 23:38:52 +0100 eb0b437 fix(target): equality of received commands (#277) 2023-11-13 11:23:55 +0100 3715512 homie5(type): add type field for device and node (#282) 2023-11-05 12:12:29 +0100 11f0378 chore(state): add some more details around state management (#272) 2023-11-05 10:59:14 +0100 b314544 fix(broadcast): broadcasted messages should be non-retained (#280) 2023-11-05 10:48:22 +0100 ed50bc6 feat(unit): add &lsquo;rpm&rsquo; (revolutions per minute) (#279) 2023-11-05 10:47:22 +0100 1331fcc Added information about unpublishing/removing a device (#278) 2023-11-05 10:45:02 +0100 250d31a fix(log): clarify all messages must be non-retained (#276) 2023-07-26 13:52:23 +0200 c8201c8 feat(color): allow to set a preferred format (#275) 2023-07-06 11:07:52 +0200 6eb4639 feat(json-type): add a JSON data type (#273) 2023-06-25 20:01:12 +0200 566a2d6 feat(log): change &ldquo;alert&rdquo; state, add logging (#262) 2023-06-25 15:13:31 +0200 adc710e feat(property): add an optional $target property (#263) 2023-06-25 14:29:22 +0200 611ec27 clarify(enum): clarify that whitesapce is significant (#268) 2023-06-25 10:00:24 +0200 4bb1287 chore(id): simplify ID checks (#261) 2023-06-24 12:08:34 +0200 da23e1f change(description): change arrays to objects (#270) 2023-06-24 12:08:17 +0200 015b328 feat(format): add color format &lsquo;xyz&rsquo; (#274) 2023-06-24 12:07:55 +0200 1c15005 fix(validation): specify float/int validation order (#269) 2023-06-18 20:06:37 +0200 13e241e fix(hierarchy): root device should not have root (#271) 2023-06-18 20:05:06 +0200 d6b4ad8 chore(properties): align the 3 property tables in the spec (#267) 2023-06-16 11:28:17 +0200 35837c2 chore(format): reformat table (flip-axis) for readability (#266) 2023-06-16 09:28:16 +0200 dbff282 fix(example): no description update in &lsquo;ready&rsquo; state (#265) 2023-06-15 09:32:00 +0200 06446c0 fix(format): properly format the integer format template (#264) 2023-06-15 00:23:36 +0200 c1a58ce chore(spec): spelling corrections (#260) 2023-06-15 00:13:04 +0200 9fa98ce fix(base-topic): some occurences of homie/5/ were missing (#259) 2023-03-19 13:58:20 +0100 6afa235 feat(formats): add step size to integer/float formats (#257) 2023-03-17 13:45:02 +0100 953a1e2 feat(units): add airquality units; ppm (co2 / co) 2023-03-12 13:20:46 +0100 da5f1b4 feat(units): add windspeed units; m/s + knots 2023-03-07 11:10:07 +0100 ce7a3ee fix(utf8): more explicit encoding details 2023-03-07 11:01:06 +0100 edf94b1 change(description-doc): relax language about omitting defaults (#258) 2023-03-17 13:42:05 +0100 67e0ad7 feat(units): add recommended units (#254) 2023-03-04 15:29:08 +0100 2fe6f7a change(QoS) clarify QoS settings and switch to 2 (#253) 2023-01-26 10:32:56 +0100 063578d refactor(mqtt) relocate all info wrt retained and qos (#252) 2023-01-26 10:30:50 +0100 c3f03bf fix(strings) use 0x00 instead of 0x00 (#251) 2023-01-19 07:46:58 +0100 158b873 fix(version) device example missing required attribute (#250) 2023-01-18 23:20:50 +0100 d59d163 feat(version) add version to description document 2023-01-18 00:12:25 +0100 4accccd feat(versioning) add improved versioning and compatibility 2023-01-17 23:29:52 +0100 4d10316 fix(property) handling empty-string values (#239) 2023-01-17 23:39:16 +0100 734d6ff change(formats) float-colors, improved numbers, add booleans 2023-01-17 07:46:27 +0100 7e50cb1 feat(units) add kW, kWh and m3 as units 2023-01-17 06:50:42 +0100 0f8439f readability(retained) reorder more logically (#245) 2023-01-17 09:11:35 +0100 1f73c11 fix(device) be more specific about empty attributes (#242) 2023-01-16 23:24:07 +0100 f974349 describe parent-child hierarchy and handling (#240) 2023-01-16 23:23:29 +0100 11dc08a fix(percent) drop the percent type (#243) 2023-01-16 23:21:58 +0100 0155f38 drop node. + + + + https://homieiot.github.io/specification/spec-core-develop-diff/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/specification/spec-core-develop-diff/ + v4.0.0 develop n3version: v4.0.0n3version: develop 4releasedate: 27. August 20194releasedate: 06. July 2024 n18A topic level ID MAY contain lowercase letters from `a` to `z`, numbers from `0` to `9` as well as the hyphen character (`-`).n18A topic level ID MAY ONLY contain lowercase letters from `a` to `z`, numbers from `0` to `9` as well as the hyphen character (`-`). n20A topic level ID MUST NOT start or end with a hyphen (`-`). + + + + https://homieiot.github.io/specification/spec-core-develop/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/specification/spec-core-develop/ + MQTT Restrictions Homie communicates through MQTT and is hence based on the basic principles of MQTT topic publication and subscription. Topic IDs An MQTT topic consists of one or more topic levels, separated by the slash character (/). A topic level ID MAY ONLY contain lowercase letters from a to z, numbers from 0 to 9 as well as the hyphen character (-). The special character $ is used and reserved for Homie attributes. + + + + https://homieiot.github.io/specification/spec-core-v1_5_0/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/specification/spec-core-v1_5_0/ + Background An instance of a physical piece of hardware (an Arduino, an ESP8266&hellip;) is called a device. A device has device properties, like the current local IP, the Wi-Fi signal, etc. A device can expose multiple nodes. For example, a weather device might expose a temperature node and an humidity node. A node can have multiple node properties. The temperature node might for example expose a temperature property containing the actual temperature, and an unit property. + + + + https://homieiot.github.io/specification/spec-core-v2_0_0-changes/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/specification/spec-core-v2_0_0-changes/ + Commit title Date Hash Add MIT LICENSE - closes #21 2017-04-28 10:06:43 +0200 3bcbb01 :memo: Add link to hodmin cli-client (#19) 2017-04-17 22:15:19 +0200 f137694 :memo: Fix typo in README.md (#20) 2017-04-17 22:14:44 +0200 ec0b666 :memo: Add note that the v2 might still change before final release 2017-02-13 10:12:11 +0100 279f55d :art: Fix backticks inside table 2016-11-16 19:52:15 +0100 bff4047 :sparkles: Add format of $mac 2016-11-16 19:51:26 +0100 c9b402c :sparkles: Say $online must be sent last 2016-11-16 12:33:46 +0100 23f4f8f :sparkles: Add MAC address 2016-11-16 12:25:31 +0100 b4ce39d :bug: Remove old $ota left-over 2016-11-16 12:21:42 +0100 724dfc5 :sparkles: Add firmware checksum 2016-11-16 12:19:50 +0100 284a1f9 :art: Create a stats device property - fix #8 2016-10-26 16:40:44 +0200 2e28318 :fire: Remove $ota - now implementation specific 2016-10-24 16:46:27 +0200 9f8a584 :sparkles: Make $signal specify an interval too - fix #8 2016-10-24 15:58:56 +0200 b5f27f0 :docs: Add reference to homie-python (#9) 2016-10-17 22:37:35 +0200 c181ec0 Center title 2016-10-12 19:46:47 +0200 860b26a Add ID format section and cosmetic tweaks 2016-10-11 13:13:07 +0200 7f5e582 Make banner transparent 2016-10-10 10:23:02 +0200 b2a1931 Replace devices base topic by homie 2016-10-03 09:59:09 +0200 b0ec78f Add broadcast channel 2016-09-07 19:36:51 +0200 cd84f93 Add range properties 2016-08-27 11:17:48 +0200 2d5e7fe Remove useless $nodes 2016-08-19 14:52:04 +0200 3bba8d3 Add $type and $properties to node 2016-08-19 14:48:23 +0200 d92ceb9 Remove old $reset property 2016-08-16 18:47:49 +0200 675f0aa Cosmetic changes and typo fixes 2016-08-16 00:31:00 +0200 d5f4ad1 Allow implementation-specific properties 2016-08-15 00:39:32 +0200 62bc55d Fix typos 2016-08-13 11:31:40 +0200 14f9d81 First v2 changes 2016-08-13 11:26:27 +0200 658f2b3 + + + + https://homieiot.github.io/specification/spec-core-v2_0_0-diff/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/specification/spec-core-v2_0_0-diff/ + v1.5.0 v2.0.0 n3version: v1.5.0n3version: v2.0.0 4releasedate: 19. June 20164releasedate: 28. April 2017 n13An instance of a physical piece of hardware (an Arduino, an ESP8266...) is called a **device**. A device has **device properties**, like the current local IP, the Wi-Fi signal, etc. A device can expose multiple **nodes**. For example, a weather device might expose a temperature node and an humidity node. A node can have multiple **node properties**. The temperature node might for example expose a temperature property containing the actual temperature, and an unit property. + + + + https://homieiot.github.io/specification/spec-core-v2_0_0/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/specification/spec-core-v2_0_0/ + Background An instance of a physical piece of hardware (an Arduino, an ESP8266&hellip;) is called a device. A device has device properties, like the current local IP, the Wi-Fi signal, etc. A device can expose multiple nodes. For example, a weather device might expose a temperature node and an humidity node. A node can have multiple node properties. The temperature node might for example expose a degrees property containing the actual temperature, and an unit property. + + + + https://homieiot.github.io/specification/spec-core-v2_0_1-changes/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/specification/spec-core-v2_0_1-changes/ + Commit title Date Hash Add Node JS Implementation 2018-03-24 12:57:05 +1100 b1f7ec0 2.0.1 Added $nodes to a device 2018-03-24 08:27:05 +1100 775ee43 + + + + https://homieiot.github.io/specification/spec-core-v2_0_1-diff/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/specification/spec-core-v2_0_1-diff/ + v2.0.0 v2.0.1 n3version: v2.0.0n3version: v2.0.1 4releasedate: 28. April 20174releasedate: 24. March 2018 tt133 &lt;/tr&gt; 134 &lt;tr&gt; 135 &lt;td&gt;$nodes&lt;/td&gt; 136 &lt;td&gt;Device → Controller&lt;/td&gt; 137 &lt;td&gt;Nodes the device exposes, with format &lt;code&gt;id&lt;/code&gt; separated by a &lt;code&gt;,&lt;/code&gt; if there are multiple nodes.&lt;/td&gt; 138 &lt;td&gt;Yes&lt;/td&gt; 139 &lt;td&gt;Yes&lt;/td&gt; + + + + https://homieiot.github.io/specification/spec-core-v2_0_1/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/specification/spec-core-v2_0_1/ + Background An instance of a physical piece of hardware (an Arduino, an ESP8266&hellip;) is called a device. A device has device properties, like the current local IP, the Wi-Fi signal, etc. A device can expose multiple nodes. For example, a weather device might expose a temperature node and an humidity node. A node can have multiple node properties. The temperature node might for example expose a degrees property containing the actual temperature, and an unit property. + + + + https://homieiot.github.io/specification/spec-core-v3_0_0-changes/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/specification/spec-core-v3_0_0-changes/ + Commit title Date Hash Merge pull request #71 from homieiot/rolling-releases 2018-03-25 08:55:18 +1100 96c3086 Add info regarding rolling releases 2018-03-24 15:18:37 +0100 c5ad0b2 Add NodeJS implementation 2018-03-24 11:25:36 +1100 110b68f Small Readme version fix 2018-03-21 09:00:35 +1100 508f6ae Fix Versioning. 2018-03-16 07:55:10 +1100 09c84e4 Merge pull request #67 from marvinroger/v2 2018-03-12 12:01:13 +1100 0b90e74 Update Version 2018-03-12 12:00:15 +1100 be4d750 Merge branch &lsquo;redesign&rsquo; into v2 2018-03-12 11:59:28 +1100 e25c5dc added micropython-homie 2018-03-12 11:54:43 +1100 00285cb Merge pull request #64 from microhomie/v2 2018-02-17 21:12:45 +1100 0a9e88b Remove WIP state 2018-02-12 10:49:02 +0100 85d6a1f Add MicroPython implementation 2018-02-10 11:56:40 +0100 d7392c0 Merge pull request #63 from nerdfirefighter/redesign 2018-02-03 18:44:59 +1100 b071d29 Typo Fix 2018-02-02 20:46:05 +1100 25b727f Merge pull request #1 from marvinroger/redesign 2018-02-02 20:43:27 +1100 71b15b9 Update Branch 2018-01-24 12:08:45 +1100 755b994 Merge pull request #60 from nerdfirefighter/v2 2018-01-24 11:50:03 +1100 0482588 Update README. + + + + https://homieiot.github.io/specification/spec-core-v3_0_0-diff/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/specification/spec-core-v3_0_0-diff/ + v2.0.1 v3.0.0 n3version: v2.0.1n3version: v3.0.0 4releasedate: 24. March 20184releasedate: 25. March 2018 n11## Backgroundn11## Table of Contents n13An instance of a physical piece of hardware (an Arduino, an ESP8266...) is called a **device**. A device has **device properties**, like the current local IP, the Wi-Fi signal, etc. A device can expose multiple **nodes**. For example, a weather device might expose a `temperature` node and an `humidity` node. A node can have multiple **node properties**. + + + + https://homieiot.github.io/specification/spec-core-v3_0_0/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/specification/spec-core-v3_0_0/ + Table of Contents Motivation MQTT restrictions Topic IDs Payload QoS and retained messages Topology Base topic Devices Device attributes Device behavior Device statistics Nodes Node attributes Properties Property attributes Arrays Broadcast channel Motivation The Homie convention strives to be a communication definition on top of MQTT between IoT devices and controlling entities. MQTT is a machine-to-machine (M2M)/&ldquo;Internet of Things&rdquo; connectivity protocol. It was designed as an extremely lightweight publish/subscribe messaging transport. + + + + https://homieiot.github.io/specification/spec-core-v3_0_1-changes/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/specification/spec-core-v3_0_1-changes/ + Commit title Date Hash Stateful attribute for properties (#108) 2018-10-27 15:22:20 +0200 730357e Remove regex requirement (#88) 2018-04-29 14:32:39 +0200 081214d Use &ldquo;true&rdquo; instead of &ldquo;on&rdquo; (#89) 2018-04-27 14:51:40 +0200 5cbe5ee Add FAQ, discuss getter topic (#87) 2018-04-27 14:29:27 +0200 fb8d153 Add my ESP32 IDF implementation (#73) 2018-04-23 15:25:24 -0700 7d9f236 + + + + https://homieiot.github.io/specification/spec-core-v3_0_1-diff/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/specification/spec-core-v3_0_1-diff/ + v3.0.0 v3.0.1 n3version: v3.0.0n3version: v3.0.1 4releasedate: 25. March 20184releasedate: 27. October 2018 nn30* [FAQ and Rationale](#faq) 31 nn97 98Properties can be **retained**. 99A property is retained by default. A non-retained property would be useful for momentary events (door bell pressed). 100 101A combination of those flags compiles into this list: 102 103* **retained + non-settable**: The node publishes a property state (temperature sensor) 104* **retained + settable**: The node publishes a property state, and can receive commands for the property (by controller or other party) (lamp power) 105* **non-retained + non-settable**: The node publishes momentary events (door bell pressed) 106* **non-retained + settable**: The node publishes momentary events, and can receive commands for the property (by controller or other party) (brew coffee) nn446 &lt;/tr&gt; 447 &lt;tr&gt; 448 &lt;td&gt;$retained&lt;/td&gt; 449 &lt;td&gt;Device → Controller&lt;/td&gt; 450 &lt;td&gt;Specifies whether the property is retained (&lt;code&gt;true&lt;/code&gt;) or non-retained (&lt;code&gt;false&lt;/code&gt;). + + + + https://homieiot.github.io/specification/spec-core-v3_0_1/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/specification/spec-core-v3_0_1/ + Table of Contents Motivation MQTT restrictions Topic IDs Payload QoS and retained messages Topology Base topic Devices Device attributes Device behavior Device statistics Nodes Node attributes Properties Property attributes Arrays Broadcast channel FAQ and Rationale Motivation The Homie convention strives to be a communication definition on top of MQTT between IoT devices and controlling entities. MQTT is a machine-to-machine (M2M)/&ldquo;Internet of Things&rdquo; connectivity protocol. It was designed as an extremely lightweight publish/subscribe messaging transport. + + + + https://homieiot.github.io/specification/spec-core-v4_0_0-changes/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/specification/spec-core-v4_0_0-changes/ + Commit title Date Hash Remove array brackets in super-car example (#177) 2019-08-27 14:01:18 +0200 12453d8 Corections to meta extension (#173) 2019-07-18 00:07:23 +0200 2c1999e Fixed typos and broken links, removed whitespace in homie_legacy_stat… (#172) 2019-07-17 21:13:33 +0200 608541a Extension Convention (#171) 2019-07-16 22:55:32 +0200 eaf3edc remove references to deprecated arrays (#169) 2019-07-03 19:56:14 +0800 9ac3433 Point Last Will section to $state (#157) 2019-03-07 17:30:43 +0900 a3ea000 Remove timings paragraph. Rename behaviour to lifecycle. + + + + https://homieiot.github.io/specification/spec-core-v4_0_0-diff/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/specification/spec-core-v4_0_0-diff/ + v3.0.1 v4.0.0 n2source: README.mdn2source: convention.md 3version: v3.0.13version: v4.0.0 4releasedate: 27. October 20184releasedate: 27. August 2019 n11## Table of Contentsn 12 13* [Motivation](#motivation) 14* [MQTT restrictions](#mqtt-restrictions) 15 * [Topic IDs](#topic-ids) 16 * [Payload](#payload) 17 * [QoS and retained messages](#qos-and-retained-messages) 18* [Topology](#topology) 19 * [Base topic](#base-topic) 20 * [Devices](#devices) 21 * [Device attributes](#device-attributes) 22 * [Device behavior](#device-behavior) 23 * [Device statistics](#device-statistics) 24 * [Nodes](#nodes) 25 * [Node attributes](#node-attributes) 26 * [Properties](#properties) 27 * [Property attributes](#property-attributes) 28 * [Arrays](#arrays) 29 * [Broadcast channel](#broadcast-channel) 30* [FAQ and Rationale](#faq) 31 32 33## Motivation 34 35The Homie convention strives to be a **communication definition on top of MQTT** between IoT devices and controlling entities. + + + + https://homieiot.github.io/specification/spec-core-v4_0_0/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://homieiot.github.io/specification/spec-core-v4_0_0/ + MQTT Restrictions Homie communicates through MQTT and is hence based on the basic principles of MQTT topic publication and subscription. Topic IDs An MQTT topic consists of one or more topic levels, separated by the slash character (/). A topic level ID MAY contain lowercase letters from a to z, numbers from 0 to 9 as well as the hyphen character (-). A topic level ID MUST NOT start or end with a hyphen (-). + + + diff --git a/specification/spec-core-develop-changes/index.html b/specification/spec-core-develop-changes/index.html new file mode 100644 index 0000000..523f8eb --- /dev/null +++ b/specification/spec-core-develop-changes/index.html @@ -0,0 +1,477 @@ + + + + + + + + + The Homie convention + + + + + + + + + + + + + +
+
+ +
+ + + +
+
+ homieiot/convention +
+
+
+ + + +
+ +
+
+
+

+ +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Commit titleDateHash
chore(convention): minor cleanup and consistency updates2024-07-06 14:23:54 +02000d7baf5
Trigger website deployment on convention changes (#293)2024-07-04 10:25:15 +0200ce4f54a
fix(logging): one level up in doc structure2024-07-03 14:54:46 +0200ce5e371
chore(headings): move some headings2024-06-30 11:05:43 +020063f21bd
Correct topic pattern for empty string payload (#288)2024-02-17 18:20:16 +0200133c6b8
homie5(alerts): replace ‘alert’ state with alert-topic (#283)2023-11-15 23:38:52 +0100eb0b437
fix(target): equality of received commands (#277)2023-11-13 11:23:55 +01003715512
homie5(type): add type field for device and node (#282)2023-11-05 12:12:29 +010011f0378
chore(state): add some more details around state management (#272)2023-11-05 10:59:14 +0100b314544
fix(broadcast): broadcasted messages should be non-retained (#280)2023-11-05 10:48:22 +0100ed50bc6
feat(unit): add ‘rpm’ (revolutions per minute) (#279)2023-11-05 10:47:22 +01001331fcc
Added information about unpublishing/removing a device (#278)2023-11-05 10:45:02 +0100250d31a
fix(log): clarify all messages must be non-retained (#276)2023-07-26 13:52:23 +0200c8201c8
feat(color): allow to set a preferred format (#275)2023-07-06 11:07:52 +02006eb4639
feat(json-type): add a JSON data type (#273)2023-06-25 20:01:12 +0200566a2d6
feat(log): change “alert” state, add logging (#262)2023-06-25 15:13:31 +0200adc710e
feat(property): add an optional $target property (#263)2023-06-25 14:29:22 +0200611ec27
clarify(enum): clarify that whitesapce is significant (#268)2023-06-25 10:00:24 +02004bb1287
chore(id): simplify ID checks (#261)2023-06-24 12:08:34 +0200da23e1f
change(description): change arrays to objects (#270)2023-06-24 12:08:17 +0200015b328
feat(format): add color format ‘xyz’ (#274)2023-06-24 12:07:55 +02001c15005
fix(validation): specify float/int validation order (#269)2023-06-18 20:06:37 +020013e241e
fix(hierarchy): root device should not have root (#271)2023-06-18 20:05:06 +0200d6b4ad8
chore(properties): align the 3 property tables in the spec (#267)2023-06-16 11:28:17 +020035837c2
chore(format): reformat table (flip-axis) for readability (#266)2023-06-16 09:28:16 +0200dbff282
fix(example): no description update in ‘ready’ state (#265)2023-06-15 09:32:00 +020006446c0
fix(format): properly format the integer format template (#264)2023-06-15 00:23:36 +0200c1a58ce
chore(spec): spelling corrections (#260)2023-06-15 00:13:04 +02009fa98ce
fix(base-topic): some occurences of homie/5/ were missing (#259)2023-03-19 13:58:20 +01006afa235
feat(formats): add step size to integer/float formats (#257)2023-03-17 13:45:02 +0100953a1e2
feat(units): add airquality units; ppm (co2 / co)2023-03-12 13:20:46 +0100da5f1b4
feat(units): add windspeed units; m/s + knots2023-03-07 11:10:07 +0100ce7a3ee
fix(utf8): more explicit encoding details2023-03-07 11:01:06 +0100edf94b1
change(description-doc): relax language about omitting defaults (#258)2023-03-17 13:42:05 +010067e0ad7
feat(units): add recommended units (#254)2023-03-04 15:29:08 +01002fe6f7a
change(QoS) clarify QoS settings and switch to 2 (#253)2023-01-26 10:32:56 +0100063578d
refactor(mqtt) relocate all info wrt retained and qos (#252)2023-01-26 10:30:50 +0100c3f03bf
fix(strings) use 0x00 instead of 0x00 (#251)2023-01-19 07:46:58 +0100158b873
fix(version) device example missing required attribute (#250)2023-01-18 23:20:50 +0100d59d163
feat(version) add version to description document2023-01-18 00:12:25 +01004accccd
feat(versioning) add improved versioning and compatibility2023-01-17 23:29:52 +01004d10316
fix(property) handling empty-string values (#239)2023-01-17 23:39:16 +0100734d6ff
change(formats) float-colors, improved numbers, add booleans2023-01-17 07:46:27 +01007e50cb1
feat(units) add kW, kWh and m3 as units2023-01-17 06:50:42 +01000f8439f
readability(retained) reorder more logically (#245)2023-01-17 09:11:35 +01001f73c11
fix(device) be more specific about empty attributes (#242)2023-01-16 23:24:07 +0100f974349
describe parent-child hierarchy and handling (#240)2023-01-16 23:23:29 +010011dc08a
fix(percent) drop the percent type (#243)2023-01-16 23:21:58 +01000155f38
drop node.$type field (#238)2023-01-16 20:12:34 +01001c39612
format required for enum and color types2023-01-15 22:06:14 +0100e2e8ce7
drop separate v5 version2023-01-15 21:18:09 +01005a34283
readability; move long payload list to bottom of MQTT chapter2023-01-15 19:40:58 +01009641bd0
minor description-size improvements2023-01-15 02:08:15 +010080f1772
implement single description topic (JSON)2022-11-27 21:44:27 +0100c97eea9
added 4.0 as the new 5.0 basis document2022-11-27 20:22:51 +01001e0ec36
fix(device) be more specific about empty attributes (#241)2023-01-16 23:22:30 +0100451c995
chore(*) add design principles (#234)2023-01-15 19:20:56 +0100c0ba283
clarify $format being required for enum and color (#224)2023-01-15 19:13:51 +0100bb5efc7
chore(LICENSE): rename to get markdown rendering (#230)2022-10-06 18:36:48 +0200fd95584
Update logo link2022-10-06 15:58:44 +020084c1c13
Fix: Added negative values to the valid range specification for floats2021-01-25 04:17:45 -0500fc643d6
Add datetime and duration to datatype enum.2020-06-28 12:42:22 +02001b3efa4
Update convention.md2020-05-11 07:38:16 -0500dd6ee8e
Update convention.md2020-05-11 07:31:26 -0500449d952
Add implementation details and other things (#200)2020-05-06 13:21:50 +02006d852df
clarify broadcast topics2020-05-05 12:50:47 -0500d68076d
added duration property2020-05-05 12:34:17 -05009905706
clarify percentage values2020-05-04 11:14:07 -05008824393
ready state clarification2020-04-28 13:20:44 -05002c41d72
added datetime payload2020-04-28 13:10:21 -0500b813a15
clarification on allowed characters in topic ID2020-04-26 11:09:58 -05002c28a5e
Fix: Grammar fix (#187)2020-01-03 03:26:12 -06006f3f98a
+ +
+
+
+ + + +
+ +
+ + + + diff --git a/specification/spec-core-develop-diff/index.html b/specification/spec-core-develop-diff/index.html new file mode 100644 index 0000000..16bc6e5 --- /dev/null +++ b/specification/spec-core-develop-diff/index.html @@ -0,0 +1,1184 @@ + + + + + + + + + The Homie convention + + + + + + + + + + + + + +
+
+ +
+ + + +
+
+ homieiot/convention +
+
+
+ + + +
+ +
+
+
+

+ Changes +

+
Changes from + v4.0.0 to + develop
+ +

Commits

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Commit titleDateHash
chore(convention): minor cleanup and consistency updates2024-07-06 14:23:54 +02000d7baf5
Trigger website deployment on convention changes (#293)2024-07-04 10:25:15 +0200ce4f54a
fix(logging): one level up in doc structure2024-07-03 14:54:46 +0200ce5e371
chore(headings): move some headings2024-06-30 11:05:43 +020063f21bd
Correct topic pattern for empty string payload (#288)2024-02-17 18:20:16 +0200133c6b8
homie5(alerts): replace ‘alert’ state with alert-topic (#283)2023-11-15 23:38:52 +0100eb0b437
fix(target): equality of received commands (#277)2023-11-13 11:23:55 +01003715512
homie5(type): add type field for device and node (#282)2023-11-05 12:12:29 +010011f0378
chore(state): add some more details around state management (#272)2023-11-05 10:59:14 +0100b314544
fix(broadcast): broadcasted messages should be non-retained (#280)2023-11-05 10:48:22 +0100ed50bc6
feat(unit): add ‘rpm’ (revolutions per minute) (#279)2023-11-05 10:47:22 +01001331fcc
Added information about unpublishing/removing a device (#278)2023-11-05 10:45:02 +0100250d31a
fix(log): clarify all messages must be non-retained (#276)2023-07-26 13:52:23 +0200c8201c8
feat(color): allow to set a preferred format (#275)2023-07-06 11:07:52 +02006eb4639
feat(json-type): add a JSON data type (#273)2023-06-25 20:01:12 +0200566a2d6
feat(log): change “alert” state, add logging (#262)2023-06-25 15:13:31 +0200adc710e
feat(property): add an optional $target property (#263)2023-06-25 14:29:22 +0200611ec27
clarify(enum): clarify that whitesapce is significant (#268)2023-06-25 10:00:24 +02004bb1287
chore(id): simplify ID checks (#261)2023-06-24 12:08:34 +0200da23e1f
change(description): change arrays to objects (#270)2023-06-24 12:08:17 +0200015b328
feat(format): add color format ‘xyz’ (#274)2023-06-24 12:07:55 +02001c15005
fix(validation): specify float/int validation order (#269)2023-06-18 20:06:37 +020013e241e
fix(hierarchy): root device should not have root (#271)2023-06-18 20:05:06 +0200d6b4ad8
chore(properties): align the 3 property tables in the spec (#267)2023-06-16 11:28:17 +020035837c2
chore(format): reformat table (flip-axis) for readability (#266)2023-06-16 09:28:16 +0200dbff282
fix(example): no description update in ‘ready’ state (#265)2023-06-15 09:32:00 +020006446c0
fix(format): properly format the integer format template (#264)2023-06-15 00:23:36 +0200c1a58ce
chore(spec): spelling corrections (#260)2023-06-15 00:13:04 +02009fa98ce
fix(base-topic): some occurences of homie/5/ were missing (#259)2023-03-19 13:58:20 +01006afa235
feat(formats): add step size to integer/float formats (#257)2023-03-17 13:45:02 +0100953a1e2
feat(units): add airquality units; ppm (co2 / co)2023-03-12 13:20:46 +0100da5f1b4
feat(units): add windspeed units; m/s + knots2023-03-07 11:10:07 +0100ce7a3ee
fix(utf8): more explicit encoding details2023-03-07 11:01:06 +0100edf94b1
change(description-doc): relax language about omitting defaults (#258)2023-03-17 13:42:05 +010067e0ad7
feat(units): add recommended units (#254)2023-03-04 15:29:08 +01002fe6f7a
change(QoS) clarify QoS settings and switch to 2 (#253)2023-01-26 10:32:56 +0100063578d
refactor(mqtt) relocate all info wrt retained and qos (#252)2023-01-26 10:30:50 +0100c3f03bf
fix(strings) use 0x00 instead of 0x00 (#251)2023-01-19 07:46:58 +0100158b873
fix(version) device example missing required attribute (#250)2023-01-18 23:20:50 +0100d59d163
feat(version) add version to description document2023-01-18 00:12:25 +01004accccd
feat(versioning) add improved versioning and compatibility2023-01-17 23:29:52 +01004d10316
fix(property) handling empty-string values (#239)2023-01-17 23:39:16 +0100734d6ff
change(formats) float-colors, improved numbers, add booleans2023-01-17 07:46:27 +01007e50cb1
feat(units) add kW, kWh and m3 as units2023-01-17 06:50:42 +01000f8439f
readability(retained) reorder more logically (#245)2023-01-17 09:11:35 +01001f73c11
fix(device) be more specific about empty attributes (#242)2023-01-16 23:24:07 +0100f974349
describe parent-child hierarchy and handling (#240)2023-01-16 23:23:29 +010011dc08a
fix(percent) drop the percent type (#243)2023-01-16 23:21:58 +01000155f38
drop node.$type field (#238)2023-01-16 20:12:34 +01001c39612
format required for enum and color types2023-01-15 22:06:14 +0100e2e8ce7
drop separate v5 version2023-01-15 21:18:09 +01005a34283
readability; move long payload list to bottom of MQTT chapter2023-01-15 19:40:58 +01009641bd0
minor description-size improvements2023-01-15 02:08:15 +010080f1772
implement single description topic (JSON)2022-11-27 21:44:27 +0100c97eea9
added 4.0 as the new 5.0 basis document2022-11-27 20:22:51 +01001e0ec36
fix(device) be more specific about empty attributes (#241)2023-01-16 23:22:30 +0100451c995
chore(*) add design principles (#234)2023-01-15 19:20:56 +0100c0ba283
clarify $format being required for enum and color (#224)2023-01-15 19:13:51 +0100bb5efc7
chore(LICENSE): rename to get markdown rendering (#230)2022-10-06 18:36:48 +0200fd95584
Update logo link2022-10-06 15:58:44 +020084c1c13
Fix: Added negative values to the valid range specification for floats2021-01-25 04:17:45 -0500fc643d6
Add datetime and duration to datatype enum.2020-06-28 12:42:22 +02001b3efa4
Update convention.md2020-05-11 07:38:16 -0500dd6ee8e
Update convention.md2020-05-11 07:31:26 -0500449d952
Add implementation details and other things (#200)2020-05-06 13:21:50 +02006d852df
clarify broadcast topics2020-05-05 12:50:47 -0500d68076d
added duration property2020-05-05 12:34:17 -05009905706
clarify percentage values2020-05-04 11:14:07 -05008824393
ready state clarification2020-04-28 13:20:44 -05002c41d72
added datetime payload2020-04-28 13:10:21 -0500b813a15
clarification on allowed characters in topic ID2020-04-26 11:09:58 -05002c28a5e
Fix: Grammar fix (#187)2020-01-03 03:26:12 -06006f3f98a
+ + + +

Differences

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

v4.0.0
develop
n3version: v4.0.0n3version: develop
4releasedate: 27. August 20194releasedate: 06. July 2024
n18A topic level ID MAY contain lowercase letters from `a` to `z`, numbers from `0` to `9` as well as the hyphen character (`-`).n18A topic level ID MAY ONLY contain lowercase letters from `a` to `z`, numbers from `0` to `9` as well as the hyphen character (`-`).
n20A topic level ID MUST NOT start or end with a hyphen (`-`).n
nn22### QoS and retained messages
23
24The recommended QoS level is **Exactly once (QoS 2)** (except for non-retained, see below).
25
26* All messages MUST be sent as **retained**, UNLESS stated otherwise.
27* Controllers setting values for device properties publish to the Property `set` topic with **non-retained** messages only.
28* Controllers setting values for **non-retained** device properties should publish to the Property `/set` topic with a QoS of **At most once (QoS 0)**.
29* Devices publishing values for their **non-retained** properties must use **non-retained** messages, with a QoS of **At most once (QoS 0)**.
30
31For QoS details see [the explanation](#qos-choices-explained).
32
33### Last Will
34
35Homie requires the last will (LWT) to set the `homie` / `5` / `[device ID]` / `$state` attribute to the value **`lost`**, see [Device Lifecycle](#device-lifecycle).
36MQTT only allows one last will message per connection, but since a device can have children, the LWT message MUST be set on the
37root device (the device at the root of the parent-child tree).
38
39### Empty string values
40
41MQTT will treat an empty string payload as a "delete" instruction for the topic, therefor an
42empty string value is represented by a 1-character string containing a single byte value 0 (Hex: `0x00`, Dec: `0`).
43
44The empty string (passed as an MQTT payload) can only occur in 3 places;
45
46- `homie` / `5` / `[device ID]` / `[node ID]` / `[property ID]`; reported property values (for string types)
47- `homie` / `5` / `[device ID]` / `[node ID]` / `[property ID]` / `set`; the topic to set properties (of string types)
48- `homie` / `5` / `[device ID]` / `[node ID]` / `[property ID]` / `$target`; the target property value (for string types)
49
50This convention specifies no way to represent an actual value of a 1-character string with a single byte 0. If a device
51needs this, then it should provide an escape mechanism on the application level.
52
23### Payload53## Payloads
nn56- The message MUST NOT include the UTF-8 [BOM](https://en.wikipedia.org/wiki/Byte_order_mark)
n28#### Stringn59### String
n31- An empty string ("") is a valid payloadn62- An [empty string](#empty-string-values) ("") is a valid payload
n33#### Integern64### Integer
n35- Integer types are UTF-8 encoded string literal representations of 64-bit signed whole numbersn66- Integer types are string literal representations of 64-bit signed whole numbers
n39- An empty string ("") is not a valid payloadn70- An [empty string](#empty-string-values) ("") is not a valid payload
n41#### Floatn72### Float
n43- Float types are UTF-8 encoded string literal representations of 64-bit signed floating point numbersn74- Float types are string literal representations of 64-bit signed floating point numbers
44- Floats range from 2<sup>-1074</sup> to (2-2<sup>-52</sup>)&ast;2<sup>1023</sup>75- Floats range from +/-(2^-1074) to +/-((2 - 2^-52) * 2^1023)
n49- An empty string ("") is not a valid payloadn80- An [empty string](#empty-string-values) ("") is not a valid payload
n51#### Booleann82### Boolean
n55- An empty string ("") is not a valid payloadn86- An [empty string](#empty-string-values) ("") is not a valid payload
n57#### Enumn88### Enum
n61- Payloads should have leading and trailing whitespace removedn92- Leading- and trailing-whitespace is significant, e.g. "Car" will not match " Car".
62- An empty string ("") is not a valid payload93- An [empty string](#empty-string-values) ("") is not a valid payload
n64#### Colorn95### Color
n66- Color payload validity varies depending on the property format definition of either "rgb" or "hsv"n97- Color payload validity varies depending on the property format definition of either "rgb", "hsv", or "xyz"
67- Both payload types contain comma separated whole numbers of differing restricted ranges98- All payload types contain comma-separated data of differing restricted ranges. The first being the type, followed by numbers. The numbers must conform to the [float](#float) format
68- The encoded string may only contain whole numbers and the comma character ",", no other characters are permitted, including spaces (" ")99- The encoded string may only contain the [float](#float) numbers and the comma character ",", no other characters are permitted, including spaces (" ")
69- Payloads for type "rgb" contains 3 comma separated values of numbers with a valid range between 0 and 255. e.g. 100,100,100100- Payloads for type "rgb" contain 3 comma-separated values of [floats](#float) (`r`, `g`, `b`) with a valid range between 0 and 255 (inclusive). e.g. `"rgb,100,100,100"`
70- Payloads for type "hsv" contains 3 comma separated values of numbers. The first number has a range of 0 to 360, the second and third numbers have a range of 0 to 100. e.g. 300,50,75101- Payloads for type "hsv" contain 3 comma-separated values of [floats](#float). The first number (`h`) has a range of 0 to 360 (inclusive), and the second and third numbers (`s` and `v`) have a range of 0 to 100 (inclusive). e.g. `"hsv,300,50,75"`
102- Payloads for type "xyz" contain 2 comma separated values of [floats](#float) (`x`, `y`) with a valid range between 0 and 1 (inclusive). The "z" value can be calculated via `z=1-x-y` and is therefore not transmitted. (see [CIE_1931_color_space](https://en.wikipedia.org/wiki/CIE_1931_color_space)). e.g. `"xyz,0.25,0.34"`
71- An empty string ("") is not a valid payload103- An [empty string](#empty-string-values) ("") is not a valid payload
72
n74### QoS and retained messagesn105### DateTime
n76The nature of the Homie convention makes it safe about duplicate messages, so the recommended QoS for reliability is **QoS 1**.n107- DateTime payloads must use the ISO [8601 format](https://en.wikipedia.org/wiki/ISO_8601).
77All messages MUST be sent as **retained**, UNLESS stated otherwise.108- An [empty string](#empty-string-values) ("") is not a valid payload
n79### Last willn110### Duration
n81MQTT only allows one last will message per connection.n112- Duration payloads must use the [ISO 8601 duration format](https://en.wikipedia.org/wiki/ISO_8601#Durations)
82Homie requires the last will (LWT) to set the `homie` / `device ID` / `$state` attribute to the value **`lost`**, see [Device Lifecycle](#device-lifecycle).113- The format is `PTxHxMxS`, where:
83As a consequence a new MQTT connection to the broker is required per published device.114`P`: Indicates a period/duration (required).
115`T`: Indicates a time (required).
116`xH`: Hours, where `x` represents the number of hours (optional).
117`xM`: Minutes, where `x` represents the number of minutes (optional).
118`xS`: Seconds, where `x` represents the number of seconds (optional).
119- Examples: `PT12H5M46S` (12 hours, 5 minutes, 46 seconds), `PT5M` (5 minutes)
120- An [empty string](#empty-string-values) ("") is not a valid payload
121
122### JSON
123
124- Contains a JSON string for transporting complex data formats that cannot be exposed as single value attributes.
125- The payload MUST be either a JSON-Array or JSON-Object type, for other types the standard Homie types should be used.
n87The root topic in this document is `homie/`.n129The root topic in this convention is `"homie/5/"`.
n89you can choose another.n131you can change the first segment, but the `"/5/"` segment must be retained. This allows controllers
132to subscribe to only the devices they are compatible with.
nn134## Auto-Discovery
135
91Homie controllers must by default perform auto-discovery on the wildcard topic "+/+/$homie".136Homie 5 controllers must by default perform auto-discovery on the wildcard topic `"+/5/+/$state"`.
nn138A zero length payload published on the `$state` topic indicates a device removal, see [device lifecycle](#device-lifecycle).
n94## Topologyn140## Topology and structure
n98For example, a car, an Arduino/ESP8266 or a coffee machine.n144For example, a car, an Arduino/ESP8266, or a coffee machine.
145Within the convention devices can be modelled to have children. For example, bridge
146devices; a zwave bridge device (the parent) exposes many child devices (the
147zwave devices). There is no depth limit set on additionally nested children.
n103For example, a car might expose a `wheels` node, an `engine` node and a `lights` node.n152For example, a car might expose a `wheels` node, an `engine` node, and a `lights` node.
n108For example the `wheels` node might expose an `angle` property.n157For example, the `wheels` node might expose an `angle` property.
109The `engine` node might expose a `speed`, `direction` and `temperature` property.158The `engine` node might expose a `speed`, `direction`, and `temperature` property.
n114Attributes are represented by topic identifier starting with `$`.n163Attributes are represented by a topic identifier starting with `$`.
n121* `homie` / **`device ID`**: this is the base topic of a device.n170* `homie` / `5` / **`[device ID]`**: this is the base topic of a device.
122Each device must have a unique device ID which adhere to the [ID format](#topic-ids).171Each device must have a unique device ID that adheres to the [ID format](#topic-ids).
nn175The following topic structure will be used to expose the device attributes:
176
126* `homie` / `device ID` / **`$device-attribute`**:177* `homie` / `5` / `[device ID]` / **`[$device-attribute]`**:
n128The following device attributes are mandatory and MUST be send, even if it is just an empty string.n179Devices have the following attributes:
n130| Topic | Description |n181| Attribute | Required | Description |
131|-------------|--------------------------------------------------------------------------:|182|-------------|----------|----------------------------------------------------------------|
132| $homie | The implemented Homie convention version |183| `$state` | yes | Reflects the current state of the device. See [Device Lifecycle](#device-lifecycle) |
133| $name | Friendly name of the device |184| `$description`| yes | The description document (JSON), describing the device, nodes, and properties of this device. **Important**: this value may only change when the device `$state` is either `init`, `disconnected`, or `lost`. |
134| $state | See [Device Lifecycle](#device-lifecycle) |185| `$log` | no | A topic that allows devices to log messages. See [Logging](#logging) |
135| $nodes | [Nodes](#nodes) the device exposes, separated by `,` for multiple ones. |
136| $extensions | Supported extensions, separated by `,` for multiple ones. |
n138Optional topics include:n187The JSON description document is a JSON object with the following fields;
n140| Topic | Description |n189| Field | Type | Required | Default | Nullable | Description |
141|-----------------|-------------------------------|190|-----------|--------------|----------|---------|----------|-------------|
142| $implementation | An identifier for the Homie implementation (example "esp8266") |191| `homie` |string | yes | | no | The implemented Homie convention version, without the "patch" level. So the format is `"5.x"`, where the `'x'` is the minor version. |
192| `version` | integer | yes | | no | The version of the description document. Whenever the document changes, a new higher version must be assigned. This does not need to be sequential, eg. a timestamp could be used. |
193| `nodes` |object | no | `{}` | no | The [Nodes](#nodes) the device exposes. An object containing the [Nodes](#nodes), indexed by their [ID](#topic-ids). Defaults to an empty object.|
194| `name` |string | yes | | no | Friendly name of the device. |
195| `type` |string | no | | no | Type of Device. Please ensure proper namespacing to prevent naming collisions. |
196| `children` |array-strings | no | `[]` | no | Array of [ID](#topic-ids)'s of child devices. Defaults to an empty array.|
197| `root` |string | yes/no | | no | [ID](#topic-ids) of the root parent device. **Required** if the device is NOT the root device, MUST be omitted otherwise. |
198| `parent` |string | yes/no | same as `root`| no | [ID](#topic-ids) of the parent device. **Required** if the parent is NOT the root device. Defaults to the value of the `root` property. |
199| `extensions`|array-strings | no | `[]` | no | Array of supported extensions. Defaults to an empty array.|
n144For example, a device with an ID of `super-car` that comprises of a `wheels`, `engine` and a `lights` node would send:n201For example, a device with an ID of `super-car` that comprises of a `wheels`, `engine`, and a `lights` node would send:
145
n147homie/super-car/$homie → "2.1.0"n
148homie/super-car/$name → "Super car"
149homie/super-car/$nodes → "wheels,engine,lights"
150homie/super-car/$implementation → "esp8266"
151homie/super-car/$state → "ready"203homie/5/super-car/$state → "init"
204homie/5/super-car/$description → following JSON document;
nn206```json
207 {
208 "homie": "5.0",
209 "name": "Supercar",
210 "version": 7,
211 "nodes": {
212 "wheels": { ... },
213 "engine": { ... },
214 "lights": { ... }
215 }
216 }
217```
218
219#### Device hierarchy
220
221Devices can be organized in parent-child relationships. These are expressed via the device
222attributes `root`, `parent`, and `children`. In any parent-child tree, there is only one
223"root" device, which is the top-level device that has no parent, but only children.
224
225Example: a ZWave bridge (`id = "bridge"`), which exposes a ZWave device with a dual-relay (`id = "dualrelay"`),
226which respectively control Light1 (`id = "light1"`) and Light2 (`id = "light2"`). So there are 4 devices in total.
227Then these are the attribute values:
228
229| | id | children | root | parent |
230|--------------|-------------|----------------------|----------|-------------|
231| Zwave bridge | "bridge" | ["dualrelay"] | | |
232| Zwave relay | "dualrelay" | ["light1", "light2"] | "bridge" | |
233| First light | "light1" | | "bridge" | "dualrelay" |
234| Second light | "light2" | | "bridge" | "dualrelay" |
235
236To monitor the state of child devices in this tree 2 topic subscriptions are needed. The `$state` attribute of the device itself, as well as the `$state` attribute of its root device.
237Because if the root device loses its connection to the MQTT server, the last will (LWT), will set its `$state` attribute to `"lost"`, but it will not update the child-device states. Hence the need for 2 topic subscriptions.
238
239The `state` of any device should be determined as follows:
240| has a `root` set | `root` state | device state |
241|------------------|--------------|--------------|
242| no | n.a. | device state is the `$state` attribute of the device itself
243| yes | not `"lost"` | device state is the `$state` attribute of the device itself
244| yes | `"lost"` | device state is `"lost"` (`$state` attribute of the root device)
245
n156The `$state` device attribute represents the current state of the device.n249The `$state` device attribute represents the current state of the device. A device exists once a valid value is set in the `$state` attribute. It doesn't mean the device is complete and valid (yet), but it does mean it exists.
157There are 6 different states:250
251There are 5 possible state values:
n160This state is optional, and may be sent if the device takes a long time to initialize, but wishes to announce to consumers that it is coming online. n254This state is optional and may be sent if the device takes a long time to initialize, but wishes to announce to consumers that it is coming online.
161* **`ready`**: this is the state the device is in when it is connected to the MQTT broker, has sent all Homie messages and is ready to operate. A Homie Controller can assume default values for all optional topics.255A device may fall back into this state to do some reconfiguration.
256* **`ready`**: this is the state the device is in when it is connected to the MQTT broker and has sent all Homie messages describing the device attributes, nodes, properties, and their values. The device has subscribed to all appropriate `/set` topics and is ready to receive messages.
n166* **`lost`**: this is the state the device is in when the device has been "badly" disconnected.n261* **`lost`**: this is the state the device is in when the device has been "badly" disconnected. **Important**: If a root-device `$state` is `"lost"` then the state of **every child device in its tree** is also `"lost"`.
167You must define this message as LWT.262You must define this message as the last will (LWT) for root devices.
168* **`alert`**: this is the state the device is when connected to the MQTT broker, but something wrong is happening. E.g. a sensor is not providing data and needs human intervention.263
169You have to send this message when something is wrong.264In order to permanently remove a device the following steps should be performed in order:
2651. remove the retained `$state` attribute from the broker by publishing a zero length payload message to its topic. The device will cease to exist.
2662. any other retained attributes or property values should be cleared via the same method afterwards.
n173* `homie` / `device ID` / **`node ID`**: this is the base topic of a node.n270* `homie` / `5` / `[device ID]` / **`[node ID]`**: this is the base topic of a node.
174Each node must have a unique node ID on a per-device basis which adhere to the [ID format](#topic-ids).271Each node must have a unique node ID on a per-device basis which adheres to the [ID format](#topic-ids).
n178* `homie` / `device ID` / `node ID` / **`$node-attribute`**:n275There are no node attributes in MQTT topics for this level.
n180All listed attributes are **required**. A node attribute MUST be one of these:n277The Node object itself is described in the `homie` / `5` / `[device ID]` / `$description` JSON document. The Node object has the following fields:
n182| Topic | Description |n279| Field | Type | Required | Default | Nullable | Description |
183|-------------|-------------------------------------------------------------------------------------------|280|-------------|--------------|----------|---------|----------|-------------|
184| $name | Friendly name of the Node |281| `name` |string | yes | | no | Friendly name of the Node. |
185| $type | Type of the node |282| `type` |string | no | | no | Type of Node. Please ensure proper namespacing to prevent naming collisions. |
186| $properties | Exposed properties, separated by `,` for multiple ones. |283| `properties`|object | no | `{}` | no | The [Properties](#properties) the Node exposes. An object containing the [Properties](#properties), indexed by their [ID](#topic-ids). Defaults to an empty object.|
n188For example, our `engine` node would send:n285For example, our `engine` node would look like this:
n190```javan287```json
191homie/super-car/engine/$name → "Car engine"288 ...
192homie/super-car/engine/$type → "V8"289 "engine": {
193homie/super-car/engine/$properties → "speed,direction,temperature"290 "name": "Car engine",
291 "properties": {
292 "speed": { ... },
293 "direction": { ... },
294 "temperature": { ... }
295 }
296 }
297 ...
n198* `homie` / `device ID` / `node ID` / **`property ID`**: this is the base topic of a property.n302* `homie` / `5` / `[device ID]` / `[node ID]` / **`[property ID]`**: this is the base topic of a property.
199Each property must have a unique property ID on a per-node basis which adhere to the [ID format](#topic-ids).303Each property must have a unique property ID on a per-node basis which adheres to the [ID format](#topic-ids).
200
201* A property payload (e.g. a sensor reading) is directly published to the property topic, e.g.:
202 ```java
203 homie/super-car/engine/temperature → "21.5"
204 ```
205
206* Properties can be **settable**.
207 For example, you don't want your `temperature` property to be settable in case of a temperature sensor
208 (like the car example), but to be settable in case of a thermostat.
209
210* Properties can be **retained**.
211 A property is retained by default. A non-retained property would be useful for momentary events (door bell pressed).
212
213A combination of those flags compiles into this list:
214
215* **retained + non-settable**: The node publishes a property state (temperature sensor)
216* **retained + settable**: The node publishes a property state, and can receive commands for the property (by controller or other party) (lamp power)
217* **non-retained + non-settable**: The node publishes momentary events (door bell pressed)
218* **non-retained + settable**: The node publishes momentary events, and can receive commands for the property (by controller or other party) (brew coffee)
219
n223* `homie` / `device ID` / `node ID` / `property ID` / **`$property-attribute`**:n307| Attribute | Required | Description |
224
225The following attributes are required:
226
227| Topic | Description | Payload type |
228|-----------|------------------------------------------------------|---------------------------------------------|308|-----------|----------|----------------------------------------------------------------|
229| $name | Friendly name of the property. | String |309| | yes | A property value (e.g. a sensor reading) is directly published to the property topic, e.g.: `homie/5/super-car/engine/temperature → "21.5"` |
230| $datatype | The data type. See [Payloads](#payloads). | Enum: \[integer, float, boolean,string, enum, color\] |310| `$target` | no | Describes an intended state change. The `$target` attribute must either be used for every value update (including the initial one), or it must never be used. |
n232The following attributes are optional:n312The Property object itself is described in the `homie` / `5` / `device ID` / `$description` JSON document. The Property object has the following fields:
n234| Topic | Description | Payload type |n314| Field | Type | Required | Default | Nullable | Description |
235|-----------|------------------------------------------------------|---------------------------------------------|315|-----------|--------------|----------|----------|----|---------|
236| $format | Specifies restrictions or options for the given data type | See below |316| `name` | string | yes | | no | Friendly name of the Property. |
237| $settable | Settable (<code>true</code>). Default is read-only (<code>false</code>) | Boolean |317| `datatype`| string | yes | | no | The data type. See [Payloads](#payload). Any of the following values: `"integer", "float", "boolean", "string", "enum", "color", "datetime", "duration", "json"`. |
238| $retained | Non-retained (<code>false</code>). Default is Retained (<code>true</code>). | Boolean |318| `format` | string | see [formats](#formats) | see [formats](#formats) | no | Specifies restrictions or options for the given data type. |
239| $unit | Optional unit of this property. See list below. | String |319| `settable`| boolean | no | `false` | no | Whether the Property is settable. |
320| `retained`| boolean | no | `true` | no | Whether the Property is retained. |
321| `unit` | string | no | | no | Unit of this property. See [units](#units). |
n241For example, our `temperature` property would send:n
nn324For example, our `temperature` property would look like this in the device/node description document:
325
326```json
327 ...
328 "temperature": {
329 "name": "Engine temperature",
330 "unit": "°C",
331 "datatype": "float",
332 "format": "-20:120"
333 }
334 ...
335```
336And the following MQTT topic with the reported property value:
n244homie/super-car/engine/temperature/$name → "Engine temperature"n
245homie/super-car/engine/temperature/$settable → "false"
246homie/super-car/engine/temperature/$unit → "°C"
247homie/super-car/engine/temperature/$datatype → "float"
248homie/super-car/engine/temperature/$format → "-20:120"
249homie/super-car/engine/temperature → "21.5"338homie/5/super-car/engine/temperature → "21.5"
n252Format:n341#### Settable and retained properties
n254* For `integer` and `float`: Describes a range of payloads e.g. `10:15`n343Properties can be **settable** and/or **retained**. For example, you don't want your `temperature`
255* For `enum`: `payload,payload,payload` for enumerating all valid payloads.344property to be settable in case of a temperature sensor (like the car example), but it should be
256* For `color`:345settable in the case of a thermostat setpoint.
257 - `rgb` to provide colors in RGB format e.g. `255,255,0` for yellow.346
258 - `hsv` to provide colors in HSV format e.g. `60,100,100` for yellow.347A property is retained by default. A non-retained property would be useful for momentary events
348(e.g. doorbell pressed). See also [QoS settings](#qos-and-retained-messages).
349
350A combination of the **settable** and **retained** flags compiles into this list:
351
352| retained | settable | description |
353|----------|----------|-------------|
354| yes | yes | The node publishes a property state and can receive commands for the property (by a controller or other party) (lamp power)
355| yes | no | (**default**) The node publishes a property state (temperature sensor)
356| no | yes | The node publishes momentary events and can receive commands for the property from a controller (brew coffee)
357| no | no | The node publishes momentary events (doorbell pressed)
358
359
360#### Formats
361
362The format attribute specifies restrictions or options for the given data type. User interfaces can derive hints from
363the formats for displaying values.
364
365| Type | Required | Default | Description |
366|--------------|----------|----------|-------------|
367| float | no | `:` | `[min]:[max][:step]` where min and max are the respective minimum and maximum (inclusive) allowed values, both represented in the format for [float types](#float). Eg. `10.123:15.123`. If the minimum and/or maximum are missing from the format, then they are open-ended, so `0:` allows a value >= 0.<br/>The optional `step` determines the step size, eg. `2:6:2` will allow values `2`, `4`, and `6`. It must be greater than 0. The base for calculating a proper value based on `step` should be `min`, `max`, or the current property value (in that order). The implementation should round property values to the nearest step (which can be outside the min/max range). The min/max validation must be done after rounding. |
368| integer | no | `:` | `[min]:[max][:step]` where min and max are the respective minimum and maximum (inclusive) allowed values, both represented in the format for [integer types](#integer). Eg. `5:35`. If the minimum and/or maximum are missing from the format, then they are open-ended, so `:10` allows a value <= 10. <br/>The optional `step` determines the step size, eg. `2:6:2` will allow values `2`, `4`, and `6`. It must be greater than 0. The base for calculating a proper value based on `step` should be `min`, `max`, or the current property value (in that order). The implementation should round property values to the nearest step (which can be outside the min/max range). The min/max validation must be done after rounding. |
369| enum | yes | | A comma-separated list of non-quoted values. Eg. `value1,value2,value3`. Leading- and trailing whitespace is significant. Individual values can not be an empty string, hence at least 1 value must be specified in the format. |
370| color | yes | | A comma-separated list of color formats supported; `rgb`, `hsv`, and/or `xyz`. The formats should be listed in order of preference (most preferred first, least preferred last). See the [color type](#color) for the resulting value formats. E.g. a device supporting RGB and HSV, where RGB is preferred, would have its format set to `"rgb,hsv"`. |
371| boolean | no | `false,true` | Identical to an enum with 2 entries. The first represents the `false` value and the second is the `true` value. Eg. `close,open` or `off,on`. If provided, then both entries must be specified. **Important**: the format does NOT specify valid payloads, they are descriptions of the valid payloads `false` and `true`. |
372| json | no | `{"anyOf": [{"type": "array"},{"type": "object"}]}` | A [JSONschema](http://json-schema.org/) definition, which is added as a string (escaped), NOT as a nested json-object. See [JSON considerations](#json-considerations), for some ideas wrt compatibility. If a client fails to parse/compile the JSONschema, then it should ignore the given schema and fall back to the default schema.
373
374
375#### Units
n262* `°C`: Degree Celsiusn379* `°C`: Degree Celsius (see 'Degree' for encoding)
263* `°F`: Degree Fahrenheit380* `°F`: Degree Fahrenheit (see 'Degree' for encoding)
nn382 * Character '°' is [Unicode: `U+00B0`](https://www.compart.com/en/unicode/U+00B0), Hex: `0xc2 0xb0`, Dec: `194 176`
n266* `gal`: Galonn384* `gal`: Gallon
nn387* `kW`: Kilowatt
388* `kWh`: Kilowatt-hour
nn390* `Hz`: Hertz
391* `rpm`: Revolutions per minute
nn394* `m³`: Cubic meter
395 * Character '³' is [Unicode: `U+00B3`](https://www.compart.com/en/unicode/U+00B3), Hex: `0xc2 0xb3`, Dec: `194 179`
nn397* `m/s`: Meters per Second
398* `kn`: Knots
nn401* `ppm`: Parts Per Million
402* `s`: Seconds
403* `min`: Minutes
404* `h`: Hours
405* `lx`: Lux
406* `K`: Kelvin
407* `MK⁻¹`: Mired
408 * Character '⁻' is [Unicode: `U+207B`](https://www.compart.com/en/unicode/U+207B), Hex: `0xe2 0x81 0xbb`, Dec: `226 129 187`
409 * Character '¹' is [Unicode: `U+00B9`](https://www.compart.com/en/unicode/U+00B9), Hex: `0xc2 0xb9`, Dec: `194 185`
nn412The non-ASCII characters are specified as Unicode codepoints and the UTF-8 byte sequence that represents them. Since the same characters can be created in many visually similar ways it is important to stick to the exact byte sequences to enable proper interoperability.
413
nn416#### Target attribute
417
418The `$target` attribute for properties allows a device to communicate an intended state change of a property. This serves 2 main
419purposes;
420
4211. closing the control loop for a controller setting a value (if the property is settable).
4222. feedback in case a change is not instantaneous (e.g. a light that slowly dimms over a longer period, or a
423 motorized valve that takes several minutes to fully open)
424
425If implemented, then a device must first update the `$target` attribute, then start the transition (with
426optional state-value updates during the transition), and when done update the property value to match the
427`$target` value (functional equivalent, not necessarily a byte-by-byte equality).
428
429If a new target is received (and accepted) from a controller by publishing to the property's `set` topic, then the exact value received must be published to the `$target` topic (byte-by-byte equality). To allow for closing the control loop.
430
431**Notes:**
432
433- a controller can only assume that the command it send to the `set` topic was received and accepted. Not necessarily that it will ever reach the target state, since if another controller updates the property again, it might never reach the target state.
434- The same goes for possible conversions (colors), rounding (number formats), etc. it will be very hard to check functional equivalence, since the value published may have a different format. So a controller should NOT implement a retry loop checking the final value. At best they should implement retries until the value set is being accepted.
435- Homie devices representing remote hardware (typically when bridging) should NOT set the `$target` attribute upon receiving a change from the hardware device. This is only allowed if the hardware explicitly distinguishes between current value and target value. This is to prevent a loop; e.g. a homie controller sets 100% as target, software instructs hardware to change, intermediate updates received from hardware; 20%, 40%, etc, should NOT overwrite the `$target` value, since that still is 100.
436
437
n281* `homie` / `device ID` / `node ID` / `property ID` / **`set`**: The device must subscribe to this topic if the property is **settable** (in case of actuators for example).n440* `homie` / `5` / `[device ID]` / `[node ID]` / `[property ID]` / **`set`**: The device must subscribe to this topic if the property is **settable** (in the case of actuators for example).
n283A Homie controller publishes to the `set` command topic with non-retained messages only.n442A Homie controller publishes to the `set` command topic with non-retained messages only. See [retained messages](#qos-and-retained-messages).
n285The assigned and processed payload must be reflected by the Homie device in the property topic `homie` / `device ID` / `node ID` / `property ID` as soon as possible.n444The assigned and processed payload must be reflected by the Homie device in the property topic `homie` / `5` / `[device ID]` / `[node ID]` / `[property ID]` or target attribute `homie` / `5` / `[device ID]` / `[node ID]` / `[property ID]` / `$target` as soon as possible.
n288To give an example: A `kitchen-light` device exposing the `light` node with a settable `power` property subscribes to the topic `homie/kitchen-light/light/power/set` for commands:n447To give an example: A `kitchen-light` device exposing the `light` node with a settable `power` property subscribes to the topic `homie/5/kitchen-light/light/power/set` for commands:
n291homie/kitchen-light/light/power/set ← "true"n450homie/5/kitchen-light/light/power/set ← "true"
n294In response the device will turn on the light and upon success update its `power` property state accordingly:n453In response, the device will turn on the light and upon success update its `power` property state accordingly:
n297homie/kitchen-light/light/power → "true"n456homie/5/kitchen-light/light/power → "true"
n300## Broadcast Channeln459If the `light` were a dimmable light with a `brightness` property (0-100%), and it would be set to slowly dim over 5 seconds, then the `$target` attribute can be used (assuming once per second updates);
nn461```java
462homie/5/kitchen-light/light/brightness/set ← 100
463homie/5/kitchen-light/light/brightness/$target → 100
464homie/5/kitchen-light/light/brightness → 20 (after 1 second)
465homie/5/kitchen-light/light/brightness → 40 (after 2 seconds)
466homie/5/kitchen-light/light/brightness → 60 (after 3 seconds)
467homie/5/kitchen-light/light/brightness → 80 (after 4 seconds)
468homie/5/kitchen-light/light/brightness → 100 (after 5 seconds)
469```
470
471## Alert topic
472
473Devices can raise alerts. Alerts are user facing messages that have an ID, they can be set and removed.
474The alert topic is defined as;
475
476* `homie` / `5` / `[device ID]` / `$alert` / `[alert ID]` → "alert message"
477
478A device can raise a message on a specific ID. Once the alert is no longer usefull or has been resolved, it can be removed by deleting the topic. Alerts must be send as retained messages. The alert ID must have a valid [ID format](#topic-ids), where topic ID's starting with `$` are reserved for Homie usage.
479
480Examples;
481```java
482/homie/5/mydevid/$alert/childlost = "Sensor xyz in livingroom hasn't reported updates for 3 hours"
483/homie/5/mydevid/$alert/battery = "Battery is low, at 8%"
484```
485
486In the examples above, once the situation is resolved (the sensor comes back to live, or the batteries are replaced), the device will delete the topics again, indicating the alerts have been handled.
487
488## Broadcast Topic
489
302Homie defines a broadcast channel, so a controller is able to broadcast a message to all Homie devices:490Homie defines a broadcast topic, so a controller can broadcast a message to all Homie devices:
n304* `homie` / `$broadcast` / **`level`**: `level` is an arbitrary broadcast identifier.n492* `homie` / `5` / `$broadcast` / **`[subtopic]`**: where `subtopic` can be any topic with single or multiple levels. Each segement must adhere to the [ID format](#topic-ids).
305It must adhere to the [ID format](#topic-ids).493
494The messages SHOULD be non-retained.
n312homie/$broadcast/alert ← "Intruder detected"n501homie/5/$broadcast/alert ← "Intruder detected"
502homie/5/$broadcast/security/alert ← "Intruder detected"
n315Any other topic is not part of the Homie convention.n505## Logging
506
507Since devices may be resource constraint they might not have logging capabilities. Homie provides a specific
508topic where devices can send log messages. The topic is defined as;
509
510* `homie` / `5` / `[device ID]` / `$log` / `[level]`
511
512The topic-value is the logged message, no sub-topics are allowed.
513All log messages send should be non-retained.
514The `level` is set according to the following table:
515
516level | description
517--------|------------
518`debug` | detailed information for troubleshooting purposes
519`info` | informational message, device is working as expected
520`warn` | something potentially harmful happened
521`error` | an error happened, the device will continue to operate but functionality might be impaired
522`fatal` | a non-recoverable error occured, operation of the device is likely suspended/stopped
523
524```java
525homie/5/my-device/$log/warn → "battery low"
526homie/5/my-device/$log/error → "sensor value is out of range"
527```
528
529Note that MQTT is not meant to be a logging solution, and hence it should be used with care. The implementation should
530try and limit the traffic on the MQTT bus. If devices implement messages and levels that can be "noisy", then the
531device should provide a configuration option to turn them off, to limit the bandwidth consumed.
n319This convention only covers discoverability of devices and its capabilities.n535This convention only covers the discoverability of devices and their capabilities.
320The aim is to have standardized MQTT topics for all kind of complex scenarios.536The aim is to have standardized MQTT topics for all kinds of complex scenarios.
n325The proper term `homie` is reserved and must not be used as the suffix or as part of the domain name.n541The proper term `homie` is reserved and must not be used as a suffix or as part of the domain name.
n331The recommended license is the [CCA 4.0](https://homieiot.github.io/license), since this is the license Homie itself uses.n547The recommended license is the [CCA 4.0](https://homieiot.github.io/license) since this is the license Homie itself uses.
tt550## Implementation notes
551
552### Device initialization
553
554Some devices require knowledge of their settable retained properties to function properly.
555The homie convention does not specify how to initialize/recover them e.g. after a power cycle.
556A few common approaches are:
557
558* A device can simply load default values from some configuration file.
559* A device can restore its previous state from some local storage. This is the recommended way.
560* A device may try to restore its state using MQTT. This can be done by subscribing to the respective channels.
561 The controller could set all properties of a device once it becomes `ready`.
562 An alternative way is to recover the state from other MQTT channels that are external to the Homie specification.
563* If a property is not critical for correctly functioning, there is no need to recover it.
564
565### Device reconfiguration
566
567If a device wishes to modify any of its nodes or properties, it can
568
569* disconnect and reconnect with other values, or
570* set `$state=init` and then modify any of the attributes.
571
572Devices can remove old properties and nodes by deleting the respective MQTT topics by publishing an empty message
573to those topics (an actual empty string on MQTT level, so NOT the escaped `0x00` byte, see also [empty string values](#empty-string-values)).
574
575When adding many child devices, implementations should take care of not publishing too many parent-updates, since every controller would have to parse the description again and again.
576
577#### Adding children
578
579The recommended way to add child device is as follows:
580
5811. first publish any child-devices, as any other device
582 1. set child-device state to `"init"`
583 1. publish child-device details (including parent details in `root` and `parent` fields)
584 1. set child-device state to `"ready"`
5851. update the parent device, as any other change
586 1. set parent state to `"init"`
587 1. update parent description (add any child IDs to its `children` array)
588 1. set parent state to `"ready"`
589
590Be aware that due to MQTT message ordering the consistency at any stage in this process cannot be guaranteed.
591
592#### Removing children
593
594The recommended way to remove child device is as follows:
595
5961. update the parent device
597 1. set parent state to `"init"`
598 1. update parent description (remove any child IDs from its `children` array)
599 1. set parent state to `"ready"`
6001. clear any child-device(s) topics, starting with the `$state` topic
601
602Be aware that due to MQTT message ordering the consistency at any stage in this process cannot be guaranteed.
603
604### Versioning
605
606Some considerations related to versioning in this specification;
607
608* compatibility is assumed to be major version only, so version 5 for this spec.
609* the base topic includes the major version. This allows controllers to only subscribe to devices they are
610compatible with.
611
612#### Backward compatibility
613
614* backward compatibility: a v5 controller controlling a v5 device with a smaller minor version. Eg. a v5.3
615controller sending commands to a v5.0 device.
616* Controllers should be aware of unsupported features in older major or minor versions they subscribe to because the spec for that version is known.
617
618#### Forward compatibility
619
620* forward compatibility: a v5 controller controlling a v5 device with a higher minor version. Eg. a v5.0
621controller sending commands to a v5.2 device.
622* Controllers should ignore unknown fields, properties, attributes, etc. within an object (device, node, or property), but keep the object itself.
623* Controllers should ignore the entire object (device, node, or property) if in a known field, property, or attribute an illegal value is encountered. For example;
624 * illegal characters in a topic or name
625 * unknown data type
626 * unknown/illegal format
627 * required element missing
628
629### JSON considerations
630
631Validation of JSON payloads is hard. The most common approach to validate JSON data is to use [JSONschema](http://json-schema.org/).
632Unfortunately JSONschema is not a standard, it is a long list of mostly incompatible drafts of a potential standard. And as such one
633has to take into account the potential differences in implementations. This is about the JSONschema specifics itself as well as its reliance on RegEx engines for string validations, which are also known to be riddled with incompatibilities (typically language/platform specific).
634
635The most popular JSONschema versions over time tend to be [`draft 4`](http://json-schema.org/specification-links.html#draft-4), [`draft 7`](http://json-schema.org/specification-links.html#draft-7) and the latest (at the time of writing) [`2020-12`](http://json-schema.org/specification-links.html#2020-12).
636
637General recommendations;
638- If possible use a library that implements the latest JSONschema version available
639- When writing schema's make sure they are compatible with the popular versions mentioned above
640- Try to avoid RegEx'es, if you have to use them, then;
641 - restrict them to character classes and modifiers (`"+", "-", "*", "?"`)
642 - do not use back-tracking and OR (`"|"`) constructs (the OR construct can typically be handled on the JSONschema level using an `anyOf` construct)
643- If a device fails to parse the JSONschema, or a RegEx, then by default it should skip validation and assume the payload is valid.
644
645### QoS choices explained
646
647The nature of the Homie convention makes it safe about duplicate messages, so QoS levels for reliability **At least once (QoS 1)**
648and **Exactly once (QoS 2)** should both be fine. The recommended level is **Exactly once (QoS 2)**, since a resend on QoS 1 might have a different order, and hence is slightly less reliable, in case another device sends a new message that lands in between the 'send' and 'resend' of the first message. However, the probability of that happening is most likely negligible.
649
650Keep in mind that if you change the QoS level to **At least once (QoS 1)**, then it should be done so for the entire Homie network.
651Because the MQTT order will not hold if the QoS levels of messages are different. That said; anyone who accepts the lesser reliability of
652**At least once (QoS 1)**, will most likely also not care about the potential ordering issue of mixed QoS levels.
653
654For **non-retained** properties the QoS level is **At most once (QoS 0)** to ensure that events don't arrive late or multiple times. Because the events and commands are time-sensitive. With **At most once (QoS 0)** messages will not be queued by the broker for delivery if the subscriber (a device or controller) is currently disconnected. Which effectively translates to "either you get it now, or you don't get it at all".
655
656
+
+
+
+ + + +
+ +
+ + + + diff --git a/specification/spec-core-develop/index.html b/specification/spec-core-develop/index.html new file mode 100644 index 0000000..abdfbb2 --- /dev/null +++ b/specification/spec-core-develop/index.html @@ -0,0 +1,1126 @@ + + + + + + + + + The Homie convention + + + + + + + + + + + + + +
+
+ +
+ + + +
+
+ homieiot/convention +
+
+
+ + + +
+ +
+ + + + + + + + + + +
+
+
+

Homie: An MQTT Convention for IoT/M2M

+
+
+
+ +
+ + + +
+ + License: CCA 4.0
+ Version: + + + + [develop] + + + + [v1.5.0] + + + + [v2.0.0] + + + + [v2.0.1] + + + + [v3.0.0] + + + + [v3.0.1] + + + + [v4.0.0] + +
+ + Changes: [Diff to previous]
+ + Release date: 06. July 2024 +
+
+ +
+ Frequently asked questions + +

How do I query/request a property?

+

You don’t. +The MQTT protocol does not implement the request-reply but rather the publish-subscribe messaging pattern. +The Homie convention follows the publish-subscribe principle by publishing data as retained messages on a regular basis. +You might want to rethink the design of your application - in most scenarios a regularly updated information is sufficient.

+

Workaround: You are free to implement your own ideas on top of the basic structure of the Homie convention. +You could either implement a get getter topic and its logic to trigger a value update, or you may exploit the concept of Homie properties and define a settable property to trigger a value update.

+ + +
+ +
+

License

+

By exercising the Licensed Rights (defined on https://creativecommons.org/licenses/by/4.0/), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions.

+
+ +
+

Table of Contents

+
+ + +

+

MQTT Restrictions

+

Homie communicates through MQTT and is hence based on the basic principles of MQTT topic publication and subscription.

+

Topic IDs

+

An MQTT topic consists of one or more topic levels, separated by the slash character (/). +A topic level ID MAY ONLY contain lowercase letters from a to z, numbers from 0 to 9 as well as the hyphen character (-).

+

The special character $ is used and reserved for Homie attributes.

+

QoS and retained messages

+

The recommended QoS level is Exactly once (QoS 2) (except for non-retained, see below).

+
    +
  • All messages MUST be sent as retained, UNLESS stated otherwise.
  • +
  • Controllers setting values for device properties publish to the Property set topic with non-retained messages only.
  • +
  • Controllers setting values for non-retained device properties should publish to the Property /set topic with a QoS of At most once (QoS 0).
  • +
  • Devices publishing values for their non-retained properties must use non-retained messages, with a QoS of At most once (QoS 0).
  • +
+

For QoS details see the explanation.

+

Last Will

+

Homie requires the last will (LWT) to set the homie / 5 / [device ID] / $state attribute to the value lost, see Device Lifecycle. +MQTT only allows one last will message per connection, but since a device can have children, the LWT message MUST be set on the +root device (the device at the root of the parent-child tree).

+

Empty string values

+

MQTT will treat an empty string payload as a “delete” instruction for the topic, therefor an +empty string value is represented by a 1-character string containing a single byte value 0 (Hex: 0x00, Dec: 0).

+

The empty string (passed as an MQTT payload) can only occur in 3 places;

+
    +
  • homie / 5 / [device ID] / [node ID] / [property ID]; reported property values (for string types)
  • +
  • homie / 5 / [device ID] / [node ID] / [property ID] / set; the topic to set properties (of string types)
  • +
  • homie / 5 / [device ID] / [node ID] / [property ID] / $target; the target property value (for string types)
  • +
+

This convention specifies no way to represent an actual value of a 1-character string with a single byte 0. If a device +needs this, then it should provide an escape mechanism on the application level.

+

Payloads

+
    +
  • Every MQTT message payload MUST be sent as a UTF-8 encoded string
  • +
  • The message MUST NOT include the UTF-8 BOM
  • +
  • The value published as payload MUST be valid for the respective property/attribute type as per the list below
  • +
+

String

+
    +
  • String types are limited to 268,435,456 characters
  • +
  • An empty string ("") is a valid payload
  • +
+

Integer

+
    +
  • Integer types are string literal representations of 64-bit signed whole numbers
  • +
  • Integers range from -9,223,372,036,854,775,808 (-263) to 9,223,372,036,854,775,807 (263-1)
  • +
  • The payload may only contain whole numbers and the negation character “-”. No other characters including spaces (" “) are permitted
  • +
  • A string with just a negation sign (”-") is not a valid payload
  • +
  • An empty string ("") is not a valid payload
  • +
+

Float

+
    +
  • Float types are string literal representations of 64-bit signed floating point numbers
  • +
  • Floats range from +/-(2^-1074) to +/-((2 - 2^-52) * 2^1023)
  • +
  • The payload may only contain whole numbers, the negation character “-”, the exponent character “e” or “E” and the decimal separator “.”, no other characters, including spaces (" “) are permitted
  • +
  • The dot character (”.") is the decimal separator (used if necessary) and may only have a single instance present in the payload
  • +
  • Representations of numeric concepts such as “NaN” (Not a Number) and “Infinity” are not a valid payload
  • +
  • A string with just a negation sign ("-") is not a valid payload
  • +
  • An empty string ("") is not a valid payload
  • +
+

Boolean

+
    +
  • Booleans must be converted to the string literals “true” or “false”
  • +
  • Representation is case sensitive, e.g. “TRUE” or “FALSE” are not valid payloads.
  • +
  • An empty string ("") is not a valid payload
  • +
+

Enum

+
    +
  • Enum payloads must be one of the values specified in the format definition of the property
  • +
  • Enum payloads are case sensitive, e.g. “Car” will not match a format definition of “car”
  • +
  • Leading- and trailing-whitespace is significant, e.g. “Car” will not match " Car".
  • +
  • An empty string ("") is not a valid payload
  • +
+

Color

+
    +
  • Color payload validity varies depending on the property format definition of either “rgb”, “hsv”, or “xyz”
  • +
  • All payload types contain comma-separated data of differing restricted ranges. The first being the type, followed by numbers. The numbers must conform to the float format
  • +
  • The encoded string may only contain the float numbers and the comma character “,”, no other characters are permitted, including spaces (" “)
  • +
  • Payloads for type “rgb” contain 3 comma-separated values of floats (r, g, b) with a valid range between 0 and 255 (inclusive). e.g. "rgb,100,100,100"
  • +
  • Payloads for type “hsv” contain 3 comma-separated values of floats. The first number (h) has a range of 0 to 360 (inclusive), and the second and third numbers (s and v) have a range of 0 to 100 (inclusive). e.g. "hsv,300,50,75"
  • +
  • Payloads for type “xyz” contain 2 comma separated values of floats (x, y) with a valid range between 0 and 1 (inclusive). The “z” value can be calculated via z=1-x-y and is therefore not transmitted. (see CIE_1931_color_space). e.g. "xyz,0.25,0.34"
  • +
  • An empty string (”") is not a valid payload
  • +
+

DateTime

+ +

Duration

+
    +
  • Duration payloads must use the ISO 8601 duration format
  • +
  • The format is PTxHxMxS, where: +P: Indicates a period/duration (required). +T: Indicates a time (required). +xH: Hours, where x represents the number of hours (optional). +xM: Minutes, where x represents the number of minutes (optional). +xS: Seconds, where x represents the number of seconds (optional).
  • +
  • Examples: PT12H5M46S (12 hours, 5 minutes, 46 seconds), PT5M (5 minutes)
  • +
  • An empty string ("") is not a valid payload
  • +
+

JSON

+
    +
  • Contains a JSON string for transporting complex data formats that cannot be exposed as single value attributes.
  • +
  • The payload MUST be either a JSON-Array or JSON-Object type, for other types the standard Homie types should be used.
  • +
+

Base Topic

+

The root topic in this convention is "homie/5/". +If this root topic does not suit your needs (in case of, e.g., a public broker or because of branding), +you can change the first segment, but the "/5/" segment must be retained. This allows controllers +to subscribe to only the devices they are compatible with.

+

Auto-Discovery

+

Homie 5 controllers must by default perform auto-discovery on the wildcard topic "+/5/+/$state". +Controllers are free to restrict discovery to a specific root topic, configurable by the user. +A zero length payload published on the $state topic indicates a device removal, see device lifecycle.

+

Topology and structure

+

Devices: +An instance of a physical piece of hardware is called a device. +For example, a car, an Arduino/ESP8266, or a coffee machine. +Within the convention devices can be modelled to have children. For example, bridge +devices; a zwave bridge device (the parent) exposes many child devices (the +zwave devices). There is no depth limit set on additionally nested children.

+

Nodes: +A device can expose multiple nodes. +Nodes are independent or logically separable parts of a device. +For example, a car might expose a wheels node, an engine node, and a lights node.

+

Properties: +A node can have multiple properties. +Properties represent basic characteristics of the node/device, often given as numbers or finite states. +For example, the wheels node might expose an angle property. +The engine node might expose a speed, direction, and temperature property. +The lights node might expose an intensity and a color property.

+

Attributes: +Devices, nodes and properties have specific attributes characterizing them. +Attributes are represented by a topic identifier starting with $. +The precise definition of attributes is important for the automatic discovery of devices following the Homie convention.

+

Examples: A device might have an IP attribute, a node will have a name attribute, and a property will have a unit attribute.

+

Devices

+
    +
  • homie / 5 / [device ID]: this is the base topic of a device. +Each device must have a unique device ID that adheres to the ID format.
  • +
+

Device Attributes

+

The following topic structure will be used to expose the device attributes:

+
    +
  • homie / 5 / [device ID] / [$device-attribute]:
  • +
+

Devices have the following attributes:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
AttributeRequiredDescription
$stateyesReflects the current state of the device. See Device Lifecycle
$descriptionyesThe description document (JSON), describing the device, nodes, and properties of this device. Important: this value may only change when the device $state is either init, disconnected, or lost.
$lognoA topic that allows devices to log messages. See Logging
+

The JSON description document is a JSON object with the following fields;

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeRequiredDefaultNullableDescription
homiestringyesnoThe implemented Homie convention version, without the “patch” level. So the format is "5.x", where the 'x' is the minor version.
versionintegeryesnoThe version of the description document. Whenever the document changes, a new higher version must be assigned. This does not need to be sequential, eg. a timestamp could be used.
nodesobjectno{}noThe Nodes the device exposes. An object containing the Nodes, indexed by their ID. Defaults to an empty object.
namestringyesnoFriendly name of the device.
typestringnonoType of Device. Please ensure proper namespacing to prevent naming collisions.
childrenarray-stringsno[]noArray of ID’s of child devices. Defaults to an empty array.
rootstringyes/nonoID of the root parent device. Required if the device is NOT the root device, MUST be omitted otherwise.
parentstringyes/nosame as rootnoID of the parent device. Required if the parent is NOT the root device. Defaults to the value of the root property.
extensionsarray-stringsno[]noArray of supported extensions. Defaults to an empty array.
+

For example, a device with an ID of super-car that comprises of a wheels, engine, and a lights node would send:

+
homie/5/super-car/$state  "init"
+homie/5/super-car/$description  following JSON document;
+
      {
+        "homie": "5.0",
+        "name": "Supercar",
+        "version": 7,
+        "nodes": { 
+          "wheels": { ... },
+          "engine": { ... },
+          "lights": { ... }
+        }
+      }
+

Device hierarchy

+

Devices can be organized in parent-child relationships. These are expressed via the device +attributes root, parent, and children. In any parent-child tree, there is only one +“root” device, which is the top-level device that has no parent, but only children.

+

Example: a ZWave bridge (id = "bridge"), which exposes a ZWave device with a dual-relay (id = "dualrelay"), +which respectively control Light1 (id = "light1") and Light2 (id = "light2"). So there are 4 devices in total. +Then these are the attribute values:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
idchildrenrootparent
Zwave bridge“bridge”[“dualrelay”]
Zwave relay“dualrelay”[“light1”, “light2”]“bridge”
First light“light1”“bridge”“dualrelay”
Second light“light2”“bridge”“dualrelay”
+

To monitor the state of child devices in this tree 2 topic subscriptions are needed. The $state attribute of the device itself, as well as the $state attribute of its root device. +Because if the root device loses its connection to the MQTT server, the last will (LWT), will set its $state attribute to "lost", but it will not update the child-device states. Hence the need for 2 topic subscriptions.

+

The state of any device should be determined as follows:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
has a root setroot statedevice state
non.a.device state is the $state attribute of the device itself
yesnot "lost"device state is the $state attribute of the device itself
yes"lost"device state is "lost" ($state attribute of the root device)
+

Device Lifecycle

+

The $state device attribute represents the current state of the device. A device exists once a valid value is set in the $state attribute. It doesn’t mean the device is complete and valid (yet), but it does mean it exists.

+

There are 5 possible state values:

+
    +
  • init: this is the state the device is in when it is connected to the MQTT broker, but has not yet sent all Homie messages and is not yet ready to operate. +This state is optional and may be sent if the device takes a long time to initialize, but wishes to announce to consumers that it is coming online. +A device may fall back into this state to do some reconfiguration.
  • +
  • ready: this is the state the device is in when it is connected to the MQTT broker and has sent all Homie messages describing the device attributes, nodes, properties, and their values. The device has subscribed to all appropriate /set topics and is ready to receive messages.
  • +
  • disconnected: this is the state the device is in when it is cleanly disconnected from the MQTT broker. +You must send this message before cleanly disconnecting.
  • +
  • sleeping: this is the state the device is in when the device is sleeping. +You have to send this message before sleeping.
  • +
  • lost: this is the state the device is in when the device has been “badly” disconnected. Important: If a root-device $state is "lost" then the state of every child device in its tree is also "lost". +You must define this message as the last will (LWT) for root devices.
  • +
+

In order to permanently remove a device the following steps should be performed in order:

+
    +
  1. remove the retained $state attribute from the broker by publishing a zero length payload message to its topic. The device will cease to exist.
  2. +
  3. any other retained attributes or property values should be cleared via the same method afterwards.
  4. +
+

Nodes

+
    +
  • homie / 5 / [device ID] / [node ID]: this is the base topic of a node. +Each node must have a unique node ID on a per-device basis which adheres to the ID format.
  • +
+

Node Attributes

+

There are no node attributes in MQTT topics for this level.

+

The Node object itself is described in the homie / 5 / [device ID] / $description JSON document. The Node object has the following fields:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeRequiredDefaultNullableDescription
namestringyesnoFriendly name of the Node.
typestringnonoType of Node. Please ensure proper namespacing to prevent naming collisions.
propertiesobjectno{}noThe Properties the Node exposes. An object containing the Properties, indexed by their ID. Defaults to an empty object.
+

For example, our engine node would look like this:

+
      ...
+      "engine": {
+        "name": "Car engine",
+        "properties": {
+          "speed": { ... },
+          "direction": { ... },
+          "temperature": { ... }
+        }
+      }
+      ...
+

Properties

+
    +
  • homie / 5 / [device ID] / [node ID] / [property ID]: this is the base topic of a property. +Each property must have a unique property ID on a per-node basis which adheres to the ID format.
  • +
+

Property Attributes

+ + + + + + + + + + + + + + + + + + + + +
AttributeRequiredDescription
yesA property value (e.g. a sensor reading) is directly published to the property topic, e.g.: homie/5/super-car/engine/temperature → "21.5"
$targetnoDescribes an intended state change. The $target attribute must either be used for every value update (including the initial one), or it must never be used.
+

The Property object itself is described in the homie / 5 / device ID / $description JSON document. The Property object has the following fields:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeRequiredDefaultNullableDescription
namestringyesnoFriendly name of the Property.
datatypestringyesnoThe data type. See Payloads. Any of the following values: "integer", "float", "boolean", "string", "enum", "color", "datetime", "duration", "json".
formatstringsee formatssee formatsnoSpecifies restrictions or options for the given data type.
settablebooleannofalsenoWhether the Property is settable.
retainedbooleannotruenoWhether the Property is retained.
unitstringnonoUnit of this property. See units.
+

For example, our temperature property would look like this in the device/node description document:

+
      ...
+      "temperature": {
+        "name": "Engine temperature",
+        "unit": "°C",
+        "datatype": "float",
+        "format": "-20:120"
+      }
+      ...
+

And the following MQTT topic with the reported property value:

+
homie/5/super-car/engine/temperature  "21.5"
+

Settable and retained properties

+

Properties can be settable and/or retained. For example, you don’t want your temperature +property to be settable in case of a temperature sensor (like the car example), but it should be +settable in the case of a thermostat setpoint.

+

A property is retained by default. A non-retained property would be useful for momentary events +(e.g. doorbell pressed). See also QoS settings.

+

A combination of the settable and retained flags compiles into this list:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
retainedsettabledescription
yesyesThe node publishes a property state and can receive commands for the property (by a controller or other party) (lamp power)
yesno(default) The node publishes a property state (temperature sensor)
noyesThe node publishes momentary events and can receive commands for the property from a controller (brew coffee)
nonoThe node publishes momentary events (doorbell pressed)
+

Formats

+

The format attribute specifies restrictions or options for the given data type. User interfaces can derive hints from +the formats for displaying values.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeRequiredDefaultDescription
floatno:[min]:[max][:step] where min and max are the respective minimum and maximum (inclusive) allowed values, both represented in the format for float types. Eg. 10.123:15.123. If the minimum and/or maximum are missing from the format, then they are open-ended, so 0: allows a value >= 0.
The optional step determines the step size, eg. 2:6:2 will allow values 2, 4, and 6. It must be greater than 0. The base for calculating a proper value based on step should be min, max, or the current property value (in that order). The implementation should round property values to the nearest step (which can be outside the min/max range). The min/max validation must be done after rounding.
integerno:[min]:[max][:step] where min and max are the respective minimum and maximum (inclusive) allowed values, both represented in the format for integer types. Eg. 5:35. If the minimum and/or maximum are missing from the format, then they are open-ended, so :10 allows a value <= 10.
The optional step determines the step size, eg. 2:6:2 will allow values 2, 4, and 6. It must be greater than 0. The base for calculating a proper value based on step should be min, max, or the current property value (in that order). The implementation should round property values to the nearest step (which can be outside the min/max range). The min/max validation must be done after rounding.
enumyesA comma-separated list of non-quoted values. Eg. value1,value2,value3. Leading- and trailing whitespace is significant. Individual values can not be an empty string, hence at least 1 value must be specified in the format.
coloryesA comma-separated list of color formats supported; rgb, hsv, and/or xyz. The formats should be listed in order of preference (most preferred first, least preferred last). See the color type for the resulting value formats. E.g. a device supporting RGB and HSV, where RGB is preferred, would have its format set to "rgb,hsv".
booleannofalse,trueIdentical to an enum with 2 entries. The first represents the false value and the second is the true value. Eg. close,open or off,on. If provided, then both entries must be specified. Important: the format does NOT specify valid payloads, they are descriptions of the valid payloads false and true.
jsonno{"anyOf": [{"type": "array"},{"type": "object"}]}A JSONschema definition, which is added as a string (escaped), NOT as a nested json-object. See JSON considerations, for some ideas wrt compatibility. If a client fails to parse/compile the JSONschema, then it should ignore the given schema and fall back to the default schema.
+

Units

+

Recommended unit strings:

+
    +
  • °C: Degree Celsius (see ‘Degree’ for encoding)
  • +
  • °F: Degree Fahrenheit (see ‘Degree’ for encoding)
  • +
  • °: Degree + +
  • +
  • L: Liter
  • +
  • gal: Gallon
  • +
  • V: Volts
  • +
  • W: Watt
  • +
  • kW: Kilowatt
  • +
  • kWh: Kilowatt-hour
  • +
  • A: Ampere
  • +
  • Hz: Hertz
  • +
  • rpm: Revolutions per minute
  • +
  • %: Percent
  • +
  • m: Meter
  • +
  • : Cubic meter + +
  • +
  • ft: Feet
  • +
  • m/s: Meters per Second
  • +
  • kn: Knots
  • +
  • Pa: Pascal
  • +
  • psi: PSI
  • +
  • ppm: Parts Per Million
  • +
  • s: Seconds
  • +
  • min: Minutes
  • +
  • h: Hours
  • +
  • lx: Lux
  • +
  • K: Kelvin
  • +
  • MK⁻¹: Mired +
      +
    • Character ‘⁻’ is Unicode: U+207B, Hex: 0xe2 0x81 0xbb, Dec: 226 129 187
    • +
    • Character ‘¹’ is Unicode: U+00B9, Hex: 0xc2 0xb9, Dec: 194 185
    • +
    +
  • +
  • #: Count or Amount
  • +
+

The non-ASCII characters are specified as Unicode codepoints and the UTF-8 byte sequence that represents them. Since the same characters can be created in many visually similar ways it is important to stick to the exact byte sequences to enable proper interoperability.

+

You are not limited to the recommended values, although they are the only well known ones that will have to be recognized by any Homie consumer.

+

Target attribute

+

The $target attribute for properties allows a device to communicate an intended state change of a property. This serves 2 main +purposes;

+
    +
  1. closing the control loop for a controller setting a value (if the property is settable).
  2. +
  3. feedback in case a change is not instantaneous (e.g. a light that slowly dimms over a longer period, or a +motorized valve that takes several minutes to fully open)
  4. +
+

If implemented, then a device must first update the $target attribute, then start the transition (with +optional state-value updates during the transition), and when done update the property value to match the +$target value (functional equivalent, not necessarily a byte-by-byte equality).

+

If a new target is received (and accepted) from a controller by publishing to the property’s set topic, then the exact value received must be published to the $target topic (byte-by-byte equality). To allow for closing the control loop.

+

Notes:

+
    +
  • a controller can only assume that the command it send to the set topic was received and accepted. Not necessarily that it will ever reach the target state, since if another controller updates the property again, it might never reach the target state.
  • +
  • The same goes for possible conversions (colors), rounding (number formats), etc. it will be very hard to check functional equivalence, since the value published may have a different format. So a controller should NOT implement a retry loop checking the final value. At best they should implement retries until the value set is being accepted.
  • +
  • Homie devices representing remote hardware (typically when bridging) should NOT set the $target attribute upon receiving a change from the hardware device. This is only allowed if the hardware explicitly distinguishes between current value and target value. This is to prevent a loop; e.g. a homie controller sets 100% as target, software instructs hardware to change, intermediate updates received from hardware; 20%, 40%, etc, should NOT overwrite the $target value, since that still is 100.
  • +
+

Property command topic

+
    +
  • homie / 5 / [device ID] / [node ID] / [property ID] / set: The device must subscribe to this topic if the property is settable (in the case of actuators for example).
  • +
+

A Homie controller publishes to the set command topic with non-retained messages only. See retained messages.

+

The assigned and processed payload must be reflected by the Homie device in the property topic homie / 5 / [device ID] / [node ID] / [property ID] or target attribute homie / 5 / [device ID] / [node ID] / [property ID] / $target as soon as possible. +This property state update not only informs other devices about the change but closes the control loop for the commanding controller, important for deterministic interaction with the client device.

+

To give an example: A kitchen-light device exposing the light node with a settable power property subscribes to the topic homie/5/kitchen-light/light/power/set for commands:

+
homie/5/kitchen-light/light/power/set  "true"
+

In response, the device will turn on the light and upon success update its power property state accordingly:

+
homie/5/kitchen-light/light/power  "true"
+

If the light were a dimmable light with a brightness property (0-100%), and it would be set to slowly dim over 5 seconds, then the $target attribute can be used (assuming once per second updates);

+
homie/5/kitchen-light/light/brightness/set  100
+homie/5/kitchen-light/light/brightness/$target  100
+homie/5/kitchen-light/light/brightness  20  (after 1 second)
+homie/5/kitchen-light/light/brightness  40  (after 2 seconds)
+homie/5/kitchen-light/light/brightness  60  (after 3 seconds)
+homie/5/kitchen-light/light/brightness  80  (after 4 seconds)
+homie/5/kitchen-light/light/brightness  100  (after 5 seconds)
+

Alert topic

+

Devices can raise alerts. Alerts are user facing messages that have an ID, they can be set and removed. +The alert topic is defined as;

+
    +
  • homie / 5 / [device ID] / $alert / [alert ID] → “alert message”
  • +
+

A device can raise a message on a specific ID. Once the alert is no longer usefull or has been resolved, it can be removed by deleting the topic. Alerts must be send as retained messages. The alert ID must have a valid ID format, where topic ID’s starting with $ are reserved for Homie usage.

+

Examples;

+
/homie/5/mydevid/$alert/childlost = "Sensor xyz in livingroom hasn't reported updates for 3 hours"
+/homie/5/mydevid/$alert/battery = "Battery is low, at 8%"
+

In the examples above, once the situation is resolved (the sensor comes back to live, or the batteries are replaced), the device will delete the topics again, indicating the alerts have been handled.

+

Broadcast Topic

+

Homie defines a broadcast topic, so a controller can broadcast a message to all Homie devices:

+
    +
  • homie / 5 / $broadcast / [subtopic]: where subtopic can be any topic with single or multiple levels. Each segement must adhere to the ID format.
  • +
+

The messages SHOULD be non-retained.

+

For example, you might want to broadcast an alert event with the alert reason as the payload. +Devices are then free to react or not. +In our case, every buzzer of your home automation system would start buzzing.

+
homie/5/$broadcast/alert  "Intruder detected"
+homie/5/$broadcast/security/alert  "Intruder detected"
+

Logging

+

Since devices may be resource constraint they might not have logging capabilities. Homie provides a specific +topic where devices can send log messages. The topic is defined as;

+
    +
  • homie / 5 / [device ID] / $log / [level]
  • +
+

The topic-value is the logged message, no sub-topics are allowed. +All log messages send should be non-retained. +The level is set according to the following table:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
leveldescription
debugdetailed information for troubleshooting purposes
infoinformational message, device is working as expected
warnsomething potentially harmful happened
erroran error happened, the device will continue to operate but functionality might be impaired
fatala non-recoverable error occured, operation of the device is likely suspended/stopped
+
homie/5/my-device/$log/warn  "battery low"
+homie/5/my-device/$log/error  "sensor value is out of range"
+

Note that MQTT is not meant to be a logging solution, and hence it should be used with care. The implementation should +try and limit the traffic on the MQTT bus. If devices implement messages and levels that can be “noisy”, then the +device should provide a configuration option to turn them off, to limit the bandwidth consumed.

+

Extensions

+

This convention only covers the discoverability of devices and their capabilities. +The aim is to have standardized MQTT topics for all kinds of complex scenarios. +A Homie device may therefore support extensions, defined in separate documents. +Every extension is identified by a unique ID.

+

The ID consists of the reverse domain name and a freely chosen suffix. +The proper term homie is reserved and must not be used as a suffix or as part of the domain name.

+

For example, an organization example.org wanting to add a feature our-feature would choose the extension ID org.example.our-feature.

+

Every extension must be published using a license. +The license can be chosen freely, even proprietary licenses are possible. +The recommended license is the CCA 4.0 since this is the license Homie itself uses.

+

Implementation notes

+

Device initialization

+

Some devices require knowledge of their settable retained properties to function properly. +The homie convention does not specify how to initialize/recover them e.g. after a power cycle. +A few common approaches are:

+
    +
  • A device can simply load default values from some configuration file.
  • +
  • A device can restore its previous state from some local storage. This is the recommended way.
  • +
  • A device may try to restore its state using MQTT. This can be done by subscribing to the respective channels. +The controller could set all properties of a device once it becomes ready. +An alternative way is to recover the state from other MQTT channels that are external to the Homie specification.
  • +
  • If a property is not critical for correctly functioning, there is no need to recover it.
  • +
+

Device reconfiguration

+

If a device wishes to modify any of its nodes or properties, it can

+
    +
  • disconnect and reconnect with other values, or
  • +
  • set $state=init and then modify any of the attributes.
  • +
+

Devices can remove old properties and nodes by deleting the respective MQTT topics by publishing an empty message +to those topics (an actual empty string on MQTT level, so NOT the escaped 0x00 byte, see also empty string values).

+

When adding many child devices, implementations should take care of not publishing too many parent-updates, since every controller would have to parse the description again and again.

+

Adding children

+

The recommended way to add child device is as follows:

+
    +
  1. first publish any child-devices, as any other device +
      +
    1. set child-device state to "init"
    2. +
    3. publish child-device details (including parent details in root and parent fields)
    4. +
    5. set child-device state to "ready"
    6. +
    +
  2. +
  3. update the parent device, as any other change +
      +
    1. set parent state to "init"
    2. +
    3. update parent description (add any child IDs to its children array)
    4. +
    5. set parent state to "ready"
    6. +
    +
  4. +
+

Be aware that due to MQTT message ordering the consistency at any stage in this process cannot be guaranteed.

+

Removing children

+

The recommended way to remove child device is as follows:

+
    +
  1. update the parent device +
      +
    1. set parent state to "init"
    2. +
    3. update parent description (remove any child IDs from its children array)
    4. +
    5. set parent state to "ready"
    6. +
    +
  2. +
  3. clear any child-device(s) topics, starting with the $state topic
  4. +
+

Be aware that due to MQTT message ordering the consistency at any stage in this process cannot be guaranteed.

+

Versioning

+

Some considerations related to versioning in this specification;

+
    +
  • compatibility is assumed to be major version only, so version 5 for this spec.
  • +
  • the base topic includes the major version. This allows controllers to only subscribe to devices they are +compatible with.
  • +
+

Backward compatibility

+
    +
  • backward compatibility: a v5 controller controlling a v5 device with a smaller minor version. Eg. a v5.3 +controller sending commands to a v5.0 device.
  • +
  • Controllers should be aware of unsupported features in older major or minor versions they subscribe to because the spec for that version is known.
  • +
+

Forward compatibility

+
    +
  • forward compatibility: a v5 controller controlling a v5 device with a higher minor version. Eg. a v5.0 +controller sending commands to a v5.2 device.
  • +
  • Controllers should ignore unknown fields, properties, attributes, etc. within an object (device, node, or property), but keep the object itself.
  • +
  • Controllers should ignore the entire object (device, node, or property) if in a known field, property, or attribute an illegal value is encountered. For example; +
      +
    • illegal characters in a topic or name
    • +
    • unknown data type
    • +
    • unknown/illegal format
    • +
    • required element missing
    • +
    +
  • +
+

JSON considerations

+

Validation of JSON payloads is hard. The most common approach to validate JSON data is to use JSONschema. +Unfortunately JSONschema is not a standard, it is a long list of mostly incompatible drafts of a potential standard. And as such one +has to take into account the potential differences in implementations. This is about the JSONschema specifics itself as well as its reliance on RegEx engines for string validations, which are also known to be riddled with incompatibilities (typically language/platform specific).

+

The most popular JSONschema versions over time tend to be draft 4, draft 7 and the latest (at the time of writing) 2020-12.

+

General recommendations;

+
    +
  • If possible use a library that implements the latest JSONschema version available
  • +
  • When writing schema’s make sure they are compatible with the popular versions mentioned above
  • +
  • Try to avoid RegEx’es, if you have to use them, then; +
      +
    • restrict them to character classes and modifiers ("+", "-", "*", "?")
    • +
    • do not use back-tracking and OR ("|") constructs (the OR construct can typically be handled on the JSONschema level using an anyOf construct)
    • +
    +
  • +
  • If a device fails to parse the JSONschema, or a RegEx, then by default it should skip validation and assume the payload is valid.
  • +
+

QoS choices explained

+

The nature of the Homie convention makes it safe about duplicate messages, so QoS levels for reliability At least once (QoS 1) +and Exactly once (QoS 2) should both be fine. The recommended level is Exactly once (QoS 2), since a resend on QoS 1 might have a different order, and hence is slightly less reliable, in case another device sends a new message that lands in between the ‘send’ and ‘resend’ of the first message. However, the probability of that happening is most likely negligible.

+

Keep in mind that if you change the QoS level to At least once (QoS 1), then it should be done so for the entire Homie network. +Because the MQTT order will not hold if the QoS levels of messages are different. That said; anyone who accepts the lesser reliability of +At least once (QoS 1), will most likely also not care about the potential ordering issue of mixed QoS levels.

+

For non-retained properties the QoS level is At most once (QoS 0) to ensure that events don’t arrive late or multiple times. Because the events and commands are time-sensitive. With At most once (QoS 0) messages will not be queued by the broker for delivery if the subscriber (a device or controller) is currently disconnected. Which effectively translates to “either you get it now, or you don’t get it at all”.

+ +
+ +
+
+ + + +
+ +
+ + + + diff --git a/specification/spec-core-v1_5_0/index.html b/specification/spec-core-v1_5_0/index.html new file mode 100644 index 0000000..498cfa6 --- /dev/null +++ b/specification/spec-core-v1_5_0/index.html @@ -0,0 +1,321 @@ + + + + + + + + + The Homie convention + + + + + + + + + + + + + +
+
+ +
+ + + +
+
+ homieiot/convention +
+
+
+ + + +
+ +
+ + + + + + + + + + +
+
+
+

Homie: An MQTT Convention for IoT/M2M

+
+
+
+ +
+ + + +
+ + License: CCA 4.0
+ Version: + + + + [develop] + + + + [v1.5.0] + + + + [v2.0.0] + + + + [v2.0.1] + + + + [v3.0.0] + + + + [v3.0.1] + + + + [v4.0.0] + +
+ + + Release date: 19. June 2016 +
+
+ +
+ Frequently asked questions + +

How do I query/request a property?

+

You don’t. +The MQTT protocol does not implement the request-reply but rather the publish-subscribe messaging pattern. +The Homie convention follows the publish-subscribe principle by publishing data as retained messages on a regular basis. +You might want to rethink the design of your application - in most scenarios a regularly updated information is sufficient.

+

Workaround: You are free to implement your own ideas on top of the basic structure of the Homie convention. +You could either implement a get getter topic and its logic to trigger a value update, or you may exploit the concept of Homie properties and define a settable property to trigger a value update.

+ + +
+ +
+

License

+

By exercising the Licensed Rights (defined on https://creativecommons.org/licenses/by/4.0/), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions.

+
+ +
+

Table of Contents

+
+ + +

+

Background

+

An instance of a physical piece of hardware (an Arduino, an ESP8266…) is called a device. A device has device properties, like the current local IP, the Wi-Fi signal, etc. A device can expose multiple nodes. For example, a weather device might expose a temperature node and an humidity node. A node can have multiple node properties. The temperature node might for example expose a temperature property containing the actual temperature, and an unit property. Properties can be settable. For example, you don’t want your temperature property to be settable in case of a temperature sensor: this depends on the environment and it would not make sense to change it. However, you will want the temperature property to be settable in case of a thermostat.

+

Convention

+

Homie devices communicate through MQTT.

+

To efficiently parse messages, Homie defines a few rules related to topic names. The base topic you will see in the following lines will be devices/. You can customize this base topic if it fits better to your needs.

+
    +
  • devices / device ID: this is the base topic name. Each device must have a unique device ID. This ID MAY be composed of lowercase letters from a to z, numbers from 0 to 9, and it MAY contain -, but MUST NOT start or end with a -.
  • +
+

Device properties

+
    +
  • devices / device ID / $ device property: a property starting with a $ at the third level of the path is related to the device. The property MUST be one of these:
  • +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PropertyDirectionDescriptionRetained
$onlineDevice → Controllertrue when the device is online, false when the device is offline (through LWT)Yes
$nameDevice → ControllerFriendly name of the deviceYes
$localipDevice → ControllerIP of the device on the local networkYes
$uptimeDevice → ControllerTime elapsed in seconds since the boot of the deviceYes
$signalDevice → ControllerInteger representing the Wi-Fi signal quality in percentage if applicableYes
$fwnameDevice → ControllerName of the firmware running on the device. This name MAY be composed of lowercase letters from a to z, numbers from 0 to 9, and it MAY contain -, but MUST NOT start or end with a -Yes
$fwversionDevice → ControllerVersion of the firmware running on the deviceYes
$nodesDevice → ControllerNodes the device has, with format id:type separated by a , if there are multiple nodesYes
$otaController → DeviceLatest OTA version available for the deviceYes or No, depending of your implementation
$ota/+Controller → Device or Device → ControllerYou can use any subtopics of `$ota` for anything related to your specific OTA implementation.Yes or No, depending of your implementation
$resetController → Devicetrue when the controller wants the device to reset its configuration. false otherwise. When the device receives a true, it should replace the retained message with a false before resettingYes
+

For example, a device with an ID of 686f6d6965 with a temperature and an humidity sensor would send:

+
devices/686f6d6965/$online → true
+devices/686f6d6965/$name → Bedroom temperature sensor
+devices/686f6d6965/$localip → 192.168.0.10
+devices/686f6d6965/$signal → 72
+devices/686f6d6965/$fwname → 1.0.0
+devices/686f6d6965/$fwversion → 1.0.0
+devices/686f6d6965/$nodes → temperature:temperature,humidity:humidity
+

And it would receive:

+
devices/686f6d6965/$ota ← 1.0.1
+devices/686f6d6965/$reset ← false
+

At this point, your device would understand there is an OTA update available, as $ota is different from $version.

+

Node properties

+
    +
  • devices / device ID / node ID / property: node ID is the ID of the node, as defined in the $nodes device property. property is the property of the node that is getting updated.
  • +
+

For example, our 686f6d6965 above would send:

+
devices/686f6d6965/temperature/temperature → 12.07
+devices/686f6d6965/humidity/humidity → 79
+
    +
  • devices / device ID / node ID / property / set: the device can subscribe to this topic if the property is settable from the controller, in case of actuators.
  • +
+

Homie is state-based. You don’t tell your smarlight to turn on, but you tell it to put it’s on state to true. This especially fits well with MQTT, because of retained message.

+

For example, an homielight device exposing a light node would subscribe to devices/homielight/light/on/set and it would receive:

+
devices/homielight/light/on/set ← true
+

The device would then turn on the light, and update its on state. This provides pessimistic feedback, which is important for home automation.

+
devices/homielight/light/on → true
+

Any other topic is not part of the Homie convention.

+ +
+ +
+
+ + + +
+ +
+ + + + diff --git a/specification/spec-core-v2_0_0-changes/index.html b/specification/spec-core-v2_0_0-changes/index.html new file mode 100644 index 0000000..def00c1 --- /dev/null +++ b/specification/spec-core-v2_0_0-changes/index.html @@ -0,0 +1,252 @@ + + + + + + + + + The Homie convention + + + + + + + + + + + + + +
+
+ +
+ + + +
+
+ homieiot/convention +
+
+
+ + + +
+ +
+
+
+

+ +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Commit titleDateHash
Add MIT LICENSE - closes #212017-04-28 10:06:43 +02003bcbb01
:memo: Add link to hodmin cli-client (#19)2017-04-17 22:15:19 +0200f137694
:memo: Fix typo in README.md (#20)2017-04-17 22:14:44 +0200ec0b666
:memo: Add note that the v2 might still change before final release2017-02-13 10:12:11 +0100279f55d
:art: Fix backticks inside table2016-11-16 19:52:15 +0100bff4047
:sparkles: Add format of $mac2016-11-16 19:51:26 +0100c9b402c
:sparkles: Say $online must be sent last2016-11-16 12:33:46 +010023f4f8f
:sparkles: Add MAC address2016-11-16 12:25:31 +0100b4ce39d
:bug: Remove old $ota left-over2016-11-16 12:21:42 +0100724dfc5
:sparkles: Add firmware checksum2016-11-16 12:19:50 +0100284a1f9
:art: Create a stats device property - fix #82016-10-26 16:40:44 +02002e28318
:fire: Remove $ota - now implementation specific2016-10-24 16:46:27 +02009f8a584
:sparkles: Make $signal specify an interval too - fix #82016-10-24 15:58:56 +0200b5f27f0
:docs: Add reference to homie-python (#9)2016-10-17 22:37:35 +0200c181ec0
Center title2016-10-12 19:46:47 +0200860b26a
Add ID format section and cosmetic tweaks2016-10-11 13:13:07 +02007f5e582
Make banner transparent2016-10-10 10:23:02 +0200b2a1931
Replace devices base topic by homie2016-10-03 09:59:09 +0200b0ec78f
Add broadcast channel2016-09-07 19:36:51 +0200cd84f93
Add range properties2016-08-27 11:17:48 +02002d5e7fe
Remove useless $nodes2016-08-19 14:52:04 +02003bba8d3
Add $type and $properties to node2016-08-19 14:48:23 +0200d92ceb9
Remove old $reset property2016-08-16 18:47:49 +0200675f0aa
Cosmetic changes and typo fixes2016-08-16 00:31:00 +0200d5f4ad1
Allow implementation-specific properties2016-08-15 00:39:32 +020062bc55d
Fix typos2016-08-13 11:31:40 +020014f9d81
First v2 changes2016-08-13 11:26:27 +0200658f2b3
+ +
+
+
+ + + +
+ +
+ + + + diff --git a/specification/spec-core-v2_0_0-diff/index.html b/specification/spec-core-v2_0_0-diff/index.html new file mode 100644 index 0000000..b243c91 --- /dev/null +++ b/specification/spec-core-v2_0_0-diff/index.html @@ -0,0 +1,495 @@ + + + + + + + + + The Homie convention + + + + + + + + + + + + + +
+
+ +
+ + + +
+
+ homieiot/convention +
+
+
+ + + +
+ +
+
+
+

+ Changes +

+
Changes from + v1.5.0 to + v2.0.0
+ +

Commits

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Commit titleDateHash
Add MIT LICENSE - closes #212017-04-28 10:06:43 +02003bcbb01
:memo: Add link to hodmin cli-client (#19)2017-04-17 22:15:19 +0200f137694
:memo: Fix typo in README.md (#20)2017-04-17 22:14:44 +0200ec0b666
:memo: Add note that the v2 might still change before final release2017-02-13 10:12:11 +0100279f55d
:art: Fix backticks inside table2016-11-16 19:52:15 +0100bff4047
:sparkles: Add format of $mac2016-11-16 19:51:26 +0100c9b402c
:sparkles: Say $online must be sent last2016-11-16 12:33:46 +010023f4f8f
:sparkles: Add MAC address2016-11-16 12:25:31 +0100b4ce39d
:bug: Remove old $ota left-over2016-11-16 12:21:42 +0100724dfc5
:sparkles: Add firmware checksum2016-11-16 12:19:50 +0100284a1f9
:art: Create a stats device property - fix #82016-10-26 16:40:44 +02002e28318
:fire: Remove $ota - now implementation specific2016-10-24 16:46:27 +02009f8a584
:sparkles: Make $signal specify an interval too - fix #82016-10-24 15:58:56 +0200b5f27f0
:docs: Add reference to homie-python (#9)2016-10-17 22:37:35 +0200c181ec0
Center title2016-10-12 19:46:47 +0200860b26a
Add ID format section and cosmetic tweaks2016-10-11 13:13:07 +02007f5e582
Make banner transparent2016-10-10 10:23:02 +0200b2a1931
Replace devices base topic by homie2016-10-03 09:59:09 +0200b0ec78f
Add broadcast channel2016-09-07 19:36:51 +0200cd84f93
Add range properties2016-08-27 11:17:48 +02002d5e7fe
Remove useless $nodes2016-08-19 14:52:04 +02003bba8d3
Add $type and $properties to node2016-08-19 14:48:23 +0200d92ceb9
Remove old $reset property2016-08-16 18:47:49 +0200675f0aa
Cosmetic changes and typo fixes2016-08-16 00:31:00 +0200d5f4ad1
Allow implementation-specific properties2016-08-15 00:39:32 +020062bc55d
Fix typos2016-08-13 11:31:40 +020014f9d81
First v2 changes2016-08-13 11:26:27 +0200658f2b3
+ + + +

Differences

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

v1.5.0
v2.0.0
n3version: v1.5.0n3version: v2.0.0
4releasedate: 19. June 20164releasedate: 28. April 2017
n13An instance of a physical piece of hardware (an Arduino, an ESP8266...) is called a **device**. A device has **device properties**, like the current local IP, the Wi-Fi signal, etc. A device can expose multiple **nodes**. For example, a weather device might expose a temperature node and an humidity node. A node can have multiple **node properties**. The temperature node might for example expose a temperature property containing the actual temperature, and an unit property. Properties can be **settable**. For example, you don't want your `temperature` property to be settable in case of a temperature sensor: this depends on the environment and it would not make sense to change it. However, you will want the `temperature` property to be settable in case of a thermostat.n13An instance of a physical piece of hardware (an Arduino, an ESP8266...) is called a **device**. A device has **device properties**, like the current local IP, the Wi-Fi signal, etc. A device can expose multiple **nodes**. For example, a weather device might expose a `temperature` node and an `humidity` node. A node can have multiple **node properties**. The `temperature` node might for example expose a `degrees` property containing the actual temperature, and an `unit` property. Node properties can be **ranges**. For example, if you have a LED strip, you can have a node property `led` ranging from `1` to `10`, to control LEDs independently. Node properties can be **settable**. For example, you don't want your `degrees` property to be settable in case of a temperature sensor: this depends on the environment and it would not make sense to change it. However, you will want the `degrees` property to be settable in case of a thermostat.
14
15## QoS and retained messages
16
17Homie devices communicate through MQTT.
18
19The nature of the Homie convention makes it safe about duplicate messages, so the recommended QoS for reliability is **QoS 1**. All messages MUST be sent as **retained**, UNLESS stated otherwise.
20
21## ID format
22
23An ID MAY contain only lowercase letters from `a` to `z`, numbers from `0` to `9`, and it MAY contain `-`, but MUST NOT start or end with a `-`.
n17Homie devices communicate through MQTT.n27To efficiently parse messages, Homie defines a few rules related to topic names. The base topic you will see in the following convention will be `homie/`. You can however choose whatever base topic you want.
n19To efficiently parse messages, Homie defines a few rules related to topic names. The base topic you will see in the following lines will be `devices/`. You can customize this base topic if it fits better to your needs.n29* `homie` / **`device ID`**: this is the base topic of a device. Each device must have an unique device ID which adhere to the [ID format](#id-format).
20
21* `devices` / **`device ID`**: this is the base topic name. Each device must have a unique device ID. This ID MAY be composed of lowercase letters from `a` to `z`, numbers from `0` to `9`, and it MAY contain `-`, but MUST NOT start or end with a `-`.
n25* `devices` / **`device ID`** / `$` **`device property`**: a property starting with a `$` at the third level of the path is related to the device. The property MUST be one of these:n33* `homie` / **`device ID`** / `$` **`device property`**: a topic starting with a `$` after the base topic of a device represents a device property. A device property MUST be one of these:
nn41 <th>Required</th>
42 </tr>
43 <tr>
44 <td>$homie</td>
45 <td>Device → Controller</td>
46 <td>Version of the Homie convention the device conforms to</td>
47 <td>Yes</td>
48 <td>Yes</td>
n37 <td><code>true</code> when the device is online, <code>false</code> when the device is offline (through LWT)</td>n53 <td><code>true</code> when the device is online, <code>false</code> when the device is offline (through LWT). When sending the device is online, this message must be sent last, to indicate every other required messages are sent and the device is ready</td>
54 <td>Yes</td>
nn62 <td>Yes</td>
n51 </tr>n69 <td>Yes</td>
52 <tr>70 </tr>
71 <tr>
72 <td>$mac</td>
73 <td>Device → Controller</td>
74 <td>Mac address of the device network interface. The format MUST be of the type <code>A1:B2:C3:D4:E5:F6</code></td>
75 <td>Yes</td>
76 <td>Yes</td>
77 </tr>
78 <tr>
53 <td>$uptime</td>79 <td>$stats/uptime</td>
n57 </tr>n83 <td>Yes</td>
58 <tr>84 </tr>
85 <tr>
59 <td>$signal</td>86 <td>$stats/signal</td>
n63 </tr>n90 <td>No, this is not applicable to an Ethernet connected device for example</td>
64 <tr>91 </tr>
92 <tr>
93 <td>$stats/interval</td>
94 <td>Device → Controller</td>
95 <td>Interval in seconds at which the <code>$stats/uptime</code> and <code>$stats/signal</code> are refreshed</td>
96 <td>Yes</td>
97 <td>Yes</td>
98 </tr>
99 <tr>
65 <td>$fwname</td>100 <td>$fw/name</td>
n67 <td>Name of the firmware running on the device. This name MAY be composed of lowercase letters from <code>a</code> to <code>z</code>, numbers from <code>0</code> to <code>9</code>, and it MAY contain <code>-</code>, but MUST NOT start or end with a <code>-</code></td>n102 <td>Name of the firmware running on the device. Allowed characters are the same as the device ID</td>
n69 </tr>n104 <td>Yes</td>
70 <tr>105 </tr>
106 <tr>
71 <td>$fwversion</td>107 <td>$fw/version</td>
n75 </tr>n111 <td>Yes</td>
76 <tr>112 </tr>
77 <td>$nodes</td>
78 <td>Device → Controller</td>
79 <td>Nodes the device has, with format <code>id:type</code> separated by a <code>,</code> if there are multiple nodes</td>
80 <td>Yes</td>
81 </tr>113 <tr>
114 <td>$fw/checksum</td>
115 <td>Device → Controller</td>
116 <td>MD5 checksum of the firmware running on the device</td>
117 <td>Yes</td>
118 <td>No, depending of your implementation</td>
82 <tr>119 </tr>
120 <tr>
121 <td>$implementation</td>
122 <td>Device → Controller</td>
123 <td>An identifier for the Homie implementation (example <code>esp8266</code>)</td>
83 <td>$ota</td>124 <td>Yes</td>
84 <td>Controller → Device</td>125 <td>Yes</td>
85 <td>Latest OTA version available for the device</td>126 </tr>
127 <tr>
128 <td>$implementation/#</td>
129 <td>Controller → Device or Device → Controller</td>
130 <td>You can use any subtopics of <code>$implementation</code> for anything related to your specific Homie implementation.</td>
n87 </tr>n
88 <tr>
89 <td>$ota/+</td>
90 <td>Controller → Device or Device → Controller</td>
91 <td>You can use any subtopics of `$ota` for anything related to your specific OTA implementation.</td>
92 <td>Yes or No, depending of your implementation</td>
93 </tr>
94 <tr>
95 <td>$reset</td>
96 <td>Controller → Device</td>
97 <td><code>true</code> when the controller wants the device to reset its configuration. <code>false</code> otherwise. When the device receives a <code>true</code>, it should replace the retained message with a <code>false</code> before resetting</td>
98 <td>Yes</td>132 <td>No</td>
n105devices/686f6d6965/$online → truen139homie/686f6d6965/$online → true
106devices/686f6d6965/$name → Bedroom temperature sensor140homie/686f6d6965/$name → Bedroom temperature sensor
107devices/686f6d6965/$localip → 192.168.0.10141homie/686f6d6965/$localip → 192.168.0.10
108devices/686f6d6965/$signal → 72142homie/686f6d6965/$signal → 72
109devices/686f6d6965/$fwname → 1.0.0143homie/686f6d6965/$fw/name → 1.0.0
110devices/686f6d6965/$fwversion → 1.0.0144homie/686f6d6965/$fw/version → 1.0.0
111devices/686f6d6965/$nodes → temperature:temperature,humidity:humidity
n113 n
114And it would receive:
115
116```
117devices/686f6d6965/$ota ← 1.0.1
118devices/686f6d6965/$reset ← false
119```
120
121At this point, your device would understand there is an OTA update available, as `$ota` is different from `$version`.
n125* `devices` / **`device ID`** / **`node ID`** / **`property`**: `node ID` is the ID of the node, as defined in the `$nodes` device property. `property` is the property of the node that is getting updated.n149* `homie` / **`device ID`** / **`node ID`** / **`property`**: `node ID` is the ID of the node, which must be unique on a per-device basis, and which adhere to the [ID format](#id-format). `property` is the property of the node that is getting updated, which must be unique on a per-node basis, and which adhere to the [ID format](#id-format).
150
151Properties starting with a `$` are special properties. It must be one of the following:
152
153<table>
154 <tr>
155 <th>Property</th>
156 <th>Direction</th>
157 <th>Description</th>
158 <th>Retained</th>
159 <th>Required</th>
160 </tr>
161 <tr>
162 <td>$type</td>
163 <td>Device → Controller</td>
164 <td>Type of the node</td>
165 <td>Yes</td>
166 <td>Yes</td>
167 </tr>
168 <tr>
169 <td>$properties</td>
170 <td>Device → Controller</td>
171 <td>Properties the node exposes, with format <code>id</code> separated by a <code>,</code> if there are multiple nodes. For ranges, define the range after the <code>id</code>, within <code>[]</code> and separated by a <code>-</code>. For settable properties, add <code>:settable</code> to the <code>id</code></td>
172 <td>Yes</td>
173 <td>Yes</td>
174 </tr>
175</table>
nn180homie/686f6d6965/temperature/$type → temperature
181homie/686f6d6965/temperature/$properties → degrees,unit
182homie/686f6d6965/temperature/unit → c
130devices/686f6d6965/temperature/temperature → 12.07183homie/686f6d6965/temperature/degrees → 12.07
131devices/686f6d6965/humidity/humidity → 79
132```
nn185homie/686f6d6965/humidity/$type → humidity
186homie/686f6d6965/humidity/$properties → percentage
187homie/686f6d6965/humidity/percentage → 79
188```
189
190A LED strip would look like this. Note that the topic for a range properties is the name of the property followed by a `_` and the index getting updated:
191
192```
193homie/ledstrip-device/ledstrip/$type → ledstrip
194homie/ledstrip-device/ledstrip/$properties → led[1-3]:settable
195homie/ledstrip-device/ledstrip/led_1 → on
196homie/ledstrip-device/ledstrip/led_2 → off
197homie/ledstrip-device/ledstrip/led_3 → on
198```
199
134* `devices` / **`device ID`** / **`node ID`** / **`property`** / `set`: the device can subscribe to this topic if the property is **settable** from the controller, in case of actuators.200* `homie` / **`device ID`** / **`node ID`** / **`property`** / `set`: the device can subscribe to this topic if the property is **settable** from the controller, in case of actuators.
n136Homie is state-based. You don't tell your smarlight to turn on, but you tell it to put it's `on` state to `true`. This especially fits well with MQTT, because of retained message.n202Homie is state-based. You don't tell your smartlight to `turn on`, but you tell it to put it's `on` state to `true`. This especially fits well with MQTT, because of retained message.
n138For example, an `homielight` device exposing a `light` node would subscribe to `devices/homielight/light/on/set` and it would receive:n204For example, a `kitchen-light` device exposing a `light` node would subscribe to `homie/kitchen-light/light/on/set` and it would receive:
n141devices/homielight/light/on/set ← truen207homie/kitchen-light/light/on/set ← true
t147devices/homielight/light/on → truet213homie/kitchen-light/light/on → true
214```
215
216### Broadcast channel
217
218Homie defines a broadcast channel, so a controller is able to broadcast a message to every Homie devices:
219
220* `homie` / `$broadcast` / **`level`**: `level` is an arbitrary broadcast identifier. It must adhere to the [ID format](#id-format).
221
222For example, you might want to broadcast an `alert` event with the alert reason as the payload. Devices are then free to react or not. In our case, every buzzer of your home automation system would start buzzing.
223
224```
225homie/$broadcast/alert ← Intruder detected
+
+
+
+ + + +
+ +
+ + + + diff --git a/specification/spec-core-v2_0_0/index.html b/specification/spec-core-v2_0_0/index.html new file mode 100644 index 0000000..5856a92 --- /dev/null +++ b/specification/spec-core-v2_0_0/index.html @@ -0,0 +1,393 @@ + + + + + + + + + The Homie convention + + + + + + + + + + + + + +
+
+ +
+ + + +
+
+ homieiot/convention +
+
+
+ + + +
+ +
+ + + + + + + + + + +
+
+
+

Homie: An MQTT Convention for IoT/M2M

+
+
+
+ +
+ + + +
+ + License: CCA 4.0
+ Version: + + + + [develop] + + + + [v1.5.0] + + + + [v2.0.0] + + + + [v2.0.1] + + + + [v3.0.0] + + + + [v3.0.1] + + + + [v4.0.0] + +
+ + Changes: [Diff to previous]
+ + Release date: 28. April 2017 +
+
+ +
+ Frequently asked questions + +

How do I query/request a property?

+

You don’t. +The MQTT protocol does not implement the request-reply but rather the publish-subscribe messaging pattern. +The Homie convention follows the publish-subscribe principle by publishing data as retained messages on a regular basis. +You might want to rethink the design of your application - in most scenarios a regularly updated information is sufficient.

+

Workaround: You are free to implement your own ideas on top of the basic structure of the Homie convention. +You could either implement a get getter topic and its logic to trigger a value update, or you may exploit the concept of Homie properties and define a settable property to trigger a value update.

+ + +
+ +
+

License

+

By exercising the Licensed Rights (defined on https://creativecommons.org/licenses/by/4.0/), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions.

+
+ +
+

Table of Contents

+
+ + +

+

Background

+

An instance of a physical piece of hardware (an Arduino, an ESP8266…) is called a device. A device has device properties, like the current local IP, the Wi-Fi signal, etc. A device can expose multiple nodes. For example, a weather device might expose a temperature node and an humidity node. A node can have multiple node properties. The temperature node might for example expose a degrees property containing the actual temperature, and an unit property. Node properties can be ranges. For example, if you have a LED strip, you can have a node property led ranging from 1 to 10, to control LEDs independently. Node properties can be settable. For example, you don’t want your degrees property to be settable in case of a temperature sensor: this depends on the environment and it would not make sense to change it. However, you will want the degrees property to be settable in case of a thermostat.

+

QoS and retained messages

+

Homie devices communicate through MQTT.

+

The nature of the Homie convention makes it safe about duplicate messages, so the recommended QoS for reliability is QoS 1. All messages MUST be sent as retained, UNLESS stated otherwise.

+

ID format

+

An ID MAY contain only lowercase letters from a to z, numbers from 0 to 9, and it MAY contain -, but MUST NOT start or end with a -.

+

Convention

+

To efficiently parse messages, Homie defines a few rules related to topic names. The base topic you will see in the following convention will be homie/. You can however choose whatever base topic you want.

+
    +
  • homie / device ID: this is the base topic of a device. Each device must have an unique device ID which adhere to the ID format.
  • +
+

Device properties

+
    +
  • homie / device ID / $ device property: a topic starting with a $ after the base topic of a device represents a device property. A device property MUST be one of these:
  • +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PropertyDirectionDescriptionRetainedRequired
$homieDevice → ControllerVersion of the Homie convention the device conforms toYesYes
$onlineDevice → Controllertrue when the device is online, false when the device is offline (through LWT). When sending the device is online, this message must be sent last, to indicate every other required messages are sent and the device is readyYesYes
$nameDevice → ControllerFriendly name of the deviceYesYes
$localipDevice → ControllerIP of the device on the local networkYesYes
$macDevice → ControllerMac address of the device network interface. The format MUST be of the type A1:B2:C3:D4:E5:F6YesYes
$stats/uptimeDevice → ControllerTime elapsed in seconds since the boot of the deviceYesYes
$stats/signalDevice → ControllerInteger representing the Wi-Fi signal quality in percentage if applicableYesNo, this is not applicable to an Ethernet connected device for example
$stats/intervalDevice → ControllerInterval in seconds at which the $stats/uptime and $stats/signal are refreshedYesYes
$fw/nameDevice → ControllerName of the firmware running on the device. Allowed characters are the same as the device IDYesYes
$fw/versionDevice → ControllerVersion of the firmware running on the deviceYesYes
$fw/checksumDevice → ControllerMD5 checksum of the firmware running on the deviceYesNo, depending of your implementation
$implementationDevice → ControllerAn identifier for the Homie implementation (example esp8266)YesYes
$implementation/#Controller → Device or Device → ControllerYou can use any subtopics of $implementation for anything related to your specific Homie implementation.Yes or No, depending of your implementationNo
+

For example, a device with an ID of 686f6d6965 with a temperature and an humidity sensor would send:

+
homie/686f6d6965/$online → true
+homie/686f6d6965/$name → Bedroom temperature sensor
+homie/686f6d6965/$localip → 192.168.0.10
+homie/686f6d6965/$signal → 72
+homie/686f6d6965/$fw/name → 1.0.0
+homie/686f6d6965/$fw/version → 1.0.0
+

Node properties

+
    +
  • homie / device ID / node ID / property: node ID is the ID of the node, which must be unique on a per-device basis, and which adhere to the ID format. property is the property of the node that is getting updated, which must be unique on a per-node basis, and which adhere to the ID format.
  • +
+

Properties starting with a $ are special properties. It must be one of the following:

+ + + + + + + + + + + + + + + + + + + + + + +
PropertyDirectionDescriptionRetainedRequired
$typeDevice → ControllerType of the nodeYesYes
$propertiesDevice → ControllerProperties the node exposes, with format id separated by a , if there are multiple nodes. For ranges, define the range after the id, within [] and separated by a -. For settable properties, add :settable to the idYesYes
+

For example, our 686f6d6965 above would send:

+
homie/686f6d6965/temperature/$type → temperature
+homie/686f6d6965/temperature/$properties → degrees,unit
+homie/686f6d6965/temperature/unit → c
+homie/686f6d6965/temperature/degrees → 12.07
+
+homie/686f6d6965/humidity/$type → humidity
+homie/686f6d6965/humidity/$properties → percentage
+homie/686f6d6965/humidity/percentage → 79
+

A LED strip would look like this. Note that the topic for a range properties is the name of the property followed by a _ and the index getting updated:

+
homie/ledstrip-device/ledstrip/$type → ledstrip
+homie/ledstrip-device/ledstrip/$properties → led[1-3]:settable
+homie/ledstrip-device/ledstrip/led_1 → on
+homie/ledstrip-device/ledstrip/led_2 → off
+homie/ledstrip-device/ledstrip/led_3 → on
+
    +
  • homie / device ID / node ID / property / set: the device can subscribe to this topic if the property is settable from the controller, in case of actuators.
  • +
+

Homie is state-based. You don’t tell your smartlight to turn on, but you tell it to put it’s on state to true. This especially fits well with MQTT, because of retained message.

+

For example, a kitchen-light device exposing a light node would subscribe to homie/kitchen-light/light/on/set and it would receive:

+
homie/kitchen-light/light/on/set ← true
+

The device would then turn on the light, and update its on state. This provides pessimistic feedback, which is important for home automation.

+
homie/kitchen-light/light/on → true
+

Broadcast channel

+

Homie defines a broadcast channel, so a controller is able to broadcast a message to every Homie devices:

+
    +
  • homie / $broadcast / level: level is an arbitrary broadcast identifier. It must adhere to the ID format.
  • +
+

For example, you might want to broadcast an alert event with the alert reason as the payload. Devices are then free to react or not. In our case, every buzzer of your home automation system would start buzzing.

+
homie/$broadcast/alert ← Intruder detected
+

Any other topic is not part of the Homie convention.

+ +
+ +
+
+ + + +
+ +
+ + + + diff --git a/specification/spec-core-v2_0_1-changes/index.html b/specification/spec-core-v2_0_1-changes/index.html new file mode 100644 index 0000000..c326f1b --- /dev/null +++ b/specification/spec-core-v2_0_1-changes/index.html @@ -0,0 +1,127 @@ + + + + + + + + + The Homie convention + + + + + + + + + + + + + +
+
+ +
+ + + +
+
+ homieiot/convention +
+
+
+ + + +
+ +
+
+
+

+ +

+
+ + + + + + + + + + + + + + + + + + + + +
Commit titleDateHash
Add Node JS Implementation2018-03-24 12:57:05 +1100b1f7ec0
2.0.1 Added $nodes to a device2018-03-24 08:27:05 +1100775ee43
+ +
+
+
+ + + +
+ +
+ + + + diff --git a/specification/spec-core-v2_0_1-diff/index.html b/specification/spec-core-v2_0_1-diff/index.html new file mode 100644 index 0000000..2539624 --- /dev/null +++ b/specification/spec-core-v2_0_1-diff/index.html @@ -0,0 +1,158 @@ + + + + + + + + + The Homie convention + + + + + + + + + + + + + +
+
+ +
+ + + +
+
+ homieiot/convention +
+
+
+ + + +
+ +
+
+
+

+ Changes +

+
Changes from + v2.0.0 to + v2.0.1
+ +

Commits

+ + + + + + + + + + + + + + + + + + + + + + + +
Commit titleDateHash
Add Node JS Implementation2018-03-24 12:57:05 +1100b1f7ec0
2.0.1 Added $nodes to a device2018-03-24 08:27:05 +1100775ee43
+ + + +

Differences

+ + + + + + + + + + + + + + + + + + + +

v2.0.0
v2.0.1
n3version: v2.0.0n3version: v2.0.1
4releasedate: 28. April 20174releasedate: 24. March 2018
tt133 </tr>
134 <tr>
135 <td>$nodes</td>
136 <td>Device → Controller</td>
137 <td>Nodes the device exposes, with format <code>id</code> separated by a <code>,</code> if there are multiple nodes.</td>
138 <td>Yes</td>
139 <td>Yes</td>
+
+
+
+ + + +
+ +
+ + + + diff --git a/specification/spec-core-v2_0_1/index.html b/specification/spec-core-v2_0_1/index.html new file mode 100644 index 0000000..13387a4 --- /dev/null +++ b/specification/spec-core-v2_0_1/index.html @@ -0,0 +1,400 @@ + + + + + + + + + The Homie convention + + + + + + + + + + + + + +
+
+ +
+ + + +
+
+ homieiot/convention +
+
+
+ + + +
+ +
+ + + + + + + + + + +
+
+
+

Homie: An MQTT Convention for IoT/M2M

+
+
+
+ +
+ + + +
+ + License: CCA 4.0
+ Version: + + + + [develop] + + + + [v1.5.0] + + + + [v2.0.0] + + + + [v2.0.1] + + + + [v3.0.0] + + + + [v3.0.1] + + + + [v4.0.0] + +
+ + Changes: [Diff to previous]
+ + Release date: 24. March 2018 +
+
+ +
+ Frequently asked questions + +

How do I query/request a property?

+

You don’t. +The MQTT protocol does not implement the request-reply but rather the publish-subscribe messaging pattern. +The Homie convention follows the publish-subscribe principle by publishing data as retained messages on a regular basis. +You might want to rethink the design of your application - in most scenarios a regularly updated information is sufficient.

+

Workaround: You are free to implement your own ideas on top of the basic structure of the Homie convention. +You could either implement a get getter topic and its logic to trigger a value update, or you may exploit the concept of Homie properties and define a settable property to trigger a value update.

+ + +
+ +
+

License

+

By exercising the Licensed Rights (defined on https://creativecommons.org/licenses/by/4.0/), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions.

+
+ +
+

Table of Contents

+
+ + +

+

Background

+

An instance of a physical piece of hardware (an Arduino, an ESP8266…) is called a device. A device has device properties, like the current local IP, the Wi-Fi signal, etc. A device can expose multiple nodes. For example, a weather device might expose a temperature node and an humidity node. A node can have multiple node properties. The temperature node might for example expose a degrees property containing the actual temperature, and an unit property. Node properties can be ranges. For example, if you have a LED strip, you can have a node property led ranging from 1 to 10, to control LEDs independently. Node properties can be settable. For example, you don’t want your degrees property to be settable in case of a temperature sensor: this depends on the environment and it would not make sense to change it. However, you will want the degrees property to be settable in case of a thermostat.

+

QoS and retained messages

+

Homie devices communicate through MQTT.

+

The nature of the Homie convention makes it safe about duplicate messages, so the recommended QoS for reliability is QoS 1. All messages MUST be sent as retained, UNLESS stated otherwise.

+

ID format

+

An ID MAY contain only lowercase letters from a to z, numbers from 0 to 9, and it MAY contain -, but MUST NOT start or end with a -.

+

Convention

+

To efficiently parse messages, Homie defines a few rules related to topic names. The base topic you will see in the following convention will be homie/. You can however choose whatever base topic you want.

+
    +
  • homie / device ID: this is the base topic of a device. Each device must have an unique device ID which adhere to the ID format.
  • +
+

Device properties

+
    +
  • homie / device ID / $ device property: a topic starting with a $ after the base topic of a device represents a device property. A device property MUST be one of these:
  • +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PropertyDirectionDescriptionRetainedRequired
$homieDevice → ControllerVersion of the Homie convention the device conforms toYesYes
$onlineDevice → Controllertrue when the device is online, false when the device is offline (through LWT). When sending the device is online, this message must be sent last, to indicate every other required messages are sent and the device is readyYesYes
$nameDevice → ControllerFriendly name of the deviceYesYes
$localipDevice → ControllerIP of the device on the local networkYesYes
$macDevice → ControllerMac address of the device network interface. The format MUST be of the type A1:B2:C3:D4:E5:F6YesYes
$stats/uptimeDevice → ControllerTime elapsed in seconds since the boot of the deviceYesYes
$stats/signalDevice → ControllerInteger representing the Wi-Fi signal quality in percentage if applicableYesNo, this is not applicable to an Ethernet connected device for example
$stats/intervalDevice → ControllerInterval in seconds at which the $stats/uptime and $stats/signal are refreshedYesYes
$fw/nameDevice → ControllerName of the firmware running on the device. Allowed characters are the same as the device IDYesYes
$fw/versionDevice → ControllerVersion of the firmware running on the deviceYesYes
$fw/checksumDevice → ControllerMD5 checksum of the firmware running on the deviceYesNo, depending of your implementation
$implementationDevice → ControllerAn identifier for the Homie implementation (example esp8266)YesYes
$implementation/#Controller → Device or Device → ControllerYou can use any subtopics of $implementation for anything related to your specific Homie implementation.Yes or No, depending of your implementationNo
$nodesDevice → ControllerNodes the device exposes, with format id separated by a , if there are multiple nodes.YesYes
+

For example, a device with an ID of 686f6d6965 with a temperature and an humidity sensor would send:

+
homie/686f6d6965/$online → true
+homie/686f6d6965/$name → Bedroom temperature sensor
+homie/686f6d6965/$localip → 192.168.0.10
+homie/686f6d6965/$signal → 72
+homie/686f6d6965/$fw/name → 1.0.0
+homie/686f6d6965/$fw/version → 1.0.0
+

Node properties

+
    +
  • homie / device ID / node ID / property: node ID is the ID of the node, which must be unique on a per-device basis, and which adhere to the ID format. property is the property of the node that is getting updated, which must be unique on a per-node basis, and which adhere to the ID format.
  • +
+

Properties starting with a $ are special properties. It must be one of the following:

+ + + + + + + + + + + + + + + + + + + + + + +
PropertyDirectionDescriptionRetainedRequired
$typeDevice → ControllerType of the nodeYesYes
$propertiesDevice → ControllerProperties the node exposes, with format id separated by a , if there are multiple nodes. For ranges, define the range after the id, within [] and separated by a -. For settable properties, add :settable to the idYesYes
+

For example, our 686f6d6965 above would send:

+
homie/686f6d6965/temperature/$type → temperature
+homie/686f6d6965/temperature/$properties → degrees,unit
+homie/686f6d6965/temperature/unit → c
+homie/686f6d6965/temperature/degrees → 12.07
+
+homie/686f6d6965/humidity/$type → humidity
+homie/686f6d6965/humidity/$properties → percentage
+homie/686f6d6965/humidity/percentage → 79
+

A LED strip would look like this. Note that the topic for a range properties is the name of the property followed by a _ and the index getting updated:

+
homie/ledstrip-device/ledstrip/$type → ledstrip
+homie/ledstrip-device/ledstrip/$properties → led[1-3]:settable
+homie/ledstrip-device/ledstrip/led_1 → on
+homie/ledstrip-device/ledstrip/led_2 → off
+homie/ledstrip-device/ledstrip/led_3 → on
+
    +
  • homie / device ID / node ID / property / set: the device can subscribe to this topic if the property is settable from the controller, in case of actuators.
  • +
+

Homie is state-based. You don’t tell your smartlight to turn on, but you tell it to put it’s on state to true. This especially fits well with MQTT, because of retained message.

+

For example, a kitchen-light device exposing a light node would subscribe to homie/kitchen-light/light/on/set and it would receive:

+
homie/kitchen-light/light/on/set ← true
+

The device would then turn on the light, and update its on state. This provides pessimistic feedback, which is important for home automation.

+
homie/kitchen-light/light/on → true
+

Broadcast channel

+

Homie defines a broadcast channel, so a controller is able to broadcast a message to every Homie devices:

+
    +
  • homie / $broadcast / level: level is an arbitrary broadcast identifier. It must adhere to the ID format.
  • +
+

For example, you might want to broadcast an alert event with the alert reason as the payload. Devices are then free to react or not. In our case, every buzzer of your home automation system would start buzzing.

+
homie/$broadcast/alert ← Intruder detected
+

Any other topic is not part of the Homie convention.

+ +
+ +
+
+ + + +
+ +
+ + + + diff --git a/specification/spec-core-v3_0_0-changes/index.html b/specification/spec-core-v3_0_0-changes/index.html new file mode 100644 index 0000000..fb5bf24 --- /dev/null +++ b/specification/spec-core-v3_0_0-changes/index.html @@ -0,0 +1,382 @@ + + + + + + + + + The Homie convention + + + + + + + + + + + + + +
+
+ +
+ + + +
+
+ homieiot/convention +
+
+
+ + + +
+ +
+
+
+

+ +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Commit titleDateHash
Merge pull request #71 from homieiot/rolling-releases2018-03-25 08:55:18 +110096c3086
Add info regarding rolling releases2018-03-24 15:18:37 +0100c5ad0b2
Add NodeJS implementation2018-03-24 11:25:36 +1100110b68f
Small Readme version fix2018-03-21 09:00:35 +1100508f6ae
Fix Versioning.2018-03-16 07:55:10 +110009c84e4
Merge pull request #67 from marvinroger/v22018-03-12 12:01:13 +11000b90e74
Update Version2018-03-12 12:00:15 +1100be4d750
Merge branch ‘redesign’ into v22018-03-12 11:59:28 +1100e25c5dc
added micropython-homie2018-03-12 11:54:43 +110000285cb
Merge pull request #64 from microhomie/v22018-02-17 21:12:45 +11000a9e88b
Remove WIP state2018-02-12 10:49:02 +010085d6a1f
Add MicroPython implementation2018-02-10 11:56:40 +0100d7392c0
Merge pull request #63 from nerdfirefighter/redesign2018-02-03 18:44:59 +1100b071d29
Typo Fix2018-02-02 20:46:05 +110025b727f
Merge pull request #1 from marvinroger/redesign2018-02-02 20:43:27 +110071b15b9
Update Branch2018-01-24 12:08:45 +1100755b994
Merge pull request #60 from nerdfirefighter/v22018-01-24 11:50:03 +11000482588
Update README.md2018-01-17 22:10:47 +110097e5ec9
:bug: Fix typos (#55)2018-01-02 15:25:25 +01002247839
:art: Update ToC2017-11-13 16:34:15 +01008d33c76
:art: Clean up MQTT restrictions (#47)2017-11-13 16:30:32 +01003e51c9e
:art: Cosmetic change2017-11-13 16:22:54 +0100d977ceb
:fire: Make properties optional with default (#48)2017-11-10 12:12:22 +0100a19b81a
:art: Add TOC2017-11-06 15:39:44 +01001b60390
:sparkles: Add $state topic (#50)2017-11-06 15:11:13 +0100e355f4e
:art: Small comestic change2017-09-25 22:00:16 +0200ddbe9c9
:sparkles: Add a link to the tags2017-09-25 21:57:58 +0200e0e3bad
:memo: Add Node.js implementation (#42)2017-09-21 03:22:32 -0700e5dcd7e
:sparkles: Allow possibility to know when a device is fully discovered2017-08-11 20:02:20 +02000003daa
:bug: Fix node attributes array2017-08-11 12:55:26 +020090f94c6
:memo: Make clear all values are string2017-08-11 12:40:44 +020011cd9ff
:sparkles: Add color datatype2017-08-11 12:34:47 +0200a17b8b3
:fire: Replace property arrays to node arrays2017-08-11 12:14:11 +0200460e547
:memo: Change to a car example2017-08-11 11:46:49 +0200949ad7d
:art: Moved statistics to seperate section and made some clarifications, when to send device attributes. (#40)2017-08-10 17:43:27 +02007ab2e87
:fire: $unit should not be mandatory (#39)2017-08-08 11:14:43 +02001a5d314
:art: Improve example presentation with strings and java syntax (#36)2017-08-07 18:39:11 +0200d81a00a
:memo: Add example for implementation (#37)2017-08-07 17:35:18 +02002be405c
:fire: $unit should not be mandatory (#35)2017-08-07 15:43:33 +020004441dc
:sparkles: Add optional stats properties2017-08-06 14:29:31 +020008666c3
:art: Rearrange and clarify topology (#31)2017-08-06 14:17:35 +020015e8df0
:memo: Add Motivation chapter (#29)2017-08-05 19:16:02 +02001e33ab8
:memo: Add implementations file2017-08-04 16:38:43 +0200e87738f
:fire: Move implementations on another page2017-08-04 16:38:00 +0200cf92268
:bug: Fix all examples2017-08-04 16:30:26 +020065a91f9
:bug: Use more adequate examples for nodes2017-08-04 16:05:25 +0200d10e380
:sparkles: Add shared array attributes2017-08-04 15:58:41 +0200b7e1b6b
:art: Make the $format regex simple2017-08-04 15:43:33 +020020c0c13
:fire: Remove $signal and2017-08-04 15:39:59 +0200081c5af
:art: Make the structure more consistent2017-08-04 15:33:49 +02004d49797
:art: Clarify background2017-08-04 15:17:09 +0200c8a270a
:art: Make each paragraph line on a newline2017-08-04 15:10:11 +0200cae1fa5
:sparkles: Make properties discoverable (#27)2017-08-04 13:56:14 +0200779093a
+ +
+
+
+ + + +
+ +
+ + + + diff --git a/specification/spec-core-v3_0_0-diff/index.html b/specification/spec-core-v3_0_0-diff/index.html new file mode 100644 index 0000000..8912c50 --- /dev/null +++ b/specification/spec-core-v3_0_0-diff/index.html @@ -0,0 +1,979 @@ + + + + + + + + + The Homie convention + + + + + + + + + + + + + +
+
+ +
+ + + +
+
+ homieiot/convention +
+
+
+ + + +
+ +
+
+
+

+ Changes +

+
Changes from + v2.0.1 to + v3.0.0
+ +

Commits

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Commit titleDateHash
Merge pull request #71 from homieiot/rolling-releases2018-03-25 08:55:18 +110096c3086
Add info regarding rolling releases2018-03-24 15:18:37 +0100c5ad0b2
Add NodeJS implementation2018-03-24 11:25:36 +1100110b68f
Small Readme version fix2018-03-21 09:00:35 +1100508f6ae
Fix Versioning.2018-03-16 07:55:10 +110009c84e4
Merge pull request #67 from marvinroger/v22018-03-12 12:01:13 +11000b90e74
Update Version2018-03-12 12:00:15 +1100be4d750
Merge branch ‘redesign’ into v22018-03-12 11:59:28 +1100e25c5dc
added micropython-homie2018-03-12 11:54:43 +110000285cb
Merge pull request #64 from microhomie/v22018-02-17 21:12:45 +11000a9e88b
Remove WIP state2018-02-12 10:49:02 +010085d6a1f
Add MicroPython implementation2018-02-10 11:56:40 +0100d7392c0
Merge pull request #63 from nerdfirefighter/redesign2018-02-03 18:44:59 +1100b071d29
Typo Fix2018-02-02 20:46:05 +110025b727f
Merge pull request #1 from marvinroger/redesign2018-02-02 20:43:27 +110071b15b9
Update Branch2018-01-24 12:08:45 +1100755b994
Merge pull request #60 from nerdfirefighter/v22018-01-24 11:50:03 +11000482588
Update README.md2018-01-17 22:10:47 +110097e5ec9
:bug: Fix typos (#55)2018-01-02 15:25:25 +01002247839
:art: Update ToC2017-11-13 16:34:15 +01008d33c76
:art: Clean up MQTT restrictions (#47)2017-11-13 16:30:32 +01003e51c9e
:art: Cosmetic change2017-11-13 16:22:54 +0100d977ceb
:fire: Make properties optional with default (#48)2017-11-10 12:12:22 +0100a19b81a
:art: Add TOC2017-11-06 15:39:44 +01001b60390
:sparkles: Add $state topic (#50)2017-11-06 15:11:13 +0100e355f4e
:art: Small comestic change2017-09-25 22:00:16 +0200ddbe9c9
:sparkles: Add a link to the tags2017-09-25 21:57:58 +0200e0e3bad
:memo: Add Node.js implementation (#42)2017-09-21 03:22:32 -0700e5dcd7e
:sparkles: Allow possibility to know when a device is fully discovered2017-08-11 20:02:20 +02000003daa
:bug: Fix node attributes array2017-08-11 12:55:26 +020090f94c6
:memo: Make clear all values are string2017-08-11 12:40:44 +020011cd9ff
:sparkles: Add color datatype2017-08-11 12:34:47 +0200a17b8b3
:fire: Replace property arrays to node arrays2017-08-11 12:14:11 +0200460e547
:memo: Change to a car example2017-08-11 11:46:49 +0200949ad7d
:art: Moved statistics to seperate section and made some clarifications, when to send device attributes. (#40)2017-08-10 17:43:27 +02007ab2e87
:fire: $unit should not be mandatory (#39)2017-08-08 11:14:43 +02001a5d314
:art: Improve example presentation with strings and java syntax (#36)2017-08-07 18:39:11 +0200d81a00a
:memo: Add example for implementation (#37)2017-08-07 17:35:18 +02002be405c
:fire: $unit should not be mandatory (#35)2017-08-07 15:43:33 +020004441dc
:sparkles: Add optional stats properties2017-08-06 14:29:31 +020008666c3
:art: Rearrange and clarify topology (#31)2017-08-06 14:17:35 +020015e8df0
:memo: Add Motivation chapter (#29)2017-08-05 19:16:02 +02001e33ab8
:memo: Add implementations file2017-08-04 16:38:43 +0200e87738f
:fire: Move implementations on another page2017-08-04 16:38:00 +0200cf92268
:bug: Fix all examples2017-08-04 16:30:26 +020065a91f9
:bug: Use more adequate examples for nodes2017-08-04 16:05:25 +0200d10e380
:sparkles: Add shared array attributes2017-08-04 15:58:41 +0200b7e1b6b
:art: Make the $format regex simple2017-08-04 15:43:33 +020020c0c13
:fire: Remove $signal and2017-08-04 15:39:59 +0200081c5af
:art: Make the structure more consistent2017-08-04 15:33:49 +02004d49797
:art: Clarify background2017-08-04 15:17:09 +0200c8a270a
:art: Make each paragraph line on a newline2017-08-04 15:10:11 +0200cae1fa5
:sparkles: Make properties discoverable (#27)2017-08-04 13:56:14 +0200779093a
+ + + +

Differences

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

v2.0.1
v3.0.0
n3version: v2.0.1n3version: v3.0.0
4releasedate: 24. March 20184releasedate: 25. March 2018
n11## Backgroundn11## Table of Contents
n13An instance of a physical piece of hardware (an Arduino, an ESP8266...) is called a **device**. A device has **device properties**, like the current local IP, the Wi-Fi signal, etc. A device can expose multiple **nodes**. For example, a weather device might expose a `temperature` node and an `humidity` node. A node can have multiple **node properties**. The `temperature` node might for example expose a `degrees` property containing the actual temperature, and an `unit` property. Node properties can be **ranges**. For example, if you have a LED strip, you can have a node property `led` ranging from `1` to `10`, to control LEDs independently. Node properties can be **settable**. For example, you don't want your `degrees` property to be settable in case of a temperature sensor: this depends on the environment and it would not make sense to change it. However, you will want the `degrees` property to be settable in case of a thermostat.n13* [Motivation](#motivation)
14* [MQTT restrictions](#mqtt-restrictions)
15 * [Topic IDs](#topic-ids)
16 * [Payload](#payload)
17 * [QoS and retained messages](#qos-and-retained-messages)
18* [Topology](#topology)
19 * [Base topic](#base-topic)
20 * [Devices](#devices)
21 * [Device attributes](#device-attributes)
22 * [Device behavior](#device-behavior)
23 * [Device statistics](#device-statistics)
24 * [Nodes](#nodes)
25 * [Node attributes](#node-attributes)
26 * [Properties](#properties)
27 * [Property attributes](#property-attributes)
28 * [Arrays](#arrays)
29 * [Broadcast channel](#broadcast-channel)
nn31## Motivation
32
33The Homie convention strives to be a **communication definition on top of MQTT** between IoT devices and controlling entities.
34
35> [MQTT](http://mqtt.org) is a machine-to-machine (M2M)/"Internet of Things" connectivity protocol.
36> It was designed as an extremely lightweight publish/subscribe messaging transport.
37
38MQTT supports easy and unrestricted message-based communication.
39However, MQTT doesn't define the structure and content of these messages and their relation.
40An IoT device publishes data and provides interaction possibilities but a controlling entity will need to be specifically configured to be able to interface with the device.
41
42The Homie convention defines a **standardized way** of how IoT devices and services announce themselves and their data on the communication channel.
43The Homie convention is thereby a crucial aspect in the support of **automatic discovery, configuration and usage** of devices and services over the MQTT protocol.
44
45----
46
47## MQTT Restrictions
48
49Homie communicates through [MQTT](http://mqtt.org) and is hence based on the basic principles of MQTT topic publication and subscription.
50
51### Topic IDs
52
53An MQTT topic consists of one or more topic levels, separated by the slash character (`/`).
54A topic level ID MAY contain lowercase letters from `a` to `z`, numbers from `0` to `9` as well as the hyphen character (`-`).
55
56A topic level ID MUST NOT start or end with a hyphen (`-`).
57The special character `$` is used and reserved for Homie *attributes*.
58The underscore (`_`) is used and reserved for Homie *node arrays*.
59
60### Payload
61
62Every MQTT message payload MUST be sent as string.
63If a value is of a numeric data type, it MUST be converted to string.
64Booleans MUST be converted to "true" or "false".
65All values MUST be encoded as UTF-8 strings.
66
15## QoS and retained messages67### QoS and retained messages
n17Homie devices communicate through MQTT.n
18
19The nature of the Homie convention makes it safe about duplicate messages, so the recommended QoS for reliability is **QoS 1**. All messages MUST be sent as **retained**, UNLESS stated otherwise.69The nature of the Homie convention makes it safe about duplicate messages, so the recommended QoS for reliability is **QoS 1**.
70All messages MUST be sent as **retained**, UNLESS stated otherwise.
n21## ID formatn72## Topology
n23An ID MAY contain only lowercase letters from `a` to `z`, numbers from `0` to `9`, and it MAY contain `-`, but MUST NOT start or end with a `-`.n74**Devices:**
75An instance of a physical piece of hardware is called a *device*.
76For example, a car, an Arduino/ESP8266 or a coffee machine.
n25## Conventionn78**Nodes:**
79A *device* can expose multiple *nodes*.
80Nodes are independent or logically separable parts of a device.
81For example, a car might expose a `wheels` node, an `engine` node and a `lights` node.
n27To efficiently parse messages, Homie defines a few rules related to topic names. The base topic you will see in the following convention will be `homie/`. You can however choose whatever base topic you want.n83Nodes can be **arrays**.
84For example, instead of creating two `lights` node to control front lights and back lights independently, we can set the `lights` node to be an array with two elements.
n29* `homie` / **`device ID`**: this is the base topic of a device. Each device must have an unique device ID which adhere to the [ID format](#id-format).n86**Properties:**
87A *node* can have multiple *properties*.
88Properties represent basic characteristics of the node/device, often given as numbers or finite states.
89For example the `wheels` node might expose an `angle` property.
90The `engine` node might expose a `speed`, `direction` and `temperature` property.
91The `lights` node might expose an `intensity` and a `color` property.
n31### Device propertiesn93Properties can be **settable**.
94For example, you don't want your `temperature` property to be settable in case of a temperature sensor (like the car example), but to be settable in case of a thermostat.
n33* `homie` / **`device ID`** / `$` **`device property`**: a topic starting with a `$` after the base topic of a device represents a device property. A device property MUST be one of these:n96**Attributes:**
97*Devices, nodes and properties* have specific *attributes* characterizing them.
98Attributes are represented by topic identifier starting with `$`.
99The precise definition of attributes is important for the automatic discovery of devices following the Homie convention.
100
101Examples: A device might have an `IP` attribute, a node will have a `name` attribute, and a property will have a `unit` attribute.
102
103----
104
105### Base Topic
106
107The base topic you will see in the following convention will be `homie/`.
108If this base topic does not suit your needs (in case of, e.g., a public broker), you can choose another.
109
110Be aware, that only the default base topic `homie/` is eligible for automatic discovery by third party controllers.
111
112----
113
114### Devices
115
116* `homie` / **`device ID`**: this is the base topic of a device.
117Each device must have a unique device ID which adhere to the [ID format](#topic-ids).
118
119#### Device Attributes
120
121* `homie` / `device ID` / **`$device-attribute`**:
122When the MQTT connection to the broker is established or re-established, the device MUST send its attributes to the broker immediately.
n37 <th>Property</th>n126 <th>Topic</th>
n51 <td>$online</td>n
52 <td>Device → Controller</td>
53 <td><code>true</code> when the device is online, <code>false</code> when the device is offline (through LWT). When sending the device is online, this message must be sent last, to indicate every other required messages are sent and the device is ready</td>
54 <td>Yes</td>
55 <td>Yes</td>
56 </tr>
57 <tr>
nn147 <td>$state</td>
148 <td>Device → Controller</td>
149 <td>
150 See <a href="#device-behavior">Device behavior</a>
151 </td>
152 <td>Yes</td>
153 <td>Yes</td>
154 </tr>
155 <tr>
n79 <td>$stats/uptime</td>n
80 <td>Device → Controller</td>
81 <td>Time elapsed in seconds since the boot of the device</td>
82 <td>Yes</td>
83 <td>Yes</td>
84 </tr>
85 <tr>
86 <td>$stats/signal</td>
87 <td>Device → Controller</td>
88 <td>Integer representing the Wi-Fi signal quality in percentage if applicable</td>
89 <td>Yes</td>
90 <td>No, this is not applicable to an Ethernet connected device for example</td>
91 </tr>
92 <tr>
93 <td>$stats/interval</td>
94 <td>Device → Controller</td>
95 <td>Interval in seconds at which the <code>$stats/uptime</code> and <code>$stats/signal</code> are refreshed</td>
96 <td>Yes</td>
97 <td>Yes</td>
98 </tr>
99 <tr>
n114 <td>$fw/checksum</td>n
115 <td>Device → Controller</td>
116 <td>MD5 checksum of the firmware running on the device</td>
117 <td>Yes</td>184 <td>$nodes</td>
118 <td>No, depending of your implementation</td>185 <td>Device → Controller</td>
186 <td>
187 Nodes the device exposes, with format <code>id</code> separated by a <code>,</code> if there are multiple nodes.
188 To make a node an array, append <code>[]</code> to the ID.
189 </td>
190 <td>Yes</td>
191 <td>Yes</td>
nn208 <td>$stats</td>
209 <td>Device → Controller</td>
210 <td>Specify all optional stats that the device will announce, with format <code>stats</code> separated by a <code>,</code> if there are multiple stats. See next section for an example</td>
135 <td>$nodes</td>211 <td>Yes</td>
212 <td>Yes</td>
213 </tr>
214 <tr>
215 <td>$stats/interval</td>
n137 <td>Nodes the device exposes, with format <code>id</code> separated by a <code>,</code> if there are multiple nodes.</td>n217 <td>Interval in seconds at which the device refreshes its <code>$stats/+</code>: See next section for details about statistical attributes</td>
n143For example, a device with an ID of `686f6d6965` with a temperature and an humidity sensor would send:n223For example, a device with an ID of `super-car` that comprises off a `wheels`, `engine` and a `lights` node would send:
nn225```java
226homie/super-car/$homie → "2.1.0"
227homie/super-car/$name → "Super car"
228homie/super-car/$localip → "192.168.0.10"
229homie/super-car/$mac → "DE:AD:BE:EF:FE:ED"
230homie/super-car/$fw/name → "weatherstation-firmware"
231homie/super-car/$fw/version → "1.0.0"
232homie/super-car/$nodes → "wheels,engine,lights[]"
233homie/super-car/$implementation → "esp8266"
234homie/super-car/$stats/interval → "60"
235homie/super-car/$state → "ready"
n146homie/686f6d6965/$online → truen
147homie/686f6d6965/$name → Bedroom temperature sensor
148homie/686f6d6965/$localip → 192.168.0.10
149homie/686f6d6965/$signal → 72
150homie/686f6d6965/$fw/name → 1.0.0
151homie/686f6d6965/$fw/version → 1.0.0
152```
n154### Node propertiesn238#### Device Behavior
n156* `homie` / **`device ID`** / **`node ID`** / **`property`**: `node ID` is the ID of the node, which must be unique on a per-device basis, and which adhere to the [ID format](#id-format). `property` is the property of the node that is getting updated, which must be unique on a per-node basis, and which adhere to the [ID format](#id-format).n240The `$state` device attribute represents, as the name suggests, the current state of the device.
241There are 6 different states:
n158Properties starting with a `$` are special properties. It must be one of the following:n243* **`init`**: this is the state the device is in when it is connected to the MQTT broker, but has not yet sent all Homie messages and is not yet ready to operate.
244This is the first message that must that must be sent.
245* **`ready`**: this is the state the device is in when it is connected to the MQTT broker, has sent all Homie messages and is ready to operate.
246You have to send this message after all other announcements message have been sent.
247* **`disconnected`**: this is the state the device is in when it is cleanly disconnected from the MQTT broker.
248You must send this message before cleanly disconnecting.
249* **`sleeping`**: this is the state the device is in when the device is sleeping.
250You have to send this message before sleeping.
251* **`lost`**: this is the state the device is in when the device has been "badly" disconnected.
252You must define this message as LWT.
253* **`alert`**: this is the state the device is when connected to the MQTT broker, but something wrong is happening. E.g. a sensor is not providing data and needs human intervention.
254You have to send this message when something is wrong.
255
256#### Device Statistics
257
258* `homie` / `device ID` / `$stats`/ **`$device-statistic-attribute`**:
259The `$stats/` hierarchy allows to send device attributes that change over time. The device MUST send them every `$stats/interval` seconds.
n162 <th>Property</th>n263 <th>Topic</th>
nn270 <td>$stats/uptime</td>
271 <td>Device → Controller</td>
272 <td>Time elapsed in seconds since the boot of the device</td>
273 <td>Yes</td>
274 <td>Yes</td>
275 </tr>
276 <tr>
277 <td>$stats/signal</td>
278 <td>Device → Controller</td>
279 <td>Signal strength in %</td>
280 <td>Yes</td>
281 <td>No</td>
282 </tr>
283 <tr>
284 <td>$stats/cputemp</td>
285 <td>Device → Controller</td>
286 <td>CPU Temperature in °C</td>
287 <td>Yes</td>
288 <td>No</td>
289 </tr>
290 <tr>
291 <td>$stats/cpuload</td>
292 <td>Device → Controller</td>
293 <td>
294 CPU Load in %.
295 Average of last <code>$interval</code> including all CPUs
296 </td>
297 <td>Yes</td>
298 <td>No</td>
299 </tr>
300 <tr>
301 <td>$stats/battery</td>
302 <td>Device → Controller</td>
303 <td>Battery level in %</td>
304 <td>Yes</td>
305 <td>No</td>
306 </tr>
307 <tr>
308 <td>$stats/freeheap</td>
309 <td>Device → Controller</td>
310 <td>Free heap in bytes</td>
311 <td>Yes</td>
312 <td>No</td>
313 </tr>
314 <tr>
315 <td>$stats/supply</td>
316 <td>Device → Controller</td>
317 <td>Supply Voltage in V</td>
318 <td>Yes</td>
319 <td>No</td>
320 </tr>
321</table>
322
323For example, our `super-car` device with `$stats/interval` value "60" is supposed to send its current values every 60 seconds:
324
325```java
326homie/super-car/$stats → "uptime,cputemp,signal,battery"
327homie/super-car/$stats/uptime → "120"
328homie/super-car/$stats/cputemp → "48"
329homie/super-car/$stats/signal → "24"
330homie/super-car/$stats/battery → "80"
331```
332
333----
334
335### Nodes
336
337* `homie` / `device ID` / **`node ID`**: this is the base topic of a node.
338Each node must have a unique node ID on a per-device basis which adhere to the [ID format](#topic-ids).
339
340#### Node Attributes
341
342* `homie` / `device ID` / `node ID` / **`$node-attribute`**:
343A node attribute MUST be one of these:
344
345<table>
346 <tr>
347 <th>Topic</th>
348 <th>Direction</th>
349 <th>Description</th>
350 <th>Retained</th>
351 <th>Required</th>
352 </tr>
353 <tr>
354 <td>$name</td>
355 <td>Device → Controller</td>
356 <td>Friendly name of the Node</td>
357 <td>Yes</td>
358 <td>Yes</td>
359 </tr>
360 <tr>
n178 <td>Properties the node exposes, with format <code>id</code> separated by a <code>,</code> if there are multiple nodes. For ranges, define the range after the <code>id</code>, within <code>[]</code> and separated by a <code>-</code>. For settable properties, add <code>:settable</code> to the <code>id</code></td>n370 <td>
371 Properties the node exposes, with format <code>id</code> separated by a <code>,</code> if there are multiple nodes.
372 </td>
373 <td>Yes</td>
374 <td>Yes</td>
375 </tr>
376 <tr>
377 <td>$array</td>
378 <td>Device → Controller</td>
379 <td>Range separated by a <code>-</code>. e.g. <code>0-2</code> for an array with the indexes <code>0</code>, <code>1</code> and <code>2</code></td>
179 <td>Yes</td>380 <td>Yes</td>
180 <td>Yes</td>381 <td>Yes, if the node is an array</td>
181 </tr>382 </tr>
n184For example, our `686f6d6965` above would send:n385For example, our `engine` node would send:
nn387```java
388homie/super-car/engine/$name → "Car engine"
389homie/super-car/engine/$type → "V8"
390homie/super-car/engine/$properties → "speed,direction,temperature"
391```
392
393----
394
395### Properties
396
397* `homie` / `device ID` / `node ID` / **`property ID`**: this is the base topic of a property.
398Each property must have a unique property ID on a per-node basis which adhere to the [ID format](#topic-ids).
399
400* A property value (e.g. a sensor reading) is directly published to the property topic, e.g.:
401 ```java
402 homie/super-car/engine/temperature → "21.5"
186```403 ```
187homie/686f6d6965/temperature/$type → temperature
188homie/686f6d6965/temperature/$properties → degrees,unit
189homie/686f6d6965/temperature/unit → c
190homie/686f6d6965/temperature/degrees → 12.07
n192homie/686f6d6965/humidity/$type → humidityn405#### Property Attributes
193homie/686f6d6965/humidity/$properties → percentage
194homie/686f6d6965/humidity/percentage → 79
195```
n197A LED strip would look like this. Note that the topic for a range properties is the name of the property followed by a `_` and the index getting updated:n407* `homie` / `device ID` / `node ID` / `property ID` / **`$property-attribute`**:
408A property attribute MUST be one of these:
n199```n410<table>
200homie/ledstrip-device/ledstrip/$type → ledstrip411 <tr>
201homie/ledstrip-device/ledstrip/$properties → led[1-3]:settable412 <th>Topic</th>
202homie/ledstrip-device/ledstrip/led_1 → on413 <th>Direction</th>
203homie/ledstrip-device/ledstrip/led_2 → off414 <th>Description</th>
204homie/ledstrip-device/ledstrip/led_3 → on415 <th>Valid values</th>
205```416 <th>Retained</th>
417 <th>Required (Default)</th>
418 </tr>
419 <tr>
420 <td>$name</td>
421 <td>Device → Controller</td>
422 <td>Friendly name of the property.</td>
423 <td>Any String</td>
424 <td>Yes</td>
425 <td>No ("")</td>
426 </tr>
427 <tr>
428 <td>$settable</td>
429 <td>Device → Controller</td>
430 <td>Specifies whether the property is settable (<code>true</code>) or readonly (<code>false</code>)</td>
431 <td><code>true</code> or <code>false</code></td>
432 <td>Yes</td>
433 <td>No (<code>false</code>)</td>
434 </tr>
435 <tr>
436 <td>$unit</td>
437 <td>Device → Controller</td>
438 <td>
439 A string containing the unit of this property.
440 You are not limited to the recommended values, although they are the only well known ones that will have to be recognized by any Homie consumer.
441 </td>
442 <td>
443 Recommended:<br>
444 <code>°C</code> Degree Celsius<br>
445 <code>°F</code> Degree Fahrenheit<br>
446 <code>°</code> Degree<br>
447 <code>L</code> Liter<br>
448 <code>gal</code> Galon<br>
449 <code>V</code> Volts<br>
450 <code>W</code> Watt<br>
451 <code>A</code> Ampere<br>
452 <code>%</code> Percent<br>
453 <code>m</code> Meter<br>
454 <code>ft</code> Feet<br>
455 <code>Pa</code> Pascal<br>
456 <code>psi</code> PSI<br>
457 <code>#</code> Count or Amount
458 </td>
459 <td>Yes</td>
460        <td>No ("")</td>
461 </tr>
462 <tr>
463 <td>$datatype</td>
464 <td>Device → Controller</td>
465 <td>Describes the format of data.</td>
466 <td>
467 <code>integer</code>,
468 <code>float</code>,
469 <code>boolean</code>,
470 <code>string</code>,
471 <code>enum</code>,
472 <code>color</code>
473 </td>
474 <td>Yes</td>
475 <td>No (<code>string</code>)</td>
476 </tr>
477 <tr>
478 <td>$format</td>
479 <td>Device → Controller</td>
480 <td>
481 Describes what are valid values for this property.
482 </td>
483 <td>
484 <ul>
485 <li>
486 <code>from:to</code> Describes a range of values e.g. <code>10:15</code>.
487 <br>Valid for datatypes <code>integer</code>, <code>float</code>
488 </li>
489 <li>
490 <code>value,value,value</code> for enumerating all valid values.
491 Escape <code>,</code> by using <code>,,</code>. e.g. <code>A,B,C</code> or <code>ON,OFF,PAUSE</code>.
492 <br>Valid for datatypes <code>enum</code>
493 </li>
494 <li>
495 <code>regex:pattern</code> to provide a regex that can be used to match the value. e.g. <code>regex:[A-Z][0-9]+</code>.
496 <br>Valid for datatype <code>string</code>
497 </li>
498 <li>
499 <code>rgb</code> to provide colors in RGB format e.g. <code>255,255,0</code> for yellow.
500 <code>hsv</code> to provide colors in HSV format e.g. <code>60,100,100</code> for yellow.
501 <br>Valid for datatype <code>color</code>
502 </li>
503 </ul>
504 </td>
505 <td>Yes</td>
506 <td>Depends on $datatype</td>
507 </tr>
508</table>
nn510For example, our `temperature` property would send:
511
512```java
513homie/super-car/engine/temperature/$name → "Engine temperature"
514homie/super-car/engine/temperature/$settable → "false"
515homie/super-car/engine/temperature/$unit → "°C"
516homie/super-car/engine/temperature/$datatype → "float"
517homie/super-car/engine/temperature/$format → "-20:120"
518homie/super-car/engine/temperature → "21.5"
519```
520
207* `homie` / **`device ID`** / **`node ID`** / **`property`** / `set`: the device can subscribe to this topic if the property is **settable** from the controller, in case of actuators.521* `homie` / `device ID` / `node ID` / `property ID` / **`set`**: the device can subscribe to this topic if the property is **settable** from the controller, in case of actuators.
n209Homie is state-based. You don't tell your smartlight to `turn on`, but you tell it to put it's `on` state to `true`. This especially fits well with MQTT, because of retained message.n523Homie is state-based.
524You don't tell your smartlight to `turn on`, but you tell it to put its `power` state to `on`.
525This especially fits well with MQTT, because of retained message.
n211For example, a `kitchen-light` device exposing a `light` node would subscribe to `homie/kitchen-light/light/on/set` and it would receive:n527For example, a `kitchen-light` device exposing a `light` node would subscribe to `homie/kitchen-light/light/power/set` and it would receive:
n213```n529```java
214homie/kitchen-light/light/on/set ← true530homie/kitchen-light/light/power/set ← "on"
n217The device would then turn on the light, and update its `on` state. This provides pessimistic feedback, which is important for home automation.n533The device would then turn on the light, and update its `power` state.
534This provides pessimistic feedback, which is important for home automation.
n219```n536```java
220homie/kitchen-light/light/on → true537homie/kitchen-light/light/power"true"
nn540### Arrays
541
542A node can be an array if you've added `[]` to its ID in the `$nodes` device attribute, and if its `$array` attribute is set to the range of the array.
543Let's consider we want to control independently the front lights and back lights of our `super-car`. Our `lights` node array would look like this. Note that the topic for an element of the array node is the name of the node followed by a `_` and the index getting updated:
544
545```java
546homie/super-car/$nodes → "lights[]"
547
548homie/super-car/lights/$name → "Lights"
549homie/super-car/lights/$properties → "intensity"
550homie/super-car/lights/$array → "0-1"
551
552homie/super-car/lights/intensity/$name → "Intensity"
553homie/super-car/lights/intensity/$settable → "true"
554homie/super-car/lights/intensity/$unit → "%"
555homie/super-car/lights/intensity/$datatype → "integer"
556homie/super-car/lights/intensity/$format → "0:100"
557
558homie/super-car/lights_0/$name → "Back lights"
559homie/super-car/lights_0/intensity → "0"
560homie/super-car/lights_1/$name → "Front lights"
561homie/super-car/lights_1/intensity → "100"
562```
563
564Note that you can name each element in your array individually ("Back lights", etc.).
565
566----
567
223### Broadcast channel568### Broadcast Channel
n227* `homie` / `$broadcast` / **`level`**: `level` is an arbitrary broadcast identifier. It must adhere to the [ID format](#id-format).n572* `homie` / `$broadcast` / **`level`**: `level` is an arbitrary broadcast identifier.
573It must adhere to the [ID format](#topic-ids).
n229For example, you might want to broadcast an `alert` event with the alert reason as the payload. Devices are then free to react or not. In our case, every buzzer of your home automation system would start buzzing.n575For example, you might want to broadcast an `alert` event with the alert reason as the payload.
576Devices are then free to react or not.
577In our case, every buzzer of your home automation system would start buzzing.
t231```t579```java
232homie/$broadcast/alert ← Intruder detected580homie/$broadcast/alert ← "Intruder detected"
+
+
+
+ + + +
+ +
+ + + + diff --git a/specification/spec-core-v3_0_0/index.html b/specification/spec-core-v3_0_0/index.html new file mode 100644 index 0000000..8cb10b3 --- /dev/null +++ b/specification/spec-core-v3_0_0/index.html @@ -0,0 +1,740 @@ + + + + + + + + + The Homie convention + + + + + + + + + + + + + +
+
+ +
+ + + +
+
+ homieiot/convention +
+
+
+ + + +
+ +
+ + + + + + + + + + +
+
+
+

Homie: An MQTT Convention for IoT/M2M

+
+
+
+ +
+ + + +
+ + License: CCA 4.0
+ Version: + + + + [develop] + + + + [v1.5.0] + + + + [v2.0.0] + + + + [v2.0.1] + + + + [v3.0.0] + + + + [v3.0.1] + + + + [v4.0.0] + +
+ + Changes: [Diff to previous]
+ + Release date: 25. March 2018 +
+
+ +
+ Frequently asked questions + +

How do I query/request a property?

+

You don’t. +The MQTT protocol does not implement the request-reply but rather the publish-subscribe messaging pattern. +The Homie convention follows the publish-subscribe principle by publishing data as retained messages on a regular basis. +You might want to rethink the design of your application - in most scenarios a regularly updated information is sufficient.

+

Workaround: You are free to implement your own ideas on top of the basic structure of the Homie convention. +You could either implement a get getter topic and its logic to trigger a value update, or you may exploit the concept of Homie properties and define a settable property to trigger a value update.

+ + +
+ +
+

License

+

By exercising the Licensed Rights (defined on https://creativecommons.org/licenses/by/4.0/), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions.

+
+ +
+

Table of Contents

+
+ + +

+

Table of Contents

+ +

Motivation

+

The Homie convention strives to be a communication definition on top of MQTT between IoT devices and controlling entities.

+
+

MQTT is a machine-to-machine (M2M)/“Internet of Things” connectivity protocol. +It was designed as an extremely lightweight publish/subscribe messaging transport.

+
+

MQTT supports easy and unrestricted message-based communication. +However, MQTT doesn’t define the structure and content of these messages and their relation. +An IoT device publishes data and provides interaction possibilities but a controlling entity will need to be specifically configured to be able to interface with the device.

+

The Homie convention defines a standardized way of how IoT devices and services announce themselves and their data on the communication channel. +The Homie convention is thereby a crucial aspect in the support of automatic discovery, configuration and usage of devices and services over the MQTT protocol.

+
+

MQTT Restrictions

+

Homie communicates through MQTT and is hence based on the basic principles of MQTT topic publication and subscription.

+

Topic IDs

+

An MQTT topic consists of one or more topic levels, separated by the slash character (/). +A topic level ID MAY contain lowercase letters from a to z, numbers from 0 to 9 as well as the hyphen character (-).

+

A topic level ID MUST NOT start or end with a hyphen (-). +The special character $ is used and reserved for Homie attributes. +The underscore (_) is used and reserved for Homie node arrays.

+

Payload

+

Every MQTT message payload MUST be sent as string. +If a value is of a numeric data type, it MUST be converted to string. +Booleans MUST be converted to “true” or “false”. +All values MUST be encoded as UTF-8 strings.

+

QoS and retained messages

+

The nature of the Homie convention makes it safe about duplicate messages, so the recommended QoS for reliability is QoS 1. +All messages MUST be sent as retained, UNLESS stated otherwise.

+

Topology

+

Devices: +An instance of a physical piece of hardware is called a device. +For example, a car, an Arduino/ESP8266 or a coffee machine.

+

Nodes: +A device can expose multiple nodes. +Nodes are independent or logically separable parts of a device. +For example, a car might expose a wheels node, an engine node and a lights node.

+

Nodes can be arrays. +For example, instead of creating two lights node to control front lights and back lights independently, we can set the lights node to be an array with two elements.

+

Properties: +A node can have multiple properties. +Properties represent basic characteristics of the node/device, often given as numbers or finite states. +For example the wheels node might expose an angle property. +The engine node might expose a speed, direction and temperature property. +The lights node might expose an intensity and a color property.

+

Properties can be settable. +For example, you don’t want your temperature property to be settable in case of a temperature sensor (like the car example), but to be settable in case of a thermostat.

+

Attributes: +Devices, nodes and properties have specific attributes characterizing them. +Attributes are represented by topic identifier starting with $. +The precise definition of attributes is important for the automatic discovery of devices following the Homie convention.

+

Examples: A device might have an IP attribute, a node will have a name attribute, and a property will have a unit attribute.

+
+

Base Topic

+

The base topic you will see in the following convention will be homie/. +If this base topic does not suit your needs (in case of, e.g., a public broker), you can choose another.

+

Be aware, that only the default base topic homie/ is eligible for automatic discovery by third party controllers.

+
+

Devices

+
    +
  • homie / device ID: this is the base topic of a device. +Each device must have a unique device ID which adhere to the ID format.
  • +
+

Device Attributes

+
    +
  • homie / device ID / $device-attribute: +When the MQTT connection to the broker is established or re-established, the device MUST send its attributes to the broker immediately.
  • +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TopicDirectionDescriptionRetainedRequired
$homieDevice → ControllerVersion of the Homie convention the device conforms toYesYes
$nameDevice → ControllerFriendly name of the deviceYesYes
$stateDevice → Controller + See Device behavior + YesYes
$localipDevice → ControllerIP of the device on the local networkYesYes
$macDevice → ControllerMac address of the device network interface. The format MUST be of the type A1:B2:C3:D4:E5:F6YesYes
$fw/nameDevice → ControllerName of the firmware running on the device. Allowed characters are the same as the device IDYesYes
$fw/versionDevice → ControllerVersion of the firmware running on the deviceYesYes
$nodesDevice → Controller + Nodes the device exposes, with format id separated by a , if there are multiple nodes. + To make a node an array, append [] to the ID. + YesYes
$implementationDevice → ControllerAn identifier for the Homie implementation (example esp8266)YesYes
$implementation/#Controller → Device or Device → ControllerYou can use any subtopics of $implementation for anything related to your specific Homie implementation.Yes or No, depending of your implementationNo
$statsDevice → ControllerSpecify all optional stats that the device will announce, with format stats separated by a , if there are multiple stats. See next section for an exampleYesYes
$stats/intervalDevice → ControllerInterval in seconds at which the device refreshes its $stats/+: See next section for details about statistical attributesYesYes
+

For example, a device with an ID of super-car that comprises off a wheels, engine and a lights node would send:

+
homie/super-car/$homie  "2.1.0"
+homie/super-car/$name  "Super car"
+homie/super-car/$localip  "192.168.0.10"
+homie/super-car/$mac  "DE:AD:BE:EF:FE:ED"
+homie/super-car/$fw/name  "weatherstation-firmware"
+homie/super-car/$fw/version  "1.0.0"
+homie/super-car/$nodes  "wheels,engine,lights[]"
+homie/super-car/$implementation  "esp8266"
+homie/super-car/$stats/interval  "60"
+homie/super-car/$state  "ready"
+

Device Behavior

+

The $state device attribute represents, as the name suggests, the current state of the device. +There are 6 different states:

+
    +
  • init: this is the state the device is in when it is connected to the MQTT broker, but has not yet sent all Homie messages and is not yet ready to operate. +This is the first message that must that must be sent.
  • +
  • ready: this is the state the device is in when it is connected to the MQTT broker, has sent all Homie messages and is ready to operate. +You have to send this message after all other announcements message have been sent.
  • +
  • disconnected: this is the state the device is in when it is cleanly disconnected from the MQTT broker. +You must send this message before cleanly disconnecting.
  • +
  • sleeping: this is the state the device is in when the device is sleeping. +You have to send this message before sleeping.
  • +
  • lost: this is the state the device is in when the device has been “badly” disconnected. +You must define this message as LWT.
  • +
  • alert: this is the state the device is when connected to the MQTT broker, but something wrong is happening. E.g. a sensor is not providing data and needs human intervention. +You have to send this message when something is wrong.
  • +
+

Device Statistics

+
    +
  • homie / device ID / $stats/ $device-statistic-attribute: +The $stats/ hierarchy allows to send device attributes that change over time. The device MUST send them every $stats/interval seconds.
  • +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TopicDirectionDescriptionRetainedRequired
$stats/uptimeDevice → ControllerTime elapsed in seconds since the boot of the deviceYesYes
$stats/signalDevice → ControllerSignal strength in %YesNo
$stats/cputempDevice → ControllerCPU Temperature in °CYesNo
$stats/cpuloadDevice → Controller + CPU Load in %. + Average of last $interval including all CPUs + YesNo
$stats/batteryDevice → ControllerBattery level in %YesNo
$stats/freeheapDevice → ControllerFree heap in bytesYesNo
$stats/supplyDevice → ControllerSupply Voltage in VYesNo
+

For example, our super-car device with $stats/interval value “60” is supposed to send its current values every 60 seconds:

+
homie/super-car/$stats  "uptime,cputemp,signal,battery"
+homie/super-car/$stats/uptime  "120"
+homie/super-car/$stats/cputemp  "48"
+homie/super-car/$stats/signal  "24"
+homie/super-car/$stats/battery  "80"
+

+

Nodes

+
    +
  • homie / device ID / node ID: this is the base topic of a node. +Each node must have a unique node ID on a per-device basis which adhere to the ID format.
  • +
+

Node Attributes

+
    +
  • homie / device ID / node ID / $node-attribute: +A node attribute MUST be one of these:
  • +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TopicDirectionDescriptionRetainedRequired
$nameDevice → ControllerFriendly name of the NodeYesYes
$typeDevice → ControllerType of the nodeYesYes
$propertiesDevice → Controller + Properties the node exposes, with format id separated by a , if there are multiple nodes. + YesYes
$arrayDevice → ControllerRange separated by a -. e.g. 0-2 for an array with the indexes 0, 1 and 2YesYes, if the node is an array
+

For example, our engine node would send:

+
homie/super-car/engine/$name  "Car engine"
+homie/super-car/engine/$type  "V8"
+homie/super-car/engine/$properties  "speed,direction,temperature"
+

+

Properties

+
    +
  • +

    homie / device ID / node ID / property ID: this is the base topic of a property. +Each property must have a unique property ID on a per-node basis which adhere to the ID format.

    +
  • +
  • +

    A property value (e.g. a sensor reading) is directly published to the property topic, e.g.:

    +
    homie/super-car/engine/temperature  "21.5"
    +
  • +
+

Property Attributes

+
    +
  • homie / device ID / node ID / property ID / $property-attribute: +A property attribute MUST be one of these:
  • +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +         + + + + + + + + + + + + + + + + + +
TopicDirectionDescriptionValid valuesRetainedRequired (Default)
$nameDevice → ControllerFriendly name of the property.Any StringYesNo ("")
$settableDevice → ControllerSpecifies whether the property is settable (true) or readonly (false)true or falseYesNo (false)
$unitDevice → Controller + A string containing the unit of this property. + You are not limited to the recommended values, although they are the only well known ones that will have to be recognized by any Homie consumer. + + Recommended:
+ °C Degree Celsius
+ °F Degree Fahrenheit
+ ° Degree
+ L Liter
+ gal Galon
+ V Volts
+ W Watt
+ A Ampere
+ % Percent
+ m Meter
+ ft Feet
+ Pa Pascal
+ psi PSI
+ # Count or Amount +
YesNo ("")
$datatypeDevice → ControllerDescribes the format of data. + integer, + float, + boolean, + string, + enum, + color + YesNo (string)
$formatDevice → Controller + Describes what are valid values for this property. + +
    +
  • + from:to Describes a range of values e.g. 10:15. +
    Valid for datatypes integer, float +
  • +
  • + value,value,value for enumerating all valid values. + Escape , by using ,,. e.g. A,B,C or ON,OFF,PAUSE. +
    Valid for datatypes enum +
  • +
  • + regex:pattern to provide a regex that can be used to match the value. e.g. regex:[A-Z][0-9]+. +
    Valid for datatype string +
  • +
  • + rgb to provide colors in RGB format e.g. 255,255,0 for yellow. + hsv to provide colors in HSV format e.g. 60,100,100 for yellow. +
    Valid for datatype color +
  • +
+
YesDepends on $datatype
+

For example, our temperature property would send:

+
homie/super-car/engine/temperature/$name  "Engine temperature"
+homie/super-car/engine/temperature/$settable  "false"
+homie/super-car/engine/temperature/$unit  "°C"
+homie/super-car/engine/temperature/$datatype  "float"
+homie/super-car/engine/temperature/$format  "-20:120"
+homie/super-car/engine/temperature  "21.5"
+
    +
  • homie / device ID / node ID / property ID / set: the device can subscribe to this topic if the property is settable from the controller, in case of actuators.
  • +
+

Homie is state-based. +You don’t tell your smartlight to turn on, but you tell it to put its power state to on. +This especially fits well with MQTT, because of retained message.

+

For example, a kitchen-light device exposing a light node would subscribe to homie/kitchen-light/light/power/set and it would receive:

+
homie/kitchen-light/light/power/set  "on"
+

The device would then turn on the light, and update its power state. +This provides pessimistic feedback, which is important for home automation.

+
homie/kitchen-light/light/power  "true"
+

Arrays

+

A node can be an array if you’ve added [] to its ID in the $nodes device attribute, and if its $array attribute is set to the range of the array. +Let’s consider we want to control independently the front lights and back lights of our super-car. Our lights node array would look like this. Note that the topic for an element of the array node is the name of the node followed by a _ and the index getting updated:

+
homie/super-car/$nodes  "lights[]"
+
+homie/super-car/lights/$name  "Lights"
+homie/super-car/lights/$properties  "intensity"
+homie/super-car/lights/$array  "0-1"
+
+homie/super-car/lights/intensity/$name  "Intensity"
+homie/super-car/lights/intensity/$settable  "true"
+homie/super-car/lights/intensity/$unit  "%"
+homie/super-car/lights/intensity/$datatype  "integer"
+homie/super-car/lights/intensity/$format  "0:100"
+
+homie/super-car/lights_0/$name  "Back lights"
+homie/super-car/lights_0/intensity  "0"
+homie/super-car/lights_1/$name  "Front lights"
+homie/super-car/lights_1/intensity  "100"
+

Note that you can name each element in your array individually (“Back lights”, etc.).

+
+

Broadcast Channel

+

Homie defines a broadcast channel, so a controller is able to broadcast a message to every Homie devices:

+
    +
  • homie / $broadcast / level: level is an arbitrary broadcast identifier. +It must adhere to the ID format.
  • +
+

For example, you might want to broadcast an alert event with the alert reason as the payload. +Devices are then free to react or not. +In our case, every buzzer of your home automation system would start buzzing.

+
homie/$broadcast/alert  "Intruder detected"
+

Any other topic is not part of the Homie convention.

+ +
+ +
+
+ + + +
+ +
+ + + + diff --git a/specification/spec-core-v3_0_1-changes/index.html b/specification/spec-core-v3_0_1-changes/index.html new file mode 100644 index 0000000..371868c --- /dev/null +++ b/specification/spec-core-v3_0_1-changes/index.html @@ -0,0 +1,142 @@ + + + + + + + + + The Homie convention + + + + + + + + + + + + + +
+
+ +
+ + + +
+
+ homieiot/convention +
+
+
+ + + +
+ +
+
+
+

+ +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Commit titleDateHash
Stateful attribute for properties (#108)2018-10-27 15:22:20 +0200730357e
Remove regex requirement (#88)2018-04-29 14:32:39 +0200081214d
Use “true” instead of “on” (#89)2018-04-27 14:51:40 +02005cbe5ee
Add FAQ, discuss getter topic (#87)2018-04-27 14:29:27 +0200fb8d153
Add my ESP32 IDF implementation (#73)2018-04-23 15:25:24 -07007d9f236
+ +
+
+
+ + + +
+ +
+ + + + diff --git a/specification/spec-core-v3_0_1-diff/index.html b/specification/spec-core-v3_0_1-diff/index.html new file mode 100644 index 0000000..85872a2 --- /dev/null +++ b/specification/spec-core-v3_0_1-diff/index.html @@ -0,0 +1,226 @@ + + + + + + + + + The Homie convention + + + + + + + + + + + + + +
+
+ +
+ + + +
+
+ homieiot/convention +
+
+
+ + + +
+ +
+
+
+

+ Changes +

+
Changes from + v3.0.0 to + v3.0.1
+ +

Commits

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Commit titleDateHash
Stateful attribute for properties (#108)2018-10-27 15:22:20 +0200730357e
Remove regex requirement (#88)2018-04-29 14:32:39 +0200081214d
Use “true” instead of “on” (#89)2018-04-27 14:51:40 +02005cbe5ee
Add FAQ, discuss getter topic (#87)2018-04-27 14:29:27 +0200fb8d153
Add my ESP32 IDF implementation (#73)2018-04-23 15:25:24 -07007d9f236
+ + + +

Differences

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

v3.0.0
v3.0.1
n3version: v3.0.0n3version: v3.0.1
4releasedate: 25. March 20184releasedate: 27. October 2018
nn30* [FAQ and Rationale](#faq)
31
nn97
98Properties can be **retained**.
99A property is retained by default. A non-retained property would be useful for momentary events (door bell pressed).
100
101A combination of those flags compiles into this list:
102
103* **retained + non-settable**: The node publishes a property state (temperature sensor)
104* **retained + settable**: The node publishes a property state, and can receive commands for the property (by controller or other party) (lamp power)
105* **non-retained + non-settable**: The node publishes momentary events (door bell pressed)
106* **non-retained + settable**: The node publishes momentary events, and can receive commands for the property (by controller or other party) (brew coffee)
nn446 </tr>
447 <tr>
448 <td>$retained</td>
449 <td>Device → Controller</td>
450 <td>Specifies whether the property is retained (<code>true</code>) or non-retained (<code>false</code>). Publishing to a non-retained property topic MUST always happen with the MQTT 'retain' flag off.</td>
451 <td><code>true</code> or <code>false</code></td>
452 <td>Yes</td>
453 <td>No (<code>true</code>)</td>
n495 <code>regex:pattern</code> to provide a regex that can be used to match the value. e.g. <code>regex:[A-Z][0-9]+</code>.n
496 <br>Valid for datatype <code>string</code>
497 </li>
498 <li>
n506 <td>Depends on $datatype</td>n522 <td>No for $datatype <code>string</code>,<code>integer</code>,<code>float</code>,<code>boolean</code>. Yes for <code>enum</code>,<code>color</code></td>
n530homie/kitchen-light/light/power/set ← "on"n546homie/kitchen-light/light/power/set ← "true"
nn601----
602----
tt604## FAQ
605
606In this section frequently asked questions will be answered.
607This includes design decisions and drawn compromises in the specifics of the Homie convention.
608
609### How do I query/request a property?
610
611You don't.
612The MQTT protocol does not implement the request-reply but rather the publish-subscribe messaging pattern.
613The Homie convention follows the publish-subscribe principle by publishing data as retained messages on a regular basis.
614You might want to rethink the design of your application - in most scenarios a regularly updated information is sufficient.
615
616*Workaround:* You are free to implement your own ideas on top of the basic structure of the Homie convention.
617You could either implement a `get` getter topic and its logic to trigger a value update, or you may exploit the concept of Homie properties and define a settable property to trigger a value update.
618
619A discussion on the matter can be found in issue [#79](https://github.com/homieiot/convention/issues/79).
620
621
+
+
+
+ + + +
+ +
+ + + + diff --git a/specification/spec-core-v3_0_1/index.html b/specification/spec-core-v3_0_1/index.html new file mode 100644 index 0000000..a814adf --- /dev/null +++ b/specification/spec-core-v3_0_1/index.html @@ -0,0 +1,772 @@ + + + + + + + + + The Homie convention + + + + + + + + + + + + + +
+
+ +
+ + + +
+
+ homieiot/convention +
+
+
+ + + +
+ +
+ + + + + + + + + + +
+
+
+

Homie: An MQTT Convention for IoT/M2M

+
+
+
+ +
+ + + +
+ + License: CCA 4.0
+ Version: + + + + [develop] + + + + [v1.5.0] + + + + [v2.0.0] + + + + [v2.0.1] + + + + [v3.0.0] + + + + [v3.0.1] + + + + [v4.0.0] + +
+ + Changes: [Diff to previous]
+ + Release date: 27. October 2018 +
+
+ +
+ Frequently asked questions + +

How do I query/request a property?

+

You don’t. +The MQTT protocol does not implement the request-reply but rather the publish-subscribe messaging pattern. +The Homie convention follows the publish-subscribe principle by publishing data as retained messages on a regular basis. +You might want to rethink the design of your application - in most scenarios a regularly updated information is sufficient.

+

Workaround: You are free to implement your own ideas on top of the basic structure of the Homie convention. +You could either implement a get getter topic and its logic to trigger a value update, or you may exploit the concept of Homie properties and define a settable property to trigger a value update.

+ + +
+ +
+

License

+

By exercising the Licensed Rights (defined on https://creativecommons.org/licenses/by/4.0/), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions.

+
+ +
+

Table of Contents

+
+ + +

+

Table of Contents

+ +

Motivation

+

The Homie convention strives to be a communication definition on top of MQTT between IoT devices and controlling entities.

+
+

MQTT is a machine-to-machine (M2M)/“Internet of Things” connectivity protocol. +It was designed as an extremely lightweight publish/subscribe messaging transport.

+
+

MQTT supports easy and unrestricted message-based communication. +However, MQTT doesn’t define the structure and content of these messages and their relation. +An IoT device publishes data and provides interaction possibilities but a controlling entity will need to be specifically configured to be able to interface with the device.

+

The Homie convention defines a standardized way of how IoT devices and services announce themselves and their data on the communication channel. +The Homie convention is thereby a crucial aspect in the support of automatic discovery, configuration and usage of devices and services over the MQTT protocol.

+
+

MQTT Restrictions

+

Homie communicates through MQTT and is hence based on the basic principles of MQTT topic publication and subscription.

+

Topic IDs

+

An MQTT topic consists of one or more topic levels, separated by the slash character (/). +A topic level ID MAY contain lowercase letters from a to z, numbers from 0 to 9 as well as the hyphen character (-).

+

A topic level ID MUST NOT start or end with a hyphen (-). +The special character $ is used and reserved for Homie attributes. +The underscore (_) is used and reserved for Homie node arrays.

+

Payload

+

Every MQTT message payload MUST be sent as string. +If a value is of a numeric data type, it MUST be converted to string. +Booleans MUST be converted to “true” or “false”. +All values MUST be encoded as UTF-8 strings.

+

QoS and retained messages

+

The nature of the Homie convention makes it safe about duplicate messages, so the recommended QoS for reliability is QoS 1. +All messages MUST be sent as retained, UNLESS stated otherwise.

+

Topology

+

Devices: +An instance of a physical piece of hardware is called a device. +For example, a car, an Arduino/ESP8266 or a coffee machine.

+

Nodes: +A device can expose multiple nodes. +Nodes are independent or logically separable parts of a device. +For example, a car might expose a wheels node, an engine node and a lights node.

+

Nodes can be arrays. +For example, instead of creating two lights node to control front lights and back lights independently, we can set the lights node to be an array with two elements.

+

Properties: +A node can have multiple properties. +Properties represent basic characteristics of the node/device, often given as numbers or finite states. +For example the wheels node might expose an angle property. +The engine node might expose a speed, direction and temperature property. +The lights node might expose an intensity and a color property.

+

Properties can be settable. +For example, you don’t want your temperature property to be settable in case of a temperature sensor (like the car example), but to be settable in case of a thermostat.

+

Properties can be retained. +A property is retained by default. A non-retained property would be useful for momentary events (door bell pressed).

+

A combination of those flags compiles into this list:

+
    +
  • retained + non-settable: The node publishes a property state (temperature sensor)
  • +
  • retained + settable: The node publishes a property state, and can receive commands for the property (by controller or other party) (lamp power)
  • +
  • non-retained + non-settable: The node publishes momentary events (door bell pressed)
  • +
  • non-retained + settable: The node publishes momentary events, and can receive commands for the property (by controller or other party) (brew coffee)
  • +
+

Attributes: +Devices, nodes and properties have specific attributes characterizing them. +Attributes are represented by topic identifier starting with $. +The precise definition of attributes is important for the automatic discovery of devices following the Homie convention.

+

Examples: A device might have an IP attribute, a node will have a name attribute, and a property will have a unit attribute.

+
+

Base Topic

+

The base topic you will see in the following convention will be homie/. +If this base topic does not suit your needs (in case of, e.g., a public broker), you can choose another.

+

Be aware, that only the default base topic homie/ is eligible for automatic discovery by third party controllers.

+
+

Devices

+
    +
  • homie / device ID: this is the base topic of a device. +Each device must have a unique device ID which adhere to the ID format.
  • +
+

Device Attributes

+
    +
  • homie / device ID / $device-attribute: +When the MQTT connection to the broker is established or re-established, the device MUST send its attributes to the broker immediately.
  • +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TopicDirectionDescriptionRetainedRequired
$homieDevice → ControllerVersion of the Homie convention the device conforms toYesYes
$nameDevice → ControllerFriendly name of the deviceYesYes
$stateDevice → Controller + See Device behavior + YesYes
$localipDevice → ControllerIP of the device on the local networkYesYes
$macDevice → ControllerMac address of the device network interface. The format MUST be of the type A1:B2:C3:D4:E5:F6YesYes
$fw/nameDevice → ControllerName of the firmware running on the device. Allowed characters are the same as the device IDYesYes
$fw/versionDevice → ControllerVersion of the firmware running on the deviceYesYes
$nodesDevice → Controller + Nodes the device exposes, with format id separated by a , if there are multiple nodes. + To make a node an array, append [] to the ID. + YesYes
$implementationDevice → ControllerAn identifier for the Homie implementation (example esp8266)YesYes
$implementation/#Controller → Device or Device → ControllerYou can use any subtopics of $implementation for anything related to your specific Homie implementation.Yes or No, depending of your implementationNo
$statsDevice → ControllerSpecify all optional stats that the device will announce, with format stats separated by a , if there are multiple stats. See next section for an exampleYesYes
$stats/intervalDevice → ControllerInterval in seconds at which the device refreshes its $stats/+: See next section for details about statistical attributesYesYes
+

For example, a device with an ID of super-car that comprises off a wheels, engine and a lights node would send:

+
homie/super-car/$homie  "2.1.0"
+homie/super-car/$name  "Super car"
+homie/super-car/$localip  "192.168.0.10"
+homie/super-car/$mac  "DE:AD:BE:EF:FE:ED"
+homie/super-car/$fw/name  "weatherstation-firmware"
+homie/super-car/$fw/version  "1.0.0"
+homie/super-car/$nodes  "wheels,engine,lights[]"
+homie/super-car/$implementation  "esp8266"
+homie/super-car/$stats/interval  "60"
+homie/super-car/$state  "ready"
+

Device Behavior

+

The $state device attribute represents, as the name suggests, the current state of the device. +There are 6 different states:

+
    +
  • init: this is the state the device is in when it is connected to the MQTT broker, but has not yet sent all Homie messages and is not yet ready to operate. +This is the first message that must that must be sent.
  • +
  • ready: this is the state the device is in when it is connected to the MQTT broker, has sent all Homie messages and is ready to operate. +You have to send this message after all other announcements message have been sent.
  • +
  • disconnected: this is the state the device is in when it is cleanly disconnected from the MQTT broker. +You must send this message before cleanly disconnecting.
  • +
  • sleeping: this is the state the device is in when the device is sleeping. +You have to send this message before sleeping.
  • +
  • lost: this is the state the device is in when the device has been “badly” disconnected. +You must define this message as LWT.
  • +
  • alert: this is the state the device is when connected to the MQTT broker, but something wrong is happening. E.g. a sensor is not providing data and needs human intervention. +You have to send this message when something is wrong.
  • +
+

Device Statistics

+
    +
  • homie / device ID / $stats/ $device-statistic-attribute: +The $stats/ hierarchy allows to send device attributes that change over time. The device MUST send them every $stats/interval seconds.
  • +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TopicDirectionDescriptionRetainedRequired
$stats/uptimeDevice → ControllerTime elapsed in seconds since the boot of the deviceYesYes
$stats/signalDevice → ControllerSignal strength in %YesNo
$stats/cputempDevice → ControllerCPU Temperature in °CYesNo
$stats/cpuloadDevice → Controller + CPU Load in %. + Average of last $interval including all CPUs + YesNo
$stats/batteryDevice → ControllerBattery level in %YesNo
$stats/freeheapDevice → ControllerFree heap in bytesYesNo
$stats/supplyDevice → ControllerSupply Voltage in VYesNo
+

For example, our super-car device with $stats/interval value “60” is supposed to send its current values every 60 seconds:

+
homie/super-car/$stats  "uptime,cputemp,signal,battery"
+homie/super-car/$stats/uptime  "120"
+homie/super-car/$stats/cputemp  "48"
+homie/super-car/$stats/signal  "24"
+homie/super-car/$stats/battery  "80"
+

+

Nodes

+
    +
  • homie / device ID / node ID: this is the base topic of a node. +Each node must have a unique node ID on a per-device basis which adhere to the ID format.
  • +
+

Node Attributes

+
    +
  • homie / device ID / node ID / $node-attribute: +A node attribute MUST be one of these:
  • +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TopicDirectionDescriptionRetainedRequired
$nameDevice → ControllerFriendly name of the NodeYesYes
$typeDevice → ControllerType of the nodeYesYes
$propertiesDevice → Controller + Properties the node exposes, with format id separated by a , if there are multiple nodes. + YesYes
$arrayDevice → ControllerRange separated by a -. e.g. 0-2 for an array with the indexes 0, 1 and 2YesYes, if the node is an array
+

For example, our engine node would send:

+
homie/super-car/engine/$name  "Car engine"
+homie/super-car/engine/$type  "V8"
+homie/super-car/engine/$properties  "speed,direction,temperature"
+

+

Properties

+
    +
  • +

    homie / device ID / node ID / property ID: this is the base topic of a property. +Each property must have a unique property ID on a per-node basis which adhere to the ID format.

    +
  • +
  • +

    A property value (e.g. a sensor reading) is directly published to the property topic, e.g.:

    +
    homie/super-car/engine/temperature  "21.5"
    +
  • +
+

Property Attributes

+
    +
  • homie / device ID / node ID / property ID / $property-attribute: +A property attribute MUST be one of these:
  • +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +         + + + + + + + + + + + + + + + + + +
TopicDirectionDescriptionValid valuesRetainedRequired (Default)
$nameDevice → ControllerFriendly name of the property.Any StringYesNo ("")
$settableDevice → ControllerSpecifies whether the property is settable (true) or readonly (false)true or falseYesNo (false)
$retainedDevice → ControllerSpecifies whether the property is retained (true) or non-retained (false). Publishing to a non-retained property topic MUST always happen with the MQTT 'retain' flag off.true or falseYesNo (true)
$unitDevice → Controller + A string containing the unit of this property. + You are not limited to the recommended values, although they are the only well known ones that will have to be recognized by any Homie consumer. + + Recommended:
+ °C Degree Celsius
+ °F Degree Fahrenheit
+ ° Degree
+ L Liter
+ gal Galon
+ V Volts
+ W Watt
+ A Ampere
+ % Percent
+ m Meter
+ ft Feet
+ Pa Pascal
+ psi PSI
+ # Count or Amount +
YesNo ("")
$datatypeDevice → ControllerDescribes the format of data. + integer, + float, + boolean, + string, + enum, + color + YesNo (string)
$formatDevice → Controller + Describes what are valid values for this property. + +
    +
  • + from:to Describes a range of values e.g. 10:15. +
    Valid for datatypes integer, float +
  • +
  • + value,value,value for enumerating all valid values. + Escape , by using ,,. e.g. A,B,C or ON,OFF,PAUSE. +
    Valid for datatypes enum +
  • +
  • + rgb to provide colors in RGB format e.g. 255,255,0 for yellow. + hsv to provide colors in HSV format e.g. 60,100,100 for yellow. +
    Valid for datatype color +
  • +
+
YesNo for $datatype string,integer,float,boolean. Yes for enum,color
+

For example, our temperature property would send:

+
homie/super-car/engine/temperature/$name  "Engine temperature"
+homie/super-car/engine/temperature/$settable  "false"
+homie/super-car/engine/temperature/$unit  "°C"
+homie/super-car/engine/temperature/$datatype  "float"
+homie/super-car/engine/temperature/$format  "-20:120"
+homie/super-car/engine/temperature  "21.5"
+
    +
  • homie / device ID / node ID / property ID / set: the device can subscribe to this topic if the property is settable from the controller, in case of actuators.
  • +
+

Homie is state-based. +You don’t tell your smartlight to turn on, but you tell it to put its power state to on. +This especially fits well with MQTT, because of retained message.

+

For example, a kitchen-light device exposing a light node would subscribe to homie/kitchen-light/light/power/set and it would receive:

+
homie/kitchen-light/light/power/set  "true"
+

The device would then turn on the light, and update its power state. +This provides pessimistic feedback, which is important for home automation.

+
homie/kitchen-light/light/power  "true"
+

Arrays

+

A node can be an array if you’ve added [] to its ID in the $nodes device attribute, and if its $array attribute is set to the range of the array. +Let’s consider we want to control independently the front lights and back lights of our super-car. Our lights node array would look like this. Note that the topic for an element of the array node is the name of the node followed by a _ and the index getting updated:

+
homie/super-car/$nodes  "lights[]"
+
+homie/super-car/lights/$name  "Lights"
+homie/super-car/lights/$properties  "intensity"
+homie/super-car/lights/$array  "0-1"
+
+homie/super-car/lights/intensity/$name  "Intensity"
+homie/super-car/lights/intensity/$settable  "true"
+homie/super-car/lights/intensity/$unit  "%"
+homie/super-car/lights/intensity/$datatype  "integer"
+homie/super-car/lights/intensity/$format  "0:100"
+
+homie/super-car/lights_0/$name  "Back lights"
+homie/super-car/lights_0/intensity  "0"
+homie/super-car/lights_1/$name  "Front lights"
+homie/super-car/lights_1/intensity  "100"
+

Note that you can name each element in your array individually (“Back lights”, etc.).

+
+

Broadcast Channel

+

Homie defines a broadcast channel, so a controller is able to broadcast a message to every Homie devices:

+
    +
  • homie / $broadcast / level: level is an arbitrary broadcast identifier. +It must adhere to the ID format.
  • +
+

For example, you might want to broadcast an alert event with the alert reason as the payload. +Devices are then free to react or not. +In our case, every buzzer of your home automation system would start buzzing.

+
homie/$broadcast/alert  "Intruder detected"
+

Any other topic is not part of the Homie convention.

+
+
+

FAQ

+

In this section frequently asked questions will be answered. +This includes design decisions and drawn compromises in the specifics of the Homie convention.

+

How do I query/request a property?

+

You don’t. +The MQTT protocol does not implement the request-reply but rather the publish-subscribe messaging pattern. +The Homie convention follows the publish-subscribe principle by publishing data as retained messages on a regular basis. +You might want to rethink the design of your application - in most scenarios a regularly updated information is sufficient.

+

Workaround: You are free to implement your own ideas on top of the basic structure of the Homie convention. +You could either implement a get getter topic and its logic to trigger a value update, or you may exploit the concept of Homie properties and define a settable property to trigger a value update.

+

A discussion on the matter can be found in issue #79.

+ +
+ +
+
+ + + +
+ +
+ + + + diff --git a/specification/spec-core-v4_0_0-changes/index.html b/specification/spec-core-v4_0_0-changes/index.html new file mode 100644 index 0000000..94c82e9 --- /dev/null +++ b/specification/spec-core-v4_0_0-changes/index.html @@ -0,0 +1,297 @@ + + + + + + + + + The Homie convention + + + + + + + + + + + + + +
+
+ +
+ + + +
+
+ homieiot/convention +
+
+
+ + + +
+ +
+
+
+

+ +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Commit titleDateHash
Remove array brackets in super-car example (#177)2019-08-27 14:01:18 +020012453d8
Corections to meta extension (#173)2019-07-18 00:07:23 +02002c1999e
Fixed typos and broken links, removed whitespace in homie_legacy_stat… (#172)2019-07-17 21:13:33 +0200608541a
Extension Convention (#171)2019-07-16 22:55:32 +0200eaf3edc
remove references to deprecated arrays (#169)2019-07-03 19:56:14 +08009ac3433
Point Last Will section to $state (#157)2019-03-07 17:30:43 +0900a3ea000
Remove timings paragraph. Rename behaviour to lifecycle. (#155)2019-01-21 09:57:06 +010096dcc73
Fix typos (#153)2019-01-10 20:50:15 -0700616f8af
Fix extension suffix typo (#147)2018-12-05 16:20:32 +01001c417dc
Correct spelling and wording (#144)2018-11-29 19:15:19 +01000c012f4
Add subheading for “Property command topic” (#145)2018-11-29 19:13:30 +0100ecc7aa4
Remove 3.x arrays2018-11-26 19:45:36 +0100bb58910
$stats: Remove from device2018-11-26 21:53:13 +01002827c1e
Remove $stats2018-11-26 19:53:41 +01003fa7acd
Move property settable/retained explanation down2018-11-26 19:48:57 +010098ae53d
Fix wording in timing2018-11-26 19:44:48 +0100d976e02
Fix formatting for the webpage markdown renderer2018-11-26 19:42:49 +010080c5fdb
Remove requirement for the homie/ base topic2018-11-26 14:35:33 +010079a214d
Fix travis-ci file2018-11-23 10:54:02 +0100cfe82b1
Additional specificity on payload definitions (#133)2018-11-22 23:21:28 -0500e83bbc2
Fix image link2018-11-21 09:28:32 +0100b0e33fd
Last will limitation2018-11-20 12:13:44 +0100bcdd965
Add travis-ci file to trigger webpage build2018-11-20 11:54:33 +0100e92702e
Array, Broadcast Channel, Extensions are on heading level 22018-11-18 13:46:21 +010084b36f3
Table clean up2018-11-18 13:29:28 +01000678972
Updates to2018-11-12 09:47:50 +0100dea1380
Update extension section2018-11-08 08:16:07 +0100c2f1fcb
Add $specs for extension specification.2018-11-07 14:54:40 +010099dc487
Update convention.md2018-11-07 00:22:18 +01008b001ee
FIX: Numeric payload decimal separator is a dot2018-11-04 23:48:54 +0100c056ccc
Fix: Overflowing table2018-11-06 01:11:34 +01005da9704
$stats/interval fix2018-11-04 23:45:22 +010098ce2a3
BREAKING: Homie is about Lightweight topic discovery2018-10-31 14:43:10 +0100240f0bb
Change values to payload2018-11-04 13:54:32 -0500f23e16c
Remove parts that belong on a web page instead. License.2018-10-28 21:30:59 +010084d2388
Fixed wording on init state. (#116)2018-10-29 14:52:53 -0400910f936
+ +
+
+
+ + + +
+ +
+ + + + diff --git a/specification/spec-core-v4_0_0-diff/index.html b/specification/spec-core-v4_0_0-diff/index.html new file mode 100644 index 0000000..932d52e --- /dev/null +++ b/specification/spec-core-v4_0_0-diff/index.html @@ -0,0 +1,974 @@ + + + + + + + + + The Homie convention + + + + + + + + + + + + + +
+
+ +
+ + + +
+
+ homieiot/convention +
+
+
+ + + +
+ +
+
+
+

+ Changes +

+
Changes from + v3.0.1 to + v4.0.0
+ +

Commits

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Commit titleDateHash
Remove array brackets in super-car example (#177)2019-08-27 14:01:18 +020012453d8
Corections to meta extension (#173)2019-07-18 00:07:23 +02002c1999e
Fixed typos and broken links, removed whitespace in homie_legacy_stat… (#172)2019-07-17 21:13:33 +0200608541a
Extension Convention (#171)2019-07-16 22:55:32 +0200eaf3edc
remove references to deprecated arrays (#169)2019-07-03 19:56:14 +08009ac3433
Point Last Will section to $state (#157)2019-03-07 17:30:43 +0900a3ea000
Remove timings paragraph. Rename behaviour to lifecycle. (#155)2019-01-21 09:57:06 +010096dcc73
Fix typos (#153)2019-01-10 20:50:15 -0700616f8af
Fix extension suffix typo (#147)2018-12-05 16:20:32 +01001c417dc
Correct spelling and wording (#144)2018-11-29 19:15:19 +01000c012f4
Add subheading for “Property command topic” (#145)2018-11-29 19:13:30 +0100ecc7aa4
Remove 3.x arrays2018-11-26 19:45:36 +0100bb58910
$stats: Remove from device2018-11-26 21:53:13 +01002827c1e
Remove $stats2018-11-26 19:53:41 +01003fa7acd
Move property settable/retained explanation down2018-11-26 19:48:57 +010098ae53d
Fix wording in timing2018-11-26 19:44:48 +0100d976e02
Fix formatting for the webpage markdown renderer2018-11-26 19:42:49 +010080c5fdb
Remove requirement for the homie/ base topic2018-11-26 14:35:33 +010079a214d
Fix travis-ci file2018-11-23 10:54:02 +0100cfe82b1
Additional specificity on payload definitions (#133)2018-11-22 23:21:28 -0500e83bbc2
Fix image link2018-11-21 09:28:32 +0100b0e33fd
Last will limitation2018-11-20 12:13:44 +0100bcdd965
Add travis-ci file to trigger webpage build2018-11-20 11:54:33 +0100e92702e
Array, Broadcast Channel, Extensions are on heading level 22018-11-18 13:46:21 +010084b36f3
Table clean up2018-11-18 13:29:28 +01000678972
Updates to2018-11-12 09:47:50 +0100dea1380
Update extension section2018-11-08 08:16:07 +0100c2f1fcb
Add $specs for extension specification.2018-11-07 14:54:40 +010099dc487
Update convention.md2018-11-07 00:22:18 +01008b001ee
FIX: Numeric payload decimal separator is a dot2018-11-04 23:48:54 +0100c056ccc
Fix: Overflowing table2018-11-06 01:11:34 +01005da9704
$stats/interval fix2018-11-04 23:45:22 +010098ce2a3
BREAKING: Homie is about Lightweight topic discovery2018-10-31 14:43:10 +0100240f0bb
Change values to payload2018-11-04 13:54:32 -0500f23e16c
Remove parts that belong on a web page instead. License.2018-10-28 21:30:59 +010084d2388
Fixed wording on init state. (#116)2018-10-29 14:52:53 -0400910f936
+ + + +

Differences

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

v3.0.1
v4.0.0
n2source: README.mdn2source: convention.md
3version: v3.0.13version: v4.0.0
4releasedate: 27. October 20184releasedate: 27. August 2019
n11## Table of Contentsn
12
13* [Motivation](#motivation)
14* [MQTT restrictions](#mqtt-restrictions)
15 * [Topic IDs](#topic-ids)
16 * [Payload](#payload)
17 * [QoS and retained messages](#qos-and-retained-messages)
18* [Topology](#topology)
19 * [Base topic](#base-topic)
20 * [Devices](#devices)
21 * [Device attributes](#device-attributes)
22 * [Device behavior](#device-behavior)
23 * [Device statistics](#device-statistics)
24 * [Nodes](#nodes)
25 * [Node attributes](#node-attributes)
26 * [Properties](#properties)
27 * [Property attributes](#property-attributes)
28 * [Arrays](#arrays)
29 * [Broadcast channel](#broadcast-channel)
30* [FAQ and Rationale](#faq)
31
32
33## Motivation
34
35The Homie convention strives to be a **communication definition on top of MQTT** between IoT devices and controlling entities.
36
37> [MQTT](http://mqtt.org) is a machine-to-machine (M2M)/"Internet of Things" connectivity protocol.
38> It was designed as an extremely lightweight publish/subscribe messaging transport.
39
40MQTT supports easy and unrestricted message-based communication.
41However, MQTT doesn't define the structure and content of these messages and their relation.
42An IoT device publishes data and provides interaction possibilities but a controlling entity will need to be specifically configured to be able to interface with the device.
43
44The Homie convention defines a **standardized way** of how IoT devices and services announce themselves and their data on the communication channel.
45The Homie convention is thereby a crucial aspect in the support of **automatic discovery, configuration and usage** of devices and services over the MQTT protocol.
46
47----
48
n60The underscore (`_`) is used and reserved for Homie *node arrays*.n
n64Every MQTT message payload MUST be sent as string.n25- Every MQTT message payload MUST be sent as a UTF-8 encoded string
65If a value is of a numeric data type, it MUST be converted to string.26- The value published as payload MUST be valid for the respective property/attribute type as per the list below
66Booleans MUST be converted to "true" or "false".27
67All values MUST be encoded as UTF-8 strings. 28#### String
29
30- String types are limited to 268,435,456 characters
31- An empty string ("") is a valid payload
32
33#### Integer
34
35- Integer types are UTF-8 encoded string literal representations of 64-bit signed whole numbers
36- Integers range from -9,223,372,036,854,775,808 (-2<sup>63</sup>) to 9,223,372,036,854,775,807 (2<sup>63</sup>-1)
37- The payload may only contain whole numbers and the negation character "-". No other characters including spaces (" ") are permitted
38- A string with just a negation sign ("-") is not a valid payload
39- An empty string ("") is not a valid payload
40
41#### Float
42
43- Float types are UTF-8 encoded string literal representations of 64-bit signed floating point numbers
44- Floats range from 2<sup>-1074</sup> to (2-2<sup>-52</sup>)&ast;2<sup>1023</sup>
45- The payload may only contain whole numbers, the negation character "-", the exponent character "e" or "E" and the decimal separator ".", no other characters, including spaces (" ") are permitted
46- The dot character (".") is the decimal separator (used if necessary) and may only have a single instance present in the payload
47- Representations of numeric concepts such as "NaN" (Not a Number) and "Infinity" are not a valid payload
48- A string with just a negation sign ("-") is not a valid payload
49- An empty string ("") is not a valid payload
50
51#### Boolean
52
53- Booleans must be converted to the string literals "true" or "false"
54- Representation is case sensitive, e.g. "TRUE" or "FALSE" are not valid payloads.
55- An empty string ("") is not a valid payload
56
57#### Enum
58
59- Enum payloads must be one of the values specified in the format definition of the property
60- Enum payloads are case sensitive, e.g. "Car" will not match a format definition of "car"
61- Payloads should have leading and trailing whitespace removed
62- An empty string ("") is not a valid payload
63
64#### Color
65
66- Color payload validity varies depending on the property format definition of either "rgb" or "hsv"
67- Both payload types contain comma separated whole numbers of differing restricted ranges
68- The encoded string may only contain whole numbers and the comma character ",", no other characters are permitted, including spaces (" ")
69- Payloads for type "rgb" contains 3 comma separated values of numbers with a valid range between 0 and 255. e.g. 100,100,100
70- Payloads for type "hsv" contains 3 comma separated values of numbers. The first number has a range of 0 to 360, the second and third numbers have a range of 0 to 100. e.g. 300,50,75
71- An empty string ("") is not a valid payload
72
nn78
79### Last will
80
81MQTT only allows one last will message per connection.
82Homie requires the last will (LWT) to set the `homie` / `device ID` / `$state` attribute to the value **`lost`**, see [Device Lifecycle](#device-lifecycle).
83As a consequence a new MQTT connection to the broker is required per published device.
84
85## Base Topic
86
87The root topic in this document is `homie/`.
88If this root topic does not suit your needs (in case of, e.g., a public broker or because of branding),
89you can choose another.
90
91Homie controllers must by default perform auto-discovery on the wildcard topic "+/+/$homie".
92Controllers are free to restrict discovery to a specific root topic, configurable by the user.
n84 n
85Nodes can be **arrays**.
86For example, instead of creating two `lights` node to control front lights and back lights independently, we can set the `lights` node to be an array with two elements.
n95Properties can be **settable**.n
96For example, you don't want your `temperature` property to be settable in case of a temperature sensor (like the car example), but to be settable in case of a thermostat.
97
98Properties can be **retained**.
99A property is retained by default. A non-retained property would be useful for momentary events (door bell pressed).
100
101A combination of those flags compiles into this list:
102
103* **retained + non-settable**: The node publishes a property state (temperature sensor)
104* **retained + settable**: The node publishes a property state, and can receive commands for the property (by controller or other party) (lamp power)
105* **non-retained + non-settable**: The node publishes momentary events (door bell pressed)
106* **non-retained + settable**: The node publishes momentary events, and can receive commands for the property (by controller or other party) (brew coffee)
107
n115----n
116
117### Base Topic
118
119The base topic you will see in the following convention will be `homie/`.
120If this base topic does not suit your needs (in case of, e.g., a public broker), you can choose another.
121
122Be aware, that only the default base topic `homie/` is eligible for automatic discovery by third party controllers.
123
124----
125
n134When the MQTT connection to the broker is established or re-established, the device MUST send its attributes to the broker immediately.n
n136<table>n128The following device attributes are mandatory and MUST be send, even if it is just an empty string.
137 <tr>
138 <th>Topic</th>
139 <th>Direction</th>
140 <th>Description</th>
141 <th>Retained</th>
142 <th>Required</th>
143 </tr>
144 <tr>
145 <td>$homie</td>
146 <td>Device → Controller</td>
147 <td>Version of the Homie convention the device conforms to</td>
148 <td>Yes</td>
149 <td>Yes</td>
150 </tr>
151 <tr>
152 <td>$name</td>
153 <td>Device → Controller</td>
154 <td>Friendly name of the device</td>
155 <td>Yes</td>
156 <td>Yes</td>
157 </tr>
158 <tr>
159 <td>$state</td>
160 <td>Device → Controller</td>
161 <td>
162 See <a href="#device-behavior">Device behavior</a>
163 </td>
164 <td>Yes</td>
165 <td>Yes</td>
166 </tr>
167 <tr>
168 <td>$localip</td>
169 <td>Device → Controller</td>
170 <td>IP of the device on the local network</td>
171 <td>Yes</td>
172 <td>Yes</td>
173 </tr>
174 <tr>
175 <td>$mac</td>
176 <td>Device → Controller</td>
177 <td>Mac address of the device network interface. The format MUST be of the type <code>A1:B2:C3:D4:E5:F6</code></td>
178 <td>Yes</td>
179 <td>Yes</td>
180 </tr>
181 <tr>
182 <td>$fw/name</td>
183 <td>Device → Controller</td>
184 <td>Name of the firmware running on the device. Allowed characters are the same as the device ID</td>
185 <td>Yes</td>
186 <td>Yes</td>
187 </tr>
188 <tr>
189 <td>$fw/version</td>
190 <td>Device → Controller</td>
191 <td>Version of the firmware running on the device</td>
192 <td>Yes</td>
193 <td>Yes</td>
194 </tr>
195 <tr>
196 <td>$nodes</td>
197 <td>Device → Controller</td>
198 <td>
199 Nodes the device exposes, with format <code>id</code> separated by a <code>,</code> if there are multiple nodes.
200 To make a node an array, append <code>[]</code> to the ID.
201 </td>
202 <td>Yes</td>
203 <td>Yes</td>
204 </tr>
205 <tr>
206 <td>$implementation</td>
207 <td>Device → Controller</td>
208 <td>An identifier for the Homie implementation (example <code>esp8266</code>)</td>
209 <td>Yes</td>
210 <td>Yes</td>
211 </tr>
212 <tr>
213 <td>$implementation/#</td>
214 <td>Controller → Device or Device → Controller</td>
215 <td>You can use any subtopics of <code>$implementation</code> for anything related to your specific Homie implementation.</td>
216 <td>Yes or No, depending of your implementation</td>
217 <td>No</td>
218 </tr>
219 <tr>
220 <td>$stats</td>
221 <td>Device → Controller</td>
222 <td>Specify all optional stats that the device will announce, with format <code>stats</code> separated by a <code>,</code> if there are multiple stats. See next section for an example</td>
223 <td>Yes</td>
224 <td>Yes</td>
225 </tr>
226 <tr>
227 <td>$stats/interval</td>
228 <td>Device → Controller</td>
229 <td>Interval in seconds at which the device refreshes its <code>$stats/+</code>: See next section for details about statistical attributes</td>
230 <td>Yes</td>
231 <td>Yes</td>
232 </tr>
233</table>
nn130| Topic | Description |
131|-------------|--------------------------------------------------------------------------:|
132| $homie | The implemented Homie convention version |
133| $name | Friendly name of the device |
134| $state | See [Device Lifecycle](#device-lifecycle) |
135| $nodes | [Nodes](#nodes) the device exposes, separated by `,` for multiple ones. |
136| $extensions | Supported extensions, separated by `,` for multiple ones. |
137
138Optional topics include:
139
140| Topic | Description |
141|-----------------|-------------------------------|
142| $implementation | An identifier for the Homie implementation (example "esp8266") |
143
235For example, a device with an ID of `super-car` that comprises off a `wheels`, `engine` and a `lights` node would send:144For example, a device with an ID of `super-car` that comprises of a `wheels`, `engine` and a `lights` node would send:
n240homie/super-car/$localip → "192.168.0.10"n
241homie/super-car/$mac → "DE:AD:BE:EF:FE:ED"
242homie/super-car/$fw/name → "weatherstation-firmware"
243homie/super-car/$fw/version → "1.0.0"
244homie/super-car/$nodes → "wheels,engine,lights[]"149homie/super-car/$nodes → "wheels,engine,lights"
n246homie/super-car/$stats/interval → "60"n
n250#### Device Behaviorn154#### Device Lifecycle
n252The `$state` device attribute represents, as the name suggests, the current state of the device.n156The `$state` device attribute represents the current state of the device.
n256This is the first message that must that must be sent.n160This state is optional, and may be sent if the device takes a long time to initialize, but wishes to announce to consumers that it is coming online.
257* **`ready`**: this is the state the device is in when it is connected to the MQTT broker, has sent all Homie messages and is ready to operate.161* **`ready`**: this is the state the device is in when it is connected to the MQTT broker, has sent all Homie messages and is ready to operate. A Homie Controller can assume default values for all optional topics.
258You have to send this message after all other announcements message have been sent.
n268#### Device Statisticsn
269
270* `homie` / `device ID` / `$stats`/ **`$device-statistic-attribute`**:
271The `$stats/` hierarchy allows to send device attributes that change over time. The device MUST send them every `$stats/interval` seconds.
272
273<table>
274 <tr>
275 <th>Topic</th>
276 <th>Direction</th>
277 <th>Description</th>
278 <th>Retained</th>
279 <th>Required</th>
280 </tr>
281 <tr>
282 <td>$stats/uptime</td>
283 <td>Device → Controller</td>
284 <td>Time elapsed in seconds since the boot of the device</td>
285 <td>Yes</td>
286 <td>Yes</td>
287 </tr>
288 <tr>
289 <td>$stats/signal</td>
290 <td>Device → Controller</td>
291 <td>Signal strength in %</td>
292 <td>Yes</td>
293 <td>No</td>
294 </tr>
295 <tr>
296 <td>$stats/cputemp</td>
297 <td>Device → Controller</td>
298 <td>CPU Temperature in °C</td>
299 <td>Yes</td>
300 <td>No</td>
301 </tr>
302 <tr>
303 <td>$stats/cpuload</td>
304 <td>Device → Controller</td>
305 <td>
306 CPU Load in %.
307 Average of last <code>$interval</code> including all CPUs
308 </td>
309 <td>Yes</td>
310 <td>No</td>
311 </tr>
312 <tr>
313 <td>$stats/battery</td>
314 <td>Device → Controller</td>
315 <td>Battery level in %</td>
316 <td>Yes</td>
317 <td>No</td>
318 </tr>
319 <tr>
320 <td>$stats/freeheap</td>
321 <td>Device → Controller</td>
322 <td>Free heap in bytes</td>
323 <td>Yes</td>
324 <td>No</td>
325 </tr>
326 <tr>
327 <td>$stats/supply</td>
328 <td>Device → Controller</td>
329 <td>Supply Voltage in V</td>
330 <td>Yes</td>
331 <td>No</td>
332 </tr>
333</table>
334
335For example, our `super-car` device with `$stats/interval` value "60" is supposed to send its current values every 60 seconds:
336
337```java
338homie/super-car/$stats → "uptime,cputemp,signal,battery"
339homie/super-car/$stats/uptime → "120"
340homie/super-car/$stats/cputemp → "48"
341homie/super-car/$stats/signal → "24"
342homie/super-car/$stats/battery → "80"
343```
344
345----
346
n355A node attribute MUST be one of these:n
n357<table>n180All listed attributes are **required**. A node attribute MUST be one of these:
358 <tr>181
359 <th>Topic</th>182| Topic | Description |
360 <th>Direction</th>183|-------------|-------------------------------------------------------------------------------------------|
361 <th>Description</th>184| $name | Friendly name of the Node |
362 <th>Retained</th>185| $type | Type of the node |
363 <th>Required</th>186| $properties | Exposed properties, separated by `,` for multiple ones. |
364 </tr>
365 <tr>
366 <td>$name</td>
367 <td>Device → Controller</td>
368 <td>Friendly name of the Node</td>
369 <td>Yes</td>
370 <td>Yes</td>
371 </tr>
372 <tr>
373 <td>$type</td>
374 <td>Device → Controller</td>
375 <td>Type of the node</td>
376 <td>Yes</td>
377 <td>Yes</td>
378 </tr>
379 <tr>
380 <td>$properties</td>
381 <td>Device → Controller</td>
382 <td>
383 Properties the node exposes, with format <code>id</code> separated by a <code>,</code> if there are multiple nodes.
384 </td>
385 <td>Yes</td>
386 <td>Yes</td>
387 </tr>
388 <tr>
389 <td>$array</td>
390 <td>Device → Controller</td>
391 <td>Range separated by a <code>-</code>. e.g. <code>0-2</code> for an array with the indexes <code>0</code>, <code>1</code> and <code>2</code></td>
392 <td>Yes</td>
393 <td>Yes, if the node is an array</td>
394 </tr>
395</table>
n405----n
406
n412* A property value (e.g. a sensor reading) is directly published to the property topic, e.g.:n201* A property payload (e.g. a sensor reading) is directly published to the property topic, e.g.:
nn205
206* Properties can be **settable**.
207 For example, you don't want your `temperature` property to be settable in case of a temperature sensor
208 (like the car example), but to be settable in case of a thermostat.
209
210* Properties can be **retained**.
211 A property is retained by default. A non-retained property would be useful for momentary events (door bell pressed).
212
213A combination of those flags compiles into this list:
214
215* **retained + non-settable**: The node publishes a property state (temperature sensor)
216* **retained + settable**: The node publishes a property state, and can receive commands for the property (by controller or other party) (lamp power)
217* **non-retained + non-settable**: The node publishes momentary events (door bell pressed)
218* **non-retained + settable**: The node publishes momentary events, and can receive commands for the property (by controller or other party) (brew coffee)
219
n420A property attribute MUST be one of these:n
n422<table>n225The following attributes are required:
423 <tr>226
424 <th>Topic</th>227| Topic | Description | Payload type |
425 <th>Direction</th>228|-----------|------------------------------------------------------|---------------------------------------------|
426 <th>Description</th>229| $name | Friendly name of the property. | String |
427 <th>Valid values</th>230| $datatype | The data type. See [Payloads](#payloads). | Enum: \[integer, float, boolean,string, enum, color\] |
428 <th>Retained</th>231
429 <th>Required (Default)</th>232The following attributes are optional:
430 </tr>233
431 <tr>234| Topic | Description | Payload type |
432 <td>$name</td>235|-----------|------------------------------------------------------|---------------------------------------------|
433 <td>Device → Controller</td>236| $format | Specifies restrictions or options for the given data type | See below |
434 <td>Friendly name of the property.</td>237| $settable | Settable (<code>true</code>). Default is read-only (<code>false</code>) | Boolean |
435 <td>Any String</td>238| $retained | Non-retained (<code>false</code>). Default is Retained (<code>true</code>). | Boolean |
436 <td>Yes</td>239| $unit | Optional unit of this property. See list below. | String |
437 <td>No ("")</td>
438 </tr>
439 <tr>
440 <td>$settable</td>
441 <td>Device → Controller</td>
442 <td>Specifies whether the property is settable (<code>true</code>) or readonly (<code>false</code>)</td>
443 <td><code>true</code> or <code>false</code></td>
444 <td>Yes</td>
445 <td>No (<code>false</code>)</td>
446 </tr>
447 <tr>
448 <td>$retained</td>
449 <td>Device → Controller</td>
450 <td>Specifies whether the property is retained (<code>true</code>) or non-retained (<code>false</code>). Publishing to a non-retained property topic MUST always happen with the MQTT 'retain' flag off.</td>
451 <td><code>true</code> or <code>false</code></td>
452 <td>Yes</td>
453 <td>No (<code>true</code>)</td>
454 </tr>
455 <tr>
456 <td>$unit</td>
457 <td>Device → Controller</td>
458 <td>
459 A string containing the unit of this property.
460 You are not limited to the recommended values, although they are the only well known ones that will have to be recognized by any Homie consumer.
461 </td>
462 <td>
463 Recommended:<br>
464 <code>°C</code> Degree Celsius<br>
465 <code>°F</code> Degree Fahrenheit<br>
466 <code>°</code> Degree<br>
467 <code>L</code> Liter<br>
468 <code>gal</code> Galon<br>
469 <code>V</code> Volts<br>
470 <code>W</code> Watt<br>
471 <code>A</code> Ampere<br>
472 <code>%</code> Percent<br>
473 <code>m</code> Meter<br>
474 <code>ft</code> Feet<br>
475 <code>Pa</code> Pascal<br>
476 <code>psi</code> PSI<br>
477 <code>#</code> Count or Amount
478 </td>
479 <td>Yes</td>
480        <td>No ("")</td>
481 </tr>
482 <tr>
483 <td>$datatype</td>
484 <td>Device → Controller</td>
485 <td>Describes the format of data.</td>
486 <td>
487 <code>integer</code>,
488 <code>float</code>,
489 <code>boolean</code>,
490 <code>string</code>,
491 <code>enum</code>,
492 <code>color</code>
493 </td>
494 <td>Yes</td>
495 <td>No (<code>string</code>)</td>
496 </tr>
497 <tr>
498 <td>$format</td>
499 <td>Device → Controller</td>
500 <td>
501 Describes what are valid values for this property.
502 </td>
503 <td>
504 <ul>
505 <li>
506 <code>from:to</code> Describes a range of values e.g. <code>10:15</code>.
507 <br>Valid for datatypes <code>integer</code>, <code>float</code>
508 </li>
509 <li>
510 <code>value,value,value</code> for enumerating all valid values.
511 Escape <code>,</code> by using <code>,,</code>. e.g. <code>A,B,C</code> or <code>ON,OFF,PAUSE</code>.
512 <br>Valid for datatypes <code>enum</code>
513 </li>
514 <li>
515 <code>rgb</code> to provide colors in RGB format e.g. <code>255,255,0</code> for yellow.
516 <code>hsv</code> to provide colors in HSV format e.g. <code>60,100,100</code> for yellow.
517 <br>Valid for datatype <code>color</code>
518 </li>
519 </ul>
520 </td>
521 <td>Yes</td>
522 <td>No for $datatype <code>string</code>,<code>integer</code>,<code>float</code>,<code>boolean</code>. Yes for <code>enum</code>,<code>color</code></td>
523 </tr>
524</table>
nn252Format:
253
254* For `integer` and `float`: Describes a range of payloads e.g. `10:15`
255* For `enum`: `payload,payload,payload` for enumerating all valid payloads.
256* For `color`:
257 - `rgb` to provide colors in RGB format e.g. `255,255,0` for yellow.
258 - `hsv` to provide colors in HSV format e.g. `60,100,100` for yellow.
259
260Recommended unit strings:
261
262* `°C`: Degree Celsius
263* `°F`: Degree Fahrenheit
264* `°`: Degree
265* `L`: Liter
266* `gal`: Galon
267* `V`: Volts
268* `W`: Watt
269* `A`: Ampere
270* `%`: Percent
271* `m`: Meter
272* `ft`: Feet
273* `Pa`: Pascal
274* `psi`: PSI
275* `#`: Count or Amount
276
277You are not limited to the recommended values, although they are the only well known ones that will have to be recognized by any Homie consumer.
278
279#### Property command topic
280
537* `homie` / `device ID` / `node ID` / `property ID` / **`set`**: the device can subscribe to this topic if the property is **settable** from the controller, in case of actuators.281* `homie` / `device ID` / `node ID` / `property ID` / **`set`**: The device must subscribe to this topic if the property is **settable** (in case of actuators for example).
n539Homie is state-based.n283A Homie controller publishes to the `set` command topic with non-retained messages only.
540You don't tell your smartlight to `turn on`, but you tell it to put its `power` state to `on`.
541This especially fits well with MQTT, because of retained message.
n543For example, a `kitchen-light` device exposing a `light` node would subscribe to `homie/kitchen-light/light/power/set` and it would receive:n285The assigned and processed payload must be reflected by the Homie device in the property topic `homie` / `device ID` / `node ID` / `property ID` as soon as possible.
286This property state update not only informs other devices about the change but closes the control loop for the commanding controller, important for deterministic interaction with the client device.
287
288To give an example: A `kitchen-light` device exposing the `light` node with a settable `power` property subscribes to the topic `homie/kitchen-light/light/power/set` for commands:
n549The device would then turn on the light, and update its `power` state.n294In response the device will turn on the light and upon success update its `power` property state accordingly:
550This provides pessimistic feedback, which is important for home automation.
n556### Arraysn
557
558A node can be an array if you've added `[]` to its ID in the `$nodes` device attribute, and if its `$array` attribute is set to the range of the array.
559Let's consider we want to control independently the front lights and back lights of our `super-car`. Our `lights` node array would look like this. Note that the topic for an element of the array node is the name of the node followed by a `_` and the index getting updated:
560
561```java
562homie/super-car/$nodes → "lights[]"
563
564homie/super-car/lights/$name → "Lights"
565homie/super-car/lights/$properties → "intensity"
566homie/super-car/lights/$array → "0-1"
567
568homie/super-car/lights/intensity/$name → "Intensity"
569homie/super-car/lights/intensity/$settable → "true"
570homie/super-car/lights/intensity/$unit → "%"
571homie/super-car/lights/intensity/$datatype → "integer"
572homie/super-car/lights/intensity/$format → "0:100"
573
574homie/super-car/lights_0/$name → "Back lights"
575homie/super-car/lights_0/intensity → "0"
576homie/super-car/lights_1/$name → "Front lights"
577homie/super-car/lights_1/intensity → "100"
578```
579
580Note that you can name each element in your array individually ("Back lights", etc.).
581
582----
583
584### Broadcast Channel300## Broadcast Channel
n586Homie defines a broadcast channel, so a controller is able to broadcast a message to every Homie devices:n302Homie defines a broadcast channel, so a controller is able to broadcast a message to all Homie devices:
n601----n317## Extensions
602----
n604## FAQn319This convention only covers discoverability of devices and its capabilities.
320The aim is to have standardized MQTT topics for all kind of complex scenarios.
321A Homie device may therefore support extensions, defined in separate documents.
322Every extension is identified by a unique ID.
n606In this section frequently asked questions will be answered.n324The ID consists of the reverse domain name and a freely chosen suffix.
607This includes design decisions and drawn compromises in the specifics of the Homie convention.325The proper term `homie` is reserved and must not be used as the suffix or as part of the domain name.
n609### How do I query/request a property?n327For example, an organization `example.org` wanting to add a feature `our-feature` would choose the extension ID `org.example.our-feature`.
n611You don't.n329Every extension must be published using a license.
612The MQTT protocol does not implement the request-reply but rather the publish-subscribe messaging pattern.330The license can be chosen freely, even proprietary licenses are possible.
613The Homie convention follows the publish-subscribe principle by publishing data as retained messages on a regular basis.331The recommended license is the [CCA 4.0](https://homieiot.github.io/license), since this is the license Homie itself uses.
614You might want to rethink the design of your application - in most scenarios a regularly updated information is sufficient.
n616*Workaround:* You are free to implement your own ideas on top of the basic structure of the Homie convention.n
617You could either implement a `get` getter topic and its logic to trigger a value update, or you may exploit the concept of Homie properties and define a settable property to trigger a value update.
t619A discussion on the matter can be found in issue [#79](https://github.com/homieiot/convention/issues/79).t
620
621
+
+
+
+ + + +
+ +
+ + + + diff --git a/specification/spec-core-v4_0_0/index.html b/specification/spec-core-v4_0_0/index.html new file mode 100644 index 0000000..f61f79b --- /dev/null +++ b/specification/spec-core-v4_0_0/index.html @@ -0,0 +1,575 @@ + + + + + + + + + The Homie convention + + + + + + + + + + + + + +
+
+ +
+ + + +
+
+ homieiot/convention +
+
+
+ + + +
+ +
+ + + + + + + + + + +
+
+
+

Homie: An MQTT Convention for IoT/M2M

+
+
+
+ +
+ + + +
+ + License: CCA 4.0
+ Version: + + + + [develop] + + + + [v1.5.0] + + + + [v2.0.0] + + + + [v2.0.1] + + + + [v3.0.0] + + + + [v3.0.1] + + + + [v4.0.0] + +
+ + Changes: [Diff to previous]
+ + Release date: 27. August 2019 +
+
+ +
+ Frequently asked questions + +

How do I query/request a property?

+

You don’t. +The MQTT protocol does not implement the request-reply but rather the publish-subscribe messaging pattern. +The Homie convention follows the publish-subscribe principle by publishing data as retained messages on a regular basis. +You might want to rethink the design of your application - in most scenarios a regularly updated information is sufficient.

+

Workaround: You are free to implement your own ideas on top of the basic structure of the Homie convention. +You could either implement a get getter topic and its logic to trigger a value update, or you may exploit the concept of Homie properties and define a settable property to trigger a value update.

+ + +
+ +
+

License

+

By exercising the Licensed Rights (defined on https://creativecommons.org/licenses/by/4.0/), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions.

+
+ +
+

Table of Contents

+
+ + +

+

MQTT Restrictions

+

Homie communicates through MQTT and is hence based on the basic principles of MQTT topic publication and subscription.

+

Topic IDs

+

An MQTT topic consists of one or more topic levels, separated by the slash character (/). +A topic level ID MAY contain lowercase letters from a to z, numbers from 0 to 9 as well as the hyphen character (-).

+

A topic level ID MUST NOT start or end with a hyphen (-). +The special character $ is used and reserved for Homie attributes.

+

Payload

+
    +
  • Every MQTT message payload MUST be sent as a UTF-8 encoded string
  • +
  • The value published as payload MUST be valid for the respective property/attribute type as per the list below
  • +
+

String

+
    +
  • String types are limited to 268,435,456 characters
  • +
  • An empty string ("") is a valid payload
  • +
+

Integer

+
    +
  • Integer types are UTF-8 encoded string literal representations of 64-bit signed whole numbers
  • +
  • Integers range from -9,223,372,036,854,775,808 (-263) to 9,223,372,036,854,775,807 (263-1)
  • +
  • The payload may only contain whole numbers and the negation character “-”. No other characters including spaces (" “) are permitted
  • +
  • A string with just a negation sign (”-") is not a valid payload
  • +
  • An empty string ("") is not a valid payload
  • +
+

Float

+
    +
  • Float types are UTF-8 encoded string literal representations of 64-bit signed floating point numbers
  • +
  • Floats range from 2-1074 to (2-2-52)*21023
  • +
  • The payload may only contain whole numbers, the negation character “-”, the exponent character “e” or “E” and the decimal separator “.”, no other characters, including spaces (" “) are permitted
  • +
  • The dot character (”.") is the decimal separator (used if necessary) and may only have a single instance present in the payload
  • +
  • Representations of numeric concepts such as “NaN” (Not a Number) and “Infinity” are not a valid payload
  • +
  • A string with just a negation sign ("-") is not a valid payload
  • +
  • An empty string ("") is not a valid payload
  • +
+

Boolean

+
    +
  • Booleans must be converted to the string literals “true” or “false”
  • +
  • Representation is case sensitive, e.g. “TRUE” or “FALSE” are not valid payloads.
  • +
  • An empty string ("") is not a valid payload
  • +
+

Enum

+
    +
  • Enum payloads must be one of the values specified in the format definition of the property
  • +
  • Enum payloads are case sensitive, e.g. “Car” will not match a format definition of “car”
  • +
  • Payloads should have leading and trailing whitespace removed
  • +
  • An empty string ("") is not a valid payload
  • +
+

Color

+
    +
  • Color payload validity varies depending on the property format definition of either “rgb” or “hsv”
  • +
  • Both payload types contain comma separated whole numbers of differing restricted ranges
  • +
  • The encoded string may only contain whole numbers and the comma character “,”, no other characters are permitted, including spaces (" “)
  • +
  • Payloads for type “rgb” contains 3 comma separated values of numbers with a valid range between 0 and 255. e.g. 100,100,100
  • +
  • Payloads for type “hsv” contains 3 comma separated values of numbers. The first number has a range of 0 to 360, the second and third numbers have a range of 0 to 100. e.g. 300,50,75
  • +
  • An empty string (”") is not a valid payload
  • +
+

QoS and retained messages

+

The nature of the Homie convention makes it safe about duplicate messages, so the recommended QoS for reliability is QoS 1. +All messages MUST be sent as retained, UNLESS stated otherwise.

+

Last will

+

MQTT only allows one last will message per connection. +Homie requires the last will (LWT) to set the homie / device ID / $state attribute to the value lost, see Device Lifecycle. +As a consequence a new MQTT connection to the broker is required per published device.

+

Base Topic

+

The root topic in this document is homie/. +If this root topic does not suit your needs (in case of, e.g., a public broker or because of branding), +you can choose another.

+

Homie controllers must by default perform auto-discovery on the wildcard topic “+/+/$homie”. +Controllers are free to restrict discovery to a specific root topic, configurable by the user.

+

Topology

+

Devices: +An instance of a physical piece of hardware is called a device. +For example, a car, an Arduino/ESP8266 or a coffee machine.

+

Nodes: +A device can expose multiple nodes. +Nodes are independent or logically separable parts of a device. +For example, a car might expose a wheels node, an engine node and a lights node.

+

Properties: +A node can have multiple properties. +Properties represent basic characteristics of the node/device, often given as numbers or finite states. +For example the wheels node might expose an angle property. +The engine node might expose a speed, direction and temperature property. +The lights node might expose an intensity and a color property.

+

Attributes: +Devices, nodes and properties have specific attributes characterizing them. +Attributes are represented by topic identifier starting with $. +The precise definition of attributes is important for the automatic discovery of devices following the Homie convention.

+

Examples: A device might have an IP attribute, a node will have a name attribute, and a property will have a unit attribute.

+

Devices

+
    +
  • homie / device ID: this is the base topic of a device. +Each device must have a unique device ID which adhere to the ID format.
  • +
+

Device Attributes

+
    +
  • homie / device ID / $device-attribute:
  • +
+

The following device attributes are mandatory and MUST be send, even if it is just an empty string.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TopicDescription
$homieThe implemented Homie convention version
$nameFriendly name of the device
$stateSee Device Lifecycle
$nodesNodes the device exposes, separated by , for multiple ones.
$extensionsSupported extensions, separated by , for multiple ones.
+

Optional topics include:

+ + + + + + + + + + + + + +
TopicDescription
$implementationAn identifier for the Homie implementation (example “esp8266”)
+

For example, a device with an ID of super-car that comprises of a wheels, engine and a lights node would send:

+
homie/super-car/$homie  "2.1.0"
+homie/super-car/$name  "Super car"
+homie/super-car/$nodes  "wheels,engine,lights"
+homie/super-car/$implementation  "esp8266"
+homie/super-car/$state  "ready"
+

Device Lifecycle

+

The $state device attribute represents the current state of the device. +There are 6 different states:

+
    +
  • init: this is the state the device is in when it is connected to the MQTT broker, but has not yet sent all Homie messages and is not yet ready to operate. +This state is optional, and may be sent if the device takes a long time to initialize, but wishes to announce to consumers that it is coming online.
  • +
  • ready: this is the state the device is in when it is connected to the MQTT broker, has sent all Homie messages and is ready to operate. A Homie Controller can assume default values for all optional topics.
  • +
  • disconnected: this is the state the device is in when it is cleanly disconnected from the MQTT broker. +You must send this message before cleanly disconnecting.
  • +
  • sleeping: this is the state the device is in when the device is sleeping. +You have to send this message before sleeping.
  • +
  • lost: this is the state the device is in when the device has been “badly” disconnected. +You must define this message as LWT.
  • +
  • alert: this is the state the device is when connected to the MQTT broker, but something wrong is happening. E.g. a sensor is not providing data and needs human intervention. +You have to send this message when something is wrong.
  • +
+

Nodes

+
    +
  • homie / device ID / node ID: this is the base topic of a node. +Each node must have a unique node ID on a per-device basis which adhere to the ID format.
  • +
+

Node Attributes

+
    +
  • homie / device ID / node ID / $node-attribute:
  • +
+

All listed attributes are required. A node attribute MUST be one of these:

+ + + + + + + + + + + + + + + + + + + + + +
TopicDescription
$nameFriendly name of the Node
$typeType of the node
$propertiesExposed properties, separated by , for multiple ones.
+

For example, our engine node would send:

+
homie/super-car/engine/$name  "Car engine"
+homie/super-car/engine/$type  "V8"
+homie/super-car/engine/$properties  "speed,direction,temperature"
+

Properties

+
    +
  • +

    homie / device ID / node ID / property ID: this is the base topic of a property. +Each property must have a unique property ID on a per-node basis which adhere to the ID format.

    +
  • +
  • +

    A property payload (e.g. a sensor reading) is directly published to the property topic, e.g.:

    +
    homie/super-car/engine/temperature  "21.5"
    +
  • +
  • +

    Properties can be settable. +For example, you don’t want your temperature property to be settable in case of a temperature sensor +(like the car example), but to be settable in case of a thermostat.

    +
  • +
  • +

    Properties can be retained. +A property is retained by default. A non-retained property would be useful for momentary events (door bell pressed).

    +
  • +
+

A combination of those flags compiles into this list:

+
    +
  • retained + non-settable: The node publishes a property state (temperature sensor)
  • +
  • retained + settable: The node publishes a property state, and can receive commands for the property (by controller or other party) (lamp power)
  • +
  • non-retained + non-settable: The node publishes momentary events (door bell pressed)
  • +
  • non-retained + settable: The node publishes momentary events, and can receive commands for the property (by controller or other party) (brew coffee)
  • +
+

Property Attributes

+
    +
  • homie / device ID / node ID / property ID / $property-attribute:
  • +
+

The following attributes are required:

+ + + + + + + + + + + + + + + + + + + + +
TopicDescriptionPayload type
$nameFriendly name of the property.String
$datatypeThe data type. See Payloads.Enum: [integer, float, boolean,string, enum, color]
+

The following attributes are optional:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TopicDescriptionPayload type
$formatSpecifies restrictions or options for the given data typeSee below
$settableSettable (true). Default is read-only (false)Boolean
$retainedNon-retained (false). Default is Retained (true).Boolean
$unitOptional unit of this property. See list below.String
+

For example, our temperature property would send:

+
homie/super-car/engine/temperature/$name  "Engine temperature"
+homie/super-car/engine/temperature/$settable  "false"
+homie/super-car/engine/temperature/$unit  "°C"
+homie/super-car/engine/temperature/$datatype  "float"
+homie/super-car/engine/temperature/$format  "-20:120"
+homie/super-car/engine/temperature  "21.5"
+

Format:

+
    +
  • For integer and float: Describes a range of payloads e.g. 10:15
  • +
  • For enum: payload,payload,payload for enumerating all valid payloads.
  • +
  • For color: +
      +
    • rgb to provide colors in RGB format e.g. 255,255,0 for yellow.
    • +
    • hsv to provide colors in HSV format e.g. 60,100,100 for yellow.
    • +
    +
  • +
+

Recommended unit strings:

+
    +
  • °C: Degree Celsius
  • +
  • °F: Degree Fahrenheit
  • +
  • °: Degree
  • +
  • L: Liter
  • +
  • gal: Galon
  • +
  • V: Volts
  • +
  • W: Watt
  • +
  • A: Ampere
  • +
  • %: Percent
  • +
  • m: Meter
  • +
  • ft: Feet
  • +
  • Pa: Pascal
  • +
  • psi: PSI
  • +
  • #: Count or Amount
  • +
+

You are not limited to the recommended values, although they are the only well known ones that will have to be recognized by any Homie consumer.

+

Property command topic

+
    +
  • homie / device ID / node ID / property ID / set: The device must subscribe to this topic if the property is settable (in case of actuators for example).
  • +
+

A Homie controller publishes to the set command topic with non-retained messages only.

+

The assigned and processed payload must be reflected by the Homie device in the property topic homie / device ID / node ID / property ID as soon as possible. +This property state update not only informs other devices about the change but closes the control loop for the commanding controller, important for deterministic interaction with the client device.

+

To give an example: A kitchen-light device exposing the light node with a settable power property subscribes to the topic homie/kitchen-light/light/power/set for commands:

+
homie/kitchen-light/light/power/set  "true"
+

In response the device will turn on the light and upon success update its power property state accordingly:

+
homie/kitchen-light/light/power  "true"
+

Broadcast Channel

+

Homie defines a broadcast channel, so a controller is able to broadcast a message to all Homie devices:

+
    +
  • homie / $broadcast / level: level is an arbitrary broadcast identifier. +It must adhere to the ID format.
  • +
+

For example, you might want to broadcast an alert event with the alert reason as the payload. +Devices are then free to react or not. +In our case, every buzzer of your home automation system would start buzzing.

+
homie/$broadcast/alert  "Intruder detected"
+

Any other topic is not part of the Homie convention.

+

Extensions

+

This convention only covers discoverability of devices and its capabilities. +The aim is to have standardized MQTT topics for all kind of complex scenarios. +A Homie device may therefore support extensions, defined in separate documents. +Every extension is identified by a unique ID.

+

The ID consists of the reverse domain name and a freely chosen suffix. +The proper term homie is reserved and must not be used as the suffix or as part of the domain name.

+

For example, an organization example.org wanting to add a feature our-feature would choose the extension ID org.example.our-feature.

+

Every extension must be published using a license. +The license can be chosen freely, even proprietary licenses are possible. +The recommended license is the CCA 4.0, since this is the license Homie itself uses.

+ +
+ +
+
+ + + +
+ +
+ + + + diff --git a/tags/index.xml b/tags/index.xml new file mode 100644 index 0000000..b178338 --- /dev/null +++ b/tags/index.xml @@ -0,0 +1,11 @@ + + + + Tags on The Homie convention + https://homieiot.github.io/tags/ + Recent content in Tags on The Homie convention + Hugo + en-us + + + diff --git a/tools/index.html b/tools/index.html new file mode 100644 index 0000000..b085c20 --- /dev/null +++ b/tools/index.html @@ -0,0 +1,160 @@ + + + + + + + + + The Homie convention + + + + + + + + + + + + + +
+
+ +
+ + + +
+
+ homieiot/convention +
+
+
+ + + +
+ +
+
+
+

+ Tools +

+
Find some useful tools for the Homie convention
+ +

Online verificator for Homie 4

+

Paste published Homie topics into the text field and let the online verificator + tell you what Homie devices, nodes and properties it has recognised. +

+Instructions: +
    +
  • Connect your Homie device
  • +
  • Use for example "mosquitto_sub -v -t homie/#" to create a list of all published homie topics and their payloads.
  • +
  • Paste the list into the textfield below
  • +
+ + + + +
1
2
+
homie/device123/$homie 4.0 +homie/device123/$name My device +homie/device123/$state ready +homie/device123/$extensions +homie/device123/$nodes mythermostat + +homie/device123/mythermostat/$name My thermostat +homie/device123/mythermostat/$properties temperature + +homie/device123/mythermostat/temperature 22 +homie/device123/mythermostat/temperature/$name Temperature +homie/device123/mythermostat/temperature/$unit °C +homie/device123/mythermostat/temperature/$datatype integer +
+
+ Validation result: Not yet validated +
+ +
+
+ +
+
+
+ + + + + + + +