]> git.basschouten.com Git - openhab-addons.git/blob
488de983244059117520e4e2487cc119b4b8dc49
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.knx.internal.client;
14
15 import static org.openhab.binding.knx.internal.KNXBindingConstants.*;
16 import static org.openhab.binding.knx.internal.handler.DeviceConstants.*;
17
18 import java.util.Collections;
19 import java.util.HashMap;
20 import java.util.Map;
21 import java.util.Set;
22
23 import org.eclipse.jdt.annotation.NonNullByDefault;
24 import org.eclipse.jdt.annotation.Nullable;
25 import org.openhab.binding.knx.internal.handler.Firmware;
26 import org.openhab.binding.knx.internal.handler.Manufacturer;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30 import tuwien.auto.calimero.DataUnitBuilder;
31 import tuwien.auto.calimero.DeviceDescriptor;
32 import tuwien.auto.calimero.DeviceDescriptor.DD0;
33 import tuwien.auto.calimero.GroupAddress;
34 import tuwien.auto.calimero.IndividualAddress;
35 import tuwien.auto.calimero.mgmt.PropertyAccess.PID;
36
37 /**
38  * Client dedicated to read device specific information using the {@link DeviceInfoClient}.
39  *
40  * @author Simon Kaufmann - initial contribution and API.
41  *
42  */
43 @NonNullByDefault
44 public class DeviceInspector {
45
46     private static final long OPERATION_TIMEOUT = 5000;
47     private static final long OPERATION_INTERVAL = 2000;
48
49     private final Logger logger = LoggerFactory.getLogger(DeviceInspector.class);
50     private final DeviceInfoClient client;
51     private final IndividualAddress address;
52
53     public static class Result {
54         private final Map<String, String> properties;
55         private final Set<GroupAddress> groupAddresses;
56
57         public Result(Map<String, String> properties, Set<GroupAddress> groupAddresses) {
58             super();
59             this.properties = properties;
60             this.groupAddresses = groupAddresses;
61         }
62
63         public Map<String, String> getProperties() {
64             return properties;
65         }
66
67         public Set<GroupAddress> getGroupAddresses() {
68             return groupAddresses;
69         }
70     }
71
72     public DeviceInspector(DeviceInfoClient client, IndividualAddress address) {
73         this.client = client;
74         this.address = address;
75     }
76
77     private DeviceInfoClient getClient() {
78         return client;
79     }
80
81     @Nullable
82     public Result readDeviceInfo() {
83         if (!getClient().isConnected()) {
84             return null;
85         }
86
87         logger.debug("Fetching device information for address {}", address);
88         Map<String, String> properties = new HashMap<>();
89         properties.putAll(readDeviceDescription(address));
90         properties.putAll(readDeviceProperties(address));
91         return new Result(properties, Collections.emptySet());
92     }
93
94     private Map<String, String> readDeviceProperties(IndividualAddress address) {
95         Map<String, String> ret = new HashMap<>();
96         try {
97             Thread.sleep(OPERATION_INTERVAL);
98             // check if there is a Device Object in the KNX device
99             byte[] elements = getClient().readDeviceProperties(address, DEVICE_OBJECT, PID.OBJECT_TYPE, 0, 1, false,
100                     OPERATION_TIMEOUT);
101             if ((elements == null ? 0 : toUnsigned(elements)) == 1) {
102                 Thread.sleep(OPERATION_INTERVAL);
103                 String manufacturerID = Manufacturer.getName(toUnsigned(getClient().readDeviceProperties(address,
104                         DEVICE_OBJECT, PID.MANUFACTURER_ID, 1, 1, false, OPERATION_TIMEOUT)));
105                 Thread.sleep(OPERATION_INTERVAL);
106                 String serialNo = toHex(getClient().readDeviceProperties(address, DEVICE_OBJECT, PID.SERIAL_NUMBER, 1,
107                         1, false, OPERATION_TIMEOUT), "");
108                 Thread.sleep(OPERATION_INTERVAL);
109                 String hardwareType = toHex(getClient().readDeviceProperties(address, DEVICE_OBJECT, HARDWARE_TYPE, 1,
110                         1, false, OPERATION_TIMEOUT), " ");
111                 Thread.sleep(OPERATION_INTERVAL);
112                 String firmwareRevision = Integer.toString(toUnsigned(getClient().readDeviceProperties(address,
113                         DEVICE_OBJECT, PID.FIRMWARE_REVISION, 1, 1, false, OPERATION_TIMEOUT)));
114
115                 ret.put(MANUFACTURER_NAME, manufacturerID);
116                 if (serialNo != null) {
117                     ret.put(MANUFACTURER_SERIAL_NO, serialNo);
118                 }
119                 if (hardwareType != null) {
120                     ret.put(MANUFACTURER_HARDWARE_TYPE, hardwareType);
121                 }
122                 ret.put(MANUFACTURER_FIRMWARE_REVISION, firmwareRevision);
123                 logger.debug("Identified device {} as a {}, type {}, revision {}, serial number {}", address,
124                         manufacturerID, hardwareType, firmwareRevision, serialNo);
125             } else {
126                 logger.debug("The KNX device with address {} does not expose a Device Object", address);
127             }
128         } catch (InterruptedException e) {
129             logger.debug("Interrupted while fetching the device description for a device '{}' : {}", address,
130                     e.getMessage());
131         }
132         return ret;
133     }
134
135     private @Nullable String toHex(byte @Nullable [] input, String separator) {
136         return input == null ? null : DataUnitBuilder.toHex(input, separator);
137     }
138
139     private Map<String, String> readDeviceDescription(IndividualAddress address) {
140         Map<String, String> ret = new HashMap<>();
141         byte[] data = getClient().readDeviceDescription(address, 0, false, OPERATION_TIMEOUT);
142         if (data != null) {
143             final DD0 dd = DeviceDescriptor.DD0.from(data);
144
145             ret.put(FIRMWARE_TYPE, Firmware.getName(dd.firmwareType()));
146             ret.put(FIRMWARE_VERSION, Firmware.getName(dd.firmwareVersion()));
147             ret.put(FIRMWARE_SUBVERSION, Firmware.getName(dd.firmwareSubcode()));
148             logger.debug("The device with address {} is of type {}, version {}, subversion {}", address,
149                     Firmware.getName(dd.firmwareType()), Firmware.getName(dd.firmwareVersion()),
150                     Firmware.getName(dd.firmwareSubcode()));
151         } else {
152             logger.debug("The KNX device with address {} does not expose a Device Descriptor", address);
153         }
154         return ret;
155     }
156
157     private int toUnsigned(final byte @Nullable [] data) {
158         if (data == null) {
159             return 0;
160         }
161         int value = data[0] & 0xff;
162         if (data.length == 1) {
163             return value;
164         }
165         value = value << 8 | data[1] & 0xff;
166         if (data.length == 2) {
167             return value;
168         }
169         value = value << 16 | data[2] & 0xff << 8 | data[3] & 0xff;
170         return value;
171     }
172 }