From 067a8479fa5112df6ccb0d1785141945626c733d Mon Sep 17 00:00:00 2001 From: Chris Jackson Date: Wed, 17 Jul 2024 18:41:31 +1200 Subject: [PATCH] Add support for PM2.5 channel Signed-off-by: Chris Jackson --- .../zigbee/ZigBeeBindingConstants.java | 4 + .../converter/ZigBeeConverterPM25.java | 178 ++++++++++++++++++ ...ZigBeeDefaultChannelConverterProvider.java | 1 + .../main/resources/OH-INF/thing/channels.xml | 9 + pom.xml | 2 +- 5 files changed, 193 insertions(+), 1 deletion(-) create mode 100644 org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/converter/ZigBeeConverterPM25.java diff --git a/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/ZigBeeBindingConstants.java b/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/ZigBeeBindingConstants.java index 04e2f2c89..8fe4d400e 100644 --- a/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/ZigBeeBindingConstants.java +++ b/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/ZigBeeBindingConstants.java @@ -71,6 +71,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"); diff --git a/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/converter/ZigBeeConverterPM25.java b/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/converter/ZigBeeConverterPM25.java new file mode 100644 index 000000000..cd44d979a --- /dev/null +++ b/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/converter/ZigBeeConverterPM25.java @@ -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 getImplementedClientClusters() { + return Collections.singleton(ZclPm25MeasurementCluster.CLUSTER_ID); + } + + @Override + public Set 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 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)); + } + } +} diff --git a/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/converter/ZigBeeDefaultChannelConverterProvider.java b/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/converter/ZigBeeDefaultChannelConverterProvider.java index d02ef25e3..b4e2b8dbf 100644 --- a/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/converter/ZigBeeDefaultChannelConverterProvider.java +++ b/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/converter/ZigBeeDefaultChannelConverterProvider.java @@ -96,6 +96,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); } diff --git a/org.openhab.binding.zigbee/src/main/resources/OH-INF/thing/channels.xml b/org.openhab.binding.zigbee/src/main/resources/OH-INF/thing/channels.xml index dbb4aee8f..8ea2d55aa 100644 --- a/org.openhab.binding.zigbee/src/main/resources/OH-INF/thing/channels.xml +++ b/org.openhab.binding.zigbee/src/main/resources/OH-INF/thing/channels.xml @@ -204,6 +204,15 @@ + + + Number:Density + + Indicates the current PM2.5 measurement + AirQuality + + + Number:Pressure diff --git a/pom.xml b/pom.xml index 917f4cc41..60f72e2e3 100644 --- a/pom.xml +++ b/pom.xml @@ -35,7 +35,7 @@ false - 1.4.14 + 1.4.15-SNAPSHOT 2.38.0 true