Skip to content

Commit

Permalink
feat: Mesh graph showing node neighbors
Browse files Browse the repository at this point in the history
  • Loading branch information
robertsLando committed Jan 2, 2020
1 parent 47166fd commit 5c73aa4
Show file tree
Hide file tree
Showing 9 changed files with 299 additions and 17 deletions.
39 changes: 39 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
"tail": "^2.0.2",
"uniqid": "^4.1.1",
"vue": "^2.5.2",
"vue-d3-network": "^0.1.28",
"vue-router": "^3.0.1",
"vuetify": "^1.0.1",
"vuex": "^3.0.1"
Expand Down
20 changes: 19 additions & 1 deletion src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@
@export="exportConfiguration"
@showSnackbar="showSnackbar"
:socket="socket"
:socketEvents="socketEvents"
:socketActions="socketActions"
/>
</v-content>
</main>
Expand Down Expand Up @@ -162,8 +164,24 @@ export default {
version: process.env.VERSION,
pages: [
{ icon: 'widgets', title: 'Control Panel', path: '/' },
{ icon: 'settings', title: 'Settings', path: '/settings' }
{ icon: 'settings', title: 'Settings', path: '/settings' },
{ icon: 'share', title: 'Network graph', path: '/mesh' }
],
socketEvents: {
init: 'INIT',
controller: 'CONTROLLER_CMD',
driver: 'DRIVER_READY',
nodeRemoved: 'NODE_REMOVED',
nodeUpdated: 'NODE_UPDATED',
valueUpdated: 'VALUE_UPDATED',
api: 'API_RETURN',
debug: 'DEBUG'
},
socketActions: {
init: 'INITED',
hass: 'HASS_API',
zwave: 'ZWAVE_API'
},
status: '',
statusColor: '',
drawer: false,
Expand Down
19 changes: 19 additions & 0 deletions src/assets/css/my-mesh.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.node.controller {
stroke: purple;
}

.node.sleep {
stroke: yellow;
}

.node.alive {
stroke: green;
}

.node.failed .node.removed {
stroke: black;
}

.node.dead {
stroke: red;
}
24 changes: 24 additions & 0 deletions src/assets/css/my-progress.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,27 @@
#nprogress .spinner {
right: 50% !important;
}

::-webkit-scrollbar {
height: 5px;
width: 4px;
background: transparent;
padding-right: 10;
}

::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.05);
border-radius: 1ex;
-webkit-border-radius: 1ex;
}

::-webkit-scrollbar-corner {
background: transparent;
}

/* Hacky hack. Remove scrollbars for 320 width screens */
@media screen and (max-width : 320px) {
body::-webkit-scrollbar {
display: none;
}
}
20 changes: 4 additions & 16 deletions src/components/ControlPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,9 @@ const MAX_DEBUG_LINES = 300
export default {
name: 'ControlPanel',
props: {
socket: Object
socket: Object,
socketActions: Object,
socketEvents: Object
},
components: {
ValueID,
Expand Down Expand Up @@ -545,21 +547,6 @@ export default {
debugActive: false,
selectedScene: null,
cnt_status: 'Unknown',
socketEvents: {
init: 'INIT',
controller: 'CONTROLLER_CMD',
driver: 'DRIVER_READY',
nodeRemoved: 'NODE_REMOVED',
nodeUpdated: 'NODE_UPDATED',
valueUpdated: 'VALUE_UPDATED',
api: 'API_RETURN',
debug: 'DEBUG'
},
socketActions: {
init: 'INITED',
hass: 'HASS_API',
zwave: 'ZWAVE_API'
},
newScene: '',
scene_values: [],
dialogValue: false,
Expand Down Expand Up @@ -773,6 +760,7 @@ export default {
self.apiRequest('setScenes', [scenes])
} else {
self.showSnackbar('Imported file not valid')
console.log(err)
}
})
}
Expand Down
184 changes: 184 additions & 0 deletions src/components/Mesh.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
<template>
<v-container fluid>
<v-card>
<v-container grid-list-md>
<v-layout row wrap>
<v-flex xs3>
<v-text-field label="Nodes size" v-model.number="nodeSize" min="10" type="number"></v-text-field>
</v-flex>
<v-flex xs3>
<v-text-field label="Link size" v-model.number="fontSize" min="10" type="number"></v-text-field>
</v-flex>
<v-flex xs3>
<v-btn color="success" @click="downloadSVG">Download SVG</v-btn>
</v-flex>
</v-layout>
</v-container>

<d3-network
ref="mesh"
:net-nodes="activeNodes"
:net-links="links"
:options="options"
@node-click="nodeClick"
/>
<v-speed-dial bottom fab right fixed v-model="fab">
<v-btn slot="activator" color="blue darken-2" dark fab hover v-model="fab">
<v-icon>add</v-icon>
<v-icon>close</v-icon>
</v-btn>
<v-btn fab dark small color="green" @click="refresh">
<v-icon>refresh</v-icon>
</v-btn>
</v-speed-dial>
</v-card>
</v-container>
</template>
<script>
import D3Network from 'vue-d3-network'
export default {
name: 'Mesh',
props: {
socket: Object,
socketActions: Object,
socketEvents: Object
},
components: {
D3Network
},
computed: {
activeNodes () {
return this.nodes.filter(n => n.node_id !== 0 && n.status !== 'Removed')
},
options () {
return {
canvas: false,
force: 1500,
offset: {
x: 0,
y: 0
},
nodeSize: this.nodeSize,
fontSize: this.fontSize,
linkWidth: 1,
nodeLabels: true,
linkLabels: false,
strLinks: true,
resizeListener: true
}
}
},
data () {
return {
nodeSize: 20,
fontSize: 10,
nodes: [],
links: [],
fab: false
}
},
methods: {
showSnackbar (text) {
this.$emit('showSnackbar', text)
},
nodeClick (e, node) {
this.selectedNode = this.nodes[node.id]
},
downloadSVG () {
this.$refs.mesh.screenShot('myNetwork.svg', true, true)
},
convertNode (n) {
return {
id: n.node_id,
_cssClass: this.nodeClass(n),
name: n.product,
node_id: n.node_id,
status: n.status,
failed: n.failed
}
},
nodeClass (n) {
if (n.node_id === 1) {
return 'controller'
}
return n.status.toLowerCase()
},
apiRequest (apiName, args) {
if (this.socket.connected) {
var data = {
api: apiName,
args: args
}
this.socket.emit(this.socketActions.zwave, data)
} else {
this.showSnackbar('Socket disconnected')
}
},
refresh () {
this.socket.emit(this.socketActions.zwave, {
api: 'refreshNeighborns',
args: []
})
},
updateLinks () {
for (const source of this.nodes) {
if (source.neighbors) {
for (const target of source.neighbors) {
this.links.push({
sid: source.node_id,
tid: target,
_color: 'black'
})
}
}
}
}
},
mounted () {
var self = this
this.socket.on(this.socketEvents.nodeRemoved, node => {
self.$set(self.nodes, node.node_id, node)
})
this.socket.on(this.socketEvents.init, data => {
var nodes = data.nodes
for (var i = 0; i < nodes.length; i++) {
self.nodes.push(self.convertNode(nodes[i]))
}
})
this.socket.on(this.socketEvents.nodeUpdated, data => {
var node = self.convertNode(data)
if (!self.nodes[data.node_id]) {
self.nodes.push(node)
}
self.$set(self.nodes, data.node_id, node)
})
this.socket.on(this.socketEvents.api, data => {
if (data.success) {
switch (data.api) {
case 'refreshNeighborns':
var neighbors = data.result
for (var i = 0; i < neighbors.length; i++) {
if (self.nodes[i]) {
self.nodes[i].neighbors = neighbors[i]
}
}
self.updateLinks()
break
}
} else {
self.showSnackbar(
'Error while calling api ' + data.api + ': ' + data.message
)
}
})
this.socket.emit(this.socketActions.init, true)
this.refresh()
}
}
</script>
2 changes: 2 additions & 0 deletions src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ import Vuetify from 'vuetify'
import 'vuetify/dist/vuetify.min.css'

import 'axios-progress-bar/dist/nprogress.css'
import 'vue-d3-network/dist/vue-d3-network.css'

// Custom assets CSS JS
require('./assets/css/my-progress.css')
require('./assets/css/my-mesh.css')

Vue.use(Vuetify)

Expand Down
Loading

0 comments on commit 5c73aa4

Please sign in to comment.