Skip to content

Commit adecb0b

Browse files
author
Andy
committed
Merge develop into main for v0.4.1 release
2 parents d53e116 + b3d96a9 commit adecb0b

18 files changed

Lines changed: 293 additions & 72 deletions

File tree

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ Open Wemo consists of two parts that work together:
8181
| **Auto-Refresh** | Device states update automatically (configurable interval) |
8282
| **Timer Schedules** | Schedule devices to turn on or off at specific times, daily or per day of week |
8383
| **Standby Threshold** | Configure the power level at which Insight devices are considered in standby (via device config panel) |
84-
| **LED Mode** | Prevent Insight devices from auto-shutting off low-power devices like LED lights (via device config panel) |
84+
| **LED Mode** | Prevent Insight devices from auto-shutting off low-power devices like LED lights while keeping manual off behavior intact |
8585
| **QR Code Setup** | Scan a code to instantly set up the app on any phone |
8686
| **Cross-Platform** | Bridge runs on Windows, macOS, and Linux |
8787
| **No Cloud Required** | Everything stays on your local network — private by design |
@@ -254,7 +254,7 @@ For Insight devices, you can adjust power monitoring settings:
254254

255255
This is useful if your device draws a small amount of power when "off" (like a TV in standby mode) and you want to fine-tune when it shows as "Standby" vs. "Off".
256256

257-
- **LED Mode** — toggle to keep low-power devices (like LED lights) from being automatically shut off by the Insight's standby detection. The bridge sends a periodic heartbeat to prevent the firmware timeout. This respects manual off commands — if you turn the device off, the keep-alive pauses.
257+
- **LED Mode** — toggle to keep low-power devices (like LED lights) from being automatically shut off by the Insight firmware. While enabled, Open Wemo keeps the device in normal "On" mode, hides the standby-threshold control, and only applies protection after you turn the device on yourself. If you turn the device off, it stays off.
258258

259259
### Header Actions
260260

@@ -356,7 +356,7 @@ chmod +x ~/.cache/node-systray/*/tray_linux_release
356356

357357
- **Give it time**: Insight devices average power over time; readings stabilize after a few minutes
358358
- **Standby mode**: Very low power (< 1 watt) shows as "Standby" state
359-
- **Adjust standby threshold**: Tap the gear icon on the device card to customize the wattage at which the device is considered "Standby"
359+
- **Adjust standby threshold**: Tap the gear icon on the device card to customize the wattage at which the device is considered "Standby" when LED Mode is off
360360
- **Device firmware**: Older WeMo firmware may report slightly different formats
361361

362362
---
@@ -571,7 +571,7 @@ A: Nope! Just install the bridge and it will automatically discover your existin
571571
A: Yes! Tap the timer icon on any device card to create schedules. Set a time, choose on or off, and select which days of the week. Timers run on the bridge, so they're reliable and consistent as long as the bridge is running.
572572

573573
**Q: What is the standby threshold?**
574-
A: For Insight devices, the standby threshold is the power level (in watts) below which the device shows as "Standby" instead of "Off". This is useful for devices that draw a small amount of power when turned off (like TVs or chargers). You can adjust it by tapping the gear icon on an Insight device card. The range is 0-50 watts.
574+
A: For Insight devices, the standby threshold is the power level (in watts) below which the device shows as "Standby" instead of "Off". This is useful for devices that draw a small amount of power when turned off (like TVs or chargers). You can adjust it by tapping the gear icon on an Insight device card when LED Mode is off. The range is 0-50 watts.
575575

576576
**Q: My Insight device keeps turning off my LED light. What can I do?**
577-
A: Enable "LED Mode" in the device's configuration panel (tap the gear icon). This sends a periodic heartbeat to prevent the Insight's firmware from auto-shutting off low-power devices that draw too little power for the sensor to detect. LED Mode respects manual off commands — if you turn the device off yourself, it stays off.
577+
A: Enable "LED Mode" in the device's configuration panel (tap the gear icon). Open Wemo lowers the Insight auto-off threshold, keeps the device reporting as "On" instead of "Standby," and sends a periodic keep-alive only after you turn the device on yourself. If you turn the device off yourself, it stays off.

docs/API.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -709,7 +709,7 @@ Gets the keep-alive (LED Mode) status for a device.
709709
PUT /api/devices/:id/keepalive
710710
```
711711

712-
Enables or disables keep-alive (LED Mode) for a device. When enabled, the bridge sends a periodic heartbeat to prevent the Insight firmware from auto-shutting off low-power devices.
712+
Enables or disables keep-alive (LED Mode) for a device. When enabled, Open Wemo also adjusts the Insight auto-off and display thresholds so low-power loads stay in normal "On" mode instead of falling into standby behavior. The keep-alive only protects devices after the user has turned them on; enabling LED Mode does not turn an intentionally off device back on.
713713

714714
**Request Body:**
715715
```json

docs/RELEASING.md

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
# Release Process
2+
3+
This checklist is the release runbook for Open Wemo.
4+
5+
The current repo workflow is:
6+
7+
- day-to-day work lands on `develop`
8+
- release candidates are merged to `main`
9+
- CI runs on `main` / `master`
10+
- a pushed tag matching `v*.*.*` triggers the GitHub release workflow
11+
- GitHub release notes are generated from commit subjects between the previous tag and the new tag
12+
13+
## Release Checklist
14+
15+
### 1. Stabilize the release branch
16+
17+
- [ ] Start from `develop`
18+
- [ ] Pull the latest branch state
19+
- [ ] Confirm the working tree is clean
20+
- [ ] Review the commits that will go into the release
21+
- [ ] Clean up noisy commit history before release if needed (squash fixups, avoid vague subjects, make commit titles user-facing)
22+
23+
```bash
24+
git checkout develop
25+
git pull origin develop
26+
git status
27+
git log --oneline origin/main..HEAD
28+
```
29+
30+
Notes:
31+
32+
- The release workflow uses commit subjects to build the GitHub release notes, so commit titles matter.
33+
- Good subjects: `Fix LED Mode reviving devices that are intentionally off`
34+
- Bad subjects: `wip`, `fix stuff`, `debug`, `try again`
35+
36+
### 2. Update release-facing docs
37+
38+
- [ ] Update `README.md` for any user-visible changes
39+
- [ ] Update API docs if endpoints or payloads changed (`docs/API.md`)
40+
- [ ] Update protocol or architecture docs if behavior changed significantly
41+
- [ ] Make sure contributor workflow is still accurate if the process changed (`CONTRIBUTING.md`)
42+
43+
For the current release, specifically verify the docs cover:
44+
45+
- LED Mode behavior
46+
- standby threshold behavior
47+
- any changed expectations for Insight devices
48+
49+
### 3. Bump versions consistently
50+
51+
- [ ] Choose the correct semver bump
52+
- [ ] Update version in root package
53+
- [ ] Update version in bridge package
54+
- [ ] Update version in web package
55+
- [ ] Re-check for any hard-coded version references that should match the release
56+
57+
Files to update:
58+
59+
- `package.json`
60+
- `packages/bridge/package.json`
61+
- `packages/web/package.json`
62+
63+
Versioning guide:
64+
65+
- patch: bug fixes, no breaking changes
66+
- minor: new features, backward-compatible
67+
- major: breaking changes
68+
69+
### 4. Run the full validation pass
70+
71+
- [ ] Install dependencies if needed
72+
- [ ] Run typecheck
73+
- [ ] Run lint
74+
- [ ] Run tests
75+
- [ ] Run at least a Linux release build locally
76+
- [ ] Prefer running all platform builds before tagging if practical
77+
78+
```bash
79+
bun install
80+
bun run typecheck
81+
bun run lint
82+
bun test
83+
bun run build:linux
84+
# optional but recommended before release
85+
bun run build:all
86+
```
87+
88+
### 5. Do release-candidate smoke testing
89+
90+
- [ ] Launch the local build you intend to ship
91+
- [ ] Verify the app starts cleanly
92+
- [ ] Verify the most important changed behaviors manually
93+
- [ ] Verify no obvious regressions in core flows
94+
95+
Suggested smoke-test areas:
96+
97+
- device discovery
98+
- device on/off control
99+
- Insight status reporting
100+
- LED Mode toggle behavior
101+
- standby threshold behavior
102+
- timer flow if related code changed
103+
104+
### 6. Prepare the final release commit set
105+
106+
- [ ] Commit doc updates, version bumps, and release-prep changes
107+
- [ ] Make sure the final commit set is understandable when read as release notes
108+
- [ ] Push the final `develop` branch state
109+
110+
```bash
111+
git add .
112+
git commit -m "Prepare vX.Y.Z release"
113+
git push origin develop
114+
```
115+
116+
### 7. Merge to the release branch
117+
118+
- [ ] Merge `develop` into `main`
119+
- [ ] Push `main`
120+
- [ ] Wait for CI on `main` to pass before tagging
121+
122+
```bash
123+
git checkout main
124+
git pull origin main
125+
git merge develop --no-ff -m "Merge develop for vX.Y.Z"
126+
git push origin main
127+
```
128+
129+
Current CI behavior from `.github/workflows/ci.yml`:
130+
131+
- lint/typecheck job
132+
- test job
133+
- Linux build verification job
134+
135+
### 8. Create and push the release tag
136+
137+
- [ ] Create an annotated semver tag
138+
- [ ] Push the tag
139+
- [ ] Confirm the release workflow starts
140+
141+
```bash
142+
git tag -a vX.Y.Z -m "Release vX.Y.Z"
143+
git push origin vX.Y.Z
144+
```
145+
146+
Current release trigger from `.github/workflows/release.yml`:
147+
148+
- push tags matching `v*.*.*`
149+
150+
### 9. Verify the GitHub release output
151+
152+
- [ ] Confirm the GitHub release was created
153+
- [ ] Confirm all expected binaries are attached
154+
- [ ] Confirm the release body reads well
155+
- [ ] Edit the release text manually if the generated notes need cleanup or grouping
156+
157+
Expected artifacts:
158+
159+
- Windows: `open-wemo-*-win.exe`
160+
- macOS ARM: `open-wemo-*-mac`
161+
- macOS Intel: `open-wemo-*-mac-intel`
162+
- Linux: `open-wemo-*-linux`
163+
164+
Release note source:
165+
166+
- generated from `git log PREV_TAG..HEAD --pretty=format:"- %s" --no-merges`
167+
168+
### 10. Post-release follow-through
169+
170+
- [ ] Smoke-test one downloaded release artifact from GitHub
171+
- [ ] Confirm the version shown in shipped binaries matches the tag
172+
- [ ] Open follow-up issues for anything intentionally deferred
173+
- [ ] Sync your local branches back to the normal development flow
174+
175+
```bash
176+
git checkout develop
177+
git pull origin develop
178+
```
179+
180+
## Quick Command Checklist
181+
182+
```bash
183+
git checkout develop
184+
git pull origin develop
185+
git status
186+
git log --oneline origin/main..HEAD
187+
188+
bun install
189+
bun run typecheck
190+
bun run lint
191+
bun test
192+
bun run build:linux
193+
194+
git checkout main
195+
git pull origin main
196+
git merge develop --no-ff -m "Merge develop for vX.Y.Z"
197+
git push origin main
198+
199+
git tag -a vX.Y.Z -m "Release vX.Y.Z"
200+
git push origin vX.Y.Z
201+
```
202+
203+
## Repo-Specific Reminders
204+
205+
- Keep commit subjects clean because they become release notes.
206+
- Bump all three package versions together.
207+
- Do not tag before CI passes on `main`.
208+
- If the release includes user-visible behavior changes, update `README.md` in the same release.
209+
- If the release includes protocol or API behavior changes, update the corresponding files in `docs/` before tagging.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "open-wemo",
3-
"version": "0.4.0",
3+
"version": "0.4.1",
44
"private": true,
55
"description": "Open-source solution for controlling Belkin WeMo smart home devices",
66
"workspaces": ["packages/*"],

packages/bridge/assets/generate-icons.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ const iconPng = renderSvgToPng(svgContent, 32);
5454
const iconIco = createIco(iconPng, 32, 32);
5555

5656
// Create error icon (red background instead of dark blue)
57-
const errorSvgContent = svgContent.replace('#1a1a2e', '#7f1d1d').replace('#4ade80', '#fca5a5');
57+
const errorSvgContent = svgContent.replace("#1a1a2e", "#7f1d1d").replace("#4ade80", "#fca5a5");
5858
const iconErrorPng = renderSvgToPng(errorSvgContent, 32);
5959
const iconErrorIco = createIco(iconErrorPng, 32, 32);
6060

packages/bridge/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@open-wemo/bridge",
3-
"version": "0.4.0",
3+
"version": "0.4.1",
44
"private": true,
55
"description": "Desktop tray app + REST API server for Open Wemo",
66
"type": "module",

packages/bridge/src/install.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ export function isInstalledLocation(): boolean {
8686
console.log(`[Install] Expected installed path: ${installedExePath}`);
8787

8888
// Check if current path matches the installed exe path exactly, or is within install directory
89-
const isInstalled = currentPath === installedExePath || currentPath.startsWith(installDir + "/");
89+
const isInstalled = currentPath === installedExePath || currentPath.startsWith(`${installDir}/`);
9090

9191
console.log(`[Install] Is installed location: ${isInstalled}`);
9292

packages/bridge/src/server/routes/devices.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,34 @@ deviceRoutes.put("/:id/keepalive", async (c) => {
481481

482482
setKeepAliveEnabled(device.id, body.enabled);
483483

484+
if (device.deviceType === "Insight") {
485+
try {
486+
const client = await getInsightClient(device);
487+
const autoThresholdWatts = body.enabled ? 0 : 8;
488+
const displayThresholdMilliwatts = body.enabled ? 1 : 8000;
489+
await Promise.all([
490+
client.setAutoPowerThreshold(autoThresholdWatts),
491+
client.setPowerThreshold(displayThresholdMilliwatts),
492+
]);
493+
494+
if (body.enabled) {
495+
const state = await client.getBinaryState();
496+
if (state === 0 || state === 8) {
497+
markManualOff(device.id);
498+
}
499+
}
500+
501+
console.log(
502+
`[KeepAlive] Set auto threshold to ${autoThresholdWatts}W, display threshold to ${displayThresholdMilliwatts}mW for "${device.name}"`
503+
);
504+
} catch (error) {
505+
console.error(
506+
`[KeepAlive] Failed to set power thresholds for "${device.name}":`,
507+
error instanceof Error ? error.message : error
508+
);
509+
}
510+
}
511+
484512
return c.json({
485513
id: device.id,
486514
enabled: body.enabled,

packages/bridge/src/server/static.ts

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -113,41 +113,6 @@ function getWebDir(): string {
113113
return devPath;
114114
}
115115

116-
/**
117-
* Recursively reads all files from a directory (development mode).
118-
*/
119-
function readFilesRecursively(dir: string, basePath = ""): void {
120-
if (!existsSync(dir)) {
121-
console.warn(`[Static] Directory not found: ${dir}`);
122-
return;
123-
}
124-
125-
const entries = readdirSync(dir);
126-
127-
for (const entry of entries) {
128-
const fullPath = join(dir, entry);
129-
const relativePath = basePath ? `${basePath}/${entry}` : entry;
130-
const stat = statSync(fullPath);
131-
132-
if (stat.isDirectory()) {
133-
readFilesRecursively(fullPath, relativePath);
134-
} else if (stat.isFile()) {
135-
try {
136-
// Use Bun.file for reading - works with both filesystem and embedded paths
137-
const file = Bun.file(fullPath);
138-
const content = new Uint8Array(file.stream() as unknown as ArrayBuffer);
139-
const mimeType = getMimeType(entry);
140-
fileCache.set(`/${relativePath}`, {
141-
content,
142-
mimeType,
143-
});
144-
} catch (error) {
145-
console.warn(`[Static] Failed to read file: ${relativePath}`, error);
146-
}
147-
}
148-
}
149-
}
150-
151116
/**
152117
* Loads embedded files (production/compiled mode).
153118
* Uses Bun.file() which works with both filesystem and $bunfs/ paths.

packages/bridge/src/tray/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* Uses systray2 for cross-platform support.
66
*/
77

8-
import { chmodSync, existsSync, readdirSync, readFileSync } from "node:fs";
8+
import { chmodSync, existsSync, readFileSync, readdirSync } from "node:fs";
99
import { homedir, platform } from "node:os";
1010
import { join } from "node:path";
1111

0 commit comments

Comments
 (0)