diff --git a/ESH-INF/thing/thing-types.xml b/ESH-INF/thing/thing-types.xml index 915c4d14f..5f17634b2 100644 --- a/ESH-INF/thing/thing-types.xml +++ b/ESH-INF/thing/thing-types.xml @@ -85,7 +85,7 @@ true 30 - + Websocket client's maximum binary message size in kB true diff --git a/META-INF/MANIFEST.MF b/META-INF/MANIFEST.MF index bba9c2726..e2c7c1a2a 100644 --- a/META-INF/MANIFEST.MF +++ b/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: Loxone Binding Bundle-SymbolicName: org.openhab.binding.loxone;singleton:=true Bundle-Vendor: openHAB -Bundle-Version: 2.1.0.12 +Bundle-Version: 2.1.0.13 Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ClassPath: . Import-Package: diff --git a/pom.xml b/pom.xml index 7645f5264..82c8f5b05 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ org.openhab.binding org.openhab.binding.loxone - 2.1.0-12 + 2.1.0-13 Loxone Binding eclipse-plugin diff --git a/src/main/java/org/openhab/binding/loxone/core/LxControl.java b/src/main/java/org/openhab/binding/loxone/core/LxControl.java index b2247b3c7..8cd15abf2 100644 --- a/src/main/java/org/openhab/binding/loxone/core/LxControl.java +++ b/src/main/java/org/openhab/binding/loxone/core/LxControl.java @@ -35,10 +35,10 @@ public abstract class LxControl { private LxContainer room; private LxCategory category; private Map states = new HashMap(); - Logger logger = LoggerFactory.getLogger(LxControl.class); LxUuid uuid; LxWsClient socketClient; + Logger logger = LoggerFactory.getLogger(LxControl.class); Map subControls = new HashMap(); /** @@ -56,6 +56,7 @@ public abstract class LxControl { * Category that this control belongs to */ LxControl(LxWsClient client, LxUuid uuid, LxJsonControl json, LxContainer room, LxCategory category) { + logger.trace("Creating new LxControl: {}", json.type); socketClient = client; this.uuid = uuid; if (json.type != null) { @@ -186,6 +187,9 @@ public boolean equals(Object object) { * New category that this control belongs to */ void update(LxJsonControl json, LxContainer room, LxCategory category) { + + logger.trace("Updating LxControl: {}", json.type); + this.name = json.name; this.room = room; this.category = category; @@ -199,6 +203,9 @@ void update(LxJsonControl json, LxContainer room, LxCategory category) { // retrieve all states from the configuration if (json.states != null) { + + logger.trace("Reading states for LxControl: {}", json.type); + for (Map.Entry jsonState : json.states.entrySet()) { JsonElement element = jsonState.getValue(); if (element instanceof JsonArray) { @@ -213,8 +220,10 @@ void update(LxJsonControl json, LxContainer room, LxCategory category) { String name = jsonState.getKey().toLowerCase(); LxControlState state = states.get(name); if (state == null) { + logger.trace("New state for LxControl {}: {}", json.type, name); state = new LxControlState(id, name, this); } else { + logger.trace("Existing state for LxControl{} : {}", json.type, name); state.getUuid().setUpdate(true); state.setName(name); } @@ -224,39 +233,55 @@ void update(LxJsonControl json, LxContainer room, LxCategory category) { } } - static LxControl createControl(LxWsClient client, LxUuid id, LxJsonControl json, LxContainer room, + /** + * + * @param client + * websocket client to facilitate communication with Miniserver + * @param uuid + * UUID of the control to be created + * @param json + * JSON describing the control as received from the Miniserver + * @param room + * Room that this control belongs to + * @param category + * Category that this control belongs to + * @return + * created control object or null if error + */ + static LxControl createControl(LxWsClient client, LxUuid uuid, LxJsonControl json, LxContainer room, LxCategory category) { + if (json == null || json.type == null || json.name == null) { return null; } - LxControl ctrl = null; String type = json.type.toLowerCase(); if (LxControlSwitch.accepts(type)) { - ctrl = new LxControlSwitch(client, id, json, room, category); + ctrl = new LxControlSwitch(client, uuid, json, room, category); } else if (LxControlPushbutton.accepts(type)) { - ctrl = new LxControlPushbutton(client, id, json, room, category); + ctrl = new LxControlPushbutton(client, uuid, json, room, category); } else if (LxControlJalousie.accepts(type)) { - ctrl = new LxControlJalousie(client, id, json, room, category); + ctrl = new LxControlJalousie(client, uuid, json, room, category); + } else if (LxControlTextState.accepts(type)) { - ctrl = new LxControlTextState(client, id, json, room, category); + ctrl = new LxControlTextState(client, uuid, json, room, category); } else if (json.details != null) { if (LxControlInfoOnlyDigital.accepts(type) && json.details.text != null) { - ctrl = new LxControlInfoOnlyDigital(client, id, json, room, category); + ctrl = new LxControlInfoOnlyDigital(client, uuid, json, room, category); } else if (LxControlInfoOnlyAnalog.accepts(type)) { - ctrl = new LxControlInfoOnlyAnalog(client, id, json, room, category); + ctrl = new LxControlInfoOnlyAnalog(client, uuid, json, room, category); } else if (LxControlLightController.accepts(type)) { - ctrl = new LxControlLightController(client, id, json, room, category); + ctrl = new LxControlLightController(client, uuid, json, room, category); } else if (LxControlRadio.accepts(type)) { - ctrl = new LxControlRadio(client, id, json, room, category); + ctrl = new LxControlRadio(client, uuid, json, room, category); } } return ctrl; diff --git a/src/main/java/org/openhab/binding/loxone/core/LxControlLightController.java b/src/main/java/org/openhab/binding/loxone/core/LxControlLightController.java index 050148d71..cf1213dc8 100644 --- a/src/main/java/org/openhab/binding/loxone/core/LxControlLightController.java +++ b/src/main/java/org/openhab/binding/loxone/core/LxControlLightController.java @@ -9,6 +9,8 @@ package org.openhab.binding.loxone.core; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.TreeMap; @@ -27,6 +29,10 @@ * */ public class LxControlLightController extends LxControl implements LxControlStateListener { + /** + * Number of scenes supported by the Miniserver. Indexing starts with 0 to NUM_OF_SCENES-1. + */ + public static final int NUM_OF_SCENES = 10; /** * A name by which Miniserver refers to light controller controls @@ -59,7 +65,6 @@ public class LxControlLightController extends LxControl implements LxControlStat private static final String CMD_PREVIOUS_SCENE = "minus"; private static final int SCENE_ALL_ON = 9; - public static final int NUM_OF_SCENES = 10; private Map sceneNames = new TreeMap(); private boolean newSceneNames = false; private int movementScene = -1; @@ -80,23 +85,13 @@ public class LxControlLightController extends LxControl implements LxControlStat */ LxControlLightController(LxWsClient client, LxUuid uuid, LxJsonControl json, LxContainer room, LxCategory category) { + super(client, uuid, json, room, category); if (json.details != null) { this.movementScene = json.details.movementScene; } - - if (json.subControls != null) { - for (LxJsonControl subControl : json.subControls.values()) { - // recursively create a subcontrol as a new control - subControl.room = json.room; - subControl.cat = json.cat; - LxUuid subId = new LxUuid(subControl.uuidAction); - LxControl control = LxControl.createControl(client, subId, subControl, room, category); - subControls.put(control.uuid, control); - } - } - + // sub-controls of this control have been created when update() method was called by super class constructor LxControlState sceneListState = getState(STATE_SCENE_LIST); if (sceneListState != null) { sceneListState.addListener(this); @@ -115,6 +110,7 @@ public class LxControlLightController extends LxControl implements LxControlStat */ @Override void update(LxJsonControl json, LxContainer room, LxCategory category) { + super.update(json, room, category); if (json.subControls != null) { @@ -127,15 +123,21 @@ void update(LxJsonControl json, LxContainer room, LxCategory category) { subControls.get(uuid).update(json, room, category); } else { LxControl control = LxControl.createControl(socketClient, uuid, subControl, room, category); - subControls.put(control.uuid, control); + if (control != null) { + subControls.put(control.uuid, control); + } } } } + List toRemove = new ArrayList(subControls.size()); for (LxControl control : subControls.values()) { if (!control.uuid.getUpdate()) { - subControls.remove(control.uuid); + toRemove.add(control.uuid); } } + for (LxUuid id : toRemove) { + subControls.remove(id); + } } /** diff --git a/src/main/java/org/openhab/binding/loxone/core/LxServer.java b/src/main/java/org/openhab/binding/loxone/core/LxServer.java index d3d174900..f8f6d57b3 100644 --- a/src/main/java/org/openhab/binding/loxone/core/LxServer.java +++ b/src/main/java/org/openhab/binding/loxone/core/LxServer.java @@ -18,6 +18,7 @@ import java.util.Set; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.atomic.AtomicInteger; import org.openhab.binding.loxone.core.LxServerEvent.EventType; import org.slf4j.Logger; @@ -55,7 +56,7 @@ public class LxServer { private final InetAddress host; private final int port; private final String user, password; - private String miniserverName, projectName, location, serial, cloudAddress; + private String miniserverName = "", projectName = "", location = "", serial = "", cloudAddress = ""; @SuppressWarnings("unused") private String roomTitle, categoryTitle; private int firstConDelay = 1, connectErrDelay = 10, userErrorDelay = 60, comErrorDelay = 30; @@ -75,7 +76,9 @@ public class LxServer { private BlockingQueue queue = new LinkedBlockingQueue(); private Logger logger = LoggerFactory.getLogger(LxServer.class); - private static int debugId = 1; + + private int debugId; + private static AtomicInteger staticDebugId = new AtomicInteger(1); /** * Reasons why Miniserver may be not reachable @@ -135,7 +138,9 @@ public LxServer(InetAddress host, int port, String user, String password) { this.port = port; this.user = user; this.password = password; - socketClient = new LxWsClient(++debugId, queue, host, port, user, password); + + debugId = staticDebugId.getAndIncrement(); + socketClient = new LxWsClient(debugId, queue, host, port, user, password); } /** @@ -200,7 +205,9 @@ public void stop() { */ public void update(int firstConDelay, int keepAlivePeriod, int connectErrDelay, int userErrorDelay, int comErrorDelay, int maxBinMsgSize, int maxTextMsgSize) { + logger.debug("[{}] Server update configuration", debugId); + if (firstConDelay >= 0) { this.firstConDelay = firstConDelay; } @@ -397,6 +404,7 @@ public void run() { LxServerEvent wsMsg = queue.take(); EventType event = wsMsg.getEvent(); logger.trace("[{}] Server received event: {}", debugId, event.toString()); + switch (event) { case RECEIVED_CONFIG: LxJsonApp3 config = (LxJsonApp3) wsMsg.getObject(); @@ -489,6 +497,7 @@ public void run() { * parsed JSON LoxApp3.json file */ private void updateConfig(LxJsonApp3 config) { + logger.trace("[{}] Updating configuration from Miniserver", debugId); for (LxUuid id : uuids) { id.setUpdate(false); @@ -497,26 +506,45 @@ private void updateConfig(LxJsonApp3 config) { id.setUpdate(false); } - miniserverName = buildName(config.msInfo.msName); - projectName = buildName(config.msInfo.projectName); - location = buildName(config.msInfo.location); - serial = buildName(config.msInfo.serialNr); - roomTitle = buildName(config.msInfo.roomTitle); - categoryTitle = buildName(config.msInfo.catTitle); - cloudAddress = buildName(config.msInfo.remoteUrl); + if (config.msInfo != null) { + logger.trace("[{}] updating global config", debugId); + miniserverName = buildName(config.msInfo.msName); + projectName = buildName(config.msInfo.projectName); + location = buildName(config.msInfo.location); + serial = buildName(config.msInfo.serialNr); + roomTitle = buildName(config.msInfo.roomTitle); + categoryTitle = buildName(config.msInfo.catTitle); + cloudAddress = buildName(config.msInfo.remoteUrl); + } else { + logger.warn("[{}] missing global configuration msInfo on Loxone", debugId); + } // create internal structures based on configuration file - for (LxJsonApp3.LxJsonRoom room : config.rooms.values()) { - addOrUpdateRoom(new LxUuid(room.uuid), room.name); + if (config.rooms != null) { + logger.trace("[{}] creating rooms", debugId); + for (LxJsonApp3.LxJsonRoom room : config.rooms.values()) { + addOrUpdateRoom(new LxUuid(room.uuid), room.name); + } } - for (LxJsonApp3.LxJsonCat cat : config.cats.values()) { - addOrUpdateCategory(new LxUuid(cat.uuid), cat.name, cat.type); + if (config.cats != null) { + logger.trace("[{}] creating categories", debugId); + for (LxJsonApp3.LxJsonCat cat : config.cats.values()) { + addOrUpdateCategory(new LxUuid(cat.uuid), cat.name, cat.type); + } } - for (LxJsonApp3.LxJsonControl ctrl : config.controls.values()) { - // create a new control or update existing one - addOrUpdateControl(ctrl); + if (config.controls != null) { + logger.trace("[{}] creating controls", debugId); + for (LxJsonApp3.LxJsonControl ctrl : config.controls.values()) { + // create a new control or update existing one + try { + addOrUpdateControl(ctrl); + } catch (Exception e) { + logger.error("[{}] exception creating control {}: {}", debugId, ctrl.name, e.toString()); + } + } } // remove items that do not exist anymore in Miniserver + logger.trace("[{}] removing unused objects", debugId); removeUnusedFromMap(rooms); removeUnusedFromMap(categories); removeUnusedFromMap(controls); @@ -697,7 +725,6 @@ private LxCategory addOrUpdateCategory(LxUuid id, String name, String type) { * JSON original object of this control to get extra parameters */ private void addOrUpdateControl(LxJsonApp3.LxJsonControl json) { - if (json == null || json.uuidAction == null || json.name == null || json.type == null) { return; } @@ -739,12 +766,12 @@ private void updateControls(LxControl control) { } /** - * Check and converts null string to empty string. + * Check and convert null string to empty string. * * @param name * string to check * @return - * string guaranteed to not be null + * string guaranteed to be not null */ private String buildName(String name) { if (name == null) { diff --git a/src/main/java/org/openhab/binding/loxone/core/LxWsClient.java b/src/main/java/org/openhab/binding/loxone/core/LxWsClient.java index 9593ce297..9b3a40f10 100644 --- a/src/main/java/org/openhab/binding/loxone/core/LxWsClient.java +++ b/src/main/java/org/openhab/binding/loxone/core/LxWsClient.java @@ -247,7 +247,7 @@ private class LxWsBinaryHeader { * true if connection request initiated correctly, false if not */ boolean connect() { - + logger.trace("[{}] connect() websocket", debugId); if (state != ClientState.IDLE) { close("Attempt to connect a websocket in non-idle state: " + state.toString()); return false; @@ -281,6 +281,7 @@ boolean connect() { * After calling this method, client is ready to perform a new connection request with {@link #connect()}. */ void disconnect() { + logger.trace("[{}] disconnect() websocket", debugId); if (wsClient != null) { try { close("Disconnecting websocket client"); @@ -290,7 +291,7 @@ void disconnect() { logger.debug("[{}] Failed to stop websocket client, message = {}", debugId, e.getMessage()); } } else { - logger.debug("[{}] Attempt to disconnect websocket client, but wsClient = null", debugId); + logger.debug("[{}] Attempt to disconnect websocket client, but wsClient == null", debugId); } } @@ -302,6 +303,7 @@ void disconnect() { * reason for closing the websocket */ private void close(String reason) { + logger.trace("[{}] close() websocket", debugId); if (socket != null) { synchronized (socket) { if (socket.session != null) { @@ -379,35 +381,8 @@ public class LxWebSocket { private ScheduledFuture keepAlive = null; private LxWsBinaryHeader header = null; - @OnWebSocketClose - public void onClose(int statusCode, String reason) { - logger.debug("[{}] Websocket connection in state {} closed with code {} reason : {}", debugId, - state.toString(), statusCode, reason); - if (keepAlive != null) { - keepAlive.cancel(true); - keepAlive = null; - } - if (state == ClientState.CLOSING) { - synchronized (this) { - session = null; - } - } else if (state != ClientState.IDLE) { - LxServer.OfflineReason reasonCode; - if (statusCode == 1001) { - reasonCode = LxServer.OfflineReason.IDLE_TIMEOUT; - } else if (statusCode == 4003) { - reasonCode = LxServer.OfflineReason.TOO_MANY_FAILED_LOGIN_ATTEMPTS; - } else { - reasonCode = LxServer.OfflineReason.COMMUNICATION_ERROR; - } - notifyMaster(EventType.SERVER_OFFLINE, reasonCode, reason); - } - setClientState(ClientState.IDLE); - } - @OnWebSocketConnect public void onConnect(Session session) { - if (state != ClientState.CONNECTING) { logger.debug("[{}] Unexpected connect received on websocket in state {}", debugId, state.toString()); return; @@ -431,16 +406,43 @@ public void onConnect(Session session) { keepAlive = SCHEDULER.scheduleAtFixedRate(new Runnable() { @Override public void run() { - try { - logger.debug("[{}] sending keepalive message", debugId); - sendString(CMD_KEEPALIVE); - } catch (IOException e) { - logger.debug("[{}] error sending keepalive message", debugId); + if (state == ClientState.CLOSING || state == ClientState.IDLE || state == ClientState.CONNECTING) { + stopKeepAlive(); + } else { + try { + logger.debug("[{}] sending keepalive message", debugId); + sendString(CMD_KEEPALIVE); + } catch (IOException e) { + logger.debug("[{}] error sending keepalive message", debugId); + } } } }, keepAlivePeriod, keepAlivePeriod, TimeUnit.SECONDS); } + @OnWebSocketClose + public void onClose(int statusCode, String reason) { + logger.debug("[{}] Websocket connection in state {} closed with code {} reason : {}", debugId, + state.toString(), statusCode, reason); + stopKeepAlive(); + if (state == ClientState.CLOSING) { + synchronized (this) { + session = null; + } + } else if (state != ClientState.IDLE) { + LxServer.OfflineReason reasonCode; + if (statusCode == 1001) { + reasonCode = LxServer.OfflineReason.IDLE_TIMEOUT; + } else if (statusCode == 4003) { + reasonCode = LxServer.OfflineReason.TOO_MANY_FAILED_LOGIN_ATTEMPTS; + } else { + reasonCode = LxServer.OfflineReason.COMMUNICATION_ERROR; + } + notifyMaster(EventType.SERVER_OFFLINE, reasonCode, reason); + } + setClientState(ClientState.IDLE); + } + @OnWebSocketError public void onError(Throwable error) { logger.debug("[{}] Websocket error : {}", debugId, error.getMessage()); @@ -581,6 +583,14 @@ public void onMessage(String msg) { } } + private void stopKeepAlive() { + logger.trace("[{}] stopping keepalives in state {}", debugId, state.toString()); + if (keepAlive != null) { + keepAlive.cancel(true); + keepAlive = null; + } + } + private void notifyMaster(EventType event, LxServer.OfflineReason reason, Object object) { if (reason == null) { reason = LxServer.OfflineReason.NONE; diff --git a/src/main/java/org/openhab/binding/loxone/handler/LoxoneMiniserverHandler.java b/src/main/java/org/openhab/binding/loxone/handler/LoxoneMiniserverHandler.java index 4df91c868..7dcf353bd 100644 --- a/src/main/java/org/openhab/binding/loxone/handler/LoxoneMiniserverHandler.java +++ b/src/main/java/org/openhab/binding/loxone/handler/LoxoneMiniserverHandler.java @@ -108,7 +108,6 @@ public void handleCommand(ChannelUID channelUID, Command command) { logger.debug("Control '{}' received command: {}", control.getName(), command.toString()); - // do not check for compatibility between command and control type here, each control has to do it itself try { if (command instanceof RefreshType) { updateChannelState(channelUID, control); @@ -131,7 +130,6 @@ public void handleCommand(ChannelUID channelUID, Command command) { } if (control instanceof LxControlJalousie) { - LxControlJalousie jalousie = (LxControlJalousie) control; if (command instanceof PercentType) { jalousie.moveToPosition(((PercentType) command).doubleValue() / 100); @@ -201,7 +199,7 @@ public void channelLinked(ChannelUID channelUID) { @Override public void initialize() { - + logger.trace("Initializing thing"); switchTypeId = addNewChannelType("switch", "Switch", "Switch", "Loxone Switch"); rollerTypeId = addNewChannelType("rollershutter", "Rollershutter", "Rollershutter", "Loxone Jalousie"); infoTypeId = addNewChannelType("infoonly", "String", "Information", "Loxone read-only information"); @@ -231,6 +229,7 @@ public void initialize() { @Override public void onNewConfig(LxServer server) { + logger.trace("Processing new configuration"); Thing thing = getThing(); thing.setProperty(MINISERVER_PROPERTY_MINISERVER_NAME, server.getMiniserverName()); thing.setProperty(MINISERVER_PROPERTY_SERIAL, server.getSerial()); @@ -241,6 +240,7 @@ public void onNewConfig(LxServer server) { thing.setLocation(server.getLocation()); } + logger.trace("Removing old channels"); ArrayList channels = new ArrayList(); ThingBuilder builder = editThing(); @@ -250,6 +250,7 @@ public void onNewConfig(LxServer server) { } } + logger.trace("Building new channels ({} controls)", server.getControls().size()); for (LxControl control : server.getControls().values()) { Channel channel = createChannelForControl(control); if (channel != null) { @@ -258,6 +259,7 @@ public void onNewConfig(LxServer server) { } } + logger.trace("Sorting channels"); channels.sort(new Comparator() { @Override public int compare(Channel c1, Channel c2) { @@ -265,6 +267,7 @@ public int compare(Channel c1, Channel c2) { } }); + logger.trace("Updating thing"); builder.withChannels(channels); updateThing(builder.build()); } @@ -277,11 +280,14 @@ public void onControlStateUpdate(LxControl control) { @Override public void onServerGoesOnline() { + logger.debug("Server goes online."); updateStatus(ThingStatus.ONLINE); } @Override public void onServerGoesOffline(LxServer.OfflineReason reason, String details) { + logger.debug("Server goes offline: {}, {}", reason.toString(), details); + switch (reason) { case AUTHENTICATION_TIMEOUT: updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "User authentication timeout"); @@ -317,6 +323,7 @@ public void onServerGoesOffline(LxServer.OfflineReason reason, String details) { @Override public void dispose() { + logger.debug("Disposing of server"); if (server != null) { server.stop(); server = null; @@ -334,6 +341,10 @@ public void dispose() { * created {@link Channel} object */ private Channel createChannelForControl(LxControl control) { + + logger.trace("Creating channel for control: {}, {}", control.getClass().getSimpleName(), + control.getUuid().toString()); + String channelLabel; String itemType = null; ChannelTypeUID typeId = null; @@ -403,6 +414,16 @@ private Channel createChannelForControl(LxControl control) { return null; } + /** + * Builds {@link StateDescription} for channel type, that has multiple options to select from + * + * @param options + * collection of options, where key is option ID (number in reality) and value is option name + * @param lastOption + * maximum value an option ID can have + * @return + * state description to be used for creating channel type + */ private StateDescription buildStateDescription(Map options, int lastOption) { if (options != null) { List optionsList = new ArrayList(); @@ -416,43 +437,13 @@ private StateDescription buildStateDescription(Map options, int } /** - * Create and register a new channel type + * Update thing's channel state with current control's state * - * @param controlType - * type of Loxone control (e.g. switch, jalousie) - * @param itemType - * type of OpenHAB item - * @param label - * label for the channel type - * @param description - * description of the channel type - * @param options - * map of options for drop down lists (can be null) - * @param lastOption - * index of last option - * @param controlUuid - * UUID of Loxone control object (can be null if channel type is generic) - * @return - * channel type ID of newly created type + * @param channelUID + * channel ID to be updated + * @param control + * control to take state from */ - private ChannelTypeUID addNewChannelType(String controlType, String itemType, String label, String description, - Map options, int lastOption, String controlUuid) { - String name = getThing().getUID().getAsString() + ":" + controlType; - if (controlUuid != null) { - name += ":" + controlUuid; - } - ChannelTypeUID typeId = new ChannelTypeUID(name); - ChannelType type = new ChannelType(typeId, false, itemType, label, description, null, null, - buildStateDescription(options, lastOption), null); - factory.removeChannelType(typeId); - factory.addChannelType(type); - return typeId; - } - - private ChannelTypeUID addNewChannelType(String controlType, String itemType, String label, String description) { - return addNewChannelType(controlType, itemType, label, description, null, 0, null); - } - private void updateChannelState(ChannelUID channelUID, LxControl control) { if (control instanceof LxControlSwitch) { double value = ((LxControlSwitch) control).getState(); @@ -506,6 +497,54 @@ private void updateChannelState(ChannelUID channelUID, LxControl control) { } } + /** + * Create and register a new channel type + * + * @param controlType + * type of Loxone control (e.g. switch, jalousie) + * @param itemType + * type of OpenHAB item + * @param label + * label for the channel type + * @param description + * description of the channel type + * @param options + * map of options for drop down lists (can be null) + * @param lastOption + * index of last option + * @param controlUuid + * UUID of Loxone control object (can be null if channel type is generic) + * @return + * channel type ID of newly created type + */ + private ChannelTypeUID addNewChannelType(String controlType, String itemType, String label, String description, + Map options, int lastOption, String controlUuid) { + logger.trace("Creating a new channel type for {}, {}", controlType, itemType); + + String name = getThing().getUID().getAsString() + ":" + controlType; + if (controlUuid != null) { + name += ":" + controlUuid; + } + ChannelTypeUID typeId = new ChannelTypeUID(name); + ChannelType type = new ChannelType(typeId, false, itemType, label, description, null, null, + buildStateDescription(options, lastOption), null); + factory.removeChannelType(typeId); + factory.addChannelType(type); + return typeId; + } + + private ChannelTypeUID addNewChannelType(String controlType, String itemType, String label, String description) { + return addNewChannelType(controlType, itemType, label, description, null, 0, null); + } + + /** + * Based on channel ID, return corresponding {@link LxControl} object + * + * @param channelUID + * channel ID of the control to find + * @return + * control corresponding to the channel ID or null if not found + */ private LxControl getControlFromChannelUID(ChannelUID channelUID) { String channelId = channelUID.getIdWithoutGroup(); return server.findControl(new LxUuid(channelId));