ha-dockermon
ha-dockermon copied to clipboard
Can't start container via rest template since HA 2023.6
More info on https://community.home-assistant.io/t/rest-switch-stopped-working/581188
Same problem
I get the same error
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.
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?
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
Thanks Phil. Luckily its still reporting state fine to HA, so we can use a workaround for the moment with rest_commands and a script that turns off when state is on and vice versa.
Eg.
rest_command: temp_frigate_on: url: http://local.ip:8126/container/frigate/start temp_frigate_off: url: http://local.ip:8126/container/frigate/stop
and script:
alias: Temp frigate switch sequence:
- choose:
- conditions:
- condition: state entity_id: switch.frigate state: "on" sequence:
- service: rest_command.temp_frigate_off data: {}
- conditions:
- condition: state entity_id: switch.frigate state: "off" sequence:
- service: rest_command.temp_frigate_on data: {} mode: restart
- conditions:
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: https://github.com/home-assistant/core/pull/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.
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.
Workaround works indeed, I tried it indeed like that before, but probably, like you said, it had wrong capitalized casing.
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
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
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
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
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.
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
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.
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 } {}
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
Hmm unfortunately with Travis no longer having a free tier (and me not updating it) I haven't got ARM builds running yet :(
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.
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? :)
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" }}'
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.
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.
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 :)
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
andarm-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!!