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 <thomas@weissschuh.net>
  • Loading branch information
t-8ch committed Dec 26, 2019
1 parent 3c1417f commit d802eed
Show file tree
Hide file tree
Showing 19 changed files with 205 additions and 34 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.openhab.binding.zigbee;

import org.eclipse.jdt.annotation.NonNullByDefault;

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

@NonNullByDefault
public interface ZigBeeCommandParameter<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;

import org.openhab.binding.zigbee.internal.ZigBeeCommandParameterImpl;

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

import org.eclipse.jdt.annotation.NonNull;
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 final 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
) {
handleCommand(getChannel(channelId), command, params);
}

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

private ChannelUID getChannel(String channelId) {
return new ChannelUID(handler.getThing().getUID(), channelId);
}

@RuleAction(label = "buildCommand")
public CommandBuilder buildCommand(
@ActionInput(name = "channelId", required = true) String channelId,
@ActionInput(name = "command", required = true) Command command
) {
return new CommandBuilder(this, getChannel(channelId), 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 <@NonNull T> CommandBuilder with(ZigBeeCommandParameter<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
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,7 +252,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<String, Object> params) {
// Overridable if a channel can be commanded
}

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.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
@@ -0,0 +1,42 @@
package org.openhab.binding.zigbee.internal;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.zigbee.ZigBeeCommandParameter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

@NonNullByDefault
public final class ZigBeeCommandParameterImpl<T> implements ZigBeeCommandParameter<T> {
private final Logger logger;
private final String name;
private final Class<T> klazz;

public ZigBeeCommandParameterImpl(Class<T> klazz, String name) {
this.logger = LoggerFactory.getLogger(ZigBeeCommandParameterImpl.class.getName() + "." + 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 @@ -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.ZigBeeCommandParameters;
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 = ZigBeeCommandParameters.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.ZigBeeCommandParameters;
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 = ZigBeeCommandParameters.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 d802eed

Please sign in to comment.