diff --git a/org.openhab.binding.zigbee/META-INF/MANIFEST.MF b/org.openhab.binding.zigbee/META-INF/MANIFEST.MF index c240d2edf..e8db284e6 100644 --- a/org.openhab.binding.zigbee/META-INF/MANIFEST.MF +++ b/org.openhab.binding.zigbee/META-INF/MANIFEST.MF @@ -30,6 +30,7 @@ Import-Package: com.thoughtworks.xstream, javax.measure.quantity, org.apache.commons.io, org.eclipse.jdt.annotation;resolution:=optional, + org.eclipse.smarthome.automation.annotation;resolution:=optional, org.eclipse.smarthome.config.core, org.eclipse.smarthome.config.discovery, org.eclipse.smarthome.core.common.registry, diff --git a/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/action/ZigBeeThingActionParameter.java b/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/action/ZigBeeThingActionParameter.java new file mode 100644 index 000000000..d83161b16 --- /dev/null +++ b/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/action/ZigBeeThingActionParameter.java @@ -0,0 +1,25 @@ +package org.openhab.binding.zigbee.action; + +import java.util.Map; +import java.util.Optional; + +public class ZigBeeThingActionParameter { + private final String name; + private final Class klazz; + + public ZigBeeThingActionParameter(Class klazz, String name) { + this.klazz = klazz; + this.name = name; + } + + public String getName() { + return name; + } + + public Optional getFromMap(Map params) { + if (!params.containsKey(name)) { + return Optional.empty(); + } + return Optional.of(klazz.cast(params.get(name))); + } +} diff --git a/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/action/ZigBeeThingActionParameters.java b/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/action/ZigBeeThingActionParameters.java new file mode 100644 index 000000000..384336483 --- /dev/null +++ b/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/action/ZigBeeThingActionParameters.java @@ -0,0 +1,6 @@ +package org.openhab.binding.zigbee.action; + +public class ZigBeeThingActionParameters { + public static final ZigBeeThingActionParameter TRANSITION_TIME = + new ZigBeeThingActionParameter<>(Integer.class, "transitionTime"); +} diff --git a/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/converter/ZigBeeBaseChannelConverter.java b/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/converter/ZigBeeBaseChannelConverter.java index 6c5c64a4b..412b07bb1 100644 --- a/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/converter/ZigBeeBaseChannelConverter.java +++ b/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/converter/ZigBeeBaseChannelConverter.java @@ -234,7 +234,7 @@ public void handleRefresh() { * * @param command the {@link Command} to send */ - public void handleCommand(final Command command) { + public void handleCommand(final Command command, final Map params) { // Overridable if a channel can be commanded } diff --git a/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/handler/ZigBeeThingHandler.java b/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/handler/ZigBeeThingHandler.java index 211ede8dd..798f50fad 100644 --- a/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/handler/ZigBeeThingHandler.java +++ b/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/handler/ZigBeeThingHandler.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2010-2018 by the respective copyright holders. * * All rights reserved. This program and the accompanying materials @@ -42,6 +42,7 @@ import org.eclipse.smarthome.core.thing.ThingStatusDetail; import org.eclipse.smarthome.core.thing.ThingStatusInfo; import org.eclipse.smarthome.core.thing.binding.BaseThingHandler; +import org.eclipse.smarthome.core.thing.binding.ThingHandlerService; import org.eclipse.smarthome.core.thing.binding.builder.ThingBuilder; import org.eclipse.smarthome.core.thing.binding.firmware.Firmware; import org.eclipse.smarthome.core.thing.binding.firmware.FirmwareUpdateHandler; @@ -57,6 +58,7 @@ import org.openhab.binding.zigbee.converter.ZigBeeChannelConverterFactory; import org.openhab.binding.zigbee.discovery.ZigBeeNodePropertyDiscoverer; import org.openhab.binding.zigbee.internal.ZigBeeDeviceConfigHandler; +import org.openhab.binding.zigbee.internal.ZigbeeThingActions; import org.openhab.binding.zigbee.internal.converter.config.ZclClusterConfigFactory; import org.openhab.binding.zigbee.internal.converter.config.ZclClusterConfigHandler; import org.openhab.binding.zigbee.internal.converter.config.ZclReportingConfig; @@ -595,8 +597,13 @@ public void handleConfigurationUpdate(Map configurationParameter updateConfiguration(configuration); } + @Override public void handleCommand(final ChannelUID channelUID, final Command command) { + handleCommand(channelUID, command, Collections.emptyMap()); + } + + public void handleCommand(final ChannelUID channelUID, final Command command, final Map params) { logger.debug("{}: Command for channel {} --> {} [{}]", nodeIeeeAddress, channelUID, command, command.getClass().getSimpleName()); @@ -621,7 +628,7 @@ public void run() { if (command == RefreshType.REFRESH) { handler.handleRefresh(); } else { - handler.handleCommand(command); + handler.handleCommand(command, params); } } catch (Exception e) { logger.debug("{}: Exception sending command to channel {}", nodeIeeeAddress, channelUID, e); @@ -907,4 +914,9 @@ public boolean isUpdateExecutable() { // Always allow the firmware to be updated return true; } + + @Override + public Collection> getServices() { + return Collections.singleton(ZigbeeThingActions.class); + } } diff --git a/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/ZigbeeThingActions.java b/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/ZigbeeThingActions.java new file mode 100644 index 000000000..da858d2fc --- /dev/null +++ b/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/ZigbeeThingActions.java @@ -0,0 +1,76 @@ +package org.openhab.binding.zigbee.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.automation.annotation.ActionInput; +import org.eclipse.smarthome.automation.annotation.RuleAction; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.binding.ThingActions; +import org.eclipse.smarthome.core.thing.binding.ThingActionsScope; +import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.eclipse.smarthome.core.types.Command; +import org.openhab.binding.zigbee.action.ZigBeeThingActionParameter; +import org.openhab.binding.zigbee.handler.ZigBeeThingHandler; + +import java.util.HashMap; +import java.util.Map; + +@ThingActionsScope(name="zigbee") +@NonNullByDefault +public class ZigbeeThingActions implements ThingActions { + private ZigBeeThingHandler handler; + + @Override + public void setThingHandler(ThingHandler handler) { + this.handler = (ZigBeeThingHandler) handler; + } + + @Override + public ThingHandler getThingHandler() { + return handler; + } + + @RuleAction(label = "sendCommand") + public void sendCommand( + @ActionInput(name = "channelId") String channelId, + @ActionInput(name = "command") Command command, + @ActionInput(name = "params") Map params + ) { + ChannelUID channel = new ChannelUID(handler.getThing().getUID(), channelId); + handleCommand(channel, command, params); + } + + private void handleCommand(ChannelUID channel, Command command, Map params) { + handler.handleCommand(channel, command, params); + } + + @RuleAction(label = "buildCommand") + public CommandBuilder buildCommand( + @ActionInput(name = "channelId") String channelId, + @ActionInput(name = "command") Command command + ) { + ChannelUID channel = new ChannelUID(handler.getThing().getUID(), channelId); + return new CommandBuilder(this, channel, command); + } + + public static class CommandBuilder { + private final ZigbeeThingActions actions; + private final ChannelUID channel; + private final Command command; + private final Map params = new HashMap<>(); + + private CommandBuilder(ZigbeeThingActions actions, ChannelUID channel, Command command) { + this.actions = actions; + this.channel = channel; + this.command = command; + } + + public CommandBuilder with(ZigBeeThingActionParameter parameter, T value) { + params.put(parameter.getName(), value); + return this; + } + + public void send() { + actions.handleCommand(channel, command, params); + } + } +} diff --git a/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/converter/ZigBeeConverterColorColor.java b/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/converter/ZigBeeConverterColorColor.java index 37dc8303e..e6b2345be 100644 --- a/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/converter/ZigBeeConverterColorColor.java +++ b/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/converter/ZigBeeConverterColorColor.java @@ -28,6 +28,7 @@ import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.core.types.UnDefType; import org.openhab.binding.zigbee.ZigBeeBindingConstants; +import org.openhab.binding.zigbee.action.ZigBeeThingActionParameters; import org.openhab.binding.zigbee.converter.ZigBeeBaseChannelConverter; import org.openhab.binding.zigbee.internal.converter.config.ZclLevelControlConfig; import org.slf4j.Logger; @@ -246,14 +247,14 @@ public void handleRefresh() { clusterColorControl.getColorMode(0); } - private void changeOnOff(OnOffType onoff) throws InterruptedException, ExecutionException { + private void changeOnOff(OnOffType onoff, int transitionTime) throws InterruptedException, ExecutionException { boolean on = onoff == OnOffType.ON; if (clusterOnOff == null) { if (clusterLevelControl == null) { logger.warn("{}: ignoring on/off command", endpoint.getIeeeAddress()); } else { - changeBrightness(on ? PercentType.HUNDRED : PercentType.ZERO); + changeBrightness(on ? PercentType.HUNDRED : PercentType.ZERO, transitionTime); } return; } @@ -265,12 +266,12 @@ private void changeOnOff(OnOffType onoff) throws InterruptedException, Execution } } - private void changeBrightness(PercentType brightness) throws InterruptedException, ExecutionException { + private void changeBrightness(PercentType brightness, int transitionTime) throws InterruptedException, ExecutionException { if (clusterLevelControl == null) { if (clusterOnOff == null) { logger.warn("{}: ignoring brightness command", endpoint.getIeeeAddress()); } else { - changeOnOff(brightness.intValue() == 0 ? OnOffType.OFF : OnOffType.ON); + changeOnOff(brightness.intValue() == 0 ? OnOffType.OFF : OnOffType.ON, transitionTime); } return; } @@ -281,23 +282,23 @@ private void changeBrightness(PercentType brightness) throws InterruptedExceptio if (brightness.equals(PercentType.ZERO)) { clusterOnOff.offCommand(); } else { - clusterLevelControl.moveToLevelWithOnOffCommand(level, configLevelControl.getDefaultTransitionTime()) + clusterLevelControl.moveToLevelWithOnOffCommand(level, transitionTime) .get(); } } else { - clusterLevelControl.moveToLevelCommand(level, configLevelControl.getDefaultTransitionTime()).get(); + clusterLevelControl.moveToLevelCommand(level, transitionTime).get(); } } - private void changeColorHueSaturation(HSBType color) throws InterruptedException, ExecutionException { + private void changeColorHueSaturation(HSBType color, int transitionTime) throws InterruptedException, ExecutionException { int hue = (int) (color.getHue().floatValue() * 254.0f / 360.0f + 0.5f); int saturation = percentToLevel(color.getSaturation()); clusterColorControl - .moveToHueAndSaturationCommand(hue, saturation, configLevelControl.getDefaultTransitionTime()).get(); + .moveToHueAndSaturationCommand(hue, saturation, transitionTime).get(); } - private void changeColorXY(HSBType color) throws InterruptedException, ExecutionException { + private void changeColorXY(HSBType color, int transitionTime) throws InterruptedException, ExecutionException { PercentType xy[] = color.toXY(); logger.debug("{}: Change Color HSV ({}, {}, {}) -> XY ({}, {})", endpoint.getIeeeAddress(), color.getHue(), @@ -305,31 +306,32 @@ private void changeColorXY(HSBType color) throws InterruptedException, Execution int x = (int) (xy[0].floatValue() / 100.0f * 65536.0f + 0.5f); // up to 65279 int y = (int) (xy[1].floatValue() / 100.0f * 65536.0f + 0.5f); // up to 65279 - clusterColorControl.moveToColorCommand(x, y, configLevelControl.getDefaultTransitionTime()).get(); + clusterColorControl.moveToColorCommand(x, y, transitionTime).get(); } @Override - public void handleCommand(final Command command) { + public void handleCommand(final Command command, final Map params) { + int transitionTime = ZigBeeThingActionParameters.TRANSITION_TIME.getFromMap(params).orElseGet(configLevelControl::getDefaultTransitionTime); try { if (command instanceof HSBType) { HSBType color = (HSBType) command; PercentType brightness = color.getBrightness(); - changeBrightness(brightness); + changeBrightness(brightness, transitionTime); if (delayedColorChange && brightness.intValue() != lastHSB.getBrightness().intValue()) { Thread.sleep(1100); } if (supportsHue) { - changeColorHueSaturation(color); + changeColorHueSaturation(color, transitionTime); } else { - changeColorXY(color); + changeColorXY(color, transitionTime); } } else if (command instanceof PercentType) { - changeBrightness((PercentType) command); + changeBrightness((PercentType) command, transitionTime); } else if (command instanceof OnOffType) { - changeOnOff((OnOffType) command); + changeOnOff((OnOffType) command, transitionTime); } } catch (InterruptedException | ExecutionException e) { logger.warn("{}: Exception processing command", endpoint.getIeeeAddress(), e); diff --git a/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/converter/ZigBeeConverterColorTemperature.java b/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/converter/ZigBeeConverterColorTemperature.java index ce1c62e19..451dea61d 100644 --- a/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/converter/ZigBeeConverterColorTemperature.java +++ b/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/converter/ZigBeeConverterColorTemperature.java @@ -8,6 +8,7 @@ */ package org.openhab.binding.zigbee.internal.converter; +import java.util.Map; import java.util.concurrent.ExecutionException; import org.eclipse.smarthome.core.library.types.OnOffType; @@ -18,6 +19,7 @@ import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.core.types.UnDefType; import org.openhab.binding.zigbee.ZigBeeBindingConstants; +import org.openhab.binding.zigbee.action.ZigBeeThingActionParameters; import org.openhab.binding.zigbee.converter.ZigBeeBaseChannelConverter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -105,7 +107,8 @@ public void handleRefresh() { } @Override - public void handleCommand(final Command command) { + public void handleCommand(final Command command, final Map params) { + int transitionTime = ZigBeeThingActionParameters.TRANSITION_TIME.getFromMap(params).orElse(10); PercentType colorTemperaturePercentage = PercentType.ZERO; if (command instanceof PercentType) { colorTemperaturePercentage = (PercentType) command; @@ -114,7 +117,7 @@ public void handleCommand(final Command command) { return; } - clusterColorControl.moveToColorTemperatureCommand(percentToMired(colorTemperaturePercentage), 10); + clusterColorControl.moveToColorTemperatureCommand(percentToMired(colorTemperaturePercentage), transitionTime); } @Override diff --git a/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/converter/ZigBeeConverterDoorLock.java b/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/converter/ZigBeeConverterDoorLock.java index c9f2c2444..9572abb67 100644 --- a/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/converter/ZigBeeConverterDoorLock.java +++ b/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/converter/ZigBeeConverterDoorLock.java @@ -8,6 +8,7 @@ */ package org.openhab.binding.zigbee.internal.converter; +import java.util.Map; import java.util.concurrent.ExecutionException; import org.eclipse.smarthome.core.library.types.OnOffType; @@ -81,7 +82,7 @@ public void handleRefresh() { } @Override - public void handleCommand(final Command command) { + public void handleCommand(final Command command, final Map params) { if (command == OnOffType.ON) { cluster.lockDoorCommand(new ByteArray(new byte[0])); } else { diff --git a/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/converter/ZigBeeConverterSwitchLevel.java b/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/converter/ZigBeeConverterSwitchLevel.java index 73cae8afd..f832b36fb 100644 --- a/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/converter/ZigBeeConverterSwitchLevel.java +++ b/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/converter/ZigBeeConverterSwitchLevel.java @@ -145,7 +145,7 @@ public void handleRefresh() { } @Override - public void handleCommand(final Command command) { + public void handleCommand(final Command command, final Map params) { if (command instanceof OnOffType) { handleOnOffCommand((OnOffType) command); } else if (command instanceof PercentType) { diff --git a/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/converter/ZigBeeConverterSwitchOnoff.java b/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/converter/ZigBeeConverterSwitchOnoff.java index a202e6a42..1f424ee5a 100644 --- a/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/converter/ZigBeeConverterSwitchOnoff.java +++ b/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/converter/ZigBeeConverterSwitchOnoff.java @@ -9,6 +9,7 @@ package org.openhab.binding.zigbee.internal.converter; import java.util.ArrayList; +import java.util.Map; import java.util.concurrent.ExecutionException; import org.eclipse.smarthome.core.library.types.OnOffType; @@ -147,7 +148,7 @@ public void handleRefresh() { } @Override - public void handleCommand(final Command command) { + public void handleCommand(final Command command, Map params) { if (clusterOnOffServer == null) { logger.warn("{}: OnOff converter is not linked to a server and cannot accept commands", endpoint.getIeeeAddress());