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 d487d4b30..abd6bdbe9 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 @@ -13,6 +13,7 @@ package org.openhab.binding.zigbee.internal.converter; import java.util.ArrayList; +import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -20,6 +21,8 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.core.library.types.OnOffType; import org.eclipse.smarthome.core.library.types.PercentType; import org.eclipse.smarthome.core.thing.Channel; @@ -28,6 +31,7 @@ import org.eclipse.smarthome.core.types.Command; import org.openhab.binding.zigbee.ZigBeeBindingConstants; import org.openhab.binding.zigbee.converter.ZigBeeBaseChannelConverter; +import org.openhab.binding.zigbee.internal.converter.config.ZclOnOffSwitchConfig; import org.openhab.binding.zigbee.internal.converter.config.ZclReportingConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -64,6 +68,7 @@ public class ZigBeeConverterSwitchOnoff extends ZigBeeBaseChannelConverter private ZclAttribute attributeClient; private ZclAttribute attributeServer; + private ZclOnOffSwitchConfig configOnOff; private ZclReportingConfig configReporting; private final AtomicBoolean currentOnOffState = new AtomicBoolean(true); @@ -127,6 +132,13 @@ public boolean initializeConverter() { return false; } + if (clusterOnOffServer != null) { + // Add the listener + clusterOnOffServer.addAttributeListener(this); + configOnOff = new ZclOnOffSwitchConfig(); + configOnOff.initialize(clusterOnOffServer); + } + if (clusterOnOffClient != null) { // Add the command listener clusterOnOffClient.addCommandListener(this); @@ -142,6 +154,7 @@ public boolean initializeConverter() { configReporting = new ZclReportingConfig(channel); configOptions = new ArrayList<>(); + configOptions.addAll(configOnOff.getConfiguration()); configOptions.addAll(configReporting.getConfiguration()); return true; @@ -226,6 +239,28 @@ public Channel getChannel(ThingUID thingUID, ZigBeeEndpoint endpoint) { .build(); } + @Override + public void updateConfiguration(@NonNull Configuration currentConfiguration, + Map updatedParameters) { + if (configReporting.updateConfiguration(currentConfiguration, updatedParameters)) { + try { + ZclAttribute attribute; + CommandResult reportingResponse; + + attribute = clusterOnOffServer.getAttribute(ZclOnOffCluster.ATTR_ONOFF); + reportingResponse = attribute + .setReporting(configReporting.getReportingTimeMin(), configReporting.getReportingTimeMax()) + .get(); + handleReportingResponse(reportingResponse, configReporting.getPollingPeriod(), + configReporting.getReportingTimeMax()); + } catch (InterruptedException | ExecutionException e) { + logger.debug("{}: OnOff exception setting reporting", endpoint.getIeeeAddress(), e); + } + } + + configOnOff.updateConfiguration(currentConfiguration, updatedParameters); + } + @Override public void attributeUpdated(ZclAttribute attribute, Object val) { logger.debug("{}: ZigBee attribute reports {}", endpoint.getIeeeAddress(), attribute); diff --git a/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/converter/config/ZclOnOffSwitchConfig.java b/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/converter/config/ZclOnOffSwitchConfig.java new file mode 100755 index 000000000..c2a793cad --- /dev/null +++ b/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/converter/config/ZclOnOffSwitchConfig.java @@ -0,0 +1,148 @@ +/** + * Copyright (c) 2010-2018 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.binding.zigbee.internal.converter.config; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.concurrent.ExecutionException; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.smarthome.config.core.ConfigDescriptionParameter; +import org.eclipse.smarthome.config.core.ConfigDescriptionParameter.Type; +import org.eclipse.smarthome.config.core.ConfigDescriptionParameterBuilder; +import org.eclipse.smarthome.config.core.Configuration; +import org.eclipse.smarthome.config.core.ParameterOption; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.zsmartsystems.zigbee.zcl.ZclAttribute; +import com.zsmartsystems.zigbee.zcl.ZclCluster; +import com.zsmartsystems.zigbee.zcl.clusters.ZclOnOffCluster; + +/** + * Configuration handler for the {@link ZclOnOffSwitchCluster} + * + * @author Chris Jackson + * + */ +public class ZclOnOffSwitchConfig implements ZclClusterConfigHandler { + private Logger logger = LoggerFactory.getLogger(ZclOnOffSwitchConfig.class); + + private static final String CONFIG_ID = "zigbee_onoff_"; + private static final String CONFIG_OFFWAITTIME = CONFIG_ID + "offwaittime"; + private static final String CONFIG_ONTIME = CONFIG_ID + "ontime"; + private static final String CONFIG_STARTUPONOFF = CONFIG_ID + "startuponoff"; + + private ZclOnOffCluster onoffCluster; + + private final List parameters = new ArrayList<>(); + + @Override + public boolean initialize(ZclCluster cluster) { + onoffCluster = (ZclOnOffCluster) cluster; + try { + Boolean result = onoffCluster.discoverAttributes(false).get(); + if (!result) { + logger.debug("{}: Unable to get supported attributes for {}.", onoffCluster.getZigBeeAddress(), + onoffCluster.getClusterName()); + } + } catch (InterruptedException | ExecutionException e) { + logger.error("{}: Error getting supported attributes for {}. ", onoffCluster.getZigBeeAddress(), + onoffCluster.getClusterName(), e); + } + + // Build a list of configuration supported by this channel based on the attributes the cluster supports + List options = new ArrayList<>(); + + if (onoffCluster.isAttributeSupported(ZclOnOffCluster.ATTR_OFFWAITTIME)) { + parameters.add(ConfigDescriptionParameterBuilder.create(CONFIG_OFFWAITTIME, Type.INTEGER) + .withLabel("Off Wait Time") + .withDescription("Time in 100ms steps to ignore ON commands after an OFF command").withDefault("0") + .withMinimum(new BigDecimal(0)).withMaximum(new BigDecimal(60000)).build()); + } + if (onoffCluster.isAttributeSupported(ZclOnOffCluster.ATTR_ONTIME)) { + parameters.add(ConfigDescriptionParameterBuilder.create(CONFIG_ONTIME, Type.INTEGER) + .withLabel("Auto OFF Time") + .withDescription("Time in 100ms steps to automatically turn off when sent with timed command") + .withDefault("65535").withMinimum(new BigDecimal(0)).withMaximum(new BigDecimal(60000)).build()); + } + if (onoffCluster.isAttributeSupported(ZclOnOffCluster.ATTR_STARTUPONOFF)) { + options = new ArrayList(); + options.add(new ParameterOption("0", "OFF")); + options.add(new ParameterOption("1", "ON")); + parameters.add(ConfigDescriptionParameterBuilder.create(CONFIG_STARTUPONOFF, Type.INTEGER) + .withLabel("Power on state").withDescription("The state to set after powering on").withDefault("0") + .withMinimum(new BigDecimal(0)).withMaximum(new BigDecimal(1)).withOptions(options) + .withLimitToOptions(true).build()); + } + + return !parameters.isEmpty(); + } + + @Override + public List getConfiguration() { + return parameters; + } + + @Override + public boolean updateConfiguration(@NonNull Configuration currentConfiguration, + Map configurationParameters) { + + boolean updated = false; + for (Entry configurationParameter : configurationParameters.entrySet()) { + if (!configurationParameter.getKey().startsWith(CONFIG_ID)) { + continue; + } + // Ignore any configuration parameters that have not changed + if (Objects.equals(configurationParameter.getValue(), + currentConfiguration.get(configurationParameter.getKey()))) { + logger.debug("Configuration update: Ignored {} as no change", configurationParameter.getKey()); + continue; + } + + logger.debug("{}: Update LevelControl configuration property {}->{} ({})", onoffCluster.getZigBeeAddress(), + configurationParameter.getKey(), configurationParameter.getValue(), + configurationParameter.getValue().getClass().getSimpleName()); + Integer response = null; + switch (configurationParameter.getKey()) { + case CONFIG_OFFWAITTIME: + response = configureAttribute(ZclOnOffCluster.ATTR_OFFWAITTIME, configurationParameter.getValue()); + break; + case CONFIG_ONTIME: + response = configureAttribute(ZclOnOffCluster.ATTR_ONTIME, configurationParameter.getValue()); + break; + case CONFIG_STARTUPONOFF: + response = configureAttribute(ZclOnOffCluster.ATTR_STARTUPONOFF, configurationParameter.getValue()); + break; + default: + logger.warn("{}: Unhandled configuration property {}", onoffCluster.getZigBeeAddress(), + configurationParameter.getKey()); + break; + } + + if (response != null) { + currentConfiguration.put(configurationParameter.getKey(), BigInteger.valueOf(response)); + updated = true; + } + } + + return updated; + } + + private Integer configureAttribute(int attributeId, Object value) { + ZclAttribute attribute = onoffCluster.getAttribute(attributeId); + attribute.writeValue(((BigDecimal) (value)).intValue()); + return (Integer) attribute.readValue(0); + } +}