Skip to content

Commit

Permalink
[hue] Add support for groups/rooms (openhab#7476)
Browse files Browse the repository at this point in the history
Fix openhab#7419

Signed-off-by: Laurent Garnier <[email protected]>
  • Loading branch information
lolodomo authored and andrewfg committed Aug 31, 2020
1 parent c2f46a0 commit d060e0c
Show file tree
Hide file tree
Showing 15 changed files with 905 additions and 46 deletions.
57 changes: 46 additions & 11 deletions bundles/org.openhab.binding.hue/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,13 @@ hue:0210:00178810d0dc:1

The thing type is the second string behind the first colon and in this example it is **0210**.

Finally, the Hue binding also supports the groups of lights and rooms set up on the Hue bridge.

## Discovery

The Hue bridge is discovered through UPnP in the local network.
Once it is added as a Thing, its authentication button (in the middle) needs to be pressed in order to authorize the binding to access it.
Once the binding is authorized, it automatically reads all devices that are set up on the Hue bridge and puts them into the Inbox.
Once the binding is authorized, it automatically reads all devices and groups that are set up on the Hue bridge and puts them into the Inbox.

## Thing Configuration

Expand Down Expand Up @@ -138,16 +140,36 @@ The following device types also have an optional configuration value to specify
| fadetime | Fade time in Milliseconds to a new state (min="0", step="100", default="400") |


### Groups

The groups are identified by the number that the Hue bridge assigns to them.
Thus, all it needs for manual configuration is this single value like

```
group kitchen-bulbs "Kitchen Lamps" @ "Kitchen" [ groupId="1" ]
```

You can freely choose the thing identifier (such as kitchen-bulbs), its name (such as "Kitchen Lamps") and the location (such as "Kitchen").
The name will then be used e.g. by Paper UI to show the item.

The group type also have an optional configuration value to specify the fade time in milliseconds for the transition to a new state.

| Parameter | Description |
|-----------|-------------------------------------------------------------------------------|
| groupId | Number of the group provided by the Hue bridge. **Mandatory** |
| fadetime | Fade time in Milliseconds to a new state (min="0", step="100", default="400") |


## Channels

The devices support some of the following channels:

| Channel Type ID | Item Type | Description | Thing types supporting this channel |
|-------------------|--------------------|-----------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------|
| switch | Switch | This channel supports switching the device on and off. | 0000, 0010 |
| color | Color | This channel supports full color control with hue, saturation and brightness values. | 0200, 0210 |
| brightness | Dimmer | This channel supports adjusting the brightness value. Note that this is not available, if the color channel is supported. | 0100, 0110, 0220 |
| color_temperature | Dimmer | This channel supports adjusting the color temperature from cold (0%) to warm (100%). | 0210, 0220 |
| switch | Switch | This channel supports switching the device on and off. | 0000, 0010, group |
| color | Color | This channel supports full color control with hue, saturation and brightness values. | 0200, 0210, group |
| brightness | Dimmer | This channel supports adjusting the brightness value. Note that this is not available, if the color channel is supported. | 0100, 0110, 0220, group |
| color_temperature | Dimmer | This channel supports adjusting the color temperature from cold (0%) to warm (100%). | 0210, 0220, group |
| alert | String | This channel supports displaying alerts by flashing the bulb either once or multiple times. Valid values are: NONE, SELECT and LSELECT. | 0000, 0100, 0200, 0210, 0220 |
| effect | Switch | This channel supports color looping. | 0200, 0210, 0220 |
| dimmer_switch | Number | This channel shows which button was last pressed on the dimmer switch. | 0820 |
Expand Down Expand Up @@ -236,12 +258,13 @@ And there is one Hue Motion Sensor (represented by three devices) and a Hue Dimm

```
Bridge hue:bridge:1 "Hue Bridge" [ ipAddress="192.168.0.64" ] {
0210 bulb1 "Lamp 1" @ "Kitchen" [ lightId="1" ]
0220 bulb2 "Lamp 2" @ "Kitchen" [ lightId="2" ]
0106 light-level-sensor "Light-Sensor" @ "Entrance" [ sensorId="3" ]
0107 motion-sensor "Motion-Sensor" @ "Entrance" [ sensorId="4" ]
0302 temperature-sensor "Temp-Sensor" @ "Entrance" [ sensorId="5" ]
0820 dimmer-switch "Dimmer-Switch" @ "Entrance" [ sensorId="6" ]
0210 bulb1 "Lamp 1" @ "Kitchen" [ lightId="1" ]
0220 bulb2 "Lamp 2" @ "Kitchen" [ lightId="2" ]
group kitchen-bulbs "Kitchen Lamps" @ "Kitchen" [ groupId="1" ]
0106 light-level-sensor "Light-Sensor" @ "Entrance" [ sensorId="3" ]
0107 motion-sensor "Motion-Sensor" @ "Entrance" [ sensorId="4" ]
0302 temperature-sensor "Temp-Sensor" @ "Entrance" [ sensorId="5" ]
0820 dimmer-switch "Dimmer-Switch" @ "Entrance" [ sensorId="6" ]
}
```

Expand All @@ -261,6 +284,12 @@ Switch Light2_Toggle { channel="hue:0220:1:bulb2:brightness" }
Dimmer Light2_Dimmer { channel="hue:0220:1:bulb2:brightness" }
Dimmer Light2_ColorTemp { channel="hue:0220:1:bulb2:color_temperature" }
// Kitchen
Switch Kitchen_Switch { channel="hue:group:1:kitchen-bulbs:switch" }
Dimmer Kitchen_Dimmer { channel="hue:group:1:kitchen-bulbs:brightness" }
Color Kitchen_Color { channel="hue:group:1:kitchen-bulbs:color" }
Dimmer Kitchen_ColorTemp { channel="hue:group:1:kitchen-bulbs:color_temperature" }
// Light Level Sensor
Number:Illuminance LightLevelSensorIlluminance { channel="hue:0106:1:light-level-sensor:illuminance" }
Expand Down Expand Up @@ -296,6 +325,12 @@ sitemap demo label="Main Menu"
Slider item= Light2_Dimmer
Slider item= Light2_ColorTemp
// Kitchen
Switch item= Kitchen_Switch
Slider item= Kitchen_Dimmer
Colorpicker item= Kitchen_Color
Slider item= Kitchen_ColorTemp
// Motion Sensor
Switch item=MotionSensorPresence
Text item=MotionSensorLastUpdate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,26 @@
*/
package org.openhab.binding.hue.internal;

import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;

import com.google.gson.reflect.TypeToken;

/**
* Detailed group information.
*
* @author Q42 - Initial contribution
* @author Denis Dudnik - moved Jue library source code inside the smarthome Hue binding
* @author Laurent Garnier - field state added
*/
public class FullGroup extends Group {
public static final Type GSON_TYPE = new TypeToken<Map<String, FullGroup>>() {
}.getType();

private State action;
private List<String> lights;
private State state; // Will not be set by hue API

FullGroup() {
}
Expand All @@ -42,7 +51,16 @@ public State getAction() {
*
* @return lights in the group
*/
public List<HueObject> getLights() {
return Util.idsToLights(lights);
public List<String> getLights() {
return lights;
}

/**
* Returns the current state of the group.
*
* @return current state
*/
public State getState() {
return state;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,22 @@
*/
package org.openhab.binding.hue.internal;

import java.lang.reflect.Type;
import java.util.Map;

import com.google.gson.reflect.TypeToken;

/**
* Basic group information.
*
* @author Q42 - Initial contribution
* @author Denis Dudnik - moved Jue library source code inside the smarthome Hue binding
* @author Laurent Garnier - field type added
*/
public class Group {
public static final Type GSON_TYPE = new TypeToken<Map<String, Group>>() {
}.getType();

private String id;
private String name;
private String type;

Group() {
this.id = "0";
this.name = "Lightset 0";
this.type = "LightGroup";
}

void setName(String name) {
Expand All @@ -43,6 +38,10 @@ void setId(String id) {
this.id = id;
}

void setType(String type) {
this.type = type;
}

/**
* Returns if the group can be modified.
* Currently only returns false for the all lights pseudo group.
Expand Down Expand Up @@ -70,4 +69,13 @@ public String getId() {
public String getName() {
return name;
}

/**
* Returns the tyoe of the group.
*
* @return type
*/
public String getType() {
return type;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
*/
package org.openhab.binding.hue.internal;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.smarthome.core.thing.ThingTypeUID;

/**
Expand All @@ -25,6 +26,7 @@
* @author Samuel Leisering - Added support for sensor API
* @author Christoph Weitkamp - Added support for sensor API
*/
@NonNullByDefault
public class HueBindingConstants {

public static final String BINDING_ID = "hue";
Expand All @@ -51,6 +53,8 @@ public class HueBindingConstants {
public static final ThingTypeUID THING_TYPE_TEMPERATURE_SENSOR = new ThingTypeUID(BINDING_ID, "0302");
public static final ThingTypeUID THING_TYPE_LIGHT_LEVEL_SENSOR = new ThingTypeUID(BINDING_ID, "0106");

public static final ThingTypeUID THING_TYPE_GROUP = new ThingTypeUID(BINDING_ID, "group");

// List all channels
public static final String CHANNEL_COLORTEMPERATURE = "color_temperature";
public static final String CHANNEL_COLOR = "color";
Expand Down Expand Up @@ -88,6 +92,7 @@ public class HueBindingConstants {
public static final String PRODUCT_NAME = "productName";
public static final String UNIQUE_ID = "uniqueId";
public static final String FADETIME = "fadetime";
public static final String GROUP_ID = "groupId";

public static final String NORMALIZE_ID_REGEX = "[^a-zA-Z0-9_]";
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
* @author Andre Fuechsel - search for lights with given serial number added
* @author Denis Dudnik - moved Jue library source code inside the smarthome Hue binding, minor code cleanup
* @author Samuel Leisering - added cached config and API-Version
* @author Laurent Garnier - change the return type of getGroups
*/
@NonNullByDefault
public class HueBridge {
Expand Down Expand Up @@ -415,20 +416,23 @@ public Group getAllGroup() {
* @return list of groups
* @throws UnauthorizedException thrown if the user no longer exists
*/
public List<Group> getGroups() throws IOException, ApiException {
public List<FullGroup> getGroups() throws IOException, ApiException {
requireAuthentication();

Result result = http.get(getRelativeURL("groups"));

handleErrors(result);

Map<String, Group> groupMap = safeFromJson(result.getBody(), Group.GSON_TYPE);
ArrayList<Group> groupList = new ArrayList<>();
Map<String, FullGroup> groupMap = safeFromJson(result.getBody(), FullGroup.GSON_TYPE);
ArrayList<FullGroup> groupList = new ArrayList<>();

groupList.add(new Group());
if (groupMap.get("0") == null) {
// Group 0 is not returned, we create it as in fact it exists
groupList.add(getGroup(new Group()));
}

for (String id : groupMap.keySet()) {
Group group = groupMap.get(id);
FullGroup group = groupMap.get(id);
group.setId(id);
groupList.add(group);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory;
import org.openhab.binding.hue.internal.discovery.HueLightDiscoveryService;
import org.openhab.binding.hue.internal.handler.HueBridgeHandler;
import org.openhab.binding.hue.internal.handler.HueGroupHandler;
import org.openhab.binding.hue.internal.handler.HueLightHandler;
import org.openhab.binding.hue.internal.handler.sensors.ClipHandler;
import org.openhab.binding.hue.internal.handler.sensors.DimmerSwitchHandler;
Expand All @@ -53,16 +54,17 @@
* @author Andre Fuechsel - implemented to use one discovery service per bridge
* @author Samuel Leisering - Added support for sensor API
* @author Christoph Weitkamp - Added support for sensor API
* @author Laurent Garnier - Added support for groups
*/
@NonNullByDefault
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.hue")
public class HueThingHandlerFactory extends BaseThingHandlerFactory {
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.unmodifiableSet(Stream
.of(HueBridgeHandler.SUPPORTED_THING_TYPES.stream(), HueLightHandler.SUPPORTED_THING_TYPES.stream(),
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.unmodifiableSet(
Stream.of(HueBridgeHandler.SUPPORTED_THING_TYPES.stream(), HueLightHandler.SUPPORTED_THING_TYPES.stream(),
DimmerSwitchHandler.SUPPORTED_THING_TYPES.stream(), TapSwitchHandler.SUPPORTED_THING_TYPES.stream(),
PresenceHandler.SUPPORTED_THING_TYPES.stream(), TemperatureHandler.SUPPORTED_THING_TYPES.stream(),
LightLevelHandler.SUPPORTED_THING_TYPES.stream(), ClipHandler.SUPPORTED_THING_TYPES.stream())
.flatMap(i -> i).collect(Collectors.toSet()));
LightLevelHandler.SUPPORTED_THING_TYPES.stream(), ClipHandler.SUPPORTED_THING_TYPES.stream(),
HueGroupHandler.SUPPORTED_THING_TYPES.stream()).flatMap(i -> i).collect(Collectors.toSet()));

private final Map<ThingUID, @Nullable ServiceRegistration<?>> discoveryServiceRegs = new HashMap<>();

Expand All @@ -82,6 +84,9 @@ public class HueThingHandlerFactory extends BaseThingHandlerFactory {
|| ClipHandler.SUPPORTED_THING_TYPES.contains(thingTypeUID)) {
ThingUID hueSensorUID = getSensorUID(thingTypeUID, thingUID, configuration, bridgeUID);
return super.createThing(thingTypeUID, configuration, hueSensorUID, bridgeUID);
} else if (HueGroupHandler.SUPPORTED_THING_TYPES.contains(thingTypeUID)) {
ThingUID hueGroupUID = getGroupUID(thingTypeUID, thingUID, configuration, bridgeUID);
return super.createThing(thingTypeUID, configuration, hueGroupUID, bridgeUID);
}

throw new IllegalArgumentException("The thing type " + thingTypeUID + " is not supported by the hue binding.");
Expand Down Expand Up @@ -110,6 +115,15 @@ private ThingUID getSensorUID(ThingTypeUID thingTypeUID, @Nullable ThingUID thin
}
}

private ThingUID getGroupUID(ThingTypeUID thingTypeUID, @Nullable ThingUID thingUID, Configuration configuration,
@Nullable ThingUID bridgeUID) {
if (thingUID != null) {
return thingUID;
} else {
return getThingUID(thingTypeUID, configuration.get(GROUP_ID).toString(), bridgeUID);
}
}

private ThingUID getThingUID(ThingTypeUID thingTypeUID, String id, @Nullable ThingUID bridgeUID) {
if (bridgeUID != null) {
return new ThingUID(thingTypeUID, id, bridgeUID.getId());
Expand Down Expand Up @@ -138,6 +152,8 @@ private ThingUID getThingUID(ThingTypeUID thingTypeUID, String id, @Nullable Thi
return new LightLevelHandler(thing);
} else if (ClipHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
return new ClipHandler(thing);
} else if (HueGroupHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
return new HueGroupHandler(thing);
} else {
return null;
}
Expand Down
Loading

0 comments on commit d060e0c

Please sign in to comment.