Skip to content

Commit

Permalink
Add support for PM2.5 channel
Browse files Browse the repository at this point in the history
Signed-off-by: Chris Jackson <[email protected]>
  • Loading branch information
cdjackson committed Dec 10, 2024
1 parent 8193316 commit 1209a53
Show file tree
Hide file tree
Showing 5 changed files with 193 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ public class ZigBeeBindingConstants {
public static final ChannelTypeUID CHANNEL_HUMIDITY_VALUE = new ChannelTypeUID(
"zigbee:measurement_relativehumidity");

public static final String CHANNEL_NAME_PM25_VALUE = "pm25";
public static final String CHANNEL_LABEL_PM25_VALUE = "Particulate Matter PM2.5";
public static final ChannelTypeUID CHANNEL_PM25_VALUE = new ChannelTypeUID("zigbee:measurement_pm25");

public static final String CHANNEL_NAME_PRESSURE_VALUE = "pressure";
public static final String CHANNEL_LABEL_PRESSURE_VALUE = "Atmospheric Pressure";
public static final ChannelTypeUID CHANNEL_PRESSURE_VALUE = new ChannelTypeUID("zigbee:measurement_pressure");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
/**
* Copyright (c) 2010-2024 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.ArrayList;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;

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.ZclReportingConfig;
import org.openhab.core.config.core.Configuration;
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.ZclPm25MeasurementCluster;
import com.zsmartsystems.zigbee.zcl.protocol.ZclClusterType;

/**
* Converter for the illuminance channel
*
* @author Chris Jackson - Initial Contribution
*
*/
public class ZigBeeConverterPM25 extends ZigBeeBaseChannelConverter implements ZclAttributeListener {
private Logger logger = LoggerFactory.getLogger(ZigBeeConverterPM25.class);

private static BigDecimal CHANGE_DEFAULT = new BigDecimal(5000);
private static BigDecimal CHANGE_MIN = new BigDecimal(10);
private static BigDecimal CHANGE_MAX = new BigDecimal(20000);

private ZclPm25MeasurementCluster cluster;
private ZclAttribute attribute;

private ZclReportingConfig configReporting;

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

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

@Override
public boolean initializeDevice() {
ZclPm25MeasurementCluster serverCluster = (ZclPm25MeasurementCluster) endpoint
.getInputCluster(ZclPm25MeasurementCluster.CLUSTER_ID);
if (serverCluster == null) {
logger.error("{}: Error opening device PM2.5 measurement cluster", endpoint.getIeeeAddress());
return false;
}

ZclReportingConfig reporting = new ZclReportingConfig(channel);

try {
CommandResult bindResponse = bind(serverCluster).get();
if (bindResponse.isSuccess()) {
// Configure reporting - no faster than once per second - no slower than 2 hours.
ZclAttribute attribute = serverCluster.getAttribute(ZclPm25MeasurementCluster.ATTR_MEASUREDVALUE);
CommandResult reportingResponse = attribute.setReporting(reporting.getReportingTimeMin(),
reporting.getReportingTimeMax(), reporting.getReportingChange()).get();
handleReportingResponse(reportingResponse, POLLING_PERIOD_DEFAULT, reporting.getPollingPeriod());
}
} catch (InterruptedException | ExecutionException e) {
logger.debug("{}: Exception configuring measured value reporting", endpoint.getIeeeAddress(), e);
return false;
}
return true;
}

@Override
public boolean initializeConverter(ZigBeeThingHandler thing) {
super.initializeConverter(thing);
cluster = (ZclPm25MeasurementCluster) endpoint.getInputCluster(ZclPm25MeasurementCluster.CLUSTER_ID);
if (cluster == null) {
logger.error("{}: Error opening device PM2.5 measurement cluster", endpoint.getIeeeAddress());
return false;
}

attribute = cluster.getAttribute(ZclPm25MeasurementCluster.ATTR_MEASUREDVALUE);
if (attribute == null) {
logger.error("{}: Error opening device PM2.5 measurement attribute", endpoint.getIeeeAddress());
return false;
}

// Add a listener, then request the status
cluster.addAttributeListener(this);

// Create a configuration handler and get the available options
configReporting = new ZclReportingConfig(channel);
configReporting.setAnalogue(CHANGE_DEFAULT, CHANGE_MIN, CHANGE_MAX);
configOptions = new ArrayList<>();
configOptions.addAll(configReporting.getConfiguration());

return true;
}

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

@Override
public int getPollingPeriod() {
return configReporting.getPollingPeriod();
}

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

@Override
public void updateConfiguration(@NonNull Configuration currentConfiguration,
Map<String, Object> updatedParameters) {
if (configReporting.updateConfiguration(currentConfiguration, updatedParameters)) {
try {
ZclAttribute attribute = cluster.getAttribute(ZclPm25MeasurementCluster.ATTR_MEASUREDVALUE);
CommandResult reportingResponse;
reportingResponse = attribute.setReporting(configReporting.getReportingTimeMin(),
configReporting.getReportingTimeMax(), configReporting.getReportingChange()).get();
handleReportingResponse(reportingResponse, configReporting.getPollingPeriod(),
configReporting.getReportingTimeMax());
} catch (InterruptedException | ExecutionException e) {
logger.debug("{}: Illuminance measurement exception setting reporting", endpoint.getIeeeAddress(), e);
}
}
}

@Override
public Channel getChannel(ThingUID thingUID, ZigBeeEndpoint endpoint) {
if (endpoint.getInputCluster(ZclPm25MeasurementCluster.CLUSTER_ID) == null) {
logger.trace("{}: Illuminance measurement cluster not found", endpoint.getIeeeAddress());
return null;
}
return ChannelBuilder
.create(createChannelUID(thingUID, endpoint, ZigBeeBindingConstants.CHANNEL_NAME_PM25_VALUE),
ZigBeeBindingConstants.ITEM_TYPE_NUMBER)
.withType(ZigBeeBindingConstants.CHANNEL_PM25_VALUE)
.withLabel(ZigBeeBindingConstants.CHANNEL_LABEL_PM25_VALUE).withProperties(createProperties(endpoint))
.build();
}

@Override
public void attributeUpdated(ZclAttribute attribute, Object val) {
if (attribute.getClusterType() == ZclClusterType.PM2_5_MEASUREMENT
&& attribute.getId() == ZclPm25MeasurementCluster.ATTR_MEASUREDVALUE) {
logger.debug("{}: ZigBee attribute reports {}", endpoint.getIeeeAddress(), attribute);
updateChannelState(new DecimalType(Math.pow(10.0, (Integer) val / 10000.0) - 1));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ public ZigBeeDefaultChannelConverterProvider() {
ZigBeeConverterMeteringSummationDelivered.class);
channelMap.put(ZigBeeBindingConstants.CHANNEL_SUMMATION_RECEIVED,
ZigBeeConverterMeteringSummationReceived.class);
channelMap.put(ZigBeeBindingConstants.CHANNEL_PM25_VALUE, ZigBeeConverterPM25.class);
channelMap.put(ZigBeeBindingConstants.CHANNEL_TUYA_BUTTON, ZigBeeConverterTuyaButton.class);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,15 @@
<state pattern="%.0f" readOnly="true" />
</channel-type>

<!-- Particulate Matter 2.5 Channel -->
<channel-type id="measurement_pm25">
<item-type>Number:Density</item-type>
<label>Air Particulate PM2.5</label>
<description>Indicates the current PM2.5 measurement</description>
<category>AirQuality</category>
<state pattern="%.1f %unit%" readOnly="true" />
</channel-type>

<!-- Atmospheric Pressure Channel -->
<channel-type id="measurement_pressure">
<item-type>Number:Pressure</item-type>
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@

<properties>
<report.fail.on.error>false</report.fail.on.error>
<zsmartsystems.version>1.4.14</zsmartsystems.version>
<zsmartsystems.version>1.4.15-SNAPSHOT</zsmartsystems.version>
<spotless.version>2.38.0</spotless.version>
<spotless.check.skip>true</spotless.check.skip> <!-- Spotless disabled for now -->
<!-- Eclipse Java formatter version 4.26+ does not check test files -->
Expand Down

0 comments on commit 1209a53

Please sign in to comment.