Skip to content

Commit 3673b9c

Browse files
committed
v4.0.0 (#330)
* Complete rewrite with unified hybrid BLE/API architecture * No backward compatibility with v3.x - migration required * Single `SwitchBot` class replaces `SwitchBotBLE` and `SwitchBotOpenAPI` * Device access via `switchbot.devices` manager pattern * Full TypeScript rewrite with comprehensive type definitions * **Hybrid Architecture**: Unified BLE-first approach with automatic OpenAPI fallback * **Automatic Discovery**: Combined BLE + OpenAPI device discovery in single call * **Smart Fallback**: Commands automatically retry via API when BLE fails * **Device Manager**: Centralized device management with `get()`, `list()`, and `clear()` methods * **Expanded Device Coverage**: Added support and parsing updates across newly modeled devices and accessories in the v4 architecture. * **macOS BLE Support**: Enabled BLE functionality on macOS in addition to Linux * Platform detection now includes macOS (`darwin`) using `@stoprocent/noble` * BLE now works on Linux and macOS systems * Windows remains unsupported for BLE operations * **Bot Password Protection**: Added BLE password support for Bot (`WoHand`) devices * Password encryption using CRC32 checksums * Password validation for 4 alphanumeric characters, case-sensitive * Methods: `setPassword()`, `clearPassword()`, `hasPassword()` * Automatic encrypted command execution when password is set * Example: `examples/bot-password.js` * Based on [homebridge-switchbot PR #1337](OpenWonderLabs/homebridge-switchbot#1337) * **Advanced Resilience Features**: Enterprise-grade reliability enhancements * Retry logic with exponential backoff and jitter * Circuit breaker pattern to prevent cascading failures * Connection intelligence tracking per-device success and failure rates * Custom fallback handler system for logging, metrics, and alerting * Intelligent connection selection based on historical performance * **Event-Driven**: EventEmitter-based architecture for discovery and command events * **TypeScript Native**: Written in TypeScript with full type safety and exports * **Custom Errors**: Specific error classes for better error handling * **BLE-Only Mode**: Works without OpenAPI credentials on Linux/macOS systems * **API-Only Mode**: Works without BLE on Windows systems * **Exports and API Surface Updates**: Updated `src/index.ts` exports for final v4 public API packaging. * **Comprehensive Examples**: Updated and aligned example files for v4 usage patterns. * Complete ES2022 module implementation * Improved error handling with custom error classes * Better connection management and timeout handling * Enhanced logging with configurable log levels * Hardened dependency graph with security overrides for vulnerable transitive packages * Full JSDoc documentation coverage * Comprehensive TypeScript type definitions for all devices * Reworked test suite structure and coverage for APIs, devices, and utilities * Auto-formatting and linting with @antfu/eslint-config * Updated README with v4 quick start and migration guide * 6 usage examples in `examples/` directory * Migration guide from v3.x to v4.0.0 * Added password protection section to [BLE.md](BLE.md) * Updated [README.md](README.md) with password examples * Created comprehensive password protection example * Updated examples index with `bot-password.js` * Updated platform support documentation to reflect macOS BLE support * Updated Linux-only references to Linux/macOS where applicable * Added comprehensive prerequisites guidance for BLE setup on macOS and Linux * macOS: Xcode and Bluetooth permissions requirements * Linux: system packages, non-root setup, and Raspberry Pi notes * Based on [@stoprocent/noble prerequisites](https://github.com/stoprocent/noble?tab=readme-ov-file#prerequisites) * Full API documentation via TypeDoc * Added 14 comprehensive password protection tests * Validation for CRC32 encryption, command building, and response parsing * Extended fallback and retry behavior coverage across BLE/API paths * 58 test files and 243 passing tests at release validation **Full Changelog**: v3.6.8...v4.0.0 chore(release): v3.6.9 [skip ci]
1 parent 72965a5 commit 3673b9c

542 files changed

Lines changed: 27697 additions & 19907 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/copilot-instructions.md

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,25 @@ Always reference these instructions first and fallback to search or bash command
2121
- **Use lint:fix for automatic fixes**: `npm run lint:fix` to automatically fix ESLint issues
2222

2323
### Platform Requirements and Constraints
24-
- **BLE functionality requires Linux-based OS only** (Raspbian, Ubuntu, etc.)
25-
- **Windows and macOS are NOT supported** for BLE operations (use OpenAPI instead)
24+
- **BLE functionality requires Linux or macOS** (Raspbian, Ubuntu, macOS, etc.)
25+
- **Windows is NOT supported** for BLE operations (use OpenAPI instead)
2626
- **Node.js versions**: Must use ^20, ^22, or ^24 (currently using v20.19.4)
2727
- **ES Modules**: This project uses `"type": "module"` - always use ES import/export syntax
2828

29+
#### BLE Prerequisites
30+
31+
**macOS:**
32+
- Xcode installed
33+
- Bluetooth permissions enabled for terminal app in System Preferences
34+
35+
**Linux (Ubuntu/Debian/Raspbian):**
36+
- Required packages: `sudo apt-get install bluetooth bluez libbluetooth-dev libudev-dev`
37+
- For non-root access: `sudo setcap cap_net_raw+eip $(eval readlink -f \`which node\`)`
38+
- Raspberry Pi: May need to disable pnat plugin in `/etc/bluetooth/main.conf`
39+
40+
**Linux (Fedora/RPM):**
41+
- Required packages: `sudo yum install bluez bluez-libs bluez-libs-devel`
42+
2943
### Testing and Validation
3044
- **Basic functionality test**: After any changes to core classes, run this validation:
3145
```javascript
@@ -116,9 +130,9 @@ Always reference these instructions first and fallback to search or bash command
116130
## Troubleshooting Common Issues
117131

118132
### BLE Not Working
119-
- **Check OS**: BLE only works on Linux-based systems
133+
- **Check OS**: BLE works on Linux and macOS systems only
120134
- **Install noble prerequisites**: May need additional system libraries for @stoprocent/noble
121-
- **Use OpenAPI instead**: For Windows/macOS development, use SwitchBotOpenAPI class
135+
- **Use OpenAPI instead**: For Windows development, use SwitchBotOpenAPI class
122136

123137
### Build Failures
124138
- **Check Node.js version**: Must be ^20, ^22, or ^24
@@ -156,7 +170,7 @@ npm run watch # Build and link for development
156170
- **Type definitions**: All device status and response types
157171

158172
### Dependencies Summary
159-
- **@stoprocent/noble**: BLE functionality (Linux only)
173+
- **@stoprocent/noble**: BLE functionality (Linux/macOS)
160174
- **undici**: HTTP client for API requests
161175
- **async-mutex**: Concurrency control
162176
- **TypeScript**: Language and compilation

.github/workflows/beta-release.yml

Lines changed: 0 additions & 52 deletions
This file was deleted.

.github/workflows/release.yml

Lines changed: 64 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,78 @@
1-
name: Unified Release
1+
name: Release
22

33
on:
44
push:
55
branches:
6-
#- "alpha-*"
7-
#- "beta-*"
8-
- latest
6+
- 'beta-*'
7+
- 'beta'
8+
- 'latest'
99
workflow_dispatch:
1010

1111
jobs:
12+
# ─── BETA RELEASE (branches starting with "beta") ────────────────────────────
13+
14+
# 1️⃣ Build and test
15+
build_and_test:
16+
if: startsWith(github.ref_name, 'beta')
17+
uses: homebridge/.github/.github/workflows/nodejs-build-and-test.yml@latest
18+
with:
19+
enable_coverage: false
20+
secrets:
21+
token: ${{ secrets.GITHUB_TOKEN }}
22+
23+
# 2️⃣ Lint
24+
lint:
25+
if: startsWith(github.ref_name, 'beta')
26+
needs: build_and_test
27+
uses: homebridge/.github/.github/workflows/eslint.yml@latest
28+
29+
# 3️⃣ Publish beta to NPM (OIDC — id-token required here and in the reusable workflow)
30+
beta-publish:
31+
if: startsWith(github.ref_name, 'beta')
32+
needs: lint
33+
permissions:
34+
id-token: write
35+
uses: homebridge/.github/.github/workflows/npm-publish-esm-oidc.yml@latest
36+
with:
37+
tag: 'beta'
38+
dynamically_adjust_version: true
39+
npm_version_command: 'pre'
40+
pre_id: 'beta'
41+
42+
# 4️⃣ Create GitHub pre-release
43+
beta-pre-release:
44+
if: startsWith(github.ref_name, 'beta')
45+
needs: beta-publish
46+
uses: homebridge/.github/.github/workflows/pre-release.yml@latest
47+
with:
48+
npm_version: ${{ needs.beta-publish.outputs.NPM_VERSION }}
49+
body: |
50+
**Beta Release**
51+
**Version**: v${{ needs.beta-publish.outputs.NPM_VERSION }}
52+
[How To Test Beta Releases](https://github.com/OpenWonderLabs/homebridge-switchbot/wiki/Beta-Version)
53+
54+
55+
# ─── STABLE RELEASE (branch: "latest") ───────────────────────────────────────
56+
1257
# 1️⃣ Determine release type, ESM status, and branch name
1358
determine-release-type:
59+
if: github.ref_name == 'latest'
1460
uses: homebridge/.github/.github/workflows/determine-release-type.yml@latest
1561
with:
1662
ref_name: ${{ github.ref_name }}
1763

1864
# 2️⃣ Update version and changelog using the scripts
1965
update-version:
66+
if: github.ref_name == 'latest'
2067
needs: determine-release-type
2168
uses: homebridge/.github/.github/workflows/update-version.yml@latest
2269
with:
2370
release_type: ${{ needs.determine-release-type.outputs.release_type }}
2471
is_esm: ${{ needs.determine-release-type.outputs.is_esm == 'true' }}
2572

26-
# 3️⃣ Publish to NPM and create GitHub release
73+
# 3️⃣ Publish to NPM and create GitHub release (OIDC — id-token required here and in the reusable workflow)
2774
publish-release:
75+
if: github.ref_name == 'latest'
2876
needs: [determine-release-type, update-version]
2977
permissions:
3078
id-token: write
@@ -39,33 +87,34 @@ jobs:
3987

4088
# 4️⃣ Promote branch if this is a prerelease (alpha/beta)
4189
promote-branch:
90+
if: ${{ github.ref_name == 'latest' && needs.determine-release-type.outputs.release_type != 'latest' && needs.determine-release-type.outputs.release_type != 'skip' }}
4291
needs: [determine-release-type, publish-release]
43-
if: ${{ needs.determine-release-type.outputs.release_type != 'latest' && needs.determine-release-type.outputs.release_type != 'skip' }}
4492
uses: homebridge/.github/.github/workflows/promote-branch.yml@latest
4593
with:
4694
branch_name: ${{ needs.determine-release-type.outputs.branch_name }}
4795
release_type: ${{ needs.determine-release-type.outputs.release_type }}
4896
is_esm: ${{ needs.determine-release-type.outputs.is_esm == 'true' }}
4997

50-
# 5️⃣ Notify if any previous job fails
98+
# 5️⃣ Notify if any stable-release job fails
5199
workflow-failure:
52-
if: ${{ failure() }}
100+
if: failure()
53101
needs: [determine-release-type, update-version, publish-release, promote-branch]
54102
uses: homebridge/.github/.github/workflows/report-failure.yml@latest
55103
with:
56104
workflow_name: ${{ github.workflow }}
57105
job_name: ${{ github.job }}
58106
run_url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
59107

60-
# 6️⃣ Post to Discord
61-
github-releases-to-discord:
62-
name: Discord Webhooks
63-
needs: [determine-release-type, update-version, publish-release]
108+
# 6️⃣ Post to Discord (Beta + Stable)
109+
discord:
110+
name: Release Notifications
111+
if: ${{ always() && ((startsWith(github.ref_name, 'beta') && needs.beta-publish.result == 'success') || (github.ref_name == 'latest' && needs.publish-release.result == 'success')) }}
112+
needs: [beta-publish, update-version, publish-release]
64113
uses: homebridge/.github/.github/workflows/discord-webhooks.yml@latest
65114
with:
66-
title: "Node-SwitchBot Module Release"
115+
title: ${{ startsWith(github.ref_name, 'beta') && 'SwitchBot-Node Beta Release' || 'SwitchBot-Node Release' }}
67116
description: |
68-
Version `v${{ needs.update-version.outputs.version }}`
69-
url: "https://github.com/OpenWonderLabs/node-switchbot/releases/tag/v${{ needs.update-version.outputs.version }}"
117+
Version `v${{ startsWith(github.ref_name, 'beta') && needs.beta-publish.outputs.NPM_VERSION || needs.update-version.outputs.version }}`
118+
url: "https://github.com/OpenWonderLabs/node-switchbot/releases/tag/v${{ startsWith(github.ref_name, 'beta') && needs.beta-publish.outputs.NPM_VERSION || needs.update-version.outputs.version }}"
70119
secrets:
71120
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK_URL_LATEST }}

.github/workflows/stale.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ name: Stale workflow
33
on:
44
workflow_dispatch:
55
schedule:
6-
- cron: '45 11 * * *'
6+
- cron: '45 7 * * *'
77

88
jobs:
99
stale:
10-
uses: OpenWonderLabs/.github/.github/workflows/stale.yml@latest
10+
uses: homebridge/.github/.github/workflows/stale.yml@latest
1111
secrets:
1212
token: ${{ secrets.GITHUB_TOKEN }}

.npmignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22

33
src
44

5+
# v4.0.0 additions
6+
test/
7+
examples/
8+
.dev-archive/
9+
vitest.config.ts
10+
typedoc.json
11+
eslint.config.js
12+
513
# ------------- Defaults -------------
614

715
# eslint

0 commit comments

Comments
 (0)