diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 93af7c3..19c54b8 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -5,6 +5,7 @@ on: branches: - master - beta + - alpha pull_request: jobs: @@ -20,4 +21,35 @@ jobs: - name: Install depencencies run: npm ci - name: Run tests - run: npm run test \ No newline at end of file + run: npm run test + + release: + needs: build-and-test + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/alpha' # only run on alpha branch for now. + steps: + - name: Checkout + uses: actions/checkout@v1 + - name: Use node 12 + uses: actions/setup-node@v1 + with: + node-version: 12 + - uses: svrooij/secret-gate-action@v1 # see https://github.com/svrooij/secret-gate-action this is to fail quiet until secret is set. + id: mygate + with: + inputsToCheck: 'NPM_TOKEN' + env: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + - name: Install depencencies + if: steps.mygate.outputs.inputsChecked == 'true' + run: npm ci + - name: Run tests + if: steps.mygate.outputs.inputsChecked == 'true' + run: npm run test + - name: Semantic Release + uses: cycjimmy/semantic-release-action@v2 + if: steps.mygate.outputs.inputsChecked == 'true' + id: semantic + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/lib/deviceDiscovery.js b/lib/deviceDiscovery.js index d93c1e7..486c28d 100644 --- a/lib/deviceDiscovery.js +++ b/lib/deviceDiscovery.js @@ -87,6 +87,8 @@ DeviceDiscovery.prototype.destroy = function (callback) { * @param {Object} options Optional Options to control search behavior. * Set 'timeout' to how long to search for devices * (in milliseconds). + * Set 'port' to use a specific inbound UDP port, + * rather than a randomly assigned one * @param {Function} listener Optional 'DeviceAvailable' listener (sonos) * @return {DeviceDiscovery} */ diff --git a/lib/events/adv-listener.js b/lib/events/adv-listener.js index a528c8f..a9b7ce9 100644 --- a/lib/events/adv-listener.js +++ b/lib/events/adv-listener.js @@ -18,7 +18,8 @@ const listenAddress = process.env.SONOS_LISTENER || ip.address('public') const globalEventEndpoints = [ '/AlarmClock/Event', - '/ZoneGroupTopology/Event' + '/ZoneGroupTopology/Event', + '/MediaServer/ContentDirectory/Event' ] const deviceEventEndpoints = [ '/MediaRenderer/AVTransport/Event', diff --git a/lib/events/eventParser.js b/lib/events/eventParser.js index 1680200..65226ff 100644 --- a/lib/events/eventParser.js +++ b/lib/events/eventParser.js @@ -19,6 +19,8 @@ EventParser.ParseAndEmitEvents = async function (endpoint, body, device) { return EventParser._parseGroupRenderingControlEvent(body, device) case '/MediaRenderer/Queue/Event': return EventParser._parseQueueEvent(body, device) + case '/MediaServer/ContentDirectory/Event': + return EventParser._parseContentDirectoryEvent(body, device) default: return EventParser._genericEvent(endpoint, body, device) } @@ -103,6 +105,12 @@ EventParser._genericEvent = function (endpoint, body, device) { return event } +EventParser._parseContentDirectoryEvent = function (body, device) { + debug('ContentDirectory event %j', body) + const event = { name: 'ContentDirectory', eventBody: body } + return event +} + EventParser._parseAlarmEvent = function (body, device) { debug('Alarm event %j', body) const event = { name: 'AlarmClock', eventBody: body } diff --git a/lib/services/ContentDirectory.js b/lib/services/ContentDirectory.js index 993d962..9421a63 100644 --- a/lib/services/ContentDirectory.js +++ b/lib/services/ContentDirectory.js @@ -25,10 +25,6 @@ class ContentDirectory extends Service { this.SCPDURL = '/xml/ContentDirectory1.xml' } - async Browse (options) { return this._request('Browse', options) } - - async DestroyObject (options) { return this._request('DestroyObject', options) } - async _parseResult (input) { input.Result = await Helpers.ParseXml(input.Result) return input @@ -66,6 +62,105 @@ class ContentDirectory extends Service { } }) } + + async Browse (options) { + return this._request('Browse', options) + } + + async DestroyObject (options) { + return this._request('DestroyObject', options) + } + + /** + * See: http://docs.python-soco.com/en/latest/api/soco.music_library.html#soco.music_library.MusicLibrary.album_artist_display_option + * + * Possible values are: + * 'WMP' - use Album Artists + * 'ITUNES' - use iTunesĀ® Compilations + * 'NONE' - do not group compilations + */ + async GetSearchCapabilities () { + return this._request('GetSearchCapabilities', {}).then( + (r) => r.SearchCaps + ) + } + + async GetSortCapabilities () { + return this._request('GetSortCapabilities', {}).then((r) => r.SortCaps) + } + + async GetSystemUpdateID () { + return this._request('GetSystemUpdateID', {}).then((r) => r.Id) + } + + async GetAlbumArtistDisplayOption () { + return this._request('GetAlbumArtistDisplayOption', {}).then( + (r) => r.AlbumArtistDisplayOption + ) + } + + async GetLastIndexChange () { + return this._request('GetLastIndexChange', {}).then( + (r) => r.LastIndexChange + ) + } + + async FindPrefix (ObjectID, Prefix) { + return this._request('FindPrefix', { + ObjectID, + Prefix + }) + } + + async GetAllPrefixLocations (ObjectID) { + return this._request('GetAllPrefixLocations', { + ObjectID + }) + } + + async CreateObject (ContainerID, Elements) { + return this._request('CreateObject', { + ContainerID, + Elements + }) + } + + async UpdateObject (ObjectID, CurrentTagValue) { + return this._request('UpdateObject', { + ObjectID, + CurrentTagValue + }) + } + + async RefreshShareIndex (AlbumArtistDisplayOption = '') { + return this._request('RefreshShareIndex', { + AlbumArtistDisplayOption + }) + } + + async RequestResort (SortOrder) { + return this._request('RequestResort', { + SortOrder + }) + } + + async GetShareIndexInProgress () { + return this._request('GetShareIndexInProgress', {}).then( + (r) => r.IsIndexing !== '0' + ) + } + + async GetBrowseable () { + return this._request('GetBrowseable', {}).then( + (r) => r.IsBrowseable !== '0' + ) + } + + async SetBrowseable (Browseable) { + return this._request('SetBrowseable', { + Browseable + }) + } } module.exports = ContentDirectory diff --git a/lib/services/RenderingControl.js b/lib/services/RenderingControl.js index a5c616f..7f3f3fd 100644 --- a/lib/services/RenderingControl.js +++ b/lib/services/RenderingControl.js @@ -111,13 +111,13 @@ class RenderingControl extends Service { } /** - * (Un)set bass of a speaker. - * @param {boolean} loudness Should it be with or without bass? + * Set bass of a speaker. + * @param {integer} bass desired level of bass, range from -10 to +10 */ async SetBass (bass) { return this._request('SetBass', { InstanceID: 0, - DesiredBass: (bass ? '1' : '0') + DesiredBass: bass }) } @@ -131,13 +131,13 @@ class RenderingControl extends Service { } /** - * (Un)set treble of a speaker. - * @param {boolean} treble Should it be with or without treble? + * Set treble of a speaker. + * @param {integer} treble desired level of treble, range from -10 to +10 */ async SetTreble (treble) { return this._request('SetTreble', { InstanceID: 0, - DesiredTreble: (treble ? '1' : '0') + DesiredTreble: treble }) } @@ -153,7 +153,7 @@ class RenderingControl extends Service { /** * (Un)set room calibration status (TruePlay) of a speaker. - * @param {boolean} enabled Should it be with or without treble? + * @param {boolean} enabled Should it be with or without truePlay? */ async SetRoomCalibrationStatus (enabled) { return this._request('SetRoomCalibrationStatus', { diff --git a/package-lock.json b/package-lock.json index 55b8b95..7757d33 100644 --- a/package-lock.json +++ b/package-lock.json @@ -235,12 +235,6 @@ "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", "dev": true }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true - }, "npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", @@ -2577,9 +2571,9 @@ } }, "lodash": { - "version": "4.17.14", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.14.tgz", - "integrity": "sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw==", + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", + "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", "dev": true }, "lodash.capitalize": { @@ -3108,9 +3102,9 @@ "dev": true }, "npm": { - "version": "6.14.2", - "resolved": "https://registry.npmjs.org/npm/-/npm-6.14.2.tgz", - "integrity": "sha512-eBVjzvGJ9v2/jRJZFtIkvUVKmJ0sCJNNwc9Z1gI6llwaT7EBYWJe5o61Ipc1QR0FaDCKM3l1GizI09Ro3STJEw==", + "version": "6.14.6", + "resolved": "https://registry.npmjs.org/npm/-/npm-6.14.6.tgz", + "integrity": "sha512-axnz6iHFK6WPE0js/+mRp+4IOwpHn5tJEw5KB6FiCU764zmffrhsYHbSHi2kKqNkRBt53XasXjngZfBD3FQzrQ==", "dev": true, "requires": { "JSONStream": "^1.3.5", @@ -3142,7 +3136,7 @@ "fs-write-stream-atomic": "~1.0.10", "gentle-fs": "^2.3.0", "glob": "^7.1.6", - "graceful-fs": "^4.2.3", + "graceful-fs": "^4.2.4", "has-unicode": "~2.0.1", "hosted-git-info": "^2.8.8", "iferr": "^1.0.2", @@ -3179,10 +3173,10 @@ "lru-cache": "^5.1.1", "meant": "~1.0.1", "mississippi": "^3.0.0", - "mkdirp": "~0.5.1", + "mkdirp": "^0.5.5", "move-concurrently": "^1.0.1", "node-gyp": "^5.1.0", - "nopt": "~4.0.1", + "nopt": "^4.0.3", "normalize-package-data": "^2.5.0", "npm-audit-report": "^1.3.2", "npm-cache-filename": "~1.0.2", @@ -3192,7 +3186,7 @@ "npm-packlist": "^1.4.8", "npm-pick-manifest": "^3.0.2", "npm-profile": "^4.0.4", - "npm-registry-fetch": "^4.0.3", + "npm-registry-fetch": "^4.0.5", "npm-user-validate": "~1.0.0", "npmlog": "~4.1.2", "once": "~1.4.0", @@ -3213,7 +3207,7 @@ "readdir-scoped-modules": "^1.1.0", "request": "^2.88.0", "retry": "^0.12.0", - "rimraf": "^2.6.3", + "rimraf": "^2.7.1", "safe-buffer": "^5.1.2", "semver": "^5.7.1", "sha": "^3.0.0", @@ -3820,7 +3814,7 @@ "dev": true }, "deep-extend": { - "version": "0.5.1", + "version": "0.6.0", "bundled": true, "dev": true }, @@ -4365,7 +4359,7 @@ } }, "graceful-fs": { - "version": "4.2.3", + "version": "4.2.4", "bundled": true, "dev": true }, @@ -4543,11 +4537,11 @@ "dev": true }, "is-ci": { - "version": "1.1.0", + "version": "1.2.1", "bundled": true, "dev": true, "requires": { - "ci-info": "^1.0.0" + "ci-info": "^1.5.0" }, "dependencies": { "ci-info": { @@ -4619,7 +4613,7 @@ } }, "is-retry-allowed": { - "version": "1.1.0", + "version": "1.2.0", "bundled": true, "dev": true }, @@ -5091,11 +5085,6 @@ "brace-expansion": "^1.1.7" } }, - "minimist": { - "version": "0.0.8", - "bundled": true, - "dev": true - }, "minizlib": { "version": "1.3.3", "bundled": true, @@ -5133,11 +5122,18 @@ } }, "mkdirp": { - "version": "0.5.1", + "version": "0.5.5", "bundled": true, "dev": true, "requires": { - "minimist": "0.0.8" + "minimist": "^1.2.5" + }, + "dependencies": { + "minimist": { + "version": "1.2.5", + "bundled": true, + "dev": true + } } }, "move-concurrently": { @@ -5204,7 +5200,7 @@ } }, "nopt": { - "version": "4.0.1", + "version": "4.0.3", "bundled": true, "dev": true, "requires": { @@ -5330,7 +5326,7 @@ } }, "npm-registry-fetch": { - "version": "4.0.3", + "version": "4.0.5", "bundled": true, "dev": true, "requires": { @@ -5344,7 +5340,7 @@ }, "dependencies": { "safe-buffer": { - "version": "5.2.0", + "version": "5.2.1", "bundled": true, "dev": true } @@ -5765,18 +5761,18 @@ "dev": true }, "rc": { - "version": "1.2.7", + "version": "1.2.8", "bundled": true, "dev": true, "requires": { - "deep-extend": "^0.5.1", + "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "dependencies": { "minimist": { - "version": "1.2.0", + "version": "1.2.5", "bundled": true, "dev": true } @@ -5856,7 +5852,7 @@ } }, "registry-auth-token": { - "version": "3.3.2", + "version": "3.4.0", "bundled": true, "dev": true, "requires": { @@ -5920,7 +5916,7 @@ "dev": true }, "rimraf": { - "version": "2.6.3", + "version": "2.7.1", "bundled": true, "dev": true, "requires": { @@ -6104,7 +6100,7 @@ } }, "spdx-license-ids": { - "version": "3.0.3", + "version": "3.0.5", "bundled": true, "dev": true }, @@ -6539,7 +6535,7 @@ } }, "widest-line": { - "version": "2.0.0", + "version": "2.0.1", "bundled": true, "dev": true, "requires": { @@ -7519,12 +7515,6 @@ } } }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true - }, "marked": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/marked/-/marked-0.7.0.tgz",