Skip to content

Commit

Permalink
[hdpowerview] Add support for repeaters (openhab#12061)
Browse files Browse the repository at this point in the history
* Add support for repeaters.

Fixes openhab#12060

Signed-off-by: Jacob Laursen <[email protected]>

* Simplify thing type filtering.

Signed-off-by: Jacob Laursen <[email protected]>

* Improve robustness of configuration ID validation/initialization.

Signed-off-by: Jacob Laursen <[email protected]>

* Convert repeater-identify to command channel.

Signed-off-by: Jacob Laursen <[email protected]>

* Fix logged warning.

Signed-off-by: Jacob Laursen <[email protected]>

* Skip unneeded bridge status logic.

Signed-off-by: Jacob Laursen <[email protected]>

* Skip redundant logging.

Signed-off-by: Jacob Laursen <[email protected]>

* Fix chanenl type label for blinking enabled.

Signed-off-by: Jacob Laursen <[email protected]>
Signed-off-by: Nick Waterton <[email protected]>
  • Loading branch information
jlaur authored and NickWaterton committed Apr 27, 2022
1 parent 2b2958e commit b3b23aa
Show file tree
Hide file tree
Showing 17 changed files with 682 additions and 96 deletions.
55 changes: 40 additions & 15 deletions bundles/org.openhab.binding.hdpowerview/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ By using a scene to control multiple shades at once, the shades will all begin m

## Supported Things

| Thing | Thing Type | Description |
|-------|------------|-------------|
| hub | Bridge | The PowerView hub provides the interface between your network and the shade's radio network. It also contains channels used to interact with scenes. |
| shade | Thing | A motorized shade. |
| Thing | Thing Type | Description |
|----------|------------|-------------|
| hub | Bridge | The PowerView hub provides the interface between your network and the shade's radio network. It also contains channels used to interact with scenes. |
| shade | Thing | A motorized shade. |
| repeater | Thing | A PowerView signal repeater. |

## Discovery

Expand Down Expand Up @@ -46,16 +47,25 @@ If in the future, you add additional shades or scenes to your system, the bindin
| hardRefresh | The number of minutes between hard refreshes of the PowerView hub's shade state (default 180 three hours). See [Refreshing the PowerView Hub Cache](#Refreshing-the-PowerView-Hub-Cache). |
| hardRefreshBatteryLevel | The number of hours between hard refreshes of battery levels from the PowerView Hub (or 0 to disable, defaulting to weekly). See [Refreshing the PowerView Hub Cache](#Refreshing-the-PowerView-Hub-Cache). |

### Thing Configuration for PowerView Shades
### Thing Configuration for PowerView Shades and Accessories

PowerView shades should preferably be configured via the automatic discovery process.
It is quite difficult to configure manually as the `id` of the shade is not exposed in the PowerView app.
However, the configuration parameters are described below:
PowerView shades and repeaters should preferably be configured via the automatic discovery process.
It is quite difficult to configure manually as the `id` of the shade or repeater is not exposed in the
PowerView app. However, the configuration parameters are described below.

#### Thing Configuration for PowerView Shades

| Configuration Parameter | Description |
|-------------------------|-------------|
| id | The ID of the PowerView shade in the app. Must be an integer. |

#### Thing Configuration for PowerView Repeaters


| Configuration Parameter | Description |
|-------------------------|-------------|
| id | The ID of the PowerView repeater in the app. Must be an integer. |

## Channels

### Channels for Hub (Thing type `hub`)
Expand Down Expand Up @@ -88,6 +98,13 @@ All of these channels appear in the binding, but only those which have a physica
| batteryVoltage | Number:ElectricPotential | Battery voltage reported by the shade. |
| signalStrength | Number | Signal strength (0 for no or unknown signal, 1 for weak, 2 for average, 3 for good or 4 for excellent) |

### Channels for Repeaters (Thing type `repeater`)

| Channel | Item Type | Description |
|-----------------|-----------|-------------------------------|
| identify | String | Flash repeater to identify. Valid values are: IDENTIFY |
| blinkingEnabled | Switch | Blink during commands. |

### Roller Shutter Up/Down Position vs. Open/Close State

The `position` and `secondary` channels are Rollershutter types.
Expand Down Expand Up @@ -191,6 +208,7 @@ For single shades the refresh takes the item's channel into consideration:
```
Bridge hdpowerview:hub:g24 "Luxaflex Hub" @ "Living Room" [host="192.168.1.123"] {
Thing shade s50150 "Living Room Shade" @ "Living Room" [id="50150"]
Thing repeater r16384 "Bedroom Repeater" @ "Bedroom" [id="16384"]
}
```

Expand All @@ -200,16 +218,19 @@ Shade items:

```
Rollershutter Living_Room_Shade_Position "Living Room Shade Position [%.0f %%]" {channel="hdpowerview:shade:g24:s50150:position"}
Rollershutter Living_Room_Shade_Secondary "Living Room Shade Secondary Position [%.0f %%]" {channel="hdpowerview:shade:g24:s50150:secondary"}
Dimmer Living_Room_Shade_Vane "Living Room Shade Vane [%.0f %%]" {channel="hdpowerview:shade:g24:s50150:vane"}
Switch Living_Room_Shade_Battery_Low_Alarm "Living Room Shade Battery Low Alarm [%s]" {channel="hdpowerview:shade:g24:s50150:lowBattery"}
Switch Living_Room_Shade_Calibrate "Living Room Shade Calibrate" {channel="hdpowerview:shade:g24:s50150:calibrate", autoupdate="false"}
```

Repeater items:

```
String Bedroom_Repeater_Identify "Bedroom Repeater Identify" {channel="hdpowerview:repeater:g24:r16384:identify"}
Switch Bedroom_Repeater_BlinkingEnabled "Bedroom Repeater Blinking Enabled [%s]" {channel="hdpowerview:repeater:g24:r16384:blinkingEnabled"}
```

Scene items:

```
Expand All @@ -219,8 +240,12 @@ Switch Living_Room_Shades_Scene_Heart "Living Room Shades Scene Heart" <blinds>
### `demo.sitemap` File

```
Frame label="Living Room Shades" {
Switch item=Living_Room_Shades_Scene_Open
Slider item=Living_Room_Shade_1_Position 
Frame label="Living Room Shades" {
Switch item=Living_Room_Shades_Scene_Open
Slider item=Living_Room_Shade_1_Position
}
Frame label="Bedroom" {
Switch item=Bedroom_Repeater_Identify mappings=[IDENTIFY="Identify"]
Switch item=Bedroom_Repeater_BlinkingEnabled
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
package org.openhab.binding.hdpowerview.internal;

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

Expand All @@ -26,7 +25,7 @@
*
* @author Andy Lintner - Initial contribution
* @author Andrew Fiddian-Green - Added support for secondary rail positions
* @author Jacob Laursen - Add support for scene groups and automations
* @author Jacob Laursen - Added support for scene groups, automations and repeaters
*/
@NonNullByDefault
public class HDPowerViewBindingConstants {
Expand All @@ -36,6 +35,7 @@ public class HDPowerViewBindingConstants {
// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_HUB = new ThingTypeUID(BINDING_ID, "hub");
public static final ThingTypeUID THING_TYPE_SHADE = new ThingTypeUID(BINDING_ID, "shade");
public static final ThingTypeUID THING_TYPE_REPEATER = new ThingTypeUID(BINDING_ID, "repeater");

// List of all Channel ids
public static final String CHANNEL_SHADE_POSITION = "position";
Expand All @@ -47,6 +47,9 @@ public class HDPowerViewBindingConstants {
public static final String CHANNEL_SHADE_BATTERY_VOLTAGE = "batteryVoltage";
public static final String CHANNEL_SHADE_SIGNAL_STRENGTH = "signalStrength";

public static final String CHANNEL_REPEATER_IDENTIFY = "identify";
public static final String CHANNEL_REPEATER_BLINKING_ENABLED = "blinkingEnabled";

public static final String CHANNEL_GROUP_SCENES = "scenes";
public static final String CHANNEL_GROUP_SCENE_GROUPS = "sceneGroups";
public static final String CHANNEL_GROUP_AUTOMATIONS = "automations";
Expand All @@ -68,10 +71,6 @@ public class HDPowerViewBindingConstants {

public static final List<String> NETBIOS_NAMES = Arrays.asList("PDBU-Hub3.0", "PowerView-Hub");

public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = new HashSet<>();

static {
SUPPORTED_THING_TYPES_UIDS.add(THING_TYPE_HUB);
SUPPORTED_THING_TYPES_UIDS.add(THING_TYPE_SHADE);
}
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_HUB, THING_TYPE_SHADE,
THING_TYPE_REPEATER);
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.hdpowerview.internal.discovery.HDPowerViewShadeDiscoveryService;
import org.openhab.binding.hdpowerview.internal.discovery.HDPowerViewDeviceDiscoveryService;
import org.openhab.binding.hdpowerview.internal.handler.HDPowerViewHubHandler;
import org.openhab.binding.hdpowerview.internal.handler.HDPowerViewRepeaterHandler;
import org.openhab.binding.hdpowerview.internal.handler.HDPowerViewShadeHandler;
import org.openhab.core.config.discovery.DiscoveryService;
import org.openhab.core.i18n.LocaleProvider;
Expand Down Expand Up @@ -67,12 +68,14 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) {
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();

if (thingTypeUID.equals(HDPowerViewBindingConstants.THING_TYPE_HUB)) {
if (HDPowerViewBindingConstants.THING_TYPE_HUB.equals(thingTypeUID)) {
HDPowerViewHubHandler handler = new HDPowerViewHubHandler((Bridge) thing, httpClient, translationProvider);
registerService(new HDPowerViewShadeDiscoveryService(handler));
registerService(new HDPowerViewDeviceDiscoveryService(handler));
return handler;
} else if (thingTypeUID.equals(HDPowerViewBindingConstants.THING_TYPE_SHADE)) {
} else if (HDPowerViewBindingConstants.THING_TYPE_SHADE.equals(thingTypeUID)) {
return new HDPowerViewShadeHandler(thing);
} else if (HDPowerViewBindingConstants.THING_TYPE_REPEATER.equals(thingTypeUID)) {
return new HDPowerViewRepeaterHandler(thing);
}

return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,15 @@
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
import org.openhab.binding.hdpowerview.internal.api.ShadePosition;
import org.openhab.binding.hdpowerview.internal.api.requests.RepeaterBlinking;
import org.openhab.binding.hdpowerview.internal.api.requests.ShadeCalibrate;
import org.openhab.binding.hdpowerview.internal.api.requests.ShadeMove;
import org.openhab.binding.hdpowerview.internal.api.requests.ShadeStop;
import org.openhab.binding.hdpowerview.internal.api.responses.FirmwareVersion;
import org.openhab.binding.hdpowerview.internal.api.responses.FirmwareVersions;
import org.openhab.binding.hdpowerview.internal.api.responses.Repeater;
import org.openhab.binding.hdpowerview.internal.api.responses.RepeaterData;
import org.openhab.binding.hdpowerview.internal.api.responses.Repeaters;
import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections;
import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection;
import org.openhab.binding.hdpowerview.internal.api.responses.Scenes;
Expand Down Expand Up @@ -83,6 +87,7 @@ public class HDPowerViewWebTargets {
private final String sceneCollectionActivate;
private final String sceneCollections;
private final String scheduledEvents;
private final String repeaters;

private final Gson gson = new Gson();
private final HttpClient httpClient;
Expand Down Expand Up @@ -135,6 +140,9 @@ public HDPowerViewWebTargets(HttpClient httpClient, String ipAddress) {
sceneCollections = base + "scenecollections/";

scheduledEvents = base + "scheduledevents";

repeaters = base + "repeaters/";

this.httpClient = httpClient;
}

Expand Down Expand Up @@ -385,6 +393,98 @@ public void enableScheduledEvent(int scheduledEventId, boolean enable)
}
}

/**
* Fetches a JSON package that describes all repeaters in the hub, and wraps it in
* a Repeaters class instance
*
* @return Repeaters class instance
* @throws HubInvalidResponseException if response is invalid
* @throws HubProcessingException if there is any processing error
* @throws HubMaintenanceException if the hub is down for maintenance
*/
public Repeaters getRepeaters()
throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
String json = invoke(HttpMethod.GET, repeaters, null, null);
try {
Repeaters repeaters = gson.fromJson(json, Repeaters.class);
if (repeaters == null) {
throw new HubInvalidResponseException("Missing repeaters response");
}
List<RepeaterData> repeaterData = repeaters.repeaterData;
if (repeaterData == null) {
throw new HubInvalidResponseException("Missing 'repeaters.repeaterData' element");
}
return repeaters;
} catch (JsonParseException e) {
throw new HubInvalidResponseException("Error parsing repeaters response", e);
}
}

/**
* Fetches a JSON package that describes a specific repeater in the hub, and wraps it
* in a RepeaterData class instance
*
* @param repeaterId id of the repeater to be fetched
* @return RepeaterData class instance
* @throws HubInvalidResponseException if response is invalid
* @throws HubProcessingException if there is any processing error
* @throws HubMaintenanceException if the hub is down for maintenance
*/
public RepeaterData getRepeater(int repeaterId)
throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
String jsonResponse = invoke(HttpMethod.GET, repeaters + Integer.toString(repeaterId), null, null);
return repeaterDataFromJson(jsonResponse);
}

private RepeaterData repeaterDataFromJson(String json) throws HubInvalidResponseException {
try {
Repeater repeater = gson.fromJson(json, Repeater.class);
if (repeater == null) {
throw new HubInvalidResponseException("Missing repeater response");
}
RepeaterData repeaterData = repeater.repeater;
if (repeaterData == null) {
throw new HubInvalidResponseException("Missing 'repeater.repeater' element");
}
return repeaterData;
} catch (JsonParseException e) {
throw new HubInvalidResponseException("Error parsing repeater response", e);
}
}

/**
* Instructs the hub to identify a specific repeater by blinking
*
* @param repeaterId id of the repeater to be identified
* @return RepeaterData class instance
* @throws HubInvalidResponseException if response is invalid
* @throws HubProcessingException if there is any processing error
* @throws HubMaintenanceException if the hub is down for maintenance
*/
public RepeaterData identifyRepeater(int repeaterId)
throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
String jsonResponse = invoke(HttpMethod.GET, repeaters + repeaterId,
Query.of("identify", Boolean.toString(true)), null);
return repeaterDataFromJson(jsonResponse);
}

/**
* Enables or disables blinking for a repeater
*
* @param repeaterId id of the repeater for which to be enable or disable blinking
* @param enable true to enable blinking, false to disable
* @return RepeaterData class instance
* @throws HubInvalidResponseException if response is invalid
* @throws HubProcessingException if there is any processing error
* @throws HubMaintenanceException if the hub is down for maintenance
*/
public RepeaterData enableRepeaterBlinking(int repeaterId, boolean enable)
throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
String jsonRequest = gson.toJson(new RepeaterBlinking(repeaterId, enable));
String jsonResponse = invoke(HttpMethod.PUT, repeaters + repeaterId, null, jsonRequest);
return repeaterDataFromJson(jsonResponse);
}

/**
* Invoke a call on the hub server to retrieve information or send a command
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* Copyright (c) 2010-2022 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.hdpowerview.internal.api.requests;

import org.eclipse.jdt.annotation.NonNullByDefault;

/**
* State of a single Repeater for being updated by an HD PowerView Hub
*
* @author Jacob Laursen - Initial contribution
*/
@NonNullByDefault
public class RepeaterBlinking {
public Repeater repeater;

public class Repeater {
public int id;
public boolean blinkEnabled;

public Repeater(int id, boolean enable) {
this.id = id;
this.blinkEnabled = enable;
}
}

public RepeaterBlinking(int id, boolean enable) {
repeater = new Repeater(id, enable);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* Copyright (c) 2010-2022 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.hdpowerview.internal.api.responses;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;

/**
* State of a single Repeater, as returned by an HD PowerView Hub
*
* @author Jacob Laursen - Initial contribution
*/
@NonNullByDefault
public class Repeater {
public @Nullable RepeaterData repeater;
}
Loading

0 comments on commit b3b23aa

Please sign in to comment.