Skip to content

Commit

Permalink
Add metering channels (openhab#646)
Browse files Browse the repository at this point in the history
Signed-off-by: Chris Jackson <[email protected]>
  • Loading branch information
cdjackson authored Apr 25, 2021
1 parent 716f3a2 commit ed133c8
Show file tree
Hide file tree
Showing 6 changed files with 590 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,19 @@ public class ZigBeeBindingConstants {
public static final String CHANNEL_LABEL_WINDOWCOVERING_LIFT = "Window Covering Lift";
public static final ChannelTypeUID CHANNEL_WINDOWCOVERING_LIFT = new ChannelTypeUID("zigbee:windowcovering_lift");

public static final String CHANNEL_NAME_INSTANTANEOUS_DEMAND = "meteringinstantdemand";
public static final String CHANNEL_LABEL_INSTANTANEOUS_DEMAND = "Metering Instantaneous Demand";
public static final ChannelTypeUID CHANNEL_INSTANTANEOUS_DEMAND = new ChannelTypeUID(
"zigbee:metering_instantdemand");

public static final String CHANNEL_NAME_SUMMATION_DELIVERED = "meteringsumdelivered";
public static final String CHANNEL_LABEL_SUMMATION_DELIVERED = "Metering Summation Delivered";
public static final ChannelTypeUID CHANNEL_SUMMATION_DELIVERED = new ChannelTypeUID("zigbee:metering_sumdelivered");

public static final String CHANNEL_NAME_SUMMATION_RECEIVED = "meteringsumreceived";
public static final String CHANNEL_LABEL_SUMMATION_RECEIVED = "Metering Summation Received";
public static final ChannelTypeUID CHANNEL_SUMMATION_RECEIVED = new ChannelTypeUID("zigbee:metering_sumreceived");

public static final String CHANNEL_PROPERTY_ENDPOINT = "zigbee_endpoint";
public static final String CHANNEL_PROPERTY_PROFILEID = "zigbee_profileid";
public static final String CHANNEL_PROPERTY_INPUTCLUSTERS = "zigbee_inputclusters";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
/**
* 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;

import java.math.BigDecimal;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ExecutionException;

import org.openhab.binding.zigbee.ZigBeeBindingConstants;
import org.openhab.binding.zigbee.converter.ZigBeeBaseChannelConverter;
import org.openhab.binding.zigbee.handler.ZigBeeThingHandler;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.builder.ChannelBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.zsmartsystems.zigbee.CommandResult;
import com.zsmartsystems.zigbee.ZigBeeEndpoint;
import com.zsmartsystems.zigbee.zcl.ZclAttribute;
import com.zsmartsystems.zigbee.zcl.ZclAttributeListener;
import com.zsmartsystems.zigbee.zcl.clusters.ZclMeteringCluster;
import com.zsmartsystems.zigbee.zcl.protocol.ZclClusterType;

/**
* ZigBee channel converter for instantaneous demand measurement
*
* @author Chris Jackson - Initial Contribution
*
*/
public class ZigBeeConverterMeteringInstantaneousDemand extends ZigBeeBaseChannelConverter
implements ZclAttributeListener {
private Logger logger = LoggerFactory.getLogger(ZigBeeConverterMeteringInstantaneousDemand.class);

private ZclMeteringCluster clusterMetering;

private ZclAttribute attribute;

private Integer divisor;
private Integer multiplier;

@Override
public Set<Integer> getImplementedClientClusters() {
return Collections.singleton(ZclMeteringCluster.CLUSTER_ID);
}

@Override
public Set<Integer> getImplementedServerClusters() {
return Collections.emptySet();
}

@Override
public boolean initializeDevice() {
logger.debug("{}: Initialising electrical measurement cluster", endpoint.getIeeeAddress());

ZclMeteringCluster serverClusterMeasurement = (ZclMeteringCluster) endpoint
.getInputCluster(ZclMeteringCluster.CLUSTER_ID);
if (serverClusterMeasurement == null) {
logger.error("{}: Error opening metering cluster", endpoint.getIeeeAddress());
return false;
}

try {
CommandResult bindResponse = bind(serverClusterMeasurement).get();
if (bindResponse.isSuccess()) {
ZclAttribute attribute = serverClusterMeasurement
.getAttribute(ZclMeteringCluster.ATTR_INSTANTANEOUSDEMAND);
// Configure reporting - no faster than once per second - no slower than 2 hours.
CommandResult reportingResponse = attribute.setReporting(3, REPORTING_PERIOD_DEFAULT_MAX, 1).get();
handleReportingResponse(reportingResponse, POLLING_PERIOD_HIGH, REPORTING_PERIOD_DEFAULT_MAX);
} else {
pollingPeriod = POLLING_PERIOD_HIGH;
}
} catch (InterruptedException | ExecutionException e) {
logger.error("{}: Exception setting reporting ", endpoint.getIeeeAddress(), e);
return false;
}

return true;
}

@Override
public boolean initializeConverter(ZigBeeThingHandler thing) {
super.initializeConverter(thing);
clusterMetering = (ZclMeteringCluster) endpoint.getInputCluster(ZclMeteringCluster.CLUSTER_ID);
if (clusterMetering == null) {
logger.error("{}: Error opening metering cluster", endpoint.getIeeeAddress());
return false;
}

attribute = clusterMetering.getAttribute(ZclMeteringCluster.ATTR_INSTANTANEOUSDEMAND);

determineDivisorAndMultiplier(clusterMetering);

// Add a listener
clusterMetering.addAttributeListener(this);
return true;
}

@Override
public void disposeConverter() {

clusterMetering.removeAttributeListener(this);
}

@Override
public void handleRefresh() {
attribute.readValue(0);
}

@Override
public Channel getChannel(ThingUID thingUID, ZigBeeEndpoint endpoint) {
ZclMeteringCluster cluster = (ZclMeteringCluster) endpoint.getInputCluster(ZclMeteringCluster.CLUSTER_ID);
if (cluster == null) {
logger.trace("{}: Metering cluster not found", endpoint.getIeeeAddress());
return null;
}

try {
if (!cluster.discoverAttributes(false).get()
&& !cluster.isAttributeSupported(ZclMeteringCluster.ATTR_INSTANTANEOUSDEMAND)) {
logger.trace("{}: Metering cluster instantaneous demand not supported", endpoint.getIeeeAddress());

return null;
} else {
ZclAttribute attribute = cluster.getAttribute(ZclMeteringCluster.ATTR_INSTANTANEOUSDEMAND);
if (attribute.readValue(Long.MAX_VALUE) == null) {

logger.trace("{}: Metering cluster instantaneous demand returned null", endpoint.getIeeeAddress());
return null;
}
}
} catch (InterruptedException | ExecutionException e) {
logger.warn("{}: Exception discovering attributes in metering cluster", endpoint.getIeeeAddress(), e);
return null;
}

return ChannelBuilder
.create(createChannelUID(thingUID, endpoint, ZigBeeBindingConstants.CHANNEL_NAME_INSTANTANEOUS_DEMAND),
ZigBeeBindingConstants.ITEM_TYPE_NUMBER)
.withType(ZigBeeBindingConstants.CHANNEL_INSTANTANEOUS_DEMAND)
.withLabel(ZigBeeBindingConstants.CHANNEL_LABEL_INSTANTANEOUS_DEMAND)
.withProperties(createProperties(endpoint)).build();
}

@Override
public void attributeUpdated(ZclAttribute attribute, Object val) {
logger.debug("{}: ZigBee attribute reports {}", endpoint.getIeeeAddress(), attribute);
if (attribute.getCluster() == ZclClusterType.ELECTRICAL_MEASUREMENT
&& attribute.getId() == ZclMeteringCluster.ATTR_INSTANTANEOUSDEMAND) {
Integer value = (Integer) val;
BigDecimal valueCalibrated = BigDecimal.valueOf(value * multiplier / divisor);
updateChannelState(new DecimalType(valueCalibrated));
}
}

private void determineDivisorAndMultiplier(ZclMeteringCluster serverClusterMeasurement) {
ZclAttribute divisorAttribute = clusterMetering.getAttribute(ZclMeteringCluster.ATTR_DIVISOR);
ZclAttribute multiplierAttribute = clusterMetering.getAttribute(ZclMeteringCluster.ATTR_MULTIPLIER);

divisor = (Integer) divisorAttribute.readValue(Long.MAX_VALUE);
multiplier = (Integer) multiplierAttribute.readValue(Long.MAX_VALUE);
if (divisor == null || multiplier == null) {
divisor = 1;
multiplier = 1;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
/**
* 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;

import java.math.BigDecimal;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ExecutionException;

import org.openhab.binding.zigbee.ZigBeeBindingConstants;
import org.openhab.binding.zigbee.converter.ZigBeeBaseChannelConverter;
import org.openhab.binding.zigbee.handler.ZigBeeThingHandler;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.builder.ChannelBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.zsmartsystems.zigbee.CommandResult;
import com.zsmartsystems.zigbee.ZigBeeEndpoint;
import com.zsmartsystems.zigbee.zcl.ZclAttribute;
import com.zsmartsystems.zigbee.zcl.ZclAttributeListener;
import com.zsmartsystems.zigbee.zcl.clusters.ZclMeteringCluster;
import com.zsmartsystems.zigbee.zcl.protocol.ZclClusterType;

/**
* ZigBee channel converter for summation delivered measurement
*
* @author Chris Jackson - Initial Contribution
*
*/
public class ZigBeeConverterMeteringSummationDelivered extends ZigBeeBaseChannelConverter
implements ZclAttributeListener {
private Logger logger = LoggerFactory.getLogger(ZigBeeConverterMeteringSummationDelivered.class);

private ZclMeteringCluster clusterMetering;

private ZclAttribute attribute;

private Integer divisor;
private Integer multiplier;

@Override
public Set<Integer> getImplementedClientClusters() {
return Collections.singleton(ZclMeteringCluster.CLUSTER_ID);
}

@Override
public Set<Integer> getImplementedServerClusters() {
return Collections.emptySet();
}

@Override
public boolean initializeDevice() {
logger.debug("{}: Initialising electrical measurement cluster", endpoint.getIeeeAddress());

ZclMeteringCluster serverClusterMeasurement = (ZclMeteringCluster) endpoint
.getInputCluster(ZclMeteringCluster.CLUSTER_ID);
if (serverClusterMeasurement == null) {
logger.error("{}: Error opening metering cluster", endpoint.getIeeeAddress());
return false;
}

try {
CommandResult bindResponse = bind(serverClusterMeasurement).get();
if (bindResponse.isSuccess()) {
ZclAttribute attribute = serverClusterMeasurement
.getAttribute(ZclMeteringCluster.ATTR_CURRENTSUMMATIONDELIVERED);
// Configure reporting - no faster than once per second - no slower than 2 hours.
CommandResult reportingResponse = attribute.setReporting(3, REPORTING_PERIOD_DEFAULT_MAX, 1).get();
handleReportingResponse(reportingResponse, POLLING_PERIOD_HIGH, REPORTING_PERIOD_DEFAULT_MAX);
} else {
pollingPeriod = POLLING_PERIOD_HIGH;
}
} catch (InterruptedException | ExecutionException e) {
logger.error("{}: Exception setting reporting ", endpoint.getIeeeAddress(), e);
return false;
}

return true;
}

@Override
public boolean initializeConverter(ZigBeeThingHandler thing) {
super.initializeConverter(thing);
clusterMetering = (ZclMeteringCluster) endpoint.getInputCluster(ZclMeteringCluster.CLUSTER_ID);
if (clusterMetering == null) {
logger.error("{}: Error opening metering cluster", endpoint.getIeeeAddress());
return false;
}

attribute = clusterMetering.getAttribute(ZclMeteringCluster.ATTR_CURRENTSUMMATIONDELIVERED);

determineDivisorAndMultiplier(clusterMetering);

// Add a listener
clusterMetering.addAttributeListener(this);
return true;
}

@Override
public void disposeConverter() {
clusterMetering.removeAttributeListener(this);
}

@Override
public void handleRefresh() {
attribute.readValue(0);
}

@Override
public Channel getChannel(ThingUID thingUID, ZigBeeEndpoint endpoint) {
ZclMeteringCluster cluster = (ZclMeteringCluster) endpoint.getInputCluster(ZclMeteringCluster.CLUSTER_ID);
if (cluster == null) {
logger.trace("{}: Metering cluster not found", endpoint.getIeeeAddress());
return null;
}

try {
if (!cluster.discoverAttributes(false).get()
&& !cluster.isAttributeSupported(ZclMeteringCluster.ATTR_CURRENTSUMMATIONDELIVERED)) {
logger.trace("{}: Metering cluster summation delivered not supported", endpoint.getIeeeAddress());

return null;
} else {
ZclAttribute attribute = cluster.getAttribute(ZclMeteringCluster.ATTR_CURRENTSUMMATIONDELIVERED);
if (attribute.readValue(Long.MAX_VALUE) == null) {

logger.trace("{}: Metering cluster summation delivered returned null", endpoint.getIeeeAddress());
return null;
}
}
} catch (InterruptedException | ExecutionException e) {
logger.warn("{}: Exception discovering attributes in metering cluster", endpoint.getIeeeAddress(), e);
return null;
}

return ChannelBuilder
.create(createChannelUID(thingUID, endpoint, ZigBeeBindingConstants.CHANNEL_NAME_SUMMATION_DELIVERED),
ZigBeeBindingConstants.ITEM_TYPE_NUMBER)
.withType(ZigBeeBindingConstants.CHANNEL_SUMMATION_DELIVERED)
.withLabel(ZigBeeBindingConstants.CHANNEL_LABEL_SUMMATION_DELIVERED)
.withProperties(createProperties(endpoint)).build();
}

@Override
public void attributeUpdated(ZclAttribute attribute, Object val) {
logger.debug("{}: ZigBee attribute reports {}", endpoint.getIeeeAddress(), attribute);
if (attribute.getCluster() == ZclClusterType.ELECTRICAL_MEASUREMENT
&& attribute.getId() == ZclMeteringCluster.ATTR_CURRENTSUMMATIONDELIVERED) {
Integer value = (Integer) val;
BigDecimal valueCalibrated = BigDecimal.valueOf(value * multiplier / divisor);
updateChannelState(new DecimalType(valueCalibrated));
}
}

private void determineDivisorAndMultiplier(ZclMeteringCluster serverClusterMeasurement) {
ZclAttribute divisorAttribute = clusterMetering.getAttribute(ZclMeteringCluster.ATTR_DIVISOR);
ZclAttribute multiplierAttribute = clusterMetering.getAttribute(ZclMeteringCluster.ATTR_MULTIPLIER);

divisor = (Integer) divisorAttribute.readValue(Long.MAX_VALUE);
multiplier = (Integer) multiplierAttribute.readValue(Long.MAX_VALUE);
if (divisor == null || multiplier == null) {
divisor = 1;
multiplier = 1;
}
}

}
Loading

0 comments on commit ed133c8

Please sign in to comment.