A => .hgignore +5 -0
@@ 0,0 1,5 @@
+target
+.idea
+.classpath
+.project
+.iml$
A => ESH-INF/binding/binding.xml +11 -0
@@ 0,0 1,11 @@
+<?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 => ESH-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 => ESH-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>
No newline at end of file
A => ESH-INF/thing/thing-types.xml +96 -0
@@ 0,0 1,96 @@
+<?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>
A => META-INF/MANIFEST.MF +33 -0
@@ 0,0 1,33 @@
+Manifest-Version: 1.0
+Bundle-ActivationPolicy: lazy
+Bundle-ClassPath: .
+Bundle-ManifestVersion: 2
+Bundle-Name: LutronMQTT Binding
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
+Bundle-SymbolicName: org.openhab.binding.lutronmqtt;singleton:=true
+Bundle-Vendor: openHAB
+Bundle-Version: 2.3.0.qualifier
+Export-Package:
+ org.openhab.binding.lutronmqtt,
+ org.openhab.binding.lutronmqtt.handler
+Import-Package:
+ org.openhab.binding.lutronmqtt,
+ org.openhab.binding.lutronmqtt.handler,
+ org.eclipse.jdt.annotation;resolution:=optional,
+ javax.jmdns;version="3.5.0",
+ org.eclipse.smarthome.config.core,
+ org.eclipse.smarthome.config.discovery,
+ org.eclipse.smarthome.config.discovery.mdns,
+ org.eclipse.smarthome.core.library.types,
+ org.eclipse.smarthome.core.thing,
+ org.eclipse.smarthome.core.thing.binding,
+ org.eclipse.smarthome.core.thing.binding.builder,
+ org.eclipse.smarthome.core.thing.type,
+ org.eclipse.smarthome.core.types,
+ org.slf4j,
+ com.google.common.collect,
+ org.osgi.framework,
+ org.eclipse.paho.client.mqttv3,
+ org.eclipse.paho.client.mqttv3.persist,
+ com.google.gson
+Service-Component: OSGI-INF/*.xml
A => OSGI-INF/.gitignore +1 -0
@@ 0,0 1,1 @@
+*.xml
No newline at end of file
A => OSGI-INF/org.openhab.binding.lutronmqtt.discovery.LutronMQTTHubDiscoveryParticipant.xml +7 -0
@@ 0,0 1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.2.0" immediate="true" name="org.openhab.binding.lutronmqtt.discovery.hub" configuration-pid="binding.lutronmqtt">
+ <service servicefactory="false">
+ <provide interface="org.eclipse.smarthome.config.discovery.mdns.MDNSDiscoveryParticipant"/>
+ </service>
+ <implementation class="org.openhab.binding.lutronmqtt.discovery.LutronMQTTHubDiscoveryParticipant"/>
+</scr:component>
A => OSGI-INF/org.openhab.binding.lutronmqtt.internal.LutronMQTTHandlerFactory.xml +7 -0
@@ 0,0 1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.2.0" immediate="true" name="lutronmqtt" configuration-pid="binding.lutronmqtt">
+ <service servicefactory="false">
+ <provide interface="org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory"/>
+ </service>
+ <implementation class="org.openhab.binding.lutronmqtt.internal.LutronMQTTHandlerFactory"/>
+</scr:component>
A => README.md +52 -0
@@ 0,0 1,52 @@
+# <bindingName> Binding
+
+_Give some details about what this binding is meant for - a protocol, system, specific device._
+
+_If possible, provide some resources like pictures, a YouTube video, etc. to give an impression of what can be done with this binding. You can place such resources into a `doc` folder next to this README.md._
+
+## Supported Things
+
+_Please describe the different supported things / devices within this section._
+_Which different types are supported, which models were tested etc.?_
+_Note that it is planned to generate some part of this based on the XML files within ```ESH-INF/thing``` of your binding._
+
+## Discovery
+
+_Describe the available auto-discovery features here. Mention for what it works and what needs to be kept in mind when using it._
+
+## Binding Configuration
+
+_If your binding requires or supports general configuration settings, please create a folder ```cfg``` and place the configuration file ```<bindingId>.cfg``` inside it. In this section, you should link to this file and provide some information about the options. The file could e.g. look like:_
+
+```
+# Configuration for the Philips Hue Binding
+#
+# Default secret key for the pairing of the Philips Hue Bridge.
+# It has to be between 10-40 (alphanumeric) characters
+# This may be changed by the user for security reasons.
+secret=EclipseSmartHome
+```
+
+_Note that it is planned to generate some part of this based on the information that is available within ```ESH-INF/binding``` of your binding._
+
+_If your binding does not offer any generic configurations, you can remove this section completely._
+
+## Thing Configuration
+
+_Describe what is needed to manually configure a thing, either through the (Paper) UI or via a thing-file. This should be mainly about its mandatory and optional configuration parameters. A short example entry for a thing file can help!_
+
+_Note that it is planned to generate some part of this based on the XML files within ```ESH-INF/thing``` of your binding._
+
+## Channels
+
+_Here you should provide information about available channel types, what their meaning is and how they can be used._
+
+_Note that it is planned to generate some part of this based on the XML files within ```ESH-INF/thing``` of your binding._
+
+## Full Example
+
+_Provide a full usage example based on textual configuration files (*.things, *.items, *.sitemap)._
+
+## Any custom content here!
+
+_Feel free to add additional sections for whatever you think should also be mentioned about your binding!_
A => about.html +32 -0
@@ 0,0 1,32 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
+ <title>About</title>
+</head>
+<body lang="EN-US">
+<h2>About This Content</h2>
+
+<p>March 30, 2017</p>
+<h3>License</h3>
+
+<p>
+ The openHAB community makes available all content in this plug-in ("Content"). Unless otherwise
+ indicated below, the Content is provided to you under the terms and conditions of the
+ Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available
+ at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>.
+ For purposes of the EPL, "Program" will mean the Content.
+</p>
+
+<p>
+ If you did not receive this Content directly from the openHAB community, the Content is
+ being redistributed by another party ("Redistributor") and different terms and conditions may
+ apply to your use of any object code in the Content. Check the Redistributor's license that was
+ provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise
+ indicated below, the terms and conditions of the EPL still apply to any source code in the Content
+ and such source code may be obtained at <a href="http://www.openhab.org/">openhab.org</a>.
+</p>
+
+</body>
+</html>
A => build.properties +6 -0
@@ 0,0 1,6 @@
+source..=src/main/java/
+output..=target/classes
+bin.includes=META-INF/,\
+ .,\
+ OSGI-INF/,\
+ ESH-INF/
A => pom.xml +166 -0
@@ 0,0 1,166 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+ <dependencies>
+ <dependency>
+ <groupId>org.eclipse.smarthome.config</groupId>
+ <artifactId>org.eclipse.smarthome.config.discovery.mdns</artifactId>
+ <version>0.9.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.paho</groupId>
+ <artifactId>org.eclipse.paho.client.mqttv3</artifactId>
+ <version>1.0.2</version>
+ </dependency>
+ </dependencies>
+
+<parent>
+ <groupId>org.openhab</groupId>
+ <artifactId>pom-tycho</artifactId>
+ <version>2.4.0-SNAPSHOT</version>
+ <relativePath/>
+ </parent>
+
+ <properties>
+ <report.fail.on.error>false</report.fail.on.error>
+ <ohc.version>2.4.0-SNAPSHOT</ohc.version>
+ </properties>
+
+ <artifactId>org.openhab.binding.lutronmqtt</artifactId>
+
+ <name>LutronMQTT Binding</name>
+ <packaging>eclipse-plugin</packaging>
+
+ <build>
+ <testSourceDirectory>src/test</testSourceDirectory>
+
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>2.19.1</version>
+ <executions>
+ <execution>
+ <id>test</id>
+ <phase>test</phase>
+ <configuration>
+ <includes>
+ <include>**/*Test.java</include>
+ </includes>
+ </configuration>
+ <goals>
+ <goal>test</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.5.1</version>
+ <configuration>
+ <source>1.7</source>
+ <target>1.7</target>
+ </configuration>
+ <executions>
+ <execution>
+ <id>compiletests</id>
+ <phase>test-compile</phase>
+ <goals>
+ <goal>testCompile</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+ <pluginRepositories>
+ <pluginRepository>
+ <id>jcenter</id>
+ <name>JCenter Repository</name>
+ <url>https://jcenter.bintray.com/</url>
+ <releases>
+ <enabled>true</enabled>
+ <updatePolicy>never</updatePolicy>
+ </releases>
+ <snapshots>
+ <enabled>false</enabled>
+ </snapshots>
+ </pluginRepository>
+ <pluginRepository>
+ <id>artifactory</id>
+ <name>JFrog Artifactory Repository</name>
+ <url>https://openhab.jfrog.io/openhab/libs-release</url>
+ <releases>
+ <enabled>true</enabled>
+ <updatePolicy>never</updatePolicy>
+ </releases>
+ <snapshots>
+ <enabled>false</enabled>
+ </snapshots>
+ </pluginRepository>
+ </pluginRepositories>
+
+ <repositories>
+
+ <!-- releases -->
+ <repository>
+ <id>jcenter</id>
+ <name>JCenter Repository</name>
+ <url>https://jcenter.bintray.com/</url>
+ <releases>
+ <enabled>true</enabled>
+ <updatePolicy>never</updatePolicy>
+ </releases>
+ <snapshots>
+ <enabled>false</enabled>
+ </snapshots>
+ </repository>
+
+ <repository>
+ <id>openhab-artifactory-release</id>
+ <name>JFrog Artifactory Repository</name>
+ <url>https://openhab.jfrog.io/openhab/libs-release</url>
+ <releases>
+ <enabled>true</enabled>
+ <updatePolicy>never</updatePolicy>
+ </releases>
+ <snapshots>
+ <enabled>false</enabled>
+ </snapshots>
+ </repository>
+
+ <!-- snapshots -->
+ <repository>
+ <id>openhab-artifactory-snapshot</id>
+ <name>JFrog Artifactory Repository</name>
+ <url>https://openhab.jfrog.io/openhab/libs-snapshot</url>
+ <releases>
+ <enabled>false</enabled>
+ </releases>
+ <snapshots>
+ <enabled>true</enabled>
+ <updatePolicy>always</updatePolicy>
+ </snapshots>
+ </repository>
+
+ <!-- SmartHome p2 repository -->
+ <repository>
+ <id>p2-smarthome</id>
+ <url>https://openhab.jfrog.io/openhab/eclipse-smarthome-stable</url>
+ <layout>p2</layout>
+ </repository>
+
+ <!-- openHAB dependencies p2 repository -->
+ <repository>
+ <id>p2-openhab-deps-repo</id>
+ <url>https://dl.bintray.com/openhab/p2/openhab-deps-repo/${ohdr.version}</url>
+ <layout>p2</layout>
+ </repository>
+
+ </repositories>
+
+</project>
A => src/main/java/org/openhab/binding/lutronmqtt/LutronMQTTBindingConstants.java +64 -0
@@ 0,0 1,64 @@
+/**
+ * Copyright (c) 2014,2018 by the respective copyright holders.
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.lutronmqtt;
+
+import com.google.common.collect.ImmutableSet;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.smarthome.core.thing.ThingTypeUID;
+
+import java.util.Set;
+
+/**
+ * The {@link LutronMQTTBindingConstants} class defines common constants, which are
+ * used across the whole binding.
+ *
+ * @author William Welliver - Initial contribution
+ */
+ @NonNullByDefault
+public class LutronMQTTBindingConstants {
+
+ private static final String BINDING_ID = "lutronmqtt";
+
+ public final static ThingTypeUID THING_TYPE_MQTTHUB = new ThingTypeUID(BINDING_ID, "hub");
+ public final static ThingTypeUID THING_TYPE_DIMMABLE_LIGHT = new ThingTypeUID(BINDING_ID, "dimmableLight");
+ public final static ThingTypeUID THING_TYPE_VARIABLE_FAN = new ThingTypeUID(BINDING_ID, "variableFan");
+ public final static ThingTypeUID THING_TYPE_REMOTE = new ThingTypeUID(BINDING_ID, "remote");
+
+ public final static Set<ThingTypeUID> SUPPORTED_HUBS_UIDS = ImmutableSet.of(THING_TYPE_MQTTHUB);
+
+ public final static Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = ImmutableSet.of(THING_TYPE_DIMMABLE_LIGHT,
+ THING_TYPE_VARIABLE_FAN, THING_TYPE_REMOTE);
+
+ // List of all Channel ids
+ public final static String PROPERTY_UUID = "uuid";
+ public final static String PROPERTY_URL = "url";
+
+ 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 CONFIG_TOKEN = "token";
+
+ // List of all Channel ids
+ public final static String CHANNEL_LIGHT_LEVEL = "lightlevel";
+ public final static String CHANNEL_LIGHT_STATE = "lightstate";
+
+ public final static String CHANNEL_LIGHT_COLORTEMPERATURE = "colorTemperature";
+ public final static String CHANNEL_LIGHT_COLOR = "color";
+
+ public final static String CHANNEL_FAN_SPEED = "fanSpeed";
+ public final static String CHANNEL_POWER_SWITCH = "powerSwitch";
+
+ public final static int LUTRON_PROPERTY_LEVEL = 1;
+}
A => src/main/java/org/openhab/binding/lutronmqtt/discovery/LutronMQTTDeviceDiscoveryService.java +143 -0
@@ 0,0 1,143 @@
+package org.openhab.binding.lutronmqtt.discovery;
+
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.smarthome.config.discovery.AbstractDiscoveryService;
+import org.eclipse.smarthome.config.discovery.DiscoveryResult;
+import org.eclipse.smarthome.config.discovery.DiscoveryResultBuilder;
+import org.eclipse.smarthome.core.thing.ThingTypeUID;
+import org.eclipse.smarthome.core.thing.ThingUID;
+import org.openhab.binding.lutronmqtt.LutronMQTTBindingConstants;
+import org.openhab.binding.lutronmqtt.handler.DeviceStatusListener;
+import org.openhab.binding.lutronmqtt.handler.LutronMQTTHubHandler;
+import org.openhab.binding.lutronmqtt.model.LutronDevice;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.openhab.binding.lutronmqtt.internal.LutronMQTTHandlerFactory.SUPPORTED_THING_TYPES_UIDS;
+
+public class LutronMQTTDeviceDiscoveryService extends AbstractDiscoveryService implements DeviceStatusListener {
+ private final static int SEARCH_TIME = 60;
+ private final Logger logger = LoggerFactory.getLogger(LutronMQTTDeviceDiscoveryService.class);
+
+ private LutronMQTTHubHandler hubHandler;
+
+ public LutronMQTTDeviceDiscoveryService(LutronMQTTHubHandler hubHandler) {
+ super(SEARCH_TIME);
+ this.hubHandler = hubHandler;
+ }
+
+ public void activate() {
+ logger.warn("activate");
+ hubHandler.registerDeviceStatusListener(this);
+ startScan();
+ }
+
+ @Override
+ public void deactivate() {
+ removeOlderResults(new Date().getTime());
+ hubHandler.unregisterDeviceStatusListener(this);
+ }
+
+ @Override
+ public Set<ThingTypeUID> getSupportedThingTypes() {
+ return SUPPORTED_THING_TYPES_UIDS;
+ }
+
+ @Override
+ public void startScan() {
+ scheduler.schedule(new Runnable() {
+ @Override
+ public void run() {
+ doScan();
+ }
+ }, 5, TimeUnit.SECONDS);
+ }
+
+ protected void doScan() {
+ logger.warn("starting scan");
+ Collection<LutronDevice> devices = hubHandler.getDevices();
+
+ for (LutronDevice d : devices) {
+ onDeviceFound(d);
+ }
+ }
+
+ @Override
+ public void onDeviceFound(LutronDevice d) {
+ ThingTypeUID thingTypeUID = getThingTypeUID(d);
+ if (thingTypeUID == null) {
+ return;
+ }
+
+ ThingUID thingUID = getThingUID(d);
+
+ if (thingUID != null) {
+ if (hubHandler.getThingByUID(thingUID) != null) {
+ logger.debug("ignoring already registered device of name '{}' with id {}", d.getName(), d.getId());
+ }
+ ThingUID bridgeUID = hubHandler.getThing().getUID();
+ Map<String, Object> properties = new HashMap<>(1);
+ properties.put(LutronMQTTBindingConstants.PROPERTY_OBJECT_ID, "" + d.getId());
+ properties.put(LutronMQTTBindingConstants.PROPERTY_INTEGRATION_ID, "" + d.getIntegrationId());
+ properties.put(LutronMQTTBindingConstants.PROPERTY_LINK_ADDRESS, "" + d.getLinkAddress());
+ properties.put(LutronMQTTBindingConstants.PROPERTY_OBJECT_NAME, d.getName());
+
+ logger.warn("discovery result " + d.getName() + " " + d.getIntegrationId());
+
+ DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withThingType(thingTypeUID)
+ .withProperties(properties).withBridge(bridgeUID).withLabel(d.getName()).build();
+
+ thingDiscovered(discoveryResult);
+ } else {
+ logger.debug("discovered unsupported device of name '{}' with id {}", d.getName(), d.getId());
+ }
+
+ }
+
+ @Override
+ protected synchronized void stopScan() {
+ super.stopScan();
+ removeOlderResults(getTimestampOfLastScan());
+ }
+
+ private ThingUID getThingUID(LutronDevice device) {
+ ThingUID bridgeUID = hubHandler.getThing().getUID();
+ ThingTypeUID thingTypeUID = getThingTypeUID(device);
+
+ if (thingTypeUID != null && getSupportedThingTypes().contains(thingTypeUID)) {
+ return new ThingUID(thingTypeUID, bridgeUID, "" + device.getId());
+ } else {
+ return null;
+ }
+ }
+
+ private ThingTypeUID getThingTypeUID(LutronDevice device) {
+ ThingTypeUID thingTypeId = null;
+ String name = device.getName();
+ if (name.endsWith("Remote")) {
+ thingTypeId = LutronMQTTBindingConstants.THING_TYPE_REMOTE;
+ } else if (name.contains("Fan")) {
+ thingTypeId = LutronMQTTBindingConstants.THING_TYPE_VARIABLE_FAN;
+ } else {
+ thingTypeId = LutronMQTTBindingConstants.THING_TYPE_DIMMABLE_LIGHT;
+ }
+ return thingTypeId;
+ }
+
+ @Override
+ public void onDeviceRemoved(LutronDevice d) {
+ // TODO Remove devices
+
+ }
+
+ @Override
+ public void onDeviceStateChanged(LutronDevice light) {
+ // Do nothing
+ }
+}
A => src/main/java/org/openhab/binding/lutronmqtt/discovery/LutronMQTTHubDiscoveryParticipant.java +115 -0
@@ 0,0 1,115 @@
+package org.openhab.binding.lutronmqtt.discovery;
+
+import org.eclipse.smarthome.config.discovery.DiscoveryResult;
+import org.eclipse.smarthome.config.discovery.DiscoveryResultBuilder;
+import org.eclipse.smarthome.config.discovery.DiscoveryServiceCallback;
+import org.eclipse.smarthome.config.discovery.ExtendedDiscoveryService;
+import org.eclipse.smarthome.config.discovery.mdns.MDNSDiscoveryParticipant;
+import org.eclipse.smarthome.core.thing.Thing;
+import org.eclipse.smarthome.core.thing.ThingTypeUID;
+import org.eclipse.smarthome.core.thing.ThingUID;
+import org.openhab.binding.lutronmqtt.LutronMQTTBindingConstants;
+import org.osgi.service.component.annotations.Component;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jmdns.ServiceInfo;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.util.*;
+
+@Component(service = MDNSDiscoveryParticipant.class, immediate = true, configurationPid = "binding.lutronmqtt", name = "org.openhab.binding.lutronmqtt.discovery.hub")
+public class LutronMQTTHubDiscoveryParticipant implements MDNSDiscoveryParticipant, ExtendedDiscoveryService {
+ private Logger logger = LoggerFactory.getLogger(LutronMQTTHubDiscoveryParticipant.class);
+ private DiscoveryServiceCallback discoveryServiceCallback;
+
+ @Override
+ public Set<ThingTypeUID> getSupportedThingTypeUIDs() {
+ return Collections.singleton(LutronMQTTBindingConstants.THING_TYPE_MQTTHUB);
+ }
+
+ @Override
+ public String getServiceType() {
+ return "_lutron_mqtt._tcp.local.";
+ }
+
+ @Override
+ public DiscoveryResult createResult(ServiceInfo service) {
+logger.warn("Discovery result: " + service.toString());
+ ThingUID uid = getThingUID(service);
+
+ logger.warn("text: " + service.getTextString());
+ logger.warn("server: " + service.getServer());
+ logger.warn("name: " + service.getName());
+ logger.warn("qname: " + service.getQualifiedName());
+ logger.warn("nice: " + service.getNiceTextString());
+ Enumeration<String> pn = service.getPropertyNames();
+ while(pn.hasMoreElements())
+ logger.warn("prop: " + pn.nextElement());
+
+ logger.debug("Got discovered device.");
+ if (true /*getDiscoveryServiceCallback() != null*/) {
+ /* logger.debug("Looking to see if this thing exists already.");
+ Thing thing = getDiscoveryServiceCallback().getExistingThing(uid);
+ if (thing != null) {
+ logger.debug("Already have thing with ID=<" + uid + ">");
+ return null;
+ } else {
+ logger.debug("Nope. This should trigger a new inbox entry.");
+ }
+ } else {
+ logger.warn("DiscoveryServiceCallback not set. This shouldn't happen!");
+ return null;
+ }
+
+*/
+ if (uid != null) {
+ Map<String, Object> properties = new HashMap<>(2);
+ String u = service.getPropertyString("uuid");
+ String s = service.getServer();
+
+ if(u == null || s == null || u.isEmpty() || s.isEmpty()) // incomplete discovery result
+ {
+ logger.warn("Found lutron-mqtt service but missing data. Try restarting device.");
+ return null;
+ }
+
+ properties.put(LutronMQTTBindingConstants.PROPERTY_URL, "tcp://" + s +
+ ":" + service.getPort());
+ properties.put(LutronMQTTBindingConstants.PROPERTY_UUID, u);
+
+ return DiscoveryResultBuilder.create(uid).withProperties(properties)
+ .withRepresentationProperty(uid.getId()).withLabel(service.getName() + " Lutron-MQTT Gateway").build();
+ }
+ }
+ return null;
+
+ }
+
+ @Override
+ public ThingUID getThingUID(ServiceInfo service) {
+
+ if (service != null) {
+ if (service.getType() != null) {
+ if (service.getType().equals(getServiceType())) {
+ logger.trace("Discovered a Lutron-MQTT gateway thing with name '{}'", service.getName());
+ return new ThingUID(LutronMQTTBindingConstants.THING_TYPE_MQTTHUB, service.getName().replace(" ", "_"));
+ }
+ }
+ }
+
+ 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/DeviceStatusListener.java +13 -0
@@ 0,0 1,13 @@
+package org.openhab.binding.lutronmqtt.handler;
+
+import org.openhab.binding.lutronmqtt.model.LutronDevice;
+
+public interface DeviceStatusListener {
+
+ public void onDeviceFound(LutronDevice d);
+
+ public void onDeviceRemoved(LutronDevice d);
+
+ public void onDeviceStateChanged(LutronDevice light);
+
+}
A => src/main/java/org/openhab/binding/lutronmqtt/handler/LightStateConverter.java +156 -0
@@ 0,0 1,156 @@
+package org.openhab.binding.lutronmqtt.handler;
+
+import org.eclipse.smarthome.core.library.types.IncreaseDecreaseType;
+import org.eclipse.smarthome.core.library.types.OnOffType;
+import org.eclipse.smarthome.core.library.types.PercentType;
+import org.openhab.binding.lutronmqtt.model.LutronDevice;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.openhab.binding.lutronmqtt.LutronMQTTBindingConstants.LUTRON_PROPERTY_LEVEL;
+
+/**
+ * The {@link LightStateConverter} is responsible for mapping Eclipse SmartHome
+ * types to jue types and vice versa.
+ *
+ * @author Dennis Nobel - Initial contribution
+ * @author Oliver Libutzki - Adjustments
+ * @author Kai Kreuzer - made code static
+ * @author Andre Fuechsel - added method for brightness
+ * @author Yordan Zhelev - added method for alert
+ * @author Denis Dudnik - switched to internally integrated source of Jue library, minor code cleanup
+ *
+ */
+public class LightStateConverter {
+
+ private static final double BRIGHTNESS_FACTOR = 1.0;
+
+ private static final int DIM_STEPSIZE = 30;
+
+ protected static Logger log = LoggerFactory.getLogger(LightStateConverter.class);
+
+
+ /**
+ * Transforms the given {@link OnOffType} into a light state containing the
+ * 'on' value.
+ *
+ * @param onOffType
+ * on or off state
+ * @return light state containing the 'on' value
+ */
+ public static int toOnOffLightState(OnOffType onOffType) {
+ int f = 0;
+ if(onOffType == OnOffType.ON) f = 65535;
+ return f;
+ }
+
+ public static int toPercentLightState(PercentType percentType) {
+ int f = 0;
+ if(PercentType.ZERO.equals(percentType))
+ f = 0;
+ else if(PercentType.HUNDRED.equals(percentType))
+ f = 65535;
+ else {
+ f = (int) Math.round(percentType.floatValue() * 65535 / 100);
+ }
+ return f;
+ }
+
+ public static Map<String,Object> toLightState(OnOffType onOffType, LutronDevice device) {
+ int level = toOnOffLightState(onOffType);
+ Map<String,Object> m = makeGoToLevelCommand(level, device);
+ return m;
+ }
+
+ public static Map<String,Object> makeGoToLevelCommand(int level, LutronDevice device) {
+ Map<String,Object> a = new HashMap<>();
+ log.warn("device: " + device);
+ a.put("ObjectId", device.getLinkAddress());
+ a.put("ObjectType", 15);
+ a.put("Fade", 7);
+ a.put("Delay", 0);
+ a.put("Level", level);
+ Map<String,Object> m = new HashMap<>();
+ m.put("cmd", "GoToLevel");
+ m.put("args", a);
+
+ return m;
+ }
+
+ /**
+ * Transforms the given {@link PercentType} into a light state containing
+ * the brightness and the 'on' value represented by {@link PercentType}.
+ *
+ * @param percentType
+ * brightness represented as {@link PercentType}
+ * @return light state containing the brightness and the 'on' value
+ */
+ public static Map<String,Object> toLightState(PercentType percentType, LutronDevice device) {
+ int level = toPercentLightState(percentType);
+
+ Map<String,Object> m = makeGoToLevelCommand(level, device);
+
+ return m;
+ }
+
+ public static Map<String,Object> toLightState(IncreaseDecreaseType increaseDecreaseType, LutronDevice device) {
+ int level = toAdjustedBrightness(increaseDecreaseType, device.getProperty(LUTRON_PROPERTY_LEVEL));
+
+ Map<String,Object> m = makeGoToLevelCommand(level, device);
+
+ return m;
+ }
+
+ /**
+ * Adjusts the given brightness using the {@link IncreaseDecreaseType} and
+ * returns the updated value.
+ *
+ * @param command
+ * The {@link IncreaseDecreaseType} to be used
+ * @param currentBrightness
+ * The current brightness
+ * @return The adjusted brightness value
+ */
+ public static int toAdjustedBrightness(IncreaseDecreaseType command, int currentBrightness) {
+ int newBrightness;
+ if (command == IncreaseDecreaseType.DECREASE) {
+ newBrightness = Math.max(currentBrightness - DIM_STEPSIZE, 0);
+ } else {
+ newBrightness = Math.min(currentBrightness + DIM_STEPSIZE, 65535);
+ }
+ return newBrightness;
+ }
+
+
+ /**
+ * Transforms Luton device state into {@link PercentType} representing
+ * the brightness.
+ *
+ * @param device
+ * lutron device
+ * @return percent type representing the brightness
+ */
+ public static PercentType toBrightnessPercentType(LutronDevice device) {
+ int percent = (int) Math.round(device.getProperty(LUTRON_PROPERTY_LEVEL) / (65535/100));
+ if (log.isTraceEnabled()) {
+ log.trace("Converting " + device.getProperty(LUTRON_PROPERTY_LEVEL) + " -> " + percent + " -> "
+ + new PercentType(restrictToBounds(percent)));
+ }
+ return new PercentType(restrictToBounds(percent));
+ }
+
+
+ private static int restrictToBounds(int percentValue) {
+ if (percentValue < 0) {
+ return 0;
+ } else if (percentValue > 100) {
+ return 100;
+ }
+ return percentValue;
+ }
+
+}
A => src/main/java/org/openhab/binding/lutronmqtt/handler/LutronMQTTDimmableLightHandler.java +27 -0
@@ 0,0 1,27 @@
+/**
+ * Copyright (c) 2014-2016 by the respective copyright holders.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.openhab.binding.lutronmqtt.handler;
+
+import org.eclipse.smarthome.core.thing.Thing;
+
+import static org.openhab.binding.lutronmqtt.LutronMQTTBindingConstants.CHANNEL_LIGHT_LEVEL;
+import static org.openhab.binding.lutronmqtt.LutronMQTTBindingConstants.CHANNEL_LIGHT_STATE;
+
+/**
+ * The {@link LutronMQTTDimmableLightHandler} is responsible for handling commands, which are
+ * sent to one of the channels.
+ *
+ * @author William Welliver - Initial contribution
+ */
+public class LutronMQTTDimmableLightHandler extends PowerLevelDeviceHandler {
+
+ public LutronMQTTDimmableLightHandler(Thing thing) {
+ super(thing, CHANNEL_LIGHT_LEVEL, CHANNEL_LIGHT_STATE);
+ }
+
+}
A => src/main/java/org/openhab/binding/lutronmqtt/handler/LutronMQTTHubHandler.java +364 -0
@@ 0,0 1,364 @@
+/**
+ * Copyright (c) 2014,2018 by the respective copyright holders.
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.lutronmqtt.handler;
+
+import com.google.common.collect.ImmutableList;
+import com.google.gson.Gson;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.paho.client.mqttv3.*;
+import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
+import org.eclipse.smarthome.config.core.Configuration;
+import org.eclipse.smarthome.core.thing.*;
+import org.eclipse.smarthome.core.thing.binding.BaseBridgeHandler;
+import org.eclipse.smarthome.core.types.Command;
+import org.openhab.binding.lutronmqtt.internal.LutronMQTTConfiguration;
+import org.openhab.binding.lutronmqtt.model.LutronDevice;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.UnsupportedEncodingException;
+import java.util.*;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import static org.openhab.binding.lutronmqtt.LutronMQTTBindingConstants.CONFIG_TOKEN;
+import static org.openhab.binding.lutronmqtt.LutronMQTTBindingConstants.PROPERTY_URL;
+import static org.openhab.binding.lutronmqtt.LutronMQTTBindingConstants.PROPERTY_UUID;
+
+/**
+ * The {@link LutronMQTTHubHandler} is responsible for handling commands, which are
+ * sent to one of the channels.
+ *
+ * @author William Welliver - Initial contribution
+ */
+public class LutronMQTTHubHandler extends BaseBridgeHandler implements MqttCallback {
+
+ private final Logger logger = LoggerFactory.getLogger(LutronMQTTHubHandler.class);
+
+ private Collection<DeviceStatusListener> deviceStatusListeners = new HashSet<>();
+
+ private List<LutronDevice> deviceList = Collections.EMPTY_LIST;
+
+ @Nullable
+ private LutronMQTTConfiguration config;
+ private String token;
+ private MqttClient mqttClient;
+ private Gson gson = new Gson();
+ private Map<Integer, LutronDevice> devicesByLinkAddress = new HashMap<>();
+ private ScheduledFuture<?> reconnectJob;
+ private ScheduledFuture<?> onlineTimeout;
+ private ScheduledFuture<?> allItemsJob;
+
+ public LutronMQTTHubHandler(Bridge thing) {
+ super(thing);
+ }
+
+ @Override
+ public void initialize() {
+ updateStatus(ThingStatus.UNKNOWN);
+ Configuration config = getThing().getConfiguration();
+ Map<String, String> properties = getThing().getProperties();
+ token = (String) config.get(CONFIG_TOKEN);
+
+ final String uuid = properties.get(PROPERTY_UUID);
+ final String broker = properties.get(PROPERTY_URL);
+
+ updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE);
+
+ MemoryPersistence persistence = new MemoryPersistence();
+ String clientId = "openhab-lutron-mqtt-" + (System.currentTimeMillis()/1000);
+
+ try {
+ if(mqttClient == null || !mqttClient.isConnected()) {
+ logger.info("Attempting to connect to MQTT Broker.");
+ MqttClient client = new MqttClient(broker, clientId, persistence);
+ MqttConnectOptions connOpts = new MqttConnectOptions();
+// connOpts.setCleanSession(true);
+// connOpts.setKeepAliveInterval(30);
+// connOpts.setConnectionTimeout(90);
+ client.connect(connOpts);
+ mqttClient = client;
+ setupSubscriptions();
+ }
+ } catch (MqttException e) {
+ logger.error("Unable to connect to MQTT broker at " + broker, e);
+ goOffline(ThingStatusDetail.COMMUNICATION_ERROR, "Unable to connect to MQTT broker at broker");
+ }
+ }
+
+
+
+ @Override
+ public void dispose() {
+ logger.info("Disposing of handler " + this);
+ super.dispose();
+ try {
+ MqttClient cl = mqttClient;
+ cl.setCallback(null);
+ cl.unsubscribe(new String[] {"lutron/status", "lutron/events", "lutron/remote"});
+ cl.disconnectForcibly(3000);
+ cl.close();
+ mqttClient = null;
+ cancelJobs();
+
+ } catch (MqttException e) {
+ logger.warn("Error while disconnecting", e);
+ }
+ }
+
+ private void cancelJobs() {
+ if(reconnectJob != null && !reconnectJob.isCancelled())
+ reconnectJob.cancel(true);
+ if(allItemsJob != null && !allItemsJob.isCancelled())
+ allItemsJob.cancel(true);
+ if(onlineTimeout != null && !allItemsJob.isCancelled())
+ onlineTimeout.cancel(true);
+ }
+
+ private void setupSubscriptions() {
+ mqttClient.setCallback(this);
+ try {
+ mqttClient.subscribe(new String[] {"lutron/status", "lutron/events", "lutron/remote"});
+ } catch (MqttException e) {
+ logger.error("subscribe failed.", e);
+ goOffline(ThingStatusDetail.COMMUNICATION_ERROR, "Unable to subscribe to events");
+ }
+
+ cancelJobs();
+
+ allItemsJob = scheduler.schedule(new Runnable() {
+ @Override
+ public void run() {
+ requestAllItems();
+ }
+ }, 5, TimeUnit.SECONDS);
+ }
+
+ private void requestAllItems() {
+ try {
+ byte[] b = ("{\"cmd\": \"GetDevices\", \"args\": {}}").getBytes("UTF-8");
+ mqttClient.publish("lutron/commands", (b), 0, false);
+ } catch (MqttException e) {
+ logger.warn("Error Sending message.", e);
+ } catch (UnsupportedEncodingException e) {
+ logger.warn("Error encoding message.", e);
+ }
+
+ allItemsJob = scheduler.schedule(new Runnable() {
+ @Override
+ public void run() {
+ requestAllItems();
+ }
+ }, 5, TimeUnit.MINUTES);
+ }
+
+ @Override
+ public void handleCommand(ChannelUID channelUID, Command command) {
+
+ }
+
+ protected void goOnline() {
+ updateStatus(ThingStatus.ONLINE);
+ }
+
+ protected void goOffline(ThingStatusDetail detail, String reason) {
+ updateStatus(ThingStatus.OFFLINE, detail, reason);
+ if(onlineTimeout != null) onlineTimeout.cancel(true);
+ if(allItemsJob != null) onlineTimeout.cancel(true);
+ }
+
+ public Collection<LutronDevice> getDevices() {
+ return ImmutableList.copyOf(deviceList);
+ }
+
+ public void registerDeviceStatusListener(DeviceStatusListener lutronDeviceDiscoveryService) {
+ deviceStatusListeners.add(lutronDeviceDiscoveryService);
+ }
+
+ public void unregisterDeviceStatusListener(DeviceStatusListener lutronDeviceDiscoveryService) {
+ deviceStatusListeners.remove(lutronDeviceDiscoveryService);
+ }
+
+ @Override
+ public void connectionLost(Throwable throwable) {
+ goOffline(ThingStatusDetail.BRIDGE_OFFLINE, throwable.getMessage());
+ logger.warn("Lost connection to MQTT server. Will rety in 20 seconds.", throwable);
+ if(mqttClient != null) scheduleReconnect(20);
+ }
+
+ private void scheduleReconnect(int seconds) {
+ if(reconnectJob == null || reconnectJob.isDone()) {
+ reconnectJob = scheduler.schedule(new Runnable() {
+ @Override
+ public void run() {
+ reconnect();
+ }
+ }, seconds, TimeUnit.SECONDS);
+ }
+ }
+
+ private void reconnect() {
+ if(mqttClient == null) {
+ logger.info("Skipping reconnect because MQTT client is null.");
+ return;
+ }
+ logger.info("Attempting to reconnect to MQTT server");
+ initialize();
+ ThingStatusInfo info = getThing().getStatusInfo();
+ if(info.getStatus() != ThingStatus.ONLINE &&
+ (info.getStatusDetail() == ThingStatusDetail.BRIDGE_OFFLINE ||
+ info.getStatusDetail() == ThingStatusDetail.COMMUNICATION_ERROR
+ )) // we're not online but have a retryable status
+ scheduleReconnect(20);
+ else {
+ reconnectJob = null;
+ }
+ }
+
+ @Override
+ public void messageArrived(String s, MqttMessage mqttMessage) throws Exception {
+ logger.debug("messageArrived: " + s + " " + mqttMessage.toString());
+ switch(s) {
+ case "lutron/status":
+ handleStatusMessage(mqttMessage);
+ break;
+ case "lutron/remote":
+ handleRemoteEvent(mqttMessage);
+ break;
+ case "lutron/events":
+ handleGatewayEvent(mqttMessage);
+ break;
+ }
+ }
+
+ private void handleGatewayEvent(MqttMessage mqttMessage) {
+ //logger.info("gateway event: " + new String(mqttMessage.getPayload()));
+ Map <String,Object> msg = gson.fromJson(new String(mqttMessage.getPayload()), HashMap.class);
+
+ if(msg.containsKey("cmd")) {
+ String key = (String)msg.get("cmd");
+ Object arg = msg.get("args");
+ switch(key) {
+ case "ListDevices":
+ int i = 0;
+ logger.warn("ListDevices Response: " + arg);
+ List<LutronDevice> devices = new ArrayList<>();
+ for(Map<String,Object> objectMap : (List<Map<String,Object>>)arg) {
+ getThing().getThings();
+ final LutronDevice device = new LutronDevice(objectMap);
+ logger.info("Device: " + device);
+ devices.add(device);
+ if(devicesByLinkAddress.containsKey(device.getLinkAddress())) {
+ // TODO copy properties to new device
+ }
+ devicesByLinkAddress.put(device.getLinkAddress(), device);
+ scheduler.schedule(new Runnable() {
+ @Override
+ public void run() {
+ requestUpdateForDevice(device.getLinkAddress());
+ }
+ }, i++%3, TimeUnit.SECONDS);
+ }
+
+ this.deviceList = devices;
+
+ break;
+ case "RuntimePropertyUpdate":
+ int linkAddress = ((Number)(((Map)arg).get("ObjectId"))).intValue();
+ LutronDevice device = getDeviceByLinkAddress(linkAddress);
+ if(device == null) {
+ logger.warn("Unable to find LutronDevice with linkAddress=" + linkAddress);
+ }
+ List<List<Object>> l = (List<List<Object>>)(((Map)arg).get("Properties"));
+ for(List property : l) {
+ int pnum = ((Number)(property.get(0))).intValue();
+ int pval = ((Number)(property.get(1))).intValue();
+ device.putProperty(pnum, pval);
+ }
+ informDeviceListeners(device);
+
+
+ break;
+ default:
+ logger.warn("Received unknown message type " + key + " with args " + arg);
+ break;
+ }
+ }
+ }
+
+ protected void requestUpdateForDevice(int linkAddress) {
+ try {
+ byte[] b = ("{\"cmd\":\"RuntimePropertyQuery\", \"args\":{\"Params\":[[" + linkAddress + ",15,[1]]]}}").getBytes();
+ MqttMessage message = new MqttMessage(b);
+ mqttClient.publish("lutron/commands", message);
+ } catch (MqttException e) {
+ logger.error("Error publishing message", e);
+ }
+ }
+
+ protected void informDeviceListeners(LutronDevice device) {
+ logger.info("informDeviceListeners " + device);
+ for (DeviceStatusListener s : deviceStatusListeners) {
+ s.onDeviceStateChanged(device);
+ }
+ }
+
+ public LutronDevice getDeviceByLinkAddress(int linkAddress) {
+ logger.warn("looking for device with link address =" + linkAddress);
+ return devicesByLinkAddress.get(linkAddress);
+ }
+
+ private void handleRemoteEvent(MqttMessage mqttMessage) {
+ logger.info("remote event: " + new String(mqttMessage.getPayload()));
+
+ Map <String,Object> msg = gson.fromJson(new String(mqttMessage.getPayload()), HashMap.class);
+ // {"serial" : "C726CA", "action": "down", "button": "select"}
+
+ }
+
+ private void handleStatusMessage(MqttMessage mqttMessage) {
+ logger.info("status message: " + new String(mqttMessage.getPayload()));
+ Map <String,Object> msg = gson.fromJson(new String(mqttMessage.getPayload()), HashMap.class);
+ if(msg.containsKey("state") && "running".equals(msg.get("state"))) {
+ if(onlineTimeout != null)
+ onlineTimeout.cancel(true);
+ goOnline();
+ onlineTimeout = scheduler.schedule(new Runnable() {
+ @Override
+ public void run() {
+ onlineTimeoutOccurred();
+ }
+ },120, TimeUnit.SECONDS);
+ }
+ }
+
+ protected void onlineTimeoutOccurred() {
+ goOffline(ThingStatusDetail.BRIDGE_OFFLINE, "Too long between status announcements.");
+ }
+
+ @Override
+ public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
+ // We really don't care too much about this status.
+ }
+
+ public void setDesiredState(int deviceId, Map<String, Object> lightState) {
+ try {
+ byte[] bytes = (gson.toJson(lightState)).getBytes("UTF-8");
+ // MqttMessage message = new MqttMessage(bytes);
+ mqttClient.publish("lutron/commands", bytes, 0, false);
+ logger.info("Sent message.");
+ } catch (MqttException|UnsupportedEncodingException e) {
+ e.printStackTrace();
+ }
+ }
+}
A => src/main/java/org/openhab/binding/lutronmqtt/handler/LutronMQTTRemoteHandler.java +50 -0
@@ 0,0 1,50 @@
+/**
+ * Copyright (c) 2014-2016 by the respective copyright holders.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.openhab.binding.lutronmqtt.handler;
+
+import org.eclipse.smarthome.core.thing.ChannelUID;
+import org.eclipse.smarthome.core.thing.Thing;
+import org.eclipse.smarthome.core.thing.binding.BaseThingHandler;
+import org.eclipse.smarthome.core.types.Command;
+import org.openhab.binding.lutronmqtt.model.LutronDevice;
+
+import static org.openhab.binding.lutronmqtt.LutronMQTTBindingConstants.CHANNEL_LIGHT_LEVEL;
+import static org.openhab.binding.lutronmqtt.LutronMQTTBindingConstants.CHANNEL_LIGHT_STATE;
+
+/**
+ * The {@link LutronMQTTRemoteHandler} is responsible for handling commands, which are
+ * sent to one of the channels.
+ *
+ * @author William Welliver - Initial contribution
+ */
+public class LutronMQTTRemoteHandler extends BaseThingHandler implements DeviceStatusListener {
+
+ public LutronMQTTRemoteHandler(Thing thing) {
+ super(thing);
+ }
+
+ @Override
+ public void handleCommand(ChannelUID channelUID, Command command) {
+
+ }
+
+ @Override
+ public void onDeviceFound(LutronDevice d) {
+
+ }
+
+ @Override
+ public void onDeviceRemoved(LutronDevice d) {
+
+ }
+
+ @Override
+ public void onDeviceStateChanged(LutronDevice light) {
+
+ }
+}
A => src/main/java/org/openhab/binding/lutronmqtt/handler/LutronMQTTVariableFanHandler.java +26 -0
@@ 0,0 1,26 @@
+/**
+ * Copyright (c) 2014-2016 by the respective copyright holders.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.openhab.binding.lutronmqtt.handler;
+
+import org.eclipse.smarthome.core.thing.Thing;
+
+import static org.openhab.binding.lutronmqtt.LutronMQTTBindingConstants.CHANNEL_FAN_SPEED;
+import static org.openhab.binding.lutronmqtt.LutronMQTTBindingConstants.CHANNEL_POWER_SWITCH;
+
+/**
+ * The {@link LutronMQTTVariableFanHandler} is responsible for handling commands, which are
+ * sent to one of the channels.
+ *
+ * @author William Welliver - Initial contribution
+ */
+public class LutronMQTTVariableFanHandler extends PowerLevelDeviceHandler {
+
+ public LutronMQTTVariableFanHandler(Thing thing) {
+ super(thing, CHANNEL_FAN_SPEED, CHANNEL_POWER_SWITCH);
+ }
+}
A => src/main/java/org/openhab/binding/lutronmqtt/handler/PowerLevelDeviceHandler.java +264 -0
@@ 0,0 1,264 @@
+/**
+ * Copyright (c) 2014-2016 by the respective copyright holders.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.openhab.binding.lutronmqtt.handler;
+
+import com.google.gson.Gson;
+import org.eclipse.smarthome.core.library.types.IncreaseDecreaseType;
+import org.eclipse.smarthome.core.library.types.OnOffType;
+import org.eclipse.smarthome.core.library.types.PercentType;
+import org.eclipse.smarthome.core.thing.Bridge;
+import org.eclipse.smarthome.core.thing.ChannelUID;
+import org.eclipse.smarthome.core.thing.Thing;
+import org.eclipse.smarthome.core.thing.ThingStatus;
+import org.eclipse.smarthome.core.thing.ThingStatusDetail;
+import org.eclipse.smarthome.core.thing.ThingStatusInfo;
+import org.eclipse.smarthome.core.thing.binding.BaseThingHandler;
+import org.eclipse.smarthome.core.thing.binding.ThingHandler;
+import org.eclipse.smarthome.core.types.Command;
+import org.openhab.binding.lutronmqtt.model.LutronDevice;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Map;
+
+import static org.openhab.binding.lutronmqtt.LutronMQTTBindingConstants.*;
+
+/**
+ * The {@link PowerLevelDeviceHandler} is responsible for handling commands, which are
+ * sent to one of the channels.
+ *
+ * @author William Welliver - Initial contribution
+ */
+public class PowerLevelDeviceHandler extends BaseThingHandler implements DeviceStatusListener {
+
+ protected Logger log = LoggerFactory.getLogger(getClass());
+
+ protected int deviceId; //
+ protected int integrationId;
+ protected LutronDevice device; // last update received for this device.
+ protected int linkAddress;
+
+ protected LutronMQTTHubHandler hubHandler;
+
+ protected final String powerLevelChannelName;
+ // protected final String powerSwitchChannelName;
+
+ public PowerLevelDeviceHandler(Thing thing, String powerLevelChannel, String powerSwitchChannel) {
+ super(thing);
+ this.powerLevelChannelName = powerLevelChannel;
+ // this.powerSwitchChannelName = powerSwitchChannel;
+ }
+
+ @Override
+ public void handleCommand(ChannelUID channelUID, Command command) {
+ Map<String,Object> lightState = null;
+ String ch = channelUID.getId();
+ log.warn("Got a command for channel id=" + ch + ", command=" + command);
+
+ if (log.isTraceEnabled()) {
+ log.trace("command= " + command + ", last device reading=" + getDevice().getProperty(LUTRON_PROPERTY_LEVEL));
+ }
+ if (powerLevelChannelName.equals(ch)) {
+ if (command instanceof PercentType) {
+ lightState = LightStateConverter.toLightState((PercentType) command, getDevice());
+ } else if (command instanceof OnOffType) {
+ lightState = LightStateConverter.toLightState((OnOffType) command, getDevice());
+ } else if (command instanceof IncreaseDecreaseType) {
+ lightState = LightStateConverter.toLightState((IncreaseDecreaseType) command, getDevice());
+ }
+ }
+
+ /*
+ * else if (powerSwitchChannelName.equals(ch)) {
+ * if (command instanceof OnOffType) {
+ * lightState = LightStateConverter.toOnOffLightState((OnOffType) command, getDevice());
+ * }
+ * }
+ */
+
+ if (lightState != null) {
+ if (log.isTraceEnabled()) {
+ Gson gson = new Gson();
+ log.trace("converted " + command + " to " + gson.toJson(lightState));
+ }
+ updateDeviceState(lightState);
+ } else {
+ log.warn("Got a command for an unhandled channel: " + ch);
+ }
+ }
+
+ protected void updateDeviceState(Map<String, Object> lightState) {
+ log.warn("updateDeviceState: " + lightState);
+ getHubHandler().setDesiredState(deviceId, lightState);
+ }
+
+ @Override
+ public void initialize() {
+ log.debug("Initializing power level device handler.");
+ initializeThing((getBridge() == null) ? null : getBridge().getStatus());
+ }
+
+ 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(getThing().getProperties().get(PROPERTY_INTEGRATION_ID));
+ linkAddress = Integer.parseInt(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);
+ }
+ }
+
+ @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 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;
+ }
+
+ 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));
+ }
+ }
+ });
+ }
+
+ public LutronDevice getDevice() {
+ if (device != null) {
+ return device;
+ }
+
+ LutronMQTTHubHandler handler = getHubHandler();
+ device = handler.getDeviceByLinkAddress(linkAddress);
+ return device;
+ }
+
+ @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.");
+ }
+ }
+
+ @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;
+
+ log.info("Lutron Device: " + d.getName() + " State Changed: " + d.getProperty(LUTRON_PROPERTY_LEVEL));
+
+ // TODO we should keep the previous state so that we don't send unnecessary updates.
+
+ PercentType percentType = LightStateConverter.toBrightnessPercentType(d);
+
+ log.info("Lutron: " + d.getName() + " Light Level: " + percentType.intValue());
+ updateState(powerLevelChannelName, percentType);
+ }
+
+ protected Integer getCurrentLevel(LutronDevice light) {
+ int brightness = light.getProperty(LUTRON_PROPERTY_LEVEL);
+
+ return brightness;
+ }
+
+}
A => src/main/java/org/openhab/binding/lutronmqtt/internal/LutronMQTTConfiguration.java +26 -0
@@ 0,0 1,26 @@
+/**
+ * Copyright (c) 2014,2018 by the respective copyright holders.
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.lutronmqtt.internal;
+
+/**
+ * The {@link LutronMQTTConfiguration} class contains fields mapping thing configuration paramters.
+ *
+ * @author William Welliver - Initial contribution
+ */
+public class LutronMQTTConfiguration {
+
+ /**
+ * Sample configuration parameter. Replace with you own.
+ */
+ public String config1;
+}
A => src/main/java/org/openhab/binding/lutronmqtt/internal/LutronMQTTHandlerFactory.java +101 -0
@@ 0,0 1,101 @@
+/**
+ * Copyright (c) 2014,2018 by the respective copyright holders.
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.lutronmqtt.internal;
+
+import static org.openhab.binding.lutronmqtt.LutronMQTTBindingConstants.*;
+
+import java.util.*;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.smarthome.config.discovery.DiscoveryService;
+import org.eclipse.smarthome.core.thing.Bridge;
+import org.eclipse.smarthome.core.thing.ThingUID;
+import org.openhab.binding.lutronmqtt.LutronMQTTBindingConstants;
+import org.eclipse.smarthome.core.thing.Thing;
+import org.eclipse.smarthome.core.thing.ThingTypeUID;
+import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory;
+import org.eclipse.smarthome.core.thing.binding.ThingHandler;
+import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory;
+import org.openhab.binding.lutronmqtt.discovery.LutronMQTTDeviceDiscoveryService;
+import org.openhab.binding.lutronmqtt.handler.LutronMQTTDimmableLightHandler;
+import org.openhab.binding.lutronmqtt.handler.LutronMQTTHubHandler;
+import org.openhab.binding.lutronmqtt.handler.LutronMQTTRemoteHandler;
+import org.openhab.binding.lutronmqtt.handler.LutronMQTTVariableFanHandler;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.component.annotations.Component;
+
+/**
+ * The {@link LutronMQTTHandlerFactory} is responsible for creating things and thing
+ * handlers.
+ *
+ * @author William Welliver - Initial contribution
+ */
+@Component(service = ThingHandlerFactory.class, immediate = true, configurationPid = "binding.lutronmqtt", name="lutronmqtt")
+@NonNullByDefault
+public class LutronMQTTHandlerFactory extends BaseThingHandlerFactory {
+
+ public final static Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = LutronMQTTBindingConstants.SUPPORTED_THING_TYPES_UIDS;
+ private Map<ThingUID, ServiceRegistration<?>> discoveryServiceRegs = new HashMap<>();
+
+ @Override
+ public boolean supportsThingType(ThingTypeUID thingTypeUID) {
+ return THING_TYPE_MQTTHUB.equals(thingTypeUID) || SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
+ }
+
+ @Override
+ protected ThingHandler createHandler(Thing thing) {
+
+ ThingTypeUID thingTypeUID = thing.getThingTypeUID();
+
+ if (thingTypeUID.equals(THING_TYPE_MQTTHUB)) {
+ LutronMQTTHubHandler handler = new LutronMQTTHubHandler((Bridge) thing);
+ registerDeviceDiscoveryService(handler);
+ return handler;
+ } else if (thingTypeUID.equals(THING_TYPE_REMOTE)) {
+ LutronMQTTRemoteHandler handler = new LutronMQTTRemoteHandler(thing);
+ return handler;
+ }
+ else if (thingTypeUID.equals(THING_TYPE_DIMMABLE_LIGHT)) {
+ LutronMQTTDimmableLightHandler handler = new LutronMQTTDimmableLightHandler(thing);
+ return handler;
+ } else if (thingTypeUID.equals(THING_TYPE_VARIABLE_FAN)) {
+ LutronMQTTVariableFanHandler handler = new LutronMQTTVariableFanHandler(thing);
+ return handler;
+ }
+
+ return null;
+ }
+
+ private synchronized void registerDeviceDiscoveryService(LutronMQTTHubHandler hubHandler) {
+ LutronMQTTDeviceDiscoveryService discoveryService = new LutronMQTTDeviceDiscoveryService(hubHandler);
+ discoveryService.activate();
+ this.discoveryServiceRegs.put(hubHandler.getThing().getUID(), bundleContext
+ .registerService(DiscoveryService.class.getName(), discoveryService, new Hashtable<String, Object>()));
+ }
+
+ @Override
+ protected synchronized void removeHandler(ThingHandler thingHandler) {
+ if (thingHandler instanceof LutronMQTTHubHandler) {
+ ServiceRegistration<?> serviceReg = this.discoveryServiceRegs.get(thingHandler.getThing().getUID());
+ if (serviceReg != null) {
+ // remove discovery service, if bridge handler is removed
+ LutronMQTTDeviceDiscoveryService service = (LutronMQTTDeviceDiscoveryService) bundleContext
+ .getService(serviceReg.getReference());
+ service.deactivate();
+ serviceReg.unregister();
+ discoveryServiceRegs.remove(thingHandler.getThing().getUID());
+ }
+ }
+ }
+
+}
A => src/main/java/org/openhab/binding/lutronmqtt/model/LutronDevice.java +132 -0
@@ 0,0 1,132 @@
+package org.openhab.binding.lutronmqtt.model;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class LutronDevice {
+ int id;
+ String name;
+ int integrationId;
+ String description;
+ int linkAddress;
+ int serialNumber;
+ int deviceClass;
+
+ Map<Integer,Integer> properties = new HashMap<>();
+ private long lastUpdated;
+
+ public LutronDevice(Map<String,Object> d) {
+ setSerialNumber(Integer.parseInt((String)d.get("SerialNumber")));
+ setDeviceClass(Integer.parseInt((String)d.get("DeviceClass")));
+ setDescription((String)d.get("Description"));
+ setLinkAddress(Integer.parseInt((String)d.get("LinkAddress")));
+ setName((String)d.get("Name"));
+ setId(Integer.parseInt((String)d.get("DeviceID")));
+ setIntegrationId(Integer.parseInt((String)d.get("IntegrationID")));
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public int getLinkAddress() { return linkAddress; }
+
+ public void setLinkAddress(int linkAddress) { this.linkAddress = linkAddress; }
+
+ public String getName() {
+ return name;
+ }
+
+ public int getIntegrationId() {
+ return integrationId;
+ }
+
+ public void setIntegrationId(int integrationId) {
+ this.integrationId = integrationId;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public int getSerialNumber() {
+ return serialNumber;
+ }
+
+ public void setSerialNumber(int serialNumber) {
+ this.serialNumber = serialNumber;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public int getDeviceClass() {
+ return deviceClass;
+ }
+
+ public void setDeviceClass(int deviceClass) {
+ this.deviceClass = deviceClass;
+ }
+
+ public int getProperty(int property) {
+ return properties.get(property);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ LutronDevice that = (LutronDevice) o;
+
+ if (id != that.id) return false;
+ if (integrationId != that.integrationId) return false;
+ if (serialNumber != that.serialNumber) return false;
+ if (deviceClass != that.deviceClass) return false;
+ if (name != null ? !name.equals(that.name) : that.name != null) return false;
+ return description != null ? description.equals(that.description) : that.description == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = id;
+ result = 31 * result + (name != null ? name.hashCode() : 0);
+ result = 31 * result + integrationId;
+ result = 31 * result + linkAddress;
+ result = 31 * result + (description != null ? description.hashCode() : 0);
+ result = 31 * result + serialNumber;
+ result = 31 * result + deviceClass;
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "LutronDevice{" +
+ "id=" + id +
+ ", name='" + name + '\'' +
+ ", integrationId=" + integrationId +
+ ", linkAddress=" + linkAddress +
+ ", description='" + description + '\'' +
+ ", serialNumber=" + serialNumber +
+ ", deviceClass=" + deviceClass +
+ '}';
+ }
+
+ public boolean hasUpdatedProperties() {
+ return !properties.isEmpty();
+ }
+
+ public void putProperty(int pnum, int pval) {
+ lastUpdated = System.currentTimeMillis();
+ properties.put(pnum, pval);
+ }
+}
A => src/main/java/org/openhab/binding/lutronmqtt/model/LutronDeviceState.java +22 -0
@@ 0,0 1,22 @@
+package org.openhab.binding.lutronmqtt.model;
+
+public class LutronDeviceState {
+ protected Boolean powered;
+ protected float level;
+
+ public Boolean getPowered() {
+ return powered;
+ }
+
+ public void setPowered(Boolean powered) {
+ this.powered = powered;
+ }
+
+ public float getLevel() {
+ return level;
+ }
+
+ public void setLevel(float level) {
+ this.level = level;
+ }
+}
No newline at end of file