Merge branch 'release/v0.1.0'

This commit is contained in:
Jay 2017-09-14 18:01:56 +08:00
commit 6589a64321
25 changed files with 1446 additions and 135 deletions

2
.gitignore vendored
View File

@ -1,3 +1,5 @@
node_modules
yarn.lock
package-lock.json
config.json
config.default.json

View File

@ -1,51 +1,58 @@
const EventEmitter = require('events')
const EventEmitter = require('events') // eslint-disable-line
const escpos = require('escpos')
const config = require('./config.json')
// const config = require('./config.json')
const fs = require('fs')
const iconv = require('iconv-lite')
class PrinterDevice {
constructor() {
class PrinterDevice extends EventEmitter {
constructor () {
super()
this._serial = ''
this._feed = 8
this._isOpen = false
this._device = null
this._printer = null
this._type = null // type = serial or console
this._count = 0
this._lastPrint = ''
}
encodeStr(str){
if(!str || typeof str != 'string') return false
encodeStr (str) {
if (!str || typeof str !== 'string') return false
return iconv.encode(str, 'big5')
}
async connect() {
async connect () {
let self = this
let chkSerial = await new Promise((resolve, reject) => {
fs.access(config.printer.serial, err => {
fs.access(self._serial, err => {
if (err) return resolve(false)
return resolve(true)
})
})
if (chkSerial) {
this._device = new escpos.Serial(config.printer.serial)
this._device = new escpos.Serial(this._serial)
this._type = 'serial'
} else {
this._device = new escpos.Console()
this._type = 'console'
}
this._printer = new escpos.Printer(this._device)
this.openSerial()
await this.openSerial()
}
async openSerial() {
if (!this._device || this._type != 'serial') return
async openSerial () {
let self = this
if (!this._device || this._type !== 'serial') return
this._isOpen = await new Promise((resolve, reject) => {
this._device.open(err => {
self._device.open(err => {
if (err) return resolve(false)
return resolve(true)
})
})
}
async printerString(str) {
async printerString (str) {
if (!this._isOpen || !this._printer) return false
this._printer.font('a')
@ -66,8 +73,8 @@ class PrinterDevice {
case 's':
let size = tmp.substring(2)
let sarr = size.trim().split(',')
if (sarr.length != 2) break
if(!isFinite(sarr[0]) || !isFinite(sarr[1])) break
if (sarr.length !== 2) break
if (!isFinite(sarr[0]) || !isFinite(sarr[1])) break
sarr = sarr.map(t => Math.floor(t))
this._printer.size(sarr[0], sarr[1])
break
@ -77,26 +84,74 @@ class PrinterDevice {
}
}
this._printer.cut(true, config.printer.cutFeed || 4)
this._printer.cut(true, this._feed || 4)
this._lastPrint = str
this.addCount()
this.emit('printDone')
return true
}
async close() {
async printTestPage () {
await this.connect()
if (!this._isOpen) return false
this._printer.align('ct')
this._printer.size(1, 2)
this._printer.text('Print Test Page')
this._printer.align('lt')
this._printer.size(1, 1)
this._printer.text('Test Page Content')
this._printer.cut(true, this._feed)
await this.close()
return true
}
async close () {
if (!this._device) return
if (this._type == 'serial') {
if (this._type === 'serial') {
let self = this
await new Promise((resolve, reject) => {
self._printer.close(() => {
self._printer = null
self._isOpen = false
resolve(1)
})
})
}
}
get isOpen() {
return this._isOpen ? true : false
addCount () {
this._count++
}
get isOpen () {
return !!this._isOpen
}
set serial (str) {
this._serial = str
}
get serial () {
return this._serial
}
set feed (str) {
if (!isFinite(str)) return
this._feed = Math.floor(parseInt(str))
}
get feed () {
return this._feed
}
get count () {
return this._count
}
get lastPrint () {
return this._lastPrint || ''
}
}
module.exports = new PrinterDevice()
module.exports = new PrinterDevice()

63
app.js
View File

@ -1,59 +1,10 @@
const bleno = require('bleno')
const config = require('./config.json')
const adapterName = 'BLE_Printer'
const serverUUID = config.uuid.main
const localEvent = require('./localEvent')
const fs = require('fs')
const MainService = require('./main-service')
// set global env
process.env.PROJECT_ROOT = __dirname
const printer = require('./PrinterDev')
require('top-level-await')
async function initSystem(){
try{
await printer.connect()
}catch(err){
throw err
}
try {
require('./server')
} catch (err) {
throw err
}
initSystem()
bleno.on('stateChange', state => {
console.log(`bt device state ${state}`)
if (state == 'poweredOn') {
bleno.startAdvertising(adapterName, [serverUUID], (error) => {
})
} else {
bleno.stopAdvertising()
}
})
bleno.on('advertisingStart', function (error) {
console.log('on -> advertisingStart: ' + (error ? 'error ' + error : 'success'));
if (!error) {
bleno.setServices([new MainService()], error => {
console.log('set service', error)
})
}
})
localEvent.on('print', async (str) => {
let status = false
if (printer.isOpen) {
try {
status = await printer.printerString(str)
} catch (err) {
status = false
}
}
localEvent.emit('printResult', status)
})
process.on('SIGINT', () => {
printer.close()
process.exit(0)
})

View File

@ -1,6 +1,6 @@
const bleno = require('bleno')
const config = require('./config.json')
const localEvent = require('./localEvent')
const config = require('../config.json')
const localEvent = require('../localEvent')
var tmp = []
var idx = 0
@ -12,9 +12,9 @@ const notifyDesciptor = new bleno.Descriptor({
})
class DataCharacteristic extends bleno.Characteristic {
constructor() {
constructor () {
super({
uuid: config.uuid.service.func.data,
uuid: config.ble.uuid.characteristic,
properties: ['write', 'writeWithoutResponse', 'notify'],
descriptors: [
notifyDesciptor
@ -23,15 +23,15 @@ class DataCharacteristic extends bleno.Characteristic {
this._notifyFunc = null
}
onWriteRequest(data, offset, withoutResponse, callback) {
onWriteRequest (data, offset, withoutResponse, callback) {
console.log(`offset :: ${offset} , withoutRes :: ${withoutResponse}`)
console.log(`get data`, data)
if (data.length == 1 && data[0] == 0x00) {
if (data.length === 1 && data[0] === 0x00) {
tmp = []
idx = 0
failFlag = false
} else if (data.length == 1 && data[0] == 0xff) {
} else if (data.length === 1 && data[0] === 0xff) {
console.log(Buffer.from(tmp).toString())
if (!failFlag) {
localEvent.emit('print', Buffer.from(tmp).toString())
@ -53,17 +53,17 @@ class DataCharacteristic extends bleno.Characteristic {
}
}
sendNotidy(str) {
sendNotidy (str) {
if (!this._notifyFunc) return
this._notifyFunc(Buffer.from(str))
}
onSubscribe(maxValueSize, cb) {
onSubscribe (maxValueSize, cb) {
console.log(maxValueSize)
this._notifyFunc = cb
}
onUnsubscribe() {
onUnsubscribe () {
this._notifyFunc = null
}
}

24
ble/index.js Normal file
View File

@ -0,0 +1,24 @@
const config = require('../config.json')
const bleno = require('bleno')
const adapterName = 'BLE_Printer'
const serverUUID = config.ble.uuid.main
const MainService = require('./main-service')
bleno.on('stateChange', state => {
console.log(`bt device state ${state}`)
if (state === 'poweredOn') {
bleno.startAdvertising(adapterName, [serverUUID])
} else {
bleno.stopAdvertising()
}
})
bleno.on('advertisingStart', function (error) {
console.log('on -> advertisingStart: ' + (error ? 'error ' + error : 'success'))
if (!error) {
bleno.setServices([new MainService()], error => {
console.log('set service', error)
})
}
})

View File

@ -1,14 +1,14 @@
const bleno = require('bleno')
const config = require('./config.json')
const localEvent = require('./localEvent')
const config = require('../config.json')
const localEvent = require('../localEvent')
const GdataCharacteristic = require('./gdata-characteristic')
const dataCharacteristic = new GdataCharacteristic()
class MainService extends bleno.PrimaryService {
constructor() {
constructor () {
super({
uuid: config.uuid.service.id,
uuid: config.ble.uuid.service,
characteristics: [
dataCharacteristic
]

View File

@ -1,15 +0,0 @@
{
"uuid":{
"main": "930da73d-1b80-4d3e-a8b6-d9b4e7e771bd",
"service":{
"id":"dd535b71-8f05-4e30-beb0-6fa7ea3dfc3e",
"func":{
"data":"00000001-8f05-4e30-beb0-6fa7ea3dfc3e"
}
}
},
"printer": {
"serial": "/dev/ttyUSB0",
"cutFeed": 8
}
}

View File

@ -1,10 +1,10 @@
const EventEmitter = require('events')
const util = require('util')
const util = require('util') //eslint-disable-line
class LocalEvent extends EventEmitter {
constructor(){
constructor () { // eslint-disable-line
super()
}
}
module.exports = new LocalEvent()
module.exports = new LocalEvent()

View File

@ -3,10 +3,26 @@
"version": "0.0.1",
"main": "app.js",
"license": "MIT",
"scripts": {
"check": "standard --fix --verbose"
},
"dependencies": {
"bleno": "^0.4.2",
"escpos": "^2.4.3",
"iconv-lite": "^0.4.18",
"uuid": "^3.1.0"
"kcors": "^2.2.1",
"koa": "^2.3.0",
"koa-body": "^2.3.0",
"koa-ejs": "^4.1.0",
"koa-morgan": "^1.0.1",
"koa-mount": "^3.0.0",
"koa-router": "^7.2.1",
"koa-static": "^4.0.1",
"top-level-await": "^1.1.0",
"uuid": "^3.1.0",
"ws": "^3.1.0"
},
"devDependencies": {
"standard": "^10.0.3"
}
}

25
public/css/main.css Normal file
View File

@ -0,0 +1,25 @@
html, body{
height: 100%;
width: 100%;
background-color: #eee;
}
.t-right {
text-align: right;
}
#qr img {
margin: 0 auto;
}
.clearfix:before,
.clearfix:after {
content: "";
display: table;
}
.clearfix:after {
clear: both;
}
.clearfix {
*zoom: 1; /*For IE 6&7 only*/
}

364
public/css/semantic.min.css vendored Executable file

File diff suppressed because one or more lines are too long

4
public/js/jquery-3.2.1.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
public/js/qrcode.min.js vendored Normal file

File diff suppressed because one or more lines are too long

19
public/js/semantic.min.js vendored Executable file

File diff suppressed because one or more lines are too long

139
route/dashboard.js Normal file
View File

@ -0,0 +1,139 @@
/* eslint-disable no-throw-literal */
/* eslint-disable */
const KoaRouter = require('koa-router')
const router = new KoaRouter()
const fs = require('fs')
const path = require('path')
const Printer = require('../PrinterDev')
const KoaBody = require('koa-body')
const uuid = require('uuid')
const config = require('../config.json')
/* eslint-enable */
router.use(async (c, n) => {
c.data = {
title: 'Printer System Dashboard'
}
let status = 1
try {
await n()
} catch (err) {
status = 0
if (typeof err === 'string') {
c.body = err
} else {
c.body = err.toString()
}
}
if (c.async) {
c.body = {
status,
msg: c.body
}
}
})
router.get('/', async (c, n) => {
c.data.route = 'index'
await c.render('dashboard/index', c.data)
})
router.get('/setting', async (c, n) => {
c.data.route = 'setting'
let ttys = await new Promise((resolve, reject) => {
fs.readdir(path.resolve('/dev'), (err, list) => {
if (err) return resolve([])
let arr = []
for (let it of list) {
if (/^tty[A-z]/.test(it)) {
arr.push(path.resolve('/dev', it))
}
}
resolve(arr)
})
})
c.data.ttys = ttys
c.data.defConfig = config
await c.render('dashboard/index', c.data)
})
router.post('/api/print', KoaBody(), async (c, n) => {
c.async = true
let arr = c.request.body
if (!arr.data) throw 'print data empty'
if (config.api.secret && !arr.key) throw 'access key enpty'
if (config.api.secret !== arr.key.trim()) throw 'access key error'
let status = await Printer.printerString(arr.data)
if (!status) throw 'printer fail'
c.body = 'print success'
})
.post('/api/printer_test', KoaBody(), async (c, n) => {
c.async = true
let arr = c.request.body
if (!arr.device) throw '請輸入印表機連接埠'
let device = arr.device
let feed = arr.feed || 8
if (!isFinite(feed) || feed < 0) throw '切紙前換行必須是整數'
feed = typeof feed !== 'number' ? Math.floor(parseInt(feed)) : feed
let oriDev = Printer.serial
let oriFeed = Printer.feed
if (Printer.isOpen) {
await Printer.close()
}
Printer.serial = device
Printer.feed = feed
let status = await Printer.printTestPage()
if (!status) c.body = '測試失敗'
else c.body = '測試成功'
Printer.serial = oriDev
Printer.feed = oriFeed
await Printer.connect()
})
.post('/api/write_config', KoaBody(), async (c, n) => {
c.async = true
let arr = c.request.body
if (!arr.tty) throw '請輸入印表機連接埠'
if (!arr.ble || (arr.ble !== 'yes' && arr.ble !== 'no')) throw '請輸入藍牙啟用設定'
let feed = arr.feed || 8
if (!isFinite(feed) || feed < 0) throw '切紙前換行必須是整數'
feed = typeof feed !== 'number' ? Math.floor(parseInt(feed)) : feed
let json = Object.assign({}, config)
json.ble.enable = arr.ble === 'yes'
json.printer.serial = arr.tty
json.printer.feed = feed
json.api.secret = arr.secret || ''
let wconfig = await new Promise((resolve, reject) => {
fs.writeFile(path.resolve(process.env.PROJECT_ROOT, 'config.json'), JSON.stringify(json, null, 2), {
mode: 0o664,
encoding: 'utf8',
flag: 'w'
}, err => {
if (err) return resolve(false)
return resolve(true)
})
})
if (!wconfig) throw '設定檔寫入失敗'
c.body = '設定檔寫入成功'
})
module.exports = router

124
route/install.js Normal file
View File

@ -0,0 +1,124 @@
/* eslint-disable no-throw-literal */
const KoaRouter = require('koa-router')
const router = new KoaRouter()
const fs = require('fs')
const path = require('path')
const Printer = require('../PrinterDev')
const KoaBody = require('koa-body')
const uuid = require('uuid')
router.use(async (c, n) => {
c.data = {
title: 'Printer System Setup'
}
let status = 1
try {
await n()
} catch (err) {
status = 0
if (typeof err === 'string') {
c.body = err
} else {
c.body = err.toString()
}
}
if (c.async) {
c.body = {
status,
msg: c.body
}
}
})
router
.get('/', async (c, n) => {
c.redirect('/install')
})
.get('/install', async (c, n) => {
// get ttys
let ttys = await new Promise((resolve, reject) => {
fs.readdir(path.resolve('/dev'), (err, list) => {
if (err) return resolve([])
let arr = []
for (let it of list) {
if (/^tty[A-z]/.test(it)) {
arr.push(path.resolve('/dev', it))
}
}
resolve(arr)
})
})
c.data.ttys = ttys
await c.render('install/index', c.data)
})
.post('/install/printer_test', KoaBody(), async (c, n) => {
c.async = true
let arr = c.request.body
if (!arr.device) throw '請輸入印表機連接埠'
let device = arr.device
let feed = arr.feed || 8
if (!isFinite(feed) || feed < 0) throw '切紙前換行必須是整數'
feed = typeof feed !== 'number' ? Math.floor(parseInt(feed)) : feed
Printer.serial = device
Printer.feed = feed
let status = await Printer.printTestPage()
if (!status) c.body = '測試失敗'
else c.body = '測試成功'
})
.post('/install/write_config', KoaBody(), async (c, n) => {
c.async = true
let arr = c.request.body
if (!arr.tty) throw '請輸入印表機連接埠'
if (!arr.ble || (arr.ble !== 'yes' && arr.ble !== 'no')) throw '請輸入藍牙啟用設定'
let feed = arr.feed || 8
if (!isFinite(feed) || feed < 0) throw '切紙前換行必須是整數'
feed = typeof feed !== 'number' ? Math.floor(parseInt(feed)) : feed
let json = {
ble: {
enable: false,
uuid: {
main: '',
service: '',
characteristic: ''
}
},
printer: {
serial: '',
feed: 8
},
api: {
secret: ''
}
}
json.ble.enable = arr.ble === 'yes'
json.ble.uuid.main = uuid.v4()
json.ble.uuid.service = uuid.v4()
json.ble.uuid.characteristic = uuid.v4()
json.printer.serial = arr.tty
json.printer.feed = feed
json.api.secret = arr.secret || ''
let wconfig = await new Promise((resolve, reject) => {
fs.writeFile(path.resolve(process.env.PROJECT_ROOT, 'config.json'), JSON.stringify(json, null, 2), {
mode: 0o664,
encoding: 'utf8',
flag: 'w'
}, err => {
if (err) return resolve(false)
return resolve(true)
})
})
if (!wconfig) throw '設定檔寫入失敗'
c.body = '設定檔寫入成功'
})
module.exports = router

164
server.js Normal file
View File

@ -0,0 +1,164 @@
const Koa = require('koa')
const fs = require('fs')
const path = require('path')
const localEvent = require('./localEvent')
const WebSocket = require('ws')
const exec = require('child_process').exec
const Printer = require('./PrinterDev')
// koa middleware
const KoaLogger = require('koa-morgan')
const KoaEjs = require('koa-ejs')
const KoaMount = require('koa-mount')
const KoaStatic = require('koa-static')
const KoaRouter = require('koa-router')
const Kcors = require('kcors')
const app = new Koa()
let config = {}
let setupMode = false
try {
let chk = new Promise((resolve, reject) => {
fs.access(path.resolve(process.env.PROJECT_ROOT, 'config.json'), (err) => {
if (err) {
setupMode = true
resolve(0)
return
}
config = require('./config.json')
resolve(1)
})
})
await chk //eslint-disable-line
} catch (err) {
setupMode = true
}
const server = app.listen(config.port || 10230, () => {
console.log(`Server start on port ${server.address().port}`)
})
const ws = new WebSocket.Server({ server })
app.use(Kcors())
app.use(KoaLogger('short'))
app.use(KoaStatic(path.resolve(__dirname, 'public')))
KoaEjs(app, {
root: path.resolve(__dirname, 'views'),
layout: false,
viewExt: 'ejs',
cache: false,
debug: false
})
let router = null
if (setupMode) {
console.log(`start setup mode`)
router = require('./route/install')
} else {
console.log(`start normal mode`)
router = require('./route/dashboard')
Printer.serial = config.printer.serial
Printer.feed = config.printer.feed
await Printer.connect()
if (config.ble.enable) {
require('./ble')
}
}
if (router !== null) {
// set Route
app.use(router.routes())
app.use(router.allowedMethods())
router.put('/reboot_sys', async (c, n) => {
setTimeout(function () {
process.exit(1)
}, 2000)
c.body = '1'
})
}
// listen print event
localEvent.on('print', async str => {
let chk = await Printer.printerString(str)
localEvent.emit('printResult', chk)
})
let systemStatus = {
ble: {
enable: false,
mac: '',
service: '',
characteristic: ''
},
printer: {
connect: false,
serial: '',
feed: 0
},
pcount: 0,
lastprint: '',
secret: ''
}
systemStatus.ble.enable = config.ble.enable
systemStatus.secret = config.api.secret
if (config.ble.enable) {
systemStatus.ble.mac = await getBTAddr()
systemStatus.ble.service = config.ble.uuid.service
systemStatus.ble.characteristic = config.ble.uuid.characteristic
}
function getPrinterStatus() {
systemStatus.printer.connect = Printer.isOpen
systemStatus.printer.serial = Printer.serial
systemStatus.printer.feed = Printer.feed
systemStatus.pcount = Printer.count
systemStatus.lastprint = Printer.lastPrint
}
getPrinterStatus()
ws.on('connection', async (client, req) => {
getPrinterStatus()
client.send(JSON.stringify({ type: 'status', data: systemStatus }))
Printer.on('printDone', () => {
getPrinterStatus()
client.send(JSON.stringify({ type: 'status', data: systemStatus }))
})
client.on('message', msg => {
let m = {}
try {
m = JSON.parse(msg)
} catch (err) {
return
}
switch (m.type) {
case 'status':
client.send(JSON.stringify({ type: 'status', data: systemStatus }))
break
}
})
})
async function getBTAddr() {
let address = await new Promise((resolve, reject) => {
exec('bt-adapter -i | grep -i address', (err, sout, serr) => {
if (err) return resolve('')
resolve(sout)
})
})
address = address.trim()
let arr = address.split(' ')
if (arr.length !== 2) return ''
return arr[1].trim()
}

View File

@ -1,24 +0,0 @@
const bleno = require('bleno')
const config = require('./config.json')
class TimeCharacteristic extends bleno.Characteristic {
constructor() {
super({
uuid: config.uuid.service.func.time,
properties: ['read'],
descriptors: [
new bleno.Descriptor({
uuid: '88ff',
value: 'Now Time'
})
]
})
}
onReadRequest(offset, callback) {
console.log(`offset :: ${offset}`)
callback(this.RESULT_SUCCESS, new Buffer(`${Date.now()}`))
}
}
module.exports = TimeCharacteristic

28
views/dashboard/index.ejs Normal file
View File

@ -0,0 +1,28 @@
<%- include ../includes/header.ejs %>
<div class="ui container" style="padding-top: 20px;">
<div class="ui grid">
<div class="row">
<div class="three wide column">
<div class="ui vertical menu">
<a href="/" class="item">Dashboard</a>
<div class="item">
<div class="header">系統功能</div>
<div class="menu">
<a href="/setting" class="item <%= route == 'setting' ? 'active' : '' %>">修改設定</a>
</div>
</div>
</div>
</div>
<div class="thirteen wide column">
<% if(route == 'index'){ %>
<%- include statusview.ejs %>
<% } else { %>
<%- include setting.ejs %>
<% } %>
</div>
</div>
</div>
</div>
<%- include ../includes/footer.ejs %>

131
views/dashboard/setting.ejs Normal file
View File

@ -0,0 +1,131 @@
<div class="ui container" style="padding-top: 20px;">
<h1 class="ui header">系統安裝設定</h1>
<form id="setup" class="ui form">
<h3 class="ui dividing header">印表機設定</h3>
<div class="field">
<label for="tty">印表機連接埠</label>
<select name="tty" id="tty">
<option value="">選擇裝置</option>
<% for(let i of ttys) { %>
<option value="<%= i %>" <%= i == defConfig.printer.serial ? 'selected' : '' %>><%= i %></option>
<% } %>
</select>
</div>
<div class="field">
<label for="feed">切紙前空行</label>
<input type="text" name="feed" value="<%= defConfig.printer.feed %>" id="feed">
</div>
<div class="field">
<button class="ui button mini" type="button" id="test-printer">測試印表機</button>
</div>
<h3 class="ui dividing header">藍芽列印設定</h3>
<div class="field">
<div class="ui checkbox">
<input type="checkbox" class="hidden" id="en-ble" <%= defConfig.ble.enable ? 'checked' : '' %>>
<label for="en-ble">啟用藍芽</label>
</div>
</div>
<h3 class="ui dividing header">WebAPI設定</h3>
<div class="field">
<label for="api-secret">API密鑰</label>
<input type="text" name="api-secret" value="<%= defConfig.api.secret %>" placeholder="請輸入密鑰,空白不設定" id="api-secret">
</div>
<div style="margin-top: 40px; text-align: right;">
<button class="ui button blue" type="submit">送出設定</button>
</div>
</form>
</div>
<div class="ui dimmer" id="loader">
<div class="ui loader text">Rebooting...</div>
</div>
<script>
function getValue(obj){
if(!obj || !('value' in obj)) return ''
return obj.value
}
$('#test-printer').click(function(){
let json = {
device: '',
feed: 0
}
let dev = getValue(document.querySelector('select#tty'))
let feed = getValue(document.querySelector('input#feed'))
if(!dev) return alert('請選擇印表機連接埠')
if (!isFinite(feed) || feed < 0) return alert('切紙前空行請輸入整數')
json.device = dev
json.feed = Math.floor(parseInt(feed))
$.ajax({
url: '/api/printer_test',
type: 'post',
data: json,
success: function(e){
alert(e.msg || '資料傳送失敗')
}
})
})
$('form#setup').submit(function(e) {
e.preventDefault()
let json = {
tty: '',
feed: 0,
ble: '',
secret: ''
}
let dev = getValue(document.querySelector('select#tty'))
let feed = getValue(document.querySelector('input#feed'))
let elBle = document.querySelector('input#en-ble')
let ble = ''
if(elBle != null && 'checked' in elBle) ble = elBle.checked ? 'yes' : 'no'
if(!dev) return alert('請選擇印表機連接埠')
if (!isFinite(feed) || feed < 0) return alert('切紙前空行請輸入整數')
let secret = getValue(document.querySelector('input#api-secret'))
json.tty = dev
json.feed = Math.floor(parseInt(feed))
json.ble = ble
json.secret = secret
$.ajax({
url: '/api/write_config',
type: 'post',
data: json,
success: function(e){
if(e.status != 1) {
alert(e.msg || '寫入失敗')
return
}
$.ajax({
url: '/reboot_sys',
type: 'put',
success: function(){
setTimeout(function(){
location.replace('/')
}, 30000)
$('#loader').addClass('active')
},
error: function(){
alert('系統重新啟動失敗')
}
})
}
})
return false
})
</script>

View File

@ -0,0 +1,152 @@
<div class="ui container">
<div class="ui grid">
<div class="column sixteen wide t-right">
<div class="ui segment">
<button class="ui button mini" type="button" id="gen-api-qr">Generate API QR</button>
<button class="ui button mini" type="button" id="gen-ble-qr">Generate BLE QR</button>
</div>
</div>
<div class="column sixteen wide">
<div class="segment ui">
<h4 class="ui header dividing">系統狀態</h4>
<div class="ui divided list">
<div class="item">
<div class="ui horizontal label basic">API Key</div>
<div class="description t-right" id="apikey"></div>
</div>
</div>
</div>
</div>
<div class="column eight wide">
<div class="segment ui">
<h4 class="ui header dividing">藍芽狀態</h4>
<div class="ui divided list">
<div class="item">
<div class="ui horizontal label basic">啟用狀態</div>
<div class="description t-right" id="en-ble">On</div>
</div>
<div class="item">
<div class="ui horizontal label basic">Mac Address</div>
<div class="description t-right" id="ble-mac">00:11:22:33:44:55:66</div>
</div>
<div class="item">
<div class="ui horizontal label basic">ServiceUUID</div>
<div class="description t-right" id="ble-service">37B2975E-1A4B-4975-9211-9D47142B8183</div>
</div>
<div class="item">
<div class="ui horizontal label basic">CharacteristicUUID</div>
<div class="description t-right" id="ble-character">37B2975E-1A4B-4975-9211-9D47142B8183</div>
</div>
</div>
</div>
</div>
<div class="column eight wide">
<div class="segment ui">
<h4 class="ui header dividing">印表機狀態</h4>
<div class="ui divided list">
<div class="item">
<div class="ui horizontal label basic">連線狀態</div>
<div class="description t-right" id="prt-con">Disconnected</div>
</div>
<div class="item">
<div class="ui horizontal label basic">連接埠</div>
<div class="description t-right" id="prt-port">/dev/ttyUSB0</div>
</div>
<div class="item">
<div class="ui horizontal label basic">切紙前空行</div>
<div class="description t-right" id="prt-feed">8</div>
</div>
</div>
</div>
</div>
<div class="column eight wide">
<div class="segment ui">
<h4 class="ui header dividing">列印計數器</h4>
<div class="ui divided list">
<div class="item">
<div class="ui horizontal label basic">列印次數</div>
<div class="description t-right" id="prt-count">0</div>
</div>
</div>
</div>
</div>
<div class="column eight wide">
<div class="segment ui">
<h4 class="ui header dividing">最後列印內容</h4>
<div id="prt-last"></div>
</div>
</div>
</div>
</div>
<div class="ui page dimmer" id="qr-dimmer">
<div class="content">
<div class="center" id="qr"></div>
</div>
</div>
<script>
var setting = {
mac: '',
service: '',
characteristic: ''
}
var apikey = ''
var ws = new WebSocket('ws://' + location.host)
ws.onmessage = msg => {
// console.log(msg)
let data = {}
try {
data = JSON.parse(msg.data)
} catch (err) {
return
}
switch (data.type) {
case 'status':
console.log(data)
let d = data.data
$('#en-ble').text(d.ble.enable ? 'On' : 'Off')
$('#ble-mac').text(d.ble.mac)
$('#ble-service').text(d.ble.service)
$('#ble-character').text(d.ble.characteristic)
setting.mac = d.ble.mac
setting.service = d.ble.service
setting.characteristic = d.ble.characteristic
apikey = d.secret
$('#apikey').text(apikey)
$('#prt-con').text(d.printer.connect ? 'Connected' : 'Disconnected')
$('#prt-port').text(d.printer.serial)
$('#prt-feed').text(d.printer.feed)
$('#prt-count').text(d.pcount)
$('#prt-last').text(d.lastprint)
break
}
}
$('#gen-ble-qr, #gen-api-qr').click(function () {
let qrdiv = document.querySelector('div#qr')
qrdiv.innerHTML = ''
let qr = new QRCode(qrdiv, {
text: this.id === 'gen-ble-qr' ? JSON.stringify(setting, null, 2) : JSON.stringify({apikey}, null, 2),
colorDark: "#000000",
colorLight: "#ffffff",
correctLevel: QRCode.CorrectLevel.L
})
// qr.makeCode(JSON.stringify(setting, null, 2))
$('#qr-dimmer').dimmer('show')
})
</script>

View File

@ -0,0 +1,2 @@
</body>
</html>

14
views/includes/header.ejs Normal file
View File

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title><%- title %></title>
<link rel="stylesheet" href="/css/semantic.min.css">
<link rel="stylesheet" href="/css/main.css">
<script src="/js/jquery-3.2.1.min.js"></script>
<script src="/js/semantic.min.js"></script>
<script src="/js/qrcode.min.js"></script>
</head>
<body>

134
views/install/index.ejs Normal file
View File

@ -0,0 +1,134 @@
<%- include ../includes/header.ejs %>
<div class="ui container" style="padding-top: 20px;">
<h1 class="ui header">系統安裝設定</h1>
<form id="setup" class="ui form">
<h3 class="ui dividing header">印表機設定</h3>
<div class="field">
<label for="tty">印表機連接埠</label>
<select name="tty" id="tty">
<option value="">選擇裝置</option>
<% for(let i of ttys) { %>
<option value="<%= i %>"><%= i %></option>
<% } %>
</select>
</div>
<div class="field">
<label for="feed">切紙前空行</label>
<input type="text" name="feed" value="8" id="feed">
</div>
<div class="field">
<button class="ui button mini" type="button" id="test-printer">測試印表機</button>
</div>
<h3 class="ui dividing header">藍芽列印設定</h3>
<div class="field">
<div class="ui checkbox">
<input type="checkbox" class="hidden" id="en-ble">
<label for="en-ble">啟用藍芽</label>
</div>
</div>
<h3 class="ui dividing header">WebAPI設定</h3>
<div class="field">
<label for="api-secret">API密鑰</label>
<input type="text" name="api-secret" placeholder="請輸入密鑰,空白不設定" id="api-secret">
</div>
<div style="margin-top: 40px; text-align: right;">
<button class="ui button blue" type="submit">送出設定</button>
</div>
</form>
</div>
<div class="ui dimmer" id="loader">
<div class="ui loader text">Rebooting...</div>
</div>
<script>
function getValue(obj){
if(!obj || !('value' in obj)) return ''
return obj.value
}
$('#test-printer').click(function(){
let json = {
device: '',
feed: 0
}
let dev = getValue(document.querySelector('select#tty'))
let feed = getValue(document.querySelector('input#feed'))
if(!dev) return alert('請選擇印表機連接埠')
if (!isFinite(feed) || feed < 0) return alert('切紙前空行請輸入整數')
json.device = dev
json.feed = Math.floor(parseInt(feed))
$.ajax({
url: '/install/printer_test',
type: 'post',
data: json,
success: function(e){
alert(e.msg || '資料傳送失敗')
}
})
})
$('form#setup').submit(function(e) {
e.preventDefault()
let json = {
tty: '',
feed: 0,
ble: '',
secret: ''
}
let dev = getValue(document.querySelector('select#tty'))
let feed = getValue(document.querySelector('input#feed'))
let elBle = document.querySelector('input#en-ble')
let ble = ''
if(elBle != null && 'checked' in elBle) ble = elBle.checked ? 'yes' : 'no'
if(!dev) return alert('請選擇印表機連接埠')
if (!isFinite(feed) || feed < 0) return alert('切紙前空行請輸入整數')
let secret = getValue(document.querySelector('input#api-secret'))
json.tty = dev
json.feed = Math.floor(parseInt(feed))
json.ble = ble
json.secret = secret
$.ajax({
url: '/install/write_config',
type: 'post',
data: json,
success: function(e){
if(e.status != 1) {
alert(e.msg || '寫入失敗')
return
}
$.ajax({
url: '/reboot_sys',
type: 'put',
success: function(){
setTimeout(function(){
location.replace('/')
}, 30000)
$('#loader').addClass('active')
},
error: function(){
alert('系統重新啟動失敗')
}
})
}
})
return false
})
</script>
<%- include ../includes/footer.ejs %>