├── .gitignore ├── DEVELOPING.md ├── LICENSE.md ├── README.md ├── compile.sh ├── examples ├── PublishSubscribe │ └── PublishSubscribe.pde └── PublishSubscribeListener │ └── PublishSubscribeListener.pde ├── lib └── org.eclipse.paho.client.mqttv3-1.2.0.jar ├── resources ├── build.properties ├── build.xml ├── code │ ├── ExampleTaglet.class │ ├── ExampleTaglet.java │ └── ant-contrib-1.0b3.jar ├── library.properties └── stylesheet.css └── src └── mqtt ├── Event.java ├── MQTTClient.java ├── MQTTListener.java ├── Message.java └── Will.java /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | bin 3 | tmp 4 | distribution 5 | out/ 6 | -------------------------------------------------------------------------------- /DEVELOPING.md: -------------------------------------------------------------------------------- 1 | # Developing 2 | 3 | 1. Commit changes. 4 | 2. Update `resources/build.properties`. 5 | 3. Compile using `compile.sh`. 6 | 4. Tag release with `latest`. 7 | 5. Upload `mqtt.zip` and `mqtt.txt`. 8 | 6. Check the following urls: 9 | - https://github.com/256dpi/processing-mqtt/releases/download/latest/mqtt.zip 10 | - https://github.com/256dpi/processing-mqtt/releases/download/latest/mqtt.txt 11 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Joël Gähwiler 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # processing-mqtt 2 | 3 | **MQTT library for Processing based on the Eclipse Paho project** 4 | 5 | This library bundles the [Java Client](https://eclipse.org/paho/clients/java/) library of the Eclipse Paho project and adds a thin wrapper to get a Processing like API. 6 | 7 | [Download the latest version of the library.](https://github.com/256dpi/processing-mqtt/releases/download/latest/mqtt.zip) 8 | 9 | *Or even better use the Library Manager in the Processing IDE.* 10 | 11 | ## Example 12 | 13 | This example sketch connects to the public shiftr.io instance and sends a message on every keystroke. After starting the sketch you can find the client here: . 14 | 15 | ```java 16 | import mqtt.*; 17 | 18 | MQTTClient client; 19 | 20 | void setup() { 21 | client = new MQTTClient(this); 22 | client.connect("mqtt://public:public@public.cloud.shiftr.io", "processing"); 23 | } 24 | 25 | void draw() {} 26 | 27 | void keyPressed() { 28 | client.publish("/hello", "world"); 29 | } 30 | 31 | void clientConnected() { 32 | println("client connected"); 33 | 34 | client.subscribe("/hello"); 35 | } 36 | 37 | void messageReceived(String topic, byte[] payload) { 38 | println("new message: " + topic + " - " + new String(payload)); 39 | } 40 | 41 | void connectionLost() { 42 | println("connection lost"); 43 | } 44 | 45 | ``` 46 | 47 | ## API 48 | 49 | Instantiate a new client by supplying the parent applet: 50 | 51 | ```java 52 | MQTTClient client = new MQTTClient(PApplet parent); 53 | ``` 54 | 55 | - The constructor expects the following method to be declared on the parent applet: `void messageReceived(String topic, byte[] payload)`. That callback will then be invoked in the future with incoming messages. 56 | - Additionally, the following callbacks will be detected: `void clientConnected()` and `void connectionLost()` and executed appropriately. 57 | 58 | Alternatively you can provide your own `Listener` class instead of relying on global methods: 59 | 60 | ```java 61 | MQTTClient client = new MQTTClient(PApplet parent, Listener listener); 62 | ``` 63 | 64 | - You can find the interface here: . 65 | 66 | Set the will message that gets transmitted to the server in all subsequent connect commands: 67 | 68 | ```java 69 | void client.setWill(String topic, String payload); 70 | void client.setWill(String topic, String payload, int qos, boolean retained); 71 | ``` 72 | 73 | - The QoS level and retained flag default to `0` and `false` respectively. 74 | 75 | Connect to the supplied broker by parsing the URL and setting the optionally supplied client id and clean session flag: 76 | 77 | ```java 78 | void client.connect(String brokerURI); 79 | void client.connect(String brokerURI, String clientId); 80 | void client.connect(String brokerURI, String clientId, boolean cleanSession); 81 | ``` 82 | 83 | - A client id will be generated if needed and the clean session flag defaults to `true`. 84 | 85 | Publish a message to the broker using the supplied topic and the optional payload in form of a String or byte-array. If available it will set the QoS level as well as the retained flag appropriately. 86 | 87 | ```java 88 | void client.publish(String topic); 89 | void client.publish(String topic, String payload); 90 | void client.publish(String topic, String payload, int qos, boolean retained); 91 | void client.publish(String topic, byte[] payload); 92 | void client.publish(String topic, byte[] payload, int qos, boolean retained); 93 | ``` 94 | 95 | - The QoS level and the retained flag default to `0` and `false` respectively. 96 | 97 | Subscribe to the supplied topic using the optionally provided QoS level that defaults to `0`: 98 | 99 | ```java 100 | void client.subscribe(String topic); 101 | void client.subscribe(String topic, int qos); 102 | ``` 103 | 104 | Unsubscribe from the supplied topic: 105 | 106 | ```java 107 | void client.unsubscribe(String topic); 108 | ``` 109 | 110 | Disconnect from the broker: 111 | 112 | ```java 113 | void client.disconnect(); 114 | ``` 115 | 116 | ## Notes 117 | 118 | - If you're running the sketch via the Android Mode you need to set the `INTERNET` permission in `Android > Sketch Permissions`. 119 | -------------------------------------------------------------------------------- /compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cd ./resources/ 4 | ant 5 | -------------------------------------------------------------------------------- /examples/PublishSubscribe/PublishSubscribe.pde: -------------------------------------------------------------------------------- 1 | // This example sketch connects to the public shiftr.io instance and sends a message on every keystroke. 2 | // After starting the sketch you can find the client here: https://www.shiftr.io/try. 3 | // 4 | // Note: If you're running the sketch via the Android Mode you need to set the INTERNET permission 5 | // in Android > Sketch Permissions. 6 | // 7 | // by Joël Gähwiler 8 | // https://github.com/256dpi/processing-mqtt 9 | 10 | import mqtt.*; 11 | 12 | MQTTClient client; 13 | 14 | void setup() { 15 | client = new MQTTClient(this); 16 | client.connect("mqtt://public:public@public.cloud.shiftr.io", "processing"); 17 | } 18 | 19 | void draw() {} 20 | 21 | void keyPressed() { 22 | client.publish("/hello", "world"); 23 | } 24 | 25 | void clientConnected() { 26 | println("client connected"); 27 | 28 | client.subscribe("/hello"); 29 | } 30 | 31 | void messageReceived(String topic, byte[] payload) { 32 | println("new message: " + topic + " - " + new String(payload)); 33 | } 34 | 35 | void connectionLost() { 36 | println("connection lost"); 37 | } 38 | -------------------------------------------------------------------------------- /examples/PublishSubscribeListener/PublishSubscribeListener.pde: -------------------------------------------------------------------------------- 1 | // This example sketch connects to the public shiftr.io instance and sends a message on every keystroke. 2 | // After starting the sketch you can find the client here: https://www.shiftr.io/try. 3 | // 4 | // Note: If you're running the sketch via the Android Mode you need to set the INTERNET permission 5 | // in Android > Sketch Permissions. 6 | // 7 | // by Joël Gähwiler 8 | // https://github.com/256dpi/processing-mqtt 9 | 10 | import mqtt.*; 11 | 12 | MQTTClient client; 13 | 14 | class Adapter implements MQTTListener { 15 | void clientConnected() { 16 | println("client connected"); 17 | 18 | client.subscribe("/hello"); 19 | } 20 | 21 | void messageReceived(String topic, byte[] payload) { 22 | println("new message: " + topic + " - " + new String(payload)); 23 | } 24 | 25 | void connectionLost() { 26 | println("connection lost"); 27 | } 28 | } 29 | 30 | Adapter adapter; 31 | 32 | void setup() { 33 | adapter = new Adapter(); 34 | client = new MQTTClient(this, adapter); 35 | client.connect("mqtt://public:public@public.cloud.shiftr.io", "processing"); 36 | } 37 | 38 | void draw() {} 39 | 40 | void keyPressed() { 41 | client.publish("/hello", "world"); 42 | } 43 | -------------------------------------------------------------------------------- /lib/org.eclipse.paho.client.mqttv3-1.2.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/256dpi/processing-mqtt/e87e005b61879052e8e28236b2af034644dad5b9/lib/org.eclipse.paho.client.mqttv3-1.2.0.jar -------------------------------------------------------------------------------- /resources/build.properties: -------------------------------------------------------------------------------- 1 | sketchbook.location=${user.home}/Development/Processing 2 | classpath.local.location=/Applications/Processing.app/Contents/Java/core/library 3 | 4 | classpath.local.include=core.jar 5 | classpath.libraries.location=${sketchbook.location}/libraries 6 | java.target.version=1.7 7 | ant.description=Processing Library Ant build file. 8 | 9 | project.name=mqtt 10 | project.prettyName=MQTT 11 | project.compile=normal 12 | 13 | author.name=Joel Gaehwiler 14 | author.url=https://github.com/256dpi 15 | 16 | library.url=https://github.com/256dpi/processing-mqtt 17 | library.categories=I/O 18 | library.sentence=MQTT library for Processing based on the Eclipse Paho project. 19 | library.paragraph= 20 | 21 | source.host=GitHub 22 | source.url=https://github.com/256dpi/processing-mqtt 23 | source.repository=https://github.com/256dpi/processing-mqtt.git 24 | 25 | library.version=20 26 | library.prettyVersion=1.7.3 27 | 28 | compatible.minRevision=0 29 | compatible.maxRevision=0 30 | 31 | tested.platform=osx,windows 32 | tested.processingVersion=3.0 33 | 34 | library.copyright=(c) 2015 35 | library.dependencies=none 36 | library.keywords=mqtt publish subscribe 37 | 38 | javadoc.java.href=http://docs.oracle.com/javase/7/docs/api/ 39 | javadoc.processing.href=http://processing.org/reference/javadoc/core/ 40 | -------------------------------------------------------------------------------- /resources/build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 13 | 14 | 15 | ${ant.description} 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | -------------------------------------------------------------------------------- /resources/code/ExampleTaglet.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/256dpi/processing-mqtt/e87e005b61879052e8e28236b2af034644dad5b9/resources/code/ExampleTaglet.class -------------------------------------------------------------------------------- /resources/code/ExampleTaglet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or 5 | * without modification, are permitted provided that the following 6 | * conditions are met: 7 | * 8 | * -Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * -Redistribution in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in 13 | * the documentation and/or other materials provided with the 14 | * distribution. 15 | * 16 | * Neither the name of Sun Microsystems, Inc. or the names of 17 | * contributors may be used to endorse or promote products derived 18 | * from this software without specific prior written permission. 19 | * 20 | * This software is provided "AS IS," without a warranty of any 21 | * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND 22 | * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, 23 | * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY 24 | * EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY 25 | * DAMAGES OR LIABILITIES SUFFERED BY LICENSEE AS A RESULT OF OR 26 | * RELATING TO USE, MODIFICATION OR DISTRIBUTION OF THE SOFTWARE OR 27 | * ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE 28 | * FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, 29 | * SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER 30 | * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF 31 | * THE USE OF OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN 32 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 33 | * 34 | * You acknowledge that Software is not designed, licensed or 35 | * intended for use in the design, construction, operation or 36 | * maintenance of any nuclear facility. 37 | */ 38 | 39 | import com.sun.tools.doclets.Taglet; 40 | import com.sun.javadoc.*; 41 | import java.util.Map; 42 | import java.io.*; 43 | /** 44 | * A sample Taglet representing @example. This tag can be used in any kind of 45 | * {@link com.sun.javadoc.Doc}. It is not an inline tag. The text is displayed 46 | * in yellow to remind the developer to perform a task. For 47 | * example, "@example Hello" would be shown as: 48 | *
49 | *
50 | * To Do: 51 | *
Fix this! 52 | *
53 | *
54 | * 55 | * @author Jamie Ho 56 | * @since 1.4 57 | */ 58 | 59 | public class ExampleTaglet implements Taglet { 60 | 61 | private static final String NAME = "example"; 62 | private static final String HEADER = "example To Do:"; 63 | 64 | /** 65 | * Return the name of this custom tag. 66 | */ 67 | public String getName() { 68 | return NAME; 69 | } 70 | 71 | /** 72 | * Will return true since @example 73 | * can be used in field documentation. 74 | * @return true since @example 75 | * can be used in field documentation and false 76 | * otherwise. 77 | */ 78 | public boolean inField() { 79 | return true; 80 | } 81 | 82 | /** 83 | * Will return true since @example 84 | * can be used in constructor documentation. 85 | * @return true since @example 86 | * can be used in constructor documentation and false 87 | * otherwise. 88 | */ 89 | public boolean inConstructor() { 90 | return true; 91 | } 92 | 93 | /** 94 | * Will return true since @example 95 | * can be used in method documentation. 96 | * @return true since @example 97 | * can be used in method documentation and false 98 | * otherwise. 99 | */ 100 | public boolean inMethod() { 101 | return true; 102 | } 103 | 104 | /** 105 | * Will return true since @example 106 | * can be used in method documentation. 107 | * @return true since @example 108 | * can be used in overview documentation and false 109 | * otherwise. 110 | */ 111 | public boolean inOverview() { 112 | return true; 113 | } 114 | 115 | /** 116 | * Will return true since @example 117 | * can be used in package documentation. 118 | * @return true since @example 119 | * can be used in package documentation and false 120 | * otherwise. 121 | */ 122 | public boolean inPackage() { 123 | return true; 124 | } 125 | 126 | /** 127 | * Will return true since @example 128 | * can be used in type documentation (classes or interfaces). 129 | * @return true since @example 130 | * can be used in type documentation and false 131 | * otherwise. 132 | */ 133 | public boolean inType() { 134 | return true; 135 | } 136 | 137 | /** 138 | * Will return false since @example 139 | * is not an inline tag. 140 | * @return false since @example 141 | * is not an inline tag. 142 | */ 143 | 144 | public boolean isInlineTag() { 145 | return false; 146 | } 147 | 148 | /** 149 | * Register this Taglet. 150 | * @param tagletMap the map to register this tag to. 151 | */ 152 | public static void register(Map tagletMap) { 153 | ExampleTaglet tag = new ExampleTaglet(); 154 | Taglet t = (Taglet) tagletMap.get(tag.getName()); 155 | if (t != null) { 156 | tagletMap.remove(tag.getName()); 157 | } 158 | tagletMap.put(tag.getName(), tag); 159 | } 160 | 161 | /** 162 | * Given the Tag representation of this custom 163 | * tag, return its string representation. 164 | * @param tag the Tag representation of this custom tag. 165 | */ 166 | public String toString(Tag tag) { 167 | return createHTML(readFile(tag.text())); 168 | } 169 | 170 | 171 | /** 172 | * Given an array of Tags representing this custom 173 | * tag, return its string representation. 174 | * @param tags the array of Tags representing of this custom tag. 175 | */ 176 | public String toString(Tag[] tags) { 177 | if (tags.length == 0) { 178 | return null; 179 | } 180 | return createHTML(readFile(tags[0].text())); 181 | } 182 | 183 | 184 | 185 | String createHTML(String theString) { 186 | if(theString!=null) { 187 | String dd = ""; 193 | 194 | return dd+"\n
" + 195 | "
+Example
" + 196 | "
"+theString+"
" + 197 | "
"; 198 | } 199 | return ""; 200 | } 201 | 202 | 203 | /** 204 | * check if the examples directory exists and return the example as given in the tag. 205 | * @param theExample the name of the example 206 | */ 207 | String readFile(String theExample) { 208 | String record = ""; 209 | String myResult = ""; 210 | int recCount = 0; 211 | String myDir = "../examples"; 212 | File file=new File(myDir); 213 | if(file.exists()==false) { 214 | myDir = "./examples"; 215 | } 216 | try { 217 | FileReader fr = new FileReader(myDir+"/"+theExample+"/"+theExample+".pde"); 218 | BufferedReader br = new BufferedReader(fr); 219 | record = new String(); 220 | while ((record = br.readLine()) != null) { 221 | myResult += record+"\n"; 222 | } 223 | } catch (IOException e) { 224 | System.out.println(e); 225 | return null; 226 | } 227 | return myResult; 228 | } 229 | } 230 | 231 | 232 | -------------------------------------------------------------------------------- /resources/code/ant-contrib-1.0b3.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/256dpi/processing-mqtt/e87e005b61879052e8e28236b2af034644dad5b9/resources/code/ant-contrib-1.0b3.jar -------------------------------------------------------------------------------- /resources/library.properties: -------------------------------------------------------------------------------- 1 | name = ##library.name## 2 | authors = [##author.name##](##author.url##) 3 | url = ##library.url## 4 | categories = ##library.categories## 5 | sentence = ##library.sentence## 6 | paragraph = ##library.paragraph## 7 | version = ##library.version## 8 | prettyVersion = ##library.prettyVersion## 9 | minRevision = ##compatible.minRevision## 10 | maxRevision = ##compatible.maxRevision## 11 | -------------------------------------------------------------------------------- /resources/stylesheet.css: -------------------------------------------------------------------------------- 1 | /* Javadoc style sheet */ 2 | /* Define colors, fonts and other style attributes here to override the defaults */ 3 | /* processingLibs style by andreas schlegel, sojamo */ 4 | 5 | 6 | body { 7 | margin : 0; 8 | padding : 0; 9 | padding-left : 10px; 10 | padding-right : 8px; 11 | background-color : #FFFFFF; 12 | font-family : Verdana, Geneva, Arial, Helvetica, sans-serif; 13 | font-size : 100%; 14 | font-size : 0.7em; 15 | font-weight : normal; 16 | line-height : normal; 17 | margin-bottom:30px; 18 | } 19 | 20 | 21 | 22 | 23 | /* Headings */ 24 | h1, h2, h3, h4, h5, th { 25 | font-family :Arial, Helvetica, sans-serif; 26 | font-size:1.2em; 27 | } 28 | 29 | 30 | p { 31 | font-size : 1em; 32 | width:80%; 33 | } 34 | 35 | pre, code { 36 | font-family : "Courier New", Courier, monospace; 37 | font-size : 12px; 38 | line-height : normal; 39 | } 40 | 41 | 42 | 43 | table { 44 | border:0; 45 | margin-bottom:10px; 46 | margin-top:10px; 47 | } 48 | 49 | 50 | tr, td { 51 | border-top: 0px solid; 52 | border-left: 0px solid; 53 | padding-top:8px; 54 | padding-bottom:8px; 55 | } 56 | 57 | 58 | 59 | hr { 60 | border:0; 61 | height:1px; 62 | padding:0; 63 | margin:0; 64 | margin-bottom:4px; 65 | 66 | } 67 | 68 | 69 | 70 | dd, th, td, font { 71 | font-size:1.0em; 72 | line-height:1.0em; 73 | } 74 | 75 | 76 | 77 | dt { 78 | margin-bottom:0px; 79 | } 80 | 81 | 82 | 83 | dd { 84 | margin-top:2px; 85 | margin-bottom:4px; 86 | } 87 | 88 | 89 | 90 | a { 91 | text-decoration: underline; 92 | font-weight: normal; 93 | } 94 | 95 | a:hover, 96 | a:active { 97 | text-decoration: underline; 98 | font-weight: normal; 99 | } 100 | 101 | a:visited, 102 | a:link:visited { 103 | text-decoration: underline; 104 | font-weight: normal; 105 | } 106 | 107 | 108 | img { 109 | border: 0px solid #000000; 110 | } 111 | 112 | 113 | 114 | /* Navigation bar fonts */ 115 | .NavBarCell1 { 116 | border:0; 117 | } 118 | 119 | .NavBarCell1Rev { 120 | border:0; 121 | } 122 | 123 | .NavBarFont1 { 124 | font-family: Arial, Helvetica, sans-serif; 125 | font-size:1.1em; 126 | } 127 | 128 | 129 | .NavBarFont1 b { 130 | font-weight:normal; 131 | } 132 | 133 | 134 | 135 | .NavBarFont1:after, .NavBarFont1Rev:after { 136 | font-weight:normal; 137 | content: " \\"; 138 | } 139 | 140 | 141 | .NavBarFont1Rev { 142 | font-family: Arial, Helvetica, sans-serif; 143 | font-size:1.1em; 144 | } 145 | 146 | .NavBarFont1Rev b { 147 | font-family: Arial, Helvetica, sans-serif; 148 | font-size:1.1em; 149 | font-weight:normal; 150 | } 151 | 152 | .NavBarCell2 { 153 | font-family: Arial, Helvetica, sans-serif; 154 | } 155 | 156 | .NavBarCell3 { 157 | font-family: Arial, Helvetica, sans-serif; 158 | } 159 | 160 | 161 | 162 | font.FrameItemFont { 163 | font-family: Helvetica, Arial, sans-serif; 164 | font-size:1.1em; 165 | line-height:1.1em; 166 | } 167 | 168 | font.FrameHeadingFont { 169 | font-family: Helvetica, Arial, sans-serif; 170 | line-height:32px; 171 | } 172 | 173 | /* Font used in left-hand frame lists */ 174 | .FrameTitleFont { 175 | font-family: Helvetica, Arial, sans-serif 176 | } 177 | 178 | 179 | .toggleList { 180 | padding:0; 181 | margin:0; 182 | margin-top:12px; 183 | } 184 | 185 | .toggleList dt { 186 | font-weight:bold; 187 | font-size:12px; 188 | font-family:arial,sans-serif; 189 | padding:0px; 190 | margin:10px 0px 10px 0px; 191 | } 192 | 193 | .toggleList dt span { 194 | font-family: monospace; 195 | padding:0; 196 | margin:0; 197 | } 198 | 199 | 200 | .toggleList dd { 201 | margin:0; 202 | padding:0; 203 | } 204 | 205 | html.isjs .toggleList dd { 206 | display: none; 207 | } 208 | 209 | .toggleList pre { 210 | padding: 4px 4px 4px 4px; 211 | } 212 | 213 | 214 | 215 | 216 | 217 | /* COLORS */ 218 | 219 | pre, code { 220 | color: #000000; 221 | } 222 | 223 | 224 | body { 225 | color : #333333; 226 | background-color :#FFFFFF; 227 | } 228 | 229 | 230 | h1, h2, h3, h4, h5, h6 { 231 | color:#555; 232 | } 233 | 234 | a, 235 | .toggleList dt { 236 | color: #1a7eb0; 237 | } 238 | 239 | a:hover, 240 | a:active { 241 | color: #1a7eb0; 242 | } 243 | 244 | a:visited, 245 | a:link:visited { 246 | color: #1a7eb0; 247 | } 248 | 249 | td,tr { 250 | border-color: #999999; 251 | } 252 | 253 | hr { 254 | color:#999999; 255 | background:#999999; 256 | } 257 | 258 | 259 | .TableHeadingColor { 260 | background: #dcdcdc; 261 | color: #555; 262 | } 263 | 264 | 265 | .TableSubHeadingColor { 266 | background: #EEEEFF 267 | } 268 | 269 | .TableRowColor { 270 | background: #FFFFFF 271 | } 272 | 273 | 274 | .NavBarCell1 { 275 | background-color:#dcdcdc; 276 | color:#000; 277 | } 278 | 279 | .NavBarCell1 a { 280 | color:#333; 281 | } 282 | 283 | 284 | .NavBarCell1Rev { 285 | background-color:transparent; 286 | } 287 | 288 | .NavBarFont1 { 289 | color:#333; 290 | } 291 | 292 | 293 | .NavBarFont1Rev { 294 | color:#fff; 295 | } 296 | 297 | .NavBarCell2 { 298 | background-color:#999; 299 | } 300 | 301 | .NavBarCell2 a { 302 | color:#fff; 303 | } 304 | 305 | 306 | 307 | .NavBarCell3 { 308 | background-color:#dcdcdc; 309 | } 310 | 311 | -------------------------------------------------------------------------------- /src/mqtt/Event.java: -------------------------------------------------------------------------------- 1 | package mqtt; 2 | 3 | class Event { 4 | boolean clientConnected; 5 | boolean connectionLost; 6 | 7 | Event(boolean clientConnected, boolean connectionLost) { 8 | this.clientConnected = clientConnected; 9 | this.connectionLost = connectionLost; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/mqtt/MQTTClient.java: -------------------------------------------------------------------------------- 1 | /** 2 | * ##library.name## 3 | * 4 | *

##library.sentence## 5 | * 6 | *

##library.url## 7 | * 8 | *

Copyright ##copyright## ##author## 9 | * 10 | *

This library is free software; you can redistribute it and/or modify it under the terms of the 11 | * GNU Lesser General Public License as published by the Free Software Foundation; either version 12 | * 2.1 of the License, or (at your option) any later version. 13 | * 14 | *

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 15 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU Lesser General Public License for more details. 17 | * 18 | *

You should have received a copy of the GNU Lesser General Public License along with this 19 | * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, 20 | * MA 02111-1307 USA 21 | * 22 | * @author ##author## 23 | * @modified ##date## 24 | * @version ##library.prettyVersion## (##library.version##) 25 | */ 26 | package mqtt; 27 | 28 | import java.lang.reflect.*; 29 | import java.lang.Exception; 30 | import java.lang.Override; 31 | import java.lang.String; 32 | import java.lang.Throwable; 33 | import java.net.URI; 34 | import java.net.URISyntaxException; 35 | import java.nio.charset.Charset; 36 | import java.util.concurrent.CopyOnWriteArrayList; 37 | 38 | import org.eclipse.paho.client.mqttv3.*; 39 | import org.eclipse.paho.client.mqttv3.persist.*; 40 | import processing.core.*; 41 | 42 | /** An MQTTClient that can publish and subscribe. */ 43 | public class MQTTClient implements MqttCallbackExtended { 44 | private MqttAsyncClient client; 45 | private PApplet parent; 46 | private CopyOnWriteArrayList messages; 47 | private CopyOnWriteArrayList events; 48 | private Will will; 49 | private MQTTListener listener; 50 | private Method messageReceivedMethod; 51 | private Method clientConnectedMethod; 52 | private Method connectionLostMethod; 53 | 54 | private final static long TIMEOUT = 2000; 55 | 56 | /** 57 | * The constructor, usually called in the setup() method in your sketch to initialize and start 58 | * the library. 59 | * 60 | * @param parent A reference to the running sketch. 61 | */ 62 | public MQTTClient(PApplet parent) { 63 | // save parent 64 | this.parent = parent; 65 | 66 | // init messages and events list 67 | messages = new CopyOnWriteArrayList<>(); 68 | events = new CopyOnWriteArrayList<>(); 69 | 70 | // register callbacks 71 | parent.registerMethod("dispose", this); 72 | parent.registerMethod("draw", this); 73 | 74 | // find callbacks 75 | messageReceivedMethod = findMessageReceivedCallback(); 76 | clientConnectedMethod = findClientConnectedCallback(); 77 | connectionLostMethod = findConnectionLostCallback(); 78 | } 79 | 80 | /** 81 | * The constructor, usually called in the setup() method in your sketch to initialize and start 82 | * the library. 83 | * 84 | * @param parent A reference to the running sketch. 85 | * @param listener A class that receives events. 86 | */ 87 | public MQTTClient(PApplet parent, MQTTListener listener) { 88 | // save parent 89 | this.parent = parent; 90 | this.listener = listener; 91 | 92 | // init messages and events list 93 | messages = new CopyOnWriteArrayList<>(); 94 | events = new CopyOnWriteArrayList<>(); 95 | 96 | // register callbacks 97 | parent.registerMethod("dispose", this); 98 | parent.registerMethod("draw", this); 99 | } 100 | 101 | /** 102 | * Set a last will message with topic, payload, QoS and the retained flag. 103 | * 104 | * @param topic The topic. 105 | * @param payload The payload. 106 | * @param qos The qos level. 107 | * @param retained The retainged flag. 108 | */ 109 | public void setWill(String topic, String payload, int qos, boolean retained) { 110 | setWill(topic, payload.getBytes(Charset.forName("UTF-8")), qos, retained); 111 | } 112 | 113 | /** 114 | * Set a last will message with topic and payload. 115 | * 116 | * @param topic The topic. 117 | * @param payload The payload. 118 | */ 119 | public void setWill(String topic, String payload) { 120 | setWill(topic, payload, 0, false); 121 | } 122 | 123 | /** 124 | * Set a last will message with topic, payload, QoS and the retained flag. 125 | * 126 | * @param topic The topic. 127 | * @param payload The payload. 128 | * @param qos The qos. 129 | * @param retained The retained flag. 130 | */ 131 | public void setWill(String topic, byte[] payload, int qos, boolean retained) { 132 | will = new Will(topic, payload, qos, retained); 133 | } 134 | 135 | /** 136 | * Connect to a broker using only a broker URI. 137 | * 138 | * @param brokerURI The broker URI. 139 | */ 140 | public void connect(String brokerURI) { 141 | connect(brokerURI, MqttClient.generateClientId()); 142 | } 143 | 144 | /** 145 | * Connect to a broker using an broker URI and client ID. 146 | * 147 | * @param brokerURI The broker URI. 148 | * @param clientID The client ID. 149 | */ 150 | public void connect(String brokerURI, String clientID) { 151 | connect(brokerURI, clientID, true); 152 | } 153 | 154 | /** 155 | * Connect to a broker using an broker URI, client Id and cleanSession flag. 156 | * 157 | * @param brokerURI The broker URI. 158 | * @param clientId The client ID. 159 | * @param cleanSession The clean session flag. 160 | */ 161 | public void connect(String brokerURI, String clientId, boolean cleanSession) { 162 | // parse uri 163 | URI uri; 164 | try { 165 | uri = new URI(brokerURI); 166 | } catch (URISyntaxException e) { 167 | throw new RuntimeException("[MQTT] Failed to parse URI: " + e.getMessage(), e); 168 | } 169 | 170 | try { 171 | // prepare connection options 172 | MqttConnectOptions options = new MqttConnectOptions(); 173 | options.setCleanSession(cleanSession); 174 | options.setAutomaticReconnect(true); 175 | 176 | // check will messafe 177 | if (will != null) { 178 | options.setWill(will.topic, will.payload, will.qos, will.retained); 179 | } 180 | 181 | // check for auth info 182 | if (uri.getUserInfo() != null) { 183 | String[] auth = uri.getUserInfo().split(":"); 184 | 185 | // check username 186 | if (auth.length > 0) { 187 | options.setUserName(auth[0]); 188 | 189 | // check passsword 190 | if (auth.length > 1) { 191 | options.setPassword(auth[1].toCharArray()); 192 | } 193 | } 194 | } 195 | 196 | // parse scheme 197 | String scheme = uri.getScheme(); 198 | if (scheme.equals("mqtt")) { 199 | scheme = "tcp"; 200 | } else if (scheme.equals("mqtts")) { 201 | scheme = "ssl"; 202 | } 203 | 204 | // finalize location 205 | String loc = scheme + "://" + uri.getHost(); 206 | if (uri.getPort() != -1) { 207 | loc = loc + ":" + uri.getPort(); 208 | } 209 | 210 | // create client 211 | client = new MqttAsyncClient(loc, clientId, new MemoryPersistence()); 212 | client.setCallback(this); 213 | 214 | // connect to broker 215 | client.connect(options).waitForCompletion(TIMEOUT); 216 | } catch (MqttException e) { 217 | throw new RuntimeException("[MQTT] Failed to connect:: " + e.getMessage(), e); 218 | } 219 | } 220 | 221 | /** 222 | * Publish a message with a topic. 223 | * 224 | * @param topic The topic. 225 | */ 226 | public void publish(String topic) { 227 | byte[] bytes = {}; 228 | publish(topic, bytes); 229 | } 230 | 231 | /** 232 | * Publish a message with a topic and payload. 233 | * 234 | * @param topic The topic. 235 | * @param payload The payload. 236 | */ 237 | public void publish(String topic, String payload) { 238 | publish(topic, payload, 0, false); 239 | } 240 | 241 | /** 242 | * Publish a message with a topic, payload qos and retain flag. 243 | * 244 | * @param topic The topic. 245 | * @param payload The payload. 246 | * @param qos The qos level. 247 | * @param retained The retained flag. 248 | */ 249 | public void publish(String topic, String payload, int qos, boolean retained) { 250 | publish(topic, payload.getBytes(Charset.forName("UTF-8")), qos, retained); 251 | } 252 | 253 | /** 254 | * Publish a message with a topic and payload. 255 | * 256 | * @param topic The topic. 257 | * @param payload The payload. 258 | */ 259 | public void publish(String topic, byte[] payload) { 260 | publish(topic, payload, 0, false); 261 | } 262 | 263 | /** 264 | * Publish a message with a topic, payload qos and retain flag. 265 | * 266 | * @param topic The topic. 267 | * @param payload The payload. 268 | * @param qos The qos level. 269 | * @param retained The retained flag. 270 | */ 271 | public void publish(String topic, byte[] payload, int qos, boolean retained) { 272 | try { 273 | client.publish(topic, payload, qos, retained).waitForCompletion(TIMEOUT); 274 | } catch (MqttException e) { 275 | throw new RuntimeException("[MQTT] Failed to publish: " + e.getMessage(), e); 276 | } 277 | } 278 | 279 | /** 280 | * Subscribe a topic. 281 | * 282 | * @param topic The topic. 283 | */ 284 | public void subscribe(String topic) { 285 | this.subscribe(topic, 0); 286 | } 287 | 288 | /** 289 | * Subscribe a topic with QoS. 290 | * 291 | * @param topic The topic. 292 | * @param qos The qos level. 293 | */ 294 | public void subscribe(String topic, int qos) { 295 | try { 296 | client.subscribe(topic, qos).waitForCompletion(TIMEOUT); 297 | } catch (MqttException e) { 298 | throw new RuntimeException("[MQTT] Failed to subscribe: " + e.getMessage(), e); 299 | } 300 | } 301 | 302 | /** 303 | * Unsubscribe a topic. 304 | * 305 | * @param topic The topic. 306 | */ 307 | public void unsubscribe(String topic) { 308 | try { 309 | client.unsubscribe(topic).waitForCompletion(TIMEOUT); 310 | } catch (MqttException e) { 311 | throw new RuntimeException("[MQTT] Failed to unsubscribe: " + e.getMessage(), e); 312 | } 313 | } 314 | 315 | /** 316 | * Disconnect from the broker. 317 | */ 318 | public void disconnect() { 319 | try { 320 | client.disconnect().waitForCompletion(TIMEOUT); 321 | } catch (MqttException e) { 322 | throw new RuntimeException("[MQTT] Failed to disconnect: " + e.getMessage(), e); 323 | } 324 | } 325 | 326 | public void dispose() { 327 | try { 328 | disconnect(); 329 | } catch(Exception ignored) { } 330 | } 331 | 332 | public void draw() { 333 | // process all queue events 334 | for (Event event : events) { 335 | // invoke client connected callback 336 | if (event.clientConnected) { 337 | if(listener != null) { 338 | try { 339 | listener.clientConnected(); 340 | } catch (Exception e) { 341 | throw new RuntimeException(e); 342 | } 343 | } else if (clientConnectedMethod != null) { 344 | try { 345 | clientConnectedMethod.invoke(parent); 346 | } catch (Exception e) { 347 | throw new RuntimeException(e); 348 | } 349 | } 350 | } 351 | 352 | // invoke connection lost callback 353 | if (event.connectionLost) { 354 | if(listener != null) { 355 | try { 356 | listener.connectionLost(); 357 | } catch (Exception e) { 358 | throw new RuntimeException(e); 359 | } 360 | } else if (connectionLostMethod != null) { 361 | try { 362 | connectionLostMethod.invoke(parent); 363 | } catch (Exception e) { 364 | throw new RuntimeException(e); 365 | } 366 | } 367 | } 368 | } 369 | 370 | // process all messages and invoke message received callback 371 | for (Message message : messages) { 372 | if(listener != null) { 373 | try { 374 | listener.messageReceived(message.topic, message.message.getPayload()); 375 | } catch (Exception e) { 376 | throw new RuntimeException(e); 377 | } 378 | } else if (messageReceivedMethod != null) { 379 | try { 380 | messageReceivedMethod.invoke(parent, message.topic, message.message.getPayload()); 381 | } catch (Exception e) { 382 | throw new RuntimeException(e); 383 | } 384 | } 385 | } 386 | 387 | // clear all queued events and messages 388 | events.clear(); 389 | messages.clear(); 390 | } 391 | 392 | @Override 393 | public void connectComplete(boolean b, String s) { 394 | events.add(new Event(true, false)); 395 | } 396 | 397 | @Override 398 | public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {} 399 | 400 | @Override 401 | public void messageArrived(String topic, MqttMessage mqttMessage) { 402 | messages.add(new Message(topic, mqttMessage)); 403 | } 404 | 405 | @Override 406 | public void connectionLost(Throwable throwable) { 407 | events.add(new Event(false, true)); 408 | } 409 | 410 | private Method findMessageReceivedCallback() { 411 | try { 412 | return parent.getClass().getMethod("messageReceived", String.class, byte[].class); 413 | } catch (Exception e) { 414 | throw new RuntimeException("MQTT] Callback not found!", e); 415 | } 416 | } 417 | 418 | private Method findClientConnectedCallback() { 419 | try { 420 | return parent.getClass().getMethod("clientConnected"); 421 | } catch (Exception e) { 422 | return null; 423 | } 424 | } 425 | 426 | private Method findConnectionLostCallback() { 427 | try { 428 | return parent.getClass().getMethod("connectionLost"); 429 | } catch (Exception e) { 430 | return null; 431 | } 432 | } 433 | } 434 | -------------------------------------------------------------------------------- /src/mqtt/MQTTListener.java: -------------------------------------------------------------------------------- 1 | package mqtt; 2 | 3 | /** 4 | * MQTTListener is an interface that can be implemented to receive MQTT events. 5 | */ 6 | public interface MQTTListener { 7 | /** 8 | * This method is called once the client successfully connected. 9 | */ 10 | void clientConnected(); 11 | 12 | /** 13 | * This method is called once the client received a message. 14 | * @param topic The topic. 15 | * @param payload The payload. 16 | */ 17 | void messageReceived(String topic, byte[] payload); 18 | 19 | /** 20 | * This method is called once the client has lost connection. 21 | */ 22 | void connectionLost(); 23 | } 24 | -------------------------------------------------------------------------------- /src/mqtt/Message.java: -------------------------------------------------------------------------------- 1 | package mqtt; 2 | 3 | import org.eclipse.paho.client.mqttv3.MqttMessage; 4 | 5 | class Message { 6 | String topic; 7 | MqttMessage message; 8 | 9 | Message(String topic, MqttMessage message) { 10 | this.topic = topic; 11 | this.message = message; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/mqtt/Will.java: -------------------------------------------------------------------------------- 1 | package mqtt; 2 | 3 | class Will { 4 | String topic; 5 | byte[] payload; 6 | int qos; 7 | boolean retained; 8 | 9 | Will(String topic, byte[] payload, int qos, boolean retained) { 10 | this.topic = topic; 11 | this.payload = payload; 12 | this.qos = qos; 13 | this.retained = retained; 14 | } 15 | } 16 | --------------------------------------------------------------------------------