From ae90a8556b45e6bbcecd7e5d593fa51b5a614786 Mon Sep 17 00:00:00 2001 From: Matias Vallejos Date: Tue, 23 Apr 2024 08:26:14 -0300 Subject: [PATCH] feat: :rocket: Preparing deploy, bug fixes. --- .eslintignore | 1 + .github/workflows/release.yml | 6 - README.md | 13 +- forge.config.ts | 1 + package.json | 1 + pages/Home/index.html | 125 --------------- pages/Home/script.ts | 207 ------------------------- pages/Scenes/index.html | 230 ---------------------------- src/bulbHelper.ts | 28 +++- src/components/Custom.tsx | 6 +- src/components/Devices.tsx | 2 +- src/components/Home.tsx | 2 +- src/components/ModalCreateColor.tsx | 1 - src/constants.ts | 2 +- src/index.ts | 39 +++-- src/react-app-env.d.ts | 7 +- src/tray.ts | 5 +- 17 files changed, 73 insertions(+), 603 deletions(-) create mode 100644 .eslintignore delete mode 100644 pages/Home/index.html delete mode 100644 pages/Home/script.ts delete mode 100644 pages/Scenes/index.html 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 - - -
- - - -
-
-
-
- Lights -
-
-
- - -
-
- - -
- - 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 = ` - ${res.name || res.moduleName} -
- - -
- `; - - 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? - -

-
-
`; -} - -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 - - - - - -
- - - -
-
-
- Scenes -
- -
-
-
-

Whites

- -
-
-

Functional

- -
-
-
-
- - - - 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' },