mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[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:
parent
80c52f4fc0
commit
b2bb9176e5
@ -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
|
||||
|
@ -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>
|
||||
|
13
bundles/org.openhab.binding.mqtt.espmilighthub/NOTICE
Normal file
13
bundles/org.openhab.binding.mqtt.espmilighthub/NOTICE
Normal 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
|
193
bundles/org.openhab.binding.mqtt.espmilighthub/README.md
Normal file
193
bundles/org.openhab.binding.mqtt.espmilighthub/README.md
Normal 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' ]
|
||||
}
|
||||
```
|
24
bundles/org.openhab.binding.mqtt.espmilighthub/pom.xml
Normal file
24
bundles/org.openhab.binding.mqtt.espmilighthub/pom.xml
Normal 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>
|
@ -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>
|
@ -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;
|
||||
}
|
@ -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";
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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 "";
|
||||
}
|
||||
}
|
@ -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) {
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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>
|
@ -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>
|
@ -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");
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user