mikakoivisto / controlmyspa-ha-mqtt Goto Github PK
View Code? Open in Web Editor NEWLicense: GNU General Public License v3.0
License: GNU General Public License v3.0
First of, I am really pleased with this addon! It is simple and I like simple 😃
~/lib/controlmyspajs/controlmyspa.js
Statements containing:
await this.sleep(5000);
Are causing premature errors. It states that the requested function (any of them) is failed but 2 seconds later is it resolved and correctly updated.
May this be doubled? That way I will not have my wife nagging day in day out that HA is being funky 😄
HA docker add-on log shows the following error:
(node:135) Warning: Setting the NODE_TLS_REJECT_UNAUTHORIZED environment variable to '0' makes TLS connections and HTTPS requests insecure by disabling certificate verification.
(Use `node --trace-warnings ...` to show where the warning was created)
2024-05-26T23:01:28.835Z app:info MQTT connection established
failed to login
/usr/src/app/lib/spa.js:34
}, (self.client.tokenData.expires_in - 60) * 1000);
^
TypeError: Cannot read properties of null (reading 'expires_in')
at Spa.<anonymous> (/usr/src/app/lib/spa.js:34:33)
at Spa.emit (node:events:531:35)
at /usr/src/app/lib/spa.js:24:12
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
Node.js v20.13.1
I get this every time I start the add-on, and it dies immediately.
In another issue thread, I saw that you mentioned having to fork controlmyspa.js to deal with an API change. Is this possibly related? I'm a software engineer, so I'm happy to help figure this out if you need a hand... but I wasn't sure where to even get started here. Have you run across this issue?
Hello Mika,
could you please give me some assistance to get your ControlMySpa integration running in HA? I installed it as an AddOn, this works fine. But when starting it the log protocoll says the following:
...s6-rc: info: service legacy-services successfully started
/run/s6/basedir/scripts/rc.init: line 76: run.sh: not found
...
As I'm not familar with Linux systems I have no idea how to proceed.
My HA version is
Home Assistant 2023.5.3, Supervisor 2023.04.1, Operating System 10.1
running on an Intel NUC.
Additionally the Mosquitto MQTT broker is installed and (as it seems) correctly configured and it's running.
I would really appriciate if you have any hints or advices to get it running.
Thanks in advance and best regards
Martin
Hi there - thanks for building this addon - it's really helpful. As of the Home Assistant 2023.8 release though, a warning has begun to display in home assistant, per the breaking changes in https://www.home-assistant.io/blog/2023/08/02/release-20238/#breaking-changes
So, as requested by the warning, I'm providing a heads up to you so that this can get fixed before it becomes a proper problem in a later release.
Since approximately 2 weeks the integration stopped working:
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
Node.js v18.12.1
app:debug {"mqttHost":"192.168.68.151","mqttPort":"1883","mqttUser":"maxim","mqttPass":"***REMOVED***","hassTopic":"homeassistant/status","spaUser":"[email protected]","spaPassword":"***REMOVED***","useCelsius":true,"refreshSpa":1} +0ms
(node:1) Warning: Setting the NODE_TLS_REJECT_UNAUTHORIZED environment variable to '0' makes TLS connections and HTTPS requests insecure by disabling certificate verification.
(Use `node --trace-warnings ...` to show where the warning was created)
failed to login
/usr/src/app/lib/spa.js:34
}, (self.client.tokenData.expires_in - 60) * 1000);
^
TypeError: Cannot read properties of null (reading 'expires_in')
at Spa.<anonymous> (/usr/src/app/lib/spa.js:34:33)
at Spa.emit (node:events:525:35)
at /usr/src/app/lib/spa.js:24:12
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
Node.js v18.12.1
I am able to log in with username/password combination to https://idmqa.controlmyspa.com/oxauth/login - I didn't change anything.
I would like to run controlmyspa-ha-mqtt as a Docker application on my Synology NAS.
Unfortunately, it terminates unexpectedly after starting.
Do I have to create a config in advance.
Can I point this to a folder
Not sure what might be wrong, but after upgrading HA core to 2023.5.4 the current temperature is missing.
Running HA in proxmox and sep vm with docker for controlmysqa-mqtt. Restarted both HA and docker a few times.
Was there any changes in HA that causes this? Any logs I can provide? See screenshot where Current temp is shown unavailable.
I am really interested of your project. I own a spa with Controlmyspa module and I have homeassistant setup working. I feel like I am not experienced enough to setup the docker container that uses the js code by myself. Would it be possible for you to give a few more details on how to get it up and working ?
I noticed that the certificate was updated over at controlmyspa.com (they updated the Android app a few days after it broke that with the new certificate). The new cert looks okay, but I didn't dive in too deep to the chain. For some reason, the app is failing to verify, probably on a root CA that wasn't available when nodejs was compiled. I believe this issue is with the upstream JS app, controlmyspajs, issue put in here: https://gitlab.com/VVlasy/controlmyspajs/-/issues/4.
There are a couple of ways to fix this I believe, either turn off verification (not ideal), or code in the root CA as trusted over at controlmyspajs. I am also rebuilding nodejs and will see if that works. Otherwise, will try to find time to code in the root CAs as trusted over at controlmyspajs.
Hi Mika.
Sorry to disturb... do you know about problems to run the controlmyspa-ha-mqtt service on a HA version
Home Assistant 2023.7.1 Supervisor 2023.07.1 Operating System 10.3
Since today or yesterday the service stops after few seconds and the protocoll log shows a couple of lines of json code; here are a few of the very end:
response: undefined,
isAxiosError: true,
toJSON: [Function: toJSON]
}
Node.js v18.16.1
s6-rc: info: service legacy-services: stopping
s6-rc: info: service legacy-services successfully stopped
s6-rc: info: service legacy-cont-init: stopping
s6-rc: info: service legacy-cont-init successfully stopped
s6-rc: info: service fix-attrs: stopping
s6-rc: info: service fix-attrs successfully stopped
s6-rc: info: service s6rc-oneshot-runner: stopping
s6-rc: info: service s6rc-oneshot-runner successfully stopped
I noticed that the ControlMySpa app was updated by Balboa on July 4th but didn't know if the did something critical...?
Thanks in advance and best regards
Martin
Log attached:
Using configuration file: ./config.json Configuration file not found, attempting to use environment variables for configuration. Error: Request failed with status code 404 at createError (/usr/src/app/node_modules/axios/lib/core/createError.js:16:15) at settle (/usr/src/app/node_modules/axios/lib/core/settle.js:17:12) at IncomingMessage.handleStreamEnd (/usr/src/app/node_modules/axios/lib/adapters/http.js:269:11) at IncomingMessage.emit (node:events:539:35) at endReadableNT (node:internal/streams/readable:1345:12) at processTicksAndRejections (node:internal/process/task_queues:83:21) { config: { url: 'https://iot.controlmyspa.com/mobile/spas/search/[email protected]', method: 'get', headers: { Accept: '*/*', 'User-Agent': 'ControlMySpa/3.0.2 (com.controlmyspa.qa; build:1; iOS 14.2.0) Alamofire/5.2.2', 'Accept-Encoding': 'br;q=1.0, gzip;q=0.9, deflate;q=0.8', 'Accept-Language': 'cs-CZ;q=1.0', Authorization: 'Bearer def3cf32-2950-4ac5-94e4-862ebba35ea3' }, transformRequest: [ [Function: transformRequest] ], transformResponse: [ [Function: transformResponse] ], timeout: 0, adapter: [Function: httpAdapter], xsrfCookieName: 'XSRF-TOKEN', xsrfHeaderName: 'X-XSRF-TOKEN', maxContentLength: -1, maxBodyLength: -1, validateStatus: [Function: validateStatus], transitional: { silentJSONParsing: true, forcedJSONParsing: true, clarifyTimeoutError: false }, data: undefined }, request: <ref *1> ClientRequest { _events: [Object: null prototype] { abort: [Function (anonymous)], aborted: [Function (anonymous)], connect: [Function (anonymous)], error: [Function (anonymous)], socket: [Function (anonymous)], timeout: [Function (anonymous)], prefinish: [Function: requestOnPrefinish] }, _eventsCount: 7, _maxListeners: undefined, outputData: [], outputSize: 0, writable: true, destroyed: false, _last: true, chunkedEncoding: false, shouldKeepAlive: false, maxRequestsOnConnectionReached: false, _defaultKeepAlive: true, useChunkedEncodingByDefault: false, sendDate: false, _removedConnection: false, _removedContLen: false, _removedTE: false, _contentLength: 0, _hasBody: true, _trailer: '', finished: true, _headerSent: true, _closed: false, socket: TLSSocket { _tlsOptions: [Object], _secureEstablished: true, _securePending: false, _newSessionPending: false, _controlReleased: true, secureConnecting: false, _SNICallback: null, servername: 'iot.controlmyspa.com', alpnProtocol: false, authorized: true, authorizationError: null, encrypted: true, _events: [Object: null prototype], _eventsCount: 10, connecting: false, _hadError: false, _parent: null, _host: 'iot.controlmyspa.com', _readableState: [ReadableState], _maxListeners: undefined, _writableState: [WritableState], allowHalfOpen: false, _sockname: null, _pendingData: null, _pendingEncoding: '', server: undefined, _server: null, ssl: [TLSWrap], _requestCert: true, _rejectUnauthorized: true, parser: null, _httpMessage: [Circular *1], [Symbol(res)]: [TLSWrap], [Symbol(verified)]: true, [Symbol(pendingSession)]: null, [Symbol(async_id_symbol)]: 74, [Symbol(kHandle)]: [TLSWrap], [Symbol(lastWriteQueueSize)]: 0, [Symbol(timeout)]: null, [Symbol(kBuffer)]: null, [Symbol(kBufferCb)]: null, [Symbol(kBufferGen)]: null, [Symbol(kCapture)]: false, [Symbol(kSetNoDelay)]: false, [Symbol(kSetKeepAlive)]: false, [Symbol(kSetKeepAliveInitialDelay)]: 0, [Symbol(kBytesRead)]: 0, [Symbol(kBytesWritten)]: 0, [Symbol(connect-options)]: [Object], [Symbol(RequestTimeout)]: undefined }, _header: 'GET /mobile/spas/search/[email protected] HTTP/1.1\r\n' + 'Accept: */*\r\n' + 'User-Agent: ControlMySpa/3.0.2 (com.controlmyspa.qa; build:1; iOS 14.2.0) Alamofire/5.2.2\r\n' + 'Accept-Encoding: br;q=1.0, gzip;q=0.9, deflate;q=0.8\r\n' + 'Accept-Language: cs-CZ;q=1.0\r\n' + 'Authorization: Bearer def3cf32-2950-4ac5-94e4-862ebba35ea3\r\n' + 'Host: iot.controlmyspa.com\r\n' + 'Connection: close\r\n' + '\r\n', _keepAliveTimeout: 0, _onPendingData: [Function: nop], agent: Agent { _events: [Object: null prototype], _eventsCount: 2, _maxListeners: undefined, defaultPort: 443, protocol: 'https:', options: [Object: null prototype], requests: [Object: null prototype] {}, sockets: [Object: null prototype], freeSockets: [Object: null prototype] {}, keepAliveMsecs: 1000, keepAlive: false, maxSockets: Infinity, maxFreeSockets: 256, scheduling: 'lifo', maxTotalSockets: Infinity, totalSocketCount: 1, maxCachedSessions: 100, _sessionCache: [Object], [Symbol(kCapture)]: false }, socketPath: undefined, method: 'GET', maxHeaderSize: undefined, insecureHTTPParser: undefined, path: '/mobile/spas/search/[email protected]', _ended: true, res: IncomingMessage { _readableState: [ReadableState], _events: [Object: null prototype], _eventsCount: 3, _maxListeners: undefined, socket: [TLSSocket], httpVersionMajor: 1, httpVersionMinor: 1, httpVersion: '1.1', complete: true, rawHeaders: [Array], rawTrailers: [], aborted: false, upgrade: false, url: '', method: null, statusCode: 404, statusMessage: 'Not Found', client: [TLSSocket], _consuming: false, _dumped: false, req: [Circular *1], responseUrl: 'https://iot.controlmyspa.com/mobile/spas/search/[email protected]', redirects: [], [Symbol(kCapture)]: false, [Symbol(kHeaders)]: [Object], [Symbol(kHeadersCount)]: 22, [Symbol(kTrailers)]: null, [Symbol(kTrailersCount)]: 0, [Symbol(RequestTimeout)]: undefined }, aborted: false, timeoutCb: null, upgradeOrConnect: false, parser: null, maxHeadersCount: null, reusedSocket: false, host: 'iot.controlmyspa.com', protocol: 'https:', _redirectable: Writable { _writableState: [WritableState], _events: [Object: null prototype], _eventsCount: 2, _maxListeners: undefined, _options: [Object], _ended: true, _ending: true, _redirectCount: 0, _redirects: [], _requestBodyLength: 0, _requestBodyBuffers: [], _onNativeResponse: [Function (anonymous)], _currentRequest: [Circular *1], _currentUrl: 'https://iot.controlmyspa.com/mobile/spas/search/[email protected]', [Symbol(kCapture)]: false }, [Symbol(kCapture)]: false, [Symbol(kNeedDrain)]: false, [Symbol(corked)]: 0, [Symbol(kOutHeaders)]: [Object: null prototype] { accept: [Array], 'user-agent': [Array], 'accept-encoding': [Array], 'accept-language': [Array], authorization: [Array], host: [Array] } }, response: { status: 404, statusText: 'Not Found', headers: { server: 'openresty/1.21.4.1', date: 'Thu, 04 Aug 2022 22:01:10 GMT', 'content-length': '0', connection: 'close', 'x-content-type-options': 'nosniff', 'x-xss-protection': '1; mode=block', 'cache-control': 'no-cache, no-store, max-age=0, must-revalidate', pragma: 'no-cache', expires: '0', 'x-frame-options': 'DENY', 'x-application-context': 'application:prod:8800' }, config: { url: 'https://iot.controlmyspa.com/mobile/spas/search/[email protected]', method: 'get', headers: [Object], transformRequest: [Array], transformResponse: [Array], timeout: 0, adapter: [Function: httpAdapter], xsrfCookieName: 'XSRF-TOKEN', xsrfHeaderName: 'X-XSRF-TOKEN', maxContentLength: -1, maxBodyLength: -1, validateStatus: [Function: validateStatus], transitional: [Object], data: undefined }, request: <ref *1> ClientRequest { _events: [Object: null prototype], _eventsCount: 7, _maxListeners: undefined, outputData: [], outputSize: 0, writable: true, destroyed: false, _last: true, chunkedEncoding: false, shouldKeepAlive: false, maxRequestsOnConnectionReached: false, _defaultKeepAlive: true, useChunkedEncodingByDefault: false, sendDate: false, _removedConnection: false, _removedContLen: false, _removedTE: false, _contentLength: 0, _hasBody: true, _trailer: '', finished: true, _headerSent: true, _closed: false, socket: [TLSSocket], _header: 'GET /mobile/spas/search/[email protected] HTTP/1.1\r\n' + 'Accept: */*\r\n' + 'User-Agent: ControlMySpa/3.0.2 (com.controlmyspa.qa; build:1; iOS 14.2.0) Alamofire/5.2.2\r\n' + 'Accept-Encoding: br;q=1.0, gzip;q=0.9, deflate;q=0.8\r\n' + 'Accept-Language: cs-CZ;q=1.0\r\n' + 'Authorization: Bearer def3cf32-2950-4ac5-94e4-862ebba35ea3\r\n' + 'Host: iot.controlmyspa.com\r\n' + 'Connection: close\r\n' + '\r\n', _keepAliveTimeout: 0, _onPendingData: [Function: nop], agent: [Agent], socketPath: undefined, method: 'GET', maxHeaderSize: undefined, insecureHTTPParser: undefined, path: '/mobile/spas/search/[email protected]', _ended: true, res: [IncomingMessage], aborted: false, timeoutCb: null, upgradeOrConnect: false, parser: null, maxHeadersCount: null, reusedSocket: false, host: 'iot.controlmyspa.com', protocol: 'https:', _redirectable: [Writable], [Symbol(kCapture)]: false, [Symbol(kNeedDrain)]: false, [Symbol(corked)]: 0, [Symbol(kOutHeaders)]: [Object: null prototype] }, data: '' }, isAxiosError: true, toJSON: [Function: toJSON] }
Greetings,
Throwing this out there for discussion before a PR, I noticed that with temp being 0.5, HA struggles sometimes submitting a new temperature if at .5 decimal (NaN error). Side note, it seems MQTT is reporting the temp without decimal.
Not sure how this tub works in Celsius land, although I could test that out in a few weeks. Would removing the decimal create any headaches or drawbacks, settings temp.step to 1?
Thank you so much for this integration, it's fantastic.
I am unable to set the heat temperature above 37 C in HA - any ideas? I can set to a maximum of 40 C via the app and the panel.
UPDATE:
I figured it out. I'm using my own MQTT outside of docker, and what happens is a race condition in which the container tries to immediately push data to MQTT and assumes the spa values from the IoT server are available (they're not). The code should wait for a successful cloud connection before trying to parse values. My workaround was to stop mosquitto, start the container and watch the mqtt errors from it, then start mosquitto service again.
I cannot seem to get this to log in with ControlMySpa IoT over HTTPS. Perhaps the login process is not called with the rewrite?
spa.js:87
return this.client.currentSpa._id;
^
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.