partially working 3.x binding
M pom.xml +4 -12
@@ 1,26 1,18 @@ 
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
 
-  <modelVersion>4.0.0</modelVersion>
 
   <parent>
     <groupId>org.openhab.addons.bundles</groupId>
     <artifactId>org.openhab.addons.reactor.bundles</artifactId>
-    <version>2.5.13-SNAPSHOT</version>
-    <relativePath>../openhab-addons/bundles</relativePath>
+    <version>3.0.1</version>
   </parent>
 
-  <artifactId>org.openhab.binding.lutron-mqtt</artifactId>
+  <version>3.0.2-SNAPSHOT</version>
 
+  <artifactId>org.openhab.binding.lutronmqtt</artifactId>
   <name>openHAB Add-ons :: Bundles :: Lutron-MQTT Binding</name>
 
-  <repositories>
-    <repository>
-      <id>openhab</id>
-      <name>openhab</name>
-      <url>https://dl.bintray.com/openhab/mvn/</url>
-    </repository>
-  </repositories>
-
 </project>

          
A => src/main/feature/feature.xml +11 -0
@@ 0,0 1,11 @@ 
+<?xml version="1.0" encoding="UTF-8"?>
+<features name="org.openhab.binding.lutronmqtt-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
+	<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
+
+	<feature name="openhab-binding-lutronmqtt" description="Lutron-MQTT Binding" version="${project.version}">
+		<feature>openhab-runtime-base</feature>
+		<feature>openhab-transport-mqtt</feature>
+		<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.lutronmqtt/${project.version}</bundle>
+	</feature>
+
+</features>

          
M src/main/java/org/openhab/binding/lutronmqtt/LutronMQTTBindingConstants.java +1 -1
@@ 53,7 53,7 @@ public class LutronMQTTBindingConstants 
     public final static String PROPERTY_OBJECT_ID = "objectId";
     public final static String PROPERTY_OBJECT_NAME = "name";
     public final static String PROPERTY_INTEGRATION_ID = "integrationId";
-    public final static String PROPERTY_LINK_ADDRESS = "linkAddress";
+    // public final static String PROPERTY_LINK_ADDRESS = "linkAddress";
 
     public final static String CONFIG_TOKEN = "token";
 

          
M src/main/java/org/openhab/binding/lutronmqtt/discovery/LutronMQTTHubDiscoveryParticipant.java +6 -13
@@ 12,14 12,18 @@ import org.eclipse.smarthome.config.disc
 import org.eclipse.smarthome.core.thing.ThingTypeUID;
 import org.eclipse.smarthome.core.thing.ThingUID;
 import org.openhab.binding.lutronmqtt.LutronMQTTBindingConstants;
+import org.openhab.core.config.discovery.DiscoveryResult;
+import org.openhab.core.config.discovery.DiscoveryResultBuilder;
+import org.openhab.core.config.discovery.mdns.MDNSDiscoveryParticipant;
+import org.openhab.core.thing.ThingTypeUID;
+import org.openhab.core.thing.ThingUID;
 import org.osgi.service.component.annotations.Component;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 @Component(service = MDNSDiscoveryParticipant.class, immediate = true, configurationPid = "binding.lutronmqtt", name = "org.openhab.binding.lutronmqtt.discovery.hub")
-public class LutronMQTTHubDiscoveryParticipant implements MDNSDiscoveryParticipant, ExtendedDiscoveryService {
+public class LutronMQTTHubDiscoveryParticipant implements MDNSDiscoveryParticipant {
     private Logger logger = LoggerFactory.getLogger(LutronMQTTHubDiscoveryParticipant.class);
-    private DiscoveryServiceCallback discoveryServiceCallback;
 
     @Override
     public Set<ThingTypeUID> getSupportedThingTypeUIDs() {

          
@@ 109,15 113,4 @@ public class LutronMQTTHubDiscoveryParti
 
         return null;
     }
-
-    @Override
-    public void setDiscoveryServiceCallback(DiscoveryServiceCallback discoveryServiceCallback) {
-        logger.warn(discoveryServiceCallback.toString());
-        this.discoveryServiceCallback = discoveryServiceCallback;
-        // log.debug(discoveryServiceCallback.toString());
-    }
-
-    public DiscoveryServiceCallback getDiscoveryServiceCallback() {
-        return discoveryServiceCallback;
-    }
 }

          
A => src/main/java/org/openhab/binding/lutronmqtt/handler/BaseLutronDeviceHandler.java +193 -0
@@ 0,0 1,193 @@ 
+package org.openhab.binding.lutronmqtt.handler;
+
+import static org.openhab.binding.lutronmqtt.LutronMQTTBindingConstants.*;
+
+import java.util.Map;
+import java.util.Objects;
+
+import org.openhab.binding.lutronmqtt.model.LutronDevice;
+import org.openhab.core.thing.*;
+import org.openhab.core.thing.binding.BaseThingHandler;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class BaseLutronDeviceHandler extends BaseThingHandler implements DeviceStatusListener {
+
+    protected int deviceId; //
+    protected int integrationId;
+    protected LutronDevice device; // last update received for this device.
+    protected int linkAddress;
+
+    protected LutronMQTTHubHandler hubHandler;
+
+    static protected Logger log = LoggerFactory.getLogger(BaseLutronDeviceHandler.class);
+
+    public BaseLutronDeviceHandler(Thing thing) {
+        super(thing);
+    }
+
+    @Override
+    public void initialize() {
+        log.debug("Initializing lutron device handler.");
+        initializeThing((getBridge() == null) ? null : getBridge().getStatus());
+    }
+
+    protected void updateDeviceState(Map<String, Object> lightState) {
+        log.warn("updateDeviceState: " + lightState);
+        getHubHandler().setDesiredState(deviceId, lightState);
+    }
+
+    private void initializeThing(ThingStatus bridgeStatus) {
+        log.debug("initializeThing thing {} bridge status {}", getThing().getUID(), bridgeStatus);
+        final String configDeviceId = getThing().getProperties().get(PROPERTY_OBJECT_ID);
+        log.warn("intializeThing " + getThing().getProperties().get(PROPERTY_INTEGRATION_ID));
+        integrationId = Integer
+                .parseInt(Objects.requireNonNull(getThing().getProperties().get(PROPERTY_INTEGRATION_ID)));
+        linkAddress = Integer.parseInt(Objects.requireNonNull(getThing().getProperties().get(PROPERTY_LINK_ADDRESS)));
+
+        if (configDeviceId != null) {
+            deviceId = Integer.valueOf(configDeviceId);
+
+            if (getHubHandler() != null) {
+                if (bridgeStatus == ThingStatus.ONLINE) {
+                    getHubHandler().requestUpdateForDevice(linkAddress);
+                    LutronDevice device = getHubHandler().getDeviceByLinkAddress(linkAddress);
+                    if (device == null) {
+                        updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
+                        return;
+                    }
+                    updateStatus(ThingStatus.ONLINE);
+
+                    // receiving a response to the request update method above should trigger a state change.
+                    // onDeviceStateChanged(getHubHandler().getDeviceByIntegrationId(integrationId));
+                    // initializeProperties();
+                } else {
+                    updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
+                }
+            } else {
+                updateStatus(ThingStatus.OFFLINE);
+            }
+        } else {
+            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
+        }
+    }
+
+    protected synchronized LutronMQTTHubHandler getHubHandler() {
+        if (this.hubHandler == null) {
+            Bridge bridge = getBridge();
+            if (bridge == null) {
+                return null;
+            }
+            ThingHandler handler = bridge.getHandler();
+            if (handler instanceof LutronMQTTHubHandler) {
+                this.hubHandler = (LutronMQTTHubHandler) handler;
+                this.hubHandler.registerDeviceStatusListener(this);
+            } else {
+                return null;
+            }
+        }
+        return this.hubHandler;
+    }
+
+    public LutronDevice getDevice() {
+        if (device != null) {
+            return device;
+        }
+
+        LutronMQTTHubHandler handler = getHubHandler();
+        device = handler.getDeviceByLinkAddress(linkAddress);
+        return device;
+    }
+
+    @Override
+    public void onDeviceFound(LutronDevice d) {
+        if (d.getId() == deviceId) {
+            updateStatus(ThingStatus.ONLINE);
+            onDeviceStateChanged(d);
+        }
+    }
+
+    @Override
+    public void onDeviceRemoved(LutronDevice d) {
+        if (d.getId() == deviceId) {
+            updateStatus(ThingStatus.OFFLINE);
+        }
+    }
+
+    @Override
+    public void onDeviceStateChanged(LutronDevice d) {
+        if (d.getLinkAddress() != linkAddress) {
+            return;
+        }
+
+        log.info("Go device status change for " + this.getThing().getLabel());
+
+        if (d.hasUpdatedProperties()) {
+            log.info("Received notice of pending state change.");
+        }
+
+        if (false && device != null
+                && d.getProperty(LUTRON_PROPERTY_LEVEL) == (device.getProperty(LUTRON_PROPERTY_LEVEL))) {
+            log.info("Lutron Device: " + d.getName() + " Received State Changed but no difference");
+            return;
+        }
+
+        device = d;
+    }
+
+    abstract void updateDeviceState(LutronDevice d);
+
+    @Override
+    public void dispose() {
+        log.debug("Handler disposed. Unregistering listener.");
+        if (deviceId != 0) { // technically 0 is a valid device id but it appears to be reserved for the hub
+            LutronMQTTHubHandler hubHandler = getHubHandler();
+            if (hubHandler != null) {
+                hubHandler.unregisterDeviceStatusListener(this);
+                this.hubHandler = null;
+            }
+            deviceId = 0;
+        }
+    }
+
+    protected void scheduleUpdateForDevice(int deviceId) {
+        if (true == true) {
+            return;
+        }
+        log.info("Scheduling an update request for deviceId=" + deviceId);
+        scheduler.submit(new Runnable() {
+            @Override
+            public void run() {
+                LutronMQTTHubHandler handler = getHubHandler();
+                if (handler != null) {
+                    onDeviceStateChanged(handler.getDeviceByLinkAddress(linkAddress));
+                }
+            }
+        });
+    }
+
+    @Override
+    public void bridgeStatusChanged(ThingStatusInfo bridgeStatus) {
+        super.bridgeStatusChanged(bridgeStatus);
+        if (bridgeStatus.getStatus() == ThingStatus.ONLINE) {
+            scheduleUpdateForDevice(linkAddress);
+        }
+    }
+
+    @Override
+    public void channelLinked(ChannelUID channelUID) {
+        if (this.getBridge().getStatus() == ThingStatus.ONLINE) {
+            // TODO really only need 1 for each device, no matter the channelse.
+            scheduleUpdateForDevice(linkAddress);
+        } else {
+            log.info("Channel Linked but hub is not online.");
+        }
+    }
+
+    protected Integer getCurrentLevel(LutronDevice light) {
+        int brightness = light.getProperty(LUTRON_PROPERTY_LEVEL);
+
+        return brightness;
+    }
+}

          
M src/main/java/org/openhab/binding/lutronmqtt/handler/LutronMQTTDimmableLightHandler.java +1 -1
@@ 10,7 10,7 @@ package org.openhab.binding.lutronmqtt.h
 import static org.openhab.binding.lutronmqtt.LutronMQTTBindingConstants.CHANNEL_LIGHT_LEVEL;
 import static org.openhab.binding.lutronmqtt.LutronMQTTBindingConstants.CHANNEL_LIGHT_STATE;
 
-import org.eclipse.smarthome.core.thing.Thing;
+import org.openhab.core.thing.Thing;
 
 /**
  * The {@link LutronMQTTDimmableLightHandler} is responsible for handling commands, which are

          
M src/main/java/org/openhab/binding/lutronmqtt/handler/LutronMQTTHubHandler.java +131 -36
@@ 31,7 31,14 @@ import org.eclipse.smarthome.core.types.
 import org.eclipse.smarthome.io.transport.mqtt.*;
 import org.eclipse.smarthome.io.transport.mqtt.reconnect.PeriodicReconnectStrategy;
 import org.openhab.binding.lutronmqtt.internal.LutronMQTTConfiguration;
+import org.openhab.binding.lutronmqtt.internal.LutronMqttBrokerConnectionConfig;
 import org.openhab.binding.lutronmqtt.model.LutronDevice;
+import org.openhab.core.config.core.Configuration;
+import org.openhab.core.io.transport.mqtt.*;
+import org.openhab.core.io.transport.mqtt.reconnect.PeriodicReconnectStrategy;
+import org.openhab.core.thing.*;
+import org.openhab.core.thing.binding.BaseBridgeHandler;
+import org.openhab.core.types.Command;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 

          
@@ 52,6 59,8 @@ public class LutronMQTTHubHandler extend
 
     private List<LutronDevice> deviceList = Collections.EMPTY_LIST;
 
+    private static final int DEFAULT_TIMEOUT = 10000;
+
     @Nullable
     private LutronMQTTConfiguration config;
     private String token;

          
@@ 66,6 75,57 @@ public class LutronMQTTHubHandler extend
         logger.warn("LutronMQTTHubHandler create.");
     }
 
+    /**
+     * Creates a broker connection based on the configuration of {@link #config}.
+     *
+     * @return Returns a valid MqttBrokerConnection
+     * @throws IllegalArgumentException If the configuration is invalid, this exception is thrown.
+     */
+    protected MqttBrokerConnection createBrokerConnection(LutronMqttBrokerConnectionConfig config)
+            throws IllegalArgumentException {
+        String host = config.host;
+        if (StringUtils.isBlank(host) || host == null) {
+            throw new IllegalArgumentException("Host is empty!");
+        }
+
+        logger.info("Connection config: " + config);
+
+        final MqttBrokerConnection connection = new MqttBrokerConnection(host, config.port, config.secure,
+                config.clientID);
+
+        final String username = config.username;
+        final String password = config.password;
+        if (StringUtils.isNotBlank(username) && password != null) {
+            connection.setCredentials(username, password); // Empty passwords are allowed
+        }
+
+        final String topic = config.lwtTopic;
+        if (topic != null) {
+            final String msg = config.lwtMessage;
+            MqttWillAndTestament will = new MqttWillAndTestament(topic, msg != null ? msg.getBytes() : null,
+                    config.lwtQos, config.lwtRetain);
+            connection.setLastWill(will);
+        }
+
+        connection.setQos(config.qos);
+        if (config.reconnectTime != null) {
+            connection.setReconnectStrategy(new PeriodicReconnectStrategy(config.reconnectTime, 10000));
+        }
+        final Integer keepAlive = config.keepAlive;
+        if (keepAlive != null) {
+            connection.setKeepAliveInterval(keepAlive);
+        }
+        final Integer timeoutInMs = config.timeoutInMs;
+        if (timeoutInMs != null) {
+            connection.setTimeoutExecutor(scheduler, timeoutInMs);
+        } else {
+            connection.setTimeoutExecutor(scheduler, DEFAULT_TIMEOUT);
+        }
+
+        connection.addConnectionObserver(this);
+        return connection;
+    }
+
     @Override
     public void initialize() {
         updateStatus(ThingStatus.UNKNOWN);

          
@@ 74,6 134,16 @@ public class LutronMQTTHubHandler extend
         token = (String) config.get(CONFIG_TOKEN);
 
         final String broker = properties.get(PROPERTY_URL);
+        URI uri = null;
+
+        try {
+            uri = new URI(broker);
+        } catch (URISyntaxException e) {
+            e.printStackTrace();
+            logger.error("Bad broker URL: " + broker, e);
+            updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.CONFIGURATION_ERROR);
+            return;
+        }
 
         String clientId = "openhab-lutron-mqtt-" + (System.currentTimeMillis() / 1000);
 

          
@@ 114,7 184,16 @@ public class LutronMQTTHubHandler extend
 
     @Override
     public void dispose() {
-        logger.info("Disposing of handler " + this);
+        try {
+            if (connection != null) {
+                connection.stop().get(1000, TimeUnit.MILLISECONDS);
+            } else {
+                logger.warn("Trying to dispose handler {} but connection is already null. Most likely this is a bug.",
+                        thing.getUID());
+            }
+        } catch (InterruptedException | ExecutionException | TimeoutException ignore) {
+        }
+        cancelJobs();
         super.dispose();
 
         mqttClient.stop();

          
@@ 122,6 201,7 @@ public class LutronMQTTHubHandler extend
 
         mqttClient = null;
     }
+    // cl.unsubscribe(new String[] { "lutron/status", "lutron/events", "lutron/remote" });
 
     private void cancelJobs() {
         if (allItemsJob != null && !allItemsJob.isCancelled())

          
@@ 130,6 210,17 @@ public class LutronMQTTHubHandler extend
             onlineTimeout.cancel(true);
     }
 
+    private void connectionResult(Boolean res, Throwable th) {
+        if (th != null) {
+            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, th.getMessage());
+            logger.error("Unable to connect to MQTT server.", th);
+        } else if (!res) {
+            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
+            logger.error("Unable to connect to MQTT server.");
+        }
+        ;
+    }
+
     private void setupSubscriptions() {
         try {
             mqttClient.unsubscribeAll().get();

          
@@ 200,7 291,7 @@ public class LutronMQTTHubHandler extend
     }
 
     public Collection<LutronDevice> getDevices() {
-        return ImmutableList.copyOf(deviceList);
+        return List.copyOf(deviceList);
     }
 
     public void registerDeviceStatusListener(DeviceStatusListener lutronDeviceDiscoveryService) {

          
@@ 281,9 372,18 @@ public class LutronMQTTHubHandler extend
         }
     }
 
-    protected void requestUpdateForDevice(int objectId) {
-        byte[] b = ("{\"cmd\":\"RuntimePropertyQuery\", \"args\":{\"Params\":[[" + objectId + ",15,[1]]]}}").getBytes();
-        mqttClient.publish("lutron/commands", b);
+    protected void requestUpdateForDevice(int linkAddress) {
+        byte[] b = ("{\"cmd\":\"RuntimePropertyQuery\", \"args\":{\"Params\":[[" + linkAddress + ",15,[1]]]}}")
+                .getBytes();
+        connection.publish("lutron/commands", b, 0, false).whenComplete(this::logErrorOnFailure);
+    }
+
+    void logErrorOnFailure(Boolean res, Throwable th) {
+        if (th != null) {
+            logger.error("Operation failed");
+        } else if (!res) {
+            logger.error(th.getMessage(), th);
+        }
     }
 
     protected void informDeviceListeners(LutronDevice device) {

          
@@ 323,54 423,49 @@ public class LutronMQTTHubHandler extend
 
     protected void onlineTimeoutOccurred() {
         goOffline(ThingStatusDetail.BRIDGE_OFFLINE, "Too long between status announcements.");
-        try {
-            mqttClient.stop().get();
-            Thread.sleep(5000);
-            initialize();
-        } catch (Exception e) {
-            logger.warn("An error occurred while restarting the service.", e);
-        }
     }
 
-    public void setDesiredState(Map<String, Object> lightState) {
+    public void setDesiredState(int deviceId, Map<String, Object> lightState) {
         byte[] bytes = new byte[0];
         try {
             bytes = (gson.toJson(lightState)).getBytes("UTF-8");
         } catch (UnsupportedEncodingException e) {
-            logger.warn("Error encoding JSON.", e);
+            logger.error("Unable to encode json", e);
+            return;
         }
-        // MqttMessage message = new MqttMessage(bytes);
-        Boolean res = null;
-        try {
-            res = mqttClient.publish("lutron/commands", bytes, 0, false).get();
-            if (res)
-                logger.info("Sent message.");
-            else
-                logger.warn("Failed to send message: " + new String(bytes));
-        } catch (InterruptedException | ExecutionException e) {
-            logger.warn("Error sending message.", e);
+        connection.publish("lutron/commands", bytes, 0, false).whenComplete(this::logErrorOnFailure);
+        logger.info("Sent message.");
+    }
+
+    @Override
+    public void processMessage(String s, byte[] bytes) {
+        String message = new String(bytes);
+        logger.debug("messageArrived: " + s + " " + message);
+        switch (s) {
+            case "lutron/status":
+                handleStatusMessage(message);
+                break;
+            case "lutron/remote":
+                handleRemoteEvent(message);
+                break;
+            case "lutron/events":
+                handleGatewayEvent(message);
+                break;
         }
     }
 
     @Override
     public void connectionStateChanged(MqttConnectionState mqttConnectionState, @Nullable Throwable throwable) {
-        if (mqttConnectionState == MqttConnectionState.CONNECTED) {
-            logger.info("MQTT connection state changed to CONNECTED.");
-            goOnline();
-            logger.info("Online");
+        if (mqttConnectionState == MqttConnectionState.DISCONNECTED) {
+            goOffline(ThingStatusDetail.BRIDGE_OFFLINE, throwable.getMessage());
+            logger.warn("Lost connection to MQTT server. Should retry", throwable);
+        } else if (mqttConnectionState == MqttConnectionState.CONNECTED) {
             scheduler.schedule(new Runnable() {
                 @Override
                 public void run() {
                     setupSubscriptions();
-                    logger.info("Subscribed.");
                 }
-            }, 1, TimeUnit.SECONDS);
-        } else if (mqttConnectionState == MqttConnectionState.CONNECTING) {
-            goOffline(ThingStatusDetail.BRIDGE_OFFLINE, "MQTT Reconnecting");
-        } else if (mqttConnectionState == MqttConnectionState.DISCONNECTED) {
-            goOffline(ThingStatusDetail.BRIDGE_OFFLINE, "MQTT Disconnected");
-            logger.warn("Lost connection to MQTT server.");
-            cancelJobs();
+            }, 2, TimeUnit.SECONDS);
         }
     }
 }

          
M src/main/java/org/openhab/binding/lutronmqtt/handler/LutronMQTTRemoteHandler.java +2 -2
@@ 19,13 19,13 @@ import org.openhab.binding.lutronmqtt.mo
  *
  * @author William Welliver - Initial contribution
  */
-public class LutronMQTTRemoteHandler extends BaseThingHandler implements DeviceStatusListener {
+public class LutronMQTTRemoteHandler extends BaseLutronDeviceHandler {
+    protected Logger log = LoggerFactory.getLogger(getClass());
 
     public LutronMQTTRemoteHandler(Thing thing) {
         super(thing);
     }
 
-    @Override
     public void handleCommand(ChannelUID channelUID, Command command) {
     }
 

          
M src/main/java/org/openhab/binding/lutronmqtt/handler/LutronMQTTVariableFanHandler.java +1 -1
@@ 10,7 10,7 @@ package org.openhab.binding.lutronmqtt.h
 import static org.openhab.binding.lutronmqtt.LutronMQTTBindingConstants.CHANNEL_FAN_SPEED;
 import static org.openhab.binding.lutronmqtt.LutronMQTTBindingConstants.CHANNEL_POWER_SWITCH;
 
-import org.eclipse.smarthome.core.thing.Thing;
+import org.openhab.core.thing.Thing;
 
 /**
  * The {@link LutronMQTTVariableFanHandler} is responsible for handling commands, which are

          
M src/main/java/org/openhab/binding/lutronmqtt/handler/PowerLevelDeviceHandler.java +12 -0
@@ 27,6 27,18 @@ import org.slf4j.LoggerFactory;
 
 import com.google.gson.Gson;
 
+import java.util.Map;
+
+import org.openhab.binding.lutronmqtt.model.LutronDevice;
+import org.openhab.core.library.types.IncreaseDecreaseType;
+import org.openhab.core.library.types.OnOffType;
+import org.openhab.core.library.types.PercentType;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.types.Command;
+
+import com.google.gson.Gson;
+
 /**
  * The {@link PowerLevelDeviceHandler} is responsible for handling commands, which are
  * sent to one of the channels.

          
A => src/main/java/org/openhab/binding/lutronmqtt/internal/LutronMqttBrokerConnectionConfig.java +8 -0
@@ 0,0 1,8 @@ 
+package org.openhab.binding.lutronmqtt.internal;
+
+import org.openhab.core.io.transport.mqtt.MqttBrokerConnectionConfig;
+
+public class LutronMqttBrokerConnectionConfig extends MqttBrokerConnectionConfig {
+    public Integer reconnectTime;
+    public Integer timeoutInMs;
+}

          
A => src/main/resources/OH-INF/binding/binding.xml +10 -0
@@ 0,0 1,10 @@ 
+<?xml version="1.0" encoding="UTF-8"?>
+<binding:binding id="lutronmqtt" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xmlns:binding="http://eclipse.org/smarthome/schemas/binding/v1.0.0"
+	xsi:schemaLocation="http://eclipse.org/smarthome/schemas/binding/v1.0.0 http://eclipse.org/smarthome/schemas/binding-1.0.0.xsd">
+
+	<name>LutronMQTT Binding</name>
+	<description>This is the binding for LutronMQTT.</description>
+	<author>William Welliver</author>
+
+</binding:binding>

          
A => src/main/resources/OH-INF/i18n/lutronmqtt_xx_XX.properties +13 -0
@@ 0,0 1,13 @@ 
+# FIXME: please substitute the xx_XX with a proper locale, ie. de_DE
+# FIXME: please do not add the file to the repo if you add or change no content
+# binding
+binding.lutronmqtt.name = <Your localized Binding name>
+binding.lutronmqtt.description = <Your localized Binding description>
+
+# thing types
+thing-type.lutronmqtt.sample.label = <Your localized Thing label>
+thing-type.lutronmqtt.sample.description = <Your localized Thing description>
+
+# channel types
+channel-type.lutronmqtt.sample-channel.label = <Your localized Channel label>
+channel-type.lutronmqtt.sample-channel.description = <Your localized Channel description>
  No newline at end of file

          
A => src/main/resources/OH-INF/thing/hub.xml +28 -0
@@ 0,0 1,28 @@ 
+<?xml version="1.0" encoding="UTF-8"?>
+<thing:thing-descriptions bindingId="lutronmqtt"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xmlns:thing="http://eclipse.org/smarthome/schemas/thing-description/v1.0.0"
+	xsi:schemaLocation="http://eclipse.org/smarthome/schemas/thing-description/v1.0.0 http://eclipse.org/smarthome/schemas/thing-description-1.0.0.xsd">
+
+	<!-- Wink Hub -->
+	<bridge-type id="hub">
+		<label>Lutron-MQTT Gateway</label>
+		<description>The Lutron-MQTT Gateway represents the network device communicating with the Lutron ClearConnect network.</description>
+
+		<properties>
+			<property name="vendor">Hacienda</property>
+			<property name="uuid"></property>
+			<property name="url"></property>
+		</properties>
+
+		<config-description>
+			<parameter name="token" type="text">
+				<label>Security Token</label>
+				<description>The token authorized by the luton-mqtt infrastructure</description>
+				<required>true</required>
+			</parameter>
+		</config-description>
+
+	</bridge-type>
+
+</thing:thing-descriptions>

          
A => src/main/resources/OH-INF/thing/thing-types.xml +97 -0
@@ 0,0 1,97 @@ 
+<?xml version="1.0" encoding="UTF-8"?>
+<thing:thing-descriptions bindingId="lutronmqtt"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xmlns:thing="http://eclipse.org/smarthome/schemas/thing-description/v1.0.0"
+	xsi:schemaLocation="http://eclipse.org/smarthome/schemas/thing-description/v1.0.0 http://eclipse.org/smarthome/schemas/thing-description-1.0.0.xsd">
+
+	<thing-type id="dimmableLight">
+		<supported-bridge-type-refs>
+			<bridge-type-ref id="hub"/>
+		</supported-bridge-type-refs>
+
+		<label>Dimmable Light</label>
+		<description>Controls dimmable loads</description>
+
+		<channels>
+			<channel id="lightlevel" typeId="lightDimmer"/>
+			<!-- <channel id="lightstate" typeId="lightSwitch"/> -->
+		</channels>
+
+		<properties>
+			<property name="objectId"></property>
+			<property name="integrationId"></property>
+			<property name="name"></property>
+		</properties>
+	</thing-type>
+
+	<thing-type id="variableFan">
+		<supported-bridge-type-refs>
+			<bridge-type-ref id="hub"/>
+		</supported-bridge-type-refs>
+
+		<label>Variable Speed Fan</label>
+		<description>Controls variable speed fans</description>
+
+		<channels>
+			<channel id="fanSpeed" typeId="fanSpeed"/>
+			<!-- <channel id="powerSwitch" typeId="powerSwitch"/> -->
+		</channels>
+
+		<properties>
+			<property name="objectId"></property>
+			<property name="integrationId"></property>
+			<property name="linkAddress"></property>
+			<property name="name"></property>
+		</properties>
+	</thing-type>
+
+	<channel-type id="lightDimmer">
+		<item-type>Dimmer</item-type>
+		<label>Light Level</label>
+		<description>Increase/decrease the light level</description>
+		<category>DimmableLight</category>
+		<state min="0" max="100" pattern="%d %%"/>
+	</channel-type>
+
+	<channel-type id="lightSwitch">
+		<item-type>Switch</item-type>
+		<label>Light State</label>
+		<description>Turn the light on or off</description>
+		<category>DimmableLight</category>
+	</channel-type>
+
+	<channel-type id="fanSpeed">
+		<item-type>Dimmer</item-type>
+		<label>Fan Speed</label>
+		<description>Increase/decrease the speed</description>
+		<category>VariableFan</category>
+		<state min="0" max="100" pattern="%d %%"/>
+	</channel-type>
+
+	<channel-type id="powerSwitch">
+		<item-type>Switch</item-type>
+		<label>Power State</label>
+		<description>Turn the item on or off</description>
+		<category>VariableFan</category>
+	</channel-type>
+
+
+	<!-- Sample Thing Type -->
+	<thing-type id="sample">
+		<label>LutronMQTT Binding Thing</label>
+		<description>Sample thing for LutronMQTT Binding</description>
+
+		<channels>
+			<channel id="channel1" typeId="sample-channel"/>
+		</channels>
+
+		<config-description>
+			<parameter name="config1" type="text" required="true">
+				<label>Sample parameter</label>
+				<description>This is a sample text configuration parameter.</description>
+			</parameter>
+		</config-description>
+
+	</thing-type>
+
+</thing:thing-descriptions>