Java tutorial
/******************************************************************************* * Copyright (c) 2015 Whizzo Software, LLC. * 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 com.whizzosoftware.hobson.mqtt; import com.whizzosoftware.hobson.api.device.DeviceContext; import com.whizzosoftware.hobson.api.device.DevicePassport; import com.whizzosoftware.hobson.api.device.DevicePassportAlreadyActivatedException; import com.whizzosoftware.hobson.api.device.DevicePassportNotFoundException; import com.whizzosoftware.hobson.api.plugin.PluginContext; import com.whizzosoftware.hobson.api.variable.VariableContext; import com.whizzosoftware.hobson.api.variable.VariableUpdate; import org.json.JSONException; import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.regex.Pattern; /** * Class responsible for processing incoming MQTT messages. Examples are device registration and device data * submission messages. * * @author Dan Noguerol */ public class MQTTMessageHandler { private static final Logger logger = LoggerFactory.getLogger(MQTTMessageHandler.class); private PluginContext ctx; private MQTTMessageSink sink; private MQTTEventDelegate delegate; private Pattern deviceDataTopicPattern = Pattern.compile("^device/.*/data$"); /** * Constructor. * * @param sink the transport to use for sending response messages * @param delegate the listener to notify of events generated by incoming messages */ public MQTTMessageHandler(PluginContext ctx, MQTTMessageSink sink, MQTTEventDelegate delegate) { this.ctx = ctx; this.sink = sink; this.delegate = delegate; } /** * Called when an MQTT message is received. * * @param topic the topic the message was received on * @param json the JSON payload of the message */ public void onMessage(String topic, JSONObject json) { logger.trace("Received MQTT message on topic {}: {}", topic, json); if ("bootstrap".equals(topic)) { if (json.has("deviceId") && json.has("nonce")) { String deviceId = json.getString("deviceId"); String nonce = json.getString("nonce"); JSONObject res = new JSONObject(); try { DevicePassport passport = delegate.activateDevicePassport(deviceId); // alert listener of the device registration delegate.onPassportRegistration(deviceId, json.has("name") ? json.getString("name") : "Unknown MQTT Device", json.has("data") ? createVariableUpdates(passport.getDeviceId(), json.getJSONObject("data")) : null); // create JSON response message res.put("id", passport.getId()); res.put("secret", passport.getSecret()); res.put("topics", createTopicJson(passport.getId())); } catch (DevicePassportAlreadyActivatedException e) { res.put("topics", createTopicJson(e.getId())); } catch (DevicePassportNotFoundException e) { res.put("error", "Unable to bootstrap device"); } // send response message String responseTopic = "bootstrap/" + deviceId + "/" + nonce; logger.debug("Registration message detected from {}; sending response on topic {}", deviceId, responseTopic); sink.sendMessage(responseTopic, res); } else { logger.error("Device registration missing device ID or nonce"); } } else if (deviceDataTopicPattern.matcher(topic).matches()) { try { // alert listener of received data int ix = topic.indexOf('/') + 1; DevicePassport passport = delegate.getDevicePassport(topic.substring(ix, topic.indexOf('/', ix))); if (passport != null) { delegate.onDeviceData(passport.getDeviceId(), createVariableUpdates(passport.getDeviceId(), json)); } else { logger.error("Received data from device with invalid bootstrap identifier"); } } catch (JSONException e) { logger.error("Error parsing device data JSON", e); } } } protected Collection<VariableUpdate> createVariableUpdates(String deviceId, JSONObject json) { List<VariableUpdate> updates = new ArrayList<>(); for (Object o : json.keySet()) { String key = o.toString(); updates.add(new VariableUpdate(VariableContext.create(DeviceContext.create(ctx, deviceId), key), json.get(key))); } return updates; } private JSONObject createTopicJson(String id) { JSONObject topics = new JSONObject(); topics.put("data", "device/" + id + "/data"); topics.put("command", "device/" + id + "/command"); return topics; } }