[espmilighthub] Initial contribution (#9218)

* espmilighthub inital

Signed-off-by: Matthew Skinner <matt@pcmus.com>

Co-authored-by: Fabian Wolter <github@fabian-wolter.de>
Co-authored-by: Connor Petty <mistercpp2000+gitsignoff@gmail.com>
This commit is contained in:
Matthew Skinner 2021-01-30 07:05:55 +11:00 committed by GitHub
parent 80c52f4fc0
commit b2bb9176e5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 1244 additions and 1 deletions

View File

@ -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

View File

@ -806,6 +806,11 @@
<artifactId>org.openhab.binding.mqtt</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.mqtt.espmilighthub</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.mqtt.generic</artifactId>

View File

@ -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

View File

@ -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: <http://blog.christophermullins.com/2017/02/11/milight-wifi-gateway-emulator-on-an-esp8266>
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 <https://github.com/sidoh/esp8266_milight_hub/releases>
+ Download esp8266flasher if you are on windows <https://github.com/nodemcu/nodemcu-flasher>
+ 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' ]
}
```

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>3.1.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.mqtt.espmilighthub</artifactId>
<name>openHAB Add-ons :: Bundles :: MQTT EspMilightHub</name>
<dependencies>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.mqtt</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.mqtt.espmilighthub-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
<feature name="openhab-binding-mqtt-espmilighthub" description="MQTT Binding EspMilightHub" version="${project.version}">
<feature>openhab-runtime-base</feature>
<feature>openhab-transport-mqtt</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.mqtt/${project.version}</bundle>
<bundle start-level="81">mvn:org.openhab.addons.bundles/org.openhab.binding.mqtt.espmilighthub/${project.version}</bundle>
</feature>
</features>

View File

@ -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;
}

View File

@ -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<ThingTypeUID> 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";
}

View File

@ -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;
}
}

View File

@ -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 "";
}
}

View File

@ -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<String, Object> 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) {
}
}

View File

@ -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);
}
}
}

View File

@ -0,0 +1,111 @@
<?xml version="1.0" encoding="UTF-8"?>
<config-description:config-descriptions
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0 https://openhab.org/schemas/config-description-1.0.0.xsd">
<config-description uri="thing-type:mqtt:rgb">
<parameter name="oneTriggersNightMode" type="boolean" required="true">
<label>1% Triggers Night Mode</label>
<description>1% on a slider will trigger the Night Mode.</description>
<default>false</default>
</parameter>
<parameter name="powerFailsToMinimum" type="boolean" required="true">
<label>Dimmed on Power Fail</label>
<description>If lights loose power when soft off, the lights will default back to the minimum brightness.</description>
<default>false</default>
</parameter>
</config-description>
<config-description uri="thing-type:mqtt:cct">
<parameter name="dimmedCT" type="integer" required="false" min="153" max="370">
<label>Dimmed Colour Temp</label>
<description>Traditional globes grow warmer the more they are dimmed. Set this to 370, or leave blank to disable.
</description>
</parameter>
<parameter name="oneTriggersNightMode" type="boolean" required="true">
<label>1% Triggers Night Mode</label>
<description>1% on a slider will trigger the Night Mode.</description>
<default>false</default>
</parameter>
</config-description>
<config-description uri="thing-type:mqtt:rgbandcct">
<parameter name="whiteHue" type="integer" required="true" min="-1" max="360">
<label>White Hue</label>
<description>When both the whiteHue and whiteSat values are seen by the binding it will trigger the white LEDS.
</description>
<default>35</default>
</parameter>
<parameter name="whiteSat" type="integer" required="true" min="-1" max="100">
<label>White Saturation</label>
<description>When both the whiteHue and whiteSat values are seen by the binding it will trigger the white LEDS.
</description>
<default>32</default>
</parameter>
<parameter name="favouriteWhite" type="integer" required="true" min="153" max="370">
<label>Favourite White</label>
<description>When a shortcut triggers white mode, use this for the colour white.</description>
<default>200</default>
</parameter>
<parameter name="dimmedCT" type="integer" required="false" min="153" max="370">
<label>Dimmed Colour Temp</label>
<description>Traditional globes grow warmer the more they are dimmed. Set this to 370, or leave blank to disable.
</description>
</parameter>
<parameter name="oneTriggersNightMode" type="boolean" required="true">
<label>1% Triggers Night Mode</label>
<description>1% on a slider will trigger the Night Mode.</description>
<default>false</default>
</parameter>
<parameter name="powerFailsToMinimum" type="boolean" required="true">
<label>Dimmed on Power Fail</label>
<description>If lights loose power, the lights will turn on to the minimum brightness.</description>
<default>true</default>
</parameter>
</config-description>
<config-description uri="thing-type:mqtt:rgbw">
<parameter name="whiteHue" type="integer" required="true" min="-1" max="360">
<label>White Hue</label>
<description>When both the whiteHue and whiteSat values are seen by the binding it will trigger the white LEDS.
</description>
<default>35</default>
</parameter>
<parameter name="whiteSat" type="integer" required="true" min="-1" max="100">
<label>White Saturation</label>
<description>When both the whiteHue and whiteSat values are seen by the binding it will trigger the white LEDS.
</description>
<default>32</default>
</parameter>
<parameter name="oneTriggersNightMode" type="boolean" required="true">
<label>1% Triggers Night Mode</label>
<description>1% on a slider will trigger the Night Mode.</description>
<default>false</default>
</parameter>
<parameter name="powerFailsToMinimum" type="boolean" required="true">
<label>Dimmed on Power Fail</label>
<description>If lights loose power, the lights will turn on to the minimum brightness.</description>
<default>false</default>
</parameter>
<parameter name="whiteThreshold" type="integer" required="true" min="-1" max="99">
<label>White Threshold</label>
<description>RGBW saturation changes, will trigger the white mode. -1 will disable this feature.
</description>
<default>12</default>
</parameter>
</config-description>
</config-description:config-descriptions>

View File

@ -0,0 +1,190 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="mqtt"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<thing-type id="rgb_cct">
<supported-bridge-type-refs>
<bridge-type-ref id="broker"/>
<bridge-type-ref id="systemBroker"/>
</supported-bridge-type-refs>
<label>Milight RGBCCT</label>
<description>Led globe with full Colour, and both cool and warm whites.</description>
<category>Lightbulb</category>
<channels>
<channel id="level" typeId="level"/>
<channel id="colourTemperature" typeId="colourTemperature"/>
<channel id="colour" typeId="colour"/>
<channel id="discoMode" typeId="discoMode"/>
<channel id="bulbMode" typeId="bulbMode"/>
<channel id="command" typeId="command"/>
</channels>
<config-description-ref uri="thing-type:mqtt:rgbandcct"/>
</thing-type>
<thing-type id="fut089">
<supported-bridge-type-refs>
<bridge-type-ref id="broker"/>
<bridge-type-ref id="systemBroker"/>
</supported-bridge-type-refs>
<label>Milight FUT089</label>
<description>Use this when your remote is the newer 8 group type called FUT089 and your globes are rgb_cct</description>
<category>Lightbulb</category>
<channels>
<channel id="level" typeId="level"/>
<channel id="colourTemperature" typeId="colourTemperature"/>
<channel id="colour" typeId="colour"/>
<channel id="discoMode" typeId="discoMode"/>
<channel id="bulbMode" typeId="bulbMode"/>
<channel id="command" typeId="command"/>
</channels>
<config-description-ref uri="thing-type:mqtt:rgbandcct"/>
</thing-type>
<thing-type id="fut091">
<supported-bridge-type-refs>
<bridge-type-ref id="broker"/>
<bridge-type-ref id="systemBroker"/>
</supported-bridge-type-refs>
<label>Milight FUT091</label>
<description>Use this when your remote is the newer fut091 and your globes are cct</description>
<category>Lightbulb</category>
<channels>
<channel id="level" typeId="level"/>
<channel id="colourTemperature" typeId="colourTemperature"/>
<channel id="command" typeId="command"/>
</channels>
<config-description-ref uri="thing-type:mqtt:cct"/>
</thing-type>
<thing-type id="cct">
<supported-bridge-type-refs>
<bridge-type-ref id="broker"/>
<bridge-type-ref id="systemBroker"/>
</supported-bridge-type-refs>
<label>Milight CCT</label>
<description>Led globe with both cool and warm white controls</description>
<category>Lightbulb</category>
<channels>
<channel id="level" typeId="level"/>
<channel id="colourTemperature" typeId="colourTemperature"/>
<channel id="command" typeId="command"/>
</channels>
<config-description-ref uri="thing-type:mqtt:cct"/>
</thing-type>
<thing-type id="rgbw">
<supported-bridge-type-refs>
<bridge-type-ref id="broker"/>
<bridge-type-ref id="systemBroker"/>
</supported-bridge-type-refs>
<label>Milight RGBW</label>
<description>RGB Globe with a fixed white</description>
<category>Lightbulb</category>
<channels>
<channel id="level" typeId="level"/>
<channel id="colour" typeId="colour"/>
<channel id="discoMode" typeId="discoMode"/>
<channel id="bulbMode" typeId="bulbMode"/>
<channel id="command" typeId="command"/>
</channels>
<config-description-ref uri="thing-type:mqtt:rgbw"/>
</thing-type>
<thing-type id="rgb">
<supported-bridge-type-refs>
<bridge-type-ref id="broker"/>
<bridge-type-ref id="systemBroker"/>
</supported-bridge-type-refs>
<label>Milight RGB</label>
<description>RGB Globe with no white</description>
<category>Lightbulb</category>
<channels>
<channel id="level" typeId="level"/>
<channel id="colour" typeId="colour"/>
<channel id="discoMode" typeId="discoMode"/>
<channel id="bulbMode" typeId="bulbMode"/>
<channel id="command" typeId="command"/>
</channels>
<config-description-ref uri="thing-type:mqtt:rgb"/>
</thing-type>
<channel-type id="level">
<item-type>Dimmer</item-type>
<label>Level</label>
<description>Level changes the brightness of the globe.</description>
<category>Slider</category>
</channel-type>
<channel-type id="colourTemperature">
<item-type>Dimmer</item-type>
<label>Colour Temperature</label>
<description>Change from cool to warm white with this control.</description>
<category>Slider</category>
</channel-type>
<channel-type id="colour">
<item-type>Color</item-type>
<label>Colour</label>
<description>Allows you to change the colour, brightness and saturation of the globe.</description>
<category>ColorLight</category>
<tags>
<tag>Lighting</tag>
</tags>
</channel-type>
<channel-type id="command" advanced="true">
<item-type>String</item-type>
<label>Command</label>
<description>Send a raw command to the globe/s.</description>
<state>
<options>
<option value="next_mode">Next Mode</option>
<option value="previous_mode">Previous Mode</option>
<option value="mode_speed_up">Mode Speed Up</option>
<option value="mode_speed_down">Mode Speed Down</option>
<option value="set_white">Set White</option>
<option value="level_down">Level Down</option>
<option value="level_up">Level Up</option>
<option value="temperature_down">Temperature Down</option>
<option value="temperature_up">Temperature Up</option>
<option value="night_mode">Night Mode</option>
</options>
</state>
</channel-type>
<channel-type id="bulbMode" advanced="true">
<item-type>String</item-type>
<label>Bulb Mode</label>
<description>Displays the mode the bulb is currently in.</description>
<state readOnly="true">
<options>
<option value="white">white</option>
<option value="color">color</option>
<option value="scene">scene</option>
<option value="night">night</option>
</options>
</state>
</channel-type>
<channel-type id="discoMode" advanced="true">
<item-type>String</item-type>
<label>Disco Mode</label>
<description>Switch to a Disco mode directly.</description>
<state>
<options>
<option value="0">Disco 0</option>
<option value="1">Disco 1</option>
<option value="2">Disco 2</option>
<option value="3">Disco 3</option>
<option value="4">Disco 4</option>
<option value="5">Disco 5</option>
<option value="6">Disco 6</option>
<option value="7">Disco 7</option>
<option value="8">Disco 8</option>
</options>
</state>
</channel-type>
</thing:thing-descriptions>

View File

@ -23,7 +23,7 @@ import org.openhab.core.thing.ThingTypeUID;
*/
@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");

View File

@ -195,6 +195,7 @@
<module>org.openhab.binding.monopriceaudio</module>
<module>org.openhab.binding.mpd</module>
<module>org.openhab.binding.mqtt</module>
<module>org.openhab.binding.mqtt.espmilighthub</module>
<module>org.openhab.binding.mqtt.generic</module>
<module>org.openhab.binding.mqtt.homeassistant</module>
<module>org.openhab.binding.mqtt.homie</module>

View File

@ -20,6 +20,7 @@
<feature>openhab-runtime-base</feature>
<feature>openhab-transport-mqtt</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.mqtt/${project.version}</bundle>
<bundle start-level="81">mvn:org.openhab.addons.bundles/org.openhab.binding.mqtt.espmilighthub/${project.version}</bundle>
<bundle start-level="81">mvn:org.openhab.addons.bundles/org.openhab.binding.mqtt.generic/${project.version}</bundle>
<bundle start-level="82">mvn:org.openhab.addons.bundles/org.openhab.binding.mqtt.homeassistant/${project.version}</bundle>
<bundle start-level="82">mvn:org.openhab.addons.bundles/org.openhab.binding.mqtt.homie/${project.version}</bundle>