Skip to content

Commit

Permalink
implement support for thing actions
Browse files Browse the repository at this point in the history
So far only transition time for color channels is supported

Signed-off-by: Thomas Weißschuh <[email protected]>
  • Loading branch information
t-8ch committed Dec 23, 2019
1 parent 3e23fdf commit 86b8025
Show file tree
Hide file tree
Showing 18 changed files with 196 additions and 30 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.openhab.binding.zigbee.action;

import java.util.Map;
import java.util.Optional;

public interface ZigBeeThingActionParameter<T> {
String getName();
Optional<T> getFromMap(Map<String, Object> params);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.openhab.binding.zigbee.action;

import org.openhab.binding.zigbee.action.internal.ZigBeeThingActionParameterImpl;

public final class ZigBeeThingActionParameters {
private ZigBeeThingActionParameters() { }
public static final ZigBeeThingActionParameter<Integer> TRANSITION_TIME =
new ZigBeeThingActionParameterImpl<>(Integer.class, "transitionTime");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package org.openhab.binding.zigbee.action;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
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.handler.ZigBeeThingHandler;
import org.openhab.core.automation.annotation.ActionInput;
import org.openhab.core.automation.annotation.RuleAction;

import java.util.HashMap;
import java.util.Map;

@SuppressWarnings("unused")
@ThingActionsScope(name="zigbee")
@NonNullByDefault
public class ZigBeeThingActions implements ThingActions {
@Nullable
private ZigBeeThingHandler handler;

@Override
public void setThingHandler(@Nullable ThingHandler handler) {
this.handler = (ZigBeeThingHandler) handler;
}

@Override
@Nullable
public ThingHandler getThingHandler() {
return handler;
}

@RuleAction(label = "sendCommand")
public void sendCommand(
@ActionInput(name = "channelId", required = true) String channelId,
@ActionInput(name = "command", required = true) Command command,
@ActionInput(name = "params", required = true) Map<String, Object> params
) {
ChannelUID channel = new ChannelUID(handler.getThing().getUID(), channelId);
handleCommand(channel, command, params);
}

private void handleCommand(ChannelUID channel, Command command, Map<String, Object> params) {
handler.handleCommand(channel, command, params);
}

@RuleAction(label = "buildCommand")
public CommandBuilder buildCommand(
@ActionInput(name = "channelId", required = true) String channelId,
@ActionInput(name = "command", required = true) 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<String, Object> params = new HashMap<>();

private CommandBuilder(ZigBeeThingActions actions, ChannelUID channel, Command command) {
this.actions = actions;
this.channel = channel;
this.command = command;
}

public <T> CommandBuilder with(ZigBeeThingActionParameter<T> parameter, T value) {
params.put(parameter.getName(), value);
return this;
}

public void send() {
actions.handleCommand(channel, command, params);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package org.openhab.binding.zigbee.action.internal;

import org.openhab.binding.zigbee.action.ZigBeeThingActionParameter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;
import java.util.Optional;

public final class ZigBeeThingActionParameterImpl<T> implements ZigBeeThingActionParameter<T> {
private final Logger logger = LoggerFactory.getLogger(ZigBeeThingActionParameterImpl.class);

private final String name;
private final Class<T> klazz;

public ZigBeeThingActionParameterImpl(Class<T> klazz, String name) {
this.klazz = klazz;
this.name = name;
}

@Override
public String getName() {
return name;
}

@Override
public Optional<T> getFromMap(Map<String, Object> params) {
Object param = params.get(getName());
if (param == null) {
return Optional.empty();
} else if (!klazz.isInstance(param)) {
logger.debug("Can not retrieve param {}: object of type {} ({}) can not be casted to {}",
getName(), param.getClass(), (param), klazz.getName()
);
return Optional.empty();
} else {
return Optional.of(klazz.cast(param));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -251,10 +252,14 @@ public void handleRefresh() {
*
* @param command the {@link Command} to send
*/
public void handleCommand(final Command command) {
public void handleCommand(final Command command, final Map<String, Object> params) {
// Overridable if a channel can be commanded
}

public final void handleCommand(final Command command) {
handleCommand(command, Collections.emptyMap());
}

/**
* Creates a {@link Channel} if this converter supports features from the {@link ZigBeeEndpoint}
* If the converter doesn't support any features, it returns null.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,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;
Expand All @@ -64,6 +65,7 @@
import org.openhab.binding.zigbee.discovery.ZigBeeNodePropertyDiscoverer;
import org.openhab.binding.zigbee.internal.ZigBeeConfigDescriptionParameters;
import org.openhab.binding.zigbee.internal.ZigBeeDeviceConfigHandler;
import org.openhab.binding.zigbee.action.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;
Expand Down Expand Up @@ -692,8 +694,13 @@ public void handleConfigurationUpdate(Map<String, Object> configurationParameter
}
}


@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<String, Object> params) {
logger.debug("{}: Command for channel {} --> {} [{}]", nodeIeeeAddress, channelUID, command,
command.getClass().getSimpleName());

Expand All @@ -718,7 +725,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);
Expand Down Expand Up @@ -1006,4 +1013,9 @@ public boolean isUpdateExecutable() {
// Always allow the firmware to be updated
return true;
}

@Override
public Collection<Class<? extends ThingHandlerService>> getServices() {
return Collections.singleton(ZigBeeThingActions.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,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;
Expand Down Expand Up @@ -274,14 +275,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;
}
Expand All @@ -293,12 +294,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;
}
Expand All @@ -309,55 +310,56 @@ 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(),
color.getSaturation(), color.getBrightness(), xy[0], xy[1]);
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<String, Object> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

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

import java.util.Map;
import java.util.concurrent.ExecutionException;

import org.eclipse.smarthome.core.library.types.OnOffType;
Expand All @@ -24,6 +25,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;
Expand Down Expand Up @@ -119,7 +121,8 @@ public void handleRefresh() {
}

@Override
public void handleCommand(final Command command) {
public void handleCommand(final Command command, final Map<String, Object> params) {
int transitionTime = ZigBeeThingActionParameters.TRANSITION_TIME.getFromMap(params).orElse(10);
PercentType colorTemperaturePercentage = PercentType.ZERO;
if (command instanceof PercentType) {
colorTemperaturePercentage = (PercentType) command;
Expand All @@ -128,7 +131,7 @@ public void handleCommand(final Command command) {
return;
}

clusterColorControl.moveToColorTemperatureCommand(percentToMired(colorTemperaturePercentage), 10);
clusterColorControl.moveToColorTemperatureCommand(percentToMired(colorTemperaturePercentage), transitionTime);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,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;
Expand Down Expand Up @@ -95,7 +96,7 @@ public void handleRefresh() {
}

@Override
public void handleCommand(final Command command) {
public void handleCommand(final Command command, final Map<String, Object> params) {
if (command == OnOffType.ON) {
cluster.lockDoorCommand(new ByteArray(new byte[0]));
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
*/
package org.openhab.binding.zigbee.internal.converter;

import java.util.Map;
import java.util.concurrent.ExecutionException;

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

@Override
public void handleCommand(final Command command) {
public void handleCommand(final Command command, Map<String, Object> params) {
int value;
if (command instanceof OnOffType) {
value = command == OnOffType.ON ? MODE_ON : MODE_OFF;
Expand Down
Loading

0 comments on commit 86b8025

Please sign in to comment.