All Downloads are FREE. Search and download functionalities are using the official Maven repository.

tuwien.auto.calimero.tools.DeviceInfo Maven / Gradle / Ivy

There is a newer version: 2.6-rc1
Show newest version
/*
    Calimero 2 - A library for KNX network access
    Copyright (c) 2011, 2018 B. Malinowsky

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

    Linking this library statically or dynamically with other modules is
    making a combined work based on this library. Thus, the terms and
    conditions of the GNU General Public License cover the whole
    combination.

    As a special exception, the copyright holders of this library give you
    permission to link this library with independent modules to produce an
    executable, regardless of the license terms of these independent
    modules, and to copy and distribute the resulting executable under terms
    of your choice, provided that you also meet, for each linked independent
    module, the terms and conditions of the license of that module. An
    independent module is a module which is not derived from or based on
    this library. If you modify this library, you may extend this exception
    to your version of the library, but you are not obligated to do so. If
    you do not wish to do so, delete this exception statement from your
    version.
*/

package tuwien.auto.calimero.tools;

import java.io.ByteArrayOutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.slf4j.Logger;

import tuwien.auto.calimero.DataUnitBuilder;
import tuwien.auto.calimero.DeviceDescriptor;
import tuwien.auto.calimero.DeviceDescriptor.DD0;
import tuwien.auto.calimero.GroupAddress;
import tuwien.auto.calimero.IndividualAddress;
import tuwien.auto.calimero.KNXAddress;
import tuwien.auto.calimero.KNXException;
import tuwien.auto.calimero.KNXFormatException;
import tuwien.auto.calimero.KNXIllegalArgumentException;
import tuwien.auto.calimero.KNXRemoteException;
import tuwien.auto.calimero.Settings;
import tuwien.auto.calimero.dptxlator.DPTXlator;
import tuwien.auto.calimero.dptxlator.DPTXlatorBoolean;
import tuwien.auto.calimero.dptxlator.TranslatorTypes;
import tuwien.auto.calimero.knxnetip.KNXnetIPConnection;
import tuwien.auto.calimero.link.KNXLinkClosedException;
import tuwien.auto.calimero.link.KNXNetworkLink;
import tuwien.auto.calimero.link.KNXNetworkLinkFT12;
import tuwien.auto.calimero.link.KNXNetworkLinkIP;
import tuwien.auto.calimero.link.KNXNetworkLinkTpuart;
import tuwien.auto.calimero.link.KNXNetworkLinkUsb;
import tuwien.auto.calimero.link.medium.KNXMediumSettings;
import tuwien.auto.calimero.link.medium.TPSettings;
import tuwien.auto.calimero.log.LogService;
import tuwien.auto.calimero.mgmt.Destination;
import tuwien.auto.calimero.mgmt.LocalDeviceManagementUsb;
import tuwien.auto.calimero.mgmt.LocalDeviceMgmtAdapter;
import tuwien.auto.calimero.mgmt.ManagementClient;
import tuwien.auto.calimero.mgmt.PropertyAccess;
import tuwien.auto.calimero.mgmt.PropertyAccess.PID;
import tuwien.auto.calimero.mgmt.PropertyAdapter;
import tuwien.auto.calimero.mgmt.PropertyClient;
import tuwien.auto.calimero.mgmt.RemotePropertyServiceAdapter;
import tuwien.auto.calimero.serial.usb.UsbConnection;
import tuwien.auto.calimero.tools.Main.ShutdownHandler;

/**
 * A tool for Calimero showing device information of a device in a KNX network.
 * 

* DeviceInfo is a {@link Runnable} tool implementation allowing a user to read information about a * KNX device.
*
* This tool supports KNX network access using a KNXnet/IP, KNX IP, USB, FT1.2, or TP-UART * connection. It uses the {@link ManagementClient} functionality of the library to read KNX device * description, properties, and memory locations. It collects and shows device information similar * to the ETS. *

* When running this tool from the console, the main- method of this class is invoked, * otherwise use this class in the context appropriate to a {@link Runnable}.
* In console mode, the KNX device information, as well as errors and problems during its execution * are written to System.out. * * @author B. Malinowsky */ public class DeviceInfo implements Runnable { /** Device parameter that can be queried by a client. */ public interface Parameter { /** * Name of parameter. * * @return unique parameter name */ String name(); } /** * Common device info parameters. */ @SuppressWarnings("checkstyle:javadocvariable") public enum CommonParameter implements Parameter { DeviceDescriptor, KnxMedium, FirmwareType, FirmwareVersion, HardwareType, SerialNumber, DomainAddress, MaxApduLength, Manufacturer, ManufacturerData, DeviceTypeNumber, SoftwareVersion, /** Actual PEI type is determined by reading the ADC. */ ActualPeiType, /** * Required PEI type for user and application software, either taken from Application Program interface object * or read from BCU PEI type address location. */ RequiredPeiType, FirmwareRevision, RunError, ProgrammingMode, SystemState, RoutingCount, GroupObjTableLocation, GroupAddrTableEntries, DeviceAddress, GroupAddresses, ProgramVersion, LoadStateControl, LoadStateError, RunStateControl, OrderInfo, } /** * cEMI server parameters. */ public enum CemiParameter implements Parameter { MediumType, SupportedCommModes, SelectedCommMode, ClientAddress, SupportedRfModes, SelectedRfMode, SupportedFilteringModes, SelectedFilteringModes } /** * KNX IP device parameters. */ @SuppressWarnings("checkstyle:javadocvariable") public enum KnxipParameter implements Parameter { DeviceName, Capabilities, MacAddress, IPAddress, SubnetMask, DefaultGateway, CurrentIPAddress, CurrentSubnetMask, CurrentDefaultGateway, IPAssignment, ConfiguredIPAssignment, DhcpServer, CurrentIPAssignment, RoutingMulticast, TimeToLive, TransmitToIP, AdditionalIndividualAddresses } /** * RF medium parameters. */ public enum RfParameter implements Parameter { DomainAddress } // not in a category yet public enum InternalParameter implements Parameter { IndividualAddressWriteEnabled, ServiceControl, AdditionalProfile, ErrorFlags } /** * Result container of reading the device information. */ public static final class Result { private final Map formatted = new HashMap<>(); private final Map raw = new HashMap<>(); /** * Returns the formatted value of the requested device information parameter. * * @param p parameter indicating the requested device information * @return value formatted as String */ public String formatted(final Parameter p) { return formatted.get(p); } /** * Returns the raw data of the requested device information parameter, if available. * * @param p parameter specifying the requested device information * @return raw data for parameter, or empty array */ public byte[] raw(final Parameter p) { return raw.get(p); } } private static final String tool = "DeviceInfo"; private static final String sep = System.getProperty("line.separator"); // Interface Object "Addresstable Object" in interface object server private static final int addresstableObject = 1; // Interface Object "Associationtable Object" in interface object server private static final int assoctableObject = 2; // Interface Object "Application Program Object" in interface object server private static final int appProgramObject = 3; // Interface Object "Interface Program Object" in interface object server private static final int interfaceProgramObject = 4; // Interface Object "cEMI Server Object" in interface object server private static final int cemiServerObject = 8; // Interface Object "KNXnet/IP Parameter Object" in interface object server private static final int knxnetipObject = 11; // Interface Object "RF Medium Object" in interface object server private static final int rfMediumObject = 19; // property id to distinguish hardware types which are using the same // device descriptor mask version private static final int pidHardwareType = 78; // Indices of interface objects in interface object server private int deviceObjectIdx = -1; private int addresstableObjectIdx = -1; private int assoctableObjectIdx = -1; private int appProgramObjectIdx = -1; private int interfaceProgramObjectIdx = -1; private int knxnetipObjectIdx = -1; private int cemiServerObjectIdx = -1; private int rfMediumObjectIdx = -1; private DeviceDescriptor dd; private static Logger out = LogService.getLogger("calimero.tools"); private ManagementClient mc; private Destination d; private PropertyClient pc; private final Map options = new HashMap<>(); private Result result; /** * Creates a new DeviceInfo instance using the supplied options. *

* Mandatory arguments are the connection options depending on the type * of connection to the KNX network, and the KNX device individual address ("area.line.device"). * See {@link #main(String[])} for the list of options. * * @param args list with options * @throws KNXIllegalArgumentException on unknown/invalid options */ public DeviceInfo(final String[] args) { // read in user-supplied command line options try { parseOptions(args); } catch (final KNXIllegalArgumentException e) { throw e; } catch (final RuntimeException e) { throw new KNXIllegalArgumentException(e.getMessage(), e); } } /** * Entry point for running DeviceInfo. *

* Syntax: DeviceInfo [options] <host|port> [<KNX device address>] *

* Running the tool without a KNX device address will read the device info of the local KNX interface (KNXnet/IP and * USB only).
* To show usage message of the tool on the console, supply the command line option --help (or -h). Command line * options are treated case sensitive. Available options for connecting to the KNX device in question: *

    *
  • no arguments: only show short description and version info
  • *
  • --help -h show help message
  • *
  • --version show tool/library version and exit
  • *
  • --verbose -v enable verbose status output
  • *
  • --localhost id  local IP/host name
  • *
  • --localport number  local UDP port (default system assigned)
  • *
  • --port -p number  UDP port on host (default 3671)
  • *
  • --nat -n enable Network Address Translation
  • *
  • --ft12 -f use FT1.2 serial communication
  • *
  • --usb -u use KNX USB communication
  • *
  • --tpuart use TP-UART communication
  • *
  • --medium -m id  KNX medium [tp1|p110|knxip|rf] (defaults to tp1)
  • *
  • --domain address  domain address on open KNX medium (PL or RF)
  • *
  • --knx-address -k KNX address  KNX device address of local endpoint
  • *
* The --knx-address option is only necessary if an access protocol is selected that directly * communicates with the KNX network, i.e., KNX IP or TP-UART. The selected KNX individual address shall be unique * in a network, and the subnetwork address (area and line) should be set to match the network configuration. * * @param args command line options for running the device info tool */ public static void main(final String[] args) { try { final DeviceInfo d = new DeviceInfo(args); final ShutdownHandler sh = new ShutdownHandler().register(); d.run(); sh.unregister(); } catch (final Throwable t) { out.error("parsing options", t); } } @Override public void run() { Exception thrown = null; boolean canceled = false; final IndividualAddress device = (IndividualAddress) options.get("device"); result = new Result(); try { if (options.isEmpty()) { out(tool + " - Read KNX device information"); showVersion(); out("Type --help for help message"); return; } if (options.containsKey("help")) { showUsage(); return; } if (options.containsKey("version")) { showVersion(); return; } if (device != null) { // setup for reading device info of remote device try (KNXNetworkLink link = createLink(); RemotePropertyServiceAdapter adapter = new RemotePropertyServiceAdapter(link, device, e -> {}, true)) { mc = adapter.managementClient(); d = adapter.destination(); pc = new PropertyClient(adapter); out.info("Reading data from device {}, might take some seconds ...", device); readDeviceInfo(); } } else if (options.containsKey("usb")) { // setup for reading device info of usb interface try (UsbConnection conn = new UsbConnection((String) options.get("host")); PropertyAdapter adapter = new LocalDeviceManagementUsb(conn, e -> {}, false)) { dd = conn.deviceDescriptor(); pc = new PropertyClient(adapter); out.info("Reading info of KNX USB adapter {}, might take some seconds ...", dd); readDeviceInfo(); } } else { // setup for reading device info of knxnet/ip interface final InetSocketAddress local = Main.createLocalSocket((InetAddress) options.get("localhost"), (Integer) options.get("localport")); final InetSocketAddress server = new InetSocketAddress(Main.parseHost((String) options.get("host")), (Integer) options.get("port")); try (PropertyAdapter adapter = new LocalDeviceMgmtAdapter(local, server, options.containsKey("nat"), e -> {}, options.containsKey("emulatewriteenable"))) { pc = new PropertyClient(adapter); out.info("Reading info of KNXnet/IP {}, might take some seconds ...", adapter.getName()); readDeviceInfo(); } } } catch (KNXException | RuntimeException e) { thrown = e; } catch (final InterruptedException e) { canceled = true; Thread.currentThread().interrupt(); } finally { if (!result.formatted.isEmpty()) onDeviceInformation(device == null ? KNXMediumSettings.BackboneRouter : device, result); onCompletion(thrown, canceled); } } /** * Invoked on successfully finishing reading the device information of a KNX device. * * @param device KNX device address * @param info holds the result of reading KNX device information; depending on the device, not all available * parameters might be set */ protected void onDeviceInformation(final IndividualAddress device, final Result info) {} /** * Invoked after successfully reading a KNX device parameter. If a device parameter is not available or accessible * in the KNX device, this method won't be called. * * @param parameter the parameter read from the device * @param value formatted value of that parameter * @param raw raw value of that parameter */ protected void onDeviceInformation(final Parameter parameter, final String value, final byte[] raw) { out(parameter, value, raw); } /** * Called by this tool on completion. * * @param thrown the thrown exception if operation completed due to a raised exception, * null otherwise * @param canceled whether the operation got canceled before its planned end */ protected void onCompletion(final Exception thrown, final boolean canceled) { if (canceled) out.info("reading device info canceled"); if (thrown != null) out.error("completed", thrown); } private void out(final Parameter p, final String value, final byte[] raw) { final boolean verbose = options.containsKey("verbose"); // create human readable name from parameter by inserting some spaces final String name = p.name().replaceAll("([A-Z])", " $1").replace("I P", "IP").trim(); final String s = name + " = " + value; final String hex = raw.length > 0 ? "0x" + DataUnitBuilder.toHex(raw, "") : "n/a"; // left-pad verbose output final int n = Math.max(1, 60 - s.length()); final String detail = verbose ? String.format("%" + n + "s%s]", "[raw=", hex) : ""; System.out.println(s + detail); } private void findInterfaceObjects() throws KNXException, InterruptedException { // check if there are any interface object at all, i.e., the Device Object if (readElements(0, PID.OBJECT_TYPE) == 0) return; deviceObjectIdx = 0; final int objects = readElements(deviceObjectIdx, PropertyAccess.PID.IO_LIST); if (objects == 0) { // device only has device- and cEMI server-object cemiServerObjectIdx = 1; // interface object type 8 out.info("Device implements only Device Object and cEMI Object"); return; } final byte[] data = read(deviceObjectIdx, PropertyAccess.PID.IO_LIST, 1, objects); if (data == null) return; // TODO this will give us only the last object instance in case of several IO instances of the same type for (int i = 0; i < objects; ++i) { final int type = (data[2 * i] & 0xff) << 8 | (data[2 * i + 1] & 0xff); if (type == addresstableObject) addresstableObjectIdx = i; else if (type == assoctableObject) assoctableObjectIdx = i; else if (type == appProgramObject) appProgramObjectIdx = i; else if (type == interfaceProgramObject) interfaceProgramObjectIdx = i; else if (type == cemiServerObject) cemiServerObjectIdx = i; else if (type == knxnetipObject) knxnetipObjectIdx = i; else if (type == rfMediumObject) rfMediumObjectIdx = i; } } private void readDeviceInfo() throws KNXException, InterruptedException { // find device descriptor if (dd != null) dd = deviceDescriptor(dd.toByteArray()); else if (mc != null) dd = deviceDescriptor(mc.readDeviceDesc(d, 0)); // check for BCU1/BCU2 first, which don't have interface objects if (dd != null) { if (dd == DD0.TYPE_1013) readPL110Bcu1(); else if (dd == DD0.TYPE_0010 || dd == DD0.TYPE_0011 || dd == DD0.TYPE_0012) readTP1Bcu1(); else if (dd == DD0.TYPE_0020 || dd == DD0.TYPE_0021 || dd == DD0.TYPE_0025) readTP1Bcu2(); else { findInterfaceObjects(); } } else { findInterfaceObjects(); } readDeviceObject(); readActualPeiType(); // Required PEI Type (Application Program Object) if (appProgramObjectIdx != -1) readUnsigned(appProgramObjectIdx, PID.PEI_TYPE, false, CommonParameter.RequiredPeiType); programmingMode(); // System B has mask version 0x07B0 or 0x17B0 and provides error code property final boolean isSystemB = dd == DD0.TYPE_07B0 || dd == DD0.TYPE_17B0; // Application Program (Application Program Object) readProgram(appProgramObjectIdx, isSystemB); // PEI Program (Interface Program Object) readProgram(interfaceProgramObjectIdx, isSystemB); // Group Communication if (addresstableObjectIdx != -1) { // group address table readLoadState(addresstableObjectIdx, isSystemB); } if (assoctableObjectIdx != -1) { // group association table readLoadState(assoctableObjectIdx, isSystemB); } if (mc != null && !result.formatted.containsKey(CommonParameter.GroupAddresses)) readGroupAddresses(); readCemiServerObject(); readRFMediumObject(); readKnxipInfo(); } // is device in programming mode private void programmingMode() throws KNXFormatException, InterruptedException { final DPTXlatorBoolean x = new DPTXlatorBoolean(DPTXlatorBoolean.DPT_SWITCH); try { x.setData(pc.getProperty(deviceObjectIdx, PID.PROGMODE, 1, 1)); } catch (final KNXException e) { // fall back and read memory location (remote device info only) try { if (mc != null) x.setData(mc.readMemory(d, 0x60, 1)); } catch (final KNXException e2) { out.error("reading memory location 0x60", e2); } } putResult(CommonParameter.ProgrammingMode, x.getValue(), x.getData()); } private DD0 deviceDescriptor(final byte[] data) { final DD0 dd = DeviceDescriptor.DD0.from(data); putResult(CommonParameter.DeviceDescriptor, dd.toString(), dd.maskVersion()); putResult(CommonParameter.KnxMedium, toMediumTypeString(dd.mediumType()), dd.mediumType()); putResult(CommonParameter.FirmwareType, toFirmwareTypeString(dd.firmwareType()), dd.firmwareType()); putResult(CommonParameter.FirmwareVersion, "" + dd.firmwareVersion(), dd.firmwareVersion()); return dd; } private void readDeviceObject() throws InterruptedException { if (deviceObjectIdx == -1) return; // Manufacturer ID (Device Object) byte[] data = read(deviceObjectIdx, PID.MANUFACTURER_ID); if (data != null) { final int mfId = (int) toUnsigned(data); putResult(CommonParameter.Manufacturer, manufacturer(mfId), data); } // Order Info readUnsigned(deviceObjectIdx, PID.ORDER_INFO, true, CommonParameter.OrderInfo); // Serial Number data = read(deviceObjectIdx, PropertyAccess.PID.SERIAL_NUMBER); if (data != null) { final String serialNo = DataUnitBuilder.toHex(data, ""); putResult(CommonParameter.SerialNumber, serialNo, data); } // Physical PEI type, i.e., the currently connected PEI type readUnsigned(deviceObjectIdx, PID.PEI_TYPE, false, CommonParameter.ActualPeiType); // Hardware Type, 6 bytes with most significant byte always 0 readUnsigned(deviceObjectIdx, pidHardwareType, true, CommonParameter.HardwareType); // Firmware Revision readUnsigned(deviceObjectIdx, PID.FIRMWARE_REVISION, false, CommonParameter.FirmwareRevision); // Get information about an optional additional KNX profile implemented in the device. // This property is optional, and only required in KNXnet/IP devices or if the device // is implemented in combination with another profile. If a DD is returned we should compare // it to our other DD. If they match, we can assume a stand-alone device (no other profile). // Also, if there is another profile, the KNX individual address has to be different to // the cEMI server one (as provided by the cEMI server object) data = read(deviceObjectIdx, PID.DEVICE_DESCRIPTOR, 1, 1); if (data != null) { final DD0 profile = DeviceDescriptor.DD0.from(data); if (dd == null) dd = deviceDescriptor(data); // device with additional profile? else if (!profile.equals(dd)) putResult(InternalParameter.AdditionalProfile, profile.toString(), data); } // Info about possible additional profile in device try { final byte[] profileSna = read(deviceObjectIdx, PID.SUBNET_ADDRESS, 1, 1); final byte[] profileDev = read(deviceObjectIdx, PID.DEVICE_ADDRESS, 1, 1); final byte[] profileAddr = new byte[] { profileSna[0], profileDev[0] }; final IndividualAddress ia = new IndividualAddress(profileAddr); putResult(CommonParameter.DeviceAddress, "Additional profile address " + ia, ia.toByteArray()); } catch (final Exception e) {} // read device service control try { final byte[] svcCtrl = read(deviceObjectIdx, PID.SERVICE_CONTROL, 1, 1); final boolean indAddrWriteEnabled = (svcCtrl[1] & 0x04) == 0x04; putResult(InternalParameter.IndividualAddressWriteEnabled, indAddrWriteEnabled ? "yes" : "no", svcCtrl[1] & 0x04); final int services = svcCtrl[0] & 0xff; final String formatted = String.format("%8s", Integer.toBinaryString(services)).replace(' ', '0'); putResult(InternalParameter.ServiceControl, "Disabled services on EMI [Mgmt App TL-conn Switch TL-group Network Link User]: " + formatted, services); } catch (final Exception e) {} // RF domain address // Device object RF domain address is mandatory if the cEMI server supports RF. // With mask 0x2311, the RF domain address is mandatory in the RF medium object (PID 56) and // optional in the device object (PID 82). At least the Weinzierl USB stores it only in the device object. try { final byte[] doaAddr = read(deviceObjectIdx, PID.RF_DOMAIN_ADDRESS, 1, 1); if (doaAddr != null) putResult(CommonParameter.DomainAddress, DataUnitBuilder.toHex(doaAddr, ""), toUnsigned(doaAddr)); } catch (final Exception e) {} readUnsigned(deviceObjectIdx, PID.MAX_APDULENGTH, false, CommonParameter.MaxApduLength); final int pidErrorFlags = 53; final byte[] flags = read(deviceObjectIdx, pidErrorFlags); if (flags != null) putResult(InternalParameter.ErrorFlags, errorFlags(flags), toUnsigned(data)); } private static final int legacyPidFilteringModeSelect = 62; private static final int legacyPidFilteringModeSupport = 63; private void readCemiServerObject() throws InterruptedException { if (cemiServerObjectIdx == -1) return; try { final byte[] d = read(cemiServerObjectIdx, PropertyAccess.PID.MEDIUM_TYPE, 1, 1); if (d != null) putResult(CemiParameter.MediumType, mediumTypes(d), toUnsigned(d)); } catch (final Exception e) {} // Get supported cEMI communication modes, DLL is mandatory for any cEMI server // communication mode can then be set using PID_COMM_MODE try { final byte[] commModes = read(cemiServerObjectIdx, PID.COMM_MODES_SUPPORTED, 1, 1); putResult(CemiParameter.SupportedCommModes, supportedCommModes(commModes), toUnsigned(commModes)); } catch (final Exception e) {} try { final byte[] d = read(cemiServerObjectIdx, PID.COMM_MODE, 1, 1); if (d != null) putResult(CemiParameter.SelectedCommMode, commMode(d), toUnsigned(d)); } catch (final Exception e) {} // if we deal with a USB stand-alone device, the Device Object stores the IA of the USB interface // if we deal with a USB device that is embedded with another end device profile, the Device Object stores // the IA of the end device. In that case, the cEMI Server Object holds the IA of the USB interface try { final byte[] dev = read(cemiServerObjectIdx, PID.CLIENT_DEVICE_ADDRESS, 1, 1); final byte[] sna = read(cemiServerObjectIdx, PID.CLIENT_SNA, 1, 1); final byte[] addr = new byte[] { sna[0], dev[0] }; final IndividualAddress ia = new IndividualAddress(addr); putResult(CemiParameter.ClientAddress, "USB cEMI client address " + ia, ia.toByteArray()); } catch (final Exception e) {} // filtering modes readSupportedFilteringModes(PID.FILTERING_MODE_SUPPORT); readSelectedFilteringMode(PID.FILTERING_MODE_SELECT); // do the same stuff again using the legacy PIDs for filtering mode readSupportedFilteringModes(legacyPidFilteringModeSupport); readSelectedFilteringMode(legacyPidFilteringModeSelect); // read supported and selected RF communication mode try { cEmiExtensionRfBiBat(); } catch (final Exception e) {} try { final byte[] data = read(cemiServerObjectIdx, PID.RF_MODE_SELECT, 1, 1); final int selected = data[0] & 0xff; final boolean slave = (data[0] & 0x04) == 0x04; final boolean master = (data[0] & 0x02) == 0x02; final boolean async = (data[0] & 0x01) == 0x01; final String formatted = "BiBat slave " + slave + ", BiBat master " + master + ", async " + async; putResult(CemiParameter.SelectedRfMode, formatted, selected); } catch (final Exception e) {} } // Supports/Disable filtering on: // ------------------------------------------------------ // Bit | 15 ... 4 | 3 | 2 | 1 | 0 | // Name | Reserved | Ext. Grp | DoA | Repeated | Own Ind. | // | | Addresses | | Frames | Address | // ------------------------------------------------------ private void readSupportedFilteringModes(final int pid) { try { final byte[] filters = read(cemiServerObjectIdx, pid, 1, 1); final int filter = filters[1] & 0xff; final boolean grp = (filter & 0x08) == 0x08; final boolean doa = (filter & 0x04) == 0x04; final boolean rep = (filter & 0x02) == 0x02; final boolean ownIa = (filter & 0x01) == 0x01; putResult(CemiParameter.SupportedFilteringModes, "ext. group addresses " + grp + ", domain address " + doa + ", repeated frames " + rep + ", own individual address " + ownIa, filters); } catch (final Exception e) {} } // Check disabled frame filters in the device // A set bit (1) indicates a disabled filter, by default all filters are active private void readSelectedFilteringMode(final int pid) { try { final byte[] filters = read(cemiServerObjectIdx, pid, 1, 1); final int selected = filters[1] & 0xff; final boolean grp = (selected & 0x08) == 0x08; final boolean doa = (selected & 0x04) == 0x04; final boolean rep = (selected & 0x02) == 0x02; final boolean ownIa = (selected & 0x01) == 0x01; if (selected == 0) putResult(CemiParameter.SelectedFilteringModes, "all supported filters active", selected); else putResult(CemiParameter.SelectedFilteringModes, "disabled frame filters: ext. group addresses " + grp + ", domain address " + doa + ", repeated frames " + rep + ", own individual address " + ownIa, selected); } catch (final Exception e) {} } private void cEmiExtensionRfBiBat() throws KNXException, InterruptedException { final byte[] support = read(cemiServerObjectIdx, PID.RF_MODE_SUPPORT, 1, 1); final boolean slave = (support[0] & 0x04) == 0x04; final boolean master = (support[0] & 0x02) == 0x02; final boolean async = (support[0] & 0x01) == 0x01; final String formatted = "BiBat slave " + slave + ", BiBat master " + master + ", Async " + async; putResult(CemiParameter.SupportedRfModes, formatted, support); } // -------------------------------------- // Bit | 15 ... 4 | 3 | 2 | 1 | 0 | // Name | Reserved | TLL | RAW | BM | DLL | // -------------------------------------- // // TLL: Transport layer local // RAW: Data link layer, RAW mode (receive L-Raw.req, L-Raw.ind from the bus) // BM: Data link layer, busmonitor mode // DLL: Data link layer, normal mode private String supportedCommModes(final byte[] commModes) { final int modes = commModes[1] & 0xff; final boolean tll = (modes & 0x08) == 0x08; final boolean raw = (modes & 0x04) == 0x04; final boolean bm = (modes & 0x02) == 0x02; final boolean dll = (modes & 0x01) == 0x01; final String s = "Transport layer local " + tll + ", Data link layer modes: normal " + dll + ", busmonitor " + bm + ", raw mode " + raw; return s; } private String commMode(final byte[] data) { final int commMode = data[0] & 0xff; switch (commMode) { case 0: return "Data link layer"; case 1: return "Data link layer busmonitor"; case 2: return "Data link layer raw frames"; case 6: return "cEMI transport layer"; case 0xff: return "no layer"; } return "unknown/unspecified (" + commMode + ")"; } private void readRFMediumObject() { if (rfMediumObjectIdx != -1) { try { // different PID as in Device Object !!! final int pidRfDomainAddress = 56; final byte[] doaAddr = read(rfMediumObjectIdx, pidRfDomainAddress, 1, 1); if (doaAddr != null) putResult(RfParameter.DomainAddress, "0x" + DataUnitBuilder.toHex(doaAddr, ""), doaAddr); } catch (final Exception e) {} } } // verbose info what the BCU is currently doing private void readSystemState() throws InterruptedException { int state = readMem(0x60, 1); // Bit 7 is parity (even parity) state &= 0x7f; final String[] mode = new String[] { "Programming mode", "Normal operation", // else busmonitor mode "Transport layer", "Application layer", "Serial PEI interface (msg protocol)", "User program", "Programming mode (ind. address)" // else normal operation }; final StringBuilder sb = new StringBuilder(); for (int bit = 0; bit < 7; bit++) if ((state & (1 << bit)) != 0) sb.append(mode[bit]).append(", "); putResult(CommonParameter.SystemState, sb.toString(), state); // reading back legal values, although there is no 1:1 mapping // String layer = null; // if (state == 0x90) // layer = "Busmonitor"; // else if (state == 0x12) // layer = "Link layer"; // else if (state == 0x96) // layer = "Transport layer"; // else if (state == 0x1E) // layer = "Application layer"; // else if (state == 0xC0) // layer = "Reset"; // if (layer != null) // putResult(Parameter.SystemState, layer, state); } private void readActualPeiType() throws InterruptedException { if (mc == null) return; final int channel = 4; final int repeat = 1; try { final int v = mc.readADC(d, channel, repeat); final int peitype = (10 * v + 60) / 128; putResult(CommonParameter.ActualPeiType, toPeiTypeString(peitype), peitype); } catch (final KNXException e) { out.error("reading actual PEI type (A/D converter channel {}, repeat {})", channel, repeat, e); } } // same as BCU error flags located at 0x10d private String errorFlags(final byte[] data) { if ((data[0] & 0xff) == 0xff) return "everything OK"; final String[] description = { "System 1 internal system error", "Illegal system state", "Checksum / CRC error in internal non-volatile memory", "Stack overflow error", "Inconsistent system tables", "Physical transceiver error", "System 2 internal system error", "System 3 internal system error" }; final List errors = new ArrayList<>(); for (int i = 0; i < 8; i++) if ((data[0] & (1 << i)) == 0) errors.add(description[i]); return errors.stream().collect(Collectors.joining(", ")); } private void putResult(final Parameter p, final long raw) { putResult(p, "" + raw, raw); } private void putResult(final Parameter p, final String formatted, final long raw) { putResult(p, formatted, ByteBuffer.allocate(Long.BYTES).putLong(raw).array()); } private void putResult(final Parameter p, final String formatted, final int raw) { putResult(p, formatted, ByteBuffer.allocate(Integer.BYTES).putInt(raw).array()); } private void putResult(final Parameter p, final String formatted, final byte[] raw) { result.formatted.put(p, formatted); result.raw.put(p, raw); onDeviceInformation(p, formatted, raw); } private static final int addrManufact = 0x0104; private static final int addrDevType = 0x0105; // length 2 private static final int addrVersion = 0x0107; private static final int addrPeiType = 0x0109; // _required_ PEI type private static final int addrRunError = 0x010d; private static final int addrRoutingCnt = 0x010e; private static final int addrGroupObjTablePtr = 0x0112; // private static final int addrProgramPtr = 0x0114; private static final int addrGroupAddrTable = 0x0116; // max. length 233 private void readPL110Bcu1() throws InterruptedException { final int addrDoA = 0x0102; // length 2 readMem(addrDoA, 2, "DoA ", true, CommonParameter.DomainAddress); readBcuInfo(); } private void readTP1Bcu1() throws InterruptedException { // Option Reg: bit 1: mem 0x120-0x1ff protected/writable // final int addrOptionReg = 0x100; final int addrManufactData = 0x0101; // length 3 // final int addrMxRstCnt = 0x010f; // bits (msb): 3 INAK / 2 (unused) / 3 BUSY (lsb) // final int addrConfigDesc = 0x0110; // final int addrAssocTablePtr = 0x0111; readMem(addrManufactData, 3, "KNX manufacturer data ", true, CommonParameter.ManufacturerData); readBcuInfo(); } private void readTP1Bcu2() throws InterruptedException, KNXException { // Option Reg: bit 0: watchdog disabled/enabled, bit 1: mem 0x300-0x4df protected/writable // final int addrOptionReg = 0x100; final int addrManufactData = 0x0101; // length 2 // App Id, length 5: App manufacturer (2), SW dev type (2), SW version (1) // the App manufacturer can differ from product manufacturer (0x101), if a compatible program was downloaded final int addrAppId = 0x0103; // address table realization type 2 // final int addrUsrEeprom = 0x0116; // max. length 858: Addr Table, Assoc Table, EEData, Code // Page 0 RAM // final int addrPeiInterface = 0x00c4; // if used // final int addrPeiInfo = 0x00c5; // if used readMem(addrManufactData, 3, "KNX manufacturer data ", true, CommonParameter.ManufacturerData); final long appId = readMemLong(addrAppId, 5); final String appMf = manufacturer.get((int) appId >> (3 * 8)); final long swDev = (appId >> 8) & 0xff; final long swVersion = appId & 0xff; out.info("appId 0x{} - app manufacturer: {}, SW dev type {}, SW version {}", Long.toHexString(appId), appMf, swDev, swVersion); readBcuInfo(); // interface objects: Device Object, Address table object, Assoc table object, App program object findInterfaceObjects(); } private void readBcuInfo() throws InterruptedException { readMem(addrManufact, 1, "KNX manufacturer ID ", DeviceInfo::manufacturer, CommonParameter.Manufacturer); readMem(addrDevType, 2, "Device type number ", true, CommonParameter.DeviceTypeNumber); readMem(addrVersion, 1, "SW version ", v -> (v >> 4) + "." + (v & 0xf), CommonParameter.SoftwareVersion); // mechanical PEI type required by the application SW readMem(addrPeiType, 1, "Hardware PEI type ", DeviceInfo::toPeiTypeString, CommonParameter.RequiredPeiType); readMem(addrRunError, 1, "Run error 0x", DeviceInfo::decodeRunError, CommonParameter.RunError); readSystemState(); readMem(addrRoutingCnt, 1, "Routing count ", v -> Integer.toString((v >> 4) & 0x7), CommonParameter.RoutingCount); // realization type 1 // Location of group object table readMem(addrGroupObjTablePtr, 1, "Group object table location ", true, CommonParameter.GroupObjTableLocation); readGroupAddresses(); // Location of user program 0x100 + progptr // final int progptr = readMem(addrProgramPtr, 1); // final int userprog = 0x100 + progptr; } private void readGroupAddresses() throws InterruptedException { // realization type 1 final int entries = readMem(addrGroupAddrTable, 1, "Group address table entries ", false, CommonParameter.GroupAddrTableEntries); // address of device address int startAddr = addrGroupAddrTable + 1; readMem(startAddr, 2, "", v -> new IndividualAddress(v & 0x7fff).toString(), CommonParameter.DeviceAddress); final StringBuilder sb = new StringBuilder(); for (int i = 0; i < entries; i++) { startAddr += 2; final int raw = readMem(startAddr, 2); final KNXAddress group = new GroupAddress(raw & 0x7fff); if (sb.length() > 0) sb.append(" "); sb.append(group); // are we the group responder if ((raw & 0x8000) == 0x8000) sb.append("(R)"); } putResult(CommonParameter.GroupAddresses, sb.toString(), new byte[0]); } private void readKnxipInfo() throws KNXException, InterruptedException { if (knxnetipObjectIdx == -1 || dd != DD0.TYPE_5705) return; // Device Name (friendly) read(KnxipParameter.DeviceName, this::readFriendlyName); // Device Capabilities Device State byte[] data = read(knxnetipObjectIdx, PropertyAccess.PID.KNXNETIP_DEVICE_CAPABILITIES); if (data == null) return; putResult(KnxipParameter.Capabilities, toCapabilitiesString(data), data); final boolean supportsTunneling = (data[1] & 0x01) == 0x01; // MAC Address data = read(knxnetipObjectIdx, PropertyAccess.PID.MAC_ADDRESS); putResult(KnxipParameter.MacAddress, DataUnitBuilder.toHex(data, " "), toUnsigned(data)); // Current IP Assignment data = read(knxnetipObjectIdx, PropertyAccess.PID.CURRENT_IP_ASSIGNMENT_METHOD); putResult(KnxipParameter.CurrentIPAssignment, toIPAssignmentString(data), toUnsigned(data)); final int currentIPAssignment = data[0] & 0x0f; // Bits (from LSB): Manual=0, BootP=1, DHCP=2, AutoIP=3 final boolean dhcpOrBoot = (data[0] & 0x06) != 0; // Read currently set IP parameters // IP Address final byte[] currentIP = read(knxnetipObjectIdx, PropertyAccess.PID.CURRENT_IP_ADDRESS); putResult(KnxipParameter.CurrentIPAddress, toIP(currentIP), toUnsigned(currentIP)); // Subnet Mask final byte[] currentMask = read(knxnetipObjectIdx, PropertyAccess.PID.CURRENT_SUBNET_MASK); putResult(KnxipParameter.CurrentSubnetMask, toIP(currentMask), toUnsigned(currentMask)); // Default Gateway final byte[] currentGw = read(knxnetipObjectIdx, PropertyAccess.PID.CURRENT_DEFAULT_GATEWAY); putResult(KnxipParameter.CurrentDefaultGateway, toIP(currentGw), toUnsigned(currentGw)); // DHCP Server (show only if current assignment method is DHCP or BootP) if (dhcpOrBoot) { data = read(knxnetipObjectIdx, PropertyAccess.PID.DHCP_BOOTP_SERVER); putResult(KnxipParameter.DhcpServer, toIP(data), toUnsigned(data)); } // IP Assignment Method (shown only if different from current IP assign. method) data = read(knxnetipObjectIdx, PropertyAccess.PID.IP_ASSIGNMENT_METHOD); final int ipAssignment = data[0] & 0x0f; if (ipAssignment != currentIPAssignment) { putResult(KnxipParameter.ConfiguredIPAssignment, ipAssignment); } // Read IP parameters for manual assignment // the following info is only shown if manual assignment method is enabled, and parameter // is different from current one final boolean manual = (ipAssignment & 0x01) == 0x01; if (manual) { // info.append("Differing manual configuration:\n"); // Manual IP Address final byte[] ip = read(knxnetipObjectIdx, PropertyAccess.PID.IP_ADDRESS); if (!Arrays.equals(currentIP, ip)) putResult(KnxipParameter.IPAddress, toIP(ip), toUnsigned(ip)); // Manual Subnet Mask final byte[] mask = read(knxnetipObjectIdx, PropertyAccess.PID.SUBNET_MASK); if (!Arrays.equals(currentMask, mask)) putResult(KnxipParameter.SubnetMask, toIP(mask), toUnsigned(mask)); // Manual Default Gateway final byte[] gw = read(knxnetipObjectIdx, PropertyAccess.PID.DEFAULT_GATEWAY); if (!Arrays.equals(currentGw, gw)) putResult(KnxipParameter.DefaultGateway, toIP(gw), toUnsigned(gw)); } // Routing Multicast Address data = read(knxnetipObjectIdx, PropertyAccess.PID.ROUTING_MULTICAST_ADDRESS); putResult(KnxipParameter.RoutingMulticast, toIP(data), toUnsigned(data)); // Multicast TTL readUnsigned(knxnetipObjectIdx, PropertyAccess.PID.TTL, false, KnxipParameter.TimeToLive); // Messages to Multicast Address readUnsigned(knxnetipObjectIdx, PID.MSG_TRANSMIT_TO_IP, false, KnxipParameter.TransmitToIP); // Additional Ind. Addresses (shown only if tunneling is implemented) if (supportsTunneling) { final int pid = PID.ADDITIONAL_INDIVIDUAL_ADDRESSES; final int elements = readElements(knxnetipObjectIdx, pid); final StringBuilder sb = new StringBuilder(); for (int i = 0; i < elements; i++) { data = read(knxnetipObjectIdx, pid); sb.append(new IndividualAddress(data)).append(" "); } putResult(KnxipParameter.AdditionalIndividualAddresses, sb.toString(), new byte[0]); } } private void readProgram(final int objectIdx, final boolean hasErrorCode) throws InterruptedException { if (objectIdx == -1) return; byte[] data = read(objectIdx, PID.PROGRAM_VERSION); if (data != null) putResult(CommonParameter.ProgramVersion, programVersion(data), data); readLoadState(objectIdx, hasErrorCode); data = read(objectIdx, PropertyAccess.PID.RUN_STATE_CONTROL); if (data != null) putResult(CommonParameter.RunStateControl, getRunState(data), data); } private static String programVersion(final byte[] data) { if (data.length != 5) return DataUnitBuilder.toHex(data, ""); final int mfr = (data[0] & 0xff) << 8 | (data[1] & 0xff); return String.format("%s %02x%02x v%d.%d", manufacturer(mfr), data[2], data[3], (data[4] & 0xff) >> 4, data[4] & 0xf); } // XXX this method is called for address table and program, we can't store into same parameter private void readLoadState(final int objectIdx, final boolean hasErrorCode) throws InterruptedException { if (objectIdx == -1) return; byte[] data = read(objectIdx, PropertyAccess.PID.LOAD_STATE_CONTROL); final String ls = getLoadState(data); putResult(CommonParameter.LoadStateControl, ls, data == null ? new byte[0] : data); // System B contains error code for load state "Error" (optional, but usually yes) if (data != null && data[0] == 3 && hasErrorCode) { data = read(objectIdx, PropertyAccess.PID.ERROR_CODE); if (data != null) { try { // enum ErrorClassSystem final DPTXlator t = TranslatorTypes.createTranslator(0, "20.011"); t.setData(data); putResult(CommonParameter.LoadStateError, t.getValue(), data); } catch (final KNXException e) { // no translator } } } } private void read(final Parameter p, final Callable c) throws KNXLinkClosedException, InterruptedException { try { out.debug("read {} ...", p); final String s = c.call(); putResult(p, s, s.getBytes(Charset.forName("ISO-8859-1"))); } catch (InterruptedException | KNXLinkClosedException e) { throw e; } catch (final KNXRemoteException e) { out.warn("reading {}: {}", p, e.getMessage()); } catch (final Exception e) { out.error("error reading {}", p, e); } } private String readFriendlyName() throws KNXException, InterruptedException { final char[] name = new char[30]; int start = 0; while (true) { final byte[] data = pc.getProperty(knxnetipObjectIdx, PID.FRIENDLY_NAME, start + 1, 10); for (int i = 0; i < 10 && data[i] != 0; ++i, ++start) name[start] = (char) (data[i] & 0xff); if (start >= 30 || data[9] == 0) return new String(name, 0, start); } } private int readElements(final int objectIndex, final int pid) throws InterruptedException { final byte[] elems = read(objectIndex, pid, 0, 1); return elems == null ? 0 : (int) toUnsigned(elems); } private byte[] read(final int objectIndex, final int pid) throws InterruptedException { return read(objectIndex, pid, 1, 1); } private byte[] read(final int objectIndex, final int pid, final int start, final int elements) throws InterruptedException { try { // since we don't know the max. allowed APDU length, play it safe final ByteArrayOutputStream res = new ByteArrayOutputStream(); for (int i = start; i < start + elements; i++) { final byte[] data = pc.getProperty(objectIndex, pid, i, 1); res.write(data, 0, data.length); } return res.toByteArray(); } catch (final KNXException e) { out.info("error reading KNX property " + objectIndex + "|" + pid + ", " + e.getMessage()); } return null; } private void readUnsigned(final int objectIndex, final int pid, final boolean hex, final Parameter p) throws InterruptedException { final byte[] data = read(objectIndex, pid); if (data == null) { result.formatted.put(p, "-"); result.raw.put(p, new byte[0]); } else { final String formatted = hex ? DataUnitBuilder.toHex(data, "") : Long.toString(toUnsigned(data)); putResult(p, formatted, data); } } private int readMem(final int startAddr, final int bytes, final String prefix, final boolean hex, final Parameter p) throws InterruptedException { final long v = readMemLong(startAddr, bytes); putResult(p, hex ? Long.toHexString(v) : Long.toString(v), v); return (int) v; } private void readMem(final int startAddr, final int bytes, final String prefix, final Function representation, final Parameter p) throws InterruptedException { final int v = readMem(startAddr, bytes); putResult(p, representation.apply(v), v); } // pre: 3 bytes max private int readMem(final int startAddr, final int bytes) throws InterruptedException { return (int) readMemLong(startAddr, bytes); } // pre: 7 bytes max private long readMemLong(final int startAddr, final int bytes) throws InterruptedException { try { return toUnsigned(mc.readMemory(d, startAddr, bytes)); } catch (final KNXException e) { return -1; } } /** * Creates the KNX network link to access the network specified in options. * * @return the KNX network link * @throws KNXException on problems on link creation * @throws InterruptedException on interrupted thread */ private KNXNetworkLink createLink() throws KNXException, InterruptedException { final String host = (String) options.get("host"); final KNXMediumSettings medium = (KNXMediumSettings) options.get("medium"); if (options.containsKey("ft12")) { // create FT1.2 network link try { return new KNXNetworkLinkFT12(Integer.parseInt(host), medium); } catch (final NumberFormatException e) { return new KNXNetworkLinkFT12(host, medium); } } if (options.containsKey("usb")) { // create USB network link return new KNXNetworkLinkUsb(host, medium); } if (options.containsKey("tpuart")) { // create TP-UART link final IndividualAddress device = (IndividualAddress) options.get("knx-address"); medium.setDeviceAddress(device); return new KNXNetworkLinkTpuart(host, medium, Collections.emptyList()); } // create local and remote socket address for network link final InetSocketAddress local = Main.createLocalSocket((InetAddress) options.get("localhost"), (Integer) options.get("localport")); final InetAddress addr = Main.parseHost(host); if (addr.isMulticastAddress()) return KNXNetworkLinkIP.newRoutingLink(local.getAddress(), addr, medium); final InetSocketAddress remote = new InetSocketAddress(addr, ((Integer) options.get("port")).intValue()); return KNXNetworkLinkIP.newTunnelingLink(local, remote, options.containsKey("nat"), medium); } /** * Reads all command line options, and puts those options into the options map. *

* Default option values are added; on unknown options, a KNXIllegalArgumentException is thrown. * * @param args array with command line options */ private void parseOptions(final String[] args) { if (args.length == 0) return; // add defaults options.put("port", KNXnetIPConnection.DEFAULT_PORT); // default subnetwork address for TP1 and unregistered device options.put("knx-address", new IndividualAddress(0, 0x02, 0xff)); int i = 0; for (; i < args.length; i++) { final String arg = args[i]; if (Main.isOption(arg, "help", "h")) { options.put("help", null); return; } if (Main.isOption(arg, "version", null)) { options.put("version", null); return; } if (Main.isOption(arg, "verbose", "v")) options.put("verbose", null); else if (Main.isOption(arg, "localhost", null)) options.put("localhost", Main.parseHost(args[++i])); else if (Main.isOption(arg, "localport", null)) options.put("localport", Integer.decode(args[++i])); else if (Main.isOption(arg, "port", "p")) options.put("port", Integer.decode(args[++i])); else if (Main.isOption(arg, "nat", "n")) options.put("nat", null); else if (Main.isOption(arg, "ft12", "f")) options.put("ft12", null); else if (Main.isOption(arg, "usb", "u")) options.put("usb", null); else if (Main.isOption(arg, "tpuart", null)) options.put("tpuart", null); else if (Main.isOption(arg, "medium", "m")) options.put("medium", Main.getMedium(args[++i])); else if (Main.isOption(arg, "domain", null)) options.put("domain", Long.decode(args[++i])); else if (Main.isOption(arg, "knx-address", "k")) options.put("knx-address", Main.getAddress(args[++i])); else if (!options.containsKey("host")) // otherwise add a host key with argument as host options.put("host", arg); else if (!options.containsKey("device")) // otherwise create the KNX device address from the argument try { options.put("device", new IndividualAddress(arg)); } catch (final KNXFormatException e) { throw new KNXIllegalArgumentException("KNX device " + e.toString(), e); } else throw new KNXIllegalArgumentException("unknown option " + arg); } if (!options.containsKey("host") || (options.containsKey("ft12") && options.containsKey("usb"))) throw new KNXIllegalArgumentException("specify either IP host, serial port, or device"); if (!options.containsKey("device")) { // we will read device info using cEMI local device management, check some invalid options // supported is knxnet/ip and usb final String adapter = options.containsKey("ft12") ? "FT1.2" : options.containsKey("tpuart") ? "TP-UART" : ""; if (!adapter.isEmpty()) throw new KNXIllegalArgumentException("reading device info of local " + adapter + " interface is not supported, specify remote KNX device address"); if (options.containsKey("medium") || options.containsKey("domain")) throw new KNXIllegalArgumentException("missing remote KNX device address"); } if (!options.containsKey("medium")) options.put("medium", TPSettings.TP1); Main.setDomainAddress(options); } private static void showUsage() { final StringBuilder sb = new StringBuilder(); sb.append("Usage: ").append(tool).append(" [options] ") .append(sep); sb.append("Options:").append(sep); sb.append(" --help -h show this help message").append(sep); sb.append(" --version show tool/library version and exit").append(sep); sb.append(" --verbose -v enable verbose status output").append(sep); sb.append(" --localhost local IP/host name").append(sep); sb.append(" --localport local UDP port (default system assigned)").append(sep); sb.append(" --port -p UDP port on (default ") .append(KNXnetIPConnection.DEFAULT_PORT).append(")").append(sep); sb.append(" --nat -n enable Network Address Translation").append(sep); sb.append(" --ft12 -f use FT1.2 serial communication").append(sep); sb.append(" --usb -u use KNX USB communication").append(sep); sb.append(" --tpuart use TP-UART communication").append(sep); sb.append(" --medium -m KNX medium [tp1|p110|knxip|rf] (default tp1)").append(sep); sb.append(" --domain

domain address on KNX PL/RF medium (defaults to broadcast domain)") .append(sep); out(sb.toString()); } private static void showVersion() { out(Settings.getLibraryHeader(false)); } private static void out(final String s) { System.out.println(s); } private static long toUnsigned(final byte[] data) { // XXX remove again if (data == null || data.length > 8) return -1; long value = 0; for (final byte b : data) { value = value << 8 | (b & 0xff); } return value; } private static String toIP(final byte[] data) { try { if (data != null) return InetAddress.getByAddress(data).getHostAddress(); } catch (final UnknownHostException e) {} return "n/a"; } private static String toMediumTypeString(final int type) { switch (type) { case 0: return "Twisted Pair 1"; case 1: return "Power-line 110"; case 2: return "Radio Frequency"; case 5: return "KNX IP"; default: return "Type " + type; } } // DPT Media private static String mediumTypes(final byte[] data) { final long types = toUnsigned(data); final List supported = new ArrayList<>(); if ((types & 0x02) > 0) supported.add("TP1"); if ((types & 0x04) > 0) supported.add("PL110"); if ((types & 0x10) > 0) supported.add("RF"); if ((types & 0x20) > 0) supported.add("KNX IP"); return supported.stream().collect(Collectors.joining(", ")); } private static String toFirmwareTypeString(final int type) { switch (type) { case 0: return "BCU 1, BCU 2, BIM M113"; case 1: return "Unidirectional devices"; case 3: return "Property based device management"; case 7: return "BIM M112"; case 8: return "IR Decoder, TP1 legacy"; case 9: return "Repeater, Coupler"; default: return "Type " + type; } } private static String toPeiTypeString(final int peitype) { if (peitype == -1) return "n/a"; final String[] desc = new String[] { "No adapter", // 0 "Illegal adapter", "4 inputs, 1 output (LED)", "Reserved", "2 inputs / 2 outputs, 1 output (LED)", "Reserved", // 5 "3 inputs / 1 output, 1 output (LED)", "Reserved", "5 inputs", "Reserved", "FT1.2 protocol", // (default) type 10 is defined twice // "Loadable serial protocol", // 10 (alternative) "Reserved", "Serial sync message protocol", "Reserved", "Serial sync data block protocol", "Reserved", // 15 "Serial async message protocol", "Programmable I/O", "Reserved", "4 outputs, 1 output (LED)", "Download", // 20 }; return desc[peitype]; } private static String decodeRunError(final int runError) { final String[] flags = new String[] {"SYS0_ERR: buffer error", "SYS1_ERR: system state parity error", "EEPROM corrupted", "Stack overflow", "OBJ_ERR: group object/assoc. table error", "SYS2_ERR: transceiver error", "SYS3_ERR: confirm error"}; final int bits = ~runError & 0xff; if (bits == 0) return "OK"; final StringBuilder sb = new StringBuilder(); for (int i = 0; i < flags.length; i++) { if ((bits & (1 << i)) != 0) sb.append(flags[i]); } return sb.toString(); } private static String getLoadState(final byte[] data) { if (data == null || data.length < 1) return "n/a"; final int state = data[0] & 0xff; switch (state) { case 0: return "Unloaded"; case 1: return "Loaded"; case 2: return "Loading"; case 3: return "Error (during load process)"; case 4: return "Unloading"; case 5: return "Load Completing (Intermediate)"; default: return "Invalid load status " + state; } } private static String getRunState(final byte[] data) { if (data == null || data.length < 1) return "n/a"; final int state = data[0] & 0xff; switch (state) { case 0: return "Halted"; case 1: return "Running"; case 2: return "Ready"; case 3: return "Terminated"; case 4: return "Starting"; case 5: return "Shutting down"; default: return "Invalid run state " + state; } } private static String toIPAssignmentString(final byte[] data) { final int bitset = data[0] & 0xff; String s = ""; final String div = ", "; if ((bitset & 0x01) != 0) s = "manual"; if ((bitset & 0x02) != 0) s += (s.length() == 0 ? "" : div) + "Bootstrap Protocol"; if ((bitset & 0x04) != 0) s += (s.length() == 0 ? "" : div) + "DHCP"; if ((bitset & 0x08) != 0) s += (s.length() == 0 ? "" : div) + "Auto IP"; return s; } private static String toCapabilitiesString(final byte[] data) { final StringBuilder sb = new StringBuilder(); if ((data[1] & 0x01) == 0x01) sb.append(" Device Management,"); if ((data[1] & 0x02) == 0x02) sb.append(" Tunneling,"); if ((data[1] & 0x04) == 0x04) sb.append(" Routing,"); if ((data[1] & 0x08) == 0x08) sb.append(" Remote Logging,"); if ((data[1] & 0x10) == 0x10) sb.append(" Remote Configuration and Diagnosis,"); if ((data[1] & 0x20) == 0x20) sb.append(" Object Server,"); return sb.length() == 0 ? "" : sb.substring(0, sb.length() - 1); } private static String manufacturer(int mf) { return manufacturer.getOrDefault(mf, "Unknown"); } // KNX manufacturer IDs as of 2015 private static final Map manufacturer = new HashMap<>(); static { manufacturer.put(1, "Siemens"); manufacturer.put(2, "ABB"); manufacturer.put(4, "Albrecht Jung"); manufacturer.put(5, "Bticino"); manufacturer.put(6, "Berker"); manufacturer.put(7, "Busch-Jaeger Elektro"); manufacturer.put(8, "GIRA Giersiepen"); manufacturer.put(9, "Hager Electro"); manufacturer.put(10, "INSTA ELEKTRO"); manufacturer.put(11, "LEGRAND Appareillage électrique"); manufacturer.put(12, "Merten"); manufacturer.put(14, "ABB SpA – SACE Division"); manufacturer.put(22, "Siedle & Söhne"); manufacturer.put(24, "Eberle"); manufacturer.put(25, "GEWISS"); manufacturer.put(27, "Albert Ackermann"); manufacturer.put(28, "Schupa GmbH"); manufacturer.put(29, "ABB SCHWEIZ"); manufacturer.put(30, "Feller"); manufacturer.put(31, "Glamox AS"); manufacturer.put(32, "DEHN & SÖHNE"); manufacturer.put(33, "CRABTREE"); manufacturer.put(36, "Paul Hochköpper"); manufacturer.put(37, "Altenburger Electronic"); manufacturer.put(41, "Grässlin"); manufacturer.put(42, "Simon"); manufacturer.put(44, "VIMAR"); manufacturer.put(45, "Moeller Gebäudeautomation KG"); manufacturer.put(46, "Eltako"); manufacturer.put(49, "Bosch-Siemens Haushaltsgeräte"); manufacturer.put(52, "RITTO GmbH&Co.KG"); manufacturer.put(53, "Power Controls"); manufacturer.put(55, "ZUMTOBEL"); manufacturer.put(57, "Phoenix Contact"); manufacturer.put(61, "WAGO Kontakttechnik"); manufacturer.put(66, "Wieland Electric"); manufacturer.put(67, "Hermann Kleinhuis"); manufacturer.put(69, "Stiebel Eltron"); manufacturer.put(71, "Tehalit"); manufacturer.put(72, "Theben AG"); manufacturer.put(73, "Wilhelm Rutenbeck"); manufacturer.put(75, "Winkhaus"); manufacturer.put(76, "Robert Bosch"); manufacturer.put(78, "Somfy"); manufacturer.put(80, "Woertz"); manufacturer.put(81, "Viessmann Werke"); manufacturer.put(82, "Theodor HEIMEIER Metallwerk"); manufacturer.put(83, "Joh. Vaillant"); manufacturer.put(85, "AMP Deutschland"); manufacturer.put(89, "Bosch Thermotechnik GmbH"); manufacturer.put(90, "SEF - ECOTEC"); manufacturer.put(92, "DORMA GmbH + Co. KG"); manufacturer.put(93, "WindowMaster A/S"); manufacturer.put(94, "Walther Werke"); manufacturer.put(95, "ORAS"); manufacturer.put(97, "Dätwyler"); manufacturer.put(98, "Electrak"); manufacturer.put(99, "Techem"); manufacturer.put(100, "Schneider Electric Industries SAS"); manufacturer.put(101, "WHD Wilhelm Huber + Söhne"); manufacturer.put(102, "Bischoff Elektronik"); manufacturer.put(104, "JEPAZ"); manufacturer.put(105, "RTS Automation"); manufacturer.put(106, "EIBMARKT GmbH"); manufacturer.put(107, "WAREMA electronic GmbH"); manufacturer.put(108, "Eelectron"); manufacturer.put(109, "Belden Wire & Cable B.V."); manufacturer.put(110, "Becker-Antriebe GmbH"); manufacturer.put(111, "J.Stehle+Söhne GmbH"); manufacturer.put(112, "AGFEO"); manufacturer.put(113, "Zennio"); manufacturer.put(114, "TAPKO Technologies"); manufacturer.put(115, "HDL"); manufacturer.put(116, "Uponor"); manufacturer.put(117, "se Lightmanagement AG"); manufacturer.put(118, "Arcus-eds"); manufacturer.put(119, "Intesis"); manufacturer.put(120, "Herholdt Controls srl"); manufacturer.put(121, "Zublin AG"); manufacturer.put(122, "Durable Technologies"); manufacturer.put(123, "Innoteam"); manufacturer.put(124, "ise GmbH"); manufacturer.put(125, "TEAM FOR TRONICS"); manufacturer.put(126, "CIAT"); manufacturer.put(127, "Remeha BV"); manufacturer.put(128, "ESYLUX"); manufacturer.put(129, "BASALTE"); manufacturer.put(130, "Vestamatic"); manufacturer.put(131, "MDT technologies"); manufacturer.put(132, "Warendorfer Küchen GmbH"); manufacturer.put(133, "Video-Star"); manufacturer.put(134, "Sitek"); manufacturer.put(135, "CONTROLtronic"); manufacturer.put(136, "function Technology"); manufacturer.put(137, "AMX"); manufacturer.put(138, "ELDAT"); manufacturer.put(139, "Panasonic"); manufacturer.put(140, "Pulse Technologies"); manufacturer.put(141, "Crestron"); manufacturer.put(142, "STEINEL professional"); manufacturer.put(143, "BILTON LED Lighting"); manufacturer.put(144, "denro AG"); manufacturer.put(145, "GePro"); manufacturer.put(146, "preussen automation"); manufacturer.put(147, "Zoppas Industries"); manufacturer.put(148, "MACTECH"); manufacturer.put(149, "TECHNO-TREND"); manufacturer.put(150, "FS Cables"); manufacturer.put(151, "Delta Dore"); manufacturer.put(152, "Eissound"); manufacturer.put(153, "Cisco"); manufacturer.put(154, "Dinuy"); manufacturer.put(155, "iKNiX"); manufacturer.put(156, "Rademacher Geräte-Elektronik GmbH & Co. KG"); manufacturer.put(157, "EGi Electroacustica General Iberica"); manufacturer.put(158, "Ingenium"); manufacturer.put(159, "ElabNET"); manufacturer.put(160, "Blumotix"); manufacturer.put(161, "Hunter Douglas"); manufacturer.put(162, "APRICUM"); manufacturer.put(163, "TIANSU Automation"); manufacturer.put(164, "Bubendorff"); manufacturer.put(165, "MBS GmbH"); manufacturer.put(166, "Enertex Bayern GmbH"); manufacturer.put(167, "BMS"); manufacturer.put(168, "Sinapsi"); manufacturer.put(169, "Embedded Systems SIA"); manufacturer.put(170, "KNX1"); manufacturer.put(171, "Tokka"); manufacturer.put(172, "NanoSense"); manufacturer.put(173, "PEAR Automation GmbH"); manufacturer.put(174, "DGA"); manufacturer.put(175, "Lutron"); manufacturer.put(176, "AIRZONE – ALTRA"); manufacturer.put(177, "Lithoss Design Switches"); manufacturer.put(178, "3ATEL"); manufacturer.put(179, "Philips Controls"); manufacturer.put(180, "VELUX A/S"); manufacturer.put(181, "LOYTEC"); manufacturer.put(182, "SBS S.p.A."); manufacturer.put(183, "SIRLAN Technologies"); manufacturer.put(184, "Bleu Comm' Azur"); manufacturer.put(185, "IT GmbH"); manufacturer.put(186, "RENSON"); manufacturer.put(187, "HEP Group"); manufacturer.put(188, "Balmart"); manufacturer.put(189, "GFS GmbH"); manufacturer.put(190, "Schenker Storen AG"); manufacturer.put(191, "Algodue Elettronica S.r.L."); manufacturer.put(192, "Newron System"); manufacturer.put(193, "maintronic"); manufacturer.put(194, "Vantage"); manufacturer.put(195, "Foresis"); manufacturer.put(196, "Research & Production Association SEM"); manufacturer.put(197, "Weinzierl Engineering GmbH"); manufacturer.put(198, "Möhlenhoff Wärmetechnik GmbH"); manufacturer.put(199, "PKC-GROUP Oyj"); manufacturer.put(200, "B.E.G."); manufacturer.put(201, "Elsner Elektronik GmbH"); manufacturer.put(202, "Siemens Building Technologies (HK/China) Ltd."); manufacturer.put(204, "Eutrac"); manufacturer.put(205, "Gustav Hensel GmbH & Co. KG"); manufacturer.put(206, "GARO AB"); manufacturer.put(207, "Waldmann Lichttechnik"); manufacturer.put(208, "SCHÜCO"); manufacturer.put(209, "EMU"); manufacturer.put(210, "JNet Systems AG"); manufacturer.put(214, "O.Y.L. Electronics"); manufacturer.put(215, "Galax System"); manufacturer.put(216, "Disch"); manufacturer.put(217, "Aucoteam"); manufacturer.put(218, "Luxmate Controls"); manufacturer.put(219, "Danfoss"); manufacturer.put(220, "AST GmbH"); manufacturer.put(222, "WILA Leuchten"); manufacturer.put(223, "b+b Automations- und Steuerungstechnik"); manufacturer.put(225, "Lingg & Janke"); manufacturer.put(227, "Sauter"); manufacturer.put(228, "SIMU"); manufacturer.put(232, "Theben HTS AG"); manufacturer.put(233, "Amann GmbH"); manufacturer.put(234, "BERG Energiekontrollsysteme GmbH"); manufacturer.put(235, "Hüppe Form Sonnenschutzsysteme GmbH"); manufacturer.put(237, "Oventrop KG"); manufacturer.put(238, "Griesser AG"); manufacturer.put(239, "IPAS GmbH"); manufacturer.put(240, "elero GmbH"); manufacturer.put(241, "Ardan Production and Industrial Controls Ltd."); manufacturer.put(242, "Metec Meßtechnik GmbH"); manufacturer.put(244, "ELKA-Elektronik GmbH"); manufacturer.put(245, "ELEKTROANLAGEN D. NAGEL"); manufacturer.put(246, "Tridonic Bauelemente GmbH"); manufacturer.put(248, "Stengler Gesellschaft"); manufacturer.put(249, "Schneider Electric (MG)"); manufacturer.put(250, "KNX Association"); manufacturer.put(251, "VIVO"); manufacturer.put(252, "Hugo Müller GmbH & Co KG"); manufacturer.put(253, "Siemens HVAC"); manufacturer.put(254, "APT"); manufacturer.put(256, "HighDom"); manufacturer.put(257, "Top Services"); manufacturer.put(258, "ambiHome"); manufacturer.put(259, "DATEC electronic AG"); manufacturer.put(260, "ABUS Security-Center"); manufacturer.put(261, "Lite-Puter"); manufacturer.put(262, "Tantron Electronic"); manufacturer.put(263, "Yönnet"); manufacturer.put(264, "DKX Tech"); manufacturer.put(265, "Viatron"); manufacturer.put(266, "Nautibus"); manufacturer.put(267, "ON Semiconductor"); manufacturer.put(268, "Longchuang"); manufacturer.put(269, "Air-On AG"); manufacturer.put(270, "ib-company GmbH"); manufacturer.put(271, "SATION"); manufacturer.put(272, "Agentilo GmbH"); manufacturer.put(273, "Makel Elektrik"); manufacturer.put(274, "Helios Ventilatoren"); manufacturer.put(275, "Otto Solutions Pte Ltd"); manufacturer.put(276, "Airmaster"); manufacturer.put(277, "Vallox GmbH"); manufacturer.put(278, "Dalitek"); manufacturer.put(279, "ASIN"); manufacturer.put(280, "Bridges Intelligence Technology Inc."); manufacturer.put(281, "ARBONIA"); manufacturer.put(282, "KERMI"); manufacturer.put(283, "PROLUX"); manufacturer.put(284, "ClicHome"); manufacturer.put(285, "COMMAX"); manufacturer.put(286, "EAE"); manufacturer.put(287, "Tense"); manufacturer.put(288, "Seyoung Electronics"); manufacturer.put(289, "Lifedomus"); manufacturer.put(290, "EUROtronic Technology GmbH"); manufacturer.put(291, "tci"); manufacturer.put(292, "Rishun Electronic"); manufacturer.put(293, "Zipato"); manufacturer.put(294, "cm-security GmbH & Co KG"); manufacturer.put(295, "Qing Cables"); manufacturer.put(296, "LABIO"); manufacturer.put(297, "Coster Tecnologie Elettroniche S.p.A."); manufacturer.put(298, "E.G.E"); manufacturer.put(299, "NETxAutomation"); manufacturer.put(300, "tecalor"); manufacturer.put(301, "Urmet Electronics (Huizhou) Ltd."); manufacturer.put(302, "Peiying Building Control"); manufacturer.put(303, "BPT S.p.A. a Socio Unico"); manufacturer.put(304, "Kanontec - KanonBUS"); manufacturer.put(305, "ISER Tech"); manufacturer.put(306, "Fineline"); manufacturer.put(307, "CP Electronics Ltd"); manufacturer.put(308, "Servodan A/S"); manufacturer.put(309, "Simon"); manufacturer.put(310, "GM modular pvt. Ltd."); manufacturer.put(311, "FU CHENG Intelligence"); manufacturer.put(312, "NexKon"); manufacturer.put(313, "FEEL s.r.l"); manufacturer.put(314, "Not Assigned"); manufacturer.put(315, "Shenzhen Fanhai Sanjiang Electronics Co., Ltd."); manufacturer.put(316, "Jiuzhou Greeble"); manufacturer.put(317, "Aumüller Aumatic GmbH"); manufacturer.put(318, "Etman Electric"); manufacturer.put(319, "EMT Controls"); manufacturer.put(320, "ZidaTech AG"); manufacturer.put(321, "IDGS bvba"); manufacturer.put(322, "dakanimo"); manufacturer.put(323, "Trebor Automation AB"); manufacturer.put(324, "Satel sp. z o.o."); manufacturer.put(325, "Russound, Inc."); manufacturer.put(326, "Midea Heating & Ventilating Equipment CO LTD"); manufacturer.put(327, "Consorzio Terranuova"); manufacturer.put(328, "Wolf Heiztechnik GmbH"); manufacturer.put(329, "SONTEC"); manufacturer.put(330, "Belcom Cables Ltd."); manufacturer.put(331, "Guangzhou SeaWin Electrical Technologies Co., Ltd."); manufacturer.put(332, "Acrel"); manufacturer.put(333, "Franke Aquarotter GmbH"); manufacturer.put(334, "Orion Systems"); manufacturer.put(335, "Schrack Technik GmbH"); manufacturer.put(336, "INSPRID"); manufacturer.put(337, "Sunricher"); manufacturer.put(338, "Menred automation system(shanghai) Co.,Ltd."); manufacturer.put(339, "Aurex"); manufacturer.put(340, "Josef Barthelme GmbH & Co. KG"); manufacturer.put(341, "Architecture Numerique"); manufacturer.put(342, "UP GROUP"); manufacturer.put(343, "Teknos-Avinno"); manufacturer.put(344, "Ningbo Dooya Mechanic & Electronic Technology"); manufacturer.put(345, "Thermokon Sensortechnik GmbH"); manufacturer.put(346, "BELIMO Automation AG"); manufacturer.put(347, "Zehnder Group International AG"); manufacturer.put(348, "sks Kinkel Elektronik"); manufacturer.put(349, "ECE Wurmitzer GmbH"); manufacturer.put(350, "LARS"); manufacturer.put(351, "URC"); manufacturer.put(352, "LightControl"); manufacturer.put(353, "ShenZhen YM"); manufacturer.put(354, "MEAN WELL Enterprises Co. Ltd."); manufacturer.put(355, "OSix"); manufacturer.put(356, "AYPRO Technology"); manufacturer.put(357, "Hefei Ecolite Software"); manufacturer.put(358, "Enno"); manufacturer.put(359, "OHOSURE"); manufacturer.put(360, "Garefowl"); manufacturer.put(361, "GEZE"); manufacturer.put(362, "LG Electronics Inc."); manufacturer.put(363, "SMC interiors"); manufacturer.put(365, "SCS Cable"); manufacturer.put(366, "Hoval"); manufacturer.put(367, "CANST"); manufacturer.put(368, "HangZhou Berlin"); manufacturer.put(369, "EVN-Lichttechnik"); manufacturer.put(370, "rutec"); manufacturer.put(371, "Finder"); manufacturer.put(372, "Fujitsu General Limited"); manufacturer.put(373, "ZF Friedrichshafen AG"); manufacturer.put(374, "Crealed"); manufacturer.put(375, "Miles Magic Automation Private Limited"); manufacturer.put(376, "E+"); manufacturer.put(377, "Italcond"); manufacturer.put(378, "SATION"); manufacturer.put(379, "NewBest"); manufacturer.put(380, "GDS DIGITAL SYSTEMS"); manufacturer.put(381, "Iddero"); manufacturer.put(382, "MBNLED"); manufacturer.put(383, "VITRUM"); manufacturer.put(384, "ekey biometric systems GmbH"); manufacturer.put(385, "AMC"); manufacturer.put(386, "TRILUX GmbH & Co. KG"); manufacturer.put(387, "WExcedo"); manufacturer.put(388, "VEMER SPA"); manufacturer.put(389, "Alexander Bürkle GmbH & Co KG"); manufacturer.put(390, "Seetroll"); manufacturer.put(391, "Shenzhen HeGuang"); manufacturer.put(392, "Not Assigned"); manufacturer.put(393, "TRANE B.V.B.A"); manufacturer.put(394, "CAREL"); manufacturer.put(395, "Prolite Controls"); manufacturer.put(396, "BOSMER"); manufacturer.put(397, "EUCHIPS"); manufacturer.put(398, "connect (Thinka connect)"); manufacturer.put(399, "PEAKnx a DOGAWIST company "); manufacturer.put(400, "ACEMATIC"); manufacturer.put(401, "ELAUSYS"); manufacturer.put(402, "ITK Engineering AG"); manufacturer.put(403, "INTEGRA METERING AG"); manufacturer.put(404, "FMS Hospitality Pte Ltd"); manufacturer.put(405, "Nuvo"); manufacturer.put(406, "u::Lux GmbH"); manufacturer.put(407, "Brumberg Leuchten"); manufacturer.put(408, "Lime"); manufacturer.put(409, "Great Empire International Group Co., Ltd."); manufacturer.put(410, "Kavoshpishro Asia"); manufacturer.put(411, "V2 SpA"); manufacturer.put(412, "Johnson Controls"); manufacturer.put(413, "Arkud"); manufacturer.put(414, "Iridium Ltd."); manufacturer.put(415, "bsmart"); manufacturer.put(416, "BAB TECHNOLOGIE GmbH"); manufacturer.put(417, "NICE Spa"); manufacturer.put(418, "Redfish Group Pty Ltd"); manufacturer.put(419, "SABIANA spa"); manufacturer.put(420, "Ubee Interactive Europe"); manufacturer.put(421, "Rexel"); manufacturer.put(422, "Ges Teknik A.S."); manufacturer.put(423, "Ave S.p.A. "); manufacturer.put(424, "Zhuhai Ltech Technology Co., Ltd. "); manufacturer.put(425, "ARCOM"); manufacturer.put(426, "VIA Technologies, Inc."); manufacturer.put(427, "FEELSMART."); manufacturer.put(428, "SUPCON"); manufacturer.put(429, "MANIC"); manufacturer.put(430, "Träum deutsche Elektronik GmbH"); manufacturer.put(431, "Nanjing Shufan Information technology Co.,Ltd."); manufacturer.put(432, "EWTech"); manufacturer.put(433, "Kluger Automation GmbH"); manufacturer.put(434, "JoongAng Control"); manufacturer.put(435, "GreenControls Technology Sdn. Bhd."); manufacturer.put(436, "IME S.p.a."); manufacturer.put(437, "SiChuan HaoDing"); manufacturer.put(438, "Mindjaga Ltd."); manufacturer.put(439, "RuiLi Smart Control"); manufacturer.put(440, "3S-Smart Software Solutions GmbH"); manufacturer.put(441, "Moorgen Deutschland GmbH"); manufacturer.put(442, "CULLMANN TECH"); manufacturer.put(443, "Merck Window Technologies B.V. "); manufacturer.put(444, "ABEGO"); manufacturer.put(445, "myGEKKO"); manufacturer.put(446, "Ergo3 Sarl"); manufacturer.put(447, "STmicroelectronics International N.V."); manufacturer.put(448, "cjc systems"); manufacturer.put(449, "Sudoku"); manufacturer.put(451, "AZ e-lite Pte Ltd"); manufacturer.put(452, "Arlight"); manufacturer.put(453, "Grünbeck Wasseraufbereitung GmbH"); manufacturer.put(454, "Module Electronic"); manufacturer.put(455, "KOPLAT"); manufacturer.put(456, "Guangzhou Letour Life Technology Co., Ltd"); manufacturer.put(457, "ILEVIA"); manufacturer.put(458, "LN SYSTEMTEQ"); manufacturer.put(459, "Hisense SmartHome"); manufacturer.put(460, "Flink Automation System"); manufacturer.put(461, "xxter bv"); manufacturer.put(462, "lynxus technology"); manufacturer.put(463, "ROBOT S.A."); manufacturer.put(464, "Shenzhen Atte Smart Life Co.,Ltd."); manufacturer.put(465, "Noblesse"); manufacturer.put(466, "Advanced Devices"); manufacturer.put(467, "Atrina Building Automation Co. Ltd"); manufacturer.put(468, "Guangdong Daming Laffey electric Co., Ltd."); manufacturer.put(469, "Westerstrand Urfabrik AB"); manufacturer.put(470, "Control4 Corporate"); manufacturer.put(471, "Ontrol"); manufacturer.put(472, "Starnet"); manufacturer.put(473, "BETA CAVI"); manufacturer.put(474, "EaseMore"); manufacturer.put(475, "Vivaldi srl"); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy