From c524f6058435641fd91a599c856950a6c6f511fc Mon Sep 17 00:00:00 2001 From: Robert Schmid Date: Fri, 14 Oct 2022 19:31:42 +0200 Subject: [PATCH] Add window open mode channel for SPZB0001 Signed-off-by: Robert Schmid --- .../zigbee/ZigBeeBindingConstants.java | 5 + ...ConverterEurotronicSpzb0001WindowOpen.java | 195 ++++++++++++++++++ ...ZigBeeDefaultChannelConverterProvider.java | 2 + .../main/resources/OH-INF/thing/channels.xml | 8 + .../OH-INF/thing/eurotronic/spzb0001.xml | 55 +++++ .../src/main/resources/discovery.txt | 1 + 6 files changed, 266 insertions(+) create mode 100644 org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/converter/ZigBeeConverterEurotronicSpzb0001WindowOpen.java create mode 100644 org.openhab.binding.zigbee/src/main/resources/OH-INF/thing/eurotronic/spzb0001.xml 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 7aa6eb919..65fb3c8e5 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 @@ -212,6 +212,11 @@ public class ZigBeeBindingConstants { public static final ChannelTypeUID CHANNEL_THERMOSTAT_COOLING_DEMAND = new ChannelTypeUID( "zigbee:thermostat_coolingdemand"); + public static final String CHANNEL_NAME_EUROTRONIC_SPZB001_WINDOW_OPEN = "spzb0001windowopen"; + public static final String CHANNEL_LABEL_EUROTRONIC_SPZB001_WINDOW_OPEN = "Cooling Demand"; + public static final ChannelTypeUID CHANNEL_EUROTRONIC_SPZB001_WINDOW_OPEN = new ChannelTypeUID( + "zigbee:eurotronic_spzb0001_window_open"); + public static final String CHANNEL_NAME_DOORLOCK_STATE = "doorlockstate"; public static final String CHANNEL_LABEL_DOORLOCK_STATE = "Door Lock State"; public static final ChannelTypeUID CHANNEL_DOORLOCK_STATE = new ChannelTypeUID("zigbee:door_state"); diff --git a/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/converter/ZigBeeConverterEurotronicSpzb0001WindowOpen.java b/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/converter/ZigBeeConverterEurotronicSpzb0001WindowOpen.java new file mode 100644 index 000000000..a5e9a4edb --- /dev/null +++ b/org.openhab.binding.zigbee/src/main/java/org/openhab/binding/zigbee/internal/converter/ZigBeeConverterEurotronicSpzb0001WindowOpen.java @@ -0,0 +1,195 @@ +/** + * Copyright (c) 2010-2022 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.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +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.OnOffType; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ThingUID; +import org.openhab.core.thing.binding.builder.ChannelBuilder; +import org.openhab.core.types.Command; +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.ZclThermostatCluster; +import com.zsmartsystems.zigbee.zcl.protocol.ZclClusterType; +import com.zsmartsystems.zigbee.zcl.protocol.ZclDataType; + +/** + * Converter for the thermostat system mode channel. The SystemMode attribute specifies the current operating mode of + * the thermostat, + * + * @author Robert Schmid - Initial Contribution + * + */ +public class ZigBeeConverterEurotronicSpzb0001WindowOpen extends ZigBeeBaseChannelConverter + implements ZclAttributeListener { + private Logger logger = LoggerFactory.getLogger(ZigBeeConverterEurotronicSpzb0001WindowOpen.class); + + private final static int MANUFACTURER_EUROTRONIC = 0x1037; + private final static int ATTR_HOST_FLAGS = 0x4008; + private final static int WINDOW_OPEN_DISABLED = 16; + private final static int WINDOW_OPEN_ENABLED = 32; + + private ZclThermostatCluster cluster; + private ZclAttribute attribute; + + @Override + public Set getImplementedClientClusters() { + return Collections.singleton(ZclThermostatCluster.CLUSTER_ID); + } + + @Override + public Set getImplementedServerClusters() { + return Collections.emptySet(); + } + + private ZclThermostatCluster getCluster(ZigBeeEndpoint endpoint) { + ZclThermostatCluster cluster = (ZclThermostatCluster) endpoint.getInputCluster(ZclThermostatCluster.CLUSTER_ID); + if (cluster == null) { + return null; + } + + ZclAttribute hostFlagsAttribute = new ZclAttribute(cluster, ATTR_HOST_FLAGS, "Host Flags", + ZclDataType.UNSIGNED_24_BIT_INTEGER, false, true, true, true, MANUFACTURER_EUROTRONIC); + cluster.addAttributes(new HashSet<>(Arrays.asList(hostFlagsAttribute))); + + return cluster; + } + + @Override + public boolean initializeDevice() { + ZclThermostatCluster serverCluster = getCluster(endpoint); + if (serverCluster == null) { + logger.error("{}: Error opening device thermostat cluster", endpoint.getIeeeAddress()); + return false; + } + + try { + CommandResult bindResponse = bind(serverCluster).get(); + if (bindResponse.isSuccess()) { + // Configure reporting + ZclAttribute attribute = serverCluster.getAttribute(ATTR_HOST_FLAGS); + CommandResult reportingResponse = attribute + .setReporting(REPORTING_PERIOD_DEFAULT_MIN, REPORTING_PERIOD_DEFAULT_MAX).get(); + handleReportingResponse(reportingResponse, POLLING_PERIOD_DEFAULT, REPORTING_PERIOD_DEFAULT_MAX); + } else { + logger.debug("{}: Failed to bind thermostat cluster", endpoint.getIeeeAddress()); + } + } catch (InterruptedException | ExecutionException e) { + logger.error("{}: Exception setting reporting ", endpoint.getIeeeAddress(), e); + } + + return true; + } + + @Override + public boolean initializeConverter(ZigBeeThingHandler thing) { + super.initializeConverter(thing); + cluster = getCluster(endpoint); + if (cluster == null) { + logger.error("{}: Error opening device thermostat cluster", endpoint.getIeeeAddress()); + return false; + } + + attribute = cluster.getAttribute(ATTR_HOST_FLAGS); + if (attribute == null) { + logger.error("{}: Error opening device thermostat Eurotronic host flags attribute", endpoint.getIeeeAddress()); + return false; + } + + // Add a listener, then request the status + cluster.addAttributeListener(this); + return true; + } + + @Override + public void disposeConverter() { + cluster.removeAttributeListener(this); + } + + @Override + public void handleCommand(final Command command) { + Integer value = null; + if (command instanceof OnOffType) { + // OnOff switches between OFF=WINDOW_OPEN_DISABLED and ON=WINDOW_OPEN_ENABLED + value = command == OnOffType.ON ? WINDOW_OPEN_ENABLED : WINDOW_OPEN_DISABLED; + } + + if (value == null) { + logger.warn("{}: Host flags command {} [{}] was not processed", endpoint.getIeeeAddress(), command, + command.getClass().getSimpleName()); + return; + } + + monitorCommandResponse(command, attribute.writeValue(value)); + } + + @Override + public void handleRefresh() { + attribute.readValue(0); + } + + @Override + public Channel getChannel(ThingUID thingUID, ZigBeeEndpoint endpoint) { + ZclThermostatCluster cluster = getCluster(endpoint); + if (cluster == null) { + logger.trace("{}: Thermostat cluster not found", endpoint.getIeeeAddress()); + return null; + } + + // Try to read the host flags attribute + ZclAttribute attribute = cluster.getAttribute(ATTR_HOST_FLAGS); + Object value = attribute.readValue(Long.MAX_VALUE); + if (value == null) { + logger.trace("{}: Thermostat host flags returned null", endpoint.getIeeeAddress()); + return null; + } + + return ChannelBuilder + .create(createChannelUID(thingUID, endpoint, + ZigBeeBindingConstants.CHANNEL_NAME_EUROTRONIC_SPZB001_WINDOW_OPEN), + ZigBeeBindingConstants.ITEM_TYPE_SWITCH) + .withType(ZigBeeBindingConstants.CHANNEL_EUROTRONIC_SPZB001_WINDOW_OPEN) + .withLabel(ZigBeeBindingConstants.CHANNEL_LABEL_EUROTRONIC_SPZB001_WINDOW_OPEN) + .withProperties(createProperties(endpoint)).build(); + } + + @Override + public void attributeUpdated(ZclAttribute attribute, Object val) { + logger.debug("{}: ZigBee attribute reports {}", endpoint.getIeeeAddress(), attribute); + if (attribute.getClusterType() == ZclClusterType.THERMOSTAT + && attribute.isManufacturerSpecific() + && attribute.getManufacturerCode() == MANUFACTURER_EUROTRONIC + && attribute.getId() == ATTR_HOST_FLAGS) { + Integer value = (Integer) val; + if (value != null && (value & WINDOW_OPEN_DISABLED) != 0) { + updateChannelState(OnOffType.ON); + } else { + updateChannelState(OnOffType.OFF); + } + } + } +} 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 8ba122932..540829449 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 @@ -88,6 +88,8 @@ public ZigBeeDefaultChannelConverterProvider() { channelMap.put(ZigBeeBindingConstants.CHANNEL_THERMOSTAT_COOLING_DEMAND, ZigBeeConverterThermostatPiCoolingDemand.class); channelMap.put(ZigBeeBindingConstants.CHANNEL_THERMOSTAT_SYSTEMMODE, ZigBeeConverterThermostatSystemMode.class); + channelMap.put(ZigBeeBindingConstants.CHANNEL_EUROTRONIC_SPZB001_WINDOW_OPEN, + ZigBeeConverterEurotronicSpzb0001WindowOpen.class); channelMap.put(ZigBeeBindingConstants.CHANNEL_FANCONTROL, ZigBeeConverterFanControl.class); channelMap.put(ZigBeeBindingConstants.CHANNEL_WINDOWCOVERING_LIFT, ZigBeeConverterWindowCoveringLift.class); channelMap.put(ZigBeeBindingConstants.CHANNEL_INSTANTANEOUS_DEMAND, 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..63c783aa5 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 @@ -398,6 +398,14 @@ + + + Switch + + Window Open Mode Enabled + HVAC + + String diff --git a/org.openhab.binding.zigbee/src/main/resources/OH-INF/thing/eurotronic/spzb0001.xml b/org.openhab.binding.zigbee/src/main/resources/OH-INF/thing/eurotronic/spzb0001.xml new file mode 100644 index 000000000..892d0d776 --- /dev/null +++ b/org.openhab.binding.zigbee/src/main/resources/OH-INF/thing/eurotronic/spzb0001.xml @@ -0,0 +1,55 @@ + + + + + + Radiator Thermostat + WallSwitch + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + + + + + + diff --git a/org.openhab.binding.zigbee/src/main/resources/discovery.txt b/org.openhab.binding.zigbee/src/main/resources/discovery.txt index 0aaebea99..a58e48259 100644 --- a/org.openhab.binding.zigbee/src/main/resources/discovery.txt +++ b/org.openhab.binding.zigbee/src/main/resources/discovery.txt @@ -20,3 +20,4 @@ tuya_ts0041,modelId=TS0041 tuya_ts0042,modelId=TS0042 tuya_ts0043,modelId=TS0043 tuya_ts0044,modelId=TS0044 +eurotronic_spzb0001,vendor=Eurotronic,modelId=SPZB0001