diff --git a/.eslintignore b/.eslintignore
new file mode 100644
index 0000000..7951405
--- /dev/null
+++ b/.eslintignore
@@ -0,0 +1 @@
+lib
\ No newline at end of file
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index c1e0a2c..334ff18 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -17,12 +17,6 @@ jobs:
with:
node-version: 20
- run: npm ci
- - name: Build API
- run: |
- cd wikari
- npm ci
- npm run build
- cd ..
- name: Publish app
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/README.md b/README.md
index b754364..3205c76 100644
--- a/README.md
+++ b/README.md
@@ -4,12 +4,11 @@ _Simple ElectronJS APP to connect and control [WiZ](https://www.wizconnected.com
## Preview 📌
-![Preview](https://i.imgur.com/d4RZz9v.png)
+![Preview](https://i.imgur.com/vs6W5wM.png)
-![Scenes](https://i.imgur.com/JySYM8y.png)
+![Scenes](https://i.imgur.com/8AsMpvc.png)
-> [!IMPORTANT]
-> This app is still in development, so it may have some bugs.
+![Custom](https://i.imgur.com/AIDfSRU.png)
## Installation 🔧
@@ -26,9 +25,6 @@ You can download the latest release from [here](https://github.com/MatiasTK/WizA
If you want to build the app yourself, you can do it by following these steps:
- Clone the repo.
-- Build [wikari](https://github.com/uditkarode/wikari)
- - `cd wikari`
- - `npm run build`
- Install the dependencies with `npm install`.
- Run `npm run make` to build the app.
- The app will be in the `out` folder.
@@ -39,8 +35,9 @@ If the app have trouble discovering your bulbs, you can enter the IP manually. Y
## Built with 🛠️
-- [ElectronJS](https://www.electronjs.org/) - Framework used.
+- [ElectronJS](https://www.electronjs.org/) - Desktop Framework used.
- [Bootstrap](https://getbootstrap.com/) - Used for the UI.
+- [React](https://react.dev/) - Web framework used.
## Responsibilities 📖
diff --git a/forge.config.ts b/forge.config.ts
index 1dcc889..e75bcf6 100644
--- a/forge.config.ts
+++ b/forge.config.ts
@@ -14,6 +14,7 @@ import { rendererConfig } from './webpack.renderer.config';
const config: ForgeConfig = {
packagerConfig: {
asar: true,
+ icon: 'src/assets/icon',
},
rebuildConfig: {},
makers: [new MakerSquirrel({}), new MakerZIP({}, ['darwin']), new MakerRpm({}), new MakerDeb({})],
diff --git a/package.json b/package.json
index b01e480..c7a7b1e 100644
--- a/package.json
+++ b/package.json
@@ -2,6 +2,7 @@
"name": "wiz_app",
"productName": "WiZApp",
"version": "2.0.0",
+ "icon": "src/assets/icon.ico",
"description": "Simple ElectronJS APP to connect and control WiZ Bulbs",
"main": ".webpack/main",
"scripts": {
diff --git a/pages/Home/index.html b/pages/Home/index.html
deleted file mode 100644
index bdd2333..0000000
--- a/pages/Home/index.html
+++ /dev/null
@@ -1,125 +0,0 @@
-
-
-
-
-
- WiZ APP
-
-
-
-
-
-
-
-
-
-
-
- Change Name
-
-
-
-
-
-
-
-
diff --git a/pages/Home/script.ts b/pages/Home/script.ts
deleted file mode 100644
index 60ed823..0000000
--- a/pages/Home/script.ts
+++ /dev/null
@@ -1,207 +0,0 @@
-import { BulbState } from '../../src/types';
-
-function switchToggleHandler() {
- const switchBtn = document.getElementById('lightSwitch') as HTMLInputElement;
- const switchBtnMain = document.getElementById('lightSwitch-main') as HTMLInputElement;
- switchBtn.addEventListener('change', () => {
- window.electronAPI.toggleBulb();
- switchBtnMain.checked = !switchBtnMain.checked;
- });
- switchBtnMain.addEventListener('change', () => {
- window.electronAPI.toggleBulb();
- switchBtn.checked = !switchBtn.checked;
- });
-}
-
-function addSwitchToggleToSidebar(res: BulbState) {
- const shortcut = document.querySelector('.shortcut');
-
- const div = document.createElement('div');
- div.innerHTML = `
-
-
-
-
-
- `;
-
- div.classList.add(
- 'd-flex',
- 'gap-2',
- 'align-items-center',
- 'justify-content-between',
- 'w-100',
- 'text-white',
- 'fw-bold',
- 'p-2'
- );
-
- shortcut.appendChild(div);
-
- const slider = document.querySelector('.form-range') as HTMLInputElement;
- slider.disabled = false;
- slider.value = String(res.dimming);
-
- slider.addEventListener('change', (e) => {
- const target = e.target as HTMLInputElement;
- window.electronAPI.setBrightness(parseInt(target.value, 10));
- });
-
- switchToggleHandler();
-}
-
-function createBulbDiv(res: BulbState) {
- document.querySelector('.edit').classList.remove('visually-hidden');
- document.querySelector('.lights').innerHTML = `
-
-
-
-
-
-
-
-
${res.name || res.moduleName}
-
-
-
-
-
-
-
-
`;
-}
-
-function createManualIpDiv() {
- document.querySelector('.lights').innerHTML = `
-
-
-
-
-
-
-
-
Searching bulb...
-
- Loading...
-
-
-
-
Can't find bulb?
- Add it manually
-
-
-
`;
-}
-
-function setUpLight() {
- window.electronAPI.bulbStateRequest();
- window.electronAPI.bulbStateResponse((res) => {
- if (res) {
- createBulbDiv(res);
- addSwitchToggleToSidebar(res);
- } else {
- createManualIpDiv();
- }
- });
-}
-
-function disableEdit(editLabel: HTMLLabelElement) {
- const input = document.querySelector('.inputEdit') as HTMLInputElement;
- const bulb = document.querySelector('.bulb');
-
- const newName = input.value;
- window.electronAPI.setBulbName(newName);
-
- const sidebarName = document.querySelector('.sidebar-nombre') as HTMLSpanElement;
- sidebarName.innerText = newName;
-
- editLabel.innerText = 'Change Name';
-
- const span = document.createElement('span');
- span.innerText = newName;
- bulb.replaceChild(span, bulb.childNodes[1]);
-}
-
-function editName() {
- let isEditActive = false;
-
- return function () {
- const editBtn = document.querySelector('.edit i');
- const editLabel = document.querySelector('.edit span') as HTMLLabelElement;
- editBtn.classList.toggle('fa-pencil');
- editBtn.classList.toggle('fa-floppy-disk');
- editLabel.innerText = 'Save Name';
-
- if (!isEditActive) {
- console.log('edit active');
- const input = document.createElement('input') as HTMLInputElement;
- input.type = 'text';
- input.classList.add('inputEdit');
- input.maxLength = 15;
-
- input.addEventListener('keyup', (e) => {
- if (e.key === 'Enter') {
- editBtn.classList.toggle('fa-pencil');
- editBtn.classList.toggle('fa-floppy-disk');
- disableEdit(editLabel);
- isEditActive = false;
- }
- });
-
- const bulb = document.querySelector('.bulb') as HTMLDivElement;
- input.value = (bulb.childNodes[1] as HTMLElement).innerText;
- bulb.replaceChild(input, bulb.childNodes[1]);
- isEditActive = true;
- } else {
- disableEdit(editLabel);
- isEditActive = false;
- }
- };
-}
-
-function setIpHandler() {
- const form = document.querySelector('.needs-validation');
- const ipInput = document.querySelector('.ipInput') as HTMLInputElement;
- document.getElementById('addModal').addEventListener('shown.bs.modal', () => {
- ipInput.focus();
- });
-
- form.addEventListener('submit', (event) => {
- if (ipInput.value.match('^(?:[0-9]{1,3}.){3}[0-9]{1,3}$')) {
- ipInput.classList.remove('is-invalid');
- ipInput.classList.add('is-valid');
- form.classList.add('was-validated');
- window.electronAPI.setIp(ipInput.value);
- } else {
- event.preventDefault();
- event.stopPropagation();
- form.classList.remove('was-validated');
- ipInput.classList.add('is-invalid');
- }
- });
-}
-
-function visitAuthorHandler() {
- document.querySelector('.redirect').addEventListener('click', () => {
- window.electronAPI.visitAuthor();
- });
-}
-
-function editNameHandler() {
- const editBtn = document.querySelector('.edit');
- const handleEdit = editName();
- editBtn.addEventListener('click', () => {
- handleEdit();
- });
-}
-
-window.addEventListener('DOMContentLoaded', () => {
- setUpLight();
- setIpHandler();
- visitAuthorHandler();
- editNameHandler();
-});
diff --git a/pages/Scenes/index.html b/pages/Scenes/index.html
deleted file mode 100644
index 234e437..0000000
--- a/pages/Scenes/index.html
+++ /dev/null
@@ -1,230 +0,0 @@
-
-
-
-
-
- WiZ APP
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/bulbHelper.ts b/src/bulbHelper.ts
index f2cd7cc..db9f08a 100644
--- a/src/bulbHelper.ts
+++ b/src/bulbHelper.ts
@@ -41,6 +41,11 @@ class BulbHelper {
data = JSON.parse(fs.readFileSync(CONFIG, 'utf-8'));
console.log('CONFIG DATA FOUND:', data);
} catch (e) {
+ data = {
+ bulbIp: undefined,
+ bulbName: undefined,
+ customColors: [],
+ };
console.warn('CONFIG DATA NOT FOUND');
}
@@ -49,24 +54,26 @@ class BulbHelper {
while (!bulbFound) {
if (data && data.bulbIp) {
- console.log('STORED BULB IP FOUND:', data.bulbIp);
const res = await discover({ addr: data.bulbIp, waitMs: 2500 });
if (res && res.length > 0) {
+ console.log('BULB FOUND:', res[0].address);
bulb = res[0];
+ data.bulbIp = res[0].address;
bulbFound = true;
}
} else {
- console.log('NO STORED BULB IP FOUND');
const res = await discover({});
if (res && res.length > 0) {
+ console.log('BULB FOUND:', res[0].address);
bulb = res[0];
+ data.bulbIp = res[0].address;
bulbFound = true;
}
}
- if (!bulbFound) {
- console.warn('NO BULB FOUND, RETRYING...');
- this.appData.bulbIp = undefined;
+ console.warn('NO BULB FOUND, RETRYING...');
+ if (!bulbFound && data) {
+ data.bulbIp = undefined;
}
}
@@ -82,8 +89,8 @@ class BulbHelper {
...configResult,
ip: bulb.address,
port: bulb.bulbPort,
- name: data.bulbName,
- customColors: data.customColors,
+ name: data && data.bulbName ? data.bulbName : configResult.moduleName,
+ customColors: data && data.customColors ? data.customColors : [],
};
this.bulb = bulb;
@@ -148,7 +155,6 @@ class BulbHelper {
}
public async setCustomColor(colorId: number) {
- console.log(colorId);
await this.bulbStateReady;
const color = this.bulbState.customColors.find((c) => c.id === colorId);
if (!color) return;
@@ -173,6 +179,12 @@ class BulbHelper {
this.appData.customColors = this.bulbState.customColors;
this.saveConfig();
}
+
+ public endConnection() {
+ if (this.bulb) {
+ this.bulb.closeConnection();
+ }
+ }
}
export default BulbHelper;
diff --git a/src/components/Custom.tsx b/src/components/Custom.tsx
index 7fc511c..04817d4 100644
--- a/src/components/Custom.tsx
+++ b/src/components/Custom.tsx
@@ -17,7 +17,7 @@ export default function Custom() {
};
const renderCustomColor = (color: BulbState['customColors'][0]) => (
-
+
diff --git a/src/constants.ts b/src/constants.ts
index 8416f4d..b0e77f0 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -1,6 +1,6 @@
import path from 'node:path';
import { app, nativeImage } from 'electron';
-const icon = require('./assets/icon.ico');
+import icon from './assets/icon.ico';
const ICON = nativeImage.createFromPath(path.join(__dirname, icon));
const CONFIG = path.join(app.getPath('userData'), 'config.json');
diff --git a/src/index.ts b/src/index.ts
index 9575f4b..63c1e5b 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -23,12 +23,12 @@ const createWindow = (): void => {
autoHideMenuBar: HIDE_MENU,
webPreferences: {
preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY,
- //devTools: false,
+ devTools: !app.isPackaged,
},
});
const bulbHelper = new BulbHelper();
- createTray(mainWindow, app);
+ createTray(mainWindow, app, bulbHelper);
ipcMain.on('bulb-state-request', (event) => {
bulbHelper.getBulbState().then((res) => {
@@ -80,15 +80,36 @@ const createWindow = (): void => {
// and load the index.html of the app.
mainWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY);
- // Open the DevTools.
- mainWindow.webContents.openDevTools();
+ mainWindow.on('close', () => {
+ bulbHelper.endConnection();
+ });
+
+ mainWindow.on('minimize', (event: Event) => {
+ event.preventDefault();
+ mainWindow.hide();
+ });
};
-// This method will be called when Electron has finished
-// initialization and is ready to create browser windows.
-// Some APIs can only be used after this event occurs.
-app.on('ready', createWindow);
-app.setName('WiZ App');
+const gotTheLock = app.requestSingleInstanceLock();
+if (!gotTheLock) {
+ app.quit();
+} else {
+ app.on('second-instance', () => {
+ const mainWindow = BrowserWindow.getAllWindows()[0];
+ if (mainWindow) {
+ if (mainWindow.isMinimized()) {
+ mainWindow.restore();
+ }
+ mainWindow.focus();
+ }
+ });
+
+ // This method will be called when Electron has finished
+ // initialization and is ready to create browser windows.
+ // Some APIs can only be used after this event occurs.
+ app.setName('WiZ App');
+ app.on('ready', createWindow);
+}
app.whenReady().then(() => {
installExtension(REACT_DEVELOPER_TOOLS)
diff --git a/src/react-app-env.d.ts b/src/react-app-env.d.ts
index a0993ed..617e679 100644
--- a/src/react-app-env.d.ts
+++ b/src/react-app-env.d.ts
@@ -1,4 +1,9 @@
declare module '*.png' {
- const value: any;
+ const value: never;
+ export = value;
+}
+
+declare module '*.ico' {
+ const value: never;
export = value;
}
diff --git a/src/tray.ts b/src/tray.ts
index fb8a853..3c18337 100644
--- a/src/tray.ts
+++ b/src/tray.ts
@@ -1,7 +1,8 @@
import { BrowserWindow, Menu, Tray } from 'electron';
import { ICON } from './constants';
+import BulbHelper from './bulbHelper';
-const createTray = (mainWindow: BrowserWindow, app: Electron.App) => {
+const createTray = (mainWindow: BrowserWindow, app: Electron.App, bulbHelper: BulbHelper) => {
const tray = new Tray(ICON);
const contextMenu = Menu.buildFromTemplate([
{
@@ -14,7 +15,7 @@ const createTray = (mainWindow: BrowserWindow, app: Electron.App) => {
{
label: 'Toggle Bulb',
click: () => {
- // TODO
+ bulbHelper.toggleBulb();
},
},
{ type: 'separator' },