- Notifications
You must be signed in to change notification settings - Fork1
An MQTT-library for ClojureScript
License
eval/otarta
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
Canonical repository:https://gitlab.com/eval/otarta
An MQTT-library for ClojureScript.
NOTE: this is pre-alpha software with an API that will change (see theCHANGELOG for breaking changes)
Leiningen:
[eval/otarta"0.3.1"]
Deps:
eval/otarta {:mvn/version"0.3.1"}
The following code assumes:
- being in a browser (ie
js/WebSockets
exists. For Node.jssee below.) - a websocket-enabled MQTT-broker on
localhost:9001
(eg viadocker run --rm -ti -p 9001:9001 toke/mosquitto
)
(nsexample.core (:require-macros [cljs.core.async.macros:refer [go go-loop]]) (:require [cljs.core.async:as a:refer [<!]] [otarta.core:as mqtt]))(defonceclient (mqtt/client"ws://localhost:9001/mqtt#weather-sensor"))(defnsubscription-handler [ch] (go-loop [] (when-let [m (<! ch)];; example m: {:topic "temperature/current" :payload "12.1" :retain? false :qos 0} (prn"Received:" m) (recur))))(go (let [[err {sub-ch:ch}] (<! (mqtt/subscribe client"temperature/#"))] (if err (println"Failed to subscribe:" err) (do (println"Subscribed!") (subscription-handler sub-ch)))) (mqtt/publish client"temperature/current""12.1"))
The first argument (thebroker-url
) should be of the formws(s):://(user:pass@)host.org:1234/path(#some/root/topic)
.
The fragment contains theroot-topic
and indicates the topic relative to which the client publishes and subscribes. This allows for pointing the client to a specific subtree of the broker (eg where it has read/write-permissions, or where it makes sense given the stage:ws://some-broker/mqtt#acceptance/sensor1
).
When you write a client that receives its broker-url from outside (ie as an environment variable), it might lack a root-topic. In order to prevent unwanted effects in that case (eg the client subscribing to "#" essentially subscribing to theroot of the broker) you can provide adefault-root-topic
:
(mqtt/client config.broker-url {:default-root-topic"weather-sensor"})
The client will then treat the broker-urlws://localhost:9001/mqtt
likews://localhost:9001/mqtt#weather-sensor
.Whenconfig.broker-url
does contain aroot-topic
, thedefault-root-topic
is ignored (but gives a nice hint as to what theroot-topic
could look like, egacceptance/weather-sensor
).
Messages have the following shape:
{:topic"temperature/current";; topic relative to `root-topic`:payload"12.1";; formatted payload:retain?false;; whether this message was from the broker's store or 'real-time' from publisher:qos0};; quality of service (0: at most once, 1: at least once, 2: exactly once)
NOTE:retain?
is not so much a property of the sent message, but tells youwhen you received it: typically you receive messages with{:retain? true}
directly after subscribing. But when you're subscribed and a message is published with the retain-flag set, the message you'll received has{:retain? false}
. This as you received it 'first hand' from the publisher, not from the broker's store.
When publishing or subscribing you can specify a format. Available formats are:string
(default),raw
,json
,edn
andtransit
:
(go (let [[err {sub-ch:ch}] (<! (mqtt/subscribe client"temperature/#" {:format:transit}))] (if err (println"Failed to subscribe:" err) (do (prn (<! sub-ch)))));; prints: {:created-at #inst "2018-09-27T13:13:21.932-00:00", :value 12.1} (mqtt/publish client"temperature/current" {:created-at (js/Date.):value12.1} {:format:transit}))
Incoming messages with a payload that is not readable, won't appear on the subscription-channel.
Similarly, when formatting fails when publishing, you'll receive an error:
(let [[err _] (<! (mqtt/publish client"foo"#"not transit!" {:format:transit}))] (when err (println err)))
You can provide your own format:
(nsexample.core (:require [otarta.format:as mqtt-fmt]))(defnextract-temperature []...);; this format piggybacks on the string-format;; after which extract-temperature will get the relevant data.;; Otarta will catch any exceptions that occur when reading/writing.(defcustom-format (reify mqtt-fmt/PayloadFormat (-read [_fmt buff] (->> buff (mqtt-fmt/-read mqtt-fmt/string) extract-temperature)) (-write [_fmt v] (->> v (mqtt-fmt/-write mqtt-fmt/string)))))
You should provide a W3C compatible websocket when running via Node.js.
I've had good experience withthis websocket-library (>= v1.0.28).
With the library included in your project (seehttps://clojurescript.org/guides/webpack for details), the following will initializejs/WebSocket
:
(nsexample.core (:require [websocket]))(set! js/WebSocket (.-w3cwebsocket websocket))
- only QoS 0
- only clean-session
- no reconnect built-in
- untested for large payloads (ie more than a couple of KB)
Viacljs-test-runner:
Viacljs-test-runner:
# once$ clojure -Atest# watching$ clojure -Atest-watch# specific tests(deftest ^{:focus true} only-this-test ...)$ clojure -Atest-watch -i :focus# more options:$ clojure -Atest-watch --help
# start figwheel$ make figwheel# wait till compiled and then from other shell:$ node target/app.js# then from emacs:# M-x cider-connect with host: localhost and port: 7890# from repl:user> (figwheel/cljs-repl);; prompt changes to:cljs.user>;; to quickly see what otarta can do:;; - evaluate the otarta.main namespace;; -theneval the comment-section of otarta.main line by line
SeeCIDER docs what you can do.
- (ensure no CLJ_CONFIG and MAVEN_OPTS env variables are set - this to target ~/.m2)
- ensure dependencies in pom.xml up to date
- clj -Spom
- ensure pom.xml with new version
- cp pom.xml{.template,}
- gsed -i 's/$RELEASE_VERSION/1.2.3/' pom.xml
- make mvn-install
- testdrive locally
- create (pre-)tag
- push to CI
Copyright (c) 2018 Gert Goet, ThinkCreate
Copyright (c) 2018 Alliander N.V.SeeLICENSE.
For licenses of third-party software that this software uses, seeLICENSE-3RD-PARTY.
About
An MQTT-library for ClojureScript
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Packages0
Uh oh!
There was an error while loading.Please reload this page.