mirror of
https://github.com/sascha-hemi/homeassistant-desktop.git
synced 2026-03-21 03:04:10 +01:00
349 lines
10 KiB
JavaScript
349 lines
10 KiB
JavaScript
const {app, Menu, Tray, dialog, ipcMain, BrowserWindow, shell, screen} = require('electron');
|
|
const {autoUpdater} = require('electron-updater');
|
|
const AutoLaunch = require('auto-launch');
|
|
const Positioner = require('electron-traywindow-positioner');
|
|
const Store = require('electron-store');
|
|
|
|
app.allowRendererProcessReuse = true;
|
|
|
|
// prevent multiple instances
|
|
if (!app.requestSingleInstanceLock()) {
|
|
app.quit()
|
|
} else {
|
|
app.on('second-instance', () => {
|
|
if (window) showWindow();
|
|
})
|
|
}
|
|
|
|
// hide dock icon on macOS
|
|
if (process.platform === 'darwin') app.dock.hide();
|
|
|
|
const store = new Store();
|
|
const autoLauncher = new AutoLaunch({name: 'Home Assistant Desktop'});
|
|
|
|
const indexFile = `file://${__dirname}/web/index.html`;
|
|
let autostartEnabled = false;
|
|
let forceQuit = false;
|
|
|
|
|
|
const useAutoUpdater = () => {
|
|
autoUpdater.on('error', message => {
|
|
console.error('There was a problem updating the application');
|
|
console.error(message)
|
|
});
|
|
|
|
autoUpdater.on('update-downloaded', () => {
|
|
forceQuit = true;
|
|
autoUpdater.quitAndInstall();
|
|
});
|
|
|
|
setInterval(() => {
|
|
autoUpdater.checkForUpdates();
|
|
}, 1000 * 60 * 30);
|
|
|
|
autoUpdater.checkForUpdates();
|
|
};
|
|
|
|
const checkAutoStart = () => {
|
|
autoLauncher.isEnabled().then((isEnabled) => {
|
|
autostartEnabled = isEnabled;
|
|
}).catch((err) => {
|
|
console.error(err)
|
|
});
|
|
};
|
|
|
|
const getMenu = () => {
|
|
let instancesMenu = [{
|
|
label: 'Open in Browser',
|
|
enabled: currentInstance(),
|
|
click: () => {
|
|
shell.openExternal(currentInstance())
|
|
}
|
|
},
|
|
{
|
|
type: 'separator'
|
|
}
|
|
];
|
|
|
|
const allInstances = store.get('allInstances');
|
|
|
|
if (allInstances) {
|
|
allInstances.forEach((e) => {
|
|
instancesMenu.push({
|
|
label: e,
|
|
type: 'checkbox',
|
|
checked: currentInstance() === e,
|
|
click: () => {
|
|
currentInstance(e);
|
|
window.loadURL(e);
|
|
window.show();
|
|
}
|
|
});
|
|
});
|
|
|
|
instancesMenu.push(
|
|
{
|
|
type: 'separator'
|
|
},
|
|
{
|
|
label: 'Add another Instance...', click: () => {
|
|
store.delete('currentInstance');
|
|
window.loadURL(indexFile);
|
|
window.show();
|
|
}
|
|
}
|
|
)
|
|
} else {
|
|
instancesMenu.push({label: 'Not Connected...', enabled: false})
|
|
}
|
|
|
|
return Menu.buildFromTemplate([...instancesMenu,
|
|
{
|
|
type: 'separator'
|
|
},
|
|
{
|
|
label: 'Hover to Show',
|
|
enabled: !store.get('detachedMode'),
|
|
type: 'checkbox',
|
|
checked: !store.get('disableHover'),
|
|
click: () => {
|
|
store.set('disableHover', !store.get('disableHover'))
|
|
}
|
|
},
|
|
{
|
|
label: 'Stay on Top',
|
|
type: 'checkbox',
|
|
checked: window.isAlwaysOnTop(),
|
|
click: () => {
|
|
window.setAlwaysOnTop(!window.isAlwaysOnTop());
|
|
if (window.isAlwaysOnTop()) showWindow();
|
|
}
|
|
},
|
|
{
|
|
label: 'Start at Login',
|
|
type: 'checkbox',
|
|
checked: autostartEnabled,
|
|
click: () => {
|
|
if (autostartEnabled) autoLauncher.disable(); else autoLauncher.enable();
|
|
checkAutoStart()
|
|
}
|
|
}, {
|
|
type: 'separator'
|
|
},
|
|
{
|
|
label: 'Use detached Window',
|
|
type: 'checkbox',
|
|
checked: store.get('detachedMode'),
|
|
click: () => {
|
|
store.set('detachedMode', !store.get('detachedMode'));
|
|
window.hide();
|
|
createMainWindow(store.get('detachedMode'))
|
|
}
|
|
},
|
|
{
|
|
type: 'separator'
|
|
},
|
|
{
|
|
label: `v${app.getVersion()} (Auto Update)`,
|
|
enabled: false
|
|
},
|
|
{
|
|
label: 'Open on github.com',
|
|
click: () => {
|
|
shell.openExternal('https://github.com/mrvnklm/homeassistant-desktop')
|
|
}
|
|
},
|
|
{
|
|
type: 'separator'
|
|
},
|
|
{
|
|
label: 'Reload Window',
|
|
click: () => {
|
|
window.reload();
|
|
}
|
|
},
|
|
{
|
|
label: 'Reset Settings...',
|
|
click: () => {
|
|
dialog.showMessageBox({
|
|
message: 'Are you sure you want to reset Home Assistant Desktop?',
|
|
buttons: ['Cancel', 'OK, Reset!']
|
|
}).then((res) => {
|
|
if (res.response === 1) {
|
|
store.clear();
|
|
window.webContents.session.clearCache();
|
|
window.webContents.session.clearStorageData();
|
|
app.relaunch();
|
|
app.exit();
|
|
}
|
|
})
|
|
}
|
|
},
|
|
{
|
|
type: 'separator'
|
|
},
|
|
{
|
|
label: 'Quit',
|
|
accelerator: 'Cmd+Q',
|
|
click: () => {
|
|
forceQuit = true;
|
|
app.quit();
|
|
}
|
|
}
|
|
])
|
|
};
|
|
|
|
const createMainWindow = (show = false) => {
|
|
window = new BrowserWindow({
|
|
width: 420,
|
|
height: 420,
|
|
show: false,
|
|
skipTaskbar: true,
|
|
autoHideMenuBar: true,
|
|
frame: false,
|
|
webPreferences: {
|
|
nodeIntegration: true
|
|
}
|
|
});
|
|
|
|
window.loadURL(indexFile);
|
|
|
|
window.webContents.on('did-finish-load', function () {
|
|
window.webContents.insertCSS('::-webkit-scrollbar { display: none; } body { -webkit-user-select: none; }');
|
|
if (store.get('detachedMode')) {
|
|
window.webContents.insertCSS('body { -webkit-app-region: drag; }');
|
|
}
|
|
});
|
|
|
|
if (store.get('detachedMode')) {
|
|
if (store.has('windowPosition')) window.setSize(...store.get('windowSizeDetached')); else store.set('windowPosition', window.getPosition());
|
|
if (store.has('windowSizeDetached')) window.setPosition(...store.get('windowPosition')); else store.set('windowSizeDetached', window.getSize())
|
|
} else {
|
|
if (store.has('windowSize')) window.setSize(...store.get('windowSize')); else store.set('windowSize', window.getSize())
|
|
}
|
|
|
|
window.on('resize', () => {
|
|
if (store.get('detachedMode')) {
|
|
store.set('windowSizeDetached', window.getSize());
|
|
} else {
|
|
Positioner.position(window, tray.getBounds());
|
|
store.set('windowSize', window.getSize());
|
|
}
|
|
|
|
});
|
|
|
|
window.on('move', () => {
|
|
if (store.get('detachedMode')) {
|
|
store.set('windowPosition', window.getPosition())
|
|
}
|
|
});
|
|
|
|
window.on('close', (e) => {
|
|
if (!forceQuit) {
|
|
window.hide();
|
|
e.preventDefault()
|
|
}
|
|
});
|
|
|
|
window.on('blur', () => {
|
|
if (!store.get('detachedMode') && !window.isAlwaysOnTop()) window.hide();
|
|
});
|
|
|
|
if (show) showWindow();
|
|
};
|
|
|
|
const showWindow = () => {
|
|
if (!store.get('detachedMode')) Positioner.position(window, tray.getBounds());
|
|
if (!window.isVisible()) {
|
|
window.show();
|
|
window.focus();
|
|
}
|
|
};
|
|
|
|
const createTray = () => {
|
|
tray = new Tray(process.platform === 'win32' ? `${__dirname}/assets/IconWin.png` : `${__dirname}/assets/IconTemplate.png`);
|
|
|
|
tray.on('click', () => {
|
|
if (window.isVisible()) window.hide(); else showWindow();
|
|
});
|
|
|
|
tray.on('right-click', () => {
|
|
if (!store.get('detachedMode')) window.hide();
|
|
tray.popUpContextMenu(getMenu())
|
|
});
|
|
|
|
let timer = undefined;
|
|
|
|
tray.on('mouse-move', (e) => {
|
|
if (store.get('detachedMode') || window.isAlwaysOnTop() || store.get('disableHover')) {
|
|
return;
|
|
}
|
|
if (!window.isVisible()) {
|
|
showWindow();
|
|
}
|
|
if (timer) clearTimeout(timer);
|
|
timer = setTimeout(() => {
|
|
let mousePos = screen.getCursorScreenPoint();
|
|
let trayBounds = tray.getBounds();
|
|
if (!(mousePos.x >= trayBounds.x && mousePos.x <= trayBounds.x + trayBounds.width) || !(mousePos.y >= trayBounds.y && mousePos.y <= trayBounds.y + trayBounds.height)) {
|
|
setWindowFocusTimer()
|
|
}
|
|
}, 100);
|
|
}
|
|
)
|
|
}
|
|
;
|
|
|
|
const setWindowFocusTimer = () => {
|
|
let timer = setTimeout(() => {
|
|
let mousePos = screen.getCursorScreenPoint();
|
|
let windowPosition = window.getPosition();
|
|
let windowSize = window.getSize();
|
|
if (!(mousePos.x >= windowPosition[0] && mousePos.x <= windowPosition[0] + windowSize[0]) || !(mousePos.y >= windowPosition[1] && mousePos.y <= windowPosition[1] + windowSize[1])) {
|
|
window.hide();
|
|
} else {
|
|
setWindowFocusTimer()
|
|
}
|
|
}, 110)
|
|
};
|
|
|
|
app.on('ready', () => {
|
|
// temporary migration of instances to avoid breaking active instance
|
|
if (store.has('instance')) {
|
|
store.set('allInstances', [store.get('instance').url]);
|
|
store.set('currentInstance', 0);
|
|
store.delete('instance');
|
|
}
|
|
checkAutoStart();
|
|
useAutoUpdater();
|
|
createTray();
|
|
createMainWindow(!store.has('currentInstance'));
|
|
});
|
|
|
|
const currentInstance = (url = null) => {
|
|
if (url) {
|
|
store.set('currentInstance', store.get('allInstances').indexOf(url));
|
|
}
|
|
if (store.has('currentInstance')) {
|
|
return store.get('allInstances')[store.get('currentInstance')];
|
|
}
|
|
return false;
|
|
};
|
|
|
|
const addInstance = (url) => {
|
|
if (!store.has('allInstances')) store.set('allInstances', []);
|
|
let instances = store.get('allInstances');
|
|
if (instances.find(e => e === url)) {
|
|
return;
|
|
}
|
|
instances.push(url);
|
|
store.set('allInstances', instances);
|
|
currentInstance(url)
|
|
};
|
|
|
|
ipcMain.on('ha-instance', (event, args) => {
|
|
if (args) addInstance(args.url);
|
|
console.log(currentInstance());
|
|
if (currentInstance()) event.reply('ha-instance', currentInstance())
|
|
});
|