diff --git a/CODEOWNERS b/CODEOWNERS
index c4aa2073cdd3e..ace489c4bc9c3 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -164,6 +164,7 @@
/bundles/org.openhab.binding.monopriceaudio/ @mlobstein
/bundles/org.openhab.binding.mpd/ @stefanroellin
/bundles/org.openhab.binding.mqtt/ @davidgraeff
+/bundles/org.openhab.binding.mqtt.espmilighthub/ @Skinah
/bundles/org.openhab.binding.mqtt.generic/ @davidgraeff
/bundles/org.openhab.binding.mqtt.homeassistant/ @davidgraeff
/bundles/org.openhab.binding.mqtt.homie/ @davidgraeff
diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml
index 95becddb6ca41..78f2ff07eb109 100644
--- a/bom/openhab-addons/pom.xml
+++ b/bom/openhab-addons/pom.xml
@@ -806,6 +806,11 @@
org.openhab.binding.mqtt${project.version}
+
+ org.openhab.addons.bundles
+ org.openhab.binding.mqtt.espmilighthub
+ ${project.version}
+ org.openhab.addons.bundlesorg.openhab.binding.mqtt.generic
diff --git a/bundles/org.openhab.binding.mqtt.espmilighthub/NOTICE b/bundles/org.openhab.binding.mqtt.espmilighthub/NOTICE
new file mode 100644
index 0000000000000..38d625e349232
--- /dev/null
+++ b/bundles/org.openhab.binding.mqtt.espmilighthub/NOTICE
@@ -0,0 +1,13 @@
+This content is produced and maintained by the openHAB project.
+
+* Project home: https://www.openhab.org
+
+== Declared Project Licenses
+
+This program and the accompanying materials are made available under the terms
+of the Eclipse Public License 2.0 which is available at
+https://www.eclipse.org/legal/epl-2.0/.
+
+== Source Code
+
+https://github.com/openhab/openhab-addons
diff --git a/bundles/org.openhab.binding.mqtt.espmilighthub/README.md b/bundles/org.openhab.binding.mqtt.espmilighthub/README.md
new file mode 100644
index 0000000000000..58a76b6c988e1
--- /dev/null
+++ b/bundles/org.openhab.binding.mqtt.espmilighthub/README.md
@@ -0,0 +1,193 @@
+# EspMilightHub Binding
+
+This binding allows an open source esp8266 based bridge to automatically find and add Milight globes.
+The hubs can be built from 2 ready made boards and only need connecting with 7 wires.
+They can be very easy to build with no soldering needed.
+
+Advantages to using this DIY bridge over the OEM bridge:
+
++ Almost unlimited groups to give individual control over an entire house of Milight globes without needing multiple bridges.
++ If using the Milight remotes to control the globes, this binding can update the openHAB controls the moment a key is pressed on the physical remotes.
++ Supports auto discovery.
+
+## Setup the hardware
+
+In depth details on how to build and what the bridge is can be found here:
+
+A quick overview of the steps to get the hardware going are:
+
++ Connect a nodemcu/D1 mini/esp8266 to your computer via a USB cable.
++ Download the latest BIN file from here
++ Download esp8266flasher if you are on windows
++ Check the blog above on more info for Mac or Linux.
++ Open the flasher tool and make sure the flash size is 4mb or whatever your esp8266 board has.
++ Flash the bin and press the reset button on the board when it completes.
++ Connect to the wifi access point of the esp directly with your phone/tablet and setup wifi details.
++ Login by using the IP address of the esp8266 in a web browser and the control panel will show up.
++ Connect 7 wires between the two ready made PCBs as shown in the blog.
++ Setup a MQTT broker as this method uses the faster and lightweight MQTT protocol and not UDP.
+
+## Setup the Firmware
+
+Enter the control panel for the ESP8266 by using any browser and enter the IP address.
+The following options need to be changed in the firmware for the binding to work.
+Click on SETTINGS>MQTT>:
+
+**mqtt_topic_pattern:**
+`milight/commands/:device_id/:device_type/:group_id`
+
+**mqtt_update_topic_pattern:**
+Leave this blank.
+
+**mqtt_state_topic_pattern:**
+`milight/states/:device_id/:device_type/:group_id`
+
+**group_state_fields:**
+IMPORTANT: Make sure only the following are ticked:
+
++ state
++ level
++ hue
++ saturation
++ mode
++ color_temp
++ bulb_mode
+
+Fill in the MQTT broker fields with the correct details so the hub can connect and then click **save**.
+Now when you use any Milight remote control, you will see MQTT topics being created that should include `level` and `hsb` in the messages.
+If you see `brightness` and not `level`, then go back and follow the above setup steps.
+
+You can use this Linux command to watch all MQTT topics from Milight:
+
+```
+mosquitto_sub -u usernamehere -P passwordhere -p 1883 -v -t 'milight/#'
+```
+
+You can also use the mosquitto_pub command to send your own commands and watch the bulbs respond all without the binding being setup.
+Everything this binding does goes in and out via MQTT and can be watched with the above command.
+Once you have setup and test the hub you can move onto using the binding.
+
+## Supported Things
+
+This binding is best thought of as a remote control emulator, so the things are really the type of remote that you own and not the globes.
+The Milight protocol is 1 way only so there is no way to find actual globes.
+
+| Thing Type ID | Description |
+|-|-|
+| `rgb_cct` | Remote that has 4 channels and controls globes with full colour, and both cool and warm whites. |
+| `fut089` | Remote is the newer 8 channel type called FUT089 and your globes are the rgb_cct. |
+| `cct` | Remote is 4 channels and the globes have no colours with only cool and warm white controls. |
+| `fut091` | Remote is the newer 8 group model called a fut091 and your globes are cct. |
+| `rgbw` | Remote is 4 channels and the globes have RGB and a fixed white. |
+| `rgb` | Remote is 4 channels and the globes have full RGB with no white. |
+
+## Discovery
+
+First install the MQTT binding and setup a `broker` thing and make sure it is ONLINE, as this binding uses the MQTT binding to talk to your broker and hence that binding must be setup first.
+Next, move a control on either a physical remote, or used a virtual control inside the esp8266 control panel web page which cause a MQTT message to be sent.
+This binding should then detect the new device the moment the control is moved and a new entry should appear in your INBOX.
+
+To remove a saved state from your MQTT broker that causes an entry in your INBOX you can use this command or use the ignore feature of openHAB.
+
+```
+mosquitto_pub -u username -P password -p 1883 -t 'milight/states/0x0/rgb_cct/1' -n -r
+```
+
+## Thing Configuration
+
+| Parameter | Description | Required | Default |
+|-|-|-|-|
+| `whiteHue` | When both the `whiteHue` and `whiteSat` values are seen by the binding it will trigger the white LEDS. Set to -1 to disable, 0 for Alexa, or 35 for Google Home. | Y | 35 |
+| `whiteSat` | When both the whiteHue and whiteSat values are seen by the binding it will trigger the white LEDS. Set to -1 to disable, 100 for Alexa or 32 for Google Home. | Y | 32 |
+| `favouriteWhite` | When one of the shortcuts triggers white mode, use this for the colour white instead of the default colour. | Y |200 |
+| `dimmedCT` | Traditional globes grow warmer the more they are dimmed. Set this to 370, or leave blank to disable. | N | blank |
+| `oneTriggersNightMode` | Night mode is a much lower level of light and this feature allows it to be auto selected when your fader/slider moves to 1%. NOTE: Night mode by design locks out some controls of a physical remote, so this feature is disabled by default. | Y | false |
+| `powerFailsToMinimum` | If lights loose power from the power switch OR a power outage, they will default to using the lowest brightness if the light was turned off before the power failure occurred. | Y | true |
+| `whiteThreshold` | RGBW globes do not respond to saturation changes, so this feature allows you to specify a number that if the saturation drops below, it will trigger the white mode. -1 will disable this feature. | Y | 12 |
+
+## Channels
+
+| Channel | Type | Description |
+|-|-|-|
+| `level` | Dimmer | Level changes the brightness of the globe. |
+| `colourTemperature` | Dimmer | Change from cool to warm white with this control. |
+| `colour` | Color | Allows you to change the colour, brightness and saturation of the globe. |
+| `discoMode` | String | Switch to a Disco mode directly from a drop down list. |
+| `bulbMode` | String (read only) | Displays the mode the bulb is currently in so that rules can determine if the globe is white, a color, disco modes or night mode are selected. |
+| `command` | String | Sends the raw commands that the buttons on a remote send. |
+
+## Note Regarding Transmission Delays
+
+If you have lots of globes and openHAB turns them all on, you may notice a delay that causes the globes to turn on one by one and the delay can add up when a lot of globes are installed in your house.
+This is caused by the time it takes to transmit the desired setting to the globe multiplied by how many times the hub repeats transmitting the setting.
+Since it takes around 2.8ms for a setting to be transmitted, if the firmware is set to repeat the packets 50 times it would then take 2.8*50 = 140ms before the next globe starts to have its new state transmitted by the hub.
+You can reduce the packet repeats to speed up the response of this binding and the hub by tweaking a few settings.
+
+Settings can be found on the radio tab in the esp control panel using your browser.
+Suggested settings are as follows:
+
++ Packet repeats = 12 (if you only turn 1 globe on or off it uses this value)
++ Packet repeat throttle threshold = 200
++ Packet repeat throttle sensitivity = 0
++ Packet repeat minimum = 8 (When turning multiple globes on and off it will use this value as it throttles the repeats back to reduce latency/delay between each globe)
+
+## Important for Textual Configuration
+
+This binding requires things to have a specific format for the unique ID, the auto discovery does this for you.
+
+If doing textual configuration you need to add the Device ID and Group ID together to create the things unique ID.
+The DeviceID is different for each remote.
+The GroupID can be 0 (all channels on the remote), or 1 to 8 for each of the individual channels on the remote).
+If you do not understand this please use auto discovery to do it for you.
+
+The formula is
+DeviceID + GroupID = ThingUID
+
+For example:
+
+| Device ID | Group ID |ThingUID |
+|-----------|----------|----------|
+| 0xE6C | 4 | 0xE6C4 |
+| 0xB4CA | 4 | 0xB4CA4 |
+| 0xB4CA | 8 | 0xB4CA8 |
+| 0xB4CA | 0 | 0xB4CA0 |
+
+## Full Example
+
+To use these examples for textual configuration, you must already have a configured a MQTT `broker` thing and know its unique ID.
+This UID will be used in the things file and will replace the text `myBroker`.
+The first line in the things file will create a `broker` thing and this can be removed if you have already setup a broker in another file or via the UI already.
+
+*.things
+
+```
+Bridge mqtt:broker:myBroker [ host="localhost", secure=false, password="*******", qos=1, username="user"]
+Thing mqtt:rgb_cct:0xE6C4 "Hallway" (mqtt:broker:myBroker) @ "MQTT"
+```
+
+*.items
+
+```
+Dimmer Hallway_Level "Front Hall" {channel="mqtt:rgb_cct:0xE6C4:level"}
+Dimmer Hallway_ColourTemperature "White Color Temp" {channel="mqtt:rgb_cct:0xE6C4:colourTemperature"}
+Color Hallway_Colour "Front Hall" ["Lighting"] {channel="mqtt:rgb_cct:0xE6C4:colour"}
+String Hallway_DiscoMode "Disco Mode" {channel="mqtt:rgb_cct:0xE6C4:discoMode"}
+String Hallway_BulbCommand "Send Command" {channel="mqtt:rgb_cct:0xE6C4:command"}
+String Hallway_BulbMode "Bulb Mode" {channel="mqtt:rgb_cct:0xE6C4:bulbMode"}
+
+```
+
+*.sitemap
+
+```
+ Text label="Hallway" icon="light"
+ {
+ Switch item=Hallway_Level
+ Slider item=Hallway_Level
+ Slider item=Hallway_ColourTemperature
+ Colorpicker item=Hallway_Colour
+ Selection item=Hallway_DiscoMode
+ Text item=Hallway_BulbMode
+ Switch item=Hallway_BulbCommand mappings=[next_mode='Mode +', previous_mode='Mode -', mode_speed_up='Speed +', mode_speed_down='Speed -', set_white='White', night_mode='Night' ]
+ }
+```
diff --git a/bundles/org.openhab.binding.mqtt.espmilighthub/pom.xml b/bundles/org.openhab.binding.mqtt.espmilighthub/pom.xml
new file mode 100644
index 0000000000000..9c0e234649af3
--- /dev/null
+++ b/bundles/org.openhab.binding.mqtt.espmilighthub/pom.xml
@@ -0,0 +1,24 @@
+
+
+
+ 4.0.0
+
+
+ org.openhab.addons.bundles
+ org.openhab.addons.reactor.bundles
+ 3.1.0-SNAPSHOT
+
+
+ org.openhab.binding.mqtt.espmilighthub
+ openHAB Add-ons :: Bundles :: MQTT EspMilightHub
+
+
+
+ org.openhab.addons.bundles
+ org.openhab.binding.mqtt
+ ${project.version}
+ provided
+
+
+
diff --git a/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/feature/feature.xml b/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/feature/feature.xml
new file mode 100644
index 0000000000000..1f8b3a2cd5052
--- /dev/null
+++ b/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/feature/feature.xml
@@ -0,0 +1,12 @@
+
+
+ mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features
+
+
+ openhab-runtime-base
+ openhab-transport-mqtt
+ mvn:org.openhab.addons.bundles/org.openhab.binding.mqtt/${project.version}
+ mvn:org.openhab.addons.bundles/org.openhab.binding.mqtt.espmilighthub/${project.version}
+
+
+
diff --git a/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/java/org/openhab/binding/mqtt/espmilighthub/internal/ConfigOptions.java b/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/java/org/openhab/binding/mqtt/espmilighthub/internal/ConfigOptions.java
new file mode 100644
index 0000000000000..138fa3ae1f829
--- /dev/null
+++ b/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/java/org/openhab/binding/mqtt/espmilighthub/internal/ConfigOptions.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.mqtt.espmilighthub.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link ConfigOptions} Holds the config for the settings.
+ *
+ * @author Matthew Skinner - Initial contribution
+ */
+@NonNullByDefault
+public class ConfigOptions {
+ public int whiteThreshold = -1;
+ public int whiteSat = 32;
+ public int whiteHue = 35;
+ public int favouriteWhite = 200;
+ public boolean oneTriggersNightMode = false;
+ public boolean powerFailsToMinimum = false;
+ public int dimmedCT = -1;
+}
diff --git a/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/java/org/openhab/binding/mqtt/espmilighthub/internal/EspMilightHubBindingConstants.java b/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/java/org/openhab/binding/mqtt/espmilighthub/internal/EspMilightHubBindingConstants.java
new file mode 100644
index 0000000000000..b62cb29642e49
--- /dev/null
+++ b/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/java/org/openhab/binding/mqtt/espmilighthub/internal/EspMilightHubBindingConstants.java
@@ -0,0 +1,53 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+package org.openhab.binding.mqtt.espmilighthub.internal;
+
+import static org.openhab.binding.mqtt.MqttBindingConstants.BINDING_ID;
+
+import java.math.BigDecimal;
+import java.util.Set;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.thing.ThingTypeUID;
+
+/**
+ * The {@link EspMilightHubBindingConstants} class defines common constants, which are
+ * used across the whole binding.
+ *
+ * @author Matthew Skinner - Initial contribution
+ */
+@NonNullByDefault
+public class EspMilightHubBindingConstants {
+ public static final String STATES_BASE_TOPIC = "milight/states/";
+ public static final String COMMANDS_BASE_TOPIC = "milight/commands/";
+ public static final BigDecimal BIG_DECIMAL_100 = new BigDecimal(100);
+ // List of all Thing Type UIDs
+ public static final ThingTypeUID THING_TYPE_RGB_CCT = new ThingTypeUID(BINDING_ID, "rgb_cct");
+ public static final ThingTypeUID THING_TYPE_CCT = new ThingTypeUID(BINDING_ID, "cct");
+ public static final ThingTypeUID THING_TYPE_RGBW = new ThingTypeUID(BINDING_ID, "rgbw");
+ public static final ThingTypeUID THING_TYPE_RGB = new ThingTypeUID(BINDING_ID, "rgb");
+ public static final ThingTypeUID THING_TYPE_FUT089 = new ThingTypeUID(BINDING_ID, "fut089");
+ public static final ThingTypeUID THING_TYPE_FUT091 = new ThingTypeUID(BINDING_ID, "fut091");
+
+ public static final Set SUPPORTED_THING_TYPES = Set.of(THING_TYPE_RGBW, THING_TYPE_RGB_CCT,
+ THING_TYPE_FUT089, THING_TYPE_FUT091, THING_TYPE_CCT, THING_TYPE_RGB);
+
+ // Channels
+ public static final String CHANNEL_LEVEL = "level";
+ public static final String CHANNEL_COLOUR = "colour";
+ public static final String CHANNEL_COLOURTEMP = "colourTemperature";
+ public static final String CHANNEL_DISCO_MODE = "discoMode";
+ public static final String CHANNEL_BULB_MODE = "bulbMode";
+ public static final String CHANNEL_COMMAND = "command";
+}
diff --git a/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/java/org/openhab/binding/mqtt/espmilighthub/internal/EspMilightHubHandlerFactory.java b/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/java/org/openhab/binding/mqtt/espmilighthub/internal/EspMilightHubHandlerFactory.java
new file mode 100644
index 0000000000000..4817d0ae5127c
--- /dev/null
+++ b/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/java/org/openhab/binding/mqtt/espmilighthub/internal/EspMilightHubHandlerFactory.java
@@ -0,0 +1,60 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+package org.openhab.binding.mqtt.espmilighthub.internal;
+
+import static org.openhab.binding.mqtt.espmilighthub.internal.EspMilightHubBindingConstants.SUPPORTED_THING_TYPES;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.mqtt.espmilighthub.internal.handler.EspMilightHubHandler;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingRegistry;
+import org.openhab.core.thing.ThingTypeUID;
+import org.openhab.core.thing.binding.BaseThingHandlerFactory;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.openhab.core.thing.binding.ThingHandlerFactory;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+
+/**
+ * The {@link EspMilightHubHandlerFactory} is responsible for creating things and thing
+ * handlers.
+ *
+ * @author Matthew Skinner - Initial contribution
+ */
+@Component(service = ThingHandlerFactory.class)
+@NonNullByDefault
+public class EspMilightHubHandlerFactory extends BaseThingHandlerFactory {
+ private final ThingRegistry thingRegistry;
+
+ @Activate
+ public EspMilightHubHandlerFactory(final @Reference ThingRegistry thingRegistry) {
+ this.thingRegistry = thingRegistry;
+ }
+
+ @Override
+ public boolean supportsThingType(ThingTypeUID thingTypeUID) {
+ return SUPPORTED_THING_TYPES.contains(thingTypeUID);
+ }
+
+ @Override
+ protected @Nullable ThingHandler createHandler(Thing thing) {
+ ThingTypeUID thingTypeUID = thing.getThingTypeUID();
+ if (SUPPORTED_THING_TYPES.contains(thingTypeUID)) {
+ return new EspMilightHubHandler(thing, thingRegistry);
+ }
+ return null;
+ }
+}
diff --git a/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/java/org/openhab/binding/mqtt/espmilighthub/internal/Helper.java b/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/java/org/openhab/binding/mqtt/espmilighthub/internal/Helper.java
new file mode 100644
index 0000000000000..5663a52fbf675
--- /dev/null
+++ b/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/java/org/openhab/binding/mqtt/espmilighthub/internal/Helper.java
@@ -0,0 +1,65 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.mqtt.espmilighthub.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link Helper} Removes the need for any external JSON libs
+ *
+ * @author Matthew Skinner - Initial contribution
+ */
+@NonNullByDefault
+public class Helper {
+ /**
+ * resolveJSON will return a value from any key/path that you give and the string can be terminated by any ,}"
+ * characters.
+ *
+ */
+ public static String resolveJSON(String messageJSON, String jsonPath, int resultMaxLength) {
+ String result = "";
+ int index = 0;
+ index = messageJSON.indexOf(jsonPath);
+ if (index != -1) {
+ if ((index + jsonPath.length() + resultMaxLength) > messageJSON.length()) {
+ result = (messageJSON.substring(index + jsonPath.length(), messageJSON.length()));
+ } else {
+ result = (messageJSON.substring(index + jsonPath.length(),
+ index + jsonPath.length() + resultMaxLength));
+ }
+ index = result.indexOf(',');
+ if (index == -1) {
+ index = result.indexOf('"');
+ if (index == -1) {
+ index = result.indexOf('}');
+ if (index == -1) {
+ return result;
+ } else {
+ return result.substring(0, index);
+ }
+ } else {
+ return result.substring(0, index);
+ }
+ } else {
+ result = result.substring(0, index);
+ index = result.indexOf('"');
+ if (index == -1) {
+ return result;
+ } else {
+ return result.substring(0, index);
+ }
+ }
+ }
+ return "";
+ }
+}
diff --git a/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/java/org/openhab/binding/mqtt/espmilighthub/internal/discovery/EspMilightHubDiscoveryService.java b/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/java/org/openhab/binding/mqtt/espmilighthub/internal/discovery/EspMilightHubDiscoveryService.java
new file mode 100644
index 0000000000000..b855ef4c37d3c
--- /dev/null
+++ b/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/java/org/openhab/binding/mqtt/espmilighthub/internal/discovery/EspMilightHubDiscoveryService.java
@@ -0,0 +1,96 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+package org.openhab.binding.mqtt.espmilighthub.internal.discovery;
+
+import static org.openhab.binding.mqtt.MqttBindingConstants.BINDING_ID;
+import static org.openhab.binding.mqtt.espmilighthub.internal.EspMilightHubBindingConstants.*;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.mqtt.discovery.AbstractMQTTDiscovery;
+import org.openhab.binding.mqtt.discovery.MQTTTopicDiscoveryService;
+import org.openhab.core.config.discovery.DiscoveryResultBuilder;
+import org.openhab.core.config.discovery.DiscoveryService;
+import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
+import org.openhab.core.thing.ThingTypeUID;
+import org.openhab.core.thing.ThingUID;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+
+/**
+ * The {@link EspMilightHubDiscoveryService} is responsible for finding globes
+ * and setting them up for the handlers.
+ *
+ * @author Matthew Skinner - Initial contribution
+ */
+
+@Component(service = DiscoveryService.class, configurationPid = "discovery.mqttespmilighthub")
+@NonNullByDefault
+public class EspMilightHubDiscoveryService extends AbstractMQTTDiscovery {
+ protected final MQTTTopicDiscoveryService discoveryService;
+
+ @Activate
+ public EspMilightHubDiscoveryService(@Reference MQTTTopicDiscoveryService discoveryService) {
+ super(SUPPORTED_THING_TYPES, 3, true, STATES_BASE_TOPIC + "#");
+ this.discoveryService = discoveryService;
+ }
+
+ @Override
+ protected MQTTTopicDiscoveryService getDiscoveryService() {
+ return discoveryService;
+ }
+
+ @Override
+ public void receivedMessage(ThingUID connectionBridge, MqttBrokerConnection connection, String topic,
+ byte[] payload) {
+ resetTimeout();
+ if (topic.startsWith(STATES_BASE_TOPIC)) {
+ String cutTopic = topic.replace(STATES_BASE_TOPIC, "");
+ int index = cutTopic.indexOf("/");
+ if (index != -1) // -1 means "not found"
+ {
+ String remoteCode = (cutTopic.substring(0, index)); // Store the remote code for use later
+ cutTopic = topic.replace(STATES_BASE_TOPIC + remoteCode + "/", "");
+ index = cutTopic.indexOf("/");
+ if (index != -1) {
+ String globeType = (cutTopic.substring(0, index));
+ String remoteGroupID = (cutTopic.substring(index + 1, index + 2));
+ // openHAB's framework has better code for handling groups then the firmware does
+ if (!remoteGroupID.equals("0")) {// Users can manually add group 0 things if they wish
+ publishDevice(connectionBridge, connection, topic, remoteCode, globeType, remoteGroupID);
+ }
+ }
+ }
+ }
+ }
+
+ void publishDevice(ThingUID connectionBridge, MqttBrokerConnection connection, String topic, String remoteCode,
+ String globeType, String remoteGroupID) {
+ Map properties = new HashMap<>();
+ properties.put("deviceid", remoteCode + remoteGroupID);
+ properties.put("basetopic", STATES_BASE_TOPIC + remoteCode + "/" + globeType + "/" + remoteGroupID);
+ thingDiscovered(DiscoveryResultBuilder
+ .create(new ThingUID(new ThingTypeUID(BINDING_ID, globeType), connectionBridge,
+ remoteCode + remoteGroupID))
+ .withProperties(properties).withRepresentationProperty("deviceid").withBridge(connectionBridge)
+ .withLabel("Milight " + globeType).build());
+ }
+
+ @Override
+ public void topicVanished(ThingUID connectionBridge, MqttBrokerConnection connection, String topic) {
+ }
+}
diff --git a/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/java/org/openhab/binding/mqtt/espmilighthub/internal/handler/EspMilightHubHandler.java b/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/java/org/openhab/binding/mqtt/espmilighthub/internal/handler/EspMilightHubHandler.java
new file mode 100644
index 0000000000000..6182f79266451
--- /dev/null
+++ b/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/java/org/openhab/binding/mqtt/espmilighthub/internal/handler/EspMilightHubHandler.java
@@ -0,0 +1,387 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+package org.openhab.binding.mqtt.espmilighthub.internal.handler;
+
+import static org.openhab.binding.mqtt.MqttBindingConstants.BINDING_ID;
+import static org.openhab.binding.mqtt.espmilighthub.internal.EspMilightHubBindingConstants.*;
+
+import java.math.BigDecimal;
+import java.nio.charset.StandardCharsets;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.mqtt.espmilighthub.internal.ConfigOptions;
+import org.openhab.binding.mqtt.espmilighthub.internal.Helper;
+import org.openhab.binding.mqtt.handler.AbstractBrokerHandler;
+import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
+import org.openhab.core.io.transport.mqtt.MqttConnectionObserver;
+import org.openhab.core.io.transport.mqtt.MqttConnectionState;
+import org.openhab.core.io.transport.mqtt.MqttMessageSubscriber;
+import org.openhab.core.library.types.HSBType;
+import org.openhab.core.library.types.IncreaseDecreaseType;
+import org.openhab.core.library.types.OnOffType;
+import org.openhab.core.library.types.PercentType;
+import org.openhab.core.library.types.StringType;
+import org.openhab.core.thing.Bridge;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingRegistry;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
+import org.openhab.core.thing.ThingUID;
+import org.openhab.core.thing.binding.BaseThingHandler;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.openhab.core.types.Command;
+import org.openhab.core.types.RefreshType;
+import org.openhab.core.types.State;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link EspMilightHubHandler} is responsible for handling commands of the globes, which are then
+ * sent to one of the bridges to be sent out by MQTT.
+ *
+ * @author Matthew Skinner - Initial contribution
+ */
+@NonNullByDefault
+public class EspMilightHubHandler extends BaseThingHandler implements MqttConnectionObserver, MqttMessageSubscriber {
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+ private @Nullable MqttBrokerConnection connection;
+ private ThingRegistry thingRegistry;
+ private String globeType = "";
+ private String bulbMode = "";
+ private String remotesGroupID = "";
+ private String channelPrefix = "";
+ private String fullCommandTopic = "";
+ private String fullStatesTopic = "";
+ private BigDecimal maxColourTemp = BigDecimal.ZERO;
+ private BigDecimal minColourTemp = BigDecimal.ZERO;
+ private BigDecimal savedLevel = BIG_DECIMAL_100;
+ private ConfigOptions config = new ConfigOptions();
+
+ public EspMilightHubHandler(Thing thing, ThingRegistry thingRegistry) {
+ super(thing);
+ this.thingRegistry = thingRegistry;
+ }
+
+ void changeChannel(String channel, State state) {
+ updateState(new ChannelUID(channelPrefix + channel), state);
+ // Remote code of 0 means that all channels need to follow this change.
+ if (remotesGroupID.equals("0")) {
+ switch (globeType) {
+ // These two are 8 channel remotes
+ case "fut091":
+ case "fut089":
+ updateState(new ChannelUID(channelPrefix.substring(0, channelPrefix.length() - 2) + "5:" + channel),
+ state);
+ updateState(new ChannelUID(channelPrefix.substring(0, channelPrefix.length() - 2) + "6:" + channel),
+ state);
+ updateState(new ChannelUID(channelPrefix.substring(0, channelPrefix.length() - 2) + "7:" + channel),
+ state);
+ updateState(new ChannelUID(channelPrefix.substring(0, channelPrefix.length() - 2) + "8:" + channel),
+ state);
+ default:
+ updateState(new ChannelUID(channelPrefix.substring(0, channelPrefix.length() - 2) + "1:" + channel),
+ state);
+ updateState(new ChannelUID(channelPrefix.substring(0, channelPrefix.length() - 2) + "2:" + channel),
+ state);
+ updateState(new ChannelUID(channelPrefix.substring(0, channelPrefix.length() - 2) + "3:" + channel),
+ state);
+ updateState(new ChannelUID(channelPrefix.substring(0, channelPrefix.length() - 2) + "4:" + channel),
+ state);
+ }
+ }
+ }
+
+ private void processIncomingState(String messageJSON) {
+ // Need to handle State and Level at the same time to process level=0 as off//
+ BigDecimal tempBulbLevel = BigDecimal.ZERO;
+ String bulbState = Helper.resolveJSON(messageJSON, "\"state\":\"", 3);
+ String bulbLevel = Helper.resolveJSON(messageJSON, "\"level\":", 3);
+ if (!bulbLevel.isEmpty()) {
+ if (bulbLevel.equals("0") || bulbState.equals("OFF")) {
+ changeChannel(CHANNEL_LEVEL, OnOffType.OFF);
+ tempBulbLevel = BigDecimal.ZERO;
+ } else {
+ tempBulbLevel = new BigDecimal(bulbLevel);
+ changeChannel(CHANNEL_LEVEL, new PercentType(tempBulbLevel));
+ }
+ } else if (bulbState.equals("ON") || bulbState.equals("OFF")) { // NOTE: Level is missing when this runs
+ changeChannel(CHANNEL_LEVEL, OnOffType.valueOf(bulbState));
+ }
+ bulbMode = Helper.resolveJSON(messageJSON, "\"bulb_mode\":\"", 5);
+ switch (bulbMode) {
+ case "white":
+ if (!globeType.equals("cct") && !globeType.equals("fut091")) {
+ changeChannel(CHANNEL_BULB_MODE, new StringType("white"));
+ changeChannel(CHANNEL_COLOUR, new HSBType("0,0," + tempBulbLevel));
+ changeChannel(CHANNEL_DISCO_MODE, new StringType("None"));
+ }
+ String bulbCTemp = Helper.resolveJSON(messageJSON, "\"color_temp\":", 3);
+ if (!bulbCTemp.isEmpty()) {
+ int ibulbCTemp = (int) Math.round(((Float.valueOf(bulbCTemp) / 2.17) - 171) * -1);
+ changeChannel(CHANNEL_COLOURTEMP, new PercentType(ibulbCTemp));
+ }
+ break;
+ case "color":
+ changeChannel(CHANNEL_BULB_MODE, new StringType("color"));
+ changeChannel(CHANNEL_DISCO_MODE, new StringType("None"));
+ String bulbHue = Helper.resolveJSON(messageJSON, "\"hue\":", 3);
+ String bulbSaturation = Helper.resolveJSON(messageJSON, "\"saturation\":", 3);
+ if (bulbHue.isEmpty()) {
+ logger.warn("Milight MQTT message came in as being a colour mode, but was missing a HUE value.");
+ } else {
+ if (bulbSaturation.isEmpty()) {
+ bulbSaturation = "100";
+ }
+ changeChannel(CHANNEL_COLOUR, new HSBType(bulbHue + "," + bulbSaturation + "," + tempBulbLevel));
+ }
+ break;
+ case "scene":
+ if (!globeType.equals("cct") && !globeType.equals("fut091")) {
+ changeChannel(CHANNEL_BULB_MODE, new StringType("scene"));
+ }
+ String bulbDiscoMode = Helper.resolveJSON(messageJSON, "\"mode\":", 1);
+ if (!bulbDiscoMode.isEmpty()) {
+ changeChannel(CHANNEL_DISCO_MODE, new StringType(bulbDiscoMode));
+ }
+ break;
+ case "night":
+ if (!globeType.equals("cct") && !globeType.equals("fut091")) {
+ changeChannel(CHANNEL_BULB_MODE, new StringType("night"));
+ if (config.oneTriggersNightMode) {
+ changeChannel(CHANNEL_LEVEL, new PercentType("1"));
+ }
+ }
+ break;
+ }
+ }
+
+ /*
+ * Used to calculate the colour temp for a globe if you want the light to get warmer as it is dimmed like a
+ * traditional halogen globe
+ */
+ private int autoColourTemp(int brightness) {
+ return minColourTemp.subtract(
+ minColourTemp.subtract(maxColourTemp).divide(BIG_DECIMAL_100).multiply(new BigDecimal(brightness)))
+ .intValue();
+ }
+
+ void turnOff() {
+ if (config.powerFailsToMinimum) {
+ sendMQTT("{\"state\":\"OFF\",\"level\":0}");
+ } else {
+ sendMQTT("{\"state\":\"OFF\"}");
+ }
+ }
+
+ void handleLevelColour(Command command) {
+ if (command instanceof OnOffType) {
+ if (OnOffType.ON.equals(command)) {
+ sendMQTT("{\"state\":\"ON\",\"level\":" + savedLevel + "}");
+ return;
+ } else {
+ turnOff();
+ }
+ } else if (command instanceof IncreaseDecreaseType) {
+ if (IncreaseDecreaseType.INCREASE.equals(command)) {
+ if (savedLevel.intValue() <= 90) {
+ savedLevel = savedLevel.add(BigDecimal.TEN);
+ }
+ } else {
+ if (savedLevel.intValue() >= 10) {
+ savedLevel = savedLevel.subtract(BigDecimal.TEN);
+ }
+ }
+ sendMQTT("{\"state\":\"ON\",\"level\":" + savedLevel.intValue() + "}");
+ return;
+ } else if (command instanceof HSBType) {
+ HSBType hsb = (HSBType) command;
+ // This feature allows google home or Echo to trigger white mode when asked to turn color to white.
+ if (hsb.getHue().intValue() == config.whiteHue && hsb.getSaturation().intValue() == config.whiteSat) {
+ if ("rgb_cct".equals(globeType) || "fut089".equals(globeType)) {
+ sendMQTT("{\"state\":\"ON\",\"color_temp\":" + config.favouriteWhite + "}");
+ } else {// globe must only have 1 type of white
+ sendMQTT("{\"command\":\"set_white\"}");
+ }
+ return;
+ } else if (PercentType.ZERO.equals(hsb.getBrightness())) {
+ turnOff();
+ return;
+ } else if (config.whiteThreshold != -1 && hsb.getSaturation().intValue() <= config.whiteThreshold
+ && "rgbw".equals(globeType)) {
+ sendMQTT("{\"command\":\"set_white\"}");
+ return;
+ }
+ sendMQTT("{\"state\":\"ON\",\"level\":" + hsb.getBrightness().intValue() + ",\"hue\":"
+ + hsb.getHue().intValue() + ",\"saturation\":" + hsb.getSaturation().intValue() + "}");
+ savedLevel = hsb.getBrightness().toBigDecimal();
+ return;
+ } else if (command instanceof PercentType) {
+ PercentType percentType = (PercentType) command;
+ if (percentType.intValue() == 0) {
+ turnOff();
+ return;
+ } else if (percentType.intValue() == 1 && config.oneTriggersNightMode) {
+ sendMQTT("{\"command\":\"night_mode\"}");
+ return;
+ }
+ sendMQTT("{\"state\":\"ON\",\"level\":" + command + "}");
+ savedLevel = percentType.toBigDecimal();
+ if (globeType.equals("rgb_cct") || globeType.equals("fut089")) {
+ if (config.dimmedCT > 0 && bulbMode.equals("white")) {
+ sendMQTT("{\"state\":\"ON\",\"color_temp\":" + autoColourTemp(savedLevel.intValue()) + "}");
+ }
+ }
+ return;
+ }
+ }
+
+ @Override
+ public void handleCommand(ChannelUID channelUID, Command command) {
+ if (command instanceof RefreshType) {
+ return;
+ }
+ switch (channelUID.getId()) {
+ case CHANNEL_LEVEL:
+ handleLevelColour(command);
+ return;
+ case CHANNEL_BULB_MODE:
+ bulbMode = command.toString();
+ break;
+ case CHANNEL_COLOURTEMP:
+ int scaledCommand = (int) Math.round((370 - (2.17 * Float.valueOf(command.toString()))));
+ sendMQTT("{\"state\":\"ON\",\"level\":" + savedLevel + ",\"color_temp\":" + scaledCommand + "}");
+ break;
+ case CHANNEL_COMMAND:
+ sendMQTT("{\"command\":\"" + command + "\"}");
+ break;
+ case CHANNEL_DISCO_MODE:
+ sendMQTT("{\"mode\":\"" + command + "\"}");
+ break;
+ case CHANNEL_COLOUR:
+ handleLevelColour(command);
+ }
+ }
+
+ @Override
+ public void initialize() {
+ config = getConfigAs(ConfigOptions.class);
+ if (config.dimmedCT > 0) {
+ maxColourTemp = new BigDecimal(config.favouriteWhite);
+ minColourTemp = new BigDecimal(config.dimmedCT);
+ if (minColourTemp.intValue() <= maxColourTemp.intValue()) {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
+ "The dimmedCT config value must be greater than the favourite White value.");
+ return;
+ }
+ }
+ Bridge localBridge = getBridge();
+ if (localBridge == null) {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING,
+ "Globe must have a valid bridge selected before it can come online.");
+ return;
+ } else {
+ globeType = thing.getThingTypeUID().getId();// eg rgb_cct
+ String globeLocation = this.getThing().getUID().getId();// eg 0x014
+ remotesGroupID = globeLocation.substring(globeLocation.length() - 1, globeLocation.length());// eg 4
+ String remotesIDCode = globeLocation.substring(0, globeLocation.length() - 1);// eg 0x01
+ fullCommandTopic = COMMANDS_BASE_TOPIC + remotesIDCode + "/" + globeType + "/" + remotesGroupID;
+ fullStatesTopic = STATES_BASE_TOPIC + remotesIDCode + "/" + globeType + "/" + remotesGroupID;
+ // Need to remove the lowercase x from 0x12AB in case it contains all numbers
+ String caseCheck = globeLocation.substring(2, globeLocation.length() - 1);
+ if (!caseCheck.equals(caseCheck.toUpperCase())) {
+ logger.warn(
+ "The milight globe {}{} is using lowercase for the remote code when the hub needs UPPERCASE",
+ remotesIDCode, remotesGroupID);
+ }
+ channelPrefix = BINDING_ID + ":" + globeType + ":" + localBridge.getUID().getId() + ":" + remotesIDCode
+ + remotesGroupID + ":";
+ connectMQTT();
+ }
+ }
+
+ private void sendMQTT(String payload) {
+ MqttBrokerConnection localConnection = connection;
+ if (localConnection != null) {
+ localConnection.publish(fullCommandTopic, payload.getBytes(), 1, false);
+ }
+ }
+
+ @Override
+ public void processMessage(String topic, byte[] payload) {
+ String state = new String(payload, StandardCharsets.UTF_8);
+ logger.trace("Recieved the following new Milight state:{}:{}", topic, state);
+ processIncomingState(state);
+ }
+
+ @Override
+ public void connectionStateChanged(MqttConnectionState state, @Nullable Throwable error) {
+ logger.debug("MQTT brokers state changed to:{}", state);
+ switch (state) {
+ case CONNECTED:
+ updateStatus(ThingStatus.ONLINE);
+ break;
+ case CONNECTING:
+ case DISCONNECTED:
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
+ "Bridge (broker) is not connected to your MQTT broker.");
+ }
+ }
+
+ public void connectMQTT() {
+ Bridge localBridge = this.getBridge();
+ if (localBridge == null) {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED,
+ "Bridge is missing or offline, you need to setup a working MQTT broker first.");
+ return;
+ }
+ ThingUID thingUID = localBridge.getBridgeUID();
+ if (thingUID == null) {
+ return;
+ }
+ Thing thing = thingRegistry.get(thingUID);
+ if (thing == null) {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED,
+ "Bridge is missing or offline, you need to setup a working MQTT broker first.");
+ return;
+ }
+ ThingHandler handler = thing.getHandler();
+ if (handler instanceof AbstractBrokerHandler) {
+ AbstractBrokerHandler abh = (AbstractBrokerHandler) handler;
+ MqttBrokerConnection localConnection = abh.getConnection();
+ if (localConnection != null) {
+ localConnection.setKeepAliveInterval(20);
+ localConnection.setQos(1);
+ localConnection.setUnsubscribeOnStop(true);
+ localConnection.addConnectionObserver(this);
+ localConnection.start();
+ localConnection.subscribe(fullStatesTopic + "/#", this);
+ connection = localConnection;
+ if (localConnection.connectionState().compareTo(MqttConnectionState.CONNECTED) == 0) {
+ updateStatus(ThingStatus.ONLINE);
+ }
+ }
+ }
+ return;
+ }
+
+ @Override
+ public void dispose() {
+ MqttBrokerConnection localConnection = connection;
+ if (localConnection != null) {
+ localConnection.unsubscribe(fullStatesTopic + "/#", this);
+ }
+ }
+}
diff --git a/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/resources/OH-INF/config/config.xml b/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/resources/OH-INF/config/config.xml
new file mode 100644
index 0000000000000..69be374fb168b
--- /dev/null
+++ b/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/resources/OH-INF/config/config.xml
@@ -0,0 +1,111 @@
+
+
+
+
+
+
+ 1% on a slider will trigger the Night Mode.
+ false
+
+
+
+
+ If lights loose power when soft off, the lights will default back to the minimum brightness.
+ false
+
+
+
+
+
+
+ Traditional globes grow warmer the more they are dimmed. Set this to 370, or leave blank to disable.
+
+
+
+
+
+ 1% on a slider will trigger the Night Mode.
+ false
+
+
+
+
+
+
+ When both the whiteHue and whiteSat values are seen by the binding it will trigger the white LEDS.
+
+ 35
+
+
+
+
+ When both the whiteHue and whiteSat values are seen by the binding it will trigger the white LEDS.
+
+ 32
+
+
+
+
+ When a shortcut triggers white mode, use this for the colour white.
+ 200
+
+
+
+
+ Traditional globes grow warmer the more they are dimmed. Set this to 370, or leave blank to disable.
+
+
+
+
+
+ 1% on a slider will trigger the Night Mode.
+ false
+
+
+
+
+ If lights loose power, the lights will turn on to the minimum brightness.
+ true
+
+
+
+
+
+
+ When both the whiteHue and whiteSat values are seen by the binding it will trigger the white LEDS.
+
+ 35
+
+
+
+
+ When both the whiteHue and whiteSat values are seen by the binding it will trigger the white LEDS.
+
+ 32
+
+
+
+
+ 1% on a slider will trigger the Night Mode.
+ false
+
+
+
+
+ If lights loose power, the lights will turn on to the minimum brightness.
+ false
+
+
+
+
+ RGBW saturation changes, will trigger the white mode. -1 will disable this feature.
+
+ 12
+
+
+
+
+
diff --git a/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/resources/OH-INF/thing/thing-types.xml
new file mode 100644
index 0000000000000..a7dd8a991170f
--- /dev/null
+++ b/bundles/org.openhab.binding.mqtt.espmilighthub/src/main/resources/OH-INF/thing/thing-types.xml
@@ -0,0 +1,190 @@
+
+
+
+
+
+
+
+
+
+ Led globe with full Colour, and both cool and warm whites.
+ Lightbulb
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Use this when your remote is the newer 8 group type called FUT089 and your globes are rgb_cct
+ Lightbulb
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Use this when your remote is the newer fut091 and your globes are cct
+ Lightbulb
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Led globe with both cool and warm white controls
+ Lightbulb
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ RGB Globe with a fixed white
+ Lightbulb
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ RGB Globe with no white
+ Lightbulb
+
+
+
+
+
+
+
+
+
+
+
+ Dimmer
+
+ Level changes the brightness of the globe.
+ Slider
+
+
+
+ Dimmer
+
+ Change from cool to warm white with this control.
+ Slider
+
+
+
+ Color
+
+ Allows you to change the colour, brightness and saturation of the globe.
+ ColorLight
+
+ Lighting
+
+
+
+
+ String
+
+ Send a raw command to the globe/s.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ String
+
+ Displays the mode the bulb is currently in.
+
+
+
+
+
+
+
+
+
+
+
+ String
+
+ Switch to a Disco mode directly.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bundles/org.openhab.binding.mqtt/src/main/java/org/openhab/binding/mqtt/MqttBindingConstants.java b/bundles/org.openhab.binding.mqtt/src/main/java/org/openhab/binding/mqtt/MqttBindingConstants.java
index 0ad0928f1b108..08d2579ac85db 100644
--- a/bundles/org.openhab.binding.mqtt/src/main/java/org/openhab/binding/mqtt/MqttBindingConstants.java
+++ b/bundles/org.openhab.binding.mqtt/src/main/java/org/openhab/binding/mqtt/MqttBindingConstants.java
@@ -23,7 +23,7 @@
*/
@NonNullByDefault
public class MqttBindingConstants {
- private static final String BINDING_ID = "mqtt";
+ public static final String BINDING_ID = "mqtt";
// List of all Thing Type UIDs
public static final ThingTypeUID BRIDGE_TYPE_SYSTEMBROKER = new ThingTypeUID(BINDING_ID, "systemBroker");
diff --git a/bundles/pom.xml b/bundles/pom.xml
index 79efb9c54e682..8915314b72115 100644
--- a/bundles/pom.xml
+++ b/bundles/pom.xml
@@ -195,6 +195,7 @@
org.openhab.binding.monopriceaudioorg.openhab.binding.mpdorg.openhab.binding.mqtt
+ org.openhab.binding.mqtt.espmilighthuborg.openhab.binding.mqtt.genericorg.openhab.binding.mqtt.homeassistantorg.openhab.binding.mqtt.homie
diff --git a/features/openhab-addons/src/main/resources/footer.xml b/features/openhab-addons/src/main/resources/footer.xml
index 0385fa97249df..291d0536502a1 100644
--- a/features/openhab-addons/src/main/resources/footer.xml
+++ b/features/openhab-addons/src/main/resources/footer.xml
@@ -20,6 +20,7 @@
openhab-runtime-baseopenhab-transport-mqttmvn:org.openhab.addons.bundles/org.openhab.binding.mqtt/${project.version}
+ mvn:org.openhab.addons.bundles/org.openhab.binding.mqtt.espmilighthub/${project.version}mvn:org.openhab.addons.bundles/org.openhab.binding.mqtt.generic/${project.version}mvn:org.openhab.addons.bundles/org.openhab.binding.mqtt.homeassistant/${project.version}mvn:org.openhab.addons.bundles/org.openhab.binding.mqtt.homie/${project.version}