Skip to content

Commit 3f22261

Browse files
authored
Merge pull request #59 from JanLunge/autosetup
Fully Automatic Setup with no Keyboard knowledge required
2 parents e6b9c55 + a927d91 commit 3f22261

25 files changed

+1763
-355
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
"daisyui": "^3.9.2",
4444
"dayjs": "^1.11.7",
4545
"decompress": "^4.2.1",
46+
"drivelist": "^12.0.2",
4647
"electron-updater": "^5.3.0",
4748
"ethers": "^5",
4849
"lighten-darken-color": "^1.0.0",

src/main/index.ts

Lines changed: 154 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
1-
import { app, shell, BrowserWindow, ipcMain, Menu, SerialPort } from 'electron'
1+
import { app, shell, BrowserWindow, ipcMain, Menu } from 'electron'
22
import { join } from 'path'
33
import { electronApp, optimizer, is } from '@electron-toolkit/utils'
44
import icon from '../../resources/icon.png?asset'
5+
import drivelist from 'drivelist'
6+
import { flashFirmware } from './kmkUpdater'
7+
// import './saveConfig'
58
import { checkForUSBKeyboards, handleSelectDrive, selectKeyboard } from './selectKeyboard'
69
import { updateFirmware } from './kmkUpdater'
7-
import { saveConfiguration } from './saveConfig'
8-
import { autoUpdater } from 'electron-updater'
10+
import './keyboardDetector' // Import keyboard detector
911
import serialPort from 'serialport'
1012
import { ReadlineParser } from '@serialport/parser-readline'
11-
import { log } from 'util'
12-
13+
import fs from 'fs'
14+
import { currentKeyboard } from './store'
15+
import { saveConfiguration } from './saveConfig'
1316
let mainWindow: BrowserWindow | null = null
1417
export { mainWindow }
1518

@@ -24,12 +27,12 @@ const template: unknown = [
2427
label: app.name,
2528
submenu: [
2629
{ role: 'about' },
27-
{
28-
label: 'Check for Updates...',
29-
click: () => {
30-
autoUpdater.checkForUpdatesAndNotify()
31-
}
32-
},
30+
// {
31+
// label: 'Check for Updates...',
32+
// click: () => {
33+
// autoUpdater.checkForUpdates()
34+
// }
35+
// },
3336
{ type: 'separator' },
3437
{ role: 'services' },
3538
{ type: 'separator' },
@@ -117,23 +120,23 @@ Menu.setApplicationMenu(menu)
117120
const createWindow = async () => {
118121
// Create the browser window.
119122
mainWindow = new BrowserWindow({
120-
width: 1100,
121-
height: 800,
122-
minWidth: 700,
123-
minHeight: 200,
123+
width: 900,
124+
height: 670,
124125
show: false,
125-
// autoHideMenuBar: true,
126+
autoHideMenuBar: true,
126127
backgroundColor: '#000000',
127128
...(process.platform === 'linux' ? { icon } : {}),
128129
webPreferences: {
129130
preload: join(__dirname, '../preload/index.js'),
130131
sandbox: false,
132+
nodeIntegration: true,
133+
contextIsolation: true,
131134
experimentalFeatures: true
132135
}
133136
})
134137

135138
mainWindow.on('ready-to-show', () => {
136-
if (mainWindow) mainWindow.show()
139+
mainWindow?.show()
137140
})
138141

139142
mainWindow.webContents.setWindowOpenHandler((details) => {
@@ -153,9 +156,9 @@ const createWindow = async () => {
153156
// This method will be called when Electron has finished
154157
// initialization and is ready to create browser windows.
155158
// Some APIs can only be used after this event occurs.
156-
app.whenReady().then(async () => {
159+
app.whenReady().then(() => {
157160
// Set app user model id for windows
158-
electronApp.setAppUserModelId('de.heaper.pog')
161+
electronApp.setAppUserModelId('de.janlunge.pog')
159162

160163
// Default open or close DevTools by F12 in development
161164
// and ignore CommandOrControl + R in production.
@@ -219,12 +222,12 @@ ipcMain.on('serialSend', (_event, data) => sendSerial(data))
219222
ipcMain.handle('serialConnect', (_event, data) => serialConnect(data))
220223
ipcMain.handle('openExternal', (_event, data) => openExternal(data))
221224

222-
autoUpdater.on('update-available', () => {
223-
if (mainWindow) mainWindow.webContents.send('update_available')
224-
})
225-
autoUpdater.on('update-downloaded', () => {
226-
if (mainWindow) mainWindow.webContents.send('update_downloaded')
227-
})
225+
// autoUpdater.on('update-available', () => {
226+
// if (mainWindow) mainWindow.webContents.send('update_available')
227+
// })
228+
// autoUpdater.on('update-downloaded', () => {
229+
// if (mainWindow) mainWindow.webContents.send('update_downloaded')
230+
// })
228231

229232
const baudRate = 9600
230233
const startTime = new Date()
@@ -489,59 +492,55 @@ const closeDebugPort = () => {
489492

490493
const serialConnect = async (port) => {
491494
console.log('connecting to serial port', port)
492-
495+
493496
try {
494497
// First ensure any existing connection is properly closed
495498
await closeDebugPort()
496-
499+
497500
// Create a promise that will reject after timeout
498501
const timeoutPromise = new Promise((_, reject) => {
499502
setTimeout(() => reject(new Error('Connection timeout')), CONNECTION_TIMEOUT)
500503
})
501-
504+
502505
// Create the connection promise
503506
const connectionPromise = new Promise((resolve, reject) => {
504507
try {
505-
debugPort = new serialPort.SerialPort(
506-
{ path: port, baudRate, autoOpen: true },
507-
(err) => {
508-
if (err) {
509-
reject(err)
510-
return
511-
}
512-
513-
const parser = debugPort.pipe(new ReadlineParser({ delimiter: '\n' }))
514-
515-
parser.on('data', (data) => {
516-
console.log('got data from serial', data)
517-
mainWindow?.webContents.send('serialData', { message: data + '\n' })
518-
})
519-
520-
debugPort.on('error', (error) => {
521-
console.error('Serial port error:', error)
522-
notifyConnectionStatus(false, error.message)
523-
closeDebugPort()
524-
})
525-
526-
debugPort.on('close', () => {
527-
console.log('Serial port closed')
528-
notifyConnectionStatus(false)
529-
})
530-
531-
resolve(true)
508+
debugPort = new serialPort.SerialPort({ path: port, baudRate, autoOpen: true }, (err) => {
509+
if (err) {
510+
reject(err)
511+
return
532512
}
533-
)
513+
514+
const parser = debugPort.pipe(new ReadlineParser({ delimiter: '\n' }))
515+
516+
parser.on('data', (data) => {
517+
console.log('got data from serial', data)
518+
mainWindow?.webContents.send('serialData', { message: data + '\n' })
519+
})
520+
521+
debugPort.on('error', (error) => {
522+
console.error('Serial port error:', error)
523+
notifyConnectionStatus(false, error.message)
524+
closeDebugPort()
525+
})
526+
527+
debugPort.on('close', () => {
528+
console.log('Serial port closed')
529+
notifyConnectionStatus(false)
530+
})
531+
532+
resolve(true)
533+
})
534534
} catch (err) {
535535
reject(err)
536536
}
537537
})
538-
538+
539539
// Wait for either connection or timeout
540540
await Promise.race([connectionPromise, timeoutPromise])
541-
541+
542542
console.log('Successfully connected to serial port')
543543
notifyConnectionStatus(true)
544-
545544
} catch (error: any) {
546545
console.error('Failed to connect:', error)
547546
await closeDebugPort()
@@ -555,10 +554,10 @@ const sendSerial = (message) => {
555554
console.error('Cannot send: port not open')
556555
return
557556
}
558-
557+
559558
console.log('sending serial', message)
560559
mainWindow?.webContents.send('serialData', { message: `> sent: ${message}\n` })
561-
560+
562561
try {
563562
let buffer
564563
if (message === 'ctrlc') {
@@ -568,7 +567,7 @@ const sendSerial = (message) => {
568567
} else {
569568
buffer = Buffer.from(message + '\r\n', 'utf8')
570569
}
571-
570+
572571
debugPort.write(buffer, (err) => {
573572
if (err) {
574573
console.error('error sending serial', err)
@@ -615,3 +614,96 @@ const checkSerialDevices = async () => {
615614
const openExternal = (url) => {
616615
shell.openExternal(url)
617616
}
617+
618+
// Drive and Firmware handlers
619+
ipcMain.handle('list-drives', async () => {
620+
try {
621+
const drives = await drivelist.list()
622+
const filteredDrives = drives
623+
.map((drive) => ({
624+
path: drive.mountpoints[0]?.path || '',
625+
name: drive.mountpoints[0]?.label || drive.description || 'Unknown Drive',
626+
isReadOnly: drive.isReadOnly,
627+
isRemovable: drive.isRemovable,
628+
isSystem: drive.isSystem,
629+
isUSB: drive.isUSB,
630+
isCard: drive.isCard
631+
}))
632+
.filter((drive) => drive.path !== '' && drive.isUSB)
633+
console.log('drives', filteredDrives)
634+
return filteredDrives
635+
} catch (error) {
636+
console.error('Failed to list drives:', error)
637+
throw error
638+
}
639+
})
640+
641+
ipcMain.handle(
642+
'flash-detection-firmware',
643+
async (_event, { drivePath, serialNumber }: { drivePath: string; serialNumber: string }) => {
644+
try {
645+
// Set the current keyboard path
646+
currentKeyboard.path = drivePath
647+
currentKeyboard.name = 'New Keyboard'
648+
currentKeyboard.id = Date.now().toString()
649+
currentKeyboard.serialNumber = serialNumber
650+
console.log('flashing detection firmware currentKeyboard', currentKeyboard)
651+
652+
// Create necessary directories if they don't exist
653+
if (!fs.existsSync(drivePath)) {
654+
throw new Error(`Drive path ${drivePath} does not exist`)
655+
}
656+
657+
// Flash the detection firmware
658+
await flashFirmware(drivePath)
659+
660+
// setup both ports to listen for detection
661+
return { success: true }
662+
} catch (error) {
663+
console.error('Failed to flash firmware:', error)
664+
throw error
665+
}
666+
}
667+
)
668+
669+
// Keyboard History handlers
670+
ipcMain.handle('list-keyboards', () => {
671+
try {
672+
return listKeyboards()
673+
} catch (error) {
674+
console.error('Failed to list keyboards:', error)
675+
throw error
676+
}
677+
})
678+
679+
// Serial Port handlers
680+
ipcMain.handle('serial-ports', async () => {
681+
try {
682+
console.log('checking serial devices')
683+
const ports = await serialPort.SerialPort.list()
684+
685+
if (ports.length === 0) {
686+
console.log('No serial ports found')
687+
return []
688+
}
689+
690+
const returnPorts = ports.map((port) => ({
691+
port: port.path,
692+
manufacturer: port.manufacturer,
693+
serialNumber: port.serialNumber
694+
}))
695+
console.log('found serial ports', returnPorts)
696+
return returnPorts
697+
} catch (error) {
698+
console.error('Error fetching the list of serial ports:', error)
699+
return []
700+
}
701+
})
702+
703+
ipcMain.handle('serial-connect', async (_event, port: string) => {
704+
return serialConnect(port)
705+
})
706+
707+
ipcMain.handle('serial-disconnect', async () => {
708+
return closeDebugPort()
709+
})

0 commit comments

Comments
 (0)