[hdpowerview] Add support for repeaters (#12061)
* Add support for repeaters. Fixes #12060 Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk> * Simplify thing type filtering. Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk> * Improve robustness of configuration ID validation/initialization. Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk> * Convert repeater-identify to command channel. Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk> * Fix logged warning. Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk> * Skip unneeded bridge status logic. Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk> * Skip redundant logging. Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk> * Fix chanenl type label for blinking enabled. Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>
This commit is contained in:
parent
3d24cfc535
commit
9d361c1fa3
@ -14,9 +14,10 @@ By using a scene to control multiple shades at once, the shades will all begin m
|
|||||||
## Supported Things
|
## Supported Things
|
||||||
|
|
||||||
| Thing | Thing Type | Description |
|
| 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. |
|
| 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. |
|
| shade | Thing | A motorized shade. |
|
||||||
|
| repeater | Thing | A PowerView signal repeater. |
|
||||||
|
|
||||||
## Discovery
|
## Discovery
|
||||||
|
|
||||||
@ -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). |
|
| 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). |
|
| 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.
|
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 is not exposed in the PowerView app.
|
It is quite difficult to configure manually as the `id` of the shade or repeater is not exposed in the
|
||||||
However, the configuration parameters are described below:
|
PowerView app. However, the configuration parameters are described below.
|
||||||
|
|
||||||
|
#### Thing Configuration for PowerView Shades
|
||||||
|
|
||||||
| Configuration Parameter | Description |
|
| Configuration Parameter | Description |
|
||||||
|-------------------------|-------------|
|
|-------------------------|-------------|
|
||||||
| id | The ID of the PowerView shade in the app. Must be an integer. |
|
| 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
|
||||||
|
|
||||||
### Channels for Hub (Thing type `hub`)
|
### Channels for Hub (Thing type `hub`)
|
||||||
@ -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. |
|
| 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) |
|
| 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
|
### Roller Shutter Up/Down Position vs. Open/Close State
|
||||||
|
|
||||||
The `position` and `secondary` channels are Rollershutter types.
|
The `position` and `secondary` channels are Rollershutter types.
|
||||||
@ -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"] {
|
Bridge hdpowerview:hub:g24 "Luxaflex Hub" @ "Living Room" [host="192.168.1.123"] {
|
||||||
Thing shade s50150 "Living Room Shade" @ "Living Room" [id="50150"]
|
Thing shade s50150 "Living Room Shade" @ "Living Room" [id="50150"]
|
||||||
|
Thing repeater r16384 "Bedroom Repeater" @ "Bedroom" [id="16384"]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -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_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"}
|
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"}
|
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_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"}
|
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:
|
Scene items:
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -219,8 +240,12 @@ Switch Living_Room_Shades_Scene_Heart "Living Room Shades Scene Heart" <blinds>
|
|||||||
### `demo.sitemap` File
|
### `demo.sitemap` File
|
||||||
|
|
||||||
```
|
```
|
||||||
Frame label="Living Room Shades" {
|
Frame label="Living Room Shades" {
|
||||||
Switch item=Living_Room_Shades_Scene_Open
|
Switch item=Living_Room_Shades_Scene_Open
|
||||||
Slider item=Living_Room_Shade_1_Position
|
Slider item=Living_Room_Shade_1_Position
|
||||||
|
}
|
||||||
|
Frame label="Bedroom" {
|
||||||
|
Switch item=Bedroom_Repeater_Identify mappings=[IDENTIFY="Identify"]
|
||||||
|
Switch item=Bedroom_Repeater_BlinkingEnabled
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@ -13,7 +13,6 @@
|
|||||||
package org.openhab.binding.hdpowerview.internal;
|
package org.openhab.binding.hdpowerview.internal;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@ -26,7 +25,7 @@ import org.openhab.core.thing.ThingTypeUID;
|
|||||||
*
|
*
|
||||||
* @author Andy Lintner - Initial contribution
|
* @author Andy Lintner - Initial contribution
|
||||||
* @author Andrew Fiddian-Green - Added support for secondary rail positions
|
* @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
|
@NonNullByDefault
|
||||||
public class HDPowerViewBindingConstants {
|
public class HDPowerViewBindingConstants {
|
||||||
@ -36,6 +35,7 @@ public class HDPowerViewBindingConstants {
|
|||||||
// List of all Thing Type UIDs
|
// List of all Thing Type UIDs
|
||||||
public static final ThingTypeUID THING_TYPE_HUB = new ThingTypeUID(BINDING_ID, "hub");
|
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_SHADE = new ThingTypeUID(BINDING_ID, "shade");
|
||||||
|
public static final ThingTypeUID THING_TYPE_REPEATER = new ThingTypeUID(BINDING_ID, "repeater");
|
||||||
|
|
||||||
// List of all Channel ids
|
// List of all Channel ids
|
||||||
public static final String CHANNEL_SHADE_POSITION = "position";
|
public static final String CHANNEL_SHADE_POSITION = "position";
|
||||||
@ -47,6 +47,9 @@ public class HDPowerViewBindingConstants {
|
|||||||
public static final String CHANNEL_SHADE_BATTERY_VOLTAGE = "batteryVoltage";
|
public static final String CHANNEL_SHADE_BATTERY_VOLTAGE = "batteryVoltage";
|
||||||
public static final String CHANNEL_SHADE_SIGNAL_STRENGTH = "signalStrength";
|
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_SCENES = "scenes";
|
||||||
public static final String CHANNEL_GROUP_SCENE_GROUPS = "sceneGroups";
|
public static final String CHANNEL_GROUP_SCENE_GROUPS = "sceneGroups";
|
||||||
public static final String CHANNEL_GROUP_AUTOMATIONS = "automations";
|
public static final String CHANNEL_GROUP_AUTOMATIONS = "automations";
|
||||||
@ -68,10 +71,6 @@ public class HDPowerViewBindingConstants {
|
|||||||
|
|
||||||
public static final List<String> NETBIOS_NAMES = Arrays.asList("PDBU-Hub3.0", "PowerView-Hub");
|
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<>();
|
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_HUB, THING_TYPE_SHADE,
|
||||||
|
THING_TYPE_REPEATER);
|
||||||
static {
|
|
||||||
SUPPORTED_THING_TYPES_UIDS.add(THING_TYPE_HUB);
|
|
||||||
SUPPORTED_THING_TYPES_UIDS.add(THING_TYPE_SHADE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,8 +17,9 @@ import java.util.Hashtable;
|
|||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.eclipse.jdt.annotation.Nullable;
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
import org.eclipse.jetty.client.HttpClient;
|
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.HDPowerViewHubHandler;
|
||||||
|
import org.openhab.binding.hdpowerview.internal.handler.HDPowerViewRepeaterHandler;
|
||||||
import org.openhab.binding.hdpowerview.internal.handler.HDPowerViewShadeHandler;
|
import org.openhab.binding.hdpowerview.internal.handler.HDPowerViewShadeHandler;
|
||||||
import org.openhab.core.config.discovery.DiscoveryService;
|
import org.openhab.core.config.discovery.DiscoveryService;
|
||||||
import org.openhab.core.i18n.LocaleProvider;
|
import org.openhab.core.i18n.LocaleProvider;
|
||||||
@ -67,12 +68,14 @@ public class HDPowerViewHandlerFactory extends BaseThingHandlerFactory {
|
|||||||
protected @Nullable ThingHandler createHandler(Thing thing) {
|
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
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);
|
HDPowerViewHubHandler handler = new HDPowerViewHubHandler((Bridge) thing, httpClient, translationProvider);
|
||||||
registerService(new HDPowerViewShadeDiscoveryService(handler));
|
registerService(new HDPowerViewDeviceDiscoveryService(handler));
|
||||||
return handler;
|
return handler;
|
||||||
} else if (thingTypeUID.equals(HDPowerViewBindingConstants.THING_TYPE_SHADE)) {
|
} else if (HDPowerViewBindingConstants.THING_TYPE_SHADE.equals(thingTypeUID)) {
|
||||||
return new HDPowerViewShadeHandler(thing);
|
return new HDPowerViewShadeHandler(thing);
|
||||||
|
} else if (HDPowerViewBindingConstants.THING_TYPE_REPEATER.equals(thingTypeUID)) {
|
||||||
|
return new HDPowerViewRepeaterHandler(thing);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@ -27,11 +27,15 @@ import org.eclipse.jetty.http.HttpHeader;
|
|||||||
import org.eclipse.jetty.http.HttpMethod;
|
import org.eclipse.jetty.http.HttpMethod;
|
||||||
import org.eclipse.jetty.http.HttpStatus;
|
import org.eclipse.jetty.http.HttpStatus;
|
||||||
import org.openhab.binding.hdpowerview.internal.api.ShadePosition;
|
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.ShadeCalibrate;
|
||||||
import org.openhab.binding.hdpowerview.internal.api.requests.ShadeMove;
|
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.requests.ShadeStop;
|
||||||
import org.openhab.binding.hdpowerview.internal.api.responses.FirmwareVersion;
|
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.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;
|
||||||
import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection;
|
import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection;
|
||||||
import org.openhab.binding.hdpowerview.internal.api.responses.Scenes;
|
import org.openhab.binding.hdpowerview.internal.api.responses.Scenes;
|
||||||
@ -83,6 +87,7 @@ public class HDPowerViewWebTargets {
|
|||||||
private final String sceneCollectionActivate;
|
private final String sceneCollectionActivate;
|
||||||
private final String sceneCollections;
|
private final String sceneCollections;
|
||||||
private final String scheduledEvents;
|
private final String scheduledEvents;
|
||||||
|
private final String repeaters;
|
||||||
|
|
||||||
private final Gson gson = new Gson();
|
private final Gson gson = new Gson();
|
||||||
private final HttpClient httpClient;
|
private final HttpClient httpClient;
|
||||||
@ -135,6 +140,9 @@ public class HDPowerViewWebTargets {
|
|||||||
sceneCollections = base + "scenecollections/";
|
sceneCollections = base + "scenecollections/";
|
||||||
|
|
||||||
scheduledEvents = base + "scheduledevents";
|
scheduledEvents = base + "scheduledevents";
|
||||||
|
|
||||||
|
repeaters = base + "repeaters/";
|
||||||
|
|
||||||
this.httpClient = httpClient;
|
this.httpClient = httpClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -385,6 +393,98 @@ public class HDPowerViewWebTargets {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
* Invoke a call on the hub server to retrieve information or send a command
|
||||||
*
|
*
|
||||||
|
|||||||
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* 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 java.util.Base64;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.binding.hdpowerview.internal.api.Firmware;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Repeater data for a single Repeater, as returned by an HD PowerView Hub
|
||||||
|
*
|
||||||
|
* @author Jacob Laursen - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class RepeaterData {
|
||||||
|
public int id;
|
||||||
|
public @Nullable String name;
|
||||||
|
public int roomId;
|
||||||
|
public int groupId;
|
||||||
|
public boolean blinkEnabled;
|
||||||
|
public @Nullable Firmware firmware;
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return new String(Base64.getDecoder().decode(name));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* 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 java.util.List;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Repeaters connected to an HD PowerView Hub
|
||||||
|
*
|
||||||
|
* @author Jacob Laursen - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class Repeaters {
|
||||||
|
public @Nullable List<RepeaterData> repeaterData;
|
||||||
|
public @Nullable List<Integer> repeaterIds;
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* 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.config;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic configuration for an HD PowerView Repeater
|
||||||
|
*
|
||||||
|
* @author Jacob Laursen - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class HDPowerViewRepeaterConfiguration {
|
||||||
|
|
||||||
|
public static final String ID = "id";
|
||||||
|
|
||||||
|
public int id;
|
||||||
|
}
|
||||||
@ -13,7 +13,6 @@
|
|||||||
package org.openhab.binding.hdpowerview.internal.config;
|
package org.openhab.binding.hdpowerview.internal.config;
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.eclipse.jdt.annotation.Nullable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Basic configuration for an HD PowerView Shade
|
* Basic configuration for an HD PowerView Shade
|
||||||
@ -25,5 +24,5 @@ public class HDPowerViewShadeConfiguration {
|
|||||||
|
|
||||||
public static final String ID = "id";
|
public static final String ID = "id";
|
||||||
|
|
||||||
public @Nullable String id;
|
public int id;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,12 +21,15 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
|
|||||||
import org.eclipse.jdt.annotation.Nullable;
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants;
|
import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants;
|
||||||
import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets;
|
import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets;
|
||||||
|
import org.openhab.binding.hdpowerview.internal.api.responses.RepeaterData;
|
||||||
import org.openhab.binding.hdpowerview.internal.api.responses.Shades;
|
import org.openhab.binding.hdpowerview.internal.api.responses.Shades;
|
||||||
import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData;
|
import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData;
|
||||||
|
import org.openhab.binding.hdpowerview.internal.config.HDPowerViewRepeaterConfiguration;
|
||||||
import org.openhab.binding.hdpowerview.internal.config.HDPowerViewShadeConfiguration;
|
import org.openhab.binding.hdpowerview.internal.config.HDPowerViewShadeConfiguration;
|
||||||
import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase;
|
import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase;
|
||||||
import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities;
|
import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities;
|
||||||
import org.openhab.binding.hdpowerview.internal.exceptions.HubException;
|
import org.openhab.binding.hdpowerview.internal.exceptions.HubException;
|
||||||
|
import org.openhab.binding.hdpowerview.internal.exceptions.HubInvalidResponseException;
|
||||||
import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException;
|
import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException;
|
||||||
import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException;
|
import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException;
|
||||||
import org.openhab.binding.hdpowerview.internal.handler.HDPowerViewHubHandler;
|
import org.openhab.binding.hdpowerview.internal.handler.HDPowerViewHubHandler;
|
||||||
@ -42,15 +45,15 @@ import org.slf4j.LoggerFactory;
|
|||||||
* @author Andy Lintner - Initial contribution
|
* @author Andy Lintner - Initial contribution
|
||||||
*/
|
*/
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public class HDPowerViewShadeDiscoveryService extends AbstractDiscoveryService {
|
public class HDPowerViewDeviceDiscoveryService extends AbstractDiscoveryService {
|
||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(HDPowerViewShadeDiscoveryService.class);
|
private final Logger logger = LoggerFactory.getLogger(HDPowerViewDeviceDiscoveryService.class);
|
||||||
private final HDPowerViewHubHandler hub;
|
private final HDPowerViewHubHandler hub;
|
||||||
private final Runnable scanner;
|
private final Runnable scanner;
|
||||||
private @Nullable ScheduledFuture<?> backgroundFuture;
|
private @Nullable ScheduledFuture<?> backgroundFuture;
|
||||||
private final ShadeCapabilitiesDatabase db = new ShadeCapabilitiesDatabase();
|
private final ShadeCapabilitiesDatabase db = new ShadeCapabilitiesDatabase();
|
||||||
|
|
||||||
public HDPowerViewShadeDiscoveryService(HDPowerViewHubHandler hub) {
|
public HDPowerViewDeviceDiscoveryService(HDPowerViewHubHandler hub) {
|
||||||
super(Collections.singleton(HDPowerViewBindingConstants.THING_TYPE_SHADE), 600, true);
|
super(Collections.singleton(HDPowerViewBindingConstants.THING_TYPE_SHADE), 600, true);
|
||||||
this.hub = hub;
|
this.hub = hub;
|
||||||
this.scanner = createScanner();
|
this.scanner = createScanner();
|
||||||
@ -87,34 +90,8 @@ public class HDPowerViewShadeDiscoveryService extends AbstractDiscoveryService {
|
|||||||
if (webTargets == null) {
|
if (webTargets == null) {
|
||||||
throw new HubProcessingException("Web targets not initialized");
|
throw new HubProcessingException("Web targets not initialized");
|
||||||
}
|
}
|
||||||
Shades shades = webTargets.getShades();
|
discoverShades(webTargets);
|
||||||
if (shades.shadeData != null) {
|
discoverRepeaters(webTargets);
|
||||||
ThingUID bridgeUID = hub.getThing().getUID();
|
|
||||||
List<ShadeData> shadesData = shades.shadeData;
|
|
||||||
if (shadesData != null) {
|
|
||||||
for (ShadeData shadeData : shadesData) {
|
|
||||||
if (shadeData.id != 0) {
|
|
||||||
String id = Integer.toString(shadeData.id);
|
|
||||||
ThingUID thingUID = new ThingUID(HDPowerViewBindingConstants.THING_TYPE_SHADE,
|
|
||||||
bridgeUID, id);
|
|
||||||
Integer caps = shadeData.capabilities;
|
|
||||||
Capabilities capabilities = db.getCapabilities((caps != null) ? caps.intValue() : -1);
|
|
||||||
|
|
||||||
DiscoveryResultBuilder builder = DiscoveryResultBuilder.create(thingUID)
|
|
||||||
.withLabel(shadeData.getName()).withBridge(bridgeUID)
|
|
||||||
.withProperty(HDPowerViewShadeConfiguration.ID, id)
|
|
||||||
.withProperty(HDPowerViewBindingConstants.PROPERTY_SHADE_TYPE,
|
|
||||||
db.getType(shadeData.type).toString())
|
|
||||||
.withProperty(HDPowerViewBindingConstants.PROPERTY_SHADE_CAPABILITIES,
|
|
||||||
capabilities.toString())
|
|
||||||
.withRepresentationProperty(HDPowerViewShadeConfiguration.ID);
|
|
||||||
|
|
||||||
logger.debug("Hub discovered shade '{}'", id);
|
|
||||||
thingDiscovered(builder.build());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (HubMaintenanceException e) {
|
} catch (HubMaintenanceException e) {
|
||||||
// exceptions are logged in HDPowerViewWebTargets
|
// exceptions are logged in HDPowerViewWebTargets
|
||||||
} catch (HubException e) {
|
} catch (HubException e) {
|
||||||
@ -123,4 +100,56 @@ public class HDPowerViewShadeDiscoveryService extends AbstractDiscoveryService {
|
|||||||
stopScan();
|
stopScan();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void discoverShades(HDPowerViewWebTargets webTargets)
|
||||||
|
throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
|
||||||
|
Shades shades = webTargets.getShades();
|
||||||
|
List<ShadeData> shadesData = shades.shadeData;
|
||||||
|
if (shadesData == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ThingUID bridgeUid = hub.getThing().getUID();
|
||||||
|
for (ShadeData shadeData : shadesData) {
|
||||||
|
if (shadeData.id == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String id = Integer.toString(shadeData.id);
|
||||||
|
ThingUID thingUID = new ThingUID(HDPowerViewBindingConstants.THING_TYPE_SHADE, bridgeUid, id);
|
||||||
|
Integer caps = shadeData.capabilities;
|
||||||
|
Capabilities capabilities = db.getCapabilities((caps != null) ? caps.intValue() : -1);
|
||||||
|
|
||||||
|
DiscoveryResultBuilder builder = DiscoveryResultBuilder.create(thingUID).withLabel(shadeData.getName())
|
||||||
|
.withBridge(bridgeUid).withProperty(HDPowerViewShadeConfiguration.ID, id)
|
||||||
|
.withProperty(HDPowerViewBindingConstants.PROPERTY_SHADE_TYPE,
|
||||||
|
db.getType(shadeData.type).toString())
|
||||||
|
.withProperty(HDPowerViewBindingConstants.PROPERTY_SHADE_CAPABILITIES, capabilities.toString())
|
||||||
|
.withRepresentationProperty(HDPowerViewShadeConfiguration.ID);
|
||||||
|
|
||||||
|
logger.debug("Hub discovered shade '{}'", id);
|
||||||
|
thingDiscovered(builder.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void discoverRepeaters(HDPowerViewWebTargets webTargets)
|
||||||
|
throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException {
|
||||||
|
List<RepeaterData> repeaters = webTargets.getRepeaters().repeaterData;
|
||||||
|
if (repeaters == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ThingUID bridgeUid = hub.getThing().getUID();
|
||||||
|
for (RepeaterData repeaterData : repeaters) {
|
||||||
|
if (repeaterData.id == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String id = Integer.toString(repeaterData.id);
|
||||||
|
ThingUID thingUid = new ThingUID(HDPowerViewBindingConstants.THING_TYPE_REPEATER, bridgeUid, id);
|
||||||
|
|
||||||
|
DiscoveryResultBuilder builder = DiscoveryResultBuilder.create(thingUid).withLabel(repeaterData.getName())
|
||||||
|
.withBridge(bridgeUid).withProperty(HDPowerViewRepeaterConfiguration.ID, id)
|
||||||
|
.withRepresentationProperty(HDPowerViewRepeaterConfiguration.ID);
|
||||||
|
|
||||||
|
logger.debug("Hub discovered repeater '{}'", id);
|
||||||
|
thingDiscovered(builder.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -325,17 +325,17 @@ public class HDPowerViewHubHandler extends BaseBridgeHandler {
|
|||||||
updateStatus(ThingStatus.ONLINE);
|
updateStatus(ThingStatus.ONLINE);
|
||||||
logger.debug("Received data for {} shades", shadesData.size());
|
logger.debug("Received data for {} shades", shadesData.size());
|
||||||
|
|
||||||
Map<String, ShadeData> idShadeDataMap = getIdShadeDataMap(shadesData);
|
Map<Integer, ShadeData> idShadeDataMap = getIdShadeDataMap(shadesData);
|
||||||
Map<Thing, String> thingIdMap = getThingIdMap();
|
Map<Thing, Integer> thingIdMap = getShadeThingIdMap();
|
||||||
for (Entry<Thing, String> item : thingIdMap.entrySet()) {
|
for (Entry<Thing, Integer> item : thingIdMap.entrySet()) {
|
||||||
Thing thing = item.getKey();
|
Thing thing = item.getKey();
|
||||||
String shadeId = item.getValue();
|
int shadeId = item.getValue();
|
||||||
ShadeData shadeData = idShadeDataMap.get(shadeId);
|
ShadeData shadeData = idShadeDataMap.get(shadeId);
|
||||||
updateShadeThing(shadeId, thing, shadeData);
|
updateShadeThing(shadeId, thing, shadeData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateShadeThing(String shadeId, Thing thing, @Nullable ShadeData shadeData) {
|
private void updateShadeThing(int shadeId, Thing thing, @Nullable ShadeData shadeData) {
|
||||||
HDPowerViewShadeHandler thingHandler = ((HDPowerViewShadeHandler) thing.getHandler());
|
HDPowerViewShadeHandler thingHandler = ((HDPowerViewShadeHandler) thing.getHandler());
|
||||||
if (thingHandler == null) {
|
if (thingHandler == null) {
|
||||||
logger.debug("Shade '{}' handler not initialized", shadeId);
|
logger.debug("Shade '{}' handler not initialized", shadeId);
|
||||||
@ -533,50 +533,52 @@ public class HDPowerViewHubHandler extends BaseBridgeHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<Thing, String> getThingIdMap() {
|
private Map<Thing, Integer> getShadeThingIdMap() {
|
||||||
Map<Thing, String> ret = new HashMap<>();
|
Map<Thing, Integer> ret = new HashMap<>();
|
||||||
for (Thing thing : getThing().getThings()) {
|
getThing().getThings().stream()
|
||||||
String id = thing.getConfiguration().as(HDPowerViewShadeConfiguration.class).id;
|
.filter(thing -> HDPowerViewBindingConstants.THING_TYPE_SHADE.equals(thing.getThingTypeUID()))
|
||||||
if (id != null && !id.isEmpty()) {
|
.forEach(thing -> {
|
||||||
|
int id = thing.getConfiguration().as(HDPowerViewShadeConfiguration.class).id;
|
||||||
|
if (id > 0) {
|
||||||
ret.put(thing, id);
|
ret.put(thing, id);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, ShadeData> getIdShadeDataMap(List<ShadeData> shadeData) {
|
private Map<Integer, ShadeData> getIdShadeDataMap(List<ShadeData> shadeData) {
|
||||||
Map<String, ShadeData> ret = new HashMap<>();
|
Map<Integer, ShadeData> ret = new HashMap<>();
|
||||||
for (ShadeData shade : shadeData) {
|
for (ShadeData shade : shadeData) {
|
||||||
if (shade.id != 0) {
|
if (shade.id > 0) {
|
||||||
ret.put(Integer.toString(shade.id), shade);
|
ret.put(shade.id, shade);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void requestRefreshShadePositions() {
|
private void requestRefreshShadePositions() {
|
||||||
Map<Thing, String> thingIdMap = getThingIdMap();
|
Map<Thing, Integer> thingIdMap = getShadeThingIdMap();
|
||||||
for (Entry<Thing, String> item : thingIdMap.entrySet()) {
|
for (Entry<Thing, Integer> item : thingIdMap.entrySet()) {
|
||||||
Thing thing = item.getKey();
|
Thing thing = item.getKey();
|
||||||
ThingHandler handler = thing.getHandler();
|
ThingHandler handler = thing.getHandler();
|
||||||
if (handler instanceof HDPowerViewShadeHandler) {
|
if (handler instanceof HDPowerViewShadeHandler) {
|
||||||
((HDPowerViewShadeHandler) handler).requestRefreshShadePosition();
|
((HDPowerViewShadeHandler) handler).requestRefreshShadePosition();
|
||||||
} else {
|
} else {
|
||||||
String shadeId = item.getValue();
|
int shadeId = item.getValue();
|
||||||
logger.debug("Shade '{}' handler not initialized", shadeId);
|
logger.debug("Shade '{}' handler not initialized", shadeId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void requestRefreshShadeBatteryLevels() {
|
private void requestRefreshShadeBatteryLevels() {
|
||||||
Map<Thing, String> thingIdMap = getThingIdMap();
|
Map<Thing, Integer> thingIdMap = getShadeThingIdMap();
|
||||||
for (Entry<Thing, String> item : thingIdMap.entrySet()) {
|
for (Entry<Thing, Integer> item : thingIdMap.entrySet()) {
|
||||||
Thing thing = item.getKey();
|
Thing thing = item.getKey();
|
||||||
ThingHandler handler = thing.getHandler();
|
ThingHandler handler = thing.getHandler();
|
||||||
if (handler instanceof HDPowerViewShadeHandler) {
|
if (handler instanceof HDPowerViewShadeHandler) {
|
||||||
((HDPowerViewShadeHandler) handler).requestRefreshShadeBatteryLevel();
|
((HDPowerViewShadeHandler) handler).requestRefreshShadeBatteryLevel();
|
||||||
} else {
|
} else {
|
||||||
String shadeId = item.getValue();
|
int shadeId = item.getValue();
|
||||||
logger.debug("Shade '{}' handler not initialized", shadeId);
|
logger.debug("Shade '{}' handler not initialized", shadeId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,212 @@
|
|||||||
|
/**
|
||||||
|
* 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.handler;
|
||||||
|
|
||||||
|
import static org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants.*;
|
||||||
|
|
||||||
|
import java.util.concurrent.ScheduledFuture;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets;
|
||||||
|
import org.openhab.binding.hdpowerview.internal.api.Firmware;
|
||||||
|
import org.openhab.binding.hdpowerview.internal.api.responses.RepeaterData;
|
||||||
|
import org.openhab.binding.hdpowerview.internal.config.HDPowerViewRepeaterConfiguration;
|
||||||
|
import org.openhab.binding.hdpowerview.internal.exceptions.HubException;
|
||||||
|
import org.openhab.binding.hdpowerview.internal.exceptions.HubInvalidResponseException;
|
||||||
|
import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException;
|
||||||
|
import org.openhab.core.library.types.OnOffType;
|
||||||
|
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.ThingStatus;
|
||||||
|
import org.openhab.core.thing.ThingStatusDetail;
|
||||||
|
import org.openhab.core.types.Command;
|
||||||
|
import org.openhab.core.types.UnDefType;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles commands for an HD PowerView Repeater
|
||||||
|
*
|
||||||
|
* @author Jacob Laursen - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class HDPowerViewRepeaterHandler extends AbstractHubbedThingHandler {
|
||||||
|
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(HDPowerViewRepeaterHandler.class);
|
||||||
|
|
||||||
|
private static final int REFRESH_INTERVAL_MINUTES = 5;
|
||||||
|
private static final int IDENTITY_PERIOD_SECONDS = 3;
|
||||||
|
private static final String COMMAND_IDENTIFY = "IDENTIFY";
|
||||||
|
|
||||||
|
private @Nullable ScheduledFuture<?> refreshStatusFuture = null;
|
||||||
|
private @Nullable ScheduledFuture<?> resetIdentifyStateFuture = null;
|
||||||
|
private int repeaterId;
|
||||||
|
|
||||||
|
public HDPowerViewRepeaterHandler(Thing thing) {
|
||||||
|
super(thing);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize() {
|
||||||
|
repeaterId = getConfigAs(HDPowerViewRepeaterConfiguration.class).id;
|
||||||
|
logger.debug("Initializing repeater handler for repeater {}", repeaterId);
|
||||||
|
if (repeaterId <= 0) {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||||
|
"@text/offline.conf-error.invalid-id");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Bridge bridge = getBridge();
|
||||||
|
if (bridge == null) {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!(bridge.getHandler() instanceof HDPowerViewHubHandler)) {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED,
|
||||||
|
"@text/offline.conf-error.invalid-bridge-handler");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
updateStatus(ThingStatus.UNKNOWN);
|
||||||
|
scheduleRefreshJob();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
logger.debug("Disposing repeater handler for repeater {}", repeaterId);
|
||||||
|
cancelRefreshJob();
|
||||||
|
cancelResetIdentifyStateJob();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||||
|
HDPowerViewHubHandler bridge = getBridgeHandler();
|
||||||
|
if (bridge == null) {
|
||||||
|
logger.warn("Missing bridge handler");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
HDPowerViewWebTargets webTargets = bridge.getWebTargets();
|
||||||
|
if (webTargets == null) {
|
||||||
|
logger.warn("Web targets not initialized");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
RepeaterData repeaterData;
|
||||||
|
|
||||||
|
switch (channelUID.getId()) {
|
||||||
|
case CHANNEL_REPEATER_IDENTIFY:
|
||||||
|
if (command instanceof StringType) {
|
||||||
|
if (COMMAND_IDENTIFY.equals(((StringType) command).toString())) {
|
||||||
|
repeaterData = webTargets.identifyRepeater(repeaterId);
|
||||||
|
scheduler.submit(() -> updatePropertyAndState(repeaterData));
|
||||||
|
cancelResetIdentifyStateJob();
|
||||||
|
resetIdentifyStateFuture = scheduler.schedule(() -> {
|
||||||
|
updateState(CHANNEL_REPEATER_IDENTIFY, UnDefType.UNDEF);
|
||||||
|
}, IDENTITY_PERIOD_SECONDS, TimeUnit.SECONDS);
|
||||||
|
} else {
|
||||||
|
logger.warn("Unsupported command: {}. Supported commands are: " + COMMAND_IDENTIFY,
|
||||||
|
command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CHANNEL_REPEATER_BLINKING_ENABLED:
|
||||||
|
repeaterData = webTargets.enableRepeaterBlinking(repeaterId, OnOffType.ON == command);
|
||||||
|
scheduler.submit(() -> updatePropertyAndState(repeaterData));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (HubInvalidResponseException e) {
|
||||||
|
Throwable cause = e.getCause();
|
||||||
|
if (cause == null) {
|
||||||
|
logger.warn("Bridge returned a bad JSON response: {}", e.getMessage());
|
||||||
|
} else {
|
||||||
|
logger.warn("Bridge returned a bad JSON response: {} -> {}", e.getMessage(), cause.getMessage());
|
||||||
|
}
|
||||||
|
} catch (HubMaintenanceException e) {
|
||||||
|
// exceptions are logged in HDPowerViewWebTargets
|
||||||
|
} catch (HubException e) {
|
||||||
|
logger.warn("Unexpected error: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cancelResetIdentifyStateJob() {
|
||||||
|
ScheduledFuture<?> scheduledJob = resetIdentifyStateFuture;
|
||||||
|
if (scheduledJob != null) {
|
||||||
|
scheduledJob.cancel(true);
|
||||||
|
}
|
||||||
|
resetIdentifyStateFuture = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void scheduleRefreshJob() {
|
||||||
|
cancelRefreshJob();
|
||||||
|
logger.debug("Scheduling poll for repeater {} now, then every {} minutes", repeaterId,
|
||||||
|
REFRESH_INTERVAL_MINUTES);
|
||||||
|
this.refreshStatusFuture = scheduler.scheduleWithFixedDelay(this::poll, 0, REFRESH_INTERVAL_MINUTES,
|
||||||
|
TimeUnit.MINUTES);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cancelRefreshJob() {
|
||||||
|
ScheduledFuture<?> future = this.refreshStatusFuture;
|
||||||
|
if (future != null) {
|
||||||
|
future.cancel(false);
|
||||||
|
}
|
||||||
|
this.refreshStatusFuture = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void poll() {
|
||||||
|
HDPowerViewHubHandler bridge = getBridgeHandler();
|
||||||
|
if (bridge == null) {
|
||||||
|
logger.warn("Missing bridge handler");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
HDPowerViewWebTargets webTargets = bridge.getWebTargets();
|
||||||
|
if (webTargets == null) {
|
||||||
|
logger.warn("Web targets not initialized");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
logger.debug("Polling for status information");
|
||||||
|
|
||||||
|
RepeaterData repeaterData = webTargets.getRepeater(repeaterId);
|
||||||
|
updatePropertyAndState(repeaterData);
|
||||||
|
|
||||||
|
} catch (HubInvalidResponseException e) {
|
||||||
|
Throwable cause = e.getCause();
|
||||||
|
if (cause == null) {
|
||||||
|
logger.warn("Bridge returned a bad JSON response: {}", e.getMessage());
|
||||||
|
} else {
|
||||||
|
logger.warn("Bridge returned a bad JSON response: {} -> {}", e.getMessage(), cause.getMessage());
|
||||||
|
}
|
||||||
|
} catch (HubMaintenanceException e) {
|
||||||
|
// exceptions are logged in HDPowerViewWebTargets
|
||||||
|
} catch (HubException e) {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updatePropertyAndState(RepeaterData repeaterData) {
|
||||||
|
updateStatus(ThingStatus.ONLINE);
|
||||||
|
|
||||||
|
Firmware firmware = repeaterData.firmware;
|
||||||
|
if (firmware != null) {
|
||||||
|
logger.debug("Repeater firmware version received: {}", firmware.toString());
|
||||||
|
updateProperty(Thing.PROPERTY_FIRMWARE_VERSION, firmware.toString());
|
||||||
|
} else {
|
||||||
|
logger.warn("Repeater firmware version missing in response");
|
||||||
|
}
|
||||||
|
|
||||||
|
updateState(CHANNEL_REPEATER_BLINKING_ENABLED, repeaterData.blinkEnabled ? OnOffType.ON : OnOffType.OFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -86,11 +86,10 @@ public class HDPowerViewShadeHandler extends AbstractHubbedThingHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
logger.debug("Initializing shade handler");
|
|
||||||
isDisposing = false;
|
isDisposing = false;
|
||||||
try {
|
shadeId = getConfigAs(HDPowerViewShadeConfiguration.class).id;
|
||||||
shadeId = getShadeId();
|
logger.debug("Initializing shade handler for shade {}", shadeId);
|
||||||
} catch (NumberFormatException e) {
|
if (shadeId <= 0) {
|
||||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||||
"@text/offline.conf-error.invalid-id");
|
"@text/offline.conf-error.invalid-id");
|
||||||
return;
|
return;
|
||||||
@ -459,14 +458,6 @@ public class HDPowerViewShadeHandler extends AbstractHubbedThingHandler {
|
|||||||
updatePositionStates(shadePosition);
|
updatePositionStates(shadePosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getShadeId() throws NumberFormatException {
|
|
||||||
String str = getConfigAs(HDPowerViewShadeConfiguration.class).id;
|
|
||||||
if (str == null) {
|
|
||||||
throw new NumberFormatException("null input string");
|
|
||||||
}
|
|
||||||
return Integer.parseInt(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request that the shade shall undergo a 'hard' refresh for querying its current position
|
* Request that the shade shall undergo a 'hard' refresh for querying its current position
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -7,6 +7,8 @@ binding.hdpowerview.description = The Hunter Douglas PowerView binding provides
|
|||||||
|
|
||||||
thing-type.hdpowerview.hub.label = PowerView Hub
|
thing-type.hdpowerview.hub.label = PowerView Hub
|
||||||
thing-type.hdpowerview.hub.description = Hunter Douglas (Luxaflex) PowerView Hub
|
thing-type.hdpowerview.hub.description = Hunter Douglas (Luxaflex) PowerView Hub
|
||||||
|
thing-type.hdpowerview.repeater.label = PowerView Repeater
|
||||||
|
thing-type.hdpowerview.repeater.description = Hunter Douglas (Luxaflex) PowerView Repeater
|
||||||
thing-type.hdpowerview.shade.label = PowerView Shade
|
thing-type.hdpowerview.shade.label = PowerView Shade
|
||||||
thing-type.hdpowerview.shade.description = Hunter Douglas (Luxaflex) PowerView Shade
|
thing-type.hdpowerview.shade.description = Hunter Douglas (Luxaflex) PowerView Shade
|
||||||
thing-type.hdpowerview.shade.channel.secondary.label = Secondary Position
|
thing-type.hdpowerview.shade.channel.secondary.label = Secondary Position
|
||||||
@ -22,6 +24,8 @@ thing-type.config.hdpowerview.hub.host.label = Host
|
|||||||
thing-type.config.hdpowerview.hub.host.description = The Host address of the PowerView Hub
|
thing-type.config.hdpowerview.hub.host.description = The Host address of the PowerView Hub
|
||||||
thing-type.config.hdpowerview.hub.refresh.label = Refresh Interval
|
thing-type.config.hdpowerview.hub.refresh.label = Refresh Interval
|
||||||
thing-type.config.hdpowerview.hub.refresh.description = The number of milliseconds between fetches of the PowerView Hub shade state
|
thing-type.config.hdpowerview.hub.refresh.description = The number of milliseconds between fetches of the PowerView Hub shade state
|
||||||
|
thing-type.config.hdpowerview.repeater.id.label = ID
|
||||||
|
thing-type.config.hdpowerview.repeater.id.description = The numeric ID of the PowerView Repeater in the Hub
|
||||||
thing-type.config.hdpowerview.shade.id.label = ID
|
thing-type.config.hdpowerview.shade.id.label = ID
|
||||||
thing-type.config.hdpowerview.shade.id.description = The numeric ID of the PowerView Shade in the Hub
|
thing-type.config.hdpowerview.shade.id.description = The numeric ID of the PowerView Shade in the Hub
|
||||||
|
|
||||||
@ -29,6 +33,11 @@ thing-type.config.hdpowerview.shade.id.description = The numeric ID of the Power
|
|||||||
|
|
||||||
channel-type.hdpowerview.battery-voltage.label = Battery Voltage
|
channel-type.hdpowerview.battery-voltage.label = Battery Voltage
|
||||||
channel-type.hdpowerview.battery-voltage.description = Battery voltage reported by the shade
|
channel-type.hdpowerview.battery-voltage.description = Battery voltage reported by the shade
|
||||||
|
channel-type.hdpowerview.repeater-blinking-enabled.label = Blinking Enabled
|
||||||
|
channel-type.hdpowerview.repeater-blinking-enabled.description = Blink during commands
|
||||||
|
channel-type.hdpowerview.repeater-identify.label = Identify
|
||||||
|
channel-type.hdpowerview.repeater-identify.description = Flash repeater to identify
|
||||||
|
channel-type.hdpowerview.repeater-identify.command.option.IDENTIFY = Identify
|
||||||
channel-type.hdpowerview.shade-calibrate.label = Calibrate
|
channel-type.hdpowerview.shade-calibrate.label = Calibrate
|
||||||
channel-type.hdpowerview.shade-calibrate.description = Perform calibration of the shade
|
channel-type.hdpowerview.shade-calibrate.description = Perform calibration of the shade
|
||||||
channel-type.hdpowerview.shade-position.label = Position
|
channel-type.hdpowerview.shade-position.label = Position
|
||||||
|
|||||||
@ -1,3 +1,16 @@
|
|||||||
|
# thing types
|
||||||
|
|
||||||
|
thing-type.hdpowerview.repeater.label = PowerView Repeater
|
||||||
|
thing-type.hdpowerview.repeater.description = Hunter Douglas (Luxaflex) PowerView Repeater
|
||||||
|
|
||||||
|
# channel types
|
||||||
|
|
||||||
|
channel-type.hdpowerview.repeater-blinking-enabled.label = Blink aktiveret
|
||||||
|
channel-type.hdpowerview.repeater-blinking-enabled.description = Blink når kommandoer aktiveres
|
||||||
|
channel-type.hdpowerview.repeater-identify.label = Identificer
|
||||||
|
channel-type.hdpowerview.repeater-identify.description = Identificer ved at lade repeater blinke
|
||||||
|
channel-type.hdpowerview.repeater-identify.command.option.IDENTIFY = Identificer
|
||||||
|
|
||||||
# dynamic channels
|
# dynamic channels
|
||||||
|
|
||||||
dynamic-channel.scene-activate.description = Aktiverer scenen ''{0}''
|
dynamic-channel.scene-activate.description = Aktiverer scenen ''{0}''
|
||||||
|
|||||||
@ -75,13 +75,40 @@
|
|||||||
<representation-property>id</representation-property>
|
<representation-property>id</representation-property>
|
||||||
|
|
||||||
<config-description>
|
<config-description>
|
||||||
<parameter name="id" type="text" required="true">
|
<parameter name="id" type="integer" min="1" required="true">
|
||||||
<label>ID</label>
|
<label>ID</label>
|
||||||
<description>The numeric ID of the PowerView Shade in the Hub</description>
|
<description>The numeric ID of the PowerView Shade in the Hub</description>
|
||||||
</parameter>
|
</parameter>
|
||||||
</config-description>
|
</config-description>
|
||||||
</thing-type>
|
</thing-type>
|
||||||
|
|
||||||
|
<thing-type id="repeater">
|
||||||
|
<supported-bridge-type-refs>
|
||||||
|
<bridge-type-ref id="hub"/>
|
||||||
|
</supported-bridge-type-refs>
|
||||||
|
<label>PowerView Repeater</label>
|
||||||
|
<description>Hunter Douglas (Luxaflex) PowerView Repeater</description>
|
||||||
|
|
||||||
|
<channels>
|
||||||
|
<channel id="identify" typeId="repeater-identify"/>
|
||||||
|
<channel id="blinkingEnabled" typeId="repeater-blinking-enabled"/>
|
||||||
|
</channels>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<property name="vendor">Hunter Douglas (Luxaflex)</property>
|
||||||
|
<property name="modelId">PowerView Repeater</property>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<representation-property>id</representation-property>
|
||||||
|
|
||||||
|
<config-description>
|
||||||
|
<parameter name="id" type="integer" min="1" required="true">
|
||||||
|
<label>ID</label>
|
||||||
|
<description>The numeric ID of the PowerView Repeater in the Hub</description>
|
||||||
|
</parameter>
|
||||||
|
</config-description>
|
||||||
|
</thing-type>
|
||||||
|
|
||||||
<channel-type id="shade-position">
|
<channel-type id="shade-position">
|
||||||
<item-type>Rollershutter</item-type>
|
<item-type>Rollershutter</item-type>
|
||||||
<label>Position</label>
|
<label>Position</label>
|
||||||
@ -122,6 +149,23 @@
|
|||||||
<state pattern="%.1f %unit%" readOnly="true"/>
|
<state pattern="%.1f %unit%" readOnly="true"/>
|
||||||
</channel-type>
|
</channel-type>
|
||||||
|
|
||||||
|
<channel-type id="repeater-identify">
|
||||||
|
<item-type>String</item-type>
|
||||||
|
<label>Identify</label>
|
||||||
|
<description>Flash repeater to identify</description>
|
||||||
|
<command>
|
||||||
|
<options>
|
||||||
|
<option value="IDENTIFY">Identify</option>
|
||||||
|
</options>
|
||||||
|
</command>
|
||||||
|
</channel-type>
|
||||||
|
|
||||||
|
<channel-type id="repeater-blinking-enabled">
|
||||||
|
<item-type>Switch</item-type>
|
||||||
|
<label>Blinking Enabled</label>
|
||||||
|
<description>Blink during commands</description>
|
||||||
|
</channel-type>
|
||||||
|
|
||||||
<channel-group-type id="scenes">
|
<channel-group-type id="scenes">
|
||||||
<label>Scenes</label>
|
<label>Scenes</label>
|
||||||
</channel-group-type>
|
</channel-group-type>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user