2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.velbus.internal.handler;
15 import static org.openhab.binding.velbus.internal.VelbusBindingConstants.SUB_ADDRESS;
17 import java.lang.invoke.MethodHandles;
18 import java.util.List;
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.openhab.binding.velbus.internal.VelbusModuleAddress;
25 import org.openhab.binding.velbus.internal.VelbusPacketListener;
26 import org.openhab.binding.velbus.internal.config.VelbusThingConfig;
27 import org.openhab.binding.velbus.internal.packets.VelbusReadMemoryBlockPacket;
28 import org.openhab.binding.velbus.internal.packets.VelbusReadMemoryPacket;
29 import org.openhab.binding.velbus.internal.packets.VelbusStatusRequestPacket;
30 import org.openhab.binding.velbus.internal.packets.VelbusWriteMemoryPacket;
31 import org.openhab.core.config.core.Configuration;
32 import org.openhab.core.thing.Bridge;
33 import org.openhab.core.thing.Channel;
34 import org.openhab.core.thing.ChannelUID;
35 import org.openhab.core.thing.Thing;
36 import org.openhab.core.thing.ThingStatus;
37 import org.openhab.core.thing.ThingStatusDetail;
38 import org.openhab.core.thing.binding.BaseThingHandler;
39 import org.openhab.core.thing.binding.ThingHandler;
40 import org.openhab.core.thing.binding.builder.ChannelBuilder;
41 import org.openhab.core.thing.binding.builder.ThingBuilder;
42 import org.openhab.core.thing.type.ChannelKind;
43 import org.openhab.core.thing.type.ChannelTypeUID;
44 import org.openhab.core.util.HexUtils;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
49 * Base ThingHandler for all Velbus handlers.
51 * @author Cedric Boon - Initial contribution
54 public abstract class VelbusThingHandler extends BaseThingHandler implements VelbusPacketListener {
55 protected final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
57 protected @Nullable VelbusThingConfig velbusThingConfig;
58 private @Nullable VelbusBridgeHandler velbusBridgeHandler;
59 private @NonNullByDefault({}) VelbusModuleAddress velbusModuleAddress;
61 private int numberOfSubAddresses;
63 public VelbusThingHandler(Thing thing, int numberOfSubAddresses) {
66 this.numberOfSubAddresses = numberOfSubAddresses;
70 public void initialize() {
71 logger.debug("Initializing velbus handler.");
73 this.velbusThingConfig = getConfigAs(VelbusThingConfig.class);
75 Bridge bridge = getBridge();
76 initializeThing(bridge == null ? ThingStatus.OFFLINE : bridge.getStatus());
77 initializeChannelNames();
78 initializeChannelStates();
82 public void handleRemoval() {
83 VelbusBridgeHandler velbusBridgeHandler = getVelbusBridgeHandler();
85 if (velbusBridgeHandler != null && velbusModuleAddress != null) {
86 byte[] activeAddresses = velbusModuleAddress.getActiveAddresses();
88 for (int i = 0; i < activeAddresses.length; i++) {
89 velbusBridgeHandler.unregisterRelayStatusListener(activeAddresses[i]);
93 super.handleRemoval();
96 protected VelbusModuleAddress getModuleAddress() {
97 return velbusModuleAddress;
100 protected void updateChannelLabel(ChannelUID channelUID, String channelName) {
101 Channel existingChannel = thing.getChannel(channelUID.getId());
102 if (existingChannel != null) {
103 String acceptedItem = existingChannel.getAcceptedItemType();
104 Configuration configuration = existingChannel.getConfiguration();
105 Set<String> defaultTags = existingChannel.getDefaultTags();
106 String description = existingChannel.getDescription();
107 ChannelKind kind = existingChannel.getKind();
108 Map<String, String> properties = existingChannel.getProperties();
109 ChannelTypeUID type = existingChannel.getChannelTypeUID();
111 ThingBuilder thingBuilder = editThing();
112 Channel channel = ChannelBuilder.create(channelUID, acceptedItem).withConfiguration(configuration)
113 .withDefaultTags(defaultTags).withDescription(description != null ? description : "").withKind(kind)
114 .withLabel(channelName).withProperties(properties).withType(type).build();
115 thingBuilder.withoutChannel(channelUID).withChannel(channel);
116 updateThing(thingBuilder.build());
120 private void initializeThing(ThingStatus bridgeStatus) {
121 this.velbusModuleAddress = createVelbusModuleAddress(numberOfSubAddresses);
123 if (this.velbusModuleAddress != null) {
124 logger.debug("initializeThing thing {} with address {} bridge status {}", getThing().getUID(),
125 velbusModuleAddress.getAddress(), bridgeStatus);
127 // note: this call implicitly registers our handler as a listener on
129 if (getVelbusBridgeHandler() != null) {
130 if (bridgeStatus == ThingStatus.ONLINE) {
131 updateStatus(ThingStatus.ONLINE);
133 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
136 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
139 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, "Address is not known!");
143 protected @Nullable VelbusModuleAddress createVelbusModuleAddress(int numberOfSubAddresses) {
144 final VelbusThingConfig velbusThingConfig = this.velbusThingConfig;
145 if (velbusThingConfig != null) {
146 byte address = hexToByte(velbusThingConfig.address);
148 byte[] subAddresses = new byte[numberOfSubAddresses];
149 for (int i = 0; i < numberOfSubAddresses; i++) {
150 String propertyKey = SUB_ADDRESS + (i + 1);
151 String subAddress = getThing().getProperties().get(propertyKey);
152 if (subAddress != null) {
153 subAddresses[i] = hexToByte(subAddress);
155 subAddresses[i] = (byte) 0xFF;
159 return new VelbusModuleAddress(address, subAddresses);
165 private void initializeChannelNames() {
166 List<Channel> channels = this.getThing().getChannels();
167 for (int i = 0; i < channels.size(); i++) {
168 Channel channel = channels.get(i);
169 String channelUID = channel.getUID().getIdWithoutGroup();
171 if (getConfig().containsKey(channelUID)) {
172 String channelName = getConfig().get(channelUID).toString();
173 if (!channelName.equals(channel.getLabel())) {
174 updateChannelLabel(channel.getUID(), channelName);
180 private void initializeChannelStates() {
181 VelbusBridgeHandler velbusBridgeHandler = this.velbusBridgeHandler;
182 if (velbusBridgeHandler != null) {
183 VelbusStatusRequestPacket packet = new VelbusStatusRequestPacket(getModuleAddress().getAddress());
184 byte[] packetBytes = packet.getBytes();
186 velbusBridgeHandler.sendPacket(packetBytes);
190 protected byte hexToByte(String hexString) {
191 if (hexString.length() > 2) {
192 throw new IllegalArgumentException("hexString contains more than one byte: " + hexString);
195 return HexUtils.hexToBytes(hexString)[0];
198 protected void sendReadMemoryBlockPacket(VelbusBridgeHandler velbusBridgeHandler, int memoryAddress) {
199 VelbusReadMemoryBlockPacket packet = new VelbusReadMemoryBlockPacket(getModuleAddress().getAddress(),
201 byte[] packetBytes = packet.getBytes();
202 velbusBridgeHandler.sendPacket(packetBytes);
205 protected void sendReadMemoryPacket(VelbusBridgeHandler velbusBridgeHandler, int memoryAddress) {
206 VelbusReadMemoryPacket packet = new VelbusReadMemoryPacket(getModuleAddress().getAddress(), memoryAddress);
207 byte[] packetBytes = packet.getBytes();
208 velbusBridgeHandler.sendPacket(packetBytes);
211 protected void sendWriteMemoryPacket(VelbusBridgeHandler velbusBridgeHandler, int memoryAddress, byte data) {
212 VelbusWriteMemoryPacket packet = new VelbusWriteMemoryPacket(getModuleAddress().getAddress(), memoryAddress,
214 byte[] packetBytes = packet.getBytes();
215 velbusBridgeHandler.sendPacket(packetBytes);
218 protected @Nullable synchronized VelbusBridgeHandler getVelbusBridgeHandler() {
219 if (this.velbusBridgeHandler == null) {
220 Bridge bridge = getBridge();
221 if (bridge == null) {
224 ThingHandler bridgeHandler = bridge.getHandler();
225 if (bridgeHandler instanceof VelbusBridgeHandler) {
226 VelbusBridgeHandler velbusBridgeHandler = (VelbusBridgeHandler) bridgeHandler;
227 this.velbusBridgeHandler = velbusBridgeHandler;
229 if (velbusModuleAddress != null) {
230 byte[] activeAddresses = velbusModuleAddress.getActiveAddresses();
232 for (int i = 0; i < activeAddresses.length; i++) {
233 velbusBridgeHandler.registerPacketListener(activeAddresses[i], this);
239 return this.velbusBridgeHandler;