Skip to content

Commit 24d96f2

Browse files
authored
[hue] Support software updating (#20498)
Signed-off-by: Andrew Fiddian-Green <software@whitebear.ch>
1 parent 55f424b commit 24d96f2

21 files changed

Lines changed: 1162 additions & 201 deletions

File tree

bundles/org.openhab.binding.hue/doc/readme_v2.md

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,10 @@ The configuration of all things (as described above) is the same regardless of w
6666

6767
Bridge Things support the following channels:
6868

69-
| Channel ID | Item Type | Description |
70-
|-------------------------------------------------|--------------------|---------------------------------------------|
71-
| automation#11111111-2222-3333-4444-555555555555 | Switch | Enable / disable the respective automation. |
69+
| Channel ID | Item Type | Description |
70+
|-------------------------------------------------|--------------------|------------------------------------------------------------------|
71+
| automation#11111111-2222-3333-4444-555555555555 | Switch | Enable / disable the respective automation. |
72+
| software#update-ready | Trigger | Sends an event when there is a software update ready to install. |
7273

7374
The Bridge dynamically creates `automation` channels corresponding to the automations in the Hue App;
7475
the '11111111-2222-3333-4444-555555555555' is the unique id of the respective automation.
@@ -243,6 +244,23 @@ openhab> openhab:hue hue:bridge-api2:g24 things > myThingsFile.things
243244

244245
## Rule Actions
245246

247+
### Software Update
248+
249+
The Bridge has a `software#update-ready` channel which is triggered if a software update is ready to install.
250+
This channel can be used in a rule to initiate a software update of the bridge or its connected `device` things.
251+
252+
```java
253+
rule "Install Software Update"
254+
when
255+
Channel 'hue:bridge-api2:g24:software#update-ready' triggered
256+
then
257+
val hueActions = getActions("hue","hue:bridge-api2:g24")
258+
hueActions.installUpdate()
259+
end
260+
```
261+
262+
### Dynamics
263+
246264
This binding includes a rule action, which implements dynamic (i.e. gradual) transitions to a new scene or light(s) state.
247265
Each thing has a separate action instance, which can be retrieved as follows.
248266

bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/HueBindingConstants.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ public class HueBindingConstants {
188188
public static final String CHANNEL_2_MOTION_AREA_ENABLED = "motion-area-enabled";
189189
public static final String CHANNEL_2_SECURITY_MOTION = "security-motion";
190190
public static final String CHANNEL_2_SECURITY_MOTION_LAST_UPDATED = "security-motion-last-updated";
191+
public static final String CHANNEL_2_UPDATE_READY_TO_INSTALL = "update-ready";
191192

192193
// channel IDs that (optionally) support dynamics
193194
public static final Set<String> DYNAMIC_CHANNELS = Set.of(CHANNEL_2_BRIGHTNESS, CHANNEL_2_COLOR,
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright (c) 2010-2026 Contributors to the openHAB project
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Eclipse Public License 2.0 which is available at
9+
* http://www.eclipse.org/legal/epl-2.0
10+
*
11+
* SPDX-License-Identifier: EPL-2.0
12+
*/
13+
package org.openhab.binding.hue.internal.action;
14+
15+
import org.eclipse.jdt.annotation.NonNullByDefault;
16+
import org.eclipse.jdt.annotation.Nullable;
17+
import org.openhab.binding.hue.internal.handler.Clip2BridgeHandler;
18+
import org.openhab.core.automation.annotation.ActionOutput;
19+
import org.openhab.core.automation.annotation.RuleAction;
20+
import org.openhab.core.thing.binding.ThingActions;
21+
import org.openhab.core.thing.binding.ThingActionsScope;
22+
import org.openhab.core.thing.binding.ThingHandler;
23+
import org.osgi.service.component.annotations.Component;
24+
import org.osgi.service.component.annotations.ServiceScope;
25+
import org.slf4j.Logger;
26+
import org.slf4j.LoggerFactory;
27+
28+
/**
29+
* Implementation of the {@link ThingActions} interface for software updating.
30+
*
31+
* @author Andrew Fiddian-Green - Initial contribution
32+
*/
33+
@Component(scope = ServiceScope.PROTOTYPE, service = SoftwareUpdateActions.class)
34+
@ThingActionsScope(name = "hue")
35+
@NonNullByDefault
36+
public class SoftwareUpdateActions implements ThingActions {
37+
38+
private final Logger logger = LoggerFactory.getLogger(SoftwareUpdateActions.class);
39+
private @Nullable Clip2BridgeHandler bridgeHandler;
40+
41+
public static String installUpdate(ThingActions actions) {
42+
if (actions instanceof SoftwareUpdateActions softwareUpdateActions) {
43+
return softwareUpdateActions.installUpdate();
44+
} else {
45+
throw new IllegalArgumentException("The 'actions' argument is not an instance of SoftwareUpdateActions");
46+
}
47+
}
48+
49+
@Override
50+
public @Nullable ThingHandler getThingHandler() {
51+
return bridgeHandler;
52+
}
53+
54+
@Override
55+
public void setThingHandler(@Nullable ThingHandler handler) {
56+
if (handler instanceof Clip2BridgeHandler bridgeHandler) {
57+
this.bridgeHandler = bridgeHandler;
58+
}
59+
}
60+
61+
@RuleAction(label = "@text/install.update.label", description = "@text/install.update.description")
62+
public @ActionOutput(type = "java.lang.String", label = "@text/install.update.result.label", description = "@text/install.update.result.description") String installUpdate() {
63+
Clip2BridgeHandler bridgeHandler = this.bridgeHandler;
64+
if (bridgeHandler == null) {
65+
logger.warn("No bridge handler available for software update action");
66+
return "";
67+
}
68+
return bridgeHandler.installUpdate();
69+
}
70+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright (c) 2010-2026 Contributors to the openHAB project
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Eclipse Public License 2.0 which is available at
9+
* http://www.eclipse.org/legal/epl-2.0
10+
*
11+
* SPDX-License-Identifier: EPL-2.0
12+
*/
13+
package org.openhab.binding.hue.internal.api.dto.clip1;
14+
15+
import java.util.HashMap;
16+
import java.util.Map;
17+
18+
import org.eclipse.jdt.annotation.NonNullByDefault;
19+
import org.eclipse.jdt.annotation.Nullable;
20+
import org.openhab.binding.hue.internal.api.dto.clip2.enums.UpdateStatusV2;
21+
22+
/**
23+
* A 'special' DTO for bridge discovery to read the configuration from a bridge using API v1.0+
24+
*
25+
* @see <a href="https://developers.meethue.com/develop/software-update/">Developer documentation</a>
26+
* @author Andrew Fiddian-Green - Initial contribution
27+
*/
28+
@NonNullByDefault
29+
public class BridgeConfig {
30+
private @Nullable String swversion;
31+
private @Nullable BridgeSwUpdate swupdate2; // for API v1.20+ ('swupdate' is deprecated)
32+
33+
public static final String V1_ANY_DEVICE = "device";
34+
public static final String V1_BRIDGE = "bridge";
35+
36+
public @Nullable String getSoftwareVersion() {
37+
return swversion;
38+
}
39+
40+
/**
41+
* Gets the update status map of the bridge and its 'any' devices, or an empty map if not available.
42+
*/
43+
public Map<String, @Nullable UpdateStatusV2> getUpdateStatusMap() {
44+
Map<String, @Nullable UpdateStatusV2> result = new HashMap<>();
45+
if (swupdate2 instanceof BridgeSwUpdate update) {
46+
result.put(V1_ANY_DEVICE, update.getUpdateStatus());
47+
result.put(V1_BRIDGE,
48+
(update.getBridge() instanceof BridgeSwUpdateBridge bridge) ? bridge.getUpdateStatus() : null);
49+
}
50+
return result;
51+
}
52+
53+
/**
54+
* Creates a swupdate2 field with install update flag set. Triggers the bridge to do an update.
55+
*/
56+
public BridgeConfig setInstallUpdate() {
57+
swupdate2 = new BridgeSwUpdate().setInstallUpdate();
58+
return this;
59+
}
60+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright (c) 2010-2026 Contributors to the openHAB project
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Eclipse Public License 2.0 which is available at
9+
* http://www.eclipse.org/legal/epl-2.0
10+
*
11+
* SPDX-License-Identifier: EPL-2.0
12+
*/
13+
package org.openhab.binding.hue.internal.api.dto.clip1;
14+
15+
import org.eclipse.jdt.annotation.NonNullByDefault;
16+
import org.eclipse.jdt.annotation.Nullable;
17+
import org.openhab.binding.hue.internal.api.dto.clip1.enums.UpdateStatusV1;
18+
import org.openhab.binding.hue.internal.api.dto.clip2.enums.UpdateStatusV2;
19+
20+
/**
21+
* DTO for an API v1.20+ bridge configuration response's software update part.
22+
*
23+
* @see <a href="https://developers.meethue.com/develop/software-update/">Developer documentation</a>
24+
* @author Andrew Fiddian-Green - Initial contribution
25+
*/
26+
@NonNullByDefault
27+
public class BridgeSwUpdate {
28+
private @SuppressWarnings("unused") @Nullable Boolean install;
29+
private @Nullable UpdateStatusV1 state;
30+
private @Nullable BridgeSwUpdateBridge bridge;
31+
32+
public @Nullable BridgeSwUpdateBridge getBridge() {
33+
return bridge;
34+
}
35+
36+
/**
37+
* Reads the update status of the devices in the bridge in v1 protocol form and converts it to v2 protocol form.
38+
*/
39+
public @Nullable UpdateStatusV2 getUpdateStatus() {
40+
return UpdateStatusV2.of(state);
41+
}
42+
43+
public BridgeSwUpdate setInstallUpdate() {
44+
install = true;
45+
return this;
46+
}
47+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright (c) 2010-2026 Contributors to the openHAB project
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Eclipse Public License 2.0 which is available at
9+
* http://www.eclipse.org/legal/epl-2.0
10+
*
11+
* SPDX-License-Identifier: EPL-2.0
12+
*/
13+
package org.openhab.binding.hue.internal.api.dto.clip1;
14+
15+
import org.eclipse.jdt.annotation.NonNullByDefault;
16+
import org.eclipse.jdt.annotation.Nullable;
17+
import org.openhab.binding.hue.internal.api.dto.clip1.enums.UpdateStatusV1;
18+
import org.openhab.binding.hue.internal.api.dto.clip2.enums.UpdateStatusV2;
19+
20+
/**
21+
* DTO for an API v1.20+ protocol bridge configuration response's software update bridge part.
22+
*
23+
* @see <a href="https://developers.meethue.com/develop/software-update/">Developer documentation</a>
24+
* @author Andrew Fiddian-Green - Initial contribution
25+
*/
26+
@NonNullByDefault
27+
public class BridgeSwUpdateBridge {
28+
private @Nullable UpdateStatusV1 state;
29+
30+
/**
31+
* Reads the update status of the bridge in v1 protocol form and converts it to v2 protocol form.
32+
*/
33+
public @Nullable UpdateStatusV2 getUpdateStatus() {
34+
return UpdateStatusV2.of(state);
35+
}
36+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright (c) 2010-2026 Contributors to the openHAB project
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Eclipse Public License 2.0 which is available at
9+
* http://www.eclipse.org/legal/epl-2.0
10+
*
11+
* SPDX-License-Identifier: EPL-2.0
12+
*/
13+
package org.openhab.binding.hue.internal.api.dto.clip1.enums;
14+
15+
import org.eclipse.jdt.annotation.NonNullByDefault;
16+
17+
import com.google.gson.annotations.SerializedName;
18+
19+
/**
20+
* Enum for bridge API v1.20+ software update status values.
21+
*
22+
* @see <a href="https://developers.meethue.com/develop/software-update/">Developer documentation</a>
23+
* @author Andrew Fiddian-Green - Initial contribution
24+
*/
25+
@NonNullByDefault
26+
public enum UpdateStatusV1 {
27+
@SerializedName("notupdatable")
28+
NOT_UPDATABLE,
29+
30+
@SerializedName("unknown")
31+
UNKNOWN,
32+
33+
@SerializedName("noupdates")
34+
NO_UPDATES,
35+
36+
@SerializedName("transferring")
37+
TRANSFERRING,
38+
39+
@SerializedName("downloading")
40+
DOWNLOADING,
41+
42+
@SerializedName("installing")
43+
INSTALLING,
44+
45+
@SerializedName("allreadytoinstall")
46+
ALL_READY_TO_INSTALL,
47+
48+
@SerializedName("anyreadytoinstall")
49+
ANY_READY_TO_INSTALL,
50+
51+
@SerializedName("readytoinstall")
52+
READY_TO_INSTALL;
53+
}

bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/api/dto/clip2/BridgeConfig.java

Lines changed: 0 additions & 26 deletions
This file was deleted.

bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/api/dto/clip2/Resource.java

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@
3535
import org.openhab.binding.hue.internal.api.dto.clip2.enums.SceneRecallAction;
3636
import org.openhab.binding.hue.internal.api.dto.clip2.enums.SmartSceneRecallAction;
3737
import org.openhab.binding.hue.internal.api.dto.clip2.enums.SmartSceneState;
38-
import org.openhab.binding.hue.internal.api.dto.clip2.enums.SoftwareUpdateStatusType;
3938
import org.openhab.binding.hue.internal.api.dto.clip2.enums.SoundValue;
4039
import org.openhab.binding.hue.internal.api.dto.clip2.enums.TamperStateType;
40+
import org.openhab.binding.hue.internal.api.dto.clip2.enums.UpdateStatusV2;
4141
import org.openhab.binding.hue.internal.api.dto.clip2.enums.ZigbeeStatus;
4242
import org.openhab.binding.hue.internal.exceptions.DTOPresentButEmptyException;
4343
import org.openhab.core.library.types.DateTimeType;
@@ -1038,23 +1038,24 @@ public State getSoundMuteState() {
10381038
* because both are represented by the 'state' JSON element. If the resource type is 'device_software_update' it
10391039
* is a software update status, if it is 'smart_scene' it is a smart scene state.
10401040
*/
1041-
public @Nullable SoftwareUpdateStatusType getSoftwareUpdateStatus() {
1042-
if (ResourceType.DEVICE_SOFTWARE_UPDATE == getType() && (state instanceof JsonPrimitive statePrimitive)) {
1043-
String state = statePrimitive.getAsString();
1044-
if (Objects.nonNull(state)) {
1045-
return SoftwareUpdateStatusType.of(state);
1041+
public @Nullable UpdateStatusV2 getUpdateStatus() {
1042+
if (ResourceType.DEVICE_SOFTWARE_UPDATE == getType() && state instanceof JsonPrimitive statePrimitive) {
1043+
try {
1044+
return GSON.fromJson(statePrimitive, UpdateStatusV2.class);
1045+
} catch (JsonSyntaxException e) {
1046+
return null;
10461047
}
10471048
}
10481049
return null;
10491050
}
10501051

10511052
/**
1052-
* Depending on the returned value from getSoftwareUpdateStatus() this method returns a StringType of the status
1053+
* Depending on the returned value from getUpdateStatus() this method returns a StringType of the status
10531054
* name, or 'UnDefType.NULL' if there is no such status.
10541055
*/
1055-
public State getSoftwareUpdateState() {
1056-
SoftwareUpdateStatusType softwareUpdateStatus = getSoftwareUpdateStatus();
1057-
return softwareUpdateStatus != null ? new StringType(softwareUpdateStatus.toString()) : UnDefType.NULL;
1056+
public State getUpdateState() {
1057+
UpdateStatusV2 updateStatus = getUpdateStatus();
1058+
return updateStatus != null ? new StringType(updateStatus.toString()) : UnDefType.NULL;
10581059
}
10591060

10601061
/**

0 commit comments

Comments
 (0)