package org.thingml.tradfri; import java.util.ArrayList; import java.util.logging.Level; import java.util.logging.Logger; import org.eclipse.californium.core.CoapClient; import org.eclipse.californium.core.CoapResponse; 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.config.DtlsConnectorConfig; import org.eclipse.californium.scandium.dtls.pskstore.StaticPskStore; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; public class TradfriGateway implements Runnable { /** * Gateway properties and constructor */ protected String gateway_ip; protected String security_key; protected int polling_rate = 5000; public TradfriGateway() { } public TradfriGateway(String gateway_ip, String security_key) { this.gateway_ip = gateway_ip; this.security_key = security_key; } public String getGateway_ip() { return gateway_ip; } public void setGateway_ip(String gateway_ip) { this.gateway_ip = gateway_ip; } public String getSecurity_key() { return security_key; } public void setSecurity_key(String security_key) { this.security_key = security_key; } public int getPolling_rate() { return polling_rate; } public void setPolling_rate(int polling_rate) { // between 1 and 60 seconds if (polling_rate < 1000) polling_rate = 1000; else if (polling_rate > 60000) polling_rate = 60000; this.polling_rate = polling_rate; } private boolean running = false; public boolean isRunning() { return running; } /** * Logger to be used for all console outputs, errors and exceptions */ private Logger logger = Logger.getLogger(TradfriGateway.class.getName()); public Logger getLogger() { return logger; } /** * Observer pattern for asynchronous event notification */ private ArrayList<TradfriGatewayListener> listners = new ArrayList<TradfriGatewayListener>(); public void addTradfriGatewayListener(TradfriGatewayListener l) { listners.add(l); } public void removeTradfriGatewayListener(TradfriGatewayListener l) { listners.remove(l); } public void clearTradfriGatewayListener() { listners.clear(); } /** * Gateway public API */ public void startTradfriGateway() { if (running) return; running = true; new Thread(this).start(); } public void stopTradfriGateway() { running = false; } public void run() { for (TradfriGatewayListener l : listners) l.gateway_initializing(); Logger.getLogger(TradfriGateway.class.getName()).log(Level.INFO, "Tradfri Gateway is initalizing..."); initCoap(); Logger.getLogger(TradfriGateway.class.getName()).log(Level.INFO, "Discovering Devices..."); if (dicoverBulbs()) { Logger.getLogger(TradfriGateway.class.getName()).log(Level.INFO, "Discovered " + bulbs.size() + " Bulbs."); for (TradfriGatewayListener l : listners) l.gateway_started(); try { while(running) { Thread.sleep(getPolling_rate()); Logger.getLogger(TradfriGateway.class.getName()).log(Level.INFO, "Polling bulbs status..."); for (TradfriGatewayListener l : listners) l.polling_started(); long before = System.currentTimeMillis(); for (LightBulb b : bulbs) { b.updateBulb(); //System.out.println(b.toString()); } long after = System.currentTimeMillis(); for (TradfriGatewayListener l : listners) l.polling_completed(bulbs.size(), (int)(after - before)); } } catch (InterruptedException ex) { Logger.getLogger(TradfriGateway.class.getName()).log(Level.SEVERE, null, ex); } } running = false; for (TradfriGatewayListener l : listners) l.gateway_stoped(); } // Collection of bulbs registered on the gateway ArrayList<LightBulb> bulbs = new ArrayList<LightBulb>(); protected boolean dicoverBulbs() { bulbs.clear(); try { CoapResponse response = get(TradfriConstants.DEVICES); if (response == null) return false; JSONArray devices = new JSONArray(response.getResponseText()); for (TradfriGatewayListener l : listners) l.bulb_discovery_started(devices.length()); for (int i = 0; i < devices.length(); i++) { response = get(TradfriConstants.DEVICES + "/" + devices.getInt(i)); if (response != null) { JSONObject json = new JSONObject(response.getResponseText()); if (json.has(TradfriConstants.TYPE) && json.getInt(TradfriConstants.TYPE) == TradfriConstants.TYPE_BULB) { LightBulb b = new LightBulb(json.getInt(TradfriConstants.INSTANCE_ID), this, response); bulbs.add(b); for (TradfriGatewayListener l : listners) l.bulb_discovered(b); } } } for (TradfriGatewayListener l : listners) l.bulb_discovery_completed(); } catch (JSONException e) { logger.log(Level.SEVERE,"Error parsing response from the Tradfri gateway", e); return false; } return true; } /** * COAPS helpers to GET and SET on the IKEA Tradfri gateway using Californium */ private CoapEndpoint coap = null; protected void initCoap() { DtlsConnectorConfig.Builder builder = new DtlsConnectorConfig.Builder(); //new InetSocketAddress(0) builder.setPskStore(new StaticPskStore("", security_key.getBytes())); coap = new CoapEndpoint(new DTLSConnector(builder.build()), NetworkConfig.getStandard()); } protected CoapResponse get(String path) { Logger.getLogger(TradfriGateway.class.getName()).log(Level.INFO, "GET: " + "coaps://" + gateway_ip + "/" + path); CoapClient client = new CoapClient("coaps://" + gateway_ip + "/" + path); client.setEndpoint(coap); CoapResponse response = client.get(1); if (response == null) { logger.log(Level.SEVERE, "Connection to Gateway timed out, please check ip address or increase the ACK_TIMEOUT in the Californium.properties file"); } return response; } protected void set(String path, String payload) { Logger.getLogger(TradfriGateway.class.getName()).log(Level.INFO, "SET: " + "coaps://" + gateway_ip + "/" + path + " = " + payload); CoapClient client = new CoapClient("coaps://" + gateway_ip + "/" + path); client.setEndpoint(coap); CoapResponse response = client.put(payload, MediaTypeRegistry.TEXT_PLAIN); if (response != null && response.isSuccess()) { //System.out.println("Yay"); } else { logger.log(Level.SEVERE, "Sending payload to " + "coaps://" + gateway_ip + "/" + path + " failed!"); } client.shutdown(); } }