Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hue control selection #710

Merged
merged 6 commits into from Nov 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -14,6 +14,7 @@

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

import java.util.ArrayList;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
Expand All @@ -27,6 +28,13 @@
import java.util.stream.Stream;

import org.eclipse.jdt.annotation.NonNull;
import org.openhab.binding.zigbee.ZigBeeBindingConstants;
import org.openhab.binding.zigbee.converter.ZigBeeBaseChannelConverter;
import org.openhab.binding.zigbee.handler.ZigBeeThingHandler;
import org.openhab.binding.zigbee.internal.converter.config.ZclColorControlConfig;
import org.openhab.binding.zigbee.internal.converter.config.ZclColorControlConfig.ControlMethod;
import org.openhab.binding.zigbee.internal.converter.config.ZclLevelControlConfig;
import org.openhab.binding.zigbee.internal.converter.config.ZclOnOffSwitchConfig;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.HSBType;
Expand All @@ -37,10 +45,6 @@
import org.openhab.core.thing.binding.builder.ChannelBuilder;
import org.openhab.core.types.Command;
import org.openhab.core.types.UnDefType;
import org.openhab.binding.zigbee.ZigBeeBindingConstants;
import org.openhab.binding.zigbee.converter.ZigBeeBaseChannelConverter;
import org.openhab.binding.zigbee.handler.ZigBeeThingHandler;
import org.openhab.binding.zigbee.internal.converter.config.ZclLevelControlConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -92,7 +96,9 @@ public class ZigBeeConverterColorColor extends ZigBeeBaseChannelConverter implem

private final AtomicBoolean currentOnOffState = new AtomicBoolean(true);

private ZclColorControlConfig configColorControl;
private ZclLevelControlConfig configLevelControl;
private ZclOnOffSwitchConfig configOnOffSwitch;

@Override
public Set<Integer> getImplementedClientClusters() {
Expand All @@ -115,19 +121,6 @@ public boolean initializeDevice() {
return false;
}

ZclLevelControlCluster serverClusterLevelControl = (ZclLevelControlCluster) endpoint
.getInputCluster(ZclLevelControlCluster.CLUSTER_ID);
if (serverClusterLevelControl == null) {
logger.warn("{}: Device does not support level control", endpoint.getIeeeAddress());
return false;
}

ZclOnOffCluster serverClusterOnOff = (ZclOnOffCluster) endpoint.getInputCluster(ZclOnOffCluster.CLUSTER_ID);
if (serverClusterOnOff == null) {
logger.debug("{}: Device does not support on/off control", endpoint.getIeeeAddress());
return false;
}

if (!discoverSupportedColorCommands(serverClusterColorControl)) {
return false;
}
Expand Down Expand Up @@ -159,6 +152,12 @@ public boolean initializeDevice() {
.setCurrentYReporting(1, REPORTING_PERIOD_DEFAULT_MAX, 1).get();
handleReportingResponse(reportingResponse, POLLING_PERIOD_HIGH, REPORTING_PERIOD_DEFAULT_MAX);
}

ZclAttribute colorModeAttribute = serverClusterColorControl
.getAttribute(ZclColorControlCluster.ATTR_COLORMODE);
reportingResponse = serverClusterColorControl
.setReporting(colorModeAttribute, 1, REPORTING_PERIOD_DEFAULT_MAX, 1).get();
handleReportingResponse(reportingResponse, POLLING_PERIOD_HIGH, REPORTING_PERIOD_DEFAULT_MAX);
} else {
logger.error("{}: Error 0x{} setting server binding", endpoint.getIeeeAddress(),
Integer.toHexString(bindResponse.getStatusCode()));
Expand All @@ -169,40 +168,40 @@ public boolean initializeDevice() {
logger.debug("{}: Exception configuring color reporting", endpoint.getIeeeAddress(), e);
}

try {
CommandResult bindResponse = bind(serverClusterLevelControl).get();
if (!bindResponse.isSuccess()) {
pollingPeriod = POLLING_PERIOD_HIGH;
ZclLevelControlCluster serverClusterLevelControl = (ZclLevelControlCluster) endpoint
.getInputCluster(ZclLevelControlCluster.CLUSTER_ID);
if (serverClusterLevelControl == null) {
logger.warn("{}: Device does not support level control", endpoint.getIeeeAddress());
} else {
try {
CommandResult bindResponse = bind(serverClusterLevelControl).get();
if (!bindResponse.isSuccess()) {
pollingPeriod = POLLING_PERIOD_HIGH;
}
CommandResult reportingResponse = serverClusterLevelControl
.setCurrentLevelReporting(1, REPORTING_PERIOD_DEFAULT_MAX, 1).get();
handleReportingResponse(reportingResponse, POLLING_PERIOD_HIGH, REPORTING_PERIOD_DEFAULT_MAX);
} catch (ExecutionException | InterruptedException e) {
logger.debug("{}: Exception configuring level reporting", endpoint.getIeeeAddress(), e);
}
CommandResult reportingResponse = serverClusterLevelControl
.setCurrentLevelReporting(1, REPORTING_PERIOD_DEFAULT_MAX, 1).get();
handleReportingResponse(reportingResponse, POLLING_PERIOD_HIGH, REPORTING_PERIOD_DEFAULT_MAX);
} catch (ExecutionException | InterruptedException e) {
logger.debug("{}: Exception configuring level reporting", endpoint.getIeeeAddress(), e);
}

try {
CommandResult bindResponse = bind(serverClusterOnOff).get();
if (!bindResponse.isSuccess()) {
pollingPeriod = POLLING_PERIOD_HIGH;
ZclOnOffCluster serverClusterOnOff = (ZclOnOffCluster) endpoint.getInputCluster(ZclOnOffCluster.CLUSTER_ID);
if (serverClusterOnOff == null) {
logger.debug("{}: Device does not support on/off control", endpoint.getIeeeAddress());
} else {
try {
CommandResult bindResponse = bind(serverClusterOnOff).get();
if (!bindResponse.isSuccess()) {
pollingPeriod = POLLING_PERIOD_HIGH;
}
CommandResult reportingResponse = serverClusterOnOff.setOnOffReporting(1, REPORTING_PERIOD_DEFAULT_MAX)
.get();
handleReportingResponse(reportingResponse, POLLING_PERIOD_HIGH, REPORTING_PERIOD_DEFAULT_MAX);
} catch (ExecutionException | InterruptedException e) {
logger.debug("{}: Exception configuring on/off reporting", endpoint.getIeeeAddress(), e);
return false;
}
CommandResult reportingResponse = serverClusterOnOff.setOnOffReporting(1, REPORTING_PERIOD_DEFAULT_MAX)
.get();
handleReportingResponse(reportingResponse, POLLING_PERIOD_HIGH, REPORTING_PERIOD_DEFAULT_MAX);
} catch (ExecutionException | InterruptedException e) {
logger.debug("{}: Exception configuring on/off reporting", endpoint.getIeeeAddress(), e);
return false;
}

try {
ZclAttribute colorModeAttribute = serverClusterColorControl
.getAttribute(ZclColorControlCluster.ATTR_COLORMODE);
CommandResult reportingResponse = serverClusterColorControl
.setReporting(colorModeAttribute, 1, REPORTING_PERIOD_DEFAULT_MAX, 1).get();
handleReportingResponse(reportingResponse, POLLING_PERIOD_HIGH, REPORTING_PERIOD_DEFAULT_MAX);
} catch (ExecutionException | InterruptedException e) {
logger.debug("{}: Exception configuring color mode reporting", endpoint.getIeeeAddress(), e);
return false;
}

return true;
Expand All @@ -212,32 +211,41 @@ public boolean initializeDevice() {
public boolean initializeConverter(ZigBeeThingHandler thing) {
super.initializeConverter(thing);
colorUpdateScheduler = Executors.newSingleThreadScheduledExecutor();

clusterColorControl = (ZclColorControlCluster) endpoint.getInputCluster(ZclColorControlCluster.CLUSTER_ID);
if (clusterColorControl == null) {
logger.error("{}: Error opening device color controls", endpoint.getIeeeAddress());
return false;
}

configOptions = new ArrayList<>();

clusterLevelControl = (ZclLevelControlCluster) endpoint.getInputCluster(ZclLevelControlCluster.CLUSTER_ID);
if (clusterLevelControl == null) {
logger.warn("{}: Device does not support level control", endpoint.getIeeeAddress());
} else {
configLevelControl = new ZclLevelControlConfig();
configLevelControl.initialize(clusterLevelControl);
configOptions.addAll(configLevelControl.getConfiguration());
}

clusterOnOff = (ZclOnOffCluster) endpoint.getInputCluster(ZclOnOffCluster.CLUSTER_ID);
if (clusterOnOff == null) {
logger.debug("{}: Device does not support on/off control", endpoint.getIeeeAddress());
} else {
configOnOffSwitch = new ZclOnOffSwitchConfig();
configOnOffSwitch.initialize(clusterOnOff);
configOptions.addAll(configOnOffSwitch.getConfiguration());
}

// Create a configuration handler and get the available options
configColorControl = new ZclColorControlConfig(channel);
configColorControl.initialize(clusterColorControl);
configOptions.addAll(configColorControl.getConfiguration());

if (!discoverSupportedColorCommands(clusterColorControl)) {
return false;
}

// Create a configuration handler and get the available options
configLevelControl = new ZclLevelControlConfig();
configLevelControl.initialize(clusterLevelControl);
configOptions = configLevelControl.getConfiguration();

clusterColorControl.addAttributeListener(this);
clusterLevelControl.addAttributeListener(this);
clusterOnOff.addAttributeListener(this);
Expand Down Expand Up @@ -446,7 +454,6 @@ private void updateOnOff(OnOffType onOff) {
} else if (!on) {
updateChannelState(OnOffType.OFF);
}

}

private void updateBrightness(PercentType brightness) {
Expand Down Expand Up @@ -500,6 +507,8 @@ private void updateColorXY() {
public void updateConfiguration(@NonNull Configuration currentConfiguration,
Map<String, Object> updatedParameters) {
configLevelControl.updateConfiguration(currentConfiguration, updatedParameters);
configColorControl.updateConfiguration(currentConfiguration, updatedParameters);
configOnOffSwitch.updateConfiguration(currentConfiguration, updatedParameters);
}

@Override
Expand Down Expand Up @@ -596,6 +605,12 @@ public void run() {
}

private boolean discoverSupportedColorCommands(ZclColorControlCluster serverClusterColorControl) {
// If the configuration is not set to AUTO, then we can override the control method
if (configColorControl != null && configColorControl.getControlMethod() != ControlMethod.AUTO) {
supportsHue = configColorControl.getControlMethod() == ControlMethod.HUE;
return true;
}

// Discover whether the device supports HUE/SAT or XY color set of commands
try {
if (!serverClusterColorControl.discoverAttributes(false).get()) {
Expand Down
@@ -0,0 +1,126 @@
/**
* Copyright (c) 2010-2021 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.converter.config;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;

import org.eclipse.jdt.annotation.NonNull;
import org.openhab.core.config.core.ConfigDescriptionParameter;
import org.openhab.core.config.core.ConfigDescriptionParameter.Type;
import org.openhab.core.config.core.ConfigDescriptionParameterBuilder;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.config.core.ParameterOption;
import org.openhab.core.thing.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.zsmartsystems.zigbee.zcl.ZclCluster;
import com.zsmartsystems.zigbee.zcl.clusters.ZclColorControlCluster;

/**
* Configuration handler for the {@link ZclColorControlCluster}
*
* @author Chris Jackson
*
*/
public class ZclColorControlConfig implements ZclClusterConfigHandler {
private Logger logger = LoggerFactory.getLogger(ZclColorControlConfig.class);

private static final String CONFIG_ID = "zigbee_color_";
private static final String CONFIG_CONTROLMETHOD = CONFIG_ID + "controlmethod";

private ZclColorControlCluster colorControlCluster;

public enum ControlMethod {
AUTO,
HUE,
XY
}

private ControlMethod controlMethod = ControlMethod.AUTO;

private final List<ConfigDescriptionParameter> parameters = new ArrayList<>();

public ZclColorControlConfig(Channel channel) {
Configuration configuration = channel.getConfiguration();
if (configuration.containsKey(CONFIG_CONTROLMETHOD)) {
controlMethod = ControlMethod.valueOf((String) configuration.get(CONFIG_CONTROLMETHOD));
}
}

@Override
public boolean initialize(ZclCluster cluster) {
colorControlCluster = (ZclColorControlCluster) cluster;

// Build a list of configuration supported by this channel
List<ParameterOption> options = new ArrayList<>();

options = new ArrayList<>();
options.add(new ParameterOption(ControlMethod.AUTO.toString(), "Auto"));
options.add(new ParameterOption(ControlMethod.HUE.toString(), "Hue Commands"));
options.add(new ParameterOption(ControlMethod.XY.toString(), "XY Commands"));
parameters.add(ConfigDescriptionParameterBuilder.create(CONFIG_CONTROLMETHOD, Type.TEXT)
.withLabel("Color Control Method")
.withDescription(
"The commands used to control color. AUTO will use HUE if the device supports, otherwise XY")
.withDefault(ControlMethod.AUTO.toString()).withOptions(options).withLimitToOptions(true).build());

return !parameters.isEmpty();
}

@Override
public List<ConfigDescriptionParameter> getConfiguration() {
return parameters;
}

@Override
public boolean updateConfiguration(@NonNull Configuration currentConfiguration,
Map<String, Object> configurationParameters) {

boolean updated = false;
for (Entry<String, Object> 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 ColorControl configuration property {}->{} ({})",
colorControlCluster.getZigBeeAddress(), configurationParameter.getKey(),
configurationParameter.getValue(), configurationParameter.getValue().getClass().getSimpleName());
switch (configurationParameter.getKey()) {
case CONFIG_CONTROLMETHOD:
controlMethod = ControlMethod.valueOf((String) configurationParameter.getValue());
break;
default:
logger.warn("{}: Unhandled configuration property {}", colorControlCluster.getZigBeeAddress(),
configurationParameter.getKey());
break;
}
}

return updated;
}

public ControlMethod getControlMethod() {
return controlMethod;
}
}
Expand Up @@ -115,7 +115,7 @@ public boolean updateConfiguration(@NonNull Configuration currentConfiguration,
continue;
}

logger.debug("{}: Update LevelControl configuration property {}->{} ({})", onoffCluster.getZigBeeAddress(),
logger.debug("{}: Update OnOff configuration property {}->{} ({})", onoffCluster.getZigBeeAddress(),
configurationParameter.getKey(), configurationParameter.getValue(),
configurationParameter.getValue().getClass().getSimpleName());
Integer response = null;
Expand Down