Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can't start container via rest template since HA 2023.6 #127

Open
jschaeke opened this issue Jun 16, 2023 · 26 comments
Open

Can't start container via rest template since HA 2023.6 #127

jschaeke opened this issue Jun 16, 2023 · 26 comments
Labels
bug Something isn't working

Comments

@jschaeke
Copy link

More info on https://community.home-assistant.io/t/rest-switch-stopped-working/581188

@raulibi
Copy link

raulibi commented Jun 16, 2023

Same problem

@evoteknic
Copy link

I get the same error

@philhawthorne
Copy link
Owner

philhawthorne commented Jun 17, 2023

Thanks for reporting.

There might have been a change to the rest sensor in home assistant. I vaguely remember talking about it on a podcast episode recently, but could be mistaken.

I am using MQTT discovery via the dev branch. I'll see if I can replicate using a rest yaml switch to see what is up.

@philhawthorne philhawthorne added the bug Something isn't working label Jun 17, 2023
@mitchellross
Copy link

mitchellross commented Jun 20, 2023

Log:

Logger: homeassistant.components.rest.switch
Source: components/rest/switch.py:180
Integration: RESTful (documentation, issues)
First occurred: 10:37:07 AM (1 occurrences)
Last logged: 10:37:07 AM

Error while switching off http://local.ip:8126/container/frigate

Not sure that's particularly helpful?

@philhawthorne
Copy link
Owner

Thanks @mitchellross

I can see the same for me at the moment.

The server I'm testing with doesn't seem to be working at all for me at the moment (even via Postman) so need to do a bit further digging.

I changed the command to use webhook.site. I suspect Home Assistant is posting the data raw, and not in the same way it used to (ie JSON or HTTP post fields)

The hunt continues for the moment

@mitchellross
Copy link

mitchellross commented Jun 20, 2023 via email

@philhawthorne
Copy link
Owner

philhawthorne commented Jun 20, 2023

Have nailed this down to Home Assistant no longer sending the content-type header application/octet-stream.

2023.5

curl -X 'POST' 'http://192.168.1.x:8126/container/xxx' -H 'connection: close' -H 'content-type: application/octet-stream' -H 'content-length: 18' -H 'accept-encoding: gzip, deflate' -H 'accept: */*' -H 'user-agent: HomeAssistant/2023.5.4 aiohttp/3.8.4 Python/3.10' -H 'host: 192.168.1.x' -d $'{"state": "start"}'

2023.6

curl -X 'POST' 'http://192.168.1.x:8126/container/xxx' -H 'connection: close' -H 'content-length: 18' -H 'user-agent: HomeAssistant/2023.6.1 httpx/0.24.1 Python/3.11' -H 'accept-encoding: gzip, deflate, br' -H 'accept: */*' -H 'host: 192.168.1.x' -H 'content-type: ' -d $'{"state": "start"}'

This could be a bug introduced when moving from aiohttp to httpx, which was done in this PR: home-assistant/core#90768

Interestingly, as @jschaeke pointed out, adding the header back to the YAML doesn't seem to fix the switch, which sounds like this new library Home Assistant is using is removing that header.

I'll raise this as an issue in Home Assistant core and see what the Home Assistant devs think here. There might be other REST sensors/switches etc that are now broken/unsupported. If the devs decide on that side they won't put a change in, then I'll update HA-Dockermon to be compatible with 2023.6 moving forward.

@philhawthorne
Copy link
Owner

2023.6 Workaround

switch:
  - platform: rest
      resource: http://192.168.1.x:8126/container/xxx
      name: XXX Test
      body_on: '{"state": "start"}'
      body_off: '{"state": "stop"}'
      is_on_template: '{{ value_json is not none and value_json.state == "running" }}'
      headers:
        content-type: 'application/octet-stream'

Seems the content-type header is case sensitive, and in the case of HA-Dockermon expects it to be sent the same way 2023.5 would send it, lowercase.

@jschaeke
Copy link
Author

Workaround works indeed, I tried it indeed like that before, but probably, like you said, it had wrong capitalized casing.

@hitesh-singh
Copy link

2023.6 Workaround

switch:
  - platform: rest
      resource: http://192.168.1.x:8126/container/xxx
      name: XXX Test
      body_on: '{"state": "start"}'
      body_off: '{"state": "stop"}'
      is_on_template: '{{ value_json is not none and value_json.state == "running" }}'
      headers:
        content-type: 'application/octet-stream'

Seems the content-type header is case sensitive, and in the case of HA-Dockermon expects it to be sent the same way 2023.5 would send it, lowercase.

Thanks much @philhawthorne, this works fine

@TomAutoHome
Copy link

2023.6 Workaround

switch:
  - platform: rest
      resource: http://192.168.1.x:8126/container/xxx
      name: XXX Test
      body_on: '{"state": "start"}'
      body_off: '{"state": "stop"}'
      is_on_template: '{{ value_json is not none and value_json.state == "running" }}'
      headers:
        content-type: 'application/octet-stream'

Seems the content-type header is case sensitive, and in the case of HA-Dockermon expects it to be sent the same way 2023.5 would send it, lowercase.

My automations using ha-dockermon and rest switch work back again, thx @philhawthorne
Since I was using these switches with my overnight automation only, I found issues about a week ago. Not know even on what version of HA it happened first time. I discovered it on 2023.6.3

@rdlvm
Copy link

rdlvm commented Aug 23, 2023

Same problem and also error in the HomeAssistant log:

ERROR (MainThread) [homeassistant.helpers.template] Template variable error: 'value_json' is undefined when rendering '{{ value_json is not none and value_json.state == "running" }}'

Trying the previous solution and it is not effective, I get an error when deactivating or activating switch:

ERROR (MainThread) [homeassistant.components.rest.switch] Error while switching off http://192.168.1.15:8126/container/tasmoadmin

@vks007
Copy link

vks007 commented Oct 23, 2023

Is this issue resolved or not, I am not able to run the start/stop operation by following the workaround mentioned above.
I get an error Error while switching off http://192.XXX.XXX.XXX:8126/container/frigate
My configuration:

switch:
  - platform: rest
    resource: http://192.XXX.XXX.XXX:8126/container/frigate
    method: post
    name: Frigate
    username: redacted
    password: redacted
    body_on: '{"state": "start"}'
    body_off: '{"state": "stop"}'
    is_on_template: '{{ value_json is not none and value_json.state == "running" }}'
    headers:
      content-type: 'application/octet-stream'

I have tried with and without method:post
The switch gets the state of the container fine but fails to either start or stop the same. I am on HA Core version 2023.10.3

@rdlvm
Copy link

rdlvm commented Nov 17, 2023

Is this issue resolved or not, I am not able to run the start/stop operation by following the workaround mentioned above. I get an error Error while switching off http://192.XXX.XXX.XXX:8126/container/frigate My configuration:

switch:
  - platform: rest
    resource: http://192.XXX.XXX.XXX:8126/container/frigate
    method: post
    name: Frigate
    username: redacted
    password: redacted
    body_on: '{"state": "start"}'
    body_off: '{"state": "stop"}'
    is_on_template: '{{ value_json is not none and value_json.state == "running" }}'
    headers:
      content-type: 'application/octet-stream'

I have tried with and without method:post The switch gets the state of the container fine but fails to either start or stop the same. I am on HA Core version 2023.10.3

same problem.

@philhawthorne
Copy link
Owner

philhawthorne commented Nov 17, 2023

Just going to re-open this while the image hasn't been completely pushed. Seems as though Travis doesn't offer Open Source for free anymore, so need to migrate the builds to GitHub actions or CircleCI.

There is a new commit that should change ha-dockernon to respond to application/json calls. There is a change required to your YAML configuration for this to work:

headers:
  Content-Type: application/json

So a full switch might look like

switch:
    - platform: rest
      resource: http://127.0.0.1:8126/container/adguard
      name: Adguard
      body_on: '{"state": "start"}'
      body_off: '{"state": "stop"}'
      headers:
        Content-Type: application/json
      is_on_template: '{{ value_json is not none and value_json.state == "running" }}'

This is in line with the documentation for the Rest switch on the Home Assistant website.

I've manually pushed up a built image and tagged it as json. A docker run command is below. If as many people here can test and if it looks good, I'll roll it out to the latest tags.

docker run -d \
--name=ha-dockermon --restart=always \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /path/to/config:/config \
-p 8126:8126 \
philhawthorne/ha-dockermon:json

@philhawthorne philhawthorne reopened this Nov 17, 2023
@TomAutoHome
Copy link

Well, weird, it works well since 2023.6 with application/octet-stream for me. I am currently on 2023.10.5.
But maybe, I did not encounter this problem yet.

@Flight777
Copy link

Flight777 commented Nov 17, 2023

Get request is working fine for me, but Post I keep getting 400 errors (UNRAID):

rawTrailers: [], joinDuplicateHeaders: undefined, aborted: false, upgrade: false, url: '/container/calibre', method: 'POST', statusCode: null, statusMessage: null, client: <ref *1> Socket { connecting: false, _hadError: false, _parent: null, _host: null, _closeAfterHandlingError: false, _readableState: ReadableState { objectMode: false, highWaterMark: 16384, buffer: BufferList { head: null, tail: null, length: 0 }, length: 0, pipes: [], flowing: true, ended: false, endEmitted: false, reading: true, constructed: true, sync: false, needReadable: true, emittedReadable: false, readableListening: false, resumeScheduled: false, errorEmitted: false, emitClose: false, autoDestroy: true, destroyed: false, errored: null, closed: false, closeEmitted: false, defaultEncoding: 'utf8', awaitDrainWriters: null, multiAwaitDrain: false, readingMore: false, dataEmitted: false, decoder: null, encoding: null, [Symbol(kPaused)]: false }, _events: [Object: null prototype] { end: [Array], timeout: [Function: socketOnTimeout], data: [Function: bound socketOnData], error: [Function: socketOnError], close: [Array], drain: [Function: bound socketOnDrain], resume: [Function: onSocketResume], pause: [Function: onSocketPause] }, _eventsCount: 8, _maxListeners: undefined, _writableState: WritableState { objectMode: false, highWaterMark: 16384, finalCalled: false, needDrain: false, ending: false, ended: false, finished: false, destroyed: false, decodeStrings: false, defaultEncoding: 'utf8', length: 0, writing: false, corked: 0, sync: true, bufferProcessing: false, onwrite: [Function: bound onwrite], writecb: null, writelen: 0, afterWriteTickInfo: null, buffered: [], bufferedIndex: 0, allBuffers: true, allNoop: true, pendingcb: 0, constructed: true, prefinished: false, errorEmitted: false, emitClose: false, autoDestroy: true, errored: null, closed: false, closeEmitted: false, [Symbol(kOnFinished)]: [] }, allowHalfOpen: true, _sockname: null, _pendingData: null, _pendingEncoding: '', server: Server { maxHeaderSize: undefined, insecureHTTPParser: undefined, requestTimeout: 300000, headersTimeout: 60000, keepAliveTimeout: 5000, connectionsCheckingInterval: 30000, joinDuplicateHeaders: undefined, _events: [Object: null prototype], _eventsCount: 2, _maxListeners: undefined, _connections: 1, _handle: [TCP], _usingWorkers: false, _workers: [], _unref: false, allowHalfOpen: true, pauseOnConnect: false, noDelay: true, keepAlive: false, keepAliveInitialDelay: 0, httpAllowHalfOpen: false, timeout: 0, maxHeadersCount: null, maxRequestsPerSocket: 0, _connectionKey: '6::::8126', [Symbol(IncomingMessage)]: [Function: IncomingMessage], [Symbol(ServerResponse)]: [Function: ServerResponse], [Symbol(kCapture)]: false, [Symbol(async_id_symbol)]: 6, [Symbol(http.server.connections)]: ConnectionsList {}, [Symbol(http.server.connectionsCheckingInterval)]: Timeout { _idleTimeout: 30000, _idlePrev: [TimersList], _idleNext: [TimersList], _idleStart: 60168, _onTimeout: [Function: bound checkConnections], _timerArgs: undefined, _repeat: 30000, _destroyed: false, [Symbol(refed)]: false, [Symbol(kHasPrimitive)]: false, [Symbol(asyncId)]: 5, [Symbol(triggerId)]: 1 }, [Symbol(kUniqueHeaders)]: null }, _server: Server { maxHeaderSize: undefined, insecureHTTPParser: undefined, requestTimeout: 300000, headersTimeout: 60000, keepAliveTimeout: 5000, connectionsCheckingInterval: 30000, joinDuplicateHeaders: undefined, _events: [Object: null prototype], _eventsCount: 2, _maxListeners: undefined, _connections: 1, _handle: [TCP], _usingWorkers: false, _workers: [], _unref: false, allowHalfOpen: true, pauseOnConnect: false, noDelay: true, keepAlive: false, keepAliveInitialDelay: 0, httpAllowHalfOpen: false, timeout: 0, maxHeadersCount: null, maxRequestsPerSocket: 0, _connectionKey: '6::::8126', [Symbol(IncomingMessage)]: [Function: IncomingMessage], [Symbol(ServerResponse)]: [Function: ServerResponse], [Symbol(kCapture)]: false, [Symbol(async_id_symbol)]: 6, [Symbol(http.server.connections)]: ConnectionsList {}, [Symbol(http.server.connectionsCheckingInterval)]: Timeout { _idleTimeout: 30000, _idlePrev: [TimersList], _idleNext: [TimersList], _idleStart: 60168, _onTimeout: [Function: bound checkConnections], _timerArgs: undefined, _repeat: 30000, _destroyed: false, [Symbol(refed)]: false, [Symbol(kHasPrimitive)]: false, [Symbol(asyncId)]: 5, [Symbol(triggerId)]: 1 }, [Symbol(kUniqueHeaders)]: null }, parser: HTTPParser { '0': null, '1': [Function: parserOnHeaders], '2': [Function: parserOnHeadersComplete], '3': [Function: parserOnBody], '4': [Function: parserOnMessageComplete], '5': [Function: bound onParserExecute], '6': [Function: bound onParserTimeout], _headers: [], _url: '', socket: [Circular *1], incoming: [Circular *2], outgoing: null, maxHeaderPairs: 2000, _consumed: true, onIncoming: [Function: bound parserOnIncoming], joinDuplicateHeaders: undefined, [Symbol(resource_symbol)]: [HTTPServerAsyncResource] }, on: [Function: socketListenerWrap], addListener: [Function: socketListenerWrap], prependListener: [Function: socketListenerWrap], setEncoding: [Function: socketSetEncoding], _paused: false, _httpMessage: ServerResponse { _events: [Object: null prototype], _eventsCount: 1, _maxListeners: undefined, outputData: [], outputSize: 0, writable: true, destroyed: false, _last: false, chunkedEncoding: false, shouldKeepAlive: true, maxRequestsOnConnectionReached: false, _defaultKeepAlive: true, useChunkedEncodingByDefault: true, sendDate: true, _removedConnection: false, _removedContLen: false, _removedTE: false, strictContentLength: false, _contentLength: null, _hasBody: true, _trailer: '', finished: false, _headerSent: false, _closed: false, socket: [Circular *1], _header: null, _keepAliveTimeout: 5000, _onPendingData: [Function: bound updateOutgoingData], req: [Circular *2], _sent100: false, _expect_continue: false, _maxRequestsPerSocket: 0, locals: [Object: null prototype] {}, [Symbol(kCapture)]: false, [Symbol(kBytesWritten)]: 0, [Symbol(kNeedDrain)]: false, [Symbol(corked)]: 0, [Symbol(kOutHeaders)]: [Object: null prototype], [Symbol(errored)]: null, [Symbol(kUniqueHeaders)]: null }, [Symbol(async_id_symbol)]: 168, [Symbol(kHandle)]: TCP { reading: true, onconnection: null, _consumed: true, [Symbol(owner_symbol)]: [Circular *1] }, [Symbol(lastWriteQueueSize)]: 0, [Symbol(timeout)]: null, [Symbol(kBuffer)]: null, [Symbol(kBufferCb)]: null, [Symbol(kBufferGen)]: null, [Symbol(kCapture)]: false, [Symbol(kSetNoDelay)]: true, [Symbol(kSetKeepAlive)]: false, [Symbol(kSetKeepAliveInitialDelay)]: 0, [Symbol(kBytesRead)]: 0, [Symbol(kBytesWritten)]: 0 }, _consuming: false, _dumped: false, next: [Function: next], baseUrl: '', originalUrl: '/container/calibre', _parsedUrl: Url { protocol: null, slashes: null, auth: null, host: null, port: null, hostname: null, hash: null, search: null, query: null, pathname: '/container/calibre', path: '/container/calibre', href: '/container/calibre', _raw: '/container/calibre' }, params: { containerId: 'calibre' }, query: {}, res: <ref *3> ServerResponse { _events: [Object: null prototype] { finish: [Function: bound resOnFinish] }, _eventsCount: 1, _maxListeners: undefined, outputData: [], outputSize: 0, writable: true, destroyed: false, _last: false, chunkedEncoding: false, shouldKeepAlive: true, maxRequestsOnConnectionReached: false, _defaultKeepAlive: true, useChunkedEncodingByDefault: true, sendDate: true, _removedConnection: false, _removedContLen: false, _removedTE: false, strictContentLength: false, _contentLength: null, _hasBody: true, _trailer: '', finished: false, _headerSent: false, _closed: false, socket: <ref *1> Socket { connecting: false, _hadError: false, _parent: null, _host: null, _closeAfterHandlingError: false, _readableState: [ReadableState], _events: [Object: null prototype], _eventsCount: 8, _maxListeners: undefined, _writableState: [WritableState], allowHalfOpen: true, _sockname: null, _pendingData: null, _pendingEncoding: '', server: [Server], _server: [Server], parser: [HTTPParser], on: [Function: socketListenerWrap], addListener: [Function: socketListenerWrap], prependListener: [Function: socketListenerWrap], setEncoding: [Function: socketSetEncoding], _paused: false, _httpMessage: [Circular *3], [Symbol(async_id_symbol)]: 168, [Symbol(kHandle)]: [TCP], [Symbol(lastWriteQueueSize)]: 0, [Symbol(timeout)]: null, [Symbol(kBuffer)]: null, [Symbol(kBufferCb)]: null, [Symbol(kBufferGen)]: null, [Symbol(kCapture)]: false, [Symbol(kSetNoDelay)]: true, [Symbol(kSetKeepAlive)]: false, [Symbol(kSetKeepAliveInitialDelay)]: 0, [Symbol(kBytesRead)]: 0, [Symbol(kBytesWritten)]: 0 }, _header: null, _keepAliveTimeout: 5000, _onPendingData: [Function: bound updateOutgoingData], req: [Circular *2], _sent100: false, _expect_continue: false, _maxRequestsPerSocket: 0, locals: [Object: null prototype] {}, [Symbol(kCapture)]: false, [Symbol(kBytesWritten)]: 0, [Symbol(kNeedDrain)]: false, [Symbol(corked)]: 0, [Symbol(kOutHeaders)]: [Object: null prototype] { 'x-powered-by': [Array] }, [Symbol(errored)]: null, [Symbol(kUniqueHeaders)]: null }, body: {}, route: Route { path: '/container/:containerId', stack: [ [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer] ], methods: { acl: true, bind: true, checkout: true, connect: true, copy: true, delete: true, get: true, head: true, link: true, lock: true, 'm-search': true, merge: true, mkactivity: true, mkcalendar: true, mkcol: true, move: true, notify: true, options: true, patch: true, post: true, propfind: true, proppatch: true, purge: true, put: true, rebind: true, report: true, search: true, source: true, subscribe: true, trace: true, unbind: true, unlink: true, unlock: true, unsubscribe: true } }, [Symbol(kCapture)]: false, [Symbol(kHeaders)]: { 'content-type': 'application/json', 'postman-token': 'e9e500c3-f1c0-4207-8f44-5f5ce5ddcd44' }, [Symbol(kHeadersCount)]: 4, [Symbol(kTrailers)]: null, [Symbol(kTrailersCount)]: 0 } {}

@rdlvm
Copy link

rdlvm commented Nov 19, 2023

Just going to re-open this while the image hasn't been completely pushed. Seems as though Travis doesn't offer Open Source for free anymore, so need to migrate the builds to GitHub actions or CircleCI.

There is a new commit that should change ha-dockernon to respond to application/json calls. There is a change required to your YAML configuration for this to work:

headers:
  Content-Type: application/json

So a full switch might look like

switch:
    - platform: rest
      resource: http://127.0.0.1:8126/container/adguard
      name: Adguard
      body_on: '{"state": "start"}'
      body_off: '{"state": "stop"}'
      headers:
        Content-Type: application/json
      is_on_template: '{{ value_json is not none and value_json.state == "running" }}'

This is in line with the documentation for the Rest switch on the Home Assistant website.

I've manually pushed up a built image and tagged it as json. A docker run command is below. If as many people here can test and if it looks good, I'll roll it out to the latest tags.

docker run -d \
--name=ha-dockermon --restart=always \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /path/to/config:/config \
-p 8126:8126 \
philhawthorne/ha-dockermon:json

I couldn't try to install HA-Dockermon in json on my docker raspberry pi 4b, when starting I get an error:
exec /usr/local/bin/docker-entrypoint.sh: exec format error

@philhawthorne
Copy link
Owner

Hmm unfortunately with Travis no longer having a free tier (and me not updating it) I haven't got ARM builds running yet :(

@rdlvm
Copy link

rdlvm commented Nov 19, 2023

Hmm unfortunately with Travis no longer having a free tier (and me not updating it) I haven't got ARM builds running yet :(

This means that there is no solution for raspberry pi 4 at the moment.

@Flight777
Copy link

Get request is working fine for me, but Post I keep getting 400 errors (UNRAID):

rawTrailers: [], joinDuplicateHeaders: undefined, aborted: false, upgrade: false, url: '/container/calibre', method: 'POST', statusCode: null, statusMessage: null, client: <ref *1> Socket { connecting: false, _hadError: false, _parent: null, _host: null, _closeAfterHandlingError: false, _readableState: ReadableState { objectMode: false, highWaterMark: 16384, buffer: BufferList { head: null, tail: null, length: 0 }, length: 0, pipes: [], flowing: true, ended: false, endEmitted: false, reading: true, constructed: true, sync: false, needReadable: true, emittedReadable: false, readableListening: false, resumeScheduled: false, errorEmitted: false, emitClose: false, autoDestroy: true, destroyed: false, errored: null, closed: false, closeEmitted: false, defaultEncoding: 'utf8', awaitDrainWriters: null, multiAwaitDrain: false, readingMore: false, dataEmitted: false, decoder: null, encoding: null, [Symbol(kPaused)]: false }, _events: [Object: null prototype] { end: [Array], timeout: [Function: socketOnTimeout], data: [Function: bound socketOnData], error: [Function: socketOnError], close: [Array], drain: [Function: bound socketOnDrain], resume: [Function: onSocketResume], pause: [Function: onSocketPause] }, _eventsCount: 8, _maxListeners: undefined, _writableState: WritableState { objectMode: false, highWaterMark: 16384, finalCalled: false, needDrain: false, ending: false, ended: false, finished: false, destroyed: false, decodeStrings: false, defaultEncoding: 'utf8', length: 0, writing: false, corked: 0, sync: true, bufferProcessing: false, onwrite: [Function: bound onwrite], writecb: null, writelen: 0, afterWriteTickInfo: null, buffered: [], bufferedIndex: 0, allBuffers: true, allNoop: true, pendingcb: 0, constructed: true, prefinished: false, errorEmitted: false, emitClose: false, autoDestroy: true, errored: null, closed: false, closeEmitted: false, [Symbol(kOnFinished)]: [] }, allowHalfOpen: true, _sockname: null, _pendingData: null, _pendingEncoding: '', server: Server { maxHeaderSize: undefined, insecureHTTPParser: undefined, requestTimeout: 300000, headersTimeout: 60000, keepAliveTimeout: 5000, connectionsCheckingInterval: 30000, joinDuplicateHeaders: undefined, _events: [Object: null prototype], _eventsCount: 2, _maxListeners: undefined, _connections: 1, _handle: [TCP], _usingWorkers: false, _workers: [], _unref: false, allowHalfOpen: true, pauseOnConnect: false, noDelay: true, keepAlive: false, keepAliveInitialDelay: 0, httpAllowHalfOpen: false, timeout: 0, maxHeadersCount: null, maxRequestsPerSocket: 0, _connectionKey: '6::::8126', [Symbol(IncomingMessage)]: [Function: IncomingMessage], [Symbol(ServerResponse)]: [Function: ServerResponse], [Symbol(kCapture)]: false, [Symbol(async_id_symbol)]: 6, [Symbol(http.server.connections)]: ConnectionsList {}, [Symbol(http.server.connectionsCheckingInterval)]: Timeout { _idleTimeout: 30000, _idlePrev: [TimersList], _idleNext: [TimersList], _idleStart: 60168, _onTimeout: [Function: bound checkConnections], _timerArgs: undefined, _repeat: 30000, _destroyed: false, [Symbol(refed)]: false, [Symbol(kHasPrimitive)]: false, [Symbol(asyncId)]: 5, [Symbol(triggerId)]: 1 }, [Symbol(kUniqueHeaders)]: null }, _server: Server { maxHeaderSize: undefined, insecureHTTPParser: undefined, requestTimeout: 300000, headersTimeout: 60000, keepAliveTimeout: 5000, connectionsCheckingInterval: 30000, joinDuplicateHeaders: undefined, _events: [Object: null prototype], _eventsCount: 2, _maxListeners: undefined, _connections: 1, _handle: [TCP], _usingWorkers: false, _workers: [], _unref: false, allowHalfOpen: true, pauseOnConnect: false, noDelay: true, keepAlive: false, keepAliveInitialDelay: 0, httpAllowHalfOpen: false, timeout: 0, maxHeadersCount: null, maxRequestsPerSocket: 0, _connectionKey: '6::::8126', [Symbol(IncomingMessage)]: [Function: IncomingMessage], [Symbol(ServerResponse)]: [Function: ServerResponse], [Symbol(kCapture)]: false, [Symbol(async_id_symbol)]: 6, [Symbol(http.server.connections)]: ConnectionsList {}, [Symbol(http.server.connectionsCheckingInterval)]: Timeout { _idleTimeout: 30000, _idlePrev: [TimersList], _idleNext: [TimersList], _idleStart: 60168, _onTimeout: [Function: bound checkConnections], _timerArgs: undefined, _repeat: 30000, _destroyed: false, [Symbol(refed)]: false, [Symbol(kHasPrimitive)]: false, [Symbol(asyncId)]: 5, [Symbol(triggerId)]: 1 }, [Symbol(kUniqueHeaders)]: null }, parser: HTTPParser { '0': null, '1': [Function: parserOnHeaders], '2': [Function: parserOnHeadersComplete], '3': [Function: parserOnBody], '4': [Function: parserOnMessageComplete], '5': [Function: bound onParserExecute], '6': [Function: bound onParserTimeout], _headers: [], _url: '', socket: [Circular *1], incoming: [Circular *2], outgoing: null, maxHeaderPairs: 2000, _consumed: true, onIncoming: [Function: bound parserOnIncoming], joinDuplicateHeaders: undefined, [Symbol(resource_symbol)]: [HTTPServerAsyncResource] }, on: [Function: socketListenerWrap], addListener: [Function: socketListenerWrap], prependListener: [Function: socketListenerWrap], setEncoding: [Function: socketSetEncoding], _paused: false, _httpMessage: ServerResponse { _events: [Object: null prototype], _eventsCount: 1, _maxListeners: undefined, outputData: [], outputSize: 0, writable: true, destroyed: false, _last: false, chunkedEncoding: false, shouldKeepAlive: true, maxRequestsOnConnectionReached: false, _defaultKeepAlive: true, useChunkedEncodingByDefault: true, sendDate: true, _removedConnection: false, _removedContLen: false, _removedTE: false, strictContentLength: false, _contentLength: null, _hasBody: true, _trailer: '', finished: false, _headerSent: false, _closed: false, socket: [Circular *1], _header: null, _keepAliveTimeout: 5000, _onPendingData: [Function: bound updateOutgoingData], req: [Circular *2], _sent100: false, _expect_continue: false, _maxRequestsPerSocket: 0, locals: [Object: null prototype] {}, [Symbol(kCapture)]: false, [Symbol(kBytesWritten)]: 0, [Symbol(kNeedDrain)]: false, [Symbol(corked)]: 0, [Symbol(kOutHeaders)]: [Object: null prototype], [Symbol(errored)]: null, [Symbol(kUniqueHeaders)]: null }, [Symbol(async_id_symbol)]: 168, [Symbol(kHandle)]: TCP { reading: true, onconnection: null, _consumed: true, [Symbol(owner_symbol)]: [Circular *1] }, [Symbol(lastWriteQueueSize)]: 0, [Symbol(timeout)]: null, [Symbol(kBuffer)]: null, [Symbol(kBufferCb)]: null, [Symbol(kBufferGen)]: null, [Symbol(kCapture)]: false, [Symbol(kSetNoDelay)]: true, [Symbol(kSetKeepAlive)]: false, [Symbol(kSetKeepAliveInitialDelay)]: 0, [Symbol(kBytesRead)]: 0, [Symbol(kBytesWritten)]: 0 }, _consuming: false, _dumped: false, next: [Function: next], baseUrl: '', originalUrl: '/container/calibre', _parsedUrl: Url { protocol: null, slashes: null, auth: null, host: null, port: null, hostname: null, hash: null, search: null, query: null, pathname: '/container/calibre', path: '/container/calibre', href: '/container/calibre', _raw: '/container/calibre' }, params: { containerId: 'calibre' }, query: {}, res: <ref *3> ServerResponse { _events: [Object: null prototype] { finish: [Function: bound resOnFinish] }, _eventsCount: 1, _maxListeners: undefined, outputData: [], outputSize: 0, writable: true, destroyed: false, _last: false, chunkedEncoding: false, shouldKeepAlive: true, maxRequestsOnConnectionReached: false, _defaultKeepAlive: true, useChunkedEncodingByDefault: true, sendDate: true, _removedConnection: false, _removedContLen: false, _removedTE: false, strictContentLength: false, _contentLength: null, _hasBody: true, _trailer: '', finished: false, _headerSent: false, _closed: false, socket: <ref *1> Socket { connecting: false, _hadError: false, _parent: null, _host: null, _closeAfterHandlingError: false, _readableState: [ReadableState], _events: [Object: null prototype], _eventsCount: 8, _maxListeners: undefined, _writableState: [WritableState], allowHalfOpen: true, _sockname: null, _pendingData: null, _pendingEncoding: '', server: [Server], _server: [Server], parser: [HTTPParser], on: [Function: socketListenerWrap], addListener: [Function: socketListenerWrap], prependListener: [Function: socketListenerWrap], setEncoding: [Function: socketSetEncoding], _paused: false, _httpMessage: [Circular *3], [Symbol(async_id_symbol)]: 168, [Symbol(kHandle)]: [TCP], [Symbol(lastWriteQueueSize)]: 0, [Symbol(timeout)]: null, [Symbol(kBuffer)]: null, [Symbol(kBufferCb)]: null, [Symbol(kBufferGen)]: null, [Symbol(kCapture)]: false, [Symbol(kSetNoDelay)]: true, [Symbol(kSetKeepAlive)]: false, [Symbol(kSetKeepAliveInitialDelay)]: 0, [Symbol(kBytesRead)]: 0, [Symbol(kBytesWritten)]: 0 }, _header: null, _keepAliveTimeout: 5000, _onPendingData: [Function: bound updateOutgoingData], req: [Circular *2], _sent100: false, _expect_continue: false, _maxRequestsPerSocket: 0, locals: [Object: null prototype] {}, [Symbol(kCapture)]: false, [Symbol(kBytesWritten)]: 0, [Symbol(kNeedDrain)]: false, [Symbol(corked)]: 0, [Symbol(kOutHeaders)]: [Object: null prototype] { 'x-powered-by': [Array] }, [Symbol(errored)]: null, [Symbol(kUniqueHeaders)]: null }, body: {}, route: Route { path: '/container/:containerId', stack: [ [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer] ], methods: { acl: true, bind: true, checkout: true, connect: true, copy: true, delete: true, get: true, head: true, link: true, lock: true, 'm-search': true, merge: true, mkactivity: true, mkcalendar: true, mkcol: true, move: true, notify: true, options: true, patch: true, post: true, propfind: true, proppatch: true, purge: true, put: true, rebind: true, report: true, search: true, source: true, subscribe: true, trace: true, unbind: true, unlink: true, unlock: true, unsubscribe: true } }, [Symbol(kCapture)]: false, [Symbol(kHeaders)]: { 'content-type': 'application/json', 'postman-token': 'e9e500c3-f1c0-4207-8f44-5f5ce5ddcd44' }, [Symbol(kHeadersCount)]: 4, [Symbol(kTrailers)]: null, [Symbol(kTrailersCount)]: 0 } {}

@philhawthorne Do you have any pointers on this, is it a bug or something I'm missing? :)

@philhawthorne
Copy link
Owner

philhawthorne commented Nov 27, 2023

Hmm unfortunately with Travis no longer having a free tier (and me not updating it) I haven't got ARM builds running yet :(

This means that there is no solution for raspberry pi 4 at the moment.

@rdlvm this should be available now, although I don't have a RPI4 to test with.

I've updated the GitHub actions, which has deployed this fix to the latest and arm-latest tags. So the fix should be available for Pis as well.

To confirm with docker-compose, you would need something like:

  ha-dockermon:
    image: philhawthorne/ha-dockermon:latest
    container_name: ha-dockermon
    restart: always
    ports:
      - 8126:8126
    volumes:
      - ./ha-dockermon:/config
      - /var/run/docker.sock:/var/run/docker.sock

Raspberry Pi:

  ha-dockermon:
    image: philhawthorne/ha-dockermon:arm-latest
    container_name: ha-dockermon
    restart: always
    ports:
      - 8126:8126
    volumes:
      - ./ha-dockermon:/config
      - /var/run/docker.sock:/var/run/docker.sock

Home Assistant Config

Your REST Switches need to be updated to:

  switch:
    - platform: rest
      resource: http://127.0.0.1:8126/container/adguard
      name: Adguard
      body_on: '{"state": "start"}'
      body_off: '{"state": "stop"}'
      headers:
        Content-Type: application/json
      is_on_template: '{{ value_json is not none and value_json.state == "running" }}'

@philhawthorne
Copy link
Owner

Get request is working fine for me, but Post I keep getting 400 errors (UNRAID):

rawTrailers: [], joinDuplicateHeaders: undefined, aborted: false, upgrade: false, url: '/container/calibre', method: 'POST', statusCode: null, statusMessage: null, client: <ref *1> Socket { connecting: false, _hadError: false, _parent: null, _host: null, _closeAfterHandlingError: false, _readableState: ReadableState { objectMode: false, highWaterMark: 16384, buffer: BufferList { head: null, tail: null, length: 0 }, length: 0, pipes: [], flowing: true, ended: false, endEmitted: false, reading: true, constructed: true, sync: false, needReadable: true, emittedReadable: false, readableListening: false, resumeScheduled: false, errorEmitted: false, emitClose: false, autoDestroy: true, destroyed: false, errored: null, closed: false, closeEmitted: false, defaultEncoding: 'utf8', awaitDrainWriters: null, multiAwaitDrain: false, readingMore: false, dataEmitted: false, decoder: null, encoding: null, [Symbol(kPaused)]: false }, _events: [Object: null prototype] { end: [Array], timeout: [Function: socketOnTimeout], data: [Function: bound socketOnData], error: [Function: socketOnError], close: [Array], drain: [Function: bound socketOnDrain], resume: [Function: onSocketResume], pause: [Function: onSocketPause] }, _eventsCount: 8, _maxListeners: undefined, _writableState: WritableState { objectMode: false, highWaterMark: 16384, finalCalled: false, needDrain: false, ending: false, ended: false, finished: false, destroyed: false, decodeStrings: false, defaultEncoding: 'utf8', length: 0, writing: false, corked: 0, sync: true, bufferProcessing: false, onwrite: [Function: bound onwrite], writecb: null, writelen: 0, afterWriteTickInfo: null, buffered: [], bufferedIndex: 0, allBuffers: true, allNoop: true, pendingcb: 0, constructed: true, prefinished: false, errorEmitted: false, emitClose: false, autoDestroy: true, errored: null, closed: false, closeEmitted: false, [Symbol(kOnFinished)]: [] }, allowHalfOpen: true, _sockname: null, _pendingData: null, _pendingEncoding: '', server: Server { maxHeaderSize: undefined, insecureHTTPParser: undefined, requestTimeout: 300000, headersTimeout: 60000, keepAliveTimeout: 5000, connectionsCheckingInterval: 30000, joinDuplicateHeaders: undefined, _events: [Object: null prototype], _eventsCount: 2, _maxListeners: undefined, _connections: 1, _handle: [TCP], _usingWorkers: false, _workers: [], _unref: false, allowHalfOpen: true, pauseOnConnect: false, noDelay: true, keepAlive: false, keepAliveInitialDelay: 0, httpAllowHalfOpen: false, timeout: 0, maxHeadersCount: null, maxRequestsPerSocket: 0, _connectionKey: '6::::8126', [Symbol(IncomingMessage)]: [Function: IncomingMessage], [Symbol(ServerResponse)]: [Function: ServerResponse], [Symbol(kCapture)]: false, [Symbol(async_id_symbol)]: 6, [Symbol(http.server.connections)]: ConnectionsList {}, [Symbol(http.server.connectionsCheckingInterval)]: Timeout { _idleTimeout: 30000, _idlePrev: [TimersList], _idleNext: [TimersList], _idleStart: 60168, _onTimeout: [Function: bound checkConnections], _timerArgs: undefined, _repeat: 30000, _destroyed: false, [Symbol(refed)]: false, [Symbol(kHasPrimitive)]: false, [Symbol(asyncId)]: 5, [Symbol(triggerId)]: 1 }, [Symbol(kUniqueHeaders)]: null }, _server: Server { maxHeaderSize: undefined, insecureHTTPParser: undefined, requestTimeout: 300000, headersTimeout: 60000, keepAliveTimeout: 5000, connectionsCheckingInterval: 30000, joinDuplicateHeaders: undefined, _events: [Object: null prototype], _eventsCount: 2, _maxListeners: undefined, _connections: 1, _handle: [TCP], _usingWorkers: false, _workers: [], _unref: false, allowHalfOpen: true, pauseOnConnect: false, noDelay: true, keepAlive: false, keepAliveInitialDelay: 0, httpAllowHalfOpen: false, timeout: 0, maxHeadersCount: null, maxRequestsPerSocket: 0, _connectionKey: '6::::8126', [Symbol(IncomingMessage)]: [Function: IncomingMessage], [Symbol(ServerResponse)]: [Function: ServerResponse], [Symbol(kCapture)]: false, [Symbol(async_id_symbol)]: 6, [Symbol(http.server.connections)]: ConnectionsList {}, [Symbol(http.server.connectionsCheckingInterval)]: Timeout { _idleTimeout: 30000, _idlePrev: [TimersList], _idleNext: [TimersList], _idleStart: 60168, _onTimeout: [Function: bound checkConnections], _timerArgs: undefined, _repeat: 30000, _destroyed: false, [Symbol(refed)]: false, [Symbol(kHasPrimitive)]: false, [Symbol(asyncId)]: 5, [Symbol(triggerId)]: 1 }, [Symbol(kUniqueHeaders)]: null }, parser: HTTPParser { '0': null, '1': [Function: parserOnHeaders], '2': [Function: parserOnHeadersComplete], '3': [Function: parserOnBody], '4': [Function: parserOnMessageComplete], '5': [Function: bound onParserExecute], '6': [Function: bound onParserTimeout], _headers: [], _url: '', socket: [Circular *1], incoming: [Circular *2], outgoing: null, maxHeaderPairs: 2000, _consumed: true, onIncoming: [Function: bound parserOnIncoming], joinDuplicateHeaders: undefined, [Symbol(resource_symbol)]: [HTTPServerAsyncResource] }, on: [Function: socketListenerWrap], addListener: [Function: socketListenerWrap], prependListener: [Function: socketListenerWrap], setEncoding: [Function: socketSetEncoding], _paused: false, _httpMessage: ServerResponse { _events: [Object: null prototype], _eventsCount: 1, _maxListeners: undefined, outputData: [], outputSize: 0, writable: true, destroyed: false, _last: false, chunkedEncoding: false, shouldKeepAlive: true, maxRequestsOnConnectionReached: false, _defaultKeepAlive: true, useChunkedEncodingByDefault: true, sendDate: true, _removedConnection: false, _removedContLen: false, _removedTE: false, strictContentLength: false, _contentLength: null, _hasBody: true, _trailer: '', finished: false, _headerSent: false, _closed: false, socket: [Circular *1], _header: null, _keepAliveTimeout: 5000, _onPendingData: [Function: bound updateOutgoingData], req: [Circular *2], _sent100: false, _expect_continue: false, _maxRequestsPerSocket: 0, locals: [Object: null prototype] {}, [Symbol(kCapture)]: false, [Symbol(kBytesWritten)]: 0, [Symbol(kNeedDrain)]: false, [Symbol(corked)]: 0, [Symbol(kOutHeaders)]: [Object: null prototype], [Symbol(errored)]: null, [Symbol(kUniqueHeaders)]: null }, [Symbol(async_id_symbol)]: 168, [Symbol(kHandle)]: TCP { reading: true, onconnection: null, _consumed: true, [Symbol(owner_symbol)]: [Circular *1] }, [Symbol(lastWriteQueueSize)]: 0, [Symbol(timeout)]: null, [Symbol(kBuffer)]: null, [Symbol(kBufferCb)]: null, [Symbol(kBufferGen)]: null, [Symbol(kCapture)]: false, [Symbol(kSetNoDelay)]: true, [Symbol(kSetKeepAlive)]: false, [Symbol(kSetKeepAliveInitialDelay)]: 0, [Symbol(kBytesRead)]: 0, [Symbol(kBytesWritten)]: 0 }, _consuming: false, _dumped: false, next: [Function: next], baseUrl: '', originalUrl: '/container/calibre', _parsedUrl: Url { protocol: null, slashes: null, auth: null, host: null, port: null, hostname: null, hash: null, search: null, query: null, pathname: '/container/calibre', path: '/container/calibre', href: '/container/calibre', _raw: '/container/calibre' }, params: { containerId: 'calibre' }, query: {}, res: <ref *3> ServerResponse { _events: [Object: null prototype] { finish: [Function: bound resOnFinish] }, _eventsCount: 1, _maxListeners: undefined, outputData: [], outputSize: 0, writable: true, destroyed: false, _last: false, chunkedEncoding: false, shouldKeepAlive: true, maxRequestsOnConnectionReached: false, _defaultKeepAlive: true, useChunkedEncodingByDefault: true, sendDate: true, _removedConnection: false, _removedContLen: false, _removedTE: false, strictContentLength: false, _contentLength: null, _hasBody: true, _trailer: '', finished: false, _headerSent: false, _closed: false, socket: <ref *1> Socket { connecting: false, _hadError: false, _parent: null, _host: null, _closeAfterHandlingError: false, _readableState: [ReadableState], _events: [Object: null prototype], _eventsCount: 8, _maxListeners: undefined, _writableState: [WritableState], allowHalfOpen: true, _sockname: null, _pendingData: null, _pendingEncoding: '', server: [Server], _server: [Server], parser: [HTTPParser], on: [Function: socketListenerWrap], addListener: [Function: socketListenerWrap], prependListener: [Function: socketListenerWrap], setEncoding: [Function: socketSetEncoding], _paused: false, _httpMessage: [Circular *3], [Symbol(async_id_symbol)]: 168, [Symbol(kHandle)]: [TCP], [Symbol(lastWriteQueueSize)]: 0, [Symbol(timeout)]: null, [Symbol(kBuffer)]: null, [Symbol(kBufferCb)]: null, [Symbol(kBufferGen)]: null, [Symbol(kCapture)]: false, [Symbol(kSetNoDelay)]: true, [Symbol(kSetKeepAlive)]: false, [Symbol(kSetKeepAliveInitialDelay)]: 0, [Symbol(kBytesRead)]: 0, [Symbol(kBytesWritten)]: 0 }, _header: null, _keepAliveTimeout: 5000, _onPendingData: [Function: bound updateOutgoingData], req: [Circular *2], _sent100: false, _expect_continue: false, _maxRequestsPerSocket: 0, locals: [Object: null prototype] {}, [Symbol(kCapture)]: false, [Symbol(kBytesWritten)]: 0, [Symbol(kNeedDrain)]: false, [Symbol(corked)]: 0, [Symbol(kOutHeaders)]: [Object: null prototype] { 'x-powered-by': [Array] }, [Symbol(errored)]: null, [Symbol(kUniqueHeaders)]: null }, body: {}, route: Route { path: '/container/:containerId', stack: [ [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer], [Layer] ], methods: { acl: true, bind: true, checkout: true, connect: true, copy: true, delete: true, get: true, head: true, link: true, lock: true, 'm-search': true, merge: true, mkactivity: true, mkcalendar: true, mkcol: true, move: true, notify: true, options: true, patch: true, post: true, propfind: true, proppatch: true, purge: true, put: true, rebind: true, report: true, search: true, source: true, subscribe: true, trace: true, unbind: true, unlink: true, unlock: true, unsubscribe: true } }, [Symbol(kCapture)]: false, [Symbol(kHeaders)]: { 'content-type': 'application/json', 'postman-token': 'e9e500c3-f1c0-4207-8f44-5f5ce5ddcd44' }, [Symbol(kHeadersCount)]: 4, [Symbol(kTrailers)]: null, [Symbol(kTrailersCount)]: 0 } {}

Hmm not much here I can work with. I assume these are the errors spit out from the ha-dockermon container?

Can you try pulling the new latest tag. It has bumped the NodeJS version, and might solve it somehow.

@Flight777
Copy link

The above was spitout by the container :P

I've tried the latest tag just now, but still getting 400 bad request errors. Get is working perfectly.

@manut999
Copy link

manut999 commented Dec 3, 2023

Would be great if you could update, https://philhawthorne.com/ha-dockermon-use-home-assistant-to-monitor-start-or-stop-docker-containers/, too. Would have saved me ages :)

@rdlvm
Copy link

rdlvm commented Dec 5, 2023

Hmm unfortunately with Travis no longer having a free tier (and me not updating it) I haven't got ARM builds running yet :(

This means that there is no solution for raspberry pi 4 at the moment.

@rdlvm this should be available now, although I don't have a RPI4 to test with.

I've updated the GitHub actions, which has deployed this fix to the latest and arm-latest tags. So the fix should be available for Pis as well.

To confirm with docker-compose, you would need something like:

  ha-dockermon:
    image: philhawthorne/ha-dockermon:latest
    container_name: ha-dockermon
    restart: always
    ports:
      - 8126:8126
    volumes:
      - ./ha-dockermon:/config
      - /var/run/docker.sock:/var/run/docker.sock

Raspberry Pi:

  ha-dockermon:
    image: philhawthorne/ha-dockermon:arm-latest
    container_name: ha-dockermon
    restart: always
    ports:
      - 8126:8126
    volumes:
      - ./ha-dockermon:/config
      - /var/run/docker.sock:/var/run/docker.sock

Home Assistant Config

Your REST Switches need to be updated to:

  switch:
    - platform: rest
      resource: http://127.0.0.1:8126/container/adguard
      name: Adguard
      body_on: '{"state": "start"}'
      body_off: '{"state": "stop"}'
      headers:
        Content-Type: application/json
      is_on_template: '{{ value_json is not none and value_json.state == "running" }}'

It seems to work now, I'll test it thoroughly and report my impressions.

thanks!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests