From 4281c5033c6827e4cefb7a79861d2156a98fc0a1 Mon Sep 17 00:00:00 2001 From: Pierre-Gilles Leymarie Date: Wed, 13 Nov 2019 17:47:20 +0100 Subject: [PATCH] First version of improved Z-Wave integration UI --- front/package-lock.json | 247 +++++++++--------- front/package.json | 2 +- front/src/components/app.jsx | 7 +- front/src/config/i18n/en.json | 15 +- .../node-operation-page/AddRemoveNode.jsx | 36 +++ .../all/zwave/node-operation-page/actions.js | 63 +++++ .../all/zwave/node-operation-page/index.js | 65 +++++ .../all/zwave/node-page/NodeTab.jsx | 32 ++- .../all/zwave/settings-page/SettingsTab.jsx | 37 ++- .../all/zwave/settings-page/actions.js | 31 ++- .../all/zwave/settings-page/index.js | 27 +- .../all/zwave/setup-page/NodeTab.jsx | 25 +- .../all/zwave/setup-page/actions.js | 31 ++- .../integration/all/zwave/setup-page/index.js | 20 +- server/services/zwave/api/zwave.controller.js | 17 ++ .../zwave/lib/commands/zwave.addNode.js | 3 + .../commands/zwave.cancelControllerCommand.js | 15 ++ .../zwave/lib/commands/zwave.connect.js | 10 +- .../zwave/lib/commands/zwave.disconnect.js | 10 +- .../zwave/lib/commands/zwave.removeNode.js | 3 + .../zwave/lib/events/zwave.driverFailed.js | 6 + .../zwave/lib/events/zwave.driverReady.js | 6 + server/services/zwave/lib/index.js | 2 + server/utils/constants.js | 2 + 24 files changed, 532 insertions(+), 180 deletions(-) create mode 100644 front/src/routes/integration/all/zwave/node-operation-page/AddRemoveNode.jsx create mode 100644 front/src/routes/integration/all/zwave/node-operation-page/actions.js create mode 100644 front/src/routes/integration/all/zwave/node-operation-page/index.js create mode 100644 server/services/zwave/lib/commands/zwave.cancelControllerCommand.js diff --git a/front/package-lock.json b/front/package-lock.json index 85e245b246..c4be234d52 100644 --- a/front/package-lock.json +++ b/front/package-lock.json @@ -74,9 +74,9 @@ } }, "@babel/runtime": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.3.tgz", - "integrity": "sha512-kq6anf9JGjW8Nt5rYfEuGRaEAaH1mkv3Bbu6rYvLOpPh/RusSJXuKPEAoZ7L7gybZkchE8+NV5g9vKF4AGAtsA==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.2.tgz", + "integrity": "sha512-JONRbXbTXc9WQE2mAZd1p0Z3DZ/6vaQIkgYMSTP3KjRCyd7rCZCcfhCyX+YjwcKxcZ82UrxbRD358bpExNgrjw==", "requires": { "regenerator-runtime": "^0.13.2" } @@ -201,9 +201,9 @@ "dev": true }, "@types/node": { - "version": "7.10.8", - "resolved": "https://registry.npmjs.org/@types/node/-/node-7.10.8.tgz", - "integrity": "sha512-QtdN5f3l8unGw6LwmrHrerxoDDrENc5/5ohQ2sVO+zEgXQZ4RNdAoNZ4CuoZpymUeDlUaOnC50VHznBLByrnRg==", + "version": "7.10.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-7.10.9.tgz", + "integrity": "sha512-usSpgoUsRtO5xNV5YEPU8PPnHisFx8u0rokj1BPVn/hDF7zwUDzVLiuKZM38B7z8V2111Fj6kd4rGtQFUZpNOw==", "dev": true }, "@types/prop-types": { @@ -2057,8 +2057,7 @@ "base64-js": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", - "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", - "dev": true + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" }, "batch": { "version": "0.6.1", @@ -2459,7 +2458,6 @@ "version": "5.4.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.4.3.tgz", "integrity": "sha512-zvj65TkFeIt3i6aj5bIvJDzjjQQGs4o/sNoezg1F1kYap9Nu2jcUdpwzRSJTHMMzG0H7bZkn4rNQpImhuxWX2A==", - "dev": true, "requires": { "base64-js": "^1.0.2", "ieee754": "^1.1.4" @@ -2469,7 +2467,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", - "dev": true, "requires": { "buffer-alloc-unsafe": "^1.1.0", "buffer-fill": "^1.0.0" @@ -2478,8 +2475,7 @@ "buffer-alloc-unsafe": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", - "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", - "dev": true + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==" }, "buffer-crc32": { "version": "0.2.13", @@ -2490,14 +2486,12 @@ "buffer-fill": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", - "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=", - "dev": true + "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=" }, "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" }, "buffer-indexof": { "version": "1.1.1", @@ -2757,15 +2751,15 @@ } }, "caniuse-db": { - "version": "1.0.30001006", - "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30001006.tgz", - "integrity": "sha512-Xn25grc0GXATFnnEX+KP3IwEv6ZdHs4CALyLKvK8pBeeBe+hSpqy3/GyKBgEp4hn6o+bI+GNeNeQBf9PBOK0EQ==", + "version": "1.0.30001009", + "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30001009.tgz", + "integrity": "sha512-klfjT6Tmyg/1GINznqr1oE89cw08gKHkA8yU801fxZGE4lao1EL7+W1fAXLniGUOS3kSuHT9dE7ZSTgSAs27iQ==", "dev": true }, "caniuse-lite": { - "version": "1.0.30001006", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001006.tgz", - "integrity": "sha512-MXnUVX27aGs/QINz+QG1sWSLDr3P1A3Hq5EUWoIt0T7K24DuvMxZEnh3Y5aHlJW6Bz2aApJdSewdYLd8zQnUuw==", + "version": "1.0.30001009", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001009.tgz", + "integrity": "sha512-M3rEqHN6SaVjgo4bIik7HsGcWXsi+lI9WA0p51RPMFx5gXfduyOXWJrc0R4xBkSK1pgNf4CNgy5M+6H+WiEP8g==", "dev": true }, "capture-exit": { @@ -3385,9 +3379,9 @@ "dev": true }, "convert-source-map": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", - "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", "dev": true, "requires": { "safe-buffer": "~5.1.1" @@ -4025,9 +4019,9 @@ "integrity": "sha512-fawhJU3ajJud093das8L3PSXqDb+LjclKhRTIbVX1xC+QeHtL/30kNTkS8s/lOiKMGMngxKvwEzQhBEmK/KQnQ==" }, "dayjs": { - "version": "1.8.16", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.16.tgz", - "integrity": "sha512-XPmqzWz/EJiaRHjBqSJ2s6hE/BUoCIHKgdS2QPtTQtKcS9E4/Qn0WomoH1lXanWCzri+g7zPcuNV4aTZ8PMORQ==" + "version": "1.8.17", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.17.tgz", + "integrity": "sha512-47VY/htqYqr9GHd7HW/h56PpQzRBSJcxIQFwqL3P20bMF/3az5c3PWdVY3LmPXFl6cQCYHL7c79b9ov+2bOBbw==" }, "debounce": { "version": "1.2.0", @@ -4182,9 +4176,9 @@ } }, "deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.0.tgz", - "integrity": "sha512-ZbfWJq/wN1Z273o7mUSjILYqehAktR2NVoSrOukDkU9kg2v/Uv89yU4Cvz8seJeAmtN5oqiefKq8FPuXOboqLw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", "dev": true, "requires": { "is-arguments": "^1.0.4", @@ -4328,9 +4322,9 @@ "dev": true }, "des.js": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", - "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", "dev": true, "requires": { "inherits": "^2.0.1", @@ -4502,9 +4496,9 @@ } }, "dom-serializer": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.1.tgz", - "integrity": "sha512-sK3ujri04WyjwQXVoK4PU3y8ula1stq10GJZpqHIUgoGZdsGzAGu65BnU3d08aTVSvO7mGPZUc0wTEDL+qGE0Q==", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", "dev": true, "requires": { "domelementtype": "^2.0.1", @@ -4666,9 +4660,9 @@ } }, "electron-to-chromium": { - "version": "1.3.296", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.296.tgz", - "integrity": "sha512-s5hv+TSJSVRsxH190De66YHb50pBGTweT9XGWYu/LMR20KX6TsjFzObo36CjVAzM+PUeeKSBRtm/mISlCzeojQ==", + "version": "1.3.306", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.306.tgz", + "integrity": "sha512-frDqXvrIROoYvikSKTIKbHbzO6M3/qC6kCIt/1FOa9kALe++c4VAJnwjSFvf1tYLEUsP2n9XZ4XSCyqc3l7A/A==", "dev": true }, "elliptic": { @@ -4828,9 +4822,9 @@ } }, "es-to-primitive": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", - "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", "dev": true, "requires": { "is-callable": "^1.1.4", @@ -5384,9 +5378,9 @@ } }, "ext": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.1.2.tgz", - "integrity": "sha512-/KLjJdTNyDepCihrk4HQt57nAE1IRCEo5jUt+WgWGCr1oARhibDvmI2DMcSNWood1T9AUWwq+jaV1wvRqaXfnA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.2.0.tgz", + "integrity": "sha512-0ccUQK/9e3NreLFg6K6np8aPyRgwycx+oFGtfx1dSp7Wj00Ozw9r05FgBRlzjf2XBM7LAzwgLyDscRrtSU91hA==", "dev": true, "requires": { "type": "^2.0.0" @@ -5717,9 +5711,9 @@ } }, "flatten": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.2.tgz", - "integrity": "sha1-2uRqnXj74lKSJYzB54CkHZXAN4I=", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.3.tgz", + "integrity": "sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg==", "dev": true }, "flush-write-stream": { @@ -6491,9 +6485,9 @@ } }, "glob": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.5.tgz", - "integrity": "sha512-J9dlskqUXK1OeTOYBEn5s8aMukWMwWfs+rPTn/jn50Ux4MNXVhubL1wu/j2t+H4NVI+cXEcCaYellqaPVGXNqQ==", + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -7446,8 +7440,7 @@ "ieee754": { "version": "1.1.13", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", - "dev": true + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" }, "iferr": { "version": "0.1.5", @@ -9411,9 +9404,9 @@ } }, "loglevel": { - "version": "1.6.4", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.4.tgz", - "integrity": "sha512-p0b6mOGKcGa+7nnmKbpzR6qloPbrgLcnio++E+14Vo/XffOGwZtRpUhr8dTH/x2oCMmEoIU0Zwm3ZauhvYD17g==", + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.6.tgz", + "integrity": "sha512-Sgr5lbboAUBo3eXCSPL4/KoVz3ROKquOjcctxmHIt+vol2DrqTQe3SwkKKuYhEiWB5kYa13YyopJ69deJ1irzQ==", "dev": true }, "longest": { @@ -9665,18 +9658,18 @@ "dev": true }, "mime-db": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", - "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==", + "version": "1.42.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.42.0.tgz", + "integrity": "sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ==", "dev": true }, "mime-types": { - "version": "2.1.24", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", - "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "version": "2.1.25", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.25.tgz", + "integrity": "sha512-5KhStqB5xpTAeGqKBAMgwaYMnQik7teQN4IAzC7npDv6kzeU6prfkR67bc87J1kWMPGkoaZSq1npmexMgkmEVg==", "dev": true, "requires": { - "mime-db": "1.40.0" + "mime-db": "1.42.0" } }, "mimic-fn": { @@ -9974,9 +9967,9 @@ }, "dependencies": { "buffer": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", - "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", "dev": true, "requires": { "base64-js": "^1.0.2", @@ -10012,9 +10005,9 @@ } }, "node-releases": { - "version": "1.1.39", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.39.tgz", - "integrity": "sha512-8MRC/ErwNCHOlAFycy9OPca46fQYUjbJRDcZTHVWIGXIjYLM73k70vv3WkYutVnM4cCo4hE0MqBVVZjP6vjISA==", + "version": "1.1.40", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.40.tgz", + "integrity": "sha512-r4LPcC5b/bS8BdtWH1fbeK88ib/wg9aqmg6/s3ngNLn2Ewkn/8J6Iw3P9RTlfIAdSdvYvQl2thCY5Y+qTAQ2iQ==", "dev": true, "requires": { "semver": "^6.3.0" @@ -10173,9 +10166,9 @@ } }, "object-inspect": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz", - "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", + "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", "dev": true }, "object-is": { @@ -10322,28 +10315,20 @@ "requires": { "minimist": "~0.0.1", "wordwrap": "~0.0.2" - }, - "dependencies": { - "wordwrap": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", - "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", - "dev": true - } } }, "optionator": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", - "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", "dev": true, "requires": { "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.4", + "fast-levenshtein": "~2.0.6", "levn": "~0.3.0", "prelude-ls": "~1.1.2", "type-check": "~0.3.2", - "wordwrap": "~1.0.0" + "word-wrap": "~1.2.3" } }, "ora": { @@ -10652,9 +10637,9 @@ "dev": true }, "path-to-regexp": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", - "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", "dev": true, "requires": { "isarray": "0.0.1" @@ -13077,9 +13062,9 @@ } }, "preact": { - "version": "10.0.4", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.0.4.tgz", - "integrity": "sha512-x9QW91LVQZ4lkMZ8gOucyaW1414MEtgSC//JwlxR0nfq/QEdbaLQZrWFFWgjtGyNBBXg2P0TZ6u6W+XlpjcK2Q==" + "version": "10.0.5", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.0.5.tgz", + "integrity": "sha512-62+J+GTrv3Uhp6DefqZTel6VYcdUA1zqHO7gjQSKdOwkU2sNOp/Vl6Zf2A3hIWV5EBgjeDA8gsOn6dmB3I1Dvg==" }, "preact-cli": { "version": "2.2.1", @@ -13278,9 +13263,9 @@ "dev": true }, "preact": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/preact/-/preact-8.5.2.tgz", - "integrity": "sha512-37tlDJGq5IQKqGUbqPZ7qPtsTOWFyxe+ojAOFfzKo0dEPreenqrqgJuS83zGpeGAqD9h9L9Yr7QuxH2W4ZrKxg==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/preact/-/preact-8.5.3.tgz", + "integrity": "sha512-O3kKP+1YdgqHOFsZF2a9JVdtqD+RPzCQc3rP+Ualf7V6rmRDchZ9MJbiGTT7LuyqFKZqlHSOyO/oMFmI2lVTsw==", "dev": true }, "preact-router": { @@ -13539,9 +13524,9 @@ "dev": true }, "prettier": { - "version": "1.18.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.18.2.tgz", - "integrity": "sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw==", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", + "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", "dev": true }, "pretty-bytes": { @@ -13767,10 +13752,13 @@ "dev": true }, "qrcode": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.4.2.tgz", - "integrity": "sha512-eR6RgxFYPDFH+zFLTJKtoNP/RlsHANQb52AUmQ2bGDPMuUw7jJb0F+DNEgx7qQGIElrbFxWYMc0/B91zLZPF9Q==", + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.4.4.tgz", + "integrity": "sha512-oLzEC5+NKFou9P0bMj5+v6Z40evexeE29Z9cummZXZ9QXyMr3lphkURzxjXgPJC5azpxcshoDWV1xE46z+/c3Q==", "requires": { + "buffer": "^5.4.3", + "buffer-alloc": "^1.2.0", + "buffer-from": "^1.1.1", "dijkstrajs": "^1.0.1", "isarray": "^2.0.1", "pngjs": "^3.3.0", @@ -16547,9 +16535,9 @@ "dev": true }, "uglify-js": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.5.tgz", - "integrity": "sha512-7L3W+Npia1OCr5Blp4/Vw83tK1mu5gnoIURtT1fUVfQ3Kf8WStWV6NJz0fdoBJZls0KlweruRTLVe6XLafmy5g==", + "version": "3.6.9", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.9.tgz", + "integrity": "sha512-pcnnhaoG6RtrvHJ1dFncAe8Od6Nuy30oaJ82ts6//sGSXOP5UjBMEthiProjXmMNHOfd93sqlkztifFMcb+4yw==", "dev": true, "optional": true, "requires": { @@ -16643,9 +16631,9 @@ } }, "uncontrollable": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.1.0.tgz", - "integrity": "sha512-ie1drnxEKHREeGehl+26HvnCkwhTxxwFGt4FpoBvjmv8qyljIkCHpYVYAwYroTQyXFgEbDc4zoZKtr/EAN9eJw==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.1.1.tgz", + "integrity": "sha512-EcPYhot3uWTS3w00R32R2+vS8Vr53tttrvMj/yA1uYRhf8hbTG2GyugGqWDY0qIskxn0uTTojVd6wPYW9ZEf8Q==", "requires": { "@babel/runtime": "^7.6.3", "@types/react": "^16.9.11", @@ -16740,9 +16728,9 @@ } }, "unistore": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/unistore/-/unistore-3.5.0.tgz", - "integrity": "sha512-jz1iBRHoA3lT5bq8eEFCZExCt0RbIBp8qsKOb0sMTv8ooCx8H4QE1nfynOJtbgJ651y768KO/5Z180Bn196erQ==" + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/unistore/-/unistore-3.5.1.tgz", + "integrity": "sha512-lagQJ5SeDssjh9tYVwOlPloDCLpn0lvHkvOpf+X4q4thnMJ3uLJcT6YO9cuww8K9qsJOyUWKufdfIF3qUe/9ow==" }, "unpipe": { "version": "1.0.0", @@ -17029,27 +17017,34 @@ } }, "vm-browserify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.0.tgz", - "integrity": "sha512-iq+S7vZJE60yejDYM0ek6zg308+UZsdtPExWP9VZoCFCz1zkJoXFnAX7aZfd/ZwrkidzdUZL0C/ryW+JwAiIGw==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", "dev": true }, "vscode-json-languageservice": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-3.4.1.tgz", - "integrity": "sha512-IWJreQ9HtBSyveqaC3UUEArUqCnt5zYLgHewSJ0CvxlIJfvY7yD8GDbLuLxGeHMWwSudYlODit1IfwNzvjZjEg==", + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-3.4.7.tgz", + "integrity": "sha512-y3MN2+/yph3yoIHGmHu4ScYpm285L58XVvfGkd49xTQzLja4apxSbwzsYcP9QsqS0W7KuvoyiPhqksiudoMwjg==", "dev": true, "requires": { "jsonc-parser": "^2.2.0", - "vscode-languageserver-types": "^3.15.0-next.5", + "vscode-languageserver-textdocument": "^1.0.0-next.4", + "vscode-languageserver-types": "^3.15.0-next.6", "vscode-nls": "^4.1.1", "vscode-uri": "^2.1.0" } }, + "vscode-languageserver-textdocument": { + "version": "1.0.0-next.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.0-next.5.tgz", + "integrity": "sha512-1jp/zAidN/bF/sqPimhBX1orH5G4rzRw63k75TesukJDuxm8yW79ECStWbDSy41BHGOwSGN4M69QFvhancSr5A==", + "dev": true + }, "vscode-languageserver-types": { - "version": "3.15.0-next.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0-next.5.tgz", - "integrity": "sha512-7hrELhTeWieUgex3+6692KjCkcmO/+V/bFItM5MHGcBotzwmjEuXjapLLYTYhIspuJ1ibRSik5MhX5YwLpsPiw==", + "version": "3.15.0-next.8", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0-next.8.tgz", + "integrity": "sha512-AEfWrSNyeamWMKPehh/kd3nBnKD9ZGCPhzfxMnW9YNqElSh28G2+Puk3knIQWyaWyV6Bzh28ok9BRJsPzXFCkQ==", "dev": true }, "vscode-nls": { @@ -17788,10 +17783,16 @@ "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", "dev": true }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", "dev": true }, "worker-farm": { diff --git a/front/package.json b/front/package.json index c762a14f7b..9b73fd1a4e 100644 --- a/front/package.json +++ b/front/package.json @@ -38,7 +38,7 @@ "leaflet": "^1.4.0", "linkstate": "^1.1.1", "moment": "^2.24.0", - "preact": "^10.0.1", + "preact": "^10.0.5", "preact-cli-plugin-fast-async": "^1.0.1", "preact-i18n": "^2.0.0-preactx.2", "preact-router": "^3.1.0", diff --git a/front/src/components/app.jsx b/front/src/components/app.jsx index 2bce0a660f..9e363fdd46 100644 --- a/front/src/components/app.jsx +++ b/front/src/components/app.jsx @@ -62,6 +62,7 @@ import ZwaveNodePage from '../routes/integration/all/zwave/node-page'; import ZwaveNetworkPage from '../routes/integration/all/zwave/network-page'; import ZwaveSettingsPage from '../routes/integration/all/zwave/settings-page'; import ZwaveSetupPage from '../routes/integration/all/zwave/setup-page'; +import ZwaveNodeOperationPage from '../routes/integration/all/zwave/node-operation-page'; import RtspCameraPage from '../routes/integration/all/rtsp-camera'; import XiaomiPage from '../routes/integration/all/xiaomi'; import EditXiaomiPage from '../routes/integration/all/xiaomi/edit-page'; @@ -158,6 +159,7 @@ const AppRouter = connect( + @@ -188,10 +190,7 @@ const AppRouter = connect( )); -@connect( - '', - actions -) +@connect('', actions) class MainApp extends Component { componentWillMount() { this.props.checkSession(); diff --git a/front/src/config/i18n/en.json b/front/src/config/i18n/en.json index 5072fad592..2b348f1386 100644 --- a/front/src/config/i18n/en.json +++ b/front/src/config/i18n/en.json @@ -308,7 +308,20 @@ "description": "To use Z-Wave in Gladys, you need to have a Z-Wave USB Stick connected to your Gladys instance.", "zwaveUsbDriverPathLabel": "Select the USB port where your Z-Wave stick is connected", "connectButton": "Connect", - "disconnectButton": "Disconnect" + "disconnectButton": "Disconnect", + "refreshButton": "Refresh USB list", + "notConnected": "Gladys is not connected to any Z-Wave USB stick.", + "connectedWithSuccess": "Z-Wave USB stick connected with success.", + "connecting": "Trying to connect to Z-Wave USB stick...", + "driverFailedError": "An error occured while trying to connect to Z-Wave USB stick." + }, + "nodeOperation": { + "addNodeInstructions": "You can now include your device following instructions in your device manual.", + "removeNodeInstructions": "You can now exclude your device following instructions in your device manual.", + "addNodeTitle": "Inclusion Mode", + "removeNodeTitle": "Exclusion Mode", + "seconds": "seconds remaining", + "cancelButton": "Cancel" } }, "darkSky": { diff --git a/front/src/routes/integration/all/zwave/node-operation-page/AddRemoveNode.jsx b/front/src/routes/integration/all/zwave/node-operation-page/AddRemoveNode.jsx new file mode 100644 index 0000000000..80710f4f0a --- /dev/null +++ b/front/src/routes/integration/all/zwave/node-operation-page/AddRemoveNode.jsx @@ -0,0 +1,36 @@ +import { Text } from 'preact-i18n'; + +const AddNode = ({ children, ...props }) => ( +
+
+

+ {props.action === 'remove' ? ( + + ) : ( + + )} +

+
+ +
+
+
+
+

+ {props.remainingTimeInSeconds} +

+

+ {props.action === 'remove' ? ( + + ) : ( + + )} +

+
+
+
+); + +export default AddNode; diff --git a/front/src/routes/integration/all/zwave/node-operation-page/actions.js b/front/src/routes/integration/all/zwave/node-operation-page/actions.js new file mode 100644 index 0000000000..7540403ec2 --- /dev/null +++ b/front/src/routes/integration/all/zwave/node-operation-page/actions.js @@ -0,0 +1,63 @@ +import { RequestStatus } from '../../../../../utils/consts'; + +const actions = store => { + const actions = { + async addNode(state, e, secure = false) { + if (e) { + e.preventDefault(); + } + store.setState({ + zwaveAddNodeStatus: RequestStatus.Getting + }); + try { + await state.httpClient.post('/api/v1/service/zwave/node/add', { + secure + }); + store.setState({ + zwaveAddNodeStatus: RequestStatus.Success + }); + } catch (e) { + store.setState({ + zwaveAddNodeStatus: RequestStatus.Error + }); + } + }, + async addNodeSecure(state, e) { + actions.addNode(state, e, true); + }, + async cancelZwaveCommand(state) { + store.setState({ + zwaveCancelZwaveCommandStatus: RequestStatus.Getting + }); + try { + await state.httpClient.post('/api/v1/service/zwave/cancel'); + store.setState({ + zwaveCancelZwaveCommandStatus: RequestStatus.Success + }); + } catch (e) { + store.setState({ + zwaveCancelZwaveCommandStatus: RequestStatus.Error + }); + } + }, + async removeNode(state, secure = false) { + store.setState({ + zwaveRemoveNodeStatus: RequestStatus.Getting + }); + try { + await state.httpClient.post('/api/v1/service/zwave/node/remove'); + store.setState({ + zwaveRemoveNodeStatus: RequestStatus.Success + }); + } catch (e) { + store.setState({ + zwaveRemoveNodeStatus: RequestStatus.Error + }); + } + } + }; + + return actions; +}; + +export default actions; diff --git a/front/src/routes/integration/all/zwave/node-operation-page/index.js b/front/src/routes/integration/all/zwave/node-operation-page/index.js new file mode 100644 index 0000000000..988d941fa2 --- /dev/null +++ b/front/src/routes/integration/all/zwave/node-operation-page/index.js @@ -0,0 +1,65 @@ +import { Component } from 'preact'; +import { connect } from 'unistore/preact'; +import { route } from 'preact-router'; +import actions from './actions'; +import ZwavePage from '../ZwavePage'; +import NodeOperationPage from './AddRemoveNode'; + +@connect('session,user,zwaveDevices,houses,getZwaveDevicesStatus', actions) +class ZwaveNodeOperationPage extends Component { + decrementTimer = () => { + this.setState(prevState => { + return { remainingTimeInSeconds: prevState.remainingTimeInSeconds - 1 }; + }); + if (this.state.remainingTimeInSeconds > 1) { + setTimeout(this.decrementTimer, 1000); + } else { + route('/dashboard/integration/device/zwave/setup'); + } + }; + addNode = () => { + this.props.addNode(); + setTimeout(this.decrementTimer, 1000); + }; + addNodeSecure = () => { + this.props.addNodeSecure(); + setTimeout(this.decrementTimer, 1000); + }; + removeNode = () => { + this.props.removeNode(); + setTimeout(this.decrementTimer, 1000); + }; + cancel = () => { + this.props.cancelZwaveCommand(); + route('/dashboard/integration/device/zwave/setup'); + }; + constructor(props) { + super(props); + this.state = { + remainingTimeInSeconds: 60 + }; + } + componentWillMount() { + switch (this.props.action) { + case 'add': + this.addNode(); + break; + case 'add-secure': + this.addNodeSecure(); + break; + case 'remove': + this.removeNode(); + break; + } + } + + render(props, { remainingTimeInSeconds }) { + return ( + + + + ); + } +} + +export default ZwaveNodeOperationPage; diff --git a/front/src/routes/integration/all/zwave/node-page/NodeTab.jsx b/front/src/routes/integration/all/zwave/node-page/NodeTab.jsx index 1c4f55beca..1f4441fa3c 100644 --- a/front/src/routes/integration/all/zwave/node-page/NodeTab.jsx +++ b/front/src/routes/integration/all/zwave/node-page/NodeTab.jsx @@ -44,20 +44,24 @@ const NodeTab = ({ children, ...props }) => (
{props.getZwaveDevicesStatus === RequestStatus.Getting &&
} -
- {props.zwaveDevices && - props.zwaveDevices.map((zwaveDevice, index) => ( - - ))} - {props.zwaveDevices && props.zwaveDevices.length === 0 && } -
+ {props.getZwaveDevicesStatus !== RequestStatus.Getting && ( +
+ {props.zwaveDevices && + props.zwaveDevices.map((zwaveDevice, index) => ( + + ))} + {props.zwaveDevices && props.zwaveDevices.length === 0 && ( + + )} +
+ )}
diff --git a/front/src/routes/integration/all/zwave/settings-page/SettingsTab.jsx b/front/src/routes/integration/all/zwave/settings-page/SettingsTab.jsx index ce3d3c1267..5019cc224e 100644 --- a/front/src/routes/integration/all/zwave/settings-page/SettingsTab.jsx +++ b/front/src/routes/integration/all/zwave/settings-page/SettingsTab.jsx @@ -1,4 +1,6 @@ import { Text } from 'preact-i18n'; +import get from 'get-value'; +import cx from 'classnames'; const SettingsTab = ({ children, ...props }) => (
@@ -6,13 +8,40 @@ const SettingsTab = ({ children, ...props }) => (

+
+ +
-
+
+
-

- -

+ {get(props, 'zwaveStatus.ready') && ( +
+ +
+ )} + {!get(props, 'zwaveStatus.ready') && ( +
+ +
+ )} + {props.zwaveConnectionInProgress && ( +
+ +
+ )} + {props.zwaveDriverFailed && ( +
+ +
+ )}

diff --git a/front/src/routes/integration/all/zwave/settings-page/actions.js b/front/src/routes/integration/all/zwave/settings-page/actions.js index 26d008d67d..48cc377f56 100644 --- a/front/src/routes/integration/all/zwave/settings-page/actions.js +++ b/front/src/routes/integration/all/zwave/settings-page/actions.js @@ -41,7 +41,8 @@ const actions = store => { }, async saveDriverPathAndConnect(state) { store.setState({ - connectZwaveStatus: RequestStatus.Getting + connectZwaveStatus: RequestStatus.Getting, + zwaveDriverFailed: false }); try { await state.httpClient.post('/api/v1/service/zwave/variable/ZWAVE_DRIVER_PATH', { @@ -49,7 +50,8 @@ const actions = store => { }); await state.httpClient.post('/api/v1/service/zwave/connect'); store.setState({ - connectZwaveStatus: RequestStatus.Success + connectZwaveStatus: RequestStatus.Success, + zwaveConnectionInProgress: true }); } catch (e) { store.setState({ @@ -79,6 +81,7 @@ const actions = store => { }); try { await state.httpClient.post('/api/v1/service/zwave/disconnect'); + await actions.getStatus(store.getState()); store.setState({ zwaveDisconnectStatus: RequestStatus.Success }); @@ -87,6 +90,30 @@ const actions = store => { zwaveDisconnectStatus: RequestStatus.Error }); } + }, + async getStatus(state) { + store.setState({ + zwaveGetStatusStatus: RequestStatus.Getting + }); + try { + const zwaveStatus = await state.httpClient.get('/api/v1/service/zwave/status'); + store.setState({ + zwaveStatus, + zwaveConnectionInProgress: false, + zwaveGetStatusStatus: RequestStatus.Success + }); + } catch (e) { + store.setState({ + zwaveGetStatusStatus: RequestStatus.Error, + zwaveConnectionInProgress: false + }); + } + }, + driverFailed(state) { + store.setState({ + zwaveDriverFailed: true, + zwaveConnectionInProgress: false + }); } }; diff --git a/front/src/routes/integration/all/zwave/settings-page/index.js b/front/src/routes/integration/all/zwave/settings-page/index.js index d1117af754..102bf54a1e 100644 --- a/front/src/routes/integration/all/zwave/settings-page/index.js +++ b/front/src/routes/integration/all/zwave/settings-page/index.js @@ -4,22 +4,45 @@ import actions from './actions'; import ZwavePage from '../ZwavePage'; import SettingsTab from './SettingsTab'; import integrationConfig from '../../../../../config/integrations'; +import { RequestStatus } from '../../../../../utils/consts'; +import { WEBSOCKET_MESSAGE_TYPES } from '../../../../../../../server/utils/constants'; @connect( - 'user,usbPorts,zwaveInfos,zwaveDriverPath', + 'user,session,usbPorts,zwaveInfos,zwaveDriverPath,zwaveStatus,getZwaveUsbPortStatus,getCurrentZwaveDriverPathStatus,zwaveGetStatusStatus,zwaveDriverFailed,zwaveDisconnectStatus,connectZwaveStatus,zwaveConnectionInProgress', actions ) class ZwaveSettingsPage extends Component { + driverReadyListener = () => this.props.getStatus(); + driverFailedListener = () => this.props.driverFailed(); + componentWillMount() { this.props.getUsbPorts(); this.props.getInfos(); + this.props.getStatus(); this.props.getCurrentZwaveDriverPath(); + this.props.session.dispatcher.addListener(WEBSOCKET_MESSAGE_TYPES.ZWAVE.DRIVER_READY, this.driverReadyListener); + this.props.session.dispatcher.addListener(WEBSOCKET_MESSAGE_TYPES.ZWAVE.DRIVER_FAILED, this.driverFailedListener); + } + + componentWillUnmount() { + this.props.session.dispatcher.removeListener(WEBSOCKET_MESSAGE_TYPES.ZWAVE.DRIVER_READY, this.driverReadyListener); + this.props.session.dispatcher.removeListener( + WEBSOCKET_MESSAGE_TYPES.ZWAVE.DRIVER_FAILED, + this.driverFailedListener + ); } render(props, {}) { + const loading = + props.getZwaveUsbPortStatus === RequestStatus.Getting || + props.getCurrentZwaveDriverPathStatus === RequestStatus.Getting || + props.zwaveGetStatusStatus === RequestStatus.Getting || + props.zwaveDisconnectStatus === RequestStatus.Getting || + props.connectZwaveStatus === RequestStatus.Getting; + return ( - + ); } diff --git a/front/src/routes/integration/all/zwave/setup-page/NodeTab.jsx b/front/src/routes/integration/all/zwave/setup-page/NodeTab.jsx index eebfd36eff..09a242b620 100644 --- a/front/src/routes/integration/all/zwave/setup-page/NodeTab.jsx +++ b/front/src/routes/integration/all/zwave/setup-page/NodeTab.jsx @@ -18,15 +18,27 @@ const NodeTab = ({ children, ...props }) => { - - - +
@@ -48,6 +60,7 @@ const NodeTab = ({ children, ...props }) => { })} > {props.zwaveNodes && + !get(props, 'zwaveStatus.scanInProgress') && props.zwaveNodes.map((zwaveNode, index) => ( ))} diff --git a/front/src/routes/integration/all/zwave/setup-page/actions.js b/front/src/routes/integration/all/zwave/setup-page/actions.js index 0fbd3ceac7..18b86533a5 100644 --- a/front/src/routes/integration/all/zwave/setup-page/actions.js +++ b/front/src/routes/integration/all/zwave/setup-page/actions.js @@ -91,7 +91,10 @@ const createActions = store => { } } }, - async addNode(state, secure = false) { + async addNode(state, e, secure = false) { + if (e) { + e.preventDefault(); + } store.setState({ zwaveAddNodeStatus: RequestStatus.Getting }); @@ -108,36 +111,36 @@ const createActions = store => { }); } }, - async healNetwork(state) { + async addNodeSecure(state, e) { + actions.addNode(state, e, true); + }, + async stopAddNode(state) { store.setState({ - zwaveHealNetworkStatus: RequestStatus.Getting + zwaveStopAddNodeStatus: RequestStatus.Getting }); try { - await state.httpClient.post('/api/v1/service/zwave/heal'); + await state.httpClient.post('/api/v1/service/zwave/cancel'); store.setState({ - zwaveHealNetworkStatus: RequestStatus.Success + zwaveStopAddNodeStatus: RequestStatus.Success }); } catch (e) { store.setState({ - zwaveHealNetworkStatus: RequestStatus.Error + zwaveStopAddNodeStatus: RequestStatus.Error }); } }, - async addNodeSecure(state) { - actions.addNode(state, true); - }, - async removeNode(state, secure = false) { + async healNetwork(state) { store.setState({ - zwaveRemoveNodeStatus: RequestStatus.Getting + zwaveHealNetworkStatus: RequestStatus.Getting }); try { - await state.httpClient.post('/api/v1/service/zwave/node/remove'); + await state.httpClient.post('/api/v1/service/zwave/heal'); store.setState({ - zwaveRemoveNodeStatus: RequestStatus.Success + zwaveHealNetworkStatus: RequestStatus.Success }); } catch (e) { store.setState({ - zwaveRemoveNodeStatus: RequestStatus.Error + zwaveHealNetworkStatus: RequestStatus.Error }); } }, diff --git a/front/src/routes/integration/all/zwave/setup-page/index.js b/front/src/routes/integration/all/zwave/setup-page/index.js index cccec305ef..7e02d41a5c 100644 --- a/front/src/routes/integration/all/zwave/setup-page/index.js +++ b/front/src/routes/integration/all/zwave/setup-page/index.js @@ -11,17 +11,25 @@ import { WEBSOCKET_MESSAGE_TYPES } from '../../../../../../../server/utils/const actions ) class ZwaveNodePage extends Component { + nodeReadyListener = payload => this.props.addLocalNode(payload); + scanCompleteListener = () => { + this.props.getStatus(); + this.props.getNodes(); + }; componentWillMount() { this.props.getIntegrationByName('zwave'); this.props.getNodes(); this.props.getStatus(); - this.props.session.dispatcher.addListener(WEBSOCKET_MESSAGE_TYPES.ZWAVE.NODE_READY, payload => - this.props.addLocalNode(payload) + this.props.session.dispatcher.addListener(WEBSOCKET_MESSAGE_TYPES.ZWAVE.NODE_READY, this.nodeReadyListener); + this.props.session.dispatcher.addListener(WEBSOCKET_MESSAGE_TYPES.ZWAVE.SCAN_COMPLETE, this.scanCompleteListener); + } + + componentWillUnmount() { + this.props.session.dispatcher.removeListener(WEBSOCKET_MESSAGE_TYPES.ZWAVE.NODE_READY, this.nodeReadyListener); + this.props.session.dispatcher.removeListener( + WEBSOCKET_MESSAGE_TYPES.ZWAVE.SCAN_COMPLETE, + this.scanCompleteListener ); - this.props.session.dispatcher.addListener(WEBSOCKET_MESSAGE_TYPES.ZWAVE.SCAN_COMPLETE, payload => { - this.props.getStatus(); - this.props.getNodes(); - }); } render(props, {}) { diff --git a/server/services/zwave/api/zwave.controller.js b/server/services/zwave/api/zwave.controller.js index 27d733ae74..e5c61b33cb 100644 --- a/server/services/zwave/api/zwave.controller.js +++ b/server/services/zwave/api/zwave.controller.js @@ -69,6 +69,7 @@ module.exports = function ZwaveController(gladys, zwaveManager, serviceId) { res.json({ connected: zwaveManager.connected, scanInProgress: zwaveManager.scanInProgress, + ready: zwaveManager.ready, }); } @@ -107,6 +108,18 @@ module.exports = function ZwaveController(gladys, zwaveManager, serviceId) { }); } + /** + * @api {post} /api/v1/service/zwave/cancel Cancel ZWave command + * @apiName cancelCommand + * @apiGroup Zwave + */ + async function cancelControllerCommand(req, res) { + zwaveManager.cancelControllerCommand(); + res.json({ + success: true, + }); + } + /** * @api {post} /api/v1/service/zwave/params/refresh Refresh params * @apiName refreshParams @@ -160,5 +173,9 @@ module.exports = function ZwaveController(gladys, zwaveManager, serviceId) { authenticated: true, controller: asyncMiddleware(removeNode), }, + 'post /api/v1/service/zwave/cancel': { + authenticated: true, + controller: asyncMiddleware(cancelControllerCommand), + }, }; }; diff --git a/server/services/zwave/lib/commands/zwave.addNode.js b/server/services/zwave/lib/commands/zwave.addNode.js index fc968fee13..a10dc961db 100644 --- a/server/services/zwave/lib/commands/zwave.addNode.js +++ b/server/services/zwave/lib/commands/zwave.addNode.js @@ -1,5 +1,7 @@ const logger = require('../../../../utils/logger'); +const ADD_NODE_TIMEOUT = 60 * 1000; + /** * @description Add node * @param {boolean} secure - Secure node. @@ -9,6 +11,7 @@ const logger = require('../../../../utils/logger'); function addNode(secure = false) { logger.debug(`Zwave : Entering inclusion mode`); this.zwave.addNode(secure); + setTimeout(this.cancelControllerCommand.bind(this), ADD_NODE_TIMEOUT); } module.exports = { diff --git a/server/services/zwave/lib/commands/zwave.cancelControllerCommand.js b/server/services/zwave/lib/commands/zwave.cancelControllerCommand.js new file mode 100644 index 0000000000..5aaf3fa66d --- /dev/null +++ b/server/services/zwave/lib/commands/zwave.cancelControllerCommand.js @@ -0,0 +1,15 @@ +const logger = require('../../../../utils/logger'); + +/** + * @description Cancel + * @example + * zwave.cancelControllerCommand(); + */ +function cancelControllerCommand() { + logger.debug(`Zwave : Cancelling controller command`); + this.zwave.cancelControllerCommand(); +} + +module.exports = { + cancelControllerCommand, +}; diff --git a/server/services/zwave/lib/commands/zwave.connect.js b/server/services/zwave/lib/commands/zwave.connect.js index 2d34f29db5..42af33e5c2 100644 --- a/server/services/zwave/lib/commands/zwave.connect.js +++ b/server/services/zwave/lib/commands/zwave.connect.js @@ -1,3 +1,4 @@ +const os = require('os'); const logger = require('../../../../utils/logger'); /** @@ -8,7 +9,14 @@ const logger = require('../../../../utils/logger'); */ function connect(driverPath) { logger.debug(`Zwave : Connecting to USB = ${driverPath}`); - this.zwave.connect(driverPath); + // special case for macOS + if (os.platform() === 'darwin') { + this.driverPath = driverPath.replace('/dev/tty.', '/dev/cu.'); + } else { + this.driverPath = driverPath; + } + this.ready = false; + this.zwave.connect(this.driverPath); this.connected = true; } diff --git a/server/services/zwave/lib/commands/zwave.disconnect.js b/server/services/zwave/lib/commands/zwave.disconnect.js index d79a34a001..9fdfe9ce53 100644 --- a/server/services/zwave/lib/commands/zwave.disconnect.js +++ b/server/services/zwave/lib/commands/zwave.disconnect.js @@ -6,9 +6,15 @@ const logger = require('../../../../utils/logger'); * zwave.disconnect(); */ function disconnect() { - logger.debug(`Zwave : Disconnecting...`); - this.zwave.disconnect(); + if (this.driverPath && this.connected) { + logger.debug(`Zwave : Disconnecting...`); + this.zwave.disconnect(this.driverPath); + } else { + logger.debug('Zwave: Not connected, disconnecting'); + } this.connected = false; + this.ready = false; + this.scanInProgress = false; } module.exports = { diff --git a/server/services/zwave/lib/commands/zwave.removeNode.js b/server/services/zwave/lib/commands/zwave.removeNode.js index 12f3132319..67f5aa8a4e 100644 --- a/server/services/zwave/lib/commands/zwave.removeNode.js +++ b/server/services/zwave/lib/commands/zwave.removeNode.js @@ -1,5 +1,7 @@ const logger = require('../../../../utils/logger'); +const REMOVE_NODE_TIMEOUT = 60 * 1000; + /** * @description Add node * @example @@ -8,6 +10,7 @@ const logger = require('../../../../utils/logger'); function removeNode() { logger.debug(`Zwave : Entering exclusion mode`); this.zwave.removeNode(); + setTimeout(this.cancelControllerCommand.bind(this), REMOVE_NODE_TIMEOUT); } module.exports = { diff --git a/server/services/zwave/lib/events/zwave.driverFailed.js b/server/services/zwave/lib/events/zwave.driverFailed.js index 9a136cd3b1..eee59c4529 100644 --- a/server/services/zwave/lib/events/zwave.driverFailed.js +++ b/server/services/zwave/lib/events/zwave.driverFailed.js @@ -1,4 +1,5 @@ const logger = require('../../../../utils/logger'); +const { EVENTS, WEBSOCKET_MESSAGE_TYPES } = require('../../../../utils/constants'); /** * @description When the driver failed to start. @@ -8,6 +9,11 @@ const logger = require('../../../../utils/logger'); function driverFailed() { logger.debug(`Zwave : Failed to start driver.`); this.connected = false; + this.ready = false; + this.eventManager.emit(EVENTS.WEBSOCKET.SEND_ALL, { + type: WEBSOCKET_MESSAGE_TYPES.ZWAVE.DRIVER_FAILED, + payload: {}, + }); } module.exports = { diff --git a/server/services/zwave/lib/events/zwave.driverReady.js b/server/services/zwave/lib/events/zwave.driverReady.js index afa00339d0..774e688f77 100644 --- a/server/services/zwave/lib/events/zwave.driverReady.js +++ b/server/services/zwave/lib/events/zwave.driverReady.js @@ -1,4 +1,5 @@ const logger = require('../../../../utils/logger'); +const { EVENTS, WEBSOCKET_MESSAGE_TYPES } = require('../../../../utils/constants'); /** * @description When the driver is ready. @@ -9,6 +10,11 @@ const logger = require('../../../../utils/logger'); function driverReady(homeId) { logger.debug(`Zwave : Driver is ready. homeId = ${homeId}`); this.scanInProgress = true; + this.ready = true; + this.eventManager.emit(EVENTS.WEBSOCKET.SEND_ALL, { + type: WEBSOCKET_MESSAGE_TYPES.ZWAVE.DRIVER_READY, + payload: {}, + }); } module.exports = { diff --git a/server/services/zwave/lib/index.js b/server/services/zwave/lib/index.js index 25706b92f0..f1f2b8572e 100644 --- a/server/services/zwave/lib/index.js +++ b/server/services/zwave/lib/index.js @@ -14,6 +14,7 @@ const { controllerCommand } = require('./events/zwave.controllerCommand'); // COMMANDS const { addNode } = require('./commands/zwave.addNode'); const { connect } = require('./commands/zwave.connect'); +const { cancelControllerCommand } = require('./commands/zwave.cancelControllerCommand'); const { disconnect } = require('./commands/zwave.disconnect'); const { healNetwork } = require('./commands/zwave.healNetwork'); const { refreshNodeParams } = require('./commands/zwave.refreshNodeParams'); @@ -65,6 +66,7 @@ ZwaveManager.prototype.controllerCommand = controllerCommand; // COMMANDS ZwaveManager.prototype.addNode = addNode; ZwaveManager.prototype.connect = connect; +ZwaveManager.prototype.cancelControllerCommand = cancelControllerCommand; ZwaveManager.prototype.disconnect = disconnect; ZwaveManager.prototype.healNetwork = healNetwork; ZwaveManager.prototype.refreshNodeParams = refreshNodeParams; diff --git a/server/utils/constants.js b/server/utils/constants.js index e41e405e8f..080579c7fd 100644 --- a/server/utils/constants.js +++ b/server/utils/constants.js @@ -339,6 +339,8 @@ const WEBSOCKET_MESSAGE_TYPES = { DOWNLOAD_FAILED: 'upgrade.download-failed', }, ZWAVE: { + DRIVER_READY: 'zwave.driver-ready', + DRIVER_FAILED: 'zwave.driver-failed', NODE_READY: 'zwave.node-ready', SCAN_COMPLETE: 'zwave.scan-complete', },