bencevans / node-sonos

🔈 Sonos Media Player Interface/Client

Home Page:https://www.npmjs.com/package/sonos

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Sonos Boost breaks the module

pierrephi opened this issue · comments

I have a Sonos Boost (bridge) in my network and whenever it is plugged in the module breaks with the following error:

[10.06.2021 22:58.30.537] [ERROR] Error: Request failed with status code 405
    at createError (/Users/pxxxx/Dropbox/Development/MagicMirror/modules/MMM-Sonos/node_modules/axios/lib/core/createError.js:16:15)
    at settle (/Users/pxxxx/Dropbox/Development/MagicMirror/modules/MMM-Sonos/node_modules/axios/lib/core/settle.js:17:12)
    at IncomingMessage.handleStreamEnd (/Users/pxxxx/Dropbox/Development/MagicMirror/modules/MMM-Sonos/node_modules/axios/lib/adapters/http.js:237:11)
    at IncomingMessage.emit (node:events:391:22)
    at endReadableNT (node:internal/streams/readable:1307:12)
    at processTicksAndRejections (node:internal/process/task_queues:81:21) {
  config: {
    url: 'http://192.168.1.162:1400/MediaRenderer/AVTransport/Control',
    method: 'post',
    data: '<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><s:Body><u:GetTransportInfo xmlns:u="urn:schemas-upnp-org:service:AVTransport:1"><InstanceID>0</InstanceID></u:GetTransportInfo></s:Body></s:Envelope>',
    headers: {
      Accept: 'application/json, text/plain, */*',
      'Content-Type': 'text/xml; charset=utf8',
      SOAPAction: '"urn:schemas-upnp-org:service:AVTransport:1#GetTransportInfo"',
      'User-Agent': 'axios/0.19.0',
      'Content-Length': 274
    },
    transformRequest: [ [Function: transformRequest] ],
    transformResponse: [ [Function: transformResponse] ],
    timeout: 0,
    adapter: [Function: httpAdapter],
    xsrfCookieName: 'XSRF-TOKEN',
    xsrfHeaderName: 'X-XSRF-TOKEN',
    maxContentLength: -1,
    validateStatus: [Function: validateStatus]
  },
  request: <ref *1> ClientRequest {
    _events: [Object: null prototype] {
      abort: [Function (anonymous)],
      aborted: [Function (anonymous)],
      error: [Function (anonymous)],
      socket: [Function (anonymous)],
      timeout: [Function (anonymous)],
      prefinish: [Function: requestOnPrefinish]
    },
    _eventsCount: 6,
    _maxListeners: undefined,
    outputData: [],
    outputSize: 0,
    writable: true,
    destroyed: false,
    _last: true,
    chunkedEncoding: false,
    shouldKeepAlive: false,
    _defaultKeepAlive: true,
    useChunkedEncodingByDefault: true,
    sendDate: false,
    _removedConnection: false,
    _removedContLen: false,
    _removedTE: false,
    _contentLength: null,
    _hasBody: true,
    _trailer: '',
    finished: true,
    _headerSent: true,
    _closed: false,
    socket: Socket {
      connecting: false,
      _hadError: false,
      _parent: null,
      _host: null,
      _readableState: [ReadableState],
      _events: [Object: null prototype],
      _eventsCount: 8,
      _maxListeners: undefined,
      _writableState: [WritableState],
      allowHalfOpen: false,
      _sockname: null,
      _pendingData: null,
      _pendingEncoding: '',
      server: null,
      _server: null,
      parser: null,
      _httpMessage: [Circular *1],
      write: [Function: writeAfterFIN],
      [Symbol(async_id_symbol)]: 1118,
      [Symbol(kHandle)]: null,
      [Symbol(kSetNoDelay)]: false,
      [Symbol(lastWriteQueueSize)]: 0,
      [Symbol(timeout)]: null,
      [Symbol(kBuffer)]: null,
      [Symbol(kBufferCb)]: null,
      [Symbol(kBufferGen)]: null,
      [Symbol(kCapture)]: false,
      [Symbol(kBytesRead)]: 252,
      [Symbol(kBytesWritten)]: 574,
      [Symbol(RequestTimeout)]: undefined
    },
    _header: 'POST /MediaRenderer/AVTransport/Control HTTP/1.1\r\n' +
      'Accept: application/json, text/plain, */*\r\n' +
      'Content-Type: text/xml; charset=utf8\r\n' +
      'SOAPAction: "urn:schemas-upnp-org:service:AVTransport:1#GetTransportInfo"\r\n' +
      'User-Agent: axios/0.19.0\r\n' +
      'Content-Length: 274\r\n' +
      'Host: 192.168.1.162:1400\r\n' +
      'Connection: close\r\n' +
      '\r\n',
    _keepAliveTimeout: 0,
    _onPendingData: {},
    agent: Agent {
      _events: [Object: null prototype],
      _eventsCount: 2,
      _maxListeners: undefined,
      defaultPort: 80,
      protocol: 'http:',
      options: [Object],
      requests: {},
      sockets: [Object],
      freeSockets: {},
      keepAliveMsecs: 1000,
      keepAlive: false,
      maxSockets: Infinity,
      maxFreeSockets: 256,
      scheduling: 'lifo',
      maxTotalSockets: Infinity,
      totalSocketCount: 48,
      [Symbol(kCapture)]: false
    },
    socketPath: undefined,
    method: 'POST',
    maxHeaderSize: undefined,
    insecureHTTPParser: undefined,
    path: '/MediaRenderer/AVTransport/Control',
    _ended: true,
    res: IncomingMessage {
      _readableState: [ReadableState],
      _events: [Object: null prototype],
      _eventsCount: 3,
      _maxListeners: undefined,
      socket: [Socket],
      httpVersionMajor: 1,
      httpVersionMinor: 1,
      httpVersion: '1.1',
      complete: true,
      rawHeaders: [Array],
      rawTrailers: [],
      aborted: false,
      upgrade: false,
      url: '',
      method: null,
      statusCode: 405,
      statusMessage: 'Method Not Allowed',
      client: [Socket],
      _consuming: true,
      _dumped: false,
      req: [Circular *1],
      responseUrl: 'http://192.168.1.162:1400/MediaRenderer/AVTransport/Control',
      redirects: [],
      [Symbol(kCapture)]: false,
      [Symbol(kHeaders)]: [Object],
      [Symbol(kHeadersCount)]: 8,
      [Symbol(kTrailers)]: null,
      [Symbol(kTrailersCount)]: 0,
      [Symbol(RequestTimeout)]: undefined
    },
    aborted: false,
    timeoutCb: null,
    upgradeOrConnect: false,
    parser: null,
    maxHeadersCount: null,
    reusedSocket: false,
    host: '192.168.1.162',
    protocol: 'http:',
    _redirectable: Writable {
      _writableState: [WritableState],
      _events: [Object: null prototype],
      _eventsCount: 2,
      _maxListeners: undefined,
      _options: [Object],
      _redirectCount: 0,
      _redirects: [],
      _requestBodyLength: 274,
      _requestBodyBuffers: [],
      _onNativeResponse: [Function (anonymous)],
      _currentRequest: [Circular *1],
      _currentUrl: 'http://192.168.1.162:1400/MediaRenderer/AVTransport/Control',
      [Symbol(kCapture)]: false
    },
    [Symbol(kCapture)]: false,
    [Symbol(kNeedDrain)]: false,
    [Symbol(corked)]: 0,
    [Symbol(kOutHeaders)]: [Object: null prototype] {
      accept: [Array],
      'content-type': [Array],
      soapaction: [Array],
      'user-agent': [Array],
      'content-length': [Array],
      host: [Array]
    }
  },
  response: {
    status: 405,
    statusText: 'Method Not Allowed',
    headers: {
      allow: 'GET, HEAD',
      'content-type': 'text/html',
      server: 'Linux UPnP/1.0 Sonos/63.2-89260 (BR200)',
      connection: 'close'
    },
    config: {
      url: 'http://192.168.1.162:1400/MediaRenderer/AVTransport/Control',
      method: 'post',
      data: '<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><s:Body><u:GetTransportInfo xmlns:u="urn:schemas-upnp-org:service:AVTransport:1"><InstanceID>0</InstanceID></u:GetTransportInfo></s:Body></s:Envelope>',
      headers: [Object],
      transformRequest: [Array],
      transformResponse: [Array],
      timeout: 0,
      adapter: [Function: httpAdapter],
      xsrfCookieName: 'XSRF-TOKEN',
      xsrfHeaderName: 'X-XSRF-TOKEN',
      maxContentLength: -1,
      validateStatus: [Function: validateStatus]
    },
    request: <ref *1> ClientRequest {
      _events: [Object: null prototype],
      _eventsCount: 6,
      _maxListeners: undefined,
      outputData: [],
      outputSize: 0,
      writable: true,
      destroyed: false,
      _last: true,
      chunkedEncoding: false,
      shouldKeepAlive: false,
      _defaultKeepAlive: true,
      useChunkedEncodingByDefault: true,
      sendDate: false,
      _removedConnection: false,
      _removedContLen: false,
      _removedTE: false,
      _contentLength: null,
      _hasBody: true,
      _trailer: '',
      finished: true,
      _headerSent: true,
      _closed: false,
      socket: [Socket],
      _header: 'POST /MediaRenderer/AVTransport/Control HTTP/1.1\r\n' +
        'Accept: application/json, text/plain, */*\r\n' +
        'Content-Type: text/xml; charset=utf8\r\n' +
        'SOAPAction: "urn:schemas-upnp-org:service:AVTransport:1#GetTransportInfo"\r\n' +
        'User-Agent: axios/0.19.0\r\n' +
        'Content-Length: 274\r\n' +
        'Host: 192.168.1.162:1400\r\n' +
        'Connection: close\r\n' +
        '\r\n',
      _keepAliveTimeout: 0,
      _onPendingData: {},
      agent: [Agent],
      socketPath: undefined,
      method: 'POST',
      maxHeaderSize: undefined,
      insecureHTTPParser: undefined,
      path: '/MediaRenderer/AVTransport/Control',
      _ended: true,
      res: [IncomingMessage],
      aborted: false,
      timeoutCb: null,
      upgradeOrConnect: false,
      parser: null,
      maxHeadersCount: null,
      reusedSocket: false,
      host: '192.168.1.162',
      protocol: 'http:',
      _redirectable: [Writable],
      [Symbol(kCapture)]: false,
      [Symbol(kNeedDrain)]: false,
      [Symbol(corked)]: 0,
      [Symbol(kOutHeaders)]: [Object: null prototype]
    },
    data: '<HTML><HEAD><TITLE>Error 405</TITLE></HEAD><BODY><H1>Error 405</H1><P>Method Not Allowed</P></BODY></HTML>'
  },
  isAxiosError: true,
  toJSON: [Function (anonymous)]
}

A command is sent to the bridge which is not supported

Current Behavior

Ideally bridges should be ignored as they cannot be piloted, they are just here to manage the Sonos network

Possible Solution

I suggest filtering the bridges in the network in ZoneGroupTopology.prototype.AllZoneGroups

by returning
return groups.filter(z => {return !z.ZoneGroupMember[0].hasOwnProperty("IsZoneBridge")})

Versions (and Environment)

Node version: v15.7.0
node-sonos version: 1.12.5
OS: MacOS, Raspberry PI OS

Why would you control a bridge?

care to create a PR for this? Can the app using this library not just filter out the boost (with the possibly good filter method above).

It would be nice to filter out non-players in most cases. But you can still rename a boost for instance. So maybe optional parameter to filter them out.

yep that's why the proposal is not to filter on the name "Boost" but rather the specific property that is only present for Boost and the older brides
hasOwnProperty("IsZoneBridge")
I'll go ahead a submit a PR then

I mean, the use case to return the boost is that you can control some things off it. Hiding them always might not be a good idea.

can you make the PR to the alpha branch?