refactored amend methods
added transport validation
changed default name spaces to all start with _ to avoid accidential namespace issues
refactored examples to showcase all possibilities with a four in one (fio) example
refactored ha hooks
added matching websocket client
all works changes made by one socket are pushed to all sockets where they do their updates including home assistant
added a home assistant package file including lovelace yaml for matching HA interface.
master
David Kebler 2019-08-27 20:10:44 -07:00
parent 21718d3998
commit 3bf0110164
51 changed files with 1818 additions and 320 deletions

View File

@ -1,43 +0,0 @@
import Base from '../src/base'
// const USOCKET = __dirname + '/sample.sock'
let dy = new Base({id:'dynamic'})
;
(async () => {
await dy.init()
console.log('started', dy.started)
await Promise.all([dy.addSocket('con','c','t'),dy.addSocket('ser','s','t')])
dy.good = {
bad: function(packet){
return new Promise( async (resolve) => {
let res = {}
res.req = packet
res.cmd='good/ugly'
res.response='The Good The Bad and The Ugly'
return resolve(res)
})
},
ugly: function (packet) {
console.log('==============reply from Good Bad command =========')
console.log(packet.response)
console.log('===========================')
}
}
let packet = {}
// console.log('=============sending============')
packet = {cmd:'good:bad'}
console.log(packet)
await dy.send(packet)
process.kill(process.pid, 'SIGTERM')
})().catch(err => {
console.error('FATAL: UNABLE TO START SYSTEM!\n',err)
process.kill(process.pid, 'SIGTERM')
})

View File

@ -1,38 +1,49 @@
import Base from '../src/base' import Base from '../src/base'
import hahooks from './ha-mqtt'
// const USOCKET = __dirname + '/sample.sock'
const socketfuncs = { const socketfuncs = {
switch: { switch: {
on: function(packet){ status: function(packet){
return new Promise( async (resolve) => { return new Promise( async (resolve) => {
packet.cmd='switch/status' packet.cmd='switch/status'
packet.status='on' packet.state=this.switches[packet.id-1]
packet.sender= packet.sender || (packet._header ? packet._header.sender.name : 'unknown') packet.sender= packet.sender || (packet._header ? packet._header.sender.name : 'unknown')
this.push(packet) // push to all active socket servers this.push(packet) // push to all active socket servers
let res = { response:'status pushed on to all clients'} let res = { response:'status pushed on to all clients'}
return resolve(res) // this goes back to sender return resolve(res) // this goes back to sender
}) })
}, },
on: function(packet){
return new Promise( async (resolve) => {
packet.cmd='switch/status'
packet.state='on'
this.switches[packet.id-1] = 'on'
packet.sender= packet.sender || (packet._header ? packet._header.sender.name : 'unknown')
this.push(packet) // push to all active socket servers
let res = { response:'status change - pushed on to all clients', id:packet.id}
return resolve(res) // this goes back to sender
})
},
off: function(packet){ off: function(packet){
return new Promise( async (resolve) => { return new Promise( async (resolve) => {
packet.cmd='switch/status' packet.cmd='switch/status'
packet.status='off' packet.state='off'
this.switches[packet.id-1] = 'off'
packet.sender= packet.sender || (packet._header ? packet._header.sender.name : 'unknown') packet.sender= packet.sender || (packet._header ? packet._header.sender.name : 'unknown')
this.push(packet) // push to all active socket servers this.push(packet) // push to all active socket servers
let res = { response:'status pushed off to all clients'} let res = { response:'status change - pushed off to all clients'}
return resolve(res) // this goes back to sender return resolve(res) // this goes back to sender
}) })
}, },
toggle: function(packet){ toggle: function(packet){
return new Promise( async (resolve) => { return new Promise( async (resolve) => {
// would check status before deciding what to push // console.log('toggle current status of ', packet.current_status || 'unknown, so random set')
if (packet.status===null) packet.status = Math.random()>=.5 ? 'on' : 'off' this.switches[packet.id-1] = this.switches[packet.id-1]==='on' ? 'off' : 'on'
else packet.status = (packet.status==='on' ? 'off' : 'on') packet.state = this.switches[packet.id-1]
packet.cmd ='switch/status' packet.cmd ='switch/status'
packet.sender= packet.sender || (packet._header ? packet._header.sender.name : 'unknown') packet.sender= packet.sender || (packet._header ? packet._header.sender.name : 'unknown')
this.push(packet) // push to all active socket servers this.push(packet) // push to all active socket servers
let res = { response:`command ${packet.cmd} with id:${packet.id} pushed to all consumers(clients)`} let res = { response:'status change - pushed toggled state to all clients'}
return resolve(res) // this goes back to sender return resolve(res) // this goes back to sender
}) })
} }
@ -40,37 +51,40 @@ const socketfuncs = {
} }
function status(state,consumer) { // client processing function
function status(transport) {
return (packet) => { return (packet) => {
return new Promise( async (resolve) => { return new Promise( async (resolve) => {
// console.log('complete packet', packet, consumer) console.log('=== a by transport processor only function ====')
console.log(`Switch ${packet.id} is ${packet.status}, sent by ${packet.sender || packet.data.sender} to ${consumer}`) console.log(`Switch ${packet.id} is ${packet.state}, sent by ${packet.sender || packet.data.sender} to ${transport}`)
console.log('==================')
return resolve() return resolve()
}) })
} }
} }
let fio = new Base({sockets:'uc#c>n,us#s>n,tc#c>t,ts#s>t', tc:{port:8100}, ts:{port:8100}, webs:{ port:8090 }, mqtts:{ topics:['switch/on','switch/off','switch/toggle']}, mqtt:{ topics:['switch/status']}}) let fio = new Base({sockets:'uc#c>n,us#s>n,tc#c>t,ts#s>t', tc:{port:8100}, ts:{port:8100}})
fio.s = socketfuncs fio.switches = ['off','off','off','off'] // database to hold switch states
fio.c = { reply: () => {return Promise.resolve()} } fio.actions = socketfuncs
fio.cn = {switch:{status:status('on','named')}} fio.addNamespace('actions')
fio.ct = {switch:{status:status('on','tcp')}} fio.amendConsumerCommands({switch: {status:status('tcp only processor function which superceeds the all transports')}},'t')
fio.cm = {switch:{status:status('on','mqtt')}} fio.amendConsumerCommands({switch: {status:status('default all transports function')}})
; ;
(async () => { (async () => {
await fio.addSocket('mqtt','s','m',{ topics:['switch/on','switch/off','switch/toggle']}) await fio.addSocket('mqtt','s','m',{ host:'nas.kebler.net',topics:['switch/on','switch/off','switch/toggle']})
await fio.addSocket('ha','s','m',{ host:'nas.kebler.net',topics:['fio/switch/set/#']})
hahooks.call(fio,'ha')
await fio.addSocket('web','s','w',{ port:8090 })
let res = await fio.init() let res = await fio.init()
console.log('initialize errors',res) console.log('initialize errors',res)
console.log('waiting for packets')
let packet = {} let packet = {}
packet = {cmd:'switch/toggle', id:'1'} packet = {cmd:'switch/toggle', id:'1'}
console.log('=====sending to socket uc======',packet) console.log('=====sending to socket uc======',packet)
console.log('this should get pushed to all listening') console.log('this should get pushed to all listening consumers')
console.log('response back to uc: ',(await fio.send('uc',packet)).response) console.log('direct response back to uc with await: ',(await fio.send('uc',packet)).response)
})().catch(err => { })().catch(err => {

View File

@ -1,108 +1,41 @@
import Base from '../src/base'
const BROKER = 'nas.kebler.net'
const TOPICS = ['set/testing/+'] // listen for a set command
const commands = {
on: function(packet){
return new Promise( async (resolve) => {
console.log(`turning switch on for id ${packet.id||packet.data}`)
console.log('entire packet',packet)
// call switch on here
let res = {}
res.cmd='status'
res.state='on'
res.id = packet.id
this.push(packet)
return resolve(res)
})
},
off: function(packet){
return new Promise( async (resolve) => {
console.log(packet)
console.log(`turning switch off for id ${packet.id||packet.data}`)
// call switch on here
let res = {}
res.cmd='status'
res.state='off'
res.id = packet.id
return resolve(res)
})
}
}
let relays = new Base({id:'homeassistant-example'})
relays.commands = relays.bindFuncs(commands)
;
(async () => {
await relays.addSocket('mqs','s','m', {host:BROKER, topics:TOPICS})
relays.addNamespace('commands','s')
register.call(relays,'mqs')
await relays.init()
})().catch(err => {
console.error('FATAL: UNABLE TO START SYSTEM!\n',err)
process.kill(process.pid, 'SIGTERM')
})
///***************** HOME ASSISTANT ************************ ///***************** HOME ASSISTANT ************************
// formats incoming and outgoing packets for HA convention // for use with four in one example
// reformats incoming and outgoing topic/payload from HA from/to a UCI json packet
// import this function then call it binding your instance of uci/base
// passing the name of the mqtt socket which will be getting packets from HA
// NOTE: using @uci/ha one can communicate with HA directly not via mqtt
const STATUS_TOPIC = 'status/testing' const STATUS_TOPIC = 'fio/switch/status'
function register(name) { export default function register(socketname) {
this.beforeProcessHook(async (packet) => { this.beforeProcessHook(async (packet) => {
console.log('incoming mqtt packet to modify') console.log('incoming mqtt packet to modify')
console.dir(packet) console.dir(packet)
let modified = Object.assign({},packet) let modified = Object.assign({},packet)
let cmd = packet.cmd.split('/') let cmd = packet.cmd.split('/')
modified.cmd = `${packet.data.toLowerCase()}` modified.cmd = `${cmd[1]}/${packet.data.toLowerCase()}`
modified.id = cmd[2] modified.id = cmd[3]
delete modified.data delete modified.data
console.log('translated to uci packet') console.log('translated to uci packet')
console.dir(modified) console.dir(modified)
return modified return modified
// return packet // return packet
}, },
{name:name}) {name:socketname})
this.afterProcessHook(async (packet) => { // will cover push sends which is how HA will know state changed
console.log('processed packet available to modify again', packet) this.beforeSendHook(async (packet) => {
if (packet.error) { console.log('beforeSendHook', packet)
let npacket = {} if (packet.cmd === 'switch/status') {
npacket.cmd = 'error'
npacket.payload = JSON.stringify(packet)
return npacket
}
if (packet.cmd === 'status') {
packet.cmd = `${STATUS_TOPIC}/${packet.id}` packet.cmd = `${STATUS_TOPIC}/${packet.id}`
packet.payload = packet.state.toUpperCase() packet.payload = packet.state
console.log('=============modified packet sent to broker================') console.log('=============modified packet sent to broker================')
console.dir(packet) console.dir(packet)
console.log('================') console.log('================')
} }
return packet return packet
}, },
{name:name}) {name:socketname})
// this.beforeSendHook(async (packet) => {
// console.log('beforeSendHook', packet)
// if (packet.cmd === 'status') {
// let num = Object.keys(packet.pins)[0]
// packet.cmd = `${STATUS_TOPIC}/${num}`
// packet.payload = packet.pins[num] ? 'ON' : 'OFF'
// console.log('=============modified packet sent to broker================')
// console.dir(packet)
// console.log('================')
// }
// return packet
// },
// {name:name})
} }

View File

@ -1,66 +0,0 @@
import Base from '../src/base'
let dy = new Base({id:'dynamic', useRootNS: true })
const HOST = 'nas.kebler.net'
let sensor = true
//dummy simulated push
dy.sensor = {
test: function(packet){
return new Promise( async (resolve) => {
console.log(`doing a dumming sensor push for id ${packet.id||packet.data}`)
console.log('entire packet',packet)
if (sensor) dy.push({cmd:'sensor/on', id:packet.id})
else dy.push({cmd:'sensor/off', id:packet.id})
let res = {}
dy.push({cmd:'sensor/on', id:packet.id})
res.cmd='sensor/test'
res.test=true
res.id = packet.id
return resolve(res)
})
}
}
dy.switch = {
on: function(packet){
return new Promise( async (resolve) => {
console.log(`turning switch on for id ${packet.id||packet.data}`)
console.log('entire packet',packet)
// call switch on here
let res = {}
res.cmd='switch/status'
res.status='on'
res.id = packet.id
return resolve(res)
})
},
off: function(packet){
return new Promise( async (resolve) => {
console.log(packet)
console.log(`turning switch off for id ${packet.id||packet.data}`)
// call switch on here
let res = {}
res.cmd='switch/status'
res.status='off'
res.id = packet.id
return resolve(res)
})
}
}
;
(async () => {
await dy.init()
await dy.addSocket('mqs','s','m',{host:HOST})
dy.socket.mqs.subscribe(['lights/#'])
console.log('ready')
})().catch(err => {
console.error('FATAL: UNABLE TO START SYSTEM!\n',err)
process.kill(process.pid, 'SIGTERM')
})

View File

@ -0,0 +1,74 @@
# package of switches to interface the the fio (four in one) socket example of @uci/base
switch:
- platform: mqtt
name: "FIO Switch 1"
state_topic: "fio/switch/status/1"
command_topic: "fio/switch/set/1"
state_on: "on"
state_off: "off"
payload_on: "on"
payload_off: "off"
icon: mdi:lightbulb
- platform: mqtt
name: "FIO Switch 2"
state_topic: "fio/switch/status/2"
command_topic: "fio/switch/set/2"
state_on: "on"
state_off: "off"
payload_on: "on"
payload_off: "off"
icon: mdi:lightbulb
- platform: mqtt
name: "FIO Switch 3"
state_topic: "fio/switch/status/3"
command_topic: "fio/switch/set/3"
state_on: "on"
state_off: "off"
payload_on: "on"
payload_off: "off"
icon: mdi:lightbulb
- platform: mqtt
name: "FIO Switch 4"
state_topic: "fio/switch/status/4"
command_topic: "fio/switch/set/4"
state_on: "on"
state_off: "off"
payload_on: "on"
payload_off: "off"
icon: mdi:lightbulb
# copy and paste and uncomment below as a view under views: in ui-lovelace.yaml
# - title: UCI Base FIO Example
# cards:
# - type: vertical-stack
# cards:
# - type: markdown
# content: >
# # Four In One UCI Base Example
# - type: horizontal-stack
# cards:
# - type: entity-button
# entity: switch.fio_switch_1
# icon: mdi:water
# name: Switch 1
# tap_action:
# action: toggle
# - type: entity-button
# entity: switch.fio_switch_2
# icon: mdi:water
# name: Switch 2
# tap_action:
# action: toggle
# - type: entity-button
# entity: switch.fio_switch_3
# icon: mdi:water
# name: Switch 3
# tap_action:
# action: toggle
# - type: entity-button
# entity: switch.fio_switch_4
# icon: mdi:water
# name: Switch 4
# tap_action:
# action: toggle

View File

@ -1,61 +0,0 @@
import Base from '../src/base'
// const USOCKET = __dirname + '/sample.sock'
let wss = new Base({id:'websocket', useRootNS: true})
wss.switch = {
on: function(packet){
return new Promise( async (resolve) => {
console.log(`turning switch on for id ${packet.id||packet.payload.id}`)
// call switch on here
let res = {}
res.log = `turning switch on for id ${packet.id||packet.payload.id}`
res.cmd='switch/status'
res.status='on'
res.id = packet.id
return resolve(res)
})
},
off: function(packet){
return new Promise( async (resolve) => {
console.log(`turning switch off for id ${packet.id||packet.data}`)
// call switch on here
let res = {}
res.cmd='switch/status'
res.status='off'
res.id = packet.id
return resolve(res)
})
},
toggle: function(packet){
return new Promise( async (resolve) => {
console.log(`toggling switch for id ${packet.id||packet.data}`)
// call switch on here
let res = {}
res.cmd='switch/status'
res.status= (packet.status === 'on')?'off':'on'
res.id = packet.id
console.log('broadcast',res)
this.push(res)
// return only acknowledge to sender, packet went as push
res = { cmd:'ack'}
return resolve(res)
})
}
}
;
(async () => {
await wss.init()
await wss.addSocket('web','s','w')
wss.push({server:'started'})
await wss.addSocket('mqs','s','m')
wss.socket.mqs.subscribe(['switch/on','switch/off','switch/toggle'])
})().catch(err => {
console.error('FATAL: UNABLE TO START SYSTEM!\n',err)
process.kill(process.pid, 'SIGTERM')
})

View File

@ -0,0 +1,9 @@
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

View File

@ -0,0 +1 @@
/dist

View File

@ -0,0 +1,55 @@
module.exports = {
root: true,
parserOptions: {
parser: 'babel-eslint',
sourceType: 'module'
},
env: {
browser: true
},
extends: [
// https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention
// consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules.
'plugin:vue/essential',
'@vue/standard'
],
// required to lint *.vue files
plugins: [
'vue'
],
globals: {
'ga': true, // Google Analytics
'cordova': true,
'__statics': true,
'process': true
},
// add your custom rules here
rules: {
// allow async-await
'generator-star-spacing': 'off',
// allow paren-less arrow functions
'arrow-parens': 'off',
'one-var': 'off',
'import/first': 'off',
'import/named': 'error',
'import/namespace': 'error',
'import/default': 'error',
'import/export': 'error',
'import/extensions': 'off',
'import/no-unresolved': 'off',
'import/no-extraneous-dependencies': 'off',
'prefer-promise-reject-errors': 'off',
// allow console.log during development only
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
// allow debugger during development only
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
}
}

20
examples/ws-fio-client/.gitignore vendored Normal file
View File

@ -0,0 +1,20 @@
.quasar
.DS_Store
.thumbs.db
node_modules
/dist
/src-cordova/node_modules
/src-cordova/platforms
/src-cordova/plugins
/src-cordova/www
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln

View File

@ -0,0 +1,8 @@
// https://github.com/michael-ciniawsky/postcss-load-config
module.exports = {
plugins: [
// to edit target browsers: use "browserslist" field in package.json
require('autoprefixer')
]
}

View File

@ -0,0 +1,35 @@
{
"blocks": "never",
"brackets": "never",
"colons": "never",
"colors": "always",
"commaSpace": "always",
"commentSpace": "always",
"cssLiteral": "never",
"depthLimit": false,
"duplicates": true,
"efficient": "always",
"extendPref": false,
"globalDupe": true,
"indentPref": 2,
"leadingZero": "never",
"maxErrors": false,
"maxWarnings": false,
"mixed": false,
"namingConvention": false,
"namingConventionStrict": false,
"none": "never",
"noImportant": false,
"parenSpace": "never",
"placeholder": false,
"prefixVarsWithDollar": "always",
"quotePref": "single",
"semicolons": "never",
"sortOrder": false,
"stackedProperties": "never",
"trailingWhitespace": "never",
"universal": "never",
"valid": true,
"zeroUnits": "never",
"zIndexNormalize": false
}

View File

@ -0,0 +1,26 @@
# Quasar App (test-client-new)
a test websocket client for uci websocket server
## Install the dependencies
```bash
yarn
```
### Start the app in development mode (hot-code reloading, error reporting, etc.)
```bash
quasar dev
```
### Lint the files
```bash
yarn run lint
```
### Build the app for production
```bash
quasar build
```
### Customize the configuration
See [Configuring quasar.conf.js](https://quasar.dev/quasar-cli/quasar-conf-js).

View File

@ -0,0 +1,5 @@
module.exports = {
presets: [
'@quasar/babel-preset-app'
]
}

View File

@ -0,0 +1,35 @@
{
"name": "fio-client",
"version": "0.0.1",
"description": "a websocket client for use with the fiod example",
"productName": "Websocket Client",
"cordovaId": "org.cordova.quasar.app",
"author": "David Kebler <d@kebler.net>",
"private": true,
"scripts": {
"lint": "eslint --ext .js,.vue src",
"client": "WSS=ws://localhost:8090 ./node_modules/.bin/quasar dev"
},
"dependencies": {
"@quasar/extras": "^1.2.0",
"@uci/websocket-client": "^0.1.7",
"quasar": "^1.0.0",
"rfs": "^8.0.4"
},
"devDependencies": {
"@quasar/app": "^1.0.0",
"@vue/eslint-config-standard": "^4.0.0",
"babel-eslint": "^10.0.1",
"eslint": "^5.10.0",
"eslint-loader": "^2.1.1",
"eslint-plugin-vue": "^5.0.0"
},
"engines": {
"node": ">= 8.9.0",
"npm": ">= 5.6.0",
"yarn": ">= 1.6.0"
},
"browserslist": [
"last 1 version, not dead, ie >= 11"
]
}

View File

@ -0,0 +1,182 @@
// Configuration for your app
// https://quasar.dev/quasar-cli/quasar-conf-js
module.exports = function (ctx) {
return {
// app boot file (/src/boot)
// --> boot files are part of "main.js"
// boot used to be plugins
boot: ['socket'], // uci-consumer-client websocket
css: [
'app.styl'
],
extras: [
// 'ionicons-v4',
// 'mdi-v3',
// 'fontawesome-v5',
// 'eva-icons',
// 'themify',
// 'roboto-font-latin-ext', // this or either 'roboto-font', NEVER both!
// 'roboto-font', // optional, you are not bound to it
'material-icons' // optional, you are not bound to it
],
framework: {
// iconSet: 'ionicons-v4',
// lang: 'de', // Quasar language
// all: true, // --- includes everything; for dev only!
components: [
'QLayout',
'QHeader',
'QDrawer',
'QPageContainer',
'QPage',
'QToolbar',
'QToolbarTitle',
'QBtn',
'QIcon',
'QList',
'QItem',
'QItemSection',
'QItemLabel',
'QInput',
'QSelect',
'QField',
'QFooter',
'QTooltip'
],
directives: [
'Ripple'
],
// Quasar plugins
plugins: [
'Notify'
]
},
supportIE: false,
build: {
// added to support communication with websocket and backend database
env: ctx.dev
// pass environment variable to browser client
? { // so on dev we'll have
DBURL: JSON.stringify(process.env.DBURL || 'ws://localhost:3030'),
WSS: JSON.stringify(process.env.WSS || 'ws://localhost:8090')
}
: { // and on build (production):
DBURL: JSON.stringify(process.env.DBURL || 'ws://switches.kebler.net:3030'),
WSS: JSON.stringify(process.env.WSS || 'ws://relays.kebler.net:8090')
},
scopeHoisting: true,
vueRouterMode: 'history',
// vueCompiler: true,
// gzip: true,
// analyze: true,
// extractCSS: false,
extendWebpack (cfg) {
cfg.module.rules.push({
enforce: 'pre',
test: /\.(js|vue)$/,
loader: 'eslint-loader',
exclude: /node_modules/,
options: {
formatter: require('eslint').CLIEngine.getFormatter('stylish')
}
})
}
},
devServer: {
// https: true,
// port: 8080,
open: true // opens browser window automatically
},
// animations: 'all', // --- includes all animations
animations: [],
ssr: {
pwa: false
},
pwa: {
// workboxPluginMode: 'InjectManifest',
// workboxOptions: {}, // only for NON InjectManifest
manifest: {
// name: 'Quasar App',
// short_name: 'Quasar App',
// description: 'a test websocket client for uci websocket server',
display: 'standalone',
orientation: 'portrait',
background_color: '#ffffff',
theme_color: '#027be3',
icons: [
{
'src': 'statics/icons/icon-128x128.png',
'sizes': '128x128',
'type': 'image/png'
},
{
'src': 'statics/icons/icon-192x192.png',
'sizes': '192x192',
'type': 'image/png'
},
{
'src': 'statics/icons/icon-256x256.png',
'sizes': '256x256',
'type': 'image/png'
},
{
'src': 'statics/icons/icon-384x384.png',
'sizes': '384x384',
'type': 'image/png'
},
{
'src': 'statics/icons/icon-512x512.png',
'sizes': '512x512',
'type': 'image/png'
}
]
}
},
cordova: {
// id: 'org.cordova.quasar.app',
// noIosLegacyBuildFlag: true, // uncomment only if you know what you are doing
},
electron: {
// bundler: 'builder', // or 'packager'
extendWebpack (cfg) {
// do something with Electron main process Webpack cfg
// chainWebpack also available besides this extendWebpack
},
packager: {
// https://github.com/electron-userland/electron-packager/blob/master/docs/api.md#options
// OS X / Mac App Store
// appBundleId: '',
// appCategoryType: '',
// osxSign: '',
// protocol: 'myapp://path',
// Windows only
// win32metadata: { ... }
},
builder: {
// https://www.electron.build/configuration/configuration
// appId: 'test-client-new'
}
}
}
}

View File

@ -0,0 +1,14 @@
<template>
<div id="q-app">
<router-view />
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
<style>
</style>

View File

@ -0,0 +1,191 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="67.407623mm"
height="62.908276mm"
viewBox="0 0 238.84591 222.90334"
id="svg3570"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="quasar-logo-full.svg">
<defs
id="defs3572" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.979899"
inkscape:cx="-39.753589"
inkscape:cy="27.706388"
inkscape:document-units="px"
inkscape:current-layer="g4895-4-4"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:window-width="1920"
inkscape:window-height="1056"
inkscape:window-x="0"
inkscape:window-y="24"
inkscape:window-maximized="1" />
<metadata
id="metadata3575">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-277.71988,-312.33911)">
<g
id="g4895-4-4"
transform="translate(1419.0442,398.9018)">
<g
transform="translate(-29.620665,-4)"
id="g4579-2-20">
<g
id="g4445-2-0"
transform="translate(12.499948,7.809312)">
<g
inkscape:export-ydpi="44.860481"
inkscape:export-xdpi="44.860481"
inkscape:export-filename="/home/emanuele/Desktop/logo1.png"
transform="translate(-712.85583,-503.26814)"
id="g4561-6-7-0">
<g
transform="translate(16.233481,0)"
style="font-style:normal;font-weight:normal;font-size:50.25774765px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#263238;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="flowRoot4513-6-6-08">
<path
d="m -402.73125,631.46823 q -0.6125,0.0438 -1.3125,0.0875 -0.65625,0 -1.4,0 l -9.31875,0 q -12.81875,0 -12.81875,-8.44375 l 0,-13.475 q 0,-8.26875 12.6,-8.26875 l 9.75625,0 q 12.6,0 12.6,8.26875 l 0,13.475 q 0,5.03125 -4.4625,7.04375 l 3.10625,2.14375 q 1.35625,0.83125 1.35625,1.70625 0,0.875 -0.7,1.3125 -0.65625,0.48125 -1.88125,0.48125 -0.30625,0 -0.7875,-0.13125 -0.4375,-0.0875 -1.05,-0.48125 l -5.6875,-3.71875 z m 5.38125,-21.74375 q 0,-4.76875 -7.9625,-4.76875 l -9.58125,0 q -7.9625,0 -7.9625,4.76875 l 0,13.3875 q 0,4.94375 8.3125,4.94375 l 8.88125,0 q 8.3125,0 8.3125,-4.94375 l 0,-13.3875 z"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:43.75px;font-family:'Neuropol X';-inkscape-font-specification:'Neuropol X';text-align:start;letter-spacing:5px;word-spacing:0px;text-anchor:start;fill:#263238;fill-opacity:1"
id="path3428" />
<path
d="m -368.0585,631.64323 q -11.2875,0 -11.2875,-6.9125 l 0,-12.73125 q 0,-1.8375 2.31875,-1.8375 2.31875,0 2.31875,1.8375 l 0,12.775 q 0,3.325 6.475,3.325 l 8.3125,0 q 6.475,0 6.475,-3.325 l 0,-12.775 q 0,-1.8375 2.31875,-1.8375 2.3625,0 2.3625,1.8375 l 0,12.73125 q 0,6.9125 -11.2875,6.9125 l -8.00625,0 z"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:43.75px;font-family:'Neuropol X';-inkscape-font-specification:'Neuropol X';text-align:start;letter-spacing:5px;word-spacing:0px;text-anchor:start;fill:#263238;fill-opacity:1"
id="path3430" />
<path
d="m -327.2833,631.64323 q -9.3625,0 -9.3625,-5.81875 l 0,-2.49375 q 0,-5.775 9.3625,-5.775 l 18.59375,0 0,-0.65625 q 0,-3.0625 -5.38125,-3.0625 l -6.16875,0 q -2.1875,0 -2.1875,-1.70625 0,-1.75 2.1875,-1.75 l 6.16875,0 q 9.93125,0 9.93125,6.51875 l 0,8.575 q 0,6.16875 -9.5375,6.16875 l -13.60625,0 z m 13.34375,-3.4125 q 5.25,0 5.25,-2.8875 l 0,-4.76875 -18.24375,0 q -5.11875,0 -5.11875,2.66875 l 0,2.275 q 0,2.7125 5.11875,2.7125 l 12.99375,0 z"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:43.75px;font-family:'Neuropol X';-inkscape-font-specification:'Neuropol X';text-align:start;letter-spacing:5px;word-spacing:0px;text-anchor:start;fill:#263238;fill-opacity:1"
id="path3432" />
<path
d="m -262.77031,626.74323 q 0,4.9 -9.975,4.9 l -17.0625,0 q -2.1875,0 -2.1875,-1.70625 0,-1.70625 2.1875,-1.70625 l 17.0625,0 q 5.38125,0 5.38125,-1.4875 l 0,-2.45 q 0,-1.4875 -5.38125,-1.4875 l -9.0125,0 q -9.975,0 -9.975,-4.76875 l 0,-2.05625 q 0,-5.6 10.28125,-5.6 l 5.99375,0 q 2.23125,0 2.23125,1.75 0,0.875 -0.6125,1.3125 -0.56875,0.39375 -1.61875,0.39375 l -5.99375,0 q -5.73125,0 -5.73125,2.14375 l 0,1.925 q 0,1.79375 5.6875,1.79375 l 9.0125,0 q 9.7125,0 9.7125,4.4625 l 0,2.58125 z"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:43.75px;font-family:'Neuropol X';-inkscape-font-specification:'Neuropol X';text-align:start;letter-spacing:5px;word-spacing:0px;text-anchor:start;fill:#263238;fill-opacity:1"
id="path3434" />
<path
d="m -241.91709,631.64323 q -9.3625,0 -9.3625,-5.81875 l 0,-2.49375 q 0,-5.775 9.3625,-5.775 l 18.59375,0 0,-0.65625 q 0,-3.0625 -5.38125,-3.0625 l -6.16875,0 q -2.1875,0 -2.1875,-1.70625 0,-1.75 2.1875,-1.75 l 6.16875,0 q 9.93125,0 9.93125,6.51875 l 0,8.575 q 0,6.16875 -9.5375,6.16875 l -13.60625,0 z m 13.34375,-3.4125 q 5.25,0 5.25,-2.8875 l 0,-4.76875 -18.24375,0 q -5.11875,0 -5.11875,2.66875 l 0,2.275 q 0,2.7125 5.11875,2.7125 l 12.99375,0 z"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:43.75px;font-family:'Neuropol X';-inkscape-font-specification:'Neuropol X';text-align:start;letter-spacing:5px;word-spacing:0px;text-anchor:start;fill:#263238;fill-opacity:1"
id="path3436" />
<path
d="m -205.62285,617.33698 q 0,-6.95625 11.2875,-6.95625 l 3.36875,0 q 2.23125,0 2.23125,1.79375 0,1.79375 -2.23125,1.79375 l -3.54375,0 q -6.475,0 -6.475,3.28125 l 0,12.775 q 0,1.8375 -2.31875,1.8375 -2.31875,0 -2.31875,-1.8375 l 0,-12.6875 z"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:43.75px;font-family:'Neuropol X';-inkscape-font-specification:'Neuropol X';text-align:start;letter-spacing:5px;word-spacing:0px;text-anchor:start;fill:#263238;fill-opacity:1"
id="path3438" />
</g>
</g>
</g>
</g>
<g
id="g5443-0-1-5-1-9"
transform="matrix(0.55595317,0,0,0.55595317,-521.93484,-328.66104)"
inkscape:export-filename="/home/emanuele/Desktop/logo1.png"
inkscape:export-xdpi="44.860481"
inkscape:export-ydpi="44.860481">
<g
inkscape:export-ydpi="3.4165223"
inkscape:export-xdpi="3.4165223"
transform="matrix(0.09527033,0,0,0.09527033,-1695.2716,706.62921)"
id="g8856-6-1-1-9-0-1-9">
<circle
r="1485"
cy="-1361.2571"
cx="8317.3574"
id="circle8858-1-3-7-6-5-3-0"
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:50;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:export-xdpi="10.031387"
inkscape:export-ydpi="10.031387" />
<path
inkscape:export-ydpi="10.031387"
inkscape:export-xdpi="10.031387"
style="opacity:1;fill:#263238;fill-opacity:1;stroke:none;stroke-width:10;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 8560.3823,-1361.3029 a 242.947,242.947 0 0 1 -242.947,242.948 242.947,242.947 0 0 1 -242.947,-242.948 242.947,242.947 0 0 1 242.947,-242.946 242.947,242.947 0 0 1 242.947,242.946 z"
id="path8860-5-4-8-2-9-0-9"
inkscape:connector-curvature="0" />
<path
id="path8862-5-5-9-1-3-6-3"
d="m 9395.8755,-1984.028 a 1245.372,1245.372 0 0 0 -190.8415,-249.4971 l -280.8618,162.1556 c -87.542,-74.7796 -187.0349,-132.0588 -293.2407,-169.9527 -95.8868,97.1766 -172.0602,205.7604 -226.9672,323.8487 312.6411,-21.2772 635.5313,91.8725 935.2898,326.0721 l 176.7612,-102.0532 a 1245.372,1245.372 0 0 0 -120.1398,-290.5734 z"
clip-path="none"
mask="none"
style="fill:#1976d2;fill-opacity:1"
inkscape:connector-curvature="0"
inkscape:transform-center-x="-514.04855"
inkscape:transform-center-y="-444.04649" />
<path
inkscape:transform-center-y="265.80217"
inkscape:transform-center-x="-689.63727"
inkscape:connector-curvature="0"
style="fill:#42a5f5;fill-opacity:1"
mask="none"
clip-path="none"
d="m 9395.9474,-738.70387 a 1245.372,1245.372 0 0 0 120.6501,-290.02213 l -280.8618,-162.1557 c 20.99,-113.2034 20.8488,-228.0063 0.563,-338.9302 -132.1008,-34.4521 -264.2238,-46.1283 -393.9448,-34.635 174.7471,260.1165 238.2017,596.32248 185.2582,973.02076 l 176.7612,102.05309 a 1245.372,1245.372 0 0 0 191.5741,-249.33082 z"
id="path8864-4-8-1-2-4-4-4" />
<path
id="path8866-7-5-5-0-6-4-7"
d="m 8317.501,-115.97954 a 1245.372,1245.372 0 0 0 311.4916,-40.52501 l 0,-324.31131 c 108.5321,-38.42382 207.8837,-95.94755 293.8037,-168.97752 -36.214,-131.6287 -92.1636,-251.88868 -166.9776,-358.48372 -137.894,281.39369 -397.3296,504.44998 -750.0316,646.9487 l 0,204.10623 a 1245.372,1245.372 0 0 0 311.7139,41.24263 z"
clip-path="none"
mask="none"
style="fill:#1976d2;fill-opacity:1"
inkscape:connector-curvature="0"
inkscape:transform-center-x="-117.49007"
inkscape:transform-center-y="639.34029" />
<path
inkscape:transform-center-y="444.04652"
inkscape:transform-center-x="514.04857"
inkscape:connector-curvature="0"
style="fill:#42a5f5;fill-opacity:1"
mask="none"
clip-path="none"
d="m 7238.9827,-738.57936 a 1245.372,1245.372 0 0 0 190.8415,249.49714 l 280.8618,-162.15566 c 87.5421,74.77965 187.0349,132.05879 293.2407,169.95271 95.8868,-97.17659 172.0602,-205.76036 226.9672,-323.8487 -312.6411,21.27714 -635.5313,-91.87254 -935.2898,-326.07203 l -176.7612,102.0531 a 1245.372,1245.372 0 0 0 120.1398,290.57344 z"
id="path8868-6-7-4-7-2-7-3" />
<path
id="path8870-5-3-9-3-5-5-1"
d="m 7238.9108,-1983.9035 a 1245.372,1245.372 0 0 0 -120.6501,290.0221 l 280.8618,162.1557 c -20.99,113.2035 -20.8488,228.0063 -0.563,338.9302 132.1008,34.4521 264.2238,46.1283 393.9448,34.635 -174.7471,-260.1165 -238.2017,-596.3225 -185.2582,-973.0207 l -176.7612,-102.0532 a 1245.372,1245.372 0 0 0 -191.5741,249.3309 z"
clip-path="none"
mask="none"
style="fill:#1976d2;fill-opacity:1"
inkscape:connector-curvature="0"
inkscape:transform-center-x="689.63729"
inkscape:transform-center-y="-265.80221" />
<path
inkscape:transform-center-y="-639.34032"
inkscape:transform-center-x="117.49005"
inkscape:connector-curvature="0"
style="fill:#42a5f5;fill-opacity:1"
mask="none"
clip-path="none"
d="m 8317.3572,-2606.6279 a 1245.372,1245.372 0 0 0 -311.4915,40.525 l -1e-4,324.3113 c -108.5321,38.4239 -207.8837,95.9476 -293.8037,168.9776 36.214,131.6287 92.1637,251.8886 166.9776,358.4837 137.894,-281.3937 397.3296,-504.45 750.0316,-646.9487 l 1e-4,-204.1063 a 1245.372,1245.372 0 0 0 -311.714,-41.2426 z"
id="path8872-6-3-2-1-3-3-7" />
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.4 KiB

View File

View File

@ -0,0 +1,11 @@
// import WebSocket from '@uci/websocket-client'
import WebSocket from '@uci/websocket-client'
const ws = new WebSocket(process.env.WSS || 'ws://0.0.0.0:8090')
export { ws }
// leave the export, even if you don't use it
export default ({ Vue }) => {
Vue.prototype.$socket = ws
}

View File

@ -0,0 +1,59 @@
// app global css
// from https://github.com/quasarframework/quasar/tree/dev/ui/src/css
@import "../../node_modules/rfs/stylus"
$rfs-base-font-size = 1.25rem
// $rfs-font-size-unit = rem
$rfs-breakpoint = 1400px
// $rfs-breakpoint-unit = px
// $rfs-factor = 10
// $rfs-rem-value = 16
$rfs-two-dimensional = true
// $rfs-class = false
$field-font-factor = 1.3
$field-label-font-factor = 1
.q-field__native
.q-item__label
rfs($rfs-base-font-size * $field-font-factor)
.q-field__label
&.no-pointer-events
rfs($rfs-base-font-size * $field-font-factor)
.q-field__label
rfs($rfs-base-font-size * $field-label-font-factor)
padding-bottom .3em // needs to be dynamic based on font size
.q-field__control
// height +??px // needs to be computed based on font size.
.q-page-container
background $accent
display: flex;
justify-content: center;
.q-page
width 80%
.q-field
padding 0
.q-input
.q-select
background white
padding 0 .2em
margin .2em
.response
.q-textarea
.q-input
background $secondary
padding 0 1em
.q-item
padding 0
margin 0
.greyedout
opacity 0.5
input
fontsize-12

View File

@ -0,0 +1,21 @@
// Quasar Stylus Variables
// --------------------------------------------------
// To customize the look and feel of this app, you can override
// the Stylus variables found in Quasar's source Stylus files.
// Check documentation for full list of Quasar variables
// It's highly recommended to change the default colors
// to match your app's branding.
// Tip: Use the "Theme Builder" on Quasar's documentation website.
// https://quasar.dev/style/theme-builder
$primary = #006cd6
$secondary = #26A69A
$accent = #278eb0
$positive = #21BA45
$negative = #C10015
$info = #31CCEC
$warning = #F2C037

View File

@ -0,0 +1,639 @@
$space-base ?= 16px
$space-x-base ?= $space-base
$space-y-base ?= $space-base
$spaces ?= {
none: {
x: 0,
y: 0
},
xs: {
x: ($space-x-base * .25),
y: ($space-y-base * .25)
},
sm: {
x: ($space-x-base * .5),
y: ($space-y-base * .5)
},
md: {
x: $space-x-base,
y: $space-y-base
},
lg: {
x: ($space-x-base * 1.5),
y: ($space-y-base * 1.5)
},
xl: {
x: ($space-x-base * 3),
y: ($space-y-base * 3)
}
}
// Max width at which point
// current size ends
$breakpoint-xs ?= 599px
$breakpoint-sm ?= 1023px
$breakpoint-md ?= 1439px
$breakpoint-lg ?= 1919px
$flex-cols ?= 12
$flex-gutter-xs ?= ($space-base * .25)
$flex-gutter-sm ?= ($space-base * .5)
$flex-gutter-md ?= $space-base
$flex-gutter-lg ?= ($space-base * 1.5)
$flex-gutter-xl ?= ($space-base * 3)
$body-font-size ?= 14px
$body-line-height ?= 1.5
$flex-gutter ?= {
none: 0,
xs: $flex-gutter-xs,
sm: $flex-gutter-sm,
md: $flex-gutter-md,
lg: $flex-gutter-lg,
xl: $flex-gutter-xl
}
$sizes ?= {
xs: 0, // Extra small screen
sm: $breakpoint-xs + 1, // Small screen
md: $breakpoint-sm + 1, // Medium screen
lg: $breakpoint-md + 1, // Large screen
xl: $breakpoint-lg + 1 // Extra large screen
}
$breakpoint-xs-min ?= 0
$breakpoint-xs-max ?= $breakpoint-xs
$breakpoint-sm-min ?= $sizes.sm
$breakpoint-sm-max ?= $breakpoint-sm
$breakpoint-md-min ?= $sizes.md
$breakpoint-md-max ?= $breakpoint-md
$breakpoint-lg-min ?= $sizes.lg
$breakpoint-lg-max ?= $breakpoint-lg
$breakpoint-xl-min ?= $sizes.xl
$breakpoint-xl-max ?= 9999px
$headings ?= {
h1: {
size: 6rem,
line-height: 6rem,
weight: 300,
letter-spacing: -.01562em
},
h2: {
size: 3.75rem,
line-height: 3.75rem,
letter-spacing: -.00833em,
weight: 300
},
h3: {
size: 3rem,
line-height: 3.125rem,
letter-spacing: normal,
weight: 400
},
h4: {
size: 2.125rem,
line-height: 2.5rem,
letter-spacing: .00735em,
weight: 400
},
h5: {
size: 1.5rem,
line-height: 2rem,
letter-spacing: normal,
weight: 400
},
h6: {
size: 1.25rem,
line-height: 2rem,
letter-spacing: .0125em,
weight: 500
},
subtitle1: {
size: 1rem,
line-height: 1.75rem,
letter-spacing: .00937em,
weight: 400
},
subtitle2: {
size: .875rem,
line-height: 1.375rem,
letter-spacing: .00714em,
weight: 500
},
body1: {
size: 1rem,
line-height: 1.5rem,
letter-spacing: .03125em,
weight: 400
},
body2: {
size: .875rem,
line-height: 1.25rem,
letter-spacing: .01786em,
weight: 400
},
overline: {
size: .75rem,
line-height: 2rem,
letter-spacing: .16667em,
weight: 500
},
caption: {
size: .75rem,
line-height: 1.25rem,
letter-spacing: .03333em,
weight: 400
}
}
$h-tags ?= {
h1: $headings.h1,
h2: $headings.h2,
h3: $headings.h3,
h4: $headings.h4,
h5: $headings.h5,
h6: $headings.h6
}
$text-weights ?= {
thin: 100,
light: 300,
regular: 400,
medium: 500,
bold: 700,
bolder: 900
}
$primary ?= #027BE3
$secondary ?= #26A69A
$accent ?= #9C27B0
$positive ?= #21BA45
$negative ?= #C10015
$info ?= #31CCEC
$warning ?= #F2C037
$light ?= #bdbdbd
$dark ?= #424242
$faded ?= #777
$dimmed-background ?= rgba(0, 0, 0, .4)
$light-dimmed-background ?= rgba(255, 255, 255, .6)
$separator-color ?= rgba(0, 0, 0, .12)
$separator-dark-color ?= rgba(255, 255, 255, .48)
$red ?= #f44336
$red-1 ?= #ffebee
$red-2 ?= #ffcdd2
$red-3 ?= #ef9a9a
$red-4 ?= #e57373
$red-5 ?= #ef5350
$red-6 ?= #f44336
$red-7 ?= #e53935
$red-8 ?= #d32f2f
$red-9 ?= #c62828
$red-10 ?= #b71c1c
$red-11 ?= #ff8a80
$red-12 ?= #ff5252
$red-13 ?= #ff1744
$red-14 ?= #d50000
$pink ?= #e91e63
$pink-1 ?= #fce4ec
$pink-2 ?= #f8bbd0
$pink-3 ?= #f48fb1
$pink-4 ?= #f06292
$pink-5 ?= #ec407a
$pink-6 ?= #e91e63
$pink-7 ?= #d81b60
$pink-8 ?= #c2185b
$pink-9 ?= #ad1457
$pink-10 ?= #880e4f
$pink-11 ?= #ff80ab
$pink-12 ?= #ff4081
$pink-13 ?= #f50057
$pink-14 ?= #c51162
$purple ?= #9c27b0
$purple-1 ?= #f3e5f5
$purple-2 ?= #e1bee7
$purple-3 ?= #ce93d8
$purple-4 ?= #ba68c8
$purple-5 ?= #ab47bc
$purple-6 ?= #9c27b0
$purple-7 ?= #8e24aa
$purple-8 ?= #7b1fa2
$purple-9 ?= #6a1b9a
$purple-10 ?= #4a148c
$purple-11 ?= #ea80fc
$purple-12 ?= #e040fb
$purple-13 ?= #d500f9
$purple-14 ?= #aa00ff
$deep-purple ?= #673ab7
$deep-purple-1 ?= #ede7f6
$deep-purple-2 ?= #d1c4e9
$deep-purple-3 ?= #b39ddb
$deep-purple-4 ?= #9575cd
$deep-purple-5 ?= #7e57c2
$deep-purple-6 ?= #673ab7
$deep-purple-7 ?= #5e35b1
$deep-purple-8 ?= #512da8
$deep-purple-9 ?= #4527a0
$deep-purple-10 ?= #311b92
$deep-purple-11 ?= #b388ff
$deep-purple-12 ?= #7c4dff
$deep-purple-13 ?= #651fff
$deep-purple-14 ?= #6200ea
$indigo ?= #3f51b5
$indigo-1 ?= #e8eaf6
$indigo-2 ?= #c5cae9
$indigo-3 ?= #9fa8da
$indigo-4 ?= #7986cb
$indigo-5 ?= #5c6bc0
$indigo-6 ?= #3f51b5
$indigo-7 ?= #3949ab
$indigo-8 ?= #303f9f
$indigo-9 ?= #283593
$indigo-10 ?= #1a237e
$indigo-11 ?= #8c9eff
$indigo-12 ?= #536dfe
$indigo-13 ?= #3d5afe
$indigo-14 ?= #304ffe
$blue ?= #2196f3
$blue-1 ?= #e3f2fd
$blue-2 ?= #bbdefb
$blue-3 ?= #90caf9
$blue-4 ?= #64b5f6
$blue-5 ?= #42a5f5
$blue-6 ?= #2196f3
$blue-7 ?= #1e88e5
$blue-8 ?= #1976d2
$blue-9 ?= #1565c0
$blue-10 ?= #0d47a1
$blue-11 ?= #82b1ff
$blue-12 ?= #448aff
$blue-13 ?= #2979ff
$blue-14 ?= #2962ff
$light-blue ?= #03a9f4
$light-blue-1 ?= #e1f5fe
$light-blue-2 ?= #b3e5fc
$light-blue-3 ?= #81d4fa
$light-blue-4 ?= #4fc3f7
$light-blue-5 ?= #29b6f6
$light-blue-6 ?= #03a9f4
$light-blue-7 ?= #039be5
$light-blue-8 ?= #0288d1
$light-blue-9 ?= #0277bd
$light-blue-10 ?= #01579b
$light-blue-11 ?= #80d8ff
$light-blue-12 ?= #40c4ff
$light-blue-13 ?= #00b0ff
$light-blue-14 ?= #0091ea
$cyan ?= #00bcd4
$cyan-1 ?= #e0f7fa
$cyan-2 ?= #b2ebf2
$cyan-3 ?= #80deea
$cyan-4 ?= #4dd0e1
$cyan-5 ?= #26c6da
$cyan-6 ?= #00bcd4
$cyan-7 ?= #00acc1
$cyan-8 ?= #0097a7
$cyan-9 ?= #00838f
$cyan-10 ?= #006064
$cyan-11 ?= #84ffff
$cyan-12 ?= #18ffff
$cyan-13 ?= #00e5ff
$cyan-14 ?= #00b8d4
$teal ?= #009688
$teal-1 ?= #e0f2f1
$teal-2 ?= #b2dfdb
$teal-3 ?= #80cbc4
$teal-4 ?= #4db6ac
$teal-5 ?= #26a69a
$teal-6 ?= #009688
$teal-7 ?= #00897b
$teal-8 ?= #00796b
$teal-9 ?= #00695c
$teal-10 ?= #004d40
$teal-11 ?= #a7ffeb
$teal-12 ?= #64ffda
$teal-13 ?= #1de9b6
$teal-14 ?= #00bfa5
$green ?= #4caf50
$green-1 ?= #e8f5e9
$green-2 ?= #c8e6c9
$green-3 ?= #a5d6a7
$green-4 ?= #81c784
$green-5 ?= #66bb6a
$green-6 ?= #4caf50
$green-7 ?= #43a047
$green-8 ?= #388e3c
$green-9 ?= #2e7d32
$green-10 ?= #1b5e20
$green-11 ?= #b9f6ca
$green-12 ?= #69f0ae
$green-13 ?= #00e676
$green-14 ?= #00c853
$light-green ?= #8bc34a
$light-green-1 ?= #f1f8e9
$light-green-2 ?= #dcedc8
$light-green-3 ?= #c5e1a5
$light-green-4 ?= #aed581
$light-green-5 ?= #9ccc65
$light-green-6 ?= #8bc34a
$light-green-7 ?= #7cb342
$light-green-8 ?= #689f38
$light-green-9 ?= #558b2f
$light-green-10 ?= #33691e
$light-green-11 ?= #ccff90
$light-green-12 ?= #b2ff59
$light-green-13 ?= #76ff03
$light-green-14 ?= #64dd17
$lime ?= #cddc39
$lime-1 ?= #f9fbe7
$lime-2 ?= #f0f4c3
$lime-3 ?= #e6ee9c
$lime-4 ?= #dce775
$lime-5 ?= #d4e157
$lime-6 ?= #cddc39
$lime-7 ?= #c0ca33
$lime-8 ?= #afb42b
$lime-9 ?= #9e9d24
$lime-10 ?= #827717
$lime-11 ?= #f4ff81
$lime-12 ?= #eeff41
$lime-13 ?= #c6ff00
$lime-14 ?= #aeea00
$yellow ?= #ffeb3b
$yellow-1 ?= #fffde7
$yellow-2 ?= #fff9c4
$yellow-3 ?= #fff59d
$yellow-4 ?= #fff176
$yellow-5 ?= #ffee58
$yellow-6 ?= #ffeb3b
$yellow-7 ?= #fdd835
$yellow-8 ?= #fbc02d
$yellow-9 ?= #f9a825
$yellow-10 ?= #f57f17
$yellow-11 ?= #ffff8d
$yellow-12 ?= #ffff00
$yellow-13 ?= #ffea00
$yellow-14 ?= #ffd600
$amber ?= #ffc107
$amber-1 ?= #fff8e1
$amber-2 ?= #ffecb3
$amber-3 ?= #ffe082
$amber-4 ?= #ffd54f
$amber-5 ?= #ffca28
$amber-6 ?= #ffc107
$amber-7 ?= #ffb300
$amber-8 ?= #ffa000
$amber-9 ?= #ff8f00
$amber-10 ?= #ff6f00
$amber-11 ?= #ffe57f
$amber-12 ?= #ffd740
$amber-13 ?= #ffc400
$amber-14 ?= #ffab00
$orange ?= #ff9800
$orange-1 ?= #fff3e0
$orange-2 ?= #ffe0b2
$orange-3 ?= #ffcc80
$orange-4 ?= #ffb74d
$orange-5 ?= #ffa726
$orange-6 ?= #ff9800
$orange-7 ?= #fb8c00
$orange-8 ?= #f57c00
$orange-9 ?= #ef6c00
$orange-10 ?= #e65100
$orange-11 ?= #ffd180
$orange-12 ?= #ffab40
$orange-13 ?= #ff9100
$orange-14 ?= #ff6d00
$deep-orange ?= #ff5722
$deep-orange-1 ?= #fbe9e7
$deep-orange-2 ?= #ffccbc
$deep-orange-3 ?= #ffab91
$deep-orange-4 ?= #ff8a65
$deep-orange-5 ?= #ff7043
$deep-orange-6 ?= #ff5722
$deep-orange-7 ?= #f4511e
$deep-orange-8 ?= #e64a19
$deep-orange-9 ?= #d84315
$deep-orange-10 ?= #bf360c
$deep-orange-11 ?= #ff9e80
$deep-orange-12 ?= #ff6e40
$deep-orange-13 ?= #ff3d00
$deep-orange-14 ?= #dd2c00
$brown ?= #795548
$brown-1 ?= #efebe9
$brown-2 ?= #d7ccc8
$brown-3 ?= #bcaaa4
$brown-4 ?= #a1887f
$brown-5 ?= #8d6e63
$brown-6 ?= #795548
$brown-7 ?= #6d4c41
$brown-8 ?= #5d4037
$brown-9 ?= #4e342e
$brown-10 ?= #3e2723
$brown-11 ?= #d7ccc8
$brown-12 ?= #bcaaa4
$brown-13 ?= #8d6e63
$brown-14 ?= #5d4037
$grey ?= #9e9e9e
$grey-1 ?= #fafafa
$grey-2 ?= #f5f5f5
$grey-3 ?= #eeeeee
$grey-4 ?= #e0e0e0
$grey-5 ?= #bdbdbd
$grey-6 ?= #9e9e9e
$grey-7 ?= #757575
$grey-8 ?= #616161
$grey-9 ?= #424242
$grey-10 ?= #212121
$grey-11 ?= #f5f5f5
$grey-12 ?= #eeeeee
$grey-13 ?= #bdbdbd
$grey-14 ?= #616161
$blue-grey ?= #607d8b
$blue-grey-1 ?= #eceff1
$blue-grey-2 ?= #cfd8dc
$blue-grey-3 ?= #b0bec5
$blue-grey-4 ?= #90a4ae
$blue-grey-5 ?= #78909c
$blue-grey-6 ?= #607d8b
$blue-grey-7 ?= #546e7a
$blue-grey-8 ?= #455a64
$blue-grey-9 ?= #37474f
$blue-grey-10 ?= #263238
$blue-grey-11 ?= #cfd8dc
$blue-grey-12 ?= #b0bec5
$blue-grey-13 ?= #78909c
$blue-grey-14 ?= #455a64
$ios-statusbar-height ?= 20px
$z-fab ?= 990
$z-side ?= 1000
$z-marginals ?= 2000
$z-fixed-drawer ?= 3000
$z-fullscreen ?= 6000
$z-menu ?= 6000
$z-top ?= 7000
$z-tooltip ?= 9000
$z-notify ?= 9500
$z-max ?= 9998
$shadow-color ?= black
$shadow-transition ?= box-shadow .28s cubic-bezier(.4, 0, .2, 1)
$inset-shadow ?= 0 7px 9px -7px rgba($shadow-color, .7) inset
$elevation-umbra ?= rgba($shadow-color, .2)
$elevation-penumbra ?= rgba($shadow-color, .14)
$elevation-ambient ?= rgba($shadow-color, .12)
$shadow-0 ?= 0 0 0 $elevation-umbra, 0 0 0 $elevation-penumbra, 0 0 0 $elevation-ambient
$shadow-1 ?= 0 1px 3px $elevation-umbra, 0 1px 1px $elevation-penumbra, 0 2px 1px -1px $elevation-ambient
$shadow-2 ?= 0 1px 5px $elevation-umbra, 0 2px 2px $elevation-penumbra, 0 3px 1px -2px $elevation-ambient
$shadow-3 ?= 0 1px 8px $elevation-umbra, 0 3px 4px $elevation-penumbra, 0 3px 3px -2px $elevation-ambient
$shadow-4 ?= 0 2px 4px -1px $elevation-umbra, 0 4px 5px $elevation-penumbra, 0 1px 10px $elevation-ambient
$shadow-5 ?= 0 3px 5px -1px $elevation-umbra, 0 5px 8px $elevation-penumbra, 0 1px 14px $elevation-ambient
$shadow-6 ?= 0 3px 5px -1px $elevation-umbra, 0 6px 10px $elevation-penumbra, 0 1px 18px $elevation-ambient
$shadow-7 ?= 0 4px 5px -2px $elevation-umbra, 0 7px 10px 1px $elevation-penumbra, 0 2px 16px 1px $elevation-ambient
$shadow-8 ?= 0 5px 5px -3px $elevation-umbra, 0 8px 10px 1px $elevation-penumbra, 0 3px 14px 2px $elevation-ambient
$shadow-9 ?= 0 5px 6px -3px $elevation-umbra, 0 9px 12px 1px $elevation-penumbra, 0 3px 16px 2px $elevation-ambient
$shadow-10 ?= 0 6px 6px -3px $elevation-umbra, 0 10px 14px 1px $elevation-penumbra, 0 4px 18px 3px $elevation-ambient
$shadow-11 ?= 0 6px 7px -4px $elevation-umbra, 0 11px 15px 1px $elevation-penumbra, 0 4px 20px 3px $elevation-ambient
$shadow-12 ?= 0 7px 8px -4px $elevation-umbra, 0 12px 17px 2px $elevation-penumbra, 0 5px 22px 4px $elevation-ambient
$shadow-13 ?= 0 7px 8px -4px $elevation-umbra, 0 13px 19px 2px $elevation-penumbra, 0 5px 24px 4px $elevation-ambient
$shadow-14 ?= 0 7px 9px -4px $elevation-umbra, 0 14px 21px 2px $elevation-penumbra, 0 5px 26px 4px $elevation-ambient
$shadow-15 ?= 0 8px 9px -5px $elevation-umbra, 0 15px 22px 2px $elevation-penumbra, 0 6px 28px 5px $elevation-ambient
$shadow-16 ?= 0 8px 10px -5px $elevation-umbra, 0 16px 24px 2px $elevation-penumbra, 0 6px 30px 5px $elevation-ambient
$shadow-17 ?= 0 8px 11px -5px $elevation-umbra, 0 17px 26px 2px $elevation-penumbra, 0 6px 32px 5px $elevation-ambient
$shadow-18 ?= 0 9px 11px -5px $elevation-umbra, 0 18px 28px 2px $elevation-penumbra, 0 7px 34px 6px $elevation-ambient
$shadow-19 ?= 0 9px 12px -6px $elevation-umbra, 0 19px 29px 2px $elevation-penumbra, 0 7px 36px 6px $elevation-ambient
$shadow-20 ?= 0 10px 13px -6px $elevation-umbra, 0 20px 31px 3px $elevation-penumbra, 0 8px 38px 7px $elevation-ambient
$shadow-21 ?= 0 10px 13px -6px $elevation-umbra, 0 21px 33px 3px $elevation-penumbra, 0 8px 40px 7px $elevation-ambient
$shadow-22 ?= 0 10px 14px -6px $elevation-umbra, 0 22px 35px 3px $elevation-penumbra, 0 8px 42px 7px $elevation-ambient
$shadow-23 ?= 0 11px 14px -7px $elevation-umbra, 0 23px 36px 3px $elevation-penumbra, 0 9px 44px 8px $elevation-ambient
$shadow-24 ?= 0 11px 15px -7px $elevation-umbra, 0 24px 38px 3px $elevation-penumbra, 0 9px 46px 8px $elevation-ambient
$shadow-up-0 ?= 0 0 0 $elevation-umbra, 0 0 0 $elevation-penumbra, 0 0 0 $elevation-ambient
$shadow-up-1 ?= 0 -1px 3px $elevation-umbra, 0 -1px 1px $elevation-penumbra, 0 -2px 1px -1px $elevation-ambient
$shadow-up-2 ?= 0 -1px 5px $elevation-umbra, 0 -2px 2px $elevation-penumbra, 0 -3px 1px -2px $elevation-ambient
$shadow-up-3 ?= 0 -1px 8px $elevation-umbra, 0 -3px 4px $elevation-penumbra, 0 -3px 3px -2px $elevation-ambient
$shadow-up-4 ?= 0 -2px 4px -1px $elevation-umbra, 0 -4px 5px $elevation-penumbra, 0 -1px 10px $elevation-ambient
$shadow-up-5 ?= 0 -3px 5px -1px $elevation-umbra, 0 -5px 8px $elevation-penumbra, 0 -1px 14px $elevation-ambient
$shadow-up-6 ?= 0 -3px 5px -1px $elevation-umbra, 0 -6px 10px $elevation-penumbra, 0 -1px 18px $elevation-ambient
$shadow-up-7 ?= 0 -4px 5px -2px $elevation-umbra, 0 -7px 10px 1px $elevation-penumbra, 0 -2px 16px 1px $elevation-ambient
$shadow-up-8 ?= 0 -5px 5px -3px $elevation-umbra, 0 -8px 10px 1px $elevation-penumbra, 0 -3px 14px 2px $elevation-ambient
$shadow-up-9 ?= 0 -5px 6px -3px $elevation-umbra, 0 -9px 12px 1px $elevation-penumbra, 0 -3px 16px 2px $elevation-ambient
$shadow-up-10 ?= 0 -6px 6px -3px $elevation-umbra, 0 -10px 14px 1px $elevation-penumbra, 0 -4px 18px 3px $elevation-ambient
$shadow-up-11 ?= 0 -6px 7px -4px $elevation-umbra, 0 -11px 15px 1px $elevation-penumbra, 0 -4px 20px 3px $elevation-ambient
$shadow-up-12 ?= 0 -7px 8px -4px $elevation-umbra, 0 -12px 17px 2px $elevation-penumbra, 0 -5px 22px 4px $elevation-ambient
$shadow-up-13 ?= 0 -7px 8px -4px $elevation-umbra, 0 -13px 19px 2px $elevation-penumbra, 0 -5px 24px 4px $elevation-ambient
$shadow-up-14 ?= 0 -7px 9px -4px $elevation-umbra, 0 -14px 21px 2px $elevation-penumbra, 0 -5px 26px 4px $elevation-ambient
$shadow-up-15 ?= 0 -8px 9px -5px $elevation-umbra, 0 -15px 22px 2px $elevation-penumbra, 0 -6px 28px 5px $elevation-ambient
$shadow-up-16 ?= 0 -8px 10px -5px $elevation-umbra, 0 -16px 24px 2px $elevation-penumbra, 0 -6px 30px 5px $elevation-ambient
$shadow-up-17 ?= 0 -8px 11px -5px $elevation-umbra, 0 -17px 26px 2px $elevation-penumbra, 0 -6px 32px 5px $elevation-ambient
$shadow-up-18 ?= 0 -9px 11px -5px $elevation-umbra, 0 -18px 28px 2px $elevation-penumbra, 0 -7px 34px 6px $elevation-ambient
$shadow-up-19 ?= 0 -9px 12px -6px $elevation-umbra, 0 -19px 29px 2px $elevation-penumbra, 0 -7px 36px 6px $elevation-ambient
$shadow-up-20 ?= 0 -10px 13px -6px $elevation-umbra, 0 -20px 31px 3px $elevation-penumbra, 0 -8px 38px 7px $elevation-ambient
$shadow-up-21 ?= 0 -10px 13px -6px $elevation-umbra, 0 -21px 33px 3px $elevation-penumbra, 0 -8px 40px 7px $elevation-ambient
$shadow-up-22 ?= 0 -10px 14px -6px $elevation-umbra, 0 -22px 35px 3px $elevation-penumbra, 0 -8px 42px 7px $elevation-ambient
$shadow-up-23 ?= 0 -11px 14px -7px $elevation-umbra, 0 -23px 36px 3px $elevation-penumbra, 0 -9px 44px 8px $elevation-ambient
$shadow-up-24 ?= 0 -11px 15px -7px $elevation-umbra, 0 -24px 38px 3px $elevation-penumbra, 0 -9px 46px 8px $elevation-ambient
$generic-border-radius ?= 4px
$generic-hover-transition ?= .3s cubic-bezier(.25, .8, .5, 1)
$typography-font-family ?= 'Roboto', '-apple-system', 'Helvetica Neue', Helvetica, Arial, sans-serif
$min-line-height ?= 1.12
$button-border-radius ?= 3px
$button-padding ?= 4px 16px
$button-dense-padding ?= .285em
$button-transition ?= $generic-hover-transition
$button-font-size ?= 14px
$button-line-height ?= 1.718em
$button-font-weight ?= 500
$button-shadow ?= $shadow-2
$button-shadow-active ?= $shadow-5
$button-rounded-border-radius ?= 28px
$button-push-border-radius ?= 7px
$chat-message-received-color ?= black
$chat-message-received-bg ?= $green-4
$chat-message-sent-color ?= black
$chat-message-sent-bg ?= $grey-4
$chat-message-avatar-size ?= 48px
$chat-message-border-radius ?= $generic-border-radius
$chat-message-distance ?= 8px
$chat-message-text-padding ?= 8px
$item-base-color ?= $grey-5
$editor-border-color ?= $separator-color
$editor-content-padding ?= 10px
$editor-content-min-height ?= 10em
$editor-toolbar-padding ?= 4px
$editor-hr-color ?= $editor-border-color
$editor-button-gutter ?= 4px
$fab-margin ?= 5px
$table-transition ?= $generic-hover-transition
$table-border-radius ?= $generic-border-radius
$table-box-shadow ?= $shadow-2
$table-border-color ?= $separator-color
$table-hover-background ?= rgba(0, 0, 0, .03)
$table-selected-background ?= rgba(0, 0, 0, .06)
$table-dark-border-color ?= $separator-dark-color
$table-dark-hover-background ?= rgba(255, 255, 255, .07)
$table-dark-selected-background ?= rgba(255, 255, 255, .1)
$toolbar-min-height ?= 50px
$toolbar-padding ?= 0 12px
$toolbar-inset-size ?= 58px
$toolbar-title-font-size ?= 21px
$toolbar-title-font-weight ?= normal
$toolbar-title-letter-spacing ?= .01em
$toolbar-title-padding ?= 0 12px
$layout-border ?= 1px solid $separator-color
$layout-shadow ?= 0 0 10px 2px rgba(0,0,0,0.2), 0 0px 10px rgba(0,0,0,0.24)
$menu-background ?= white
$menu-box-shadow ?= $shadow-2
$menu-max-width ?= 95vw
$rating-grade-color ?= $yellow
$rating-shadow ?= 0 1px 3px rgba(0, 0, 0, .12), 0 1px 2px rgba(0, 0, 0, .24)
$tooltip-color ?= #fafafa
$tooltip-background ?= $grey-7
$tooltip-padding ?= 6px 10px
$tooltip-border-radius ?= $generic-border-radius
$tooltip-fontsize ?= 10px
$tooltip-mobile-padding ?= 8px 16px
$tooltip-mobile-fontsize ?= 14px
$option-focus-transition ?= .22s cubic-bezier(0,0,.2,1)
$input-font-size ?= 14px
$input-text-color ?= rgba(0,0,0,.87)
$input-label-color ?= rgba(0,0,0,.6)
$input-autofill-color ?= inherit
$img-width ?= 100%
$img-background-repeat ?= no-repeat
$img-loading-font-size ?= 50px
$img-content-position ?= absolute
$img-content-padding ?= 16px
$img-content-color ?= white
$img-content-background ?= rgba(0, 0, 0, .47)

View File

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<head>
<title><%= htmlWebpackPlugin.options.productName %></title>
<meta charset="utf-8">
<meta name="description" content="<%= htmlWebpackPlugin.options.productDescription %>">
<meta name="format-detection" content="telephone=no">
<meta name="msapplication-tap-highlight" content="no">
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width<% if (htmlWebpackPlugin.options.ctx.mode.cordova) { %>, viewport-fit=cover<% } %>">
<link rel="icon" type="image/png" href="statics/app-logo-128x128.png">
<link rel="icon" type="image/png" sizes="16x16" href="statics/icons/favicon-16x16.png">
<link rel="icon" type="image/png" sizes="32x32" href="statics/icons/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="96x96" href="statics/icons/favicon-96x96.png">
<link rel="icon" type="image/ico" href="statics/icons/favicon.ico">
</head>
<body>
<!-- DO NOT touch the following DIV -->
<div id="q-app"></div>
</body>
</html>

View File

@ -0,0 +1,38 @@
<template>
<q-layout view="hHh lpR fFf">
<q-header elevated class="bg-primary text-white">
<q-toolbar>
<q-toolbar-title>
Websocket Client for Four In One UCI Base Example
</q-toolbar-title>
</q-toolbar>
</q-header>
<q-page-container>
<router-view />
</q-page-container>
<q-footer elevated class="bg-grey-8 text-white">
<q-toolbar>
<q-toolbar-title>
A UCommandIt Library Example
</q-toolbar-title>
</q-toolbar>
</q-footer>
</q-layout>
</template>
<script>
export default {
data () {
return {
}
}
}
</script>
<style lang="stylus">
</style>

View File

@ -0,0 +1,22 @@
<template>
<div class="fixed-center text-center">
<p>
<img
src="~assets/sad.svg"
style="width:30vw;max-width:150px;"
>
</p>
<p class="text-faded">Sorry, nothing here...<strong>(404)</strong></p>
<q-btn
color="secondary"
style="width:200px;"
@click="$router.push('/')"
>Go back</q-btn>
</div>
</template>
<script>
export default {
name: 'Error404'
}
</script>

View File

@ -0,0 +1,167 @@
<template>
<q-page padding :class="{greyedout: offLine}" >
<q-list class="">
<q-item class="">
<q-item-section side>
<q-btn :disabled="offLine" color="secondary" round @click="send" icon="send">
<q-tooltip>
Click to send this command and payload to the sever
</q-tooltip>
</q-btn>
</q-item-section>
<q-item-section >
<q-item-label>Command</q-item-label>
<q-select
v-model="cmd"
:options="commands"
label="Choose An Available Command to Send"
emit-value
></q-select>
</q-item-section>
</q-item>
<q-item><div class="col">Property</div>:<div class="col">Value</div></q-item>
<q-item><q-input class="col" readonly v-model="key[0]" />:<q-input dense class="col" v-model="value[0]" /></q-item>
<q-item><q-input class="col" readonly v-model="key[1]" />:<q-input class="col" v-model="value[1]" /></q-item>
<q-item><q-input class="col" label="a custom property in payload" v-model="key[2]" />:<q-input class="col" v-model="value[2]" /></q-item>
</q-list>
<q-btn :class="switches[0]" @click="toggle(1)" label="Switch 1"><q-tooltip>Click to Toggle Switch</q-tooltip></q-btn>
<q-btn :class="switches[1]" @click="toggle(2)" label="Switch 2"><q-tooltip>Click to Toggle Switch</q-tooltip></q-btn>
<q-btn :class="switches[2]" @click="toggle(3)" label="Switch 3"><q-tooltip>Click to Toggle Switch</q-tooltip></q-btn>
<q-btn :class="switches[3]" @click="toggle(4)" label="Switch 4"><q-tooltip>Click to Toggle Switch</q-tooltip></q-btn>
<q-list class="response">
<q-input v-model="packet" readonly label="Sending Packet:" />
<q-input v-model="res" readonly label="Socket/Server Response Packet" />
<q-input v-model="pushed" readonly type="textarea" label="A Packet Pushed from Socket/Server:" />
</q-list>
</q-page>
</template>
<script>
import btc from 'better-try-catch'
// import socket from '../socket.js'
export default {
data () {
return {
cmd: 'switch/toggle',
offLine: false,
commands: [
{
label: 'turn a switch on',
value: 'switch/on',
description: 'turn on something',
icon: 'mdi:lightbulb'
},
{
label: 'turn a switch off',
value: 'switch/off',
description: 'turn off something',
icon: 'lightbulb-off'
},
{
label: 'toggle a switch',
value: 'switch/toggle',
description: 'turn off something',
icon: 'mdi:lightbulb'
},
{
label: 'ack',
value: 'ack',
description: 'turn off something',
icon: 'no'
},
{
label: 'echo',
value: 'echo',
description: 'echo',
icon: 'no'
}
],
packet: '',
res: '',
key: ['id', 'state', 'sender'],
value: [1, 'off', 'websocket client'],
pushed: '',
switches: ['off', 'off', 'off', 'off']
}
},
methods: {
async send (packet) {
this.clear()
if (!packet.cmd) {
packet = {
cmd: this.cmd,
[this.key[0]]: this.value[0],
[this.key[1]]: this.value[1],
[this.key[2]]: this.value[2]
}
}
this.packet = JSON.stringify(packet)
let [err, res] = await btc(this.$socket.send)(packet)
if (err) {
console.log('error ', err)
this.$q.notify({
color: 'negative',
message: 'Error in repsonse of packet send'
})
} else {
delete res._header
if (res.ack) {
this.$q.notify({
color: 'positive',
message: 'ack reply received'
})
}
this.res = JSON.stringify(res)
}
},
clear () {
this.pushed = ''
this.resMsg = ''
this.resPayload = ''
this.packet = ''
},
toggle (id) {
let packet = { cmd: 'switch/toggle', id: id, state: this.switches[id - 1] }
this.send(packet)
}
},
async mounted () {
this.$q.notify({
color: 'info',
message: `Client connecting to', ${this.$socket.url}`
})
let [err] = await btc(this.$socket.connect)()
if (err) {
this.$q.notify({
color: 'negative',
// message: 'Websocket Server Not Available'
message: err.msg
})
this.offLine = true
} else {
this.$q.notify({ color: 'positive', message: 'Ready' })
this.$socket.listen()
this.clear()
this.offLine = false
}
this.$socket.on('pushed', packet => {
delete packet._header
this.pushed = JSON.stringify(packet)
if (packet.cmd === 'switch/status') {
this.switches[packet.id - 1] = packet.state
}
})
}
}
</script>
<style lang="stylus">
.on
background $positive
.off
background grey
</style>

View File

@ -0,0 +1,26 @@
import Vue from 'vue'
import VueRouter from 'vue-router'
import routes from './routes'
Vue.use(VueRouter)
/*
* If not building with SSR mode, you can
* directly export the Router instantiation
*/
export default function (/* { store, ssrContext } */) {
const Router = new VueRouter({
scrollBehavior: () => ({ x: 0, y: 0 }),
routes,
// Leave these as is and change from quasar.conf.js instead!
// quasar.conf.js -> build -> vueRouterMode
// quasar.conf.js -> build -> publicPath
mode: process.env.VUE_ROUTER_MODE,
base: process.env.VUE_ROUTER_BASE
})
return Router
}

View File

@ -0,0 +1,20 @@
const routes = [
{
path: '/',
component: () => import('layouts/default.vue'),
children: [
{ path: '', component: () => import('pages/Index.vue') }
]
}
]
// Always leave this as last one
if (process.env.MODE !== 'ssr') {
routes.push({
path: '*',
component: () => import('pages/Error404.vue')
})
}
export default routes

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 700 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1240" height="1240"><path d="M585.5 119.157c-24.392 1.775-55.454 6.582-80.743 12.495l-10.743 2.512-.257 65.522-.257 65.523-12.5 4.911c-24.453 9.608-54.301 25.574-75.692 40.488-14.402 10.041-29.52 22.393-29.025 23.713.243.648 1.692 5.443 3.22 10.655 11.868 40.48 32.267 84.639 55.173 119.442l8.503 12.917 6.683-12.417c23.502-43.672 46.807-74.722 84.066-112.002 25.738-25.754 47.825-44.173 78.072-65.109 33.386-23.108 77-46.934 117.761-64.332l16.261-6.94-.261-40.974-.261-40.973-4.5-1.319c-8.958-2.625-43.862-9.169-59.5-11.155-31.749-4.032-66.513-5.102-96-2.957M734.906 277.25c-22.472 24.627-42.334 51.05-58.443 77.75-5.099 8.45-21.464 39.014-21.462 40.083 0 .229 14.287.445 31.75.479 33.627.065 45.612 1.017 75.303 5.978 85.759 14.331 172.63 53.33 254.813 114.394l15.221 11.31 25.706-14.808c14.138-8.144 30.112-17.312 35.497-20.372l9.79-5.564-.61-3.5c-1.096-6.29-14.149-44.185-19.977-58-17.139-40.622-35.248-72.077-61.599-107-11.686-15.488-30.818-37.844-39.039-45.62l-4.356-4.119-55 31.858c-30.25 17.522-55.747 32.106-56.66 32.408-1.079.358-4.75-1.93-10.5-6.543-20.084-16.116-40.545-29.274-64.607-41.547C777.601 277.739 748.585 265 746.461 265c-.207 0-5.407 5.512-11.555 12.25m-483.911 2.889c-52.729 57.452-89.592 121.745-111.254 194.044l-3.501 11.683 56.707 32.735 56.706 32.734-1.326 7.583c-4.069 23.272-5.894 57.248-4.381 81.582.926 14.898 4.591 46.254 5.526 47.28.898.986 34.289 8.122 48.028 10.264 28.958 4.515 64.488 6.499 90.867 5.074 10.377-.56 19.07-1.222 19.319-1.471.248-.248-2.035-4.306-5.074-9.017-37.96-58.843-62.879-131.564-72.005-210.13-3.219-27.711-3.869-43.866-3.307-82.149.534-36.338 1.631-53.441 5.12-79.832.798-6.039 1.368-11.04 1.266-11.112-.102-.073-15.261-8.815-33.686-19.427-18.425-10.613-34.464-19.919-35.642-20.682-2.027-1.312-2.739-.735-13.363 10.841M606.5 522.653c-25.669 4.073-49.712 18.357-64.437 38.283-12.956 17.531-18.769 33.768-19.713 55.064-1.087 24.502 5.901 45.6 21.676 65.452 13.091 16.473 32.315 28.634 53.974 34.141 10.222 2.599 34.507 2.629 44 .054 24.469-6.637 42.328-18.418 56.253-37.107 8.576-11.511 13.261-21.347 17.403-36.54 2.782-10.202 2.502-34.762-.523-46-4.654-17.286-13.403-32.422-26.057-45.076-18.639-18.639-40.322-27.911-66.576-28.47-7.15-.152-14.35-.062-16 .199m243.676 14.396c-9.529.421-17.487.928-17.685 1.126-.198.198 3.43 6.427 8.061 13.843 9.872 15.805 25.076 45.255 32.515 62.982 17.89 42.628 30.726 92.267 36.33 140.5 5.776 49.707 5.41 110.661-.955 158.936-.793 6.015-1.442 12.144-1.442 13.62 0 2.476 2.712 4.248 35.027 22.888 19.264 11.113 35.44 20.059 35.946 19.88.506-.178 5.205-4.957 10.444-10.619 40.968-44.285 71.417-91.321 94.113-145.385 7.518-17.906 21.003-57.989 20.208-60.062-.302-.785-25.229-15.681-55.393-33.103-30.165-17.421-55.37-32.011-56.011-32.422-.843-.54-.908-2.132-.233-5.74 6.158-32.924 6.854-78.418 1.771-115.688l-2.141-15.695-10.616-2.49c-29.261-6.862-56.983-10.974-81.615-12.105-25.026-1.149-31.483-1.212-48.324-.466M173.193 732.877c-18.869 10.91-34.771 20.301-35.338 20.868-.708.708.416 5.685 3.59 15.893 22.372 71.955 57.534 133.26 108.468 189.112 6.144 6.737 11.715 12.235 12.379 12.217.665-.019 26.402-14.607 57.195-32.419l55.987-32.386 5.013 4.231c28.719 24.239 69.849 48.122 106.705 61.961l6.691 2.512 11.972-13.183c14.282-15.727 20.74-23.512 31.675-38.183 15.78-21.171 34.174-51.134 42.972-70l4.197-9-32.6-.072c-33.662-.073-44.236-.877-72.599-5.517-52.086-8.52-104.017-26.026-157-52.926-34.47-17.5-69.941-39.589-100.682-62.699-7.525-5.657-13.825-10.277-14-10.266-.175.011-15.756 8.946-34.625 19.857M791.52 772.25c-22.058 42.207-48.977 78.371-85.449 114.795-38.124 38.072-77.236 67.416-127.546 95.69-17.969 10.099-51.743 26.686-70.775 34.76l-13.75 5.834v82.327l13.75 3.194c23.233 5.395 43.059 8.689 68.25 11.336 16.09 1.691 73.361 1.705 89 .021 25.772-2.774 47.439-6.375 68.25-11.344l12.75-3.045V974.95l2.75-1.001c20.982-7.643 52.157-22.755 69.847-33.859 18.882-11.852 45.274-31.559 45.357-33.868.102-2.831-15.119-46.644-20.841-59.99-7.171-16.726-16.651-35.878-24.655-49.808-6.712-11.68-20.708-33.401-21.531-33.415-.315-.005-2.748 4.154-5.407 9.241" fill="#42a5f5" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

4
nodemon.json Normal file
View File

@ -0,0 +1,4 @@
{
"ignoreRoot": [".git","examples/ws-fio-client"],
"watch": ["node_modules/@uci/","node_modules/@uci-utils/","src/","index.js","examples/"]
}

View File

@ -1,18 +1,17 @@
{ {
"name": "@uci/base", "name": "@uci/base",
"version": "0.1.25", "version": "0.1.26",
"description": "Multi type and transport JSON packet communication base class. Used in UCI extended classes", "description": "Multi type and transport JSON packet communication base class. Used in UCI extended classes",
"main": "src/base", "main": "src/base",
"scripts": { "scripts": {
"fiod": "UCI_ENV=dev ./node_modules/.bin/nodemon -r esm examples/four-in-one", "fiod": "UCI_ENV=dev ./node_modules/.bin/nodemon -r esm --preserve-symlinks examples/four-in-one",
"fio": "nodemon -r esm examples/four-in-one", "fio": "nodemon -r esm examples/four-in-one",
"dy": "node -r esm examples/dynamic", "dy": "node -r esm examples/dynamic",
"web": "UCI_DEV=true nodemon -r esm examples/web", "web": "UCI_DEV=true nodemon -r esm examples/web",
"mqtt": "nodemon -r esm examples/mqtt", "mqtt": "nodemon -r esm examples/mqtt",
"ha-mqtt": "nodemon -r esm examples/ha-mqtt", "ha-mqtt": "nodemon -r esm examples/ha-mqtt",
"testw": "mocha -r esm test/*.test.mjs --watch --recurse --watch-extensions mjs", "testw": "mocha -r esm test/*.test.mjs --watch --recurse ",
"test": "mocha -r esm test/*.test.mjs", "test": "mocha -r esm test/*.test.mjs"
"testci": "istanbul cover ./node_modules/.bin/_mocha --report lcovonly -- -R spec --recursive && codecov || true"
}, },
"author": "David Kebler", "author": "David Kebler",
"license": "MIT", "license": "MIT",
@ -40,7 +39,7 @@
"@uci-utils/bind-funcs": "^0.2.4", "@uci-utils/bind-funcs": "^0.2.4",
"@uci-utils/logger": "^0.0.15", "@uci-utils/logger": "^0.0.15",
"@uci/mqtt": "^0.1.12", "@uci/mqtt": "^0.1.12",
"@uci/socket": "^0.2.17", "@uci/socket": "^0.2.19",
"@uci/websocket": "^0.3.8", "@uci/websocket": "^0.3.8",
"await-to-js": "^2.1.1", "await-to-js": "^2.1.1",
"p-settle": "^3.1.0" "p-settle": "^3.1.0"

View File

@ -304,25 +304,35 @@ class Base extends EventEmitter {
// add set of functions to class prop/space and then register with this // add set of functions to class prop/space and then register with this
addNamespace(space, type, trans) { addNamespace(space, type, trans) {
if (type !=='c' || type !=='s') {
trans = type
type = 's' }
trans = this._validateTransport(trans)
if (trans) return this._namespaces[type + trans].unshift(space) if (trans) return this._namespaces[type + trans].unshift(space)
else return this._namespaces[type].unshift(space) else return this._namespaces[type].unshift(space)
} }
// TODO confirm Object.assign will be ok as it is not a deep copy // TODO confirm Object.assign will be ok as it is not a deep copy
// one off add a command function or two to basic namespaces which is called before default // one off add a command function or two to basic namespaces which is called before default
amendCommands(funcs, trans, type) {
if (!trans && !type) type = 's'
if (trans ==='c' || trans ==='s') {
type = trans
trans = ''
}
trans = this._validateTransport(trans)
if (!this['_'+type+trans]) this['_'+type+trans] = {}
Object.assign(this['_'+type+trans], funcs) // trans is type here
log.debug({msg:'amended namespace', default_key:'_'+type+trans, functions:this['_'+type+trans]})
}
amendConsumerCommands(funcs, trans) { amendConsumerCommands(funcs, trans) {
if (trans) { this.amendCommands(funcs,trans,'c')
if (!this._c[trans]) this._c[trans] = {}
Object.assign(this._c[trans], funcs)
}
Object.assign(this._c, funcs)
} }
amendSocketCommands(funcs, trans) { amendSocketCommands(funcs, trans) {
if (trans) { this.amendCommands(funcs,trans)
if (!this._s[trans]) this._s[trans] = {}
Object.assign(this._s[trans], funcs)
}
Object.assign(this._s, funcs)
} }
// func should take and return a packet. if type // func should take and return a packet. if type
@ -338,21 +348,6 @@ class Base extends EventEmitter {
this._packetHook('afterProcess', func,opts) this._packetHook('afterProcess', func,opts)
} }
_packetHook(hook,func,opts) {
log.debug({msg:'hooking a socket(s)', method:'_packetHook', line:334, hook:hook, function:func, options:opts})
let {name,type,trans,all} = opts
if (name) this._socket[name][hook] = func
else {
log.debug({msg:'sockets available to hook', method:'_packetHook', line:338, sockets: Object.keys(this._socket)})
for (let name of Object.keys(this._socket)) {
if (this._socket[name].type === type) this._socket[name][hook] = func
if (this._socket[name].transport === trans) this._socket[name][hook] = func
if (all) this._socket[name][hook] = func
if (this._socket[name][hook]) log.debug({msg:'hooked socket', method:'_packetHook', line:343, name:name, type:this._socket[name].type, trans:this._socket[name].transport})
}
}
}
// A Big Hammer - use only if necessary - default with hooks should suffice // A Big Hammer - use only if necessary - default with hooks should suffice
// these three will pre-empt default processor to be called in ._packetProcess // these three will pre-empt default processor to be called in ._packetProcess
@ -388,6 +383,24 @@ class Base extends EventEmitter {
* *
*/ */
_packetHook(hook,func,opts) {
log.debug({msg:'hooking a socket(s)', method:'_packetHook', line:334, hook:hook, function:func, options:opts})
let {name,type,trans,all} = opts
if (name) this._socket[name][hook] = func
else {
log.debug({msg:'sockets available to hook', method:'_packetHook', line:338, sockets: Object.keys(this._socket)})
for (let name of Object.keys(this._socket)) {
if (this._socket[name].type === type) this._socket[name][hook] = func
if (this._socket[name].transport === trans) this._socket[name][hook] = func
if (all) this._socket[name][hook] = func
if (this._socket[name][hook]) log.debug({msg:'hooked socket', method:'_packetHook', line:343, name:name, type:this._socket[name].type, trans:this._socket[name].transport})
}
}
}
/* /*
**********default packet processor for all sockets **********default packet processor for all sockets
* this can be hooked or replaced all together * this can be hooked or replaced all together
@ -436,6 +449,27 @@ class Base extends EventEmitter {
} }
} }
_validateTransport(trans, type='s') {
const valids = {
w:'w',
web:'w',
n:'n',
named:'n',
unix:'n',
pipe:'n',
t:'t',
tcp:'t',
net:'t',
network:'t',
m:'m',
mqtt:'m',
}
trans = valids[trans] || ''
if (type !== 'c' && trans ==='w') trans = ''
return trans
}
_transport(name) { _transport(name) {
return this._socket[name].transport return this._socket[name].transport
} //getter for socket transport } //getter for socket transport
@ -457,7 +491,7 @@ class Base extends EventEmitter {
return cmd_func return cmd_func
} }
// takes command and returns corresponding function in a hash // takes command and returns corresponding function in a hash, recurisve walk
_getCmdFunc(cmd, obj) { _getCmdFunc(cmd, obj) {
if (typeof cmd === 'string') { if (typeof cmd === 'string') {
if (!obj) obj = this if (!obj) obj = this

View File

@ -45,16 +45,18 @@ const _process = {
} }
} }
// default name spaces
const namespaces = { const namespaces = {
s: ['_s'], s: ['_s'], // default command functions below
c: ['_c'], c: ['_c'], // default command functions below
cn: ['cn'], cn: ['_cn'],
ct: ['ct'], ct: ['_ct'],
sn: ['sn'], cm: ['_cm'],
st: ['st'], // no cw because websocket client runs in browser only
cm: ['cm'], sn: ['_sn'],
sm: ['sm'], st: ['_st'],
sw: ['sw'], sm: ['_sm'],
sw: ['_sw'],
} }
/* /*
@ -67,14 +69,14 @@ const defaultCmds ={
s:{ s:{
echo: async packet => { echo: async packet => {
packet.processed = true packet.processed = true
packet.info = 'default socket echo' packet.msg = 'default socket echo'
return packet return packet
}, },
// add sedning along an ack to any consumers and or pushing to other sockets on this device // add sedning along an ack to any consumers and or pushing to other sockets on this device
ack: async packet => { ack: async packet => {
packet.cmd = 'reply' packet.cmd = 'reply'
packet.ack = true packet.ack = true
packet.info = 'this is the base default ack, superceed in your extended class' packet.msg = 'this is the base default ack, superceed in your extended class'
return packet return packet
} }
}, },