Skip to content

Commit

Permalink
Only restore attribute state from the node database for non-manufactu…
Browse files Browse the repository at this point in the history
…rer-specific attributes (#1001)

* Fix typo in JavaDoc

* Only restore dynamic attribute state from the node database, except for
manufacturer-specific attributes

This resolves #989 and #236.

Signed-off-by: Henning Sudbrock <[email protected]>
  • Loading branch information
hsudbrock authored Mar 14, 2020
1 parent 0d6508f commit 44c2c35
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,17 @@ public void setDao(ZclCluster cluster, ZclAttributeDao dao) {
reportingTimeout = dao.getReportingTimeout();
manufacturerCode = dao.getManufacturerCode();
}

/**
* Sets the dynamic state of the attribute from a {@link ZclAttributeDao} which has been restored from a persisted state.
*
* @param dao the {@link ZclAttributeDao} from which to restore the dynamic state
*/
public void setDynamicStateFromDao(ZclAttributeDao dao) {
implemented = dao.isImplemented();
lastValue = dao.getLastValue();
lastReportTime = dao.getLastReportTime();
}

/**
* Returns a Data Acquisition Object for this attribute. This is a clean class recording the state of the primary
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,14 +141,14 @@ public abstract class ZclCluster {

/**
* Map of client attributes supported by the cluster. This contains all attributes, even if they are not supported
* by the remote device. To check what attributes are supported by the remove device, us the
* by the remote device. To check what attributes are supported by the remove device, use the
* {@link #discoverAttributes()} method followed by the {@link #getSupportedAttributes()} method.
*/
protected Map<Integer, ZclAttribute> clientAttributes = initializeClientAttributes();

/**
* Map of server attributes supported by the cluster. This contains all attributes, even if they are not supported
* by the remote device. To check what attributes are supported by the remove device, us the
* by the remote device. To check what attributes are supported by the remove device, use the
* {@link #discoverAttributes()} method followed by the {@link #getSupportedAttributes()} method.
*/
protected Map<Integer, ZclAttribute> serverAttributes = initializeServerAttributes();
Expand Down Expand Up @@ -1598,20 +1598,21 @@ public void setDao(ZclClusterDao dao) {
supportedCommandsGenerated.addAll(dao.getSupportedCommandsGenerated());
supportedCommandsReceived.addAll(dao.getSupportedCommandsReceived());

Map<Integer, ZclAttribute> daoZclAttributes = new HashMap<>();
Map<Integer, ZclAttribute> attributes = isClient ? clientAttributes : serverAttributes;

for (ZclAttributeDao daoAttribute : dao.getAttributes().values()) {
// Normalize the data to protect against the users serialisation system restoring incorrect data classes
daoAttribute
.setLastValue(normalizer.normalizeZclData(daoAttribute.getDataType(), daoAttribute.getLastValue()));
ZclAttribute attribute = new ZclAttribute();
attribute.setDao(this, daoAttribute);
daoZclAttributes.put(daoAttribute.getId(), attribute);
}

if (isClient) {
clientAttributes = daoZclAttributes;
} else {
serverAttributes = daoZclAttributes;

ZclAttribute attribute = attributes.get(daoAttribute.getId());
if (attribute == null || daoAttribute.getManufacturerCode() != null) {
attribute = new ZclAttribute();
attribute.setDao(this, daoAttribute);
attributes.put(daoAttribute.getId(), attribute);
} else {
attribute.setDynamicStateFromDao(daoAttribute);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
*/
package com.zsmartsystems.zigbee.zcl;

import static com.zsmartsystems.zigbee.zcl.clusters.ZclOnOffCluster.ATTR_ONOFF;
import static java.lang.Boolean.TRUE;
import static java.util.stream.Collectors.toSet;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
Expand All @@ -15,7 +18,6 @@

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -652,22 +654,54 @@ record = records.get(1);

@Test
public void setDao() {
// Given an On/Off cluster and the DAO created from that cluster
createEndpoint();

ZclOnOffCluster cluster = new ZclOnOffCluster(endpoint);
Set<Integer> onOffClusterAttributeIds = cluster.getAttributes().stream().map(ZclAttribute::getId)
.collect(toSet());

ZclClusterDao clusterDao = cluster.getDao();
ZclAttributeDao attributeDao = new ZclAttributeDao();
attributeDao.setDataType(ZclDataType.SIGNED_16_BIT_INTEGER);
attributeDao.setId(1);
attributeDao.setLastValue(Double.valueOf(123));
Map<Integer, ZclAttributeDao> attributes = new HashMap<>();
attributes.put(1, attributeDao);
clusterDao.setAttributes(attributes);
Map<Integer, ZclAttributeDao> attributeDaos = clusterDao.getAttributes();

// Add one attribute for a manufacturer-specific attribute to the DAO
ZclAttributeDao manufacturerSpecificAttributeDao = new ZclAttributeDao();
manufacturerSpecificAttributeDao.setId(0xF000);
manufacturerSpecificAttributeDao.setManufacturerCode(0x1234);
manufacturerSpecificAttributeDao.setDataType(ZclDataType.SIGNED_16_BIT_INTEGER);
manufacturerSpecificAttributeDao.setLastValue(Double.valueOf(123));
attributeDaos.put(manufacturerSpecificAttributeDao.getId(), manufacturerSpecificAttributeDao);

// Set last value and implemented status for the on/off attribute in the DAO
ZclAttributeDao onOffAttributeDao = attributeDaos.get(ATTR_ONOFF);
onOffAttributeDao.setLastValue(TRUE);
onOffAttributeDao.setImplemented(true);

clusterDao.setAttributes(attributeDaos);

// Now update the cluster from the DAO
cluster.setDao(clusterDao);
assertEquals(1, cluster.getAttributes().size());
assertEquals(Integer.class, cluster.getAttributes().iterator().next().getLastValue().getClass());

// Then the cluster contains all attributes from the on/off cluster, plus the manufacturer-specific attribute
assertEquals(onOffClusterAttributeIds.size() + 1, cluster.getAttributes().size());
for (Integer attributeId : onOffClusterAttributeIds) {
ZclAttribute attribute = cluster.getAttribute(attributeId);
assertNotNull(attribute);
assertEquals(new ZclOnOffCluster(endpoint).getAttribute(attributeId).getDataType(),
attribute.getDataType());
assertEquals(new ZclOnOffCluster(endpoint).getAttribute(attributeId).getMaximumReportingPeriod(),
attribute.getMaximumReportingPeriod());
assertEquals(new ZclOnOffCluster(endpoint).getAttribute(attributeId).getName(), attribute.getName());
}

// The onOff attribute has the state set correctly
ZclAttribute onOffAttribute = cluster.getAttribute(ATTR_ONOFF);
assertEquals(TRUE, onOffAttribute.getLastValue());
assertTrue(onOffAttribute.isImplemented());

// And the manufacturer-specific attribute has the state set correctly
ZclAttribute manufacturerSpecificAttribute = cluster.getAttribute(0xF000);
assertEquals(manufacturerSpecificAttribute.getManufacturerCode(), Integer.valueOf(0x1234));
assertEquals(manufacturerSpecificAttribute.getLastValue(), Integer.valueOf(123));
}

@Test
Expand Down

0 comments on commit 44c2c35

Please sign in to comment.