Skip to content

Commit 8c2f358

Browse files
committed
Refactor Tuya into separate bundle
Signed-off-by: Chris Jackson <[email protected]> (+4 squashed commits) Squashed commits: [8c397c9] Add tests for ZigBeeConverterTuyaButton Signed-off-by: Daniel Schall <[email protected]> [d68a96a] Fix build break Signed-off-by: Daniel Schall <[email protected]> [9a37f0e] Add Tuya button support Signed-off-by: Daniel Schall <[email protected]> [af45e77] Create Tuya specific handler binding Signed-off-by: Chris Jackson <[email protected]>
1 parent 0d9206e commit 8c2f358

File tree

18 files changed

+967
-6
lines changed

18 files changed

+967
-6
lines changed

bom/pom.xml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
<artifactId>org.openhab.addons.bom.zigbee</artifactId>
1313
<packaging>pom</packaging>
1414

15-
<name>openHAB ZigBee :: BOM :: openHAB ZigBee Binding</name>
15+
<name>openHAB Add-ons :: Bundles :: ZigBee Binding BOM</name>
1616

1717
<dependencies>
1818
<dependency>
@@ -50,6 +50,11 @@
5050
<artifactId>org.openhab.binding.zigbee.telegesis</artifactId>
5151
<version>${project.version}</version>
5252
</dependency>
53+
<dependency>
54+
<groupId>org.openhab.addons.bundles</groupId>
55+
<artifactId>org.openhab.binding.zigbee.tuya</artifactId>
56+
<version>${project.version}</version>
57+
</dependency>
5358
<dependency>
5459
<groupId>org.openhab.addons.bundles</groupId>
5560
<artifactId>org.openhab.binding.zigbee.xbee</artifactId>

feature/pom.xml

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,16 @@
4545
<artifactId>org.openhab.binding.zigbee.ember</artifactId>
4646
<version>${project.version}</version>
4747
</dependency>
48-
<dependency>
49-
<groupId>org.openhab.addons.bundles</groupId>
50-
<artifactId>org.openhab.binding.zigbee.telegesis</artifactId>
51-
<version>${project.version}</version>
52-
</dependency>
48+
<dependency>
49+
<groupId>org.openhab.addons.bundles</groupId>
50+
<artifactId>org.openhab.binding.zigbee.telegesis</artifactId>
51+
<version>${project.version}</version>
52+
</dependency>
53+
<dependency>
54+
<groupId>org.openhab.addons.bundles</groupId>
55+
<artifactId>org.openhab.binding.zigbee.tuya</artifactId>
56+
<version>${project.version}</version>
57+
</dependency>
5358
<dependency>
5459
<groupId>org.openhab.addons.bundles</groupId>
5560
<artifactId>org.openhab.binding.zigbee.xbee</artifactId>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/bin/
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
This content is produced and maintained by the openHAB project.
2+
3+
* Project home: https://www.openhab.org
4+
5+
== Declared Project Licenses
6+
7+
This program and the accompanying materials are made available under the terms
8+
of the Eclipse Public License 2.0 which is available at
9+
https://www.eclipse.org/legal/epl-2.0/.
10+
11+
== Source Code
12+
13+
https://github.com/openhab/org.openhab.binding.zigbee
14+
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="no"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
2+
3+
<modelVersion>4.0.0</modelVersion>
4+
5+
<parent>
6+
<groupId>org.openhab.addons.bundles</groupId>
7+
<artifactId>org.openhab.addons.zigbee.reactor</artifactId>
8+
<version>3.2.0-SNAPSHOT</version>
9+
</parent>
10+
11+
<artifactId>org.openhab.binding.zigbee.tuya</artifactId>
12+
13+
<name>openHAB Add-ons :: Bundles :: ZigBee Tuya Handler</name>
14+
15+
<dependencies>
16+
<dependency>
17+
<groupId>org.openhab.addons.bundles</groupId>
18+
<artifactId>org.openhab.binding.zigbee</artifactId>
19+
<version>${project.version}</version>
20+
<scope>provided</scope>
21+
</dependency>
22+
</dependencies>
23+
24+
</project>
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/**
2+
* Copyright (c) 2010-2021 Contributors to the openHAB project
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Eclipse Public License 2.0 which is available at
9+
* http://www.eclipse.org/legal/epl-2.0
10+
*
11+
* SPDX-License-Identifier: EPL-2.0
12+
*/
13+
package org.openhab.binding.zigbee.tuya;
14+
15+
import org.eclipse.jdt.annotation.NonNullByDefault;
16+
import org.openhab.core.thing.type.ChannelTypeUID;
17+
18+
/**
19+
* The {@link TuyaBindingConstants} class defines common constants, which are
20+
* used across the whole binding.
21+
*
22+
* @author Chris Jackson - Initial contribution
23+
*/
24+
@NonNullByDefault
25+
public class TuyaBindingConstants {
26+
27+
public static final String BINDING_ID = "zigbee";
28+
29+
public static final String CHANNEL_NAME_TUYA_BUTTON = "tuyabutton";
30+
public static final String CHANNEL_LABEL_TUYA_BUTTON = "Button";
31+
public static final ChannelTypeUID CHANNEL_TUYA_BUTTON = new ChannelTypeUID("zigbee:tuya_button");
32+
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
/**
2+
* Copyright (c) 2010-2021 Contributors to the openHAB project
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Eclipse Public License 2.0 which is available at
9+
* http://www.eclipse.org/legal/epl-2.0
10+
*
11+
* SPDX-License-Identifier: EPL-2.0
12+
*/
13+
package org.openhab.binding.zigbee.tuya.converter;
14+
15+
import static java.lang.Integer.*;
16+
17+
import java.util.Collections;
18+
import java.util.HashMap;
19+
import java.util.Set;
20+
import java.util.concurrent.ExecutionException;
21+
22+
import org.openhab.core.thing.Channel;
23+
import org.openhab.core.thing.CommonTriggerEvents;
24+
import org.openhab.core.thing.ThingUID;
25+
import org.openhab.binding.zigbee.converter.ZigBeeBaseChannelConverter;
26+
import org.openhab.binding.zigbee.handler.ZigBeeThingHandler;
27+
import org.openhab.binding.zigbee.internal.converter.config.ZclReportingConfig;
28+
import org.openhab.binding.zigbee.tuya.internal.TuyaButtonPressCommand;
29+
import org.slf4j.Logger;
30+
import org.slf4j.LoggerFactory;
31+
32+
import com.zsmartsystems.zigbee.CommandResult;
33+
import com.zsmartsystems.zigbee.ZigBeeEndpoint;
34+
import com.zsmartsystems.zigbee.zcl.ZclAttribute;
35+
import com.zsmartsystems.zigbee.zcl.ZclAttributeListener;
36+
import com.zsmartsystems.zigbee.zcl.ZclCluster;
37+
import com.zsmartsystems.zigbee.zcl.ZclCommand;
38+
import com.zsmartsystems.zigbee.zcl.ZclCommandListener;
39+
import com.zsmartsystems.zigbee.zcl.ZclStatus;
40+
import com.zsmartsystems.zigbee.zcl.clusters.ZclOnOffCluster;
41+
42+
/**
43+
* Generic converter for Tuya buttons (e.g., Zemismart).
44+
* <p>
45+
* This converter is handling the Tuya-specific command (ID: 253) and emits button-pressed events.
46+
* <p>
47+
* As the configuration is done via channel properties, this converter is usable via static thing types only.
48+
*
49+
* @author Daniel Schall - initial contribution
50+
*/
51+
public class ZigBeeConverterTuyaButton extends ZigBeeBaseChannelConverter
52+
implements ZclAttributeListener, ZclCommandListener {
53+
54+
private Logger logger = LoggerFactory.getLogger(ZigBeeConverterTuyaButton.class);
55+
56+
private ZclCluster clientCluster = null;
57+
private ZclCluster serverCluster = null;
58+
59+
// Tuya devices sometimes send duplicate commands with the same tx id.
60+
// We keep track of the last received Tx id and ignore the duplicate.
61+
private Integer lastTxId = -1;
62+
63+
@Override
64+
public Set<Integer> getImplementedClientClusters() {
65+
return Collections.singleton(ZclOnOffCluster.CLUSTER_ID);
66+
}
67+
68+
@Override
69+
public Set<Integer> getImplementedServerClusters() {
70+
return Collections.singleton(ZclOnOffCluster.CLUSTER_ID);
71+
}
72+
73+
@Override
74+
public boolean initializeDevice() {
75+
ZclCluster clientCluster = endpoint.getOutputCluster(ZclOnOffCluster.CLUSTER_ID);
76+
ZclCluster serverCluster = endpoint.getInputCluster(ZclOnOffCluster.CLUSTER_ID);
77+
78+
if (clientCluster == null) {
79+
logger.error("{}: Error opening client cluster {} on endpoint {}", endpoint.getIeeeAddress(),
80+
ZclOnOffCluster.CLUSTER_ID, endpoint.getEndpointId());
81+
return false;
82+
}
83+
84+
if (serverCluster == null) {
85+
logger.error("{}: Error opening server cluster {} on endpoint {}", endpoint.getIeeeAddress(),
86+
ZclOnOffCluster.CLUSTER_ID, endpoint.getEndpointId());
87+
return false;
88+
}
89+
90+
ZclReportingConfig reporting = new ZclReportingConfig(channel);
91+
92+
try {
93+
CommandResult bindResponse = bind(serverCluster).get();
94+
if (bindResponse.isSuccess()) {
95+
// Configure reporting
96+
ZclAttribute attribute = serverCluster.getAttribute(ZclOnOffCluster.ATTR_ONOFF);
97+
CommandResult reportingResponse = attribute
98+
.setReporting(reporting.getReportingTimeMin(), reporting.getReportingTimeMax()).get();
99+
handleReportingResponse(reportingResponse, POLLING_PERIOD_HIGH, reporting.getPollingPeriod());
100+
} else {
101+
logger.debug("{}: Error 0x{} setting server binding", endpoint.getIeeeAddress(),
102+
Integer.toHexString(bindResponse.getStatusCode()));
103+
pollingPeriod = POLLING_PERIOD_HIGH;
104+
}
105+
106+
} catch (InterruptedException | ExecutionException e) {
107+
logger.error("{}: Exception setting reporting ", endpoint.getIeeeAddress(), e);
108+
}
109+
110+
try {
111+
CommandResult bindResponse = bind(clientCluster).get();
112+
if (!bindResponse.isSuccess()) {
113+
logger.error("{}: Error 0x{} setting client binding for cluster {}", endpoint.getIeeeAddress(),
114+
toHexString(bindResponse.getStatusCode()), ZclOnOffCluster.CLUSTER_ID);
115+
}
116+
} catch (InterruptedException | ExecutionException e) {
117+
logger.error("{}: Exception setting client binding to cluster {}: {}", endpoint.getIeeeAddress(),
118+
ZclOnOffCluster.CLUSTER_ID, e);
119+
}
120+
121+
return true;
122+
}
123+
124+
@Override
125+
public synchronized boolean initializeConverter(ZigBeeThingHandler thing) {
126+
super.initializeConverter(thing);
127+
128+
clientCluster = endpoint.getOutputCluster(ZclOnOffCluster.CLUSTER_ID);
129+
serverCluster = endpoint.getInputCluster(ZclOnOffCluster.CLUSTER_ID);
130+
131+
if (clientCluster == null) {
132+
logger.error("{}: Error opening device client controls", endpoint.getIeeeAddress());
133+
return false;
134+
}
135+
136+
if (serverCluster == null) {
137+
logger.error("{}: Error opening device server controls", endpoint.getIeeeAddress());
138+
return false;
139+
}
140+
141+
clientCluster.addCommandListener(this);
142+
serverCluster.addAttributeListener(this);
143+
144+
// Add Tuya-specific command
145+
//
146+
HashMap<Integer, Class<? extends ZclCommand>> commandMap = new HashMap<>();
147+
commandMap.put(TuyaButtonPressCommand.COMMAND_ID, TuyaButtonPressCommand.class);
148+
clientCluster.addClientCommands(commandMap);
149+
150+
return true;
151+
}
152+
153+
@Override
154+
public void disposeConverter() {
155+
if(clientCluster != null) {
156+
clientCluster.removeCommandListener(this);
157+
}
158+
if (serverCluster != null) {
159+
serverCluster.removeAttributeListener(this);
160+
}
161+
}
162+
163+
@Override
164+
public void handleRefresh() {
165+
// nothing to do, as we only listen to commands
166+
}
167+
168+
@Override
169+
public Channel getChannel(ThingUID thingUID, ZigBeeEndpoint endpoint) {
170+
// This converter is used only for channels specified in static thing types, and cannot be used to construct
171+
// channels based on an endpoint alone.
172+
return null;
173+
}
174+
175+
@Override
176+
public boolean commandReceived(ZclCommand command) {
177+
logger.debug("{} received command {}", endpoint.getIeeeAddress(), command);
178+
Integer thisTxId = command.getTransactionId();
179+
if(lastTxId == thisTxId)
180+
{
181+
logger.debug("{} ignoring duplicate command {}", endpoint.getIeeeAddress(), thisTxId);
182+
}
183+
else if (command instanceof TuyaButtonPressCommand) {
184+
TuyaButtonPressCommand tuyaButtonPressCommand = (TuyaButtonPressCommand) command;
185+
thing.triggerChannel(channel.getUID(), getEventType(tuyaButtonPressCommand.getPressType()));
186+
clientCluster.sendDefaultResponse(command, ZclStatus.SUCCESS);
187+
}
188+
else {
189+
logger.warn("{} received unknown command {}", endpoint.getIeeeAddress(), command);
190+
}
191+
192+
lastTxId = thisTxId;
193+
return true;
194+
}
195+
196+
@Override
197+
public void attributeUpdated(ZclAttribute attribute, Object val) {
198+
logger.debug("{}: ZigBee attribute reports {}", endpoint.getIeeeAddress(), attribute);
199+
}
200+
201+
private String getEventType(Integer pressType)
202+
{
203+
switch(pressType)
204+
{
205+
case 0:
206+
return CommonTriggerEvents.SHORT_PRESSED;
207+
case 1:
208+
return CommonTriggerEvents.DOUBLE_PRESSED;
209+
case 2:
210+
return CommonTriggerEvents.LONG_PRESSED;
211+
default:
212+
logger.warn("{} received unknown pressType {}", endpoint.getIeeeAddress(), pressType);
213+
return CommonTriggerEvents.SHORT_PRESSED;
214+
}
215+
}
216+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/**
2+
* Copyright (c) 2010-2021 Contributors to the openHAB project
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Eclipse Public License 2.0 which is available at
9+
* http://www.eclipse.org/legal/epl-2.0
10+
*
11+
* SPDX-License-Identifier: EPL-2.0
12+
*/
13+
package org.openhab.binding.zigbee.tuya.converter;
14+
15+
import java.util.HashMap;
16+
import java.util.Map;
17+
18+
import org.openhab.binding.zigbee.ZigBeeBindingConstants;
19+
import org.openhab.binding.zigbee.converter.ZigBeeBaseChannelConverter;
20+
import org.openhab.binding.zigbee.converter.ZigBeeChannelConverterFactory;
21+
import org.openhab.binding.zigbee.converter.ZigBeeChannelConverterProvider;
22+
import org.openhab.core.thing.type.ChannelTypeUID;
23+
import org.osgi.service.component.annotations.Component;
24+
25+
/**
26+
* The base {@link ZigBeeTuyaChannelConverterProvider} of the binding making the Tuya specific
27+
* {@link ZigBeeBaseChannelConverter}s available for the {@link ZigBeeChannelConverterFactory}.
28+
*
29+
* @author Chris Jackson - Initial contribution
30+
*/
31+
@Component(immediate = true, service = ZigBeeChannelConverterProvider.class)
32+
public final class ZigBeeTuyaChannelConverterProvider implements ZigBeeChannelConverterProvider {
33+
34+
private final Map<ChannelTypeUID, Class<? extends ZigBeeBaseChannelConverter>> channelMap = new HashMap<>();
35+
36+
public ZigBeeTuyaChannelConverterProvider() {
37+
// Add all the converters into the map...
38+
channelMap.put(ZigBeeBindingConstants.CHANNEL_COLOR_COLOR, ZigBeeConverterTuyaButton.class);
39+
}
40+
41+
@Override
42+
public Map<ChannelTypeUID, Class<? extends ZigBeeBaseChannelConverter>> getChannelConverters() {
43+
return channelMap;
44+
}
45+
}

0 commit comments

Comments
 (0)