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 28, 2019
1 parent 9667b90 commit a5cd438
Show file tree
Hide file tree
Showing 20 changed files with 386 additions and 87 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* Copyright (c) 2010-2019 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.zigbee;

import org.eclipse.jdt.annotation.NonNullByDefault;

/**
* The {@link ZigBeeCommandParameter} class represents a single pre-defined parameter to a ZigBee command.
*
* @author Thomas Weißschuh - Initial contribution
*/
@NonNullByDefault
public final class ZigBeeCommandParameter<T> {
private final String name;
private final Class<T> type;

public static final ZigBeeCommandParameter<Integer> TRANSITION_TIME =
new ZigBeeCommandParameter<>(Integer.class, "transitionTime");

ZigBeeCommandParameter(final Class<T> type, final String name) {
this.type = type;
this.name = name;
}

public Class<T> getType() {
return type;
}

@Override
public String toString() {
return name;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* Copyright (c) 2010-2019 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.zigbee;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.zigbee.internal.ZigBeeCommandParametersImpl;

import java.util.Collection;
import java.util.Optional;

/**
* The {@link ZigBeeCommandParameters} interface represents a collection of parameters to pass to a ZigBee command.
*
* @author Thomas Weißschuh - Initial contribution
*/
@NonNullByDefault
public interface ZigBeeCommandParameters {
static ZigBeeCommandParameters empty() {
return new ZigBeeCommandParametersImpl();
}

<T> ZigBeeCommandParameters add(final ZigBeeCommandParameter<T> param, final T value);
<T> Optional<T> get(final ZigBeeCommandParameter<T> param);
Collection<ZigBeeCommandParameter<?>> setParameters();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/**
* Copyright (c) 2010-2019 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.zigbee;

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 static org.eclipse.jdt.annotation.Checks.requireNonNull;

/**
* The {@link ZigBeeThingActions} defines actions to be triggered by rules.
* It allows the specification of parameters to commands which is not possible via normal OH commands.
*
* @author Thomas Weißschuh - Initial contribution
*/
@SuppressWarnings("unused")
@ThingActionsScope(name="zigbee")
@NonNullByDefault
public final class ZigBeeThingActions implements ThingActions {
private @Nullable ZigBeeThingHandler handler;

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

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

@RuleAction(label = "sendCommand")
public void sendCommand(
@ActionInput(name = "channelId", required = true) final String channelId,
@ActionInput(name = "command", required = true) final Command command,
@ActionInput(name = "params") @Nullable final ZigBeeCommandParameters params
) {
handleCommand(getChannel(channelId), command, params != null ? params : ZigBeeCommandParameters.empty());
}

private void handleCommand(final ChannelUID channel, final Command command, final ZigBeeCommandParameters params) {
requireNonNull(handler).handleCommand(channel, command, params);
}

private ChannelUID getChannel(final String channelId) {
return new ChannelUID(requireNonNull(handler).getThing().getUID(), channelId);
}
}
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 All @@ -34,6 +35,7 @@
import org.eclipse.smarthome.core.types.State;
import org.eclipse.smarthome.core.types.StateDescription;
import org.openhab.binding.zigbee.ZigBeeBindingConstants;
import org.openhab.binding.zigbee.ZigBeeCommandParameters;
import org.openhab.binding.zigbee.handler.ZigBeeCoordinatorHandler;
import org.openhab.binding.zigbee.handler.ZigBeeThingHandler;
import org.slf4j.Logger;
Expand Down Expand Up @@ -251,7 +253,7 @@ public void handleRefresh() {
*
* @param command the {@link Command} to send
*/
public void handleCommand(final Command command) {
public void handleCommand(final Command command, final ZigBeeCommandParameters 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 @@ -59,11 +60,15 @@
import org.eclipse.smarthome.core.types.State;
import org.eclipse.smarthome.core.types.StateDescription;
import org.openhab.binding.zigbee.ZigBeeBindingConstants;
import org.openhab.binding.zigbee.ZigBeeCommandParameter;
import org.openhab.binding.zigbee.ZigBeeCommandParameters;
import org.openhab.binding.zigbee.converter.ZigBeeBaseChannelConverter;
import org.openhab.binding.zigbee.converter.ZigBeeChannelConverterFactory;
import org.openhab.binding.zigbee.discovery.ZigBeeNodePropertyDiscoverer;
import org.openhab.binding.zigbee.internal.ZigBeeCommandParametersImpl;
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 @@ -694,6 +699,10 @@ public void handleConfigurationUpdate(Map<String, Object> configurationParameter

@Override
public void handleCommand(final ChannelUID channelUID, final Command command) {
handleCommand(channelUID, command, ZigBeeCommandParameters.empty());
}

public void handleCommand(final ChannelUID channelUID, final Command command, final ZigBeeCommandParameters params) {
logger.debug("{}: Command for channel {} --> {} [{}]", nodeIeeeAddress, channelUID, command,
command.getClass().getSimpleName());

Expand All @@ -718,7 +727,12 @@ public void run() {
if (command == RefreshType.REFRESH) {
handler.handleRefresh();
} else {
handler.handleCommand(command);
ZigBeeCommandParametersImpl.UsageTracker parameterTracker = new ZigBeeCommandParametersImpl.UsageTracker(params);
handler.handleCommand(command, parameterTracker);
Set<ZigBeeCommandParameter<?>> unusedParams = parameterTracker.unusedParams();
if (!unusedParams.isEmpty() && logger.isWarnEnabled()) {
logger.warn("Handler {} did not use given parameters {}", handler, unusedParams);
}
}
} catch (Exception e) {
logger.debug("{}: Exception sending command to channel {}", nodeIeeeAddress, channelUID, e);
Expand Down Expand Up @@ -1006,4 +1020,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,92 @@
/**
* Copyright (c) 2010-2019 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.zigbee.internal;

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

import java.util.*;

/**
* The {@link ZigBeeCommandParametersImpl} class provides a stateless and a usage-tracking
* implementations of {@link ZigBeeCommandParameters}.
*
* @author Thomas Weißschuh - Initial contribution
*/
@NonNullByDefault
public class ZigBeeCommandParametersImpl implements ZigBeeCommandParameters {
private static final Logger logger = LoggerFactory.getLogger(ZigBeeCommandParametersImpl.class);

private final Map<ZigBeeCommandParameter<?>, Object> params = new HashMap<>();

@Override
public <T> ZigBeeCommandParameters add(final ZigBeeCommandParameter<T> param, final T value) {
params.put(param, value);
return this;
}

@Override
public <T> Optional<T> get(final ZigBeeCommandParameter<T> param) {
@Nullable Object v = params.get(param);
if (v == null) {
return Optional.empty();
} else if (!param.getType().isInstance(v)) {
logger.warn("Can not retrieve param {}: object of type {} ({}) can not be casted to {}",
param, v.getClass(), v, param.getType()
);
return Optional.empty();
} else {
return Optional.of(param.getType().cast(v));
}
}

@Override
public Collection<ZigBeeCommandParameter<?>> setParameters() {
return Collections.unmodifiableSet(params.keySet());
}

public static class UsageTracker implements ZigBeeCommandParameters {
private final Set<ZigBeeCommandParameter<?>> usedParams = new HashSet<>();
private final ZigBeeCommandParameters delegate;

public UsageTracker(ZigBeeCommandParameters delegate) {
this.delegate = delegate;
}

public Set<ZigBeeCommandParameter<?>> unusedParams() {
Set<ZigBeeCommandParameter<?>> unusedParams = new HashSet<>(setParameters());
unusedParams.removeAll(usedParams);
return Collections.unmodifiableSet(unusedParams);
}

@Override
public <T> ZigBeeCommandParameters add(ZigBeeCommandParameter<T> param, T value) {
return delegate.add(param, value);
}

@Override
public <T> Optional<T> get(ZigBeeCommandParameter<T> param) {
usedParams.add(param);
return delegate.get(param);
}

@Override
public Collection<ZigBeeCommandParameter<?>> setParameters() {
return delegate.setParameters();
}
}
}
Loading

0 comments on commit a5cd438

Please sign in to comment.