diff --git a/com.zsmartsystems.zigbee/src/main/java/com/zsmartsystems/zigbee/zcl/ZclAttribute.java b/com.zsmartsystems.zigbee/src/main/java/com/zsmartsystems/zigbee/zcl/ZclAttribute.java index 4cbf929f0..044df27fc 100644 --- a/com.zsmartsystems.zigbee/src/main/java/com/zsmartsystems/zigbee/zcl/ZclAttribute.java +++ b/com.zsmartsystems.zigbee/src/main/java/com/zsmartsystems/zigbee/zcl/ZclAttribute.java @@ -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 diff --git a/com.zsmartsystems.zigbee/src/main/java/com/zsmartsystems/zigbee/zcl/ZclCluster.java b/com.zsmartsystems.zigbee/src/main/java/com/zsmartsystems/zigbee/zcl/ZclCluster.java index 0e9071d5f..f18280dc8 100644 --- a/com.zsmartsystems.zigbee/src/main/java/com/zsmartsystems/zigbee/zcl/ZclCluster.java +++ b/com.zsmartsystems.zigbee/src/main/java/com/zsmartsystems/zigbee/zcl/ZclCluster.java @@ -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 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 serverAttributes = initializeServerAttributes(); @@ -1598,20 +1598,21 @@ public void setDao(ZclClusterDao dao) { supportedCommandsGenerated.addAll(dao.getSupportedCommandsGenerated()); supportedCommandsReceived.addAll(dao.getSupportedCommandsReceived()); - Map daoZclAttributes = new HashMap<>(); + Map 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); + } } } diff --git a/com.zsmartsystems.zigbee/src/test/java/com/zsmartsystems/zigbee/zcl/ZclClusterTest.java b/com.zsmartsystems.zigbee/src/test/java/com/zsmartsystems/zigbee/zcl/ZclClusterTest.java index 0df7c80c3..1dda9e2b2 100644 --- a/com.zsmartsystems.zigbee/src/test/java/com/zsmartsystems/zigbee/zcl/ZclClusterTest.java +++ b/com.zsmartsystems.zigbee/src/test/java/com/zsmartsystems/zigbee/zcl/ZclClusterTest.java @@ -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; @@ -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; @@ -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 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 attributes = new HashMap<>(); - attributes.put(1, attributeDao); - clusterDao.setAttributes(attributes); + Map 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