I’ve been working on integrating the new IKEA TRÅDFRI Lights into my Home Automation system. I’d really like a native NodeJS system so I can plug it directly into Node-RED, but I’ve not found a working CoAP over DTLS setup just yet.
So in the mean time I’ve been working on a very basic MQTT to CoAP client bridge in Java using the Eclipse Californium library.
It still needs some work, but here is the first pass:
package uk.me.hardill.coap2mqtt; import java.net.InetSocketAddress; import java.net.URI; import java.net.URISyntaxException; import java.util.HashMap; import java.util.logging.Level; import org.eclipse.californium.core.CaliforniumLogger; import org.eclipse.californium.core.CoapClient; import org.eclipse.californium.core.CoapHandler; import org.eclipse.californium.core.CoapResponse; import org.eclipse.californium.core.Utils; import org.eclipse.californium.core.coap.MediaTypeRegistry; import org.eclipse.californium.core.network.CoapEndpoint; import org.eclipse.californium.core.network.config.NetworkConfig; import org.eclipse.californium.scandium.DTLSConnector; import org.eclipse.californium.scandium.ScandiumLogger; import org.eclipse.californium.scandium.config.DtlsConnectorConfig; import org.eclipse.californium.scandium.dtls.pskstore.StaticPskStore; import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; import org.eclipse.paho.client.mqttv3.MqttCallback; import org.eclipse.paho.client.mqttv3.MqttClient; import org.eclipse.paho.client.mqttv3.MqttException; import org.eclipse.paho.client.mqttv3.MqttMessage; import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; import org.json.JSONArray; import org.json.JSONObject; /** * @author hardillb * */ public class Main { static { CaliforniumLogger.disableLogging(); ScandiumLogger.disable(); // ScandiumLogger.initialize(); // ScandiumLogger.setLevel(Level.FINE); } private DTLSConnector dtlsConnector; private MqttClient mqttClient; private CoapEndpoint endPoint; private String ip; private HashMap<String, Integer> name2id = new HashMap<>(); Main(String psk, String ip) { this.ip = ip; DtlsConnectorConfig.Builder builder = new DtlsConnectorConfig.Builder(new InetSocketAddress(0)); builder.setPskStore(new StaticPskStore("", psk.getBytes())); dtlsConnector = new DTLSConnector(builder.build()); endPoint = new CoapEndpoint(dtlsConnector, NetworkConfig.getStandard()); MemoryPersistence persistence = new MemoryPersistence(); try { mqttClient = new MqttClient("tcp://localhost", MqttClient.generateClientId(), persistence); mqttClient.connect(); mqttClient.setCallback(new MqttCallback() { @Override public void messageArrived(String topic, MqttMessage message) throws Exception { // TODO Auto-generated method stub System.out.println(topic + " " + message.toString()); String parts[] = topic.split("/"); int id = name2id.get(parts[1]); System.out.println(id); String command = parts[3]; System.out.println(command); try{ JSONObject json = new JSONObject("{\"9001\":\"Living Room Light\",\"9020\":1491515804,\"9002\":1491158817,\"9003\":65537,\"9054\":0,\"5750\":2,\"3\":{\"0\":\"IKEA of Sweden\",\"1\":\"TRADFRI bulb E27 opal 1000lm\",\"2\":\"\",\"3\":\"1.1.1.0-5.7.2.0\",\"6\":1},\"9019\":1,\"3311\":[{\"5850\":1,\"5851\":10,\"9003\":0}]}"); JSONObject settings = json.getJSONArray("3311").getJSONObject(0); if (command.equals("dim")) { settings.put("5851", Integer.parseInt(message.toString())); } else if (command.equals("on")) { if (message.toString().equals("0")) { settings.put("5850", 0); settings.put("5851", 0); } else { settings.put("5850", 1); settings.put("5851", 128); } } String payload = json.toString(); Main.this.set("coaps://" + ip + "//15001/" + id, payload); } catch (Exception e) { e.printStackTrace(); } } @Override public void deliveryComplete(IMqttDeliveryToken token) { // TODO Auto-generated method stub } @Override public void connectionLost(Throwable cause) { // TODO Auto-generated method stub } }); mqttClient.subscribe("TRÅDFRI/+/control/+"); } catch (MqttException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private void discover() { try { URI uri = new URI("coaps://" + ip + "//15001"); CoapClient client = new CoapClient(uri); client.setEndpoint(endPoint); CoapResponse response = client.get(); JSONArray array = new JSONArray(response.getResponseText()); for (int i=0; i<array.length(); i++) { String devUri = "coaps://"+ ip + "//15001/" + array.getInt(i); this.watch(devUri); } client.shutdown(); } catch (URISyntaxException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private void set(String uriString, String payload) { System.out.println("payload\n" + payload); try { URI uri = new URI(uriString); CoapClient client = new CoapClient(uri); client.setEndpoint(endPoint); CoapResponse response = client.put(payload, MediaTypeRegistry.TEXT_PLAIN); if (response.isSuccess()) { System.out.println("Yay"); } else { System.out.println("Boo"); } client.shutdown(); } catch (URISyntaxException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private void watch(String uriString) { try { URI uri = new URI(uriString); CoapClient client = new CoapClient(uri); client.setEndpoint(endPoint); CoapHandler handler = new CoapHandler() { @Override public void onLoad(CoapResponse response) { System.out.println(response.getResponseText()); JSONObject json = new JSONObject(response.getResponseText()); if (json.has("3311")){ MqttMessage message = new MqttMessage(); int state = json.getJSONArray("3311").getJSONObject(0).getInt("5850"); message.setPayload(Integer.toString(state).getBytes()); message.setRetained(true); String topic = "TRÅDFRI/" + json.getString("9001") + "/state/on"; String topic2 = "TRÅDFRI/" + json.getString("9001") + "/state/dim"; name2id.put(json.getString("9001"), json.getInt("9003")); MqttMessage message2 = new MqttMessage(); int dim = json.getJSONArray("3311").getJSONObject(0).getInt("5851"); message2.setPayload(Integer.toString(dim).getBytes()); message2.setRetained(true); try { mqttClient.publish(topic, message); mqttClient.publish(topic2, message2); } catch (MqttException e) { // TODO Auto-generated catch block e.printStackTrace(); } } else { System.out.println("not bulb"); } } @Override public void onError() { // TODO Auto-generated method stub } }; client.observe(handler); } catch (URISyntaxException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * @param args */ public static void main(String[] args) throws InterruptedException { String psk = args[0]; String ip = args[1]; Main m = new Main(psk, ip); m.discover(); } }
I’ve tagged the code onto the gist as well for now, but I’ll check the whole thing in as a separate project soon.
EDIT: now with it’s own Github repo here