From 9a92e5648dd00501aa4b8b2bdf3b6b4d1204b629 Mon Sep 17 00:00:00 2001 From: Jon Maddox Date: Sat, 19 Sep 2015 01:06:01 -0400 Subject: [PATCH 01/10] add mqtt --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 2461453..3fd90e8 100755 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "body-parser": "^1.12.4", "local-itunes": "^0.3.0", "osa": "2.2.0", - "parameterize": "^0.0.7" + "parameterize": "^0.0.7", + "mqtt": "^1.4.1" } } From dc296dfb90d8a09e55abf55437866659ba0b31dd Mon Sep 17 00:00:00 2001 From: Jon Maddox Date: Sat, 19 Sep 2015 01:06:09 -0400 Subject: [PATCH 02/10] mqtt client --- app.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app.js b/app.js index 6e49f5e..725b573 100755 --- a/app.js +++ b/app.js @@ -1,6 +1,7 @@ var fs = require('fs') var path = require('path') var util = require('util') +var mqtt = require('mqtt'); var express = require('express') var morgan = require('morgan') var bodyParser = require('body-parser') @@ -17,6 +18,7 @@ app.use(express.static(path.join(__dirname, 'public'))); var logFormat = "'[:date[iso]] - :remote-addr - :method :url :status :response-time ms - :res[content-length]b'" app.use(morgan(logFormat)) +var mqttClient = mqtt.connect('mqtt://192.168.1.16'); function getCurrentState(){ itunes = Application('iTunes'); playerState = itunes.playerState(); From 5a3da86484276e5a11cd1612bf6f5bb569c07cd4 Mon Sep 17 00:00:00 2001 From: Jon Maddox Date: Sat, 19 Sep 2015 01:06:18 -0400 Subject: [PATCH 03/10] add a namespace to it's topics --- app.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app.js b/app.js index 725b573..d0fa6c0 100755 --- a/app.js +++ b/app.js @@ -19,6 +19,8 @@ var logFormat = "'[:date[iso]] - :remote-addr - :method :url :status :response-t app.use(morgan(logFormat)) var mqttClient = mqtt.connect('mqtt://192.168.1.16'); +var TOPIC_NAMESPACE = "itunes-api" + function getCurrentState(){ itunes = Application('iTunes'); playerState = itunes.playerState(); From 87342cdb5ec6c58da1152635fc5e81d3081ff057 Mon Sep 17 00:00:00 2001 From: Jon Maddox Date: Sat, 19 Sep 2015 01:06:27 -0400 Subject: [PATCH 04/10] general function to publish to mqtt --- app.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app.js b/app.js index d0fa6c0..bb3d6fe 100755 --- a/app.js +++ b/app.js @@ -133,6 +133,11 @@ function getPlaylists(callback){ }) } +function publish(topic, message, options){ + topic = TOPIC_NAMESPACE + "/" + topic + mqttClient.publish(topic, message, options); +} + app.get('/_ping', function(req, res){ res.send('OK'); }) From 7bc88861ce7129b4a934968b3f556181444e798b Mon Sep 17 00:00:00 2001 From: Jon Maddox Date: Sat, 19 Sep 2015 01:14:41 -0400 Subject: [PATCH 05/10] publish state --- app.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app.js b/app.js index bb3d6fe..a86369a 100755 --- a/app.js +++ b/app.js @@ -148,12 +148,14 @@ app.get('/', function(req, res){ app.put('/play', function(req, res){ iTunes.play(function (error){ + if (!error) publish('state', 'playing', {retain: true}); sendResponse(error, res) }) }) app.put('/pause', function(req, res){ iTunes.pause(function (error){ + if (!error) publish('state', 'paused', {retain: true}); sendResponse(error, res) }) }) @@ -166,6 +168,7 @@ app.put('/playpause', function(req, res){ app.put('/stop', function(req, res){ iTunes.stop(function (error){ + if (!error) publish('state', 'stopped', {retain: true}); sendResponse(error, res) }) }) From 3b8b0aecb4868c3c03704bd0f96c9e67eb5f64fd Mon Sep 17 00:00:00 2001 From: Jon Maddox Date: Sun, 27 Sep 2015 08:18:30 -0400 Subject: [PATCH 06/10] add config --- config/config.sample.json | 3 +++ script/bootstrap | 8 ++++++++ 2 files changed, 11 insertions(+) create mode 100644 config/config.sample.json diff --git a/config/config.sample.json b/config/config.sample.json new file mode 100644 index 0000000..38cb4ca --- /dev/null +++ b/config/config.sample.json @@ -0,0 +1,3 @@ +{ + "mqtt_host": "http://127.0.0.1" +} diff --git a/script/bootstrap b/script/bootstrap index 0f2126b..08829d9 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -18,4 +18,12 @@ mkdir -p log npm install +if [ ! -f config/config.json ] +then + echo + echo "==> Creating your config. Please edit config/config.json." + echo + cp config/config.sample.json config/config.json +fi + echo "Finished setting up itunes-api! run it with script/server or install it with script/install." From a4b119165212889d74ce11cca4fc3443718b312e Mon Sep 17 00:00:00 2001 From: Jon Maddox Date: Sun, 27 Sep 2015 08:18:38 -0400 Subject: [PATCH 07/10] use config for mqtt host --- app.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app.js b/app.js index a86369a..6252427 100755 --- a/app.js +++ b/app.js @@ -11,6 +11,8 @@ var osascript = require(path.join(__dirname, 'node_modules', 'local-itunes', 'no var airplay = require('./lib/airplay') var parameterize = require('parameterize'); +var config = require('./config/config.json'); + var app = express() app.use(bodyParser.urlencoded({ extended: false })) app.use(express.static(path.join(__dirname, 'public'))); @@ -18,7 +20,7 @@ app.use(express.static(path.join(__dirname, 'public'))); var logFormat = "'[:date[iso]] - :remote-addr - :method :url :status :response-time ms - :res[content-length]b'" app.use(morgan(logFormat)) -var mqttClient = mqtt.connect('mqtt://192.168.1.16'); +var mqttClient = mqtt.connect(config.mqtt_host); var TOPIC_NAMESPACE = "itunes-api" function getCurrentState(){ From cb2a60fcf836997181fdb47092284ab9f4282751 Mon Sep 17 00:00:00 2001 From: Jon Maddox Date: Sun, 27 Sep 2015 08:23:41 -0400 Subject: [PATCH 08/10] support all the options for mqtt --- .gitignore | 1 + app.js | 2 +- config/config.sample.json | 7 ++++++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index eb03e3e..de42cc6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules +config/config.json *.log diff --git a/app.js b/app.js index 6252427..e283f2a 100755 --- a/app.js +++ b/app.js @@ -20,7 +20,7 @@ app.use(express.static(path.join(__dirname, 'public'))); var logFormat = "'[:date[iso]] - :remote-addr - :method :url :status :response-time ms - :res[content-length]b'" app.use(morgan(logFormat)) -var mqttClient = mqtt.connect(config.mqtt_host); +var mqttClient = mqtt.connect(config.mqtt); var TOPIC_NAMESPACE = "itunes-api" function getCurrentState(){ diff --git a/config/config.sample.json b/config/config.sample.json index 38cb4ca..4f681a8 100644 --- a/config/config.sample.json +++ b/config/config.sample.json @@ -1,3 +1,8 @@ { - "mqtt_host": "http://127.0.0.1" + "mqtt": { + "host": "127.0.0.1", + "port": "1883", + "username": "", + "password": "" + } } From 451117d1b9ca7bae4181a9a21e19d7d2cb23e99a Mon Sep 17 00:00:00 2001 From: Jon Maddox Date: Sun, 27 Sep 2015 08:37:14 -0400 Subject: [PATCH 09/10] add more mqtt publishing --- app.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app.js b/app.js index e283f2a..2cbaa53 100755 --- a/app.js +++ b/app.js @@ -193,6 +193,7 @@ app.put('/volume', function(req, res){ console.log(error) res.sendStatus(500) }else{ + if (!error) publish('volume', req.body.level, {retain: true}); sendResponse(error, res) } }) @@ -286,6 +287,7 @@ app.put('/airplay_devices/:id/on', function (req, res) { console.log(error) res.sendStatus(500) }else{ + publish('airplay_devices/' + req.params.id + '/state', 'on', {retain: true}); res.json(data) } }) @@ -297,6 +299,7 @@ app.put('/airplay_devices/:id/off', function (req, res) { console.log(error) res.sendStatus(500) }else{ + publish('airplay_devices/' + req.params.id + '/state', 'off', {retain: true}); res.json(data) } }) @@ -308,6 +311,7 @@ app.put('/airplay_devices/:id/volume', function (req, res) { console.log(error) res.sendStatus(500) }else{ + publish('airplay_devices/' + req.params.id + '/volume', req.body.level, {retain: true}); res.json(data) } }) From 0b78ecae5c46ca45fa42cce15c96c43e5e55ede4 Mon Sep 17 00:00:00 2001 From: Jon Maddox Date: Sun, 27 Sep 2015 08:49:21 -0400 Subject: [PATCH 10/10] document mqtt topics in README --- README.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 91bff23..e7201bf 100755 --- a/README.md +++ b/README.md @@ -52,7 +52,25 @@ In `development` mode, it just logs to stdout. Launch the app via `script/server` to run it in the development environment. -## Docs +## MQTT + +itunes-api can report its state changes to your MQTT broker. Just edit your +config file in `config/config.json` to add your MQTT host. + +itunes-api publishes topics with the namespace of: `itunes-api`. + +### Topics + +* `itunes-api/state`, `playing`|`paused`|`stop` - This is published whenever the +state is changed. +* `itunes-api/volume`, `45` - This is published whenever the volume level is +changed. +* `itunes-api/airplay_devices/:airplay_device_id/state`, `on`|`off` - This is +published whenever the state of an airplay device has changed. +* `itunes-api/airplay_devices/:airplay_device_id/volume`, `45` - This is +published whenever the volume level of an airplay device is changed. + +## API Docs This is a quick overview of the service. Read [app.js](app.js) if you need more info.