Skip to content

Commit

Permalink
[hdpowerview] Add support for enabling/disabling automations (openhab…
Browse files Browse the repository at this point in the history
…#11637)

* Add support for enabling/disabling automations.

Fixes openhab#11516

Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>

* Fix class description.

Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>

* Document automation channel and channel groups.

Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>

* Update scene example in documentation.

Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>

* Consolidate method for getting channel map.

Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>

* Extract channel updating from data fetching methods.

Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>

* Draft implementation of better automation description.

Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>

* Simplify and optimize building weekday string.

Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>

* Further simplify building weekday string.

Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>

* Update scheduled event channels when modified.

Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>

* Update scene channels when modified.

Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>

* Update scene group channels when modified.

Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>

* Fix cache synchronization during initialization.

Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>

* Reduced code duplication.

Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>

* Shorten time formatting.

Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>

* Danish translations for dynamic channels.

Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>

* Simplify, optimize and fix dynamic channel creation.

Channel order is now preserved when updating an existing channel.

Scenes and scene collection are sorted correctly.

Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>

* Provide backwards compatibility for deprecated channels.

Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>

* Document purpose of createDeprecatedSceneChannels.

Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>

* Cleaned up poll method for improved readability.

Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>

* Fix potential race condition when initialize() is called while updating channels.

Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>
Signed-off-by: Andras Uhrin <andras.uhrin@gmail.com>
  • Loading branch information
jlaur authored and andrasU committed Nov 12, 2022
1 parent 3e1def4 commit aa57b9a
Show file tree
Hide file tree
Showing 11 changed files with 629 additions and 97 deletions.
14 changes: 8 additions & 6 deletions bundles/org.openhab.binding.hdpowerview/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,15 @@ However, the configuration parameters are described below:

### Channels for PowerView Hub

Scene and scene group channels will be added dynamically to the binding as they are discovered in the hub.
Each scene/scene group channel will have an entry in the hub as shown below, whereby different scenes/scene groups
Scene, scene group and automation channels will be added dynamically to the binding as they are discovered in the hub.
Each will have an entry in the hub as shown below, whereby different scenes, scene groups and automations
have different `id` values:

| Channel | Item Type | Description |
|----------|-----------| ------------|
| id | Switch | Turning this to ON will activate the scene/scene group. Scenes/scene groups are stateless in the PowerView hub; they have no on/off state. Note: include `{autoupdate="false"}` in the item configuration to avoid having to reset it to off after use. |
| Channel Group | Channel | Item Type | Description |
|---------------|---------|-----------|-------------|
| scenes | id | Switch | Setting this to ON will activate the scene. Scenes are stateless in the PowerView hub; they have no on/off state. Note: include `{autoupdate="false"}` in the item configuration to avoid having to reset it to off after use. |
| sceneGroups | id | Switch | Setting this to ON will activate the scene group. Scene groups are stateless in the PowerView hub; they have no on/off state. Note: include `{autoupdate="false"}` in the item configuration to avoid having to reset it to off after use. |
| automations | id | Switch | Setting this to ON will enable the automation, while OFF will disable it. |

### Channels for PowerView Shade

Expand Down Expand Up @@ -181,7 +183,7 @@ Switch Living_Room_Shade_Battery_Low_Alarm "Living Room Shade Battery Low Alarm
Scene items:

```
Switch Living_Room_Shades_Scene_Heart "Living Room Shades Scene Heart" <blinds> (g_Shades_Scene_Trigger) {channel="hdpowerview:hub:g24:22663", autoupdate="false"}
Switch Living_Room_Shades_Scene_Heart "Living Room Shades Scene Heart" <blinds> (g_Shades_Scene_Trigger) {channel="hdpowerview:hub:g24:scenes#22663", autoupdate="false"}
```

### `demo.sitemap` File
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
*
* @author Andy Lintner - Initial contribution
* @author Andrew Fiddian-Green - Added support for secondary rail positions
* @author Jacob Laursen - Add support for scene groups
* @author Jacob Laursen - Add support for scene groups and automations
*/
@NonNullByDefault
public class HDPowerViewBindingConstants {
Expand All @@ -46,8 +46,13 @@ 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_GROUP_SCENES = "scenes";
public static final String CHANNEL_GROUP_SCENE_GROUPS = "sceneGroups";
public static final String CHANNEL_GROUP_AUTOMATIONS = "automations";

public static final String CHANNELTYPE_SCENE_ACTIVATE = "scene-activate";
public static final String CHANNELTYPE_SCENE_GROUP_ACTIVATE = "scene-group-activate";
public static final String CHANNELTYPE_AUTOMATION_ENABLED = "automation-enabled";

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
*/
package org.openhab.binding.hdpowerview.internal;

import java.util.Locale;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.i18n.LocaleProvider;
Expand Down Expand Up @@ -44,4 +46,8 @@ public String getText(String key, @Nullable Object... arguments) {
}
return key;
}

public Locale getLocale() {
return localeProvider.getLocale();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,23 @@
import org.openhab.binding.hdpowerview.internal.api.requests.ShadeStop;
import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections;
import org.openhab.binding.hdpowerview.internal.api.responses.Scenes;
import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents;
import org.openhab.binding.hdpowerview.internal.api.responses.Shade;
import org.openhab.binding.hdpowerview.internal.api.responses.Shades;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;

/**
* JAX-RS targets for communicating with an HD PowerView hub
*
* @author Andy Lintner - Initial contribution
* @author Andrew Fiddian-Green - Added support for secondary rail positions
* @author Jacob Laursen - Add support for scene groups
* @author Jacob Laursen - Add support for scene groups and automations
*/
@NonNullByDefault
public class HDPowerViewWebTargets {
Expand All @@ -65,6 +68,7 @@ public class HDPowerViewWebTargets {
private final String scenes;
private final String sceneCollectionActivate;
private final String sceneCollections;
private final String scheduledEvents;

private final Gson gson = new Gson();
private final HttpClient httpClient;
Expand Down Expand Up @@ -107,6 +111,7 @@ public HDPowerViewWebTargets(HttpClient httpClient, String ipAddress) {
scenes = base + "scenes/";
sceneCollectionActivate = base + "sceneCollections";
sceneCollections = base + "sceneCollections/";
scheduledEvents = base + "scheduledevents";
this.httpClient = httpClient;
}

Expand Down Expand Up @@ -189,6 +194,41 @@ public void activateSceneCollection(int sceneCollectionId) throws HubProcessingE
Query.of("sceneCollectionId", Integer.toString(sceneCollectionId)), null);
}

/**
* Fetches a JSON package that describes all scheduled events in the hub, and wraps it in
* a ScheduledEvents class instance
*
* @return ScheduledEvents class instance
* @throws JsonParseException if there is a JSON parsing error
* @throws HubProcessingException if there is any processing error
* @throws HubMaintenanceException if the hub is down for maintenance
*/
public @Nullable ScheduledEvents getScheduledEvents()
throws JsonParseException, HubProcessingException, HubMaintenanceException {
String json = invoke(HttpMethod.GET, scheduledEvents, null, null);
return gson.fromJson(json, ScheduledEvents.class);
}

/**
* Enables or disables a scheduled event in the hub.
*
* @param scheduledEventId id of the scheduled event to be enabled or disabled
* @param enable true to enable scheduled event, false to disable
* @throws JsonParseException if there is a JSON parsing error
* @throws JsonSyntaxException if there is a JSON syntax error
* @throws HubProcessingException if there is any processing error
* @throws HubMaintenanceException if the hub is down for maintenance
*/
public void enableScheduledEvent(int scheduledEventId, boolean enable)
throws JsonParseException, HubProcessingException, HubMaintenanceException {
String uri = scheduledEvents + "/" + scheduledEventId;
String json = invoke(HttpMethod.GET, uri, null, null);
JsonObject jsonObject = JsonParser.parseString(json).getAsJsonObject();
JsonObject scheduledEventObject = jsonObject.get("scheduledEvent").getAsJsonObject();
scheduledEventObject.addProperty("enabled", enable);
invoke(HttpMethod.PUT, uri, null, jsonObject.toString());
}

/**
* 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
Expand Up @@ -20,7 +20,7 @@
import org.eclipse.jdt.annotation.Nullable;

/**
* State of all Scenes in an HD PowerView hub
* State of all Scene Collections in an HD PowerView hub
*
* @author Jacob Laursen - Initial contribution
*/
Expand All @@ -38,13 +38,45 @@ public class SceneCollections {
*/
@SuppressWarnings("null")
@NonNullByDefault
public static class SceneCollection {
public static class SceneCollection implements Comparable<SceneCollection> {
public int id;
public @Nullable String name;
public int order;
public int colorId;
public int iconId;

@Override
public boolean equals(@Nullable Object o) {
if (o == this) {
return true;
}
if (!(o instanceof SceneCollection)) {
return false;
}
SceneCollection other = (SceneCollection) o;

return this.id == other.id && this.name.equals(other.name) && this.order == other.order
&& this.colorId == other.colorId && this.iconId == other.iconId;
}

@Override
public final int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + id;
result = prime * result + (name == null ? 0 : name.hashCode());
result = prime * result + order;
result = prime * result + colorId;
result = prime * result + iconId;

return result;
}

@Override
public int compareTo(SceneCollection other) {
return Integer.compare(order, other.order);
}

public String getName() {
return new String(Base64.getDecoder().decode(name), StandardCharsets.UTF_8);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,47 @@ public class Scenes {
*/
@SuppressWarnings("null")
@NonNullByDefault
public static class Scene {
public static class Scene implements Comparable<Scene> {
public int id;
public @Nullable String name;
public int roomId;
public int order;
public int colorId;
public int iconId;

@Override
public boolean equals(@Nullable Object o) {
if (o == this) {
return true;
}
if (!(o instanceof Scene)) {
return false;
}
Scene other = (Scene) o;

return this.id == other.id && this.name.equals(other.name) && this.roomId == other.roomId
&& this.order == other.order && this.colorId == other.colorId && this.iconId == other.iconId;
}

@Override
public final int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + id;
result = prime * result + (name == null ? 0 : name.hashCode());
result = prime * result + roomId;
result = prime * result + order;
result = prime * result + colorId;
result = prime * result + iconId;

return result;
}

@Override
public int compareTo(Scene other) {
return Integer.compare(order, other.order);
}

public String getName() {
return new String(Base64.getDecoder().decode(name), StandardCharsets.UTF_8);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.hdpowerview.internal.api.responses;

import java.time.DayOfWeek;
import java.util.EnumSet;
import java.util.List;

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

/**
* State of all Scheduled Events in an HD PowerView hub
*
* @author Jacob Laursen - Initial contribution
*/
@NonNullByDefault
public class ScheduledEvents {

public static final EnumSet<DayOfWeek> WEEKDAYS = EnumSet.of(DayOfWeek.MONDAY, DayOfWeek.TUESDAY,
DayOfWeek.WEDNESDAY, DayOfWeek.THURSDAY, DayOfWeek.FRIDAY);

public static final EnumSet<DayOfWeek> WEEKENDS = EnumSet.of(DayOfWeek.SATURDAY, DayOfWeek.SUNDAY);

public static final int SCHEDULED_EVENT_TYPE_TIME = 0;
public static final int SCHEDULED_EVENT_TYPE_SUNRISE = 1;
public static final int SCHEDULED_EVENT_TYPE_SUNSET = 2;

public @Nullable List<ScheduledEvent> scheduledEventData;
public @Nullable List<Integer> scheduledEventIds;

/*
* the following SuppressWarnings annotation is because the Eclipse compiler
* does NOT expect a NonNullByDefault annotation on the inner class, since it is
* implicitly inherited from the outer class, whereas the Maven compiler always
* requires an explicit NonNullByDefault annotation on all classes
*/
@SuppressWarnings("null")
@NonNullByDefault
public static class ScheduledEvent {
public int id;
public boolean enabled;
public int sceneId;
public int sceneCollectionId;
public boolean daySunday;
public boolean dayMonday;
public boolean dayTuesday;
public boolean dayWednesday;
public boolean dayThursday;
public boolean dayFriday;
public boolean daySaturday;
public int eventType;
public int hour;
public int minute;

@Override
public boolean equals(@Nullable Object o) {
if (o == this) {
return true;
}
if (!(o instanceof ScheduledEvent)) {
return false;
}
ScheduledEvent other = (ScheduledEvent) o;

return this.id == other.id && this.enabled == other.enabled && this.sceneId == other.sceneId
&& this.sceneCollectionId == other.sceneCollectionId && this.daySunday == other.daySunday
&& this.dayMonday == other.dayMonday && this.dayTuesday == other.dayTuesday
&& this.dayWednesday == other.dayWednesday && this.dayThursday == other.dayThursday
&& this.dayFriday == other.dayFriday && this.daySaturday == other.daySaturday
&& this.eventType == other.eventType && this.hour == other.hour && this.minute == other.minute;
}

@Override
public final int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + id;
result = prime * result + (enabled ? 1 : 0);
result = prime * result + sceneId;
result = prime * result + sceneCollectionId;
result = prime * result + (daySunday ? 1 : 0);
result = prime * result + (dayMonday ? 1 : 0);
result = prime * result + (dayTuesday ? 1 : 0);
result = prime * result + (dayWednesday ? 1 : 0);
result = prime * result + (dayThursday ? 1 : 0);
result = prime * result + (dayFriday ? 1 : 0);
result = prime * result + (daySaturday ? 1 : 0);
result = prime * result + eventType;
result = prime * result + hour;
result = prime * result + minute;

return result;
}

public EnumSet<DayOfWeek> getDays() {
EnumSet<DayOfWeek> days = EnumSet.noneOf(DayOfWeek.class);
if (daySunday) {
days.add(DayOfWeek.SUNDAY);
}
if (dayMonday) {
days.add(DayOfWeek.MONDAY);
}
if (dayTuesday) {
days.add(DayOfWeek.TUESDAY);
}
if (dayWednesday) {
days.add(DayOfWeek.WEDNESDAY);
}
if (dayThursday) {
days.add(DayOfWeek.THURSDAY);
}
if (dayFriday) {
days.add(DayOfWeek.FRIDAY);
}
if (daySaturday) {
days.add(DayOfWeek.SATURDAY);
}
return days;
}
}
}
Loading

0 comments on commit aa57b9a

Please sign in to comment.