From 4e9a7ebd3850b0f226eb9f46eea591c8619a79fc Mon Sep 17 00:00:00 2001 From: Jarrett Vance Date: Mon, 1 Feb 2021 17:57:55 -0600 Subject: [PATCH 01/13] feat: Improved add remove device UX --- .gitignore | 3 + src/components/ControlPanel.vue | 133 ++++++++++++++++++--- src/components/dialogs/DialogAddRemove.vue | 81 +++++++++++++ 3 files changed, 198 insertions(+), 19 deletions(-) create mode 100644 src/components/dialogs/DialogAddRemove.vue diff --git a/.gitignore b/.gitignore index 474e1b351cb..59af318de05 100644 --- a/.gitignore +++ b/.gitignore @@ -73,3 +73,6 @@ typings/ *.ntvs* *.njsproj *.sln + +# ignore settings +src/store/settings.json diff --git a/src/components/ControlPanel.vue b/src/components/ControlPanel.vue index 9bef38d3333..f570b17adc2 100644 --- a/src/components/ControlPanel.vue +++ b/src/components/ControlPanel.vue @@ -27,6 +27,16 @@ + + + Add/Remove Device + + + + + { + this.now = new Date() + + let s = Math.trunc((this.addRemoveEndDate - this.now)/1000) + this.addRemoveWorking = s > 0 ? `${this.addRemoveMode} started: ${s}s remaining` : null + + if(this.now > newVal){ + this.now = newVal + clearInterval(this.addRemoveTimer) + } + }, 100) + } + }, + appInfo : { + deep : true, + handler(newVal){ + if (newVal.controllerStatus.indexOf('clusion') > 0) { + this.onAddRemoveStateChange(newVal.controllerStatus) + } + } + } }, - watch: {}, data () { return { settings: new Settings(localStorage), + now: new Date(), + addRemoveShowDialog: false, + addRemoveMode: null, + addRemoveWorking: null, + addRemoveSucceeded: null, + addRemoveFailed: null, + addRemoveEndDate: new Date(), + addRemoveTimer : null, + addRemoveStartNodeCount: 0, node_actions: [ { text: 'Heal node', @@ -127,22 +185,6 @@ export default { ], cnt_action: 'healNetwork', cnt_actions: [ - { - text: 'Start inclusion', - value: 'startInclusion' - }, - { - text: 'Stop inclusion', - value: 'stopInclusion' - }, - { - text: 'Start exclusion', - value: 'startExclusion' - }, - { - text: 'Stop exclusion', - value: 'stopExclusion' - }, { text: 'Heal Network', value: 'beginHealingNetwork' @@ -201,6 +243,58 @@ export default { }) }, + async onAddRemoveAction(data) { + this.addRemoveMode = data.mode + this.addRemoveSucceeded = null + this.addRemoveFailed = null + const startStop = this.addRemoveWorking ? "stop" : "start" + this.addRemoveWorking = null + const cnt_action = data.mode === 'Exclusion' ? `${startStop}Exclusion` : `${startStop}Inclusion` + const args = [] + if (data.mode !== "Exclusion" && startStop === "start") args.push(data.mode.indexOf('Secure') === 0) + this.apiRequest(cnt_action, args) + }, + + onAddRemoveStateChange(controllerStatus) { + const nodeCount = this.addRemoveMode === 'Exclusion' ? + this.nodes.filter(x => x.status === 'Removed').length : + this.nodes.filter(x => x.status !== 'Removed').length + console.debug(`onAddRemoveStateChange:${controllerStatus} ${this.addRemoveStartNodeCount}→${nodeCount}`) + if (controllerStatus.indexOf("started") > 0) { + this.addRemoveEndDate = new Date(new Date().getTime() + (this.zwave.commandsTimeout * 1000)) + this.addRemoveStartNodeCount = nodeCount + } else if (controllerStatus.indexOf("stopped") > 0) { + this.addRemoveEndDate = this.now + if (this.addRemoveStartNodeCount === nodeCount) { + this.addRemoveSucceeded = `${this.addRemoveMode} finished, discovering...` + setTimeout(this.showResults, 5000) // add some discovery time if no changes detected + } else { + this.showResults() + } + } else { + this.addRemoveEndDate = this.now + this.addRemoveFailed = controllerStatus // TODO: better formatting? + } + }, + + showResults() { + const nodeCount = this.addRemoveMode === 'Exclusion' ? + this.nodes.filter(x => x.status === 'Removed').length : + this.nodes.filter(x => x.status !== 'Removed').length + console.debug(`showResults:${this.addRemoveMode} ${this.addRemoveStartNodeCount}→${nodeCount}`) + if (this.addRemoveStartNodeCount === nodeCount) { + this.addRemoveSucceeded = null + this.addRemoveFailed = `${this.addRemoveMode} stopped, none found` + } else if (this.addRemoveMode === 'Exclusion') { + this.addRemoveSucceeded = `Device found! Exclusion complete` + this.addRemoveFailed = null + } else if (this.addRemoveStartNodeCount > 0) { + const node = this.nodes[this.nodes.length - 1] + this.addRemoveSucceeded = `Device found! Node ${node.id} added ${node.isSecure ? 'with' : 'without'} security` + this.addRemoveFailed = null + } + }, + async sendCntAction () { if (this.cnt_action) { const args = [] @@ -340,6 +434,7 @@ export default { // unbind events this.socket.off(socketEvents.api) } + clearInterval(this.addRemoveTimer) } } diff --git a/src/components/dialogs/DialogAddRemove.vue b/src/components/dialogs/DialogAddRemove.vue new file mode 100644 index 00000000000..24b21767386 --- /dev/null +++ b/src/components/dialogs/DialogAddRemove.vue @@ -0,0 +1,81 @@ + + + + + From 97513999df1341aea22450db2b9237c1cae39464 Mon Sep 17 00:00:00 2001 From: Jarrett Vance Date: Mon, 1 Feb 2021 19:01:35 -0600 Subject: [PATCH 02/13] Lint cleanup --- src/components/ControlPanel.vue | 54 +++++++++++----------- src/components/dialogs/DialogAddRemove.vue | 6 +-- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/components/ControlPanel.vue b/src/components/ControlPanel.vue index f570b17adc2..d41a30a09b7 100644 --- a/src/components/ControlPanel.vue +++ b/src/components/ControlPanel.vue @@ -56,7 +56,7 @@ - + { + this.timer = setInterval(() => { this.now = new Date() - let s = Math.trunc((this.addRemoveEndDate - this.now)/1000) + const s = Math.trunc((this.addRemoveEndDate - this.now) / 1000) this.addRemoveWorking = s > 0 ? `${this.addRemoveMode} started: ${s}s remaining` : null - if(this.now > newVal){ + if (this.now > newVal) { this.now = newVal clearInterval(this.addRemoveTimer) } }, 100) } }, - appInfo : { - deep : true, - handler(newVal){ + appInfo: { + deep: true, + handler(newVal) { if (newVal.controllerStatus.indexOf('clusion') > 0) { this.onAddRemoveStateChange(newVal.controllerStatus) } @@ -243,27 +243,27 @@ export default { }) }, - async onAddRemoveAction(data) { + async onAddRemoveAction (data) { this.addRemoveMode = data.mode this.addRemoveSucceeded = null this.addRemoveFailed = null - const startStop = this.addRemoveWorking ? "stop" : "start" + const startStop = this.addRemoveWorking ? 'stop' : 'start' this.addRemoveWorking = null - const cnt_action = data.mode === 'Exclusion' ? `${startStop}Exclusion` : `${startStop}Inclusion` + const action = data.mode === 'Exclusion' ? `${startStop}Exclusion` : `${startStop}Inclusion` const args = [] - if (data.mode !== "Exclusion" && startStop === "start") args.push(data.mode.indexOf('Secure') === 0) - this.apiRequest(cnt_action, args) + if (data.mode !== 'Exclusion' && startStop === 'start') args.push(data.mode.indexOf('Secure') === 0) + this.apiRequest(action, args) }, - onAddRemoveStateChange(controllerStatus) { - const nodeCount = this.addRemoveMode === 'Exclusion' ? - this.nodes.filter(x => x.status === 'Removed').length : - this.nodes.filter(x => x.status !== 'Removed').length + onAddRemoveStateChange (controllerStatus) { + const nodeCount = this.addRemoveMode === 'Exclusion' + ? this.nodes.filter(x => x.status === 'Removed').length + : this.nodes.filter(x => x.status !== 'Removed').length console.debug(`onAddRemoveStateChange:${controllerStatus} ${this.addRemoveStartNodeCount}→${nodeCount}`) - if (controllerStatus.indexOf("started") > 0) { + if (controllerStatus.indexOf('started') > 0) { this.addRemoveEndDate = new Date(new Date().getTime() + (this.zwave.commandsTimeout * 1000)) this.addRemoveStartNodeCount = nodeCount - } else if (controllerStatus.indexOf("stopped") > 0) { + } else if (controllerStatus.indexOf('stopped') > 0) { this.addRemoveEndDate = this.now if (this.addRemoveStartNodeCount === nodeCount) { this.addRemoveSucceeded = `${this.addRemoveMode} finished, discovering...` @@ -278,15 +278,15 @@ export default { }, showResults() { - const nodeCount = this.addRemoveMode === 'Exclusion' ? - this.nodes.filter(x => x.status === 'Removed').length : - this.nodes.filter(x => x.status !== 'Removed').length + const nodeCount = this.addRemoveMode === 'Exclusion' + ? this.nodes.filter(x => x.status === 'Removed').length + : this.nodes.filter(x => x.status !== 'Removed').length console.debug(`showResults:${this.addRemoveMode} ${this.addRemoveStartNodeCount}→${nodeCount}`) if (this.addRemoveStartNodeCount === nodeCount) { this.addRemoveSucceeded = null this.addRemoveFailed = `${this.addRemoveMode} stopped, none found` } else if (this.addRemoveMode === 'Exclusion') { - this.addRemoveSucceeded = `Device found! Exclusion complete` + this.addRemoveSucceeded = 'Device found! Exclusion complete' this.addRemoveFailed = null } else if (this.addRemoveStartNodeCount > 0) { const node = this.nodes[this.nodes.length - 1] diff --git a/src/components/dialogs/DialogAddRemove.vue b/src/components/dialogs/DialogAddRemove.vue index 24b21767386..e75724d3e3a 100644 --- a/src/components/dialogs/DialogAddRemove.vue +++ b/src/components/dialogs/DialogAddRemove.vue @@ -59,15 +59,15 @@ export default { value: Boolean, // show or hide working: String, failed: String, - succeeded: String, + succeeded: String }, watch: { }, computed: { }, data () { - return { - mode: "Inclusion", // most common action should be default + return { + mode: 'Inclusion' // most common action should be default } }, methods: { From fbf319c6056b857fe9e139b1db39e05628eebee9 Mon Sep 17 00:00:00 2001 From: Jarrett Vance Date: Mon, 1 Feb 2021 19:03:49 -0600 Subject: [PATCH 03/13] More lint --- src/components/ControlPanel.vue | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/ControlPanel.vue b/src/components/ControlPanel.vue index d41a30a09b7..e2c4c960afb 100644 --- a/src/components/ControlPanel.vue +++ b/src/components/ControlPanel.vue @@ -103,7 +103,7 @@ export default { watch: { addRemoveEndDate: { immediate: true, - handler(newVal) { + handler (newVal) { if (this.addRemoveTimer) { clearInterval(this.addRemoveTimer) } @@ -122,7 +122,7 @@ export default { }, appInfo: { deep: true, - handler(newVal) { + handler (newVal) { if (newVal.controllerStatus.indexOf('clusion') > 0) { this.onAddRemoveStateChange(newVal.controllerStatus) } @@ -139,7 +139,7 @@ export default { addRemoveSucceeded: null, addRemoveFailed: null, addRemoveEndDate: new Date(), - addRemoveTimer : null, + addRemoveTimer: null, addRemoveStartNodeCount: 0, node_actions: [ { @@ -277,7 +277,7 @@ export default { } }, - showResults() { + showResults () { const nodeCount = this.addRemoveMode === 'Exclusion' ? this.nodes.filter(x => x.status === 'Removed').length : this.nodes.filter(x => x.status !== 'Removed').length From 1d08ad60354785a1d9d852fb7dfacc78ec0bac6f Mon Sep 17 00:00:00 2001 From: Jarrett Vance Date: Tue, 2 Feb 2021 05:42:18 -0600 Subject: [PATCH 04/13] Fix timer --- .gitignore | 3 --- src/components/ControlPanel.vue | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 59af318de05..474e1b351cb 100644 --- a/.gitignore +++ b/.gitignore @@ -73,6 +73,3 @@ typings/ *.ntvs* *.njsproj *.sln - -# ignore settings -src/store/settings.json diff --git a/src/components/ControlPanel.vue b/src/components/ControlPanel.vue index e2c4c960afb..6d07affb238 100644 --- a/src/components/ControlPanel.vue +++ b/src/components/ControlPanel.vue @@ -107,7 +107,7 @@ export default { if (this.addRemoveTimer) { clearInterval(this.addRemoveTimer) } - this.timer = setInterval(() => { + this.addRemoveTimer = setInterval(() => { this.now = new Date() const s = Math.trunc((this.addRemoveEndDate - this.now) / 1000) From 030e2639f4d143ca457559c50299a60c864555cc Mon Sep 17 00:00:00 2001 From: Jarrett Vance Date: Tue, 2 Feb 2021 12:01:59 -0600 Subject: [PATCH 05/13] use socket events --- lib/SocketManager.js | 1 + lib/ZwaveClient.js | 1 + src/components/ControlPanel.vue | 89 +++++++++++++--------- src/components/dialogs/DialogAddRemove.vue | 8 +- 4 files changed, 60 insertions(+), 39 deletions(-) diff --git a/lib/SocketManager.js b/lib/SocketManager.js index 6bd36552e89..22fc3b6a186 100644 --- a/lib/SocketManager.js +++ b/lib/SocketManager.js @@ -11,6 +11,7 @@ const socketEvents = { init: 'INIT', // automatically sent when a new client connects to the socket controller: 'CONTROLLER_CMD', // controller status updates connected: 'CONNECTED', // socket status + nodeAdded: 'NODE_ADDED', nodeRemoved: 'NODE_REMOVED', nodeUpdated: 'NODE_UPDATED', valueUpdated: 'VALUE_UPDATED', diff --git a/lib/ZwaveClient.js b/lib/ZwaveClient.js index 2fd111bf9f5..0a48e2c7503 100644 --- a/lib/ZwaveClient.js +++ b/lib/ZwaveClient.js @@ -245,6 +245,7 @@ function onNodeAdded (zwaveNode) { // the driver is ready so this node has been added on fly if (this.driverReady) { addNode.call(this, zwaveNode) + this.sendToSocket(socketEvents.onNodeAdded, zwaveNode) } this.emit( diff --git a/src/components/ControlPanel.vue b/src/components/ControlPanel.vue index 6d07affb238..66d9e78ab2f 100644 --- a/src/components/ControlPanel.vue +++ b/src/components/ControlPanel.vue @@ -59,6 +59,7 @@ { this.now = new Date() - const s = Math.trunc((this.addRemoveEndDate - this.now) / 1000) - this.addRemoveWorking = s > 0 ? `${this.addRemoveMode} started: ${s}s remaining` : null - + if (this.addRemoveStatus === 'start') { + this.addRemoveWorking = `${this.addRemoveMode} started: ${s}s remaining` + } if (this.now > newVal) { this.now = newVal clearInterval(this.addRemoveTimer) @@ -120,11 +126,10 @@ export default { }, 100) } }, - appInfo: { - deep: true, + controllerStatus: { handler (newVal) { - if (newVal.controllerStatus.indexOf('clusion') > 0) { - this.onAddRemoveStateChange(newVal.controllerStatus) + if (newVal.indexOf('clusion') > 0) { + this.onAddRemoveStateChange(newVal) } } } @@ -135,12 +140,13 @@ export default { now: new Date(), addRemoveShowDialog: false, addRemoveMode: null, + addRemoveStatus: "stop", addRemoveWorking: null, addRemoveSucceeded: null, addRemoveFailed: null, addRemoveEndDate: new Date(), addRemoveTimer: null, - addRemoveStartNodeCount: 0, + addRemoveNode: null, node_actions: [ { text: 'Heal node', @@ -244,55 +250,52 @@ export default { }, async onAddRemoveAction (data) { + this.addRemoveStatus = 'wait' // make them wait for actual action to happen this.addRemoveMode = data.mode + this.addRemoveEndDate = this.now this.addRemoveSucceeded = null this.addRemoveFailed = null - const startStop = this.addRemoveWorking ? 'stop' : 'start' - this.addRemoveWorking = null - const action = data.mode === 'Exclusion' ? `${startStop}Exclusion` : `${startStop}Inclusion` + this.addRemoveWorking = `${data.mode} ${data.status === 'start' ? 'starting…' : 'stopping…'}` + const action = data.mode === 'Exclusion' ? `${data.status}Exclusion` : `${data.status}Inclusion` const args = [] - if (data.mode !== 'Exclusion' && startStop === 'start') args.push(data.mode.indexOf('Secure') === 0) + if (data.isSecure) args.push(data.mode.indexOf('Secure') === 0) this.apiRequest(action, args) }, onAddRemoveStateChange (controllerStatus) { - const nodeCount = this.addRemoveMode === 'Exclusion' - ? this.nodes.filter(x => x.status === 'Removed').length - : this.nodes.filter(x => x.status !== 'Removed').length - console.debug(`onAddRemoveStateChange:${controllerStatus} ${this.addRemoveStartNodeCount}→${nodeCount}`) + if (this.addRemoveMode === null) return // ignore initial status + if (controllerStatus.indexOf('started') > 0) { - this.addRemoveEndDate = new Date(new Date().getTime() + (this.zwave.commandsTimeout * 1000)) - this.addRemoveStartNodeCount = nodeCount + this.addRemoveEndDate = new Date(new Date().getTime() + this.timeoutMs) + this.addRemoveNode = null + this.addRemoveStatus = 'start' } else if (controllerStatus.indexOf('stopped') > 0) { this.addRemoveEndDate = this.now - if (this.addRemoveStartNodeCount === nodeCount) { - this.addRemoveSucceeded = `${this.addRemoveMode} finished, discovering...` - setTimeout(this.showResults, 5000) // add some discovery time if no changes detected - } else { - this.showResults() - } + this.addRemoveWorking = `${this.addRemoveMode} stopped, discovering…` + this.addRemoveStatus = 'wait' + setTimeout(this.showResults, 5000) // add additional discovery time } else { this.addRemoveEndDate = this.now + this.addRemoveWorking = null this.addRemoveFailed = controllerStatus // TODO: better formatting? + this.addRemoveStatus = 'stop' } }, showResults () { - const nodeCount = this.addRemoveMode === 'Exclusion' - ? this.nodes.filter(x => x.status === 'Removed').length - : this.nodes.filter(x => x.status !== 'Removed').length - console.debug(`showResults:${this.addRemoveMode} ${this.addRemoveStartNodeCount}→${nodeCount}`) - if (this.addRemoveStartNodeCount === nodeCount) { - this.addRemoveSucceeded = null + this.addRemoveWorking = null + this.addRemoveSucceeded = null + this.addRemoveFailed = null + + if (this.addRemoveNode == null) { this.addRemoveFailed = `${this.addRemoveMode} stopped, none found` } else if (this.addRemoveMode === 'Exclusion') { - this.addRemoveSucceeded = 'Device found! Exclusion complete' - this.addRemoveFailed = null + this.addRemoveSucceeded = `Device found! Node ${this.addRemoveNode.id} removed` } else if (this.addRemoveStartNodeCount > 0) { - const node = this.nodes[this.nodes.length - 1] - this.addRemoveSucceeded = `Device found! Node ${node.id} added ${node.isSecure ? 'with' : 'without'} security` - this.addRemoveFailed = null + this.addRemoveSucceeded = `Device found! Node ${this.addRemoveNode.id} added ${this.addRemoveNode.isSecure ? 'with' : 'without'} security` } + + this.addRemoveStatus = 'stop' }, async sendCntAction () { @@ -428,11 +431,23 @@ export default { ) } }) + + this.socket.on(socketEvents.nodeRemoved, async node => { + console.debug(node) + this.addRemoveNode = node + }) + + this.socket.on(socketEvents.nodeAdded, async node => { + console.debug(node) + this.addRemoveNode = node + }) }, beforeDestroy () { if (this.socket) { // unbind events this.socket.off(socketEvents.api) + this.socket.off(socketEvents.nodeRemoved) + this.socket.off(socketEvents.nodeAdded) } clearInterval(this.addRemoveTimer) } diff --git a/src/components/dialogs/DialogAddRemove.vue b/src/components/dialogs/DialogAddRemove.vue index e75724d3e3a..57037a96aa7 100644 --- a/src/components/dialogs/DialogAddRemove.vue +++ b/src/components/dialogs/DialogAddRemove.vue @@ -43,8 +43,8 @@ - Close - {{ working ? 'Stop' : 'Start' }} + Close + {{ nextStatus }} @@ -57,6 +57,7 @@ export default { }, props: { value: Boolean, // show or hide + status: String, working: String, failed: String, succeeded: String @@ -64,6 +65,9 @@ export default { watch: { }, computed: { + nextStatus () { + return this.status === 'stop' ? 'start' : 'stop' + } }, data () { return { From 8ae8c087c0e6eb9833c4bfec65fab24bf29c3c8a Mon Sep 17 00:00:00 2001 From: Jarrett Vance Date: Tue, 2 Feb 2021 15:03:30 -0600 Subject: [PATCH 06/13] changes suggested by @robertsLando --- lib/ZwaveClient.js | 4 +- src/components/ControlPanel.vue | 103 ++++++++++----------- src/components/dialogs/DialogAddRemove.vue | 98 +++++++++++++++----- 3 files changed, 124 insertions(+), 81 deletions(-) diff --git a/lib/ZwaveClient.js b/lib/ZwaveClient.js index 0a48e2c7503..3392f536bb4 100644 --- a/lib/ZwaveClient.js +++ b/lib/ZwaveClient.js @@ -245,7 +245,7 @@ function onNodeAdded (zwaveNode) { // the driver is ready so this node has been added on fly if (this.driverReady) { addNode.call(this, zwaveNode) - this.sendToSocket(socketEvents.onNodeAdded, zwaveNode) + this.sendToSocket(socketEvents.nodeAdded, this.nodes[zwaveNode.id]) } this.emit( @@ -744,7 +744,7 @@ function removeNode (nodeid) { this.emit('nodeRemoved', node) this.addEmptyNodes() - this.sendToSocket(socketEvents.nodeRemoved, this.nodes[nodeid]) + this.sendToSocket(socketEvents.nodeRemoved, node) } } diff --git a/src/components/ControlPanel.vue b/src/components/ControlPanel.vue index 66d9e78ab2f..8be39bbe483 100644 --- a/src/components/ControlPanel.vue +++ b/src/components/ControlPanel.vue @@ -101,35 +101,46 @@ export default { computed: { ...mapGetters(['nodes', 'appInfo', 'zwave']), timeoutMs () { - return (this.zwave.commandsTimeout * 1000) + 800 // add small buffer + return this.zwave.commandsTimeout * 1000 + 800 // add small buffer }, controllerStatus () { return this.appInfo.controllerStatus } }, watch: { - addRemoveEndDate: { - handler (newVal) { - if (this.addRemoveTimer) { - clearInterval(this.addRemoveTimer) - } - this.addRemoveTimer = setInterval(() => { - this.now = new Date() - const s = Math.trunc((this.addRemoveEndDate - this.now) / 1000) - if (this.addRemoveStatus === 'start') { - this.addRemoveWorking = `${this.addRemoveMode} started: ${s}s remaining` - } - if (this.now > newVal) { - this.now = newVal - clearInterval(this.addRemoveTimer) - } - }, 100) + addRemoveEndDate (newVal) { + if (this.addRemoveTimer) { + clearInterval(this.addRemoveTimer) } + this.addRemoveTimer = setInterval(() => { + const now = new Date() + const s = Math.trunc((this.addRemoveEndDate - now) / 1000) + if (this.addRemoveStatus === 'start') { + this.addRemoveWorking = `${this.addRemoveName} started: ${s}s remaining` + } + if (now > newVal) clearInterval(this.addRemoveTimer) + }, 100) }, - controllerStatus: { - handler (newVal) { - if (newVal.indexOf('clusion') > 0) { - this.onAddRemoveStateChange(newVal) + controllerStatus (newVal) { + if (newVal.indexOf('clusion') > 0) { + if (this.addRemoveName === null) return // ignore initial status + + if (newVal.indexOf('started') > 0) { + this.addRemoveEndDate = new Date( + new Date().getTime() + this.timeoutMs + ) + this.addRemoveNode = null + this.addRemoveStatus = 'start' + } else if (newVal.indexOf('stopped') > 0) { + this.addRemoveEndDate = new Date() + this.addRemoveWorking = `${this.addRemoveName} stopped, discovering…` + this.addRemoveStatus = 'wait' + setTimeout(this.showResults, 5000) // add additional discovery time + } else { + this.addRemoveEndDate = new Date() + this.addRemoveWorking = null + this.addRemoveFailed = newVal // TODO: better formatting? + this.addRemoveStatus = 'stop' } } } @@ -137,10 +148,9 @@ export default { data () { return { settings: new Settings(localStorage), - now: new Date(), addRemoveShowDialog: false, - addRemoveMode: null, - addRemoveStatus: "stop", + addRemoveName: null, + addRemoveStatus: 'stop', addRemoveWorking: null, addRemoveSucceeded: null, addRemoveFailed: null, @@ -250,36 +260,17 @@ export default { }, async onAddRemoveAction (data) { - this.addRemoveStatus = 'wait' // make them wait for actual action to happen - this.addRemoveMode = data.mode - this.addRemoveEndDate = this.now + this.addRemoveStatus = 'wait' // make sure user can't trigger another action too soon + this.addRemoveName = data.name + this.addRemoveEndDate = new Date() this.addRemoveSucceeded = null this.addRemoveFailed = null - this.addRemoveWorking = `${data.mode} ${data.status === 'start' ? 'starting…' : 'stopping…'}` - const action = data.mode === 'Exclusion' ? `${data.status}Exclusion` : `${data.status}Inclusion` + this.addRemoveWorking = `${data.name} ${ + data.method === 'start' ? 'starting…' : 'stopping…' + }` const args = [] - if (data.isSecure) args.push(data.mode.indexOf('Secure') === 0) - this.apiRequest(action, args) - }, - - onAddRemoveStateChange (controllerStatus) { - if (this.addRemoveMode === null) return // ignore initial status - - if (controllerStatus.indexOf('started') > 0) { - this.addRemoveEndDate = new Date(new Date().getTime() + this.timeoutMs) - this.addRemoveNode = null - this.addRemoveStatus = 'start' - } else if (controllerStatus.indexOf('stopped') > 0) { - this.addRemoveEndDate = this.now - this.addRemoveWorking = `${this.addRemoveMode} stopped, discovering…` - this.addRemoveStatus = 'wait' - setTimeout(this.showResults, 5000) // add additional discovery time - } else { - this.addRemoveEndDate = this.now - this.addRemoveWorking = null - this.addRemoveFailed = controllerStatus // TODO: better formatting? - this.addRemoveStatus = 'stop' - } + if (data.secure && data.id < 2) args.push(data.secure) + this.apiRequest(data.method + data.baseAction, args) }, showResults () { @@ -288,11 +279,13 @@ export default { this.addRemoveFailed = null if (this.addRemoveNode == null) { - this.addRemoveFailed = `${this.addRemoveMode} stopped, none found` - } else if (this.addRemoveMode === 'Exclusion') { + this.addRemoveFailed = `${this.addRemoveName} stopped, none found` + } else if (this.addRemoveName === 'Exclusion') { this.addRemoveSucceeded = `Device found! Node ${this.addRemoveNode.id} removed` } else if (this.addRemoveStartNodeCount > 0) { - this.addRemoveSucceeded = `Device found! Node ${this.addRemoveNode.id} added ${this.addRemoveNode.isSecure ? 'with' : 'without'} security` + this.addRemoveSucceeded = `Device found! Node ${ + this.addRemoveNode.id + } added ${this.addRemoveNode.isSecure ? 'with' : 'without'} security` } this.addRemoveStatus = 'stop' @@ -438,6 +431,7 @@ export default { }) this.socket.on(socketEvents.nodeAdded, async node => { + console.log('node added') console.debug(node) this.addRemoveNode = node }) @@ -446,7 +440,6 @@ export default { if (this.socket) { // unbind events this.socket.off(socketEvents.api) - this.socket.off(socketEvents.nodeRemoved) this.socket.off(socketEvents.nodeAdded) } clearInterval(this.addRemoveTimer) diff --git a/src/components/dialogs/DialogAddRemove.vue b/src/components/dialogs/DialogAddRemove.vue index 57037a96aa7..04244d94f82 100644 --- a/src/components/dialogs/DialogAddRemove.vue +++ b/src/components/dialogs/DialogAddRemove.vue @@ -8,26 +8,33 @@ - + - + - + @@ -35,26 +42,39 @@ - {{working}} - {{succeeded}} - {{failed}} - + {{ + working + }} + {{ + succeeded + }} + {{ failed }} - Close - {{ nextStatus }} + Close + {{ method }} From 44d2efd142fc90014094c9ddaf3d976457c19fdc Mon Sep 17 00:00:00 2001 From: Jarrett Vance Date: Tue, 2 Feb 2021 16:42:09 -0600 Subject: [PATCH 07/13] Fix node added event --- lib/ZwaveClient.js | 2 +- src/components/ControlPanel.vue | 9 ++++----- src/plugins/socket.js | 1 + 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/ZwaveClient.js b/lib/ZwaveClient.js index 3392f536bb4..d947f64ecde 100644 --- a/lib/ZwaveClient.js +++ b/lib/ZwaveClient.js @@ -744,7 +744,7 @@ function removeNode (nodeid) { this.emit('nodeRemoved', node) this.addEmptyNodes() - this.sendToSocket(socketEvents.nodeRemoved, node) + this.sendToSocket(socketEvents.nodeRemoved, this.nodes[nodeid]) } } diff --git a/src/components/ControlPanel.vue b/src/components/ControlPanel.vue index 8be39bbe483..acdeb47e48f 100644 --- a/src/components/ControlPanel.vue +++ b/src/components/ControlPanel.vue @@ -269,7 +269,8 @@ export default { data.method === 'start' ? 'starting…' : 'stopping…' }` const args = [] - if (data.secure && data.id < 2) args.push(data.secure) + if (data.secure && data.id < 2 && data.method === 'start') + args.push(data.secure) this.apiRequest(data.method + data.baseAction, args) }, @@ -282,10 +283,8 @@ export default { this.addRemoveFailed = `${this.addRemoveName} stopped, none found` } else if (this.addRemoveName === 'Exclusion') { this.addRemoveSucceeded = `Device found! Node ${this.addRemoveNode.id} removed` - } else if (this.addRemoveStartNodeCount > 0) { - this.addRemoveSucceeded = `Device found! Node ${ - this.addRemoveNode.id - } added ${this.addRemoveNode.isSecure ? 'with' : 'without'} security` + } else { + this.addRemoveSucceeded = `Device found! Node ${this.addRemoveNode.id} added` // we don't yet know if it was secure, interview underway } this.addRemoveStatus = 'stop' diff --git a/src/plugins/socket.js b/src/plugins/socket.js index e097c30e0cc..49ca2f08431 100644 --- a/src/plugins/socket.js +++ b/src/plugins/socket.js @@ -12,6 +12,7 @@ export const socketEvents = { init: 'INIT', // automatically sent when a new client connects to the socket controller: 'CONTROLLER_CMD', // controller status updates connected: 'CONNECTED', // socket status + nodeAdded: 'NODE_ADDED', nodeRemoved: 'NODE_REMOVED', nodeUpdated: 'NODE_UPDATED', valueUpdated: 'VALUE_UPDATED', From 833a4a0bea197ffb15f8996636dc60dc79fde8eb Mon Sep 17 00:00:00 2001 From: Jarrett Vance Date: Tue, 2 Feb 2021 17:11:01 -0600 Subject: [PATCH 08/13] lint fix --- src/components/ControlPanel.vue | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/ControlPanel.vue b/src/components/ControlPanel.vue index acdeb47e48f..b3b67aed631 100644 --- a/src/components/ControlPanel.vue +++ b/src/components/ControlPanel.vue @@ -269,8 +269,9 @@ export default { data.method === 'start' ? 'starting…' : 'stopping…' }` const args = [] - if (data.secure && data.id < 2 && data.method === 'start') + if (data.secure && data.id < 2 && data.method === 'start') { args.push(data.secure) + } this.apiRequest(data.method + data.baseAction, args) }, From fdc0de7ccb8526dc9e60d836769088402aad1673 Mon Sep 17 00:00:00 2001 From: Jarrett Vance Date: Tue, 2 Feb 2021 19:23:31 -0600 Subject: [PATCH 09/13] cleanup debug --- src/components/ControlPanel.vue | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/components/ControlPanel.vue b/src/components/ControlPanel.vue index b3b67aed631..ba0774c605b 100644 --- a/src/components/ControlPanel.vue +++ b/src/components/ControlPanel.vue @@ -426,13 +426,10 @@ export default { }) this.socket.on(socketEvents.nodeRemoved, async node => { - console.debug(node) this.addRemoveNode = node }) this.socket.on(socketEvents.nodeAdded, async node => { - console.log('node added') - console.debug(node) this.addRemoveNode = node }) }, From adce20e1d92c86a072e8ba2b90aa643b85db3163 Mon Sep 17 00:00:00 2001 From: Daniel Lando Date: Wed, 3 Feb 2021 09:18:34 +0100 Subject: [PATCH 10/13] fix: events bind/unbind, var names and better code style --- src/components/ControlPanel.vue | 140 ++++++++++++--------- src/components/dialogs/DialogAddRemove.vue | 12 +- 2 files changed, 84 insertions(+), 68 deletions(-) diff --git a/src/components/ControlPanel.vue b/src/components/ControlPanel.vue index ba0774c605b..b89020ebfbe 100644 --- a/src/components/ControlPanel.vue +++ b/src/components/ControlPanel.vue @@ -60,9 +60,7 @@ @@ -116,30 +114,41 @@ export default { const now = new Date() const s = Math.trunc((this.addRemoveEndDate - now) / 1000) if (this.addRemoveStatus === 'start') { - this.addRemoveWorking = `${this.addRemoveName} started: ${s}s remaining` + this.addRemoveAlert = { + type: 'info', + text: `${this.addRemoveAction} started: ${s}s remaining` + } } if (now > newVal) clearInterval(this.addRemoveTimer) }, 100) }, - controllerStatus (newVal) { - if (newVal.indexOf('clusion') > 0) { - if (this.addRemoveName === null) return // ignore initial status + controllerStatus (status) { + if (status.indexOf('clusion') > 0) { + if (this.addRemoveAction === null) return // ignore initial status - if (newVal.indexOf('started') > 0) { + // inclusion/exclusion started, start the countdown timer + if (status.indexOf('started') > 0) { this.addRemoveEndDate = new Date( new Date().getTime() + this.timeoutMs ) this.addRemoveNode = null this.addRemoveStatus = 'start' - } else if (newVal.indexOf('stopped') > 0) { + } else if (status.indexOf('stopped') > 0) { + // inclusion/exclusion stopped, check what happened this.addRemoveEndDate = new Date() - this.addRemoveWorking = `${this.addRemoveName} stopped, discovering…` + this.addRemoveAlert = { + type: 'info', + text: `${this.addRemoveAction} stopped, checking nodes…` + } this.addRemoveStatus = 'wait' - setTimeout(this.showResults, 5000) // add additional discovery time + setTimeout(this.showResults, 1000) // add additional discovery time } else { + // error this.addRemoveEndDate = new Date() - this.addRemoveWorking = null - this.addRemoveFailed = newVal // TODO: better formatting? + this.addRemoveAlert = { + type: 'error', + text: status // TODO: better formatting? + } this.addRemoveStatus = 'stop' } } @@ -148,12 +157,11 @@ export default { data () { return { settings: new Settings(localStorage), + bindedSocketEvents: {}, // keep track of the events-handlers addRemoveShowDialog: false, - addRemoveName: null, + addRemoveAction: null, addRemoveStatus: 'stop', - addRemoveWorking: null, - addRemoveSucceeded: null, - addRemoveFailed: null, + addRemoveAlert: null, addRemoveEndDate: new Date(), addRemoveTimer: null, addRemoveNode: null, @@ -258,34 +266,40 @@ export default { console.log(error) }) }, - - async onAddRemoveAction (data) { + async onAddRemoveAction (action) { this.addRemoveStatus = 'wait' // make sure user can't trigger another action too soon - this.addRemoveName = data.name + this.addRemoveAction = action.name // Inclusion/Secure inclusion/Exclusion this.addRemoveEndDate = new Date() - this.addRemoveSucceeded = null - this.addRemoveFailed = null - this.addRemoveWorking = `${data.name} ${ - data.method === 'start' ? 'starting…' : 'stopping…' - }` + this.addRemoveAlert = { + type: 'info', + text: `${action.name} ${ + action.method === 'start' ? 'starting…' : 'stopping…' + }` + } const args = [] - if (data.secure && data.id < 2 && data.method === 'start') { - args.push(data.secure) + if (action.secure && action.id < 2 && action.method === 'start') { + args.push(action.secure) } - this.apiRequest(data.method + data.baseAction, args) + this.apiRequest(action.method + action.baseAction, args) }, - showResults () { - this.addRemoveWorking = null - this.addRemoveSucceeded = null - this.addRemoveFailed = null + this.addRemoveAlert = null if (this.addRemoveNode == null) { - this.addRemoveFailed = `${this.addRemoveName} stopped, none found` - } else if (this.addRemoveName === 'Exclusion') { - this.addRemoveSucceeded = `Device found! Node ${this.addRemoveNode.id} removed` + this.addRemoveAlert = { + type: 'warning', + text: `${this.addRemoveAction} stopped, none found` + } + } else if (this.addRemoveAction === 'Exclusion') { + this.addRemoveAlert = { + type: 'success', + text: `Node ${this.addRemoveNode.id} removed` + } } else { - this.addRemoveSucceeded = `Device found! Node ${this.addRemoveNode.id} added` // we don't yet know if it was secure, interview underway + this.addRemoveAlert = { + type: 'success', + text: `Device found! Node ${this.addRemoveNode.id} added` // we don't know yet if it's added securely or not, need to wait interview + } } this.addRemoveStatus = 'stop' @@ -395,51 +409,59 @@ export default { for (const k in obj) s += k + ': ' + obj[k] + '\n' return s - } - }, - mounted () { - const self = this - - this.socket.on(socketEvents.api, async data => { + }, + onApiResponse (data) { if (data.success) { switch (data.api) { case 'getDriverStatistics': - self.$listeners.showConfirm( + this.$listeners.showConfirm( 'Driver statistics', - self.jsonToList(data.result) + this.jsonToList(data.result) ) break case 'getNodeStatistics': - self.$listeners.showConfirm( + this.$listeners.showConfirm( 'Node statistics', - self.jsonToList(data.result) + this.jsonToList(data.result) ) break default: - self.showSnackbar('Successfully call api ' + data.api) + this.showSnackbar('Successfully call api ' + data.api) } } else { - self.showSnackbar( + this.showSnackbar( 'Error while calling api ' + data.api + ': ' + data.message ) } - }) - - this.socket.on(socketEvents.nodeRemoved, async node => { + }, + onNodeAddedRemoved (node) { this.addRemoveNode = node - }) + }, + bindEvent (eventName, handler) { + this.socket.on(socketEvents[eventName], handler) + this.bindedSocketEvents[eventName] = handler + }, + unbindEvents () { + for (const event in this.bindedSocketEvents) { + this.socket.off(event, this.bindedSocketEvents[event]) + } + } + }, + mounted () { + const onApiResponse = this.onApiResponse.bind(this) + const onNodeAddedRemoved = this.onNodeAddedRemoved.bind(this) - this.socket.on(socketEvents.nodeAdded, async node => { - this.addRemoveNode = node - }) + this.bindEvent('api', onApiResponse) + this.bindEvent('nodeRemoved', onNodeAddedRemoved) + this.bindEvent('nodeAdded', onNodeAddedRemoved) }, beforeDestroy () { if (this.socket) { - // unbind events - this.socket.off(socketEvents.api) - this.socket.off(socketEvents.nodeAdded) + this.unbindEvents() + } + if (this.addRemoveTimer) { + clearInterval(this.addRemoveTimer) } - clearInterval(this.addRemoveTimer) } } diff --git a/src/components/dialogs/DialogAddRemove.vue b/src/components/dialogs/DialogAddRemove.vue index 04244d94f82..767fdb4da97 100644 --- a/src/components/dialogs/DialogAddRemove.vue +++ b/src/components/dialogs/DialogAddRemove.vue @@ -42,13 +42,9 @@ - {{ - working + {{ + alert.text }} - {{ - succeeded - }} - {{ failed }} @@ -78,9 +74,7 @@ export default { props: { value: Boolean, // show or hide status: String, - working: String, - failed: String, - succeeded: String + alert: Object }, watch: {}, computed: { From d01218d00feb1e9698481039a2f5bfe2a2ee2952 Mon Sep 17 00:00:00 2001 From: Daniel Lando Date: Wed, 3 Feb 2021 10:40:21 +0100 Subject: [PATCH 11/13] fix: clear dialog alert state on close --- src/components/ControlPanel.vue | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/ControlPanel.vue b/src/components/ControlPanel.vue index b89020ebfbe..3a72359d69e 100644 --- a/src/components/ControlPanel.vue +++ b/src/components/ControlPanel.vue @@ -61,7 +61,7 @@ v-model="addRemoveShowDialog" :status="addRemoveStatus" :alert="addRemoveAlert" - @close="addRemoveShowDialog = false" + @close="onAddRemoveClose" @action="onAddRemoveAction" /> @@ -266,6 +266,10 @@ export default { console.log(error) }) }, + onAddRemoveClose () { + this.addRemoveShowDialog = false + this.addRemoveAlert = null + }, async onAddRemoveAction (action) { this.addRemoveStatus = 'wait' // make sure user can't trigger another action too soon this.addRemoveAction = action.name // Inclusion/Secure inclusion/Exclusion From 0cdecf201a0bf5b5feeacc80d69ceaf83cc90c58 Mon Sep 17 00:00:00 2001 From: Jarrett Vance Date: Fri, 5 Feb 2021 19:50:56 -0600 Subject: [PATCH 12/13] feat(ui): refactor move code into dialog --- src/components/ControlPanel.vue | 118 +------------- src/components/dialogs/DialogAddRemove.vue | 171 +++++++++++++++++---- 2 files changed, 148 insertions(+), 141 deletions(-) diff --git a/src/components/ControlPanel.vue b/src/components/ControlPanel.vue index 8f84abb9c74..750bd3cb6b0 100644 --- a/src/components/ControlPanel.vue +++ b/src/components/ControlPanel.vue @@ -58,10 +58,9 @@ { - const now = new Date() - const s = Math.trunc((this.addRemoveEndDate - now) / 1000) - if (this.addRemoveStatus === 'start') { - this.addRemoveAlert = { - type: 'info', - text: `${this.addRemoveAction} started: ${s}s remaining` - } - } - if (now > newVal) clearInterval(this.addRemoveTimer) - }, 100) - }, - controllerStatus (status) { - if (status.indexOf('clusion') > 0) { - if (this.addRemoveAction === null) return // ignore initial status - - // inclusion/exclusion started, start the countdown timer - if (status.indexOf('started') > 0) { - this.addRemoveEndDate = new Date( - new Date().getTime() + this.timeoutMs - ) - this.addRemoveNode = null - this.addRemoveStatus = 'start' - } else if (status.indexOf('stopped') > 0) { - // inclusion/exclusion stopped, check what happened - this.addRemoveEndDate = new Date() - this.addRemoveAlert = { - type: 'info', - text: `${this.addRemoveAction} stopped, checking nodes…` - } - this.addRemoveStatus = 'wait' - this.waitTimeout = setTimeout(this.showResults, 5000) // add additional discovery time - } else { - // error - this.addRemoveEndDate = new Date() - this.addRemoveAlert = { - type: 'error', - text: status // TODO: better formatting? - } - this.addRemoveStatus = 'stop' - } - } - } - }, + watch: {}, data () { return { settings: new Settings(localStorage), bindedSocketEvents: {}, // keep track of the events-handlers addRemoveShowDialog: false, - addRemoveAction: null, - addRemoveStatus: 'stop', - waitTimeout: null, - addRemoveAlert: null, - addRemoveEndDate: new Date(), - addRemoveTimer: null, addRemoveNode: null, node_actions: [ { @@ -268,52 +213,8 @@ export default { }, onAddRemoveClose () { this.addRemoveShowDialog = false - this.addRemoveAlert = null - }, - async onAddRemoveAction (action) { - this.addRemoveStatus = 'wait' // make sure user can't trigger another action too soon - this.addRemoveAction = action.name // Inclusion/Secure inclusion/Exclusion - this.addRemoveEndDate = new Date() - this.addRemoveAlert = { - type: 'info', - text: `${action.name} ${ - action.method === 'start' ? 'starting…' : 'stopping…' - }` - } - const args = [] - if (action.secure && action.id < 2 && action.method === 'start') { - args.push(action.secure) - } - this.apiRequest(action.method + action.baseAction, args) + this.addRemoveNode = null }, - showResults () { - if (this.waitTimeout) { - clearTimeout(this.waitTimeout) - this.waitTimeout = null - } - - this.addRemoveAlert = null - - if (this.addRemoveNode == null) { - this.addRemoveAlert = { - type: 'warning', - text: `${this.addRemoveAction} stopped, none found` - } - } else if (this.addRemoveAction === 'Exclusion') { - this.addRemoveAlert = { - type: 'success', - text: `Node ${this.addRemoveNode.id} removed` - } - } else { - this.addRemoveAlert = { - type: 'success', - text: `Device found! Node ${this.addRemoveNode.id} added` // we don't know yet if it's added securely or not, need to wait interview - } - } - - this.addRemoveStatus = 'stop' - }, - async sendCntAction () { if (this.cnt_action) { const args = [] @@ -445,10 +346,6 @@ export default { }, onNodeAddedRemoved (node) { this.addRemoveNode = node - // the add/remove dialog is waiting for a feedback - if (this.waitTimeout) { - this.showResults() - } }, bindEvent (eventName, handler) { this.socket.on(socketEvents[eventName], handler) @@ -472,13 +369,6 @@ export default { if (this.socket) { this.unbindEvents() } - if (this.addRemoveTimer) { - clearInterval(this.addRemoveTimer) - } - - if (this.waitTimeout) { - clearTimeout(this.waitTimeout) - } } } diff --git a/src/components/dialogs/DialogAddRemove.vue b/src/components/dialogs/DialogAddRemove.vue index 767fdb4da97..77d986d45ef 100644 --- a/src/components/dialogs/DialogAddRemove.vue +++ b/src/components/dialogs/DialogAddRemove.vue @@ -7,8 +7,8 @@ - - + + - + - + From fa88e582044336be3fad851028b6654eecb686c6 Mon Sep 17 00:00:00 2001 From: Jarrett Vance Date: Sat, 6 Feb 2021 10:54:13 -0600 Subject: [PATCH 13/13] feat(ui): refactor @robertsLando requested changes --- src/components/ControlPanel.vue | 2 +- src/components/dialogs/DialogAddRemove.vue | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/ControlPanel.vue b/src/components/ControlPanel.vue index 750bd3cb6b0..b6dfad82586 100644 --- a/src/components/ControlPanel.vue +++ b/src/components/ControlPanel.vue @@ -58,7 +58,7 @@ diff --git a/src/components/dialogs/DialogAddRemove.vue b/src/components/dialogs/DialogAddRemove.vue index 77d986d45ef..0f4730f77e8 100644 --- a/src/components/dialogs/DialogAddRemove.vue +++ b/src/components/dialogs/DialogAddRemove.vue @@ -74,7 +74,7 @@ import { mapGetters } from 'vuex' export default { props: { value: Boolean, // show or hide - lastNodeFound: Object + nodeAddedOrRemoved: Object }, data () { return { @@ -121,7 +121,7 @@ export default { } }, watch: { - lastNodeFound (node) { + nodeAddedOrRemoved (node) { this.nodeFound = node // the add/remove dialog is waiting for a feedback @@ -202,10 +202,10 @@ export default { this.waitTimeout = null } - if (this.nodeFound == null) { + if (this.nodeFound === null) { this.alert = { type: 'warning', - text: `${this.modeName} stopped, none found` + text: `${this.modeName} stopped, no changes detected` } } else if (this.mode === 2) { this.alert = {