Skip to content

Commit 86b8025

Browse files
committed
implement support for thing actions
So far only transition time for color channels is supported Signed-off-by: Thomas Weißschuh <[email protected]>
1 parent 3e23fdf commit 86b8025

18 files changed

+196
-30
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package org.openhab.binding.zigbee.action;
2+
3+
import java.util.Map;
4+
import java.util.Optional;
5+
6+
public interface ZigBeeThingActionParameter<T> {
7+
String getName();
8+
Optional<T> getFromMap(Map<String, Object> params);
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package org.openhab.binding.zigbee.action;
2+
3+
import org.openhab.binding.zigbee.action.internal.ZigBeeThingActionParameterImpl;
4+
5+
public final class ZigBeeThingActionParameters {
6+
private ZigBeeThingActionParameters() { }
7+
public static final ZigBeeThingActionParameter<Integer> TRANSITION_TIME =
8+
new ZigBeeThingActionParameterImpl<>(Integer.class, "transitionTime");
9+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package org.openhab.binding.zigbee.action;
2+
3+
import org.eclipse.jdt.annotation.NonNullByDefault;
4+
import org.eclipse.jdt.annotation.Nullable;
5+
import org.eclipse.smarthome.core.thing.ChannelUID;
6+
import org.eclipse.smarthome.core.thing.binding.ThingActions;
7+
import org.eclipse.smarthome.core.thing.binding.ThingActionsScope;
8+
import org.eclipse.smarthome.core.thing.binding.ThingHandler;
9+
import org.eclipse.smarthome.core.types.Command;
10+
import org.openhab.binding.zigbee.handler.ZigBeeThingHandler;
11+
import org.openhab.core.automation.annotation.ActionInput;
12+
import org.openhab.core.automation.annotation.RuleAction;
13+
14+
import java.util.HashMap;
15+
import java.util.Map;
16+
17+
@SuppressWarnings("unused")
18+
@ThingActionsScope(name="zigbee")
19+
@NonNullByDefault
20+
public class ZigBeeThingActions implements ThingActions {
21+
@Nullable
22+
private ZigBeeThingHandler handler;
23+
24+
@Override
25+
public void setThingHandler(@Nullable ThingHandler handler) {
26+
this.handler = (ZigBeeThingHandler) handler;
27+
}
28+
29+
@Override
30+
@Nullable
31+
public ThingHandler getThingHandler() {
32+
return handler;
33+
}
34+
35+
@RuleAction(label = "sendCommand")
36+
public void sendCommand(
37+
@ActionInput(name = "channelId", required = true) String channelId,
38+
@ActionInput(name = "command", required = true) Command command,
39+
@ActionInput(name = "params", required = true) Map<String, Object> params
40+
) {
41+
ChannelUID channel = new ChannelUID(handler.getThing().getUID(), channelId);
42+
handleCommand(channel, command, params);
43+
}
44+
45+
private void handleCommand(ChannelUID channel, Command command, Map<String, Object> params) {
46+
handler.handleCommand(channel, command, params);
47+
}
48+
49+
@RuleAction(label = "buildCommand")
50+
public CommandBuilder buildCommand(
51+
@ActionInput(name = "channelId", required = true) String channelId,
52+
@ActionInput(name = "command", required = true) Command command
53+
) {
54+
ChannelUID channel = new ChannelUID(handler.getThing().getUID(), channelId);
55+
return new CommandBuilder(this, channel, command);
56+
}
57+
58+
public static class CommandBuilder {
59+
private final ZigBeeThingActions actions;
60+
private final ChannelUID channel;
61+
private final Command command;
62+
private final Map<String, Object> params = new HashMap<>();
63+
64+
private CommandBuilder(ZigBeeThingActions actions, ChannelUID channel, Command command) {
65+
this.actions = actions;
66+
this.channel = channel;
67+
this.command = command;
68+
}
69+
70+
public <T> CommandBuilder with(ZigBeeThingActionParameter<T> parameter, T value) {
71+
params.put(parameter.getName(), value);
72+
return this;
73+
}
74+
75+
public void send() {
76+
actions.handleCommand(channel, command, params);
77+
}
78+
}
79+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package org.openhab.binding.zigbee.action.internal;
2+
3+
import org.openhab.binding.zigbee.action.ZigBeeThingActionParameter;
4+
import org.slf4j.Logger;
5+
import org.slf4j.LoggerFactory;
6+
7+
import java.util.Map;
8+
import java.util.Optional;
9+
10+
public final class ZigBeeThingActionParameterImpl<T> implements ZigBeeThingActionParameter<T> {
11+
private final Logger logger = LoggerFactory.getLogger(ZigBeeThingActionParameterImpl.class);
12+
13+
private final String name;
14+
private final Class<T> klazz;
15+
16+
public ZigBeeThingActionParameterImpl(Class<T> klazz, String name) {
17+
this.klazz = klazz;
18+
this.name = name;
19+
}
20+
21+
@Override
22+
public String getName() {
23+
return name;
24+
}
25+
26+
@Override
27+
public Optional<T> getFromMap(Map<String, Object> params) {
28+
Object param = params.get(getName());
29+
if (param == null) {
30+
return Optional.empty();
31+
} else if (!klazz.isInstance(param)) {
32+
logger.debug("Can not retrieve param {}: object of type {} ({}) can not be casted to {}",
33+
getName(), param.getClass(), (param), klazz.getName()
34+
);
35+
return Optional.empty();
36+
} else {
37+
return Optional.of(klazz.cast(param));
38+
}
39+
}
40+
}

org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/converter/ZigBeeBaseChannelConverter.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import java.math.BigDecimal;
1616
import java.math.RoundingMode;
17+
import java.util.Collections;
1718
import java.util.HashMap;
1819
import java.util.List;
1920
import java.util.Map;
@@ -251,10 +252,14 @@ public void handleRefresh() {
251252
*
252253
* @param command the {@link Command} to send
253254
*/
254-
public void handleCommand(final Command command) {
255+
public void handleCommand(final Command command, final Map<String, Object> params) {
255256
// Overridable if a channel can be commanded
256257
}
257258

259+
public final void handleCommand(final Command command) {
260+
handleCommand(command, Collections.emptyMap());
261+
}
262+
258263
/**
259264
* Creates a {@link Channel} if this converter supports features from the {@link ZigBeeEndpoint}
260265
* If the converter doesn't support any features, it returns null.

org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/handler/ZigBeeThingHandler.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
import org.eclipse.smarthome.core.thing.ThingStatusDetail;
4949
import org.eclipse.smarthome.core.thing.ThingStatusInfo;
5050
import org.eclipse.smarthome.core.thing.binding.BaseThingHandler;
51+
import org.eclipse.smarthome.core.thing.binding.ThingHandlerService;
5152
import org.eclipse.smarthome.core.thing.binding.builder.ThingBuilder;
5253
import org.eclipse.smarthome.core.thing.binding.firmware.Firmware;
5354
import org.eclipse.smarthome.core.thing.binding.firmware.FirmwareUpdateHandler;
@@ -64,6 +65,7 @@
6465
import org.openhab.binding.zigbee.discovery.ZigBeeNodePropertyDiscoverer;
6566
import org.openhab.binding.zigbee.internal.ZigBeeConfigDescriptionParameters;
6667
import org.openhab.binding.zigbee.internal.ZigBeeDeviceConfigHandler;
68+
import org.openhab.binding.zigbee.action.ZigBeeThingActions;
6769
import org.openhab.binding.zigbee.internal.converter.config.ZclClusterConfigFactory;
6870
import org.openhab.binding.zigbee.internal.converter.config.ZclClusterConfigHandler;
6971
import org.openhab.binding.zigbee.internal.converter.config.ZclReportingConfig;
@@ -692,8 +694,13 @@ public void handleConfigurationUpdate(Map<String, Object> configurationParameter
692694
}
693695
}
694696

697+
695698
@Override
696699
public void handleCommand(final ChannelUID channelUID, final Command command) {
700+
handleCommand(channelUID, command, Collections.emptyMap());
701+
}
702+
703+
public void handleCommand(final ChannelUID channelUID, final Command command, final Map<String, Object> params) {
697704
logger.debug("{}: Command for channel {} --> {} [{}]", nodeIeeeAddress, channelUID, command,
698705
command.getClass().getSimpleName());
699706

@@ -718,7 +725,7 @@ public void run() {
718725
if (command == RefreshType.REFRESH) {
719726
handler.handleRefresh();
720727
} else {
721-
handler.handleCommand(command);
728+
handler.handleCommand(command, params);
722729
}
723730
} catch (Exception e) {
724731
logger.debug("{}: Exception sending command to channel {}", nodeIeeeAddress, channelUID, e);
@@ -1006,4 +1013,9 @@ public boolean isUpdateExecutable() {
10061013
// Always allow the firmware to be updated
10071014
return true;
10081015
}
1016+
1017+
@Override
1018+
public Collection<Class<? extends ThingHandlerService>> getServices() {
1019+
return Collections.singleton(ZigBeeThingActions.class);
1020+
}
10091021
}

org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/converter/ZigBeeConverterColorColor.java

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import org.eclipse.smarthome.core.types.Command;
3535
import org.eclipse.smarthome.core.types.UnDefType;
3636
import org.openhab.binding.zigbee.ZigBeeBindingConstants;
37+
import org.openhab.binding.zigbee.action.ZigBeeThingActionParameters;
3738
import org.openhab.binding.zigbee.converter.ZigBeeBaseChannelConverter;
3839
import org.openhab.binding.zigbee.internal.converter.config.ZclLevelControlConfig;
3940
import org.slf4j.Logger;
@@ -274,14 +275,14 @@ public void handleRefresh() {
274275
clusterColorControl.getColorMode(0);
275276
}
276277

277-
private void changeOnOff(OnOffType onoff) throws InterruptedException, ExecutionException {
278+
private void changeOnOff(OnOffType onoff, int transitionTime) throws InterruptedException, ExecutionException {
278279
boolean on = onoff == OnOffType.ON;
279280

280281
if (clusterOnOff == null) {
281282
if (clusterLevelControl == null) {
282283
logger.warn("{}: ignoring on/off command", endpoint.getIeeeAddress());
283284
} else {
284-
changeBrightness(on ? PercentType.HUNDRED : PercentType.ZERO);
285+
changeBrightness(on ? PercentType.HUNDRED : PercentType.ZERO, transitionTime);
285286
}
286287
return;
287288
}
@@ -293,12 +294,12 @@ private void changeOnOff(OnOffType onoff) throws InterruptedException, Execution
293294
}
294295
}
295296

296-
private void changeBrightness(PercentType brightness) throws InterruptedException, ExecutionException {
297+
private void changeBrightness(PercentType brightness, int transitionTime) throws InterruptedException, ExecutionException {
297298
if (clusterLevelControl == null) {
298299
if (clusterOnOff == null) {
299300
logger.warn("{}: ignoring brightness command", endpoint.getIeeeAddress());
300301
} else {
301-
changeOnOff(brightness.intValue() == 0 ? OnOffType.OFF : OnOffType.ON);
302+
changeOnOff(brightness.intValue() == 0 ? OnOffType.OFF : OnOffType.ON, transitionTime);
302303
}
303304
return;
304305
}
@@ -309,55 +310,56 @@ private void changeBrightness(PercentType brightness) throws InterruptedExceptio
309310
if (brightness.equals(PercentType.ZERO)) {
310311
clusterOnOff.offCommand();
311312
} else {
312-
clusterLevelControl.moveToLevelWithOnOffCommand(level, configLevelControl.getDefaultTransitionTime())
313+
clusterLevelControl.moveToLevelWithOnOffCommand(level, transitionTime)
313314
.get();
314315
}
315316
} else {
316-
clusterLevelControl.moveToLevelCommand(level, configLevelControl.getDefaultTransitionTime()).get();
317+
clusterLevelControl.moveToLevelCommand(level, transitionTime).get();
317318
}
318319
}
319320

320-
private void changeColorHueSaturation(HSBType color) throws InterruptedException, ExecutionException {
321+
private void changeColorHueSaturation(HSBType color, int transitionTime) throws InterruptedException, ExecutionException {
321322
int hue = (int) (color.getHue().floatValue() * 254.0f / 360.0f + 0.5f);
322323
int saturation = percentToLevel(color.getSaturation());
323324

324325
clusterColorControl
325-
.moveToHueAndSaturationCommand(hue, saturation, configLevelControl.getDefaultTransitionTime()).get();
326+
.moveToHueAndSaturationCommand(hue, saturation, transitionTime).get();
326327
}
327328

328-
private void changeColorXY(HSBType color) throws InterruptedException, ExecutionException {
329+
private void changeColorXY(HSBType color, int transitionTime) throws InterruptedException, ExecutionException {
329330
PercentType xy[] = color.toXY();
330331

331332
logger.debug("{}: Change Color HSV ({}, {}, {}) -> XY ({}, {})", endpoint.getIeeeAddress(), color.getHue(),
332333
color.getSaturation(), color.getBrightness(), xy[0], xy[1]);
333334
int x = (int) (xy[0].floatValue() / 100.0f * 65536.0f + 0.5f); // up to 65279
334335
int y = (int) (xy[1].floatValue() / 100.0f * 65536.0f + 0.5f); // up to 65279
335336

336-
clusterColorControl.moveToColorCommand(x, y, configLevelControl.getDefaultTransitionTime()).get();
337+
clusterColorControl.moveToColorCommand(x, y, transitionTime).get();
337338
}
338339

339340
@Override
340-
public void handleCommand(final Command command) {
341+
public void handleCommand(final Command command, final Map<String, Object> params) {
342+
int transitionTime = ZigBeeThingActionParameters.TRANSITION_TIME.getFromMap(params).orElseGet(configLevelControl::getDefaultTransitionTime);
341343
try {
342344
if (command instanceof HSBType) {
343345
HSBType color = (HSBType) command;
344346
PercentType brightness = color.getBrightness();
345347

346-
changeBrightness(brightness);
348+
changeBrightness(brightness, transitionTime);
347349

348350
if (delayedColorChange && brightness.intValue() != lastHSB.getBrightness().intValue()) {
349351
Thread.sleep(1100);
350352
}
351353

352354
if (supportsHue) {
353-
changeColorHueSaturation(color);
355+
changeColorHueSaturation(color, transitionTime);
354356
} else {
355-
changeColorXY(color);
357+
changeColorXY(color, transitionTime);
356358
}
357359
} else if (command instanceof PercentType) {
358-
changeBrightness((PercentType) command);
360+
changeBrightness((PercentType) command, transitionTime);
359361
} else if (command instanceof OnOffType) {
360-
changeOnOff((OnOffType) command);
362+
changeOnOff((OnOffType) command, transitionTime);
361363
}
362364
} catch (InterruptedException | ExecutionException e) {
363365
logger.warn("{}: Exception processing command", endpoint.getIeeeAddress(), e);

org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/converter/ZigBeeConverterColorTemperature.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import static com.zsmartsystems.zigbee.zcl.clusters.ZclColorControlCluster.ATTR_COLORTEMPERATURE;
1616

17+
import java.util.Map;
1718
import java.util.concurrent.ExecutionException;
1819

1920
import org.eclipse.smarthome.core.library.types.OnOffType;
@@ -24,6 +25,7 @@
2425
import org.eclipse.smarthome.core.types.Command;
2526
import org.eclipse.smarthome.core.types.UnDefType;
2627
import org.openhab.binding.zigbee.ZigBeeBindingConstants;
28+
import org.openhab.binding.zigbee.action.ZigBeeThingActionParameters;
2729
import org.openhab.binding.zigbee.converter.ZigBeeBaseChannelConverter;
2830
import org.slf4j.Logger;
2931
import org.slf4j.LoggerFactory;
@@ -119,7 +121,8 @@ public void handleRefresh() {
119121
}
120122

121123
@Override
122-
public void handleCommand(final Command command) {
124+
public void handleCommand(final Command command, final Map<String, Object> params) {
125+
int transitionTime = ZigBeeThingActionParameters.TRANSITION_TIME.getFromMap(params).orElse(10);
123126
PercentType colorTemperaturePercentage = PercentType.ZERO;
124127
if (command instanceof PercentType) {
125128
colorTemperaturePercentage = (PercentType) command;
@@ -128,7 +131,7 @@ public void handleCommand(final Command command) {
128131
return;
129132
}
130133

131-
clusterColorControl.moveToColorTemperatureCommand(percentToMired(colorTemperaturePercentage), 10);
134+
clusterColorControl.moveToColorTemperatureCommand(percentToMired(colorTemperaturePercentage), transitionTime);
132135
}
133136

134137
@Override

org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/converter/ZigBeeConverterDoorLock.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
*/
1313
package org.openhab.binding.zigbee.internal.converter;
1414

15+
import java.util.Map;
1516
import java.util.concurrent.ExecutionException;
1617

1718
import org.eclipse.smarthome.core.library.types.OnOffType;
@@ -95,7 +96,7 @@ public void handleRefresh() {
9596
}
9697

9798
@Override
98-
public void handleCommand(final Command command) {
99+
public void handleCommand(final Command command, final Map<String, Object> params) {
99100
if (command == OnOffType.ON) {
100101
cluster.lockDoorCommand(new ByteArray(new byte[0]));
101102
} else {

org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/converter/ZigBeeConverterFanControl.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
*/
1313
package org.openhab.binding.zigbee.internal.converter;
1414

15+
import java.util.Map;
1516
import java.util.concurrent.ExecutionException;
1617

1718
import org.eclipse.smarthome.core.library.types.DecimalType;
@@ -106,7 +107,7 @@ public void handleRefresh() {
106107
}
107108

108109
@Override
109-
public void handleCommand(final Command command) {
110+
public void handleCommand(final Command command, Map<String, Object> params) {
110111
int value;
111112
if (command instanceof OnOffType) {
112113
value = command == OnOffType.ON ? MODE_ON : MODE_OFF;

0 commit comments

Comments
 (0)