Skip to content

Commit 6942985

Browse files
Initial commit: Razer Battery Widget
0 parents  commit 6942985

File tree

17 files changed

+6125
-0
lines changed

17 files changed

+6125
-0
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
node_modules/
2+
dist/
3+
*.log
4+
.DS_Store

GIT_SETUP.md

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# Git setup for Razer Battery Widget
2+
3+
Follow these steps in **Command Prompt** or **PowerShell**. Replace placeholders with your details.
4+
5+
---
6+
7+
## 1. Check if Git is installed
8+
9+
```bat
10+
git --version
11+
```
12+
13+
If you see a version (e.g. `git version 2.43.0`), Git is installed. If you get an error, install Git from https://git-scm.com/download/win and run the installer, then open a new terminal.
14+
15+
---
16+
17+
## 2. Configure your identity (one-time per PC)
18+
19+
Git needs your name and email for every commit. Set them globally:
20+
21+
```bat
22+
git config --global user.name "Your Name"
23+
git config --global user.email "your.email@example.com"
24+
```
25+
26+
Use the **same email** as your GitHub account if you plan to push to GitHub.
27+
28+
**Check that it worked:**
29+
30+
```bat
31+
git config --global user.name
32+
git config --global user.email
33+
```
34+
35+
---
36+
37+
## 3. Optional: default branch name
38+
39+
```bat
40+
git config --global init.defaultBranch main
41+
```
42+
43+
---
44+
45+
## 4. Turn this project into a Git repo and push to GitHub
46+
47+
Run these from the **project folder** (e.g. `d:\Cursos\Battery_widget`).
48+
49+
**Replace `YOUR_USERNAME`** with your GitHub username. Create the repo on GitHub first (New repository, name it e.g. `razer-battery-widget`, leave README/.gitignore/license unchecked).
50+
51+
```bat
52+
cd /d d:\Cursos\Battery_widget
53+
54+
git init
55+
git add .
56+
git status
57+
git commit -m "Initial commit: Razer Battery Widget"
58+
git branch -M main
59+
git remote add origin https://github.com/YOUR_USERNAME/razer-battery-widget.git
60+
git push -u origin main
61+
```
62+
63+
- **`git status`** — Shows what will be committed. You should see no `node_modules` or `dist` (they’re in `.gitignore`).
64+
- **GitHub login:** On first `git push`, Windows may open a browser or ask for credentials. Sign in with your GitHub account. If it asks for a password, use a **Personal Access Token** (GitHub → Settings → Developer settings → Personal access tokens → Generate new token).
65+
66+
---
67+
68+
## 5. After the first push
69+
70+
- Edit **README.md** and **package.json**: replace `YOUR_USERNAME` with your real GitHub username so the “Download” and repo links work.
71+
- Create a **Release** on GitHub and upload the installer from `dist\` so people can download it.
72+
73+
---
74+
75+
## Quick reference
76+
77+
| Task | Command |
78+
|-------------------|--------|
79+
| See status | `git status` |
80+
| Add all changes | `git add .` |
81+
| Commit | `git commit -m "Your message"` |
82+
| Push to GitHub | `git push` |
83+
| Pull from GitHub | `git pull` |

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2025 Razer Battery Widget contributors
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# Razer Battery Widget
2+
3+
A small desktop widget that shows battery level for your Razer wireless gear (headphones, keyboard, mouse) with circular indicators and a charging (lightning) icon when applicable. Styled with Razer’s dark theme and green accents.
4+
5+
## Download
6+
7+
**[→ Latest release (installer)](https://github.com/YOUR_USERNAME/razer-battery-widget/releases/latest)** — Download the `.exe` installer, run it, and you’re done. Requires [Razer Synapse 3](https://www.razer.com/synapse-3) or [Razer Synapse 4](https://www.razer.com/synapse) to be running.
8+
9+
*(Replace `YOUR_USERNAME` with your GitHub username after you create the repo and your first release.)*
10+
11+
## How it works
12+
13+
Like [razer-taskbar](https://github.com/sanraith/razer-taskbar), this app reads battery data from **Razer Synapse** log files—no USB or driver needed. You must have **Razer Synapse 3** or **Razer Synapse 4** running.
14+
15+
- **Synapse 3:** `%LOCALAPPDATA%\Razer\Synapse3\Log\Razer Synapse 3.log`
16+
- **Synapse 4:** `%LOCALAPPDATA%\Razer\RazerAppEngine\User Data\Logs\systray_systrayv2*.log`
17+
18+
## Requirements
19+
20+
- Windows (tested on 10 & 11)
21+
- Razer Synapse 3 or 4 running in the background
22+
- Node.js (for running from source or building the .exe)
23+
24+
## Run from source
25+
26+
```bash
27+
npm install
28+
npm start
29+
```
30+
31+
The widget appears as a floating window. Drag the background to move it. Close the window to minimize to the system tray; double‑click the tray icon or use **Show Widget** in the tray menu to open it again.
32+
33+
## Start with Windows
34+
35+
- Right‑click the tray icon → **Start with Windows** (checked by default).
36+
- When enabled, the widget will start automatically when you log in.
37+
38+
## Build an installer (.exe)
39+
40+
```bash
41+
npm install
42+
npm run build
43+
```
44+
45+
The installer is created in the `dist/` folder. Run it to install Razer Battery Widget; you can choose the install location and create a desktop shortcut. After installation, enable **Start with Windows** in the tray menu if you want it to launch at login.
46+
47+
## Device mapping
48+
49+
Devices are matched to slots by name. If a device isn’t recognized, it is still shown in the first available slot (headphones, then keyboard, then mouse).
50+
51+
- **Headphones:** headset, headphone, BlackShark, Kraken, Barracuda, Nari, etc.
52+
- **Keyboard:** keyboard, BlackWidow, Huntsman, Ornata, Cynosa, etc.
53+
- **Mouse:** mouse, DeathAdder, Viper, Basilisk, Mamba, Naga, Orochi, Cobra, Pro Click, etc.
54+
55+
You can extend the keywords in `renderer.js` if needed.
56+
57+
## Tray icon
58+
59+
The system tray uses `assets/tray-icon.png` (a 32×32 Razer-green battery icon). To regenerate it:
60+
61+
```bash
62+
npm run tray-icon
63+
```
64+
65+
## Publishing on GitHub
66+
67+
### 1. Create the repository and push
68+
69+
1. On GitHub, click **New repository**. Name it (e.g. `razer-battery-widget`). Do **not** add a README, .gitignore, or license.
70+
2. In your project folder, run (replace `YOUR_USERNAME` with your GitHub username):
71+
72+
```bash
73+
git init
74+
git add .
75+
git commit -m "Initial commit: Razer Battery Widget"
76+
git branch -M main
77+
git remote add origin https://github.com/YOUR_USERNAME/razer-battery-widget.git
78+
git push -u origin main
79+
```
80+
81+
### 2. Optional: repo description and topics
82+
83+
In the repo **Settings → General**, set a short description and topics (e.g. `razer`, `battery`, `electron`, `windows`, `synapse`).
84+
85+
### 3. Publish a release (so others can download the installer)
86+
87+
1. **Build the installer:**
88+
89+
```bash
90+
npm run build
91+
```
92+
93+
2. **Create a release on GitHub:**
94+
- Open your repo → **Releases****Create a new release**.
95+
- **Choose a tag:** e.g. `v1.0.0` (create new tag).
96+
- **Release title:** e.g. `v1.0.0`.
97+
- **Description:** e.g. “First release. Windows installer for Razer Battery Widget.”
98+
- **Attach the installer:** drag and drop the file from `dist/` — it’s named like **Razer Battery Widget Setup 1.0.0.exe**.
99+
- Click **Publish release**.
100+
101+
3. **Update the Download link** in this README: replace `YOUR_USERNAME` in the “Latest release” link with your GitHub username so the link points to your repo.
102+
103+
After that, the **Download** section at the top will point to your latest release and users can get the installer from there.

assets/tray-icon.png

124 Bytes
Loading

index.html

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; style-src 'self' 'unsafe-inline'" />
7+
<title>Razer Battery Widget</title>
8+
<link rel="stylesheet" href="styles.css" />
9+
</head>
10+
<body>
11+
<div class="widget">
12+
<div class="devices" id="devices"></div>
13+
</div>
14+
<script src="renderer.js"></script>
15+
</body>
16+
</html>

main.js

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
const { app, BrowserWindow, ipcMain, Tray, Menu, nativeImage } = require('electron');
2+
const path = require('path');
3+
const fs = require('fs');
4+
const { RazerWatcher } = require('./watcher/razer_watcher');
5+
6+
let mainWindow = null;
7+
let razerWatcher = null;
8+
let tray = null;
9+
let isQuitting = false;
10+
11+
const SETTINGS_PATH = path.join(app.getPath('userData'), 'settings.json');
12+
13+
function loadSettings() {
14+
try {
15+
const data = fs.readFileSync(SETTINGS_PATH, 'utf8');
16+
return { ...defaultSettings(), ...JSON.parse(data) };
17+
} catch (_) {
18+
return defaultSettings();
19+
}
20+
}
21+
22+
function defaultSettings() {
23+
return { openAtLogin: true, alwaysOnTop: true };
24+
}
25+
26+
function saveSettings(settings) {
27+
try {
28+
fs.mkdirSync(path.dirname(SETTINGS_PATH), { recursive: true });
29+
fs.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2), 'utf8');
30+
} catch (e) {
31+
console.warn('Could not save settings:', e.message);
32+
}
33+
}
34+
35+
function createWindow() {
36+
if (mainWindow && !mainWindow.isDestroyed()) {
37+
mainWindow.show();
38+
mainWindow.focus();
39+
return;
40+
}
41+
42+
const settings = loadSettings();
43+
mainWindow = new BrowserWindow({
44+
width: 440,
45+
height: 200,
46+
frame: false,
47+
transparent: true,
48+
resizable: false,
49+
alwaysOnTop: settings.alwaysOnTop !== false,
50+
skipTaskbar: true,
51+
webPreferences: {
52+
nodeIntegration: false,
53+
contextIsolation: true,
54+
preload: path.join(__dirname, 'preload.js'),
55+
},
56+
});
57+
58+
mainWindow.loadFile('index.html');
59+
mainWindow.setIgnoreMouseEvents(false);
60+
mainWindow.setAlwaysOnTop(settings.alwaysOnTop !== false);
61+
mainWindow.on('close', (e) => {
62+
if (!isQuitting) {
63+
e.preventDefault();
64+
mainWindow.hide();
65+
}
66+
});
67+
}
68+
69+
function createTray() {
70+
const iconPath = path.join(__dirname, 'assets', 'tray-icon.png');
71+
let icon = null;
72+
try {
73+
const buf = fs.readFileSync(iconPath);
74+
if (buf && buf.length > 0) {
75+
icon = nativeImage.createFromBuffer(buf);
76+
if (!icon.isEmpty() && process.platform === 'win32') {
77+
icon = icon.resize({ width: 16, height: 16 });
78+
}
79+
}
80+
} catch (_) {}
81+
if (!icon || icon.isEmpty()) icon = nativeImage.createEmpty();
82+
tray = new Tray(icon);
83+
tray.setToolTip('Razer Battery Widget');
84+
tray.on('double-click', () => createWindow());
85+
updateTrayMenu();
86+
}
87+
88+
function updateTrayMenu() {
89+
const settings = loadSettings();
90+
const menu = Menu.buildFromTemplate([
91+
{ label: 'Show Widget', type: 'normal', click: () => createWindow() },
92+
{ type: 'separator' },
93+
{
94+
label: 'Start with Windows',
95+
type: 'checkbox',
96+
checked: settings.openAtLogin,
97+
click: (item) => {
98+
const next = { ...loadSettings(), openAtLogin: item.checked };
99+
saveSettings(next);
100+
app.setLoginItemSettings({ openAtLogin: next.openAtLogin });
101+
},
102+
},
103+
{
104+
label: 'Always on top',
105+
type: 'checkbox',
106+
checked: settings.alwaysOnTop !== false,
107+
click: (item) => {
108+
const next = { ...loadSettings(), alwaysOnTop: item.checked };
109+
saveSettings(next);
110+
if (mainWindow && !mainWindow.isDestroyed()) mainWindow.setAlwaysOnTop(next.alwaysOnTop);
111+
},
112+
},
113+
{ type: 'separator' },
114+
{ label: 'Quit', type: 'normal', click: () => { isQuitting = true; app.quit(); } },
115+
]);
116+
tray.setContextMenu(menu);
117+
}
118+
119+
app.whenReady().then(() => {
120+
const settings = loadSettings();
121+
app.setLoginItemSettings({ openAtLogin: settings.openAtLogin });
122+
123+
razerWatcher = new RazerWatcher((devices) => {
124+
if (mainWindow && !mainWindow.isDestroyed()) {
125+
const list = Array.from(devices.values()).filter((d) => d.isConnected);
126+
mainWindow.webContents.send('devices-update', list);
127+
}
128+
});
129+
razerWatcher.initialize();
130+
razerWatcher.start();
131+
132+
createTray();
133+
createWindow();
134+
});
135+
136+
app.on('window-all-closed', (e) => {
137+
if (!isQuitting) {
138+
e.preventDefault();
139+
if (mainWindow && !mainWindow.isDestroyed()) mainWindow.hide();
140+
}
141+
});
142+
143+
app.on('activate', () => {
144+
createWindow();
145+
});
146+
147+
app.on('before-quit', () => {
148+
isQuitting = true;
149+
if (tray) try { tray.destroy(); tray = null; } catch (_) {}
150+
if (razerWatcher) razerWatcher.stop();
151+
razerWatcher = null;
152+
if (mainWindow && !mainWindow.isDestroyed()) mainWindow.destroy();
153+
mainWindow = null;
154+
});
155+
156+
ipcMain.handle('get-devices', () => (razerWatcher ? razerWatcher.listDevices() : []));
157+
ipcMain.handle('get-settings', () => loadSettings());
158+
ipcMain.handle('set-open-at-login', (_, value) => {
159+
const settings = loadSettings();
160+
settings.openAtLogin = !!value;
161+
saveSettings(settings);
162+
app.setLoginItemSettings({ openAtLogin: settings.openAtLogin });
163+
});

0 commit comments

Comments
 (0)