homebridge-carrier-infinity 1.8.0 → 1.9.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.devcontainer/.uix-hb-service-homebridge-startup.json.example +5 -0
- package/.devcontainer/README.md +211 -0
- package/.devcontainer/config.json.example +28 -0
- package/.devcontainer/devcontainer.json +55 -0
- package/.devcontainer/setup-homebridge.sh +51 -0
- package/.devcontainer/start-homebridge.sh +29 -0
- package/.devcontainer/stop-homebridge.sh +7 -0
- package/CLAUDE.md +186 -0
- package/dist/accessory_base.d.ts +2 -2
- package/dist/accessory_base.d.ts.map +1 -1
- package/dist/accessory_base.js.map +1 -1
- package/dist/api/graphql_client.d.ts +71 -0
- package/dist/api/graphql_client.d.ts.map +1 -0
- package/dist/api/graphql_client.js +261 -0
- package/dist/api/graphql_client.js.map +1 -0
- package/dist/api/graphql_operations.d.ts +56 -0
- package/dist/api/graphql_operations.d.ts.map +1 -0
- package/dist/api/graphql_operations.js +325 -0
- package/dist/api/graphql_operations.js.map +1 -0
- package/dist/api/interface_graphql_mutations.d.ts +149 -0
- package/dist/api/interface_graphql_mutations.d.ts.map +1 -0
- package/dist/api/interface_graphql_mutations.js +8 -0
- package/dist/api/interface_graphql_mutations.js.map +1 -0
- package/dist/api/interface_graphql_system.d.ts +287 -0
- package/dist/api/interface_graphql_system.d.ts.map +1 -0
- package/dist/api/interface_graphql_system.js +9 -0
- package/dist/api/interface_graphql_system.js.map +1 -0
- package/dist/api/models_graphql.d.ts +167 -0
- package/dist/api/models_graphql.d.ts.map +1 -0
- package/dist/api/models_graphql.js +590 -0
- package/dist/api/models_graphql.js.map +1 -0
- package/dist/api/oauth2.d.ts +45 -0
- package/dist/api/oauth2.d.ts.map +1 -0
- package/dist/api/oauth2.js +35 -0
- package/dist/api/oauth2.js.map +1 -0
- package/dist/characteristics_base.d.ts +2 -2
- package/dist/characteristics_base.d.ts.map +1 -1
- package/dist/characteristics_base.js.map +1 -1
- package/dist/platform.d.ts +4 -4
- package/dist/platform.d.ts.map +1 -1
- package/dist/platform.js +7 -12
- package/dist/platform.js.map +1 -1
- package/dist/settings.d.ts +6 -3
- package/dist/settings.d.ts.map +1 -1
- package/dist/settings.js +7 -4
- package/dist/settings.js.map +1 -1
- package/eslint.config.mjs +172 -0
- package/package.json +24 -26
- package/dist/api/interface_config.d.ts +0 -185
- package/dist/api/interface_config.d.ts.map +0 -1
- package/dist/api/interface_config.js +0 -5
- package/dist/api/interface_config.js.map +0 -1
- package/dist/api/interface_locations.d.ts +0 -45
- package/dist/api/interface_locations.d.ts.map +0 -1
- package/dist/api/interface_locations.js +0 -5
- package/dist/api/interface_locations.js.map +0 -1
- package/dist/api/interface_profile.d.ts +0 -80
- package/dist/api/interface_profile.d.ts.map +0 -1
- package/dist/api/interface_profile.js +0 -5
- package/dist/api/interface_profile.js.map +0 -1
- package/dist/api/interface_status.d.ts +0 -90
- package/dist/api/interface_status.d.ts.map +0 -1
- package/dist/api/interface_status.js +0 -5
- package/dist/api/interface_status.js.map +0 -1
- package/dist/api/models.d.ts +0 -111
- package/dist/api/models.d.ts.map +0 -1
- package/dist/api/models.js +0 -572
- package/dist/api/models.js.map +0 -1
- package/dist/api/oauth.d.ts +0 -6
- package/dist/api/oauth.d.ts.map +0 -1
- package/dist/api/oauth.js +0 -49
- package/dist/api/oauth.js.map +0 -1
- package/dist/api/rest_client.d.ts +0 -15
- package/dist/api/rest_client.d.ts.map +0 -1
- package/dist/api/rest_client.js +0 -120
- package/dist/api/rest_client.js.map +0 -1
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# Devcontainer Development Environment
|
|
2
|
+
|
|
3
|
+
This directory contains configuration for a complete Homebridge development environment that runs in GitHub Codespaces or VS Code devcontainers.
|
|
4
|
+
|
|
5
|
+
## What Happens Automatically
|
|
6
|
+
|
|
7
|
+
### On Container Creation (First Time)
|
|
8
|
+
The setup script ([setup-homebridge.sh](setup-homebridge.sh)) automatically:
|
|
9
|
+
- Installs Homebridge, Homebridge Config UI X, and pm2 globally
|
|
10
|
+
- Creates `~/.homebridge/` directory structure
|
|
11
|
+
- Copies [config.json.example](config.json.example) to `~/.homebridge/config.json`
|
|
12
|
+
- Installs project dependencies
|
|
13
|
+
- Builds the plugin
|
|
14
|
+
- Symlinks the plugin to `~/.homebridge/node_modules/`
|
|
15
|
+
|
|
16
|
+
### On Every Container Start
|
|
17
|
+
The startup script ([start-homebridge.sh](start-homebridge.sh)) automatically:
|
|
18
|
+
- Starts Homebridge and Config UI via `hb-service` managed by `pm2`
|
|
19
|
+
- pm2 keeps the process running persistently (auto-restart on crashes)
|
|
20
|
+
- Verifies the UI is accessible on port 8581
|
|
21
|
+
- Displays status information
|
|
22
|
+
|
|
23
|
+
**Result**: When you open the codespace, Homebridge is already running under pm2 and will persist!
|
|
24
|
+
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
### 1. Configure Credentials
|
|
28
|
+
Edit your Carrier Infinity credentials:
|
|
29
|
+
|
|
30
|
+
**Option A** - Via Config UI (easiest):
|
|
31
|
+
- Open http://localhost:8581 (credentials: admin/admin)
|
|
32
|
+
- Navigate to the CarrierInfinity platform settings
|
|
33
|
+
- Update username and password
|
|
34
|
+
|
|
35
|
+
**Option B** - Via command line:
|
|
36
|
+
```bash
|
|
37
|
+
nano ~/.homebridge/config.json
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### 2. Development Workflow
|
|
41
|
+
|
|
42
|
+
**Automatic rebuild** (recommended):
|
|
43
|
+
```bash
|
|
44
|
+
npm run watch
|
|
45
|
+
```
|
|
46
|
+
This watches for file changes and rebuilds automatically.
|
|
47
|
+
|
|
48
|
+
**After making changes**:
|
|
49
|
+
```bash
|
|
50
|
+
# Restart Homebridge to pick up plugin changes
|
|
51
|
+
pm2 restart homebridge
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Configuration Files
|
|
55
|
+
|
|
56
|
+
| File | Purpose |
|
|
57
|
+
|------|---------|
|
|
58
|
+
| **devcontainer.json** | Main devcontainer configuration |
|
|
59
|
+
| **setup-homebridge.sh** | One-time setup script (runs on container creation) |
|
|
60
|
+
| **start-homebridge.sh** | Startup script (runs on every container start) |
|
|
61
|
+
| **stop-homebridge.sh** | Stop Homebridge (pm2 delete) |
|
|
62
|
+
| **config.json.example** | Template Homebridge configuration |
|
|
63
|
+
|
|
64
|
+
### devcontainer.json Features
|
|
65
|
+
- Base image: Node.js 22 (TypeScript)
|
|
66
|
+
- Auto-installs VS Code extensions (Claude Code, ESLint, Copilot, Jest, etc.)
|
|
67
|
+
- Forwards port 8581 (Homebridge Config UI)
|
|
68
|
+
- Runs setup on creation, starts Homebridge on every startup
|
|
69
|
+
|
|
70
|
+
## Useful Commands
|
|
71
|
+
|
|
72
|
+
### Homebridge Management
|
|
73
|
+
```bash
|
|
74
|
+
# Check Homebridge status (managed by pm2)
|
|
75
|
+
pm2 status
|
|
76
|
+
|
|
77
|
+
# View live logs
|
|
78
|
+
pm2 logs homebridge
|
|
79
|
+
|
|
80
|
+
# View Homebridge log file
|
|
81
|
+
tail -f ~/.homebridge/homebridge.log
|
|
82
|
+
|
|
83
|
+
# Restart Homebridge
|
|
84
|
+
pm2 restart homebridge
|
|
85
|
+
|
|
86
|
+
# Stop Homebridge (will auto-start on next codespace start)
|
|
87
|
+
pm2 stop homebridge
|
|
88
|
+
# Or permanently remove:
|
|
89
|
+
.devcontainer/stop-homebridge.sh
|
|
90
|
+
|
|
91
|
+
# Start Homebridge manually (if stopped)
|
|
92
|
+
.devcontainer/start-homebridge.sh
|
|
93
|
+
|
|
94
|
+
# View pm2 process info
|
|
95
|
+
pm2 info homebridge
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Plugin Development
|
|
99
|
+
```bash
|
|
100
|
+
# Build plugin
|
|
101
|
+
npm run build
|
|
102
|
+
|
|
103
|
+
# Auto-rebuild on changes (recommended)
|
|
104
|
+
npm run watch
|
|
105
|
+
|
|
106
|
+
# Relink plugin manually (if needed)
|
|
107
|
+
npm run relink
|
|
108
|
+
# or
|
|
109
|
+
ln -sf $(pwd) ~/.homebridge/node_modules/homebridge-carrier-infinity
|
|
110
|
+
|
|
111
|
+
# Run tests
|
|
112
|
+
npm test
|
|
113
|
+
|
|
114
|
+
# Lint code
|
|
115
|
+
npm run lint
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Accessing Homebridge Config UI
|
|
119
|
+
- **URL**: http://localhost:8581 (auto-forwarded in VS Code)
|
|
120
|
+
- **Default credentials**: admin/admin
|
|
121
|
+
- Click the "Ports" tab in VS Code to open directly
|
|
122
|
+
|
|
123
|
+
## Debugging Tips
|
|
124
|
+
|
|
125
|
+
1. **Check plugin is loaded**: Look for "Loaded plugin: homebridge-carrier-infinity" in logs
|
|
126
|
+
2. **Check platform is registered**: Look for "Loading platform: CarrierInfinity"
|
|
127
|
+
3. **API errors**: Check `~/.homebridge/homebridge.log` for debug output
|
|
128
|
+
4. **Config issues**: Homebridge will log validation errors on startup
|
|
129
|
+
|
|
130
|
+
### Common Issues
|
|
131
|
+
|
|
132
|
+
**Plugin not loading**:
|
|
133
|
+
```bash
|
|
134
|
+
# Verify plugin is linked
|
|
135
|
+
ls -la ~/.homebridge/node_modules/homebridge-carrier-infinity
|
|
136
|
+
|
|
137
|
+
# Rebuild and relink
|
|
138
|
+
npm run build && npm run relink
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
**Homebridge not starting**:
|
|
142
|
+
```bash
|
|
143
|
+
# Check pm2 status
|
|
144
|
+
pm2 status
|
|
145
|
+
|
|
146
|
+
# Check logs for errors
|
|
147
|
+
pm2 logs homebridge --lines 50
|
|
148
|
+
|
|
149
|
+
# Or check Homebridge log file
|
|
150
|
+
tail -n 50 ~/.homebridge/homebridge.log
|
|
151
|
+
|
|
152
|
+
# Restart via pm2
|
|
153
|
+
pm2 restart homebridge
|
|
154
|
+
|
|
155
|
+
# Or restart via script (stops and starts fresh)
|
|
156
|
+
pm2 delete homebridge
|
|
157
|
+
.devcontainer/start-homebridge.sh
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Environment Details
|
|
161
|
+
|
|
162
|
+
- **Homebridge config**: `~/.homebridge/config.json`
|
|
163
|
+
- **Homebridge storage**: `~/.homebridge/`
|
|
164
|
+
- **Plugin source**: `/workspaces/homebridge-carrier-infinity`
|
|
165
|
+
- **Node version**: 22.x
|
|
166
|
+
- **Homebridge version**: Latest
|
|
167
|
+
- **Config UI version**: Latest
|
|
168
|
+
|
|
169
|
+
## Persistence
|
|
170
|
+
|
|
171
|
+
The following persists across codespace rebuilds:
|
|
172
|
+
- `~/.homebridge/config.json` (your configuration)
|
|
173
|
+
- `~/.homebridge/` directory (accessories, cache, persist data)
|
|
174
|
+
|
|
175
|
+
The plugin code is always from your working directory, so changes are reflected immediately after rebuild + restart.
|
|
176
|
+
|
|
177
|
+
## VS Code Extensions
|
|
178
|
+
|
|
179
|
+
The following extensions are automatically installed:
|
|
180
|
+
- **Claude Code**: AI coding assistant
|
|
181
|
+
- **ESLint**: Linting and formatting
|
|
182
|
+
- **GitHub Copilot**: AI code completion
|
|
183
|
+
- **Jest**: Test runner integration
|
|
184
|
+
- **GitHub Actions**: Workflow management
|
|
185
|
+
|
|
186
|
+
## Advanced Usage
|
|
187
|
+
|
|
188
|
+
### Running Multiple Instances
|
|
189
|
+
|
|
190
|
+
To test different configurations:
|
|
191
|
+
```bash
|
|
192
|
+
# Stop pm2-managed instance
|
|
193
|
+
pm2 stop homebridge
|
|
194
|
+
|
|
195
|
+
# Run with custom config
|
|
196
|
+
homebridge -D -U /path/to/custom/homebridge/directory
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Debugging with Breakpoints
|
|
200
|
+
|
|
201
|
+
1. Stop the pm2-managed Homebridge: `pm2 stop homebridge`
|
|
202
|
+
2. Use VS Code's debugger to start Homebridge
|
|
203
|
+
3. Set breakpoints in your plugin code
|
|
204
|
+
|
|
205
|
+
### Manual Testing
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
# Test specific API endpoints
|
|
209
|
+
npm run build
|
|
210
|
+
node -e "require('./dist/api/rest_client.js')"
|
|
211
|
+
```
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"bridge": {
|
|
3
|
+
"name": "Homebridge Dev",
|
|
4
|
+
"username": "CC:22:3D:E3:CE:30",
|
|
5
|
+
"port": 51826,
|
|
6
|
+
"pin": "031-45-154"
|
|
7
|
+
},
|
|
8
|
+
"accessories": [],
|
|
9
|
+
"platforms": [
|
|
10
|
+
{
|
|
11
|
+
"name": "Config",
|
|
12
|
+
"port": 8581,
|
|
13
|
+
"platform": "config",
|
|
14
|
+
"tempUnits": "f"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"platform": "CarrierInfinity",
|
|
18
|
+
"name": "Carrier Infinity",
|
|
19
|
+
"username": "YOUR_CARRIER_USERNAME",
|
|
20
|
+
"password": "YOUR_CARRIER_PASSWORD",
|
|
21
|
+
"holdBehavior": "forever",
|
|
22
|
+
"showOutdoorTemperatureSensor": false,
|
|
23
|
+
"showFanControl": false,
|
|
24
|
+
"showIndoorHumiditySensors": false,
|
|
25
|
+
"showZoneComfortActivityControls": false
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Homebridge Carrier Infinity Dev",
|
|
3
|
+
"image": "mcr.microsoft.com/devcontainers/typescript-node:1-22-bookworm",
|
|
4
|
+
|
|
5
|
+
"features": {
|
|
6
|
+
"ghcr.io/devcontainers/features/github-cli:1": {},
|
|
7
|
+
"ghcr.io/devcontainers/features/git:1": {}
|
|
8
|
+
},
|
|
9
|
+
|
|
10
|
+
"customizations": {
|
|
11
|
+
"vscode": {
|
|
12
|
+
"extensions": [
|
|
13
|
+
"anthropic.claude-code",
|
|
14
|
+
"dbaeumer.vscode-eslint",
|
|
15
|
+
"github.copilot",
|
|
16
|
+
"github.copilot-chat",
|
|
17
|
+
"github.github-vscode-theme",
|
|
18
|
+
"github.vscode-github-actions",
|
|
19
|
+
"github.vscode-pull-request-github",
|
|
20
|
+
"ms-vscode.vscode-typescript-next",
|
|
21
|
+
"orta.vscode-jest"
|
|
22
|
+
],
|
|
23
|
+
"settings": {
|
|
24
|
+
"editor.formatOnSave": true,
|
|
25
|
+
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
|
|
26
|
+
"editor.codeActionsOnSave": {
|
|
27
|
+
"source.fixAll.eslint": "explicit"
|
|
28
|
+
},
|
|
29
|
+
"eslint.validate": [
|
|
30
|
+
"typescript"
|
|
31
|
+
],
|
|
32
|
+
"typescript.tsdk": "node_modules/typescript/lib"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
"postCreateCommand": "chmod +x .devcontainer/setup-homebridge.sh && .devcontainer/setup-homebridge.sh",
|
|
38
|
+
|
|
39
|
+
"postStartCommand": "chmod +x .devcontainer/start-homebridge.sh && .devcontainer/start-homebridge.sh",
|
|
40
|
+
|
|
41
|
+
"forwardPorts": [8581],
|
|
42
|
+
|
|
43
|
+
"portsAttributes": {
|
|
44
|
+
"8581": {
|
|
45
|
+
"label": "Homebridge UI",
|
|
46
|
+
"onAutoForward": "notify"
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
"remoteUser": "node",
|
|
51
|
+
|
|
52
|
+
"mounts": [
|
|
53
|
+
"source=${localEnv:HOME}${localEnv:USERPROFILE}/.gitconfig,target=/home/node/.gitconfig,type=bind,consistency=cached"
|
|
54
|
+
]
|
|
55
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
echo "Setting up Homebridge development environment..."
|
|
5
|
+
|
|
6
|
+
# Install Homebridge and pm2 globally
|
|
7
|
+
echo "Installing Homebridge and pm2..."
|
|
8
|
+
sudo npm install -g --unsafe-perm homebridge homebridge-config-ui-x pm2
|
|
9
|
+
|
|
10
|
+
# Create Homebridge directory structure
|
|
11
|
+
echo "Creating Homebridge directory structure..."
|
|
12
|
+
mkdir -p ~/.homebridge
|
|
13
|
+
|
|
14
|
+
# Create default config if it doesn't exist
|
|
15
|
+
if [ ! -f ~/.homebridge/config.json ]; then
|
|
16
|
+
echo "Creating Homebridge config from example..."
|
|
17
|
+
cp .devcontainer/config.json.example ~/.homebridge/config.json
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
# Create debug mode config if it doesn't exist
|
|
21
|
+
if [ ! -f ~/.homebridge/.uix-hb-service-homebridge-startup.json ]; then
|
|
22
|
+
echo "Creating Homebridge service config from example..."
|
|
23
|
+
cp .devcontainer/.uix-hb-service-homebridge-startup.json.example ~/.homebridge/.uix-hb-service-homebridge-startup.json
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
# Install project dependencies
|
|
27
|
+
echo "Installing project dependencies..."
|
|
28
|
+
npm install
|
|
29
|
+
|
|
30
|
+
# Build the plugin
|
|
31
|
+
echo "Building plugin..."
|
|
32
|
+
npm run build
|
|
33
|
+
|
|
34
|
+
# Link the plugin to Homebridge
|
|
35
|
+
echo "Linking plugin to Homebridge..."
|
|
36
|
+
PLUGIN_PATH="$(pwd)"
|
|
37
|
+
HOMEBRIDGE_PATH="$HOME/.homebridge/node_modules"
|
|
38
|
+
PLUGIN_NAME="homebridge-carrier-infinity"
|
|
39
|
+
|
|
40
|
+
mkdir -p "$HOMEBRIDGE_PATH"
|
|
41
|
+
ln -sf "$PLUGIN_PATH" "$HOMEBRIDGE_PATH/$PLUGIN_NAME"
|
|
42
|
+
|
|
43
|
+
echo ""
|
|
44
|
+
echo "✅ Homebridge development environment setup complete!"
|
|
45
|
+
echo ""
|
|
46
|
+
echo "Quick start:"
|
|
47
|
+
echo " 1. Edit ~/.homebridge/config.json to add your Carrier credentials"
|
|
48
|
+
echo " 2. Homebridge will start automatically on codespace startup"
|
|
49
|
+
echo " - Access Config UI at http://localhost:8581 (credentials: admin/admin)"
|
|
50
|
+
echo " 3. Run 'npm run watch' for auto-rebuild on code changes"
|
|
51
|
+
echo ""
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Start Homebridge and Config UI automatically using pm2
|
|
3
|
+
# This script is run by the devcontainer postStartCommand
|
|
4
|
+
|
|
5
|
+
echo "Starting Homebridge and Config UI with pm2..."
|
|
6
|
+
|
|
7
|
+
# Stop any existing instance (in case of restart)
|
|
8
|
+
pm2 delete homebridge 2>/dev/null || true
|
|
9
|
+
|
|
10
|
+
# Start hb-service using pm2
|
|
11
|
+
pm2 start hb-service --name homebridge -- run -U ~/.homebridge
|
|
12
|
+
|
|
13
|
+
# Save pm2 process list
|
|
14
|
+
pm2 save
|
|
15
|
+
|
|
16
|
+
# Wait a moment for startup
|
|
17
|
+
sleep 3
|
|
18
|
+
|
|
19
|
+
# Check if it's running
|
|
20
|
+
if curl -s http://localhost:8581 > /dev/null 2>&1; then
|
|
21
|
+
echo "✅ Homebridge and Config UI started successfully!"
|
|
22
|
+
echo " - Homebridge UI: http://localhost:8581"
|
|
23
|
+
echo " - Default credentials: admin/admin"
|
|
24
|
+
echo " - Logs: pm2 logs homebridge"
|
|
25
|
+
echo " - Status: pm2 status"
|
|
26
|
+
else
|
|
27
|
+
echo "⚠️ Homebridge may still be starting..."
|
|
28
|
+
echo " Check logs: pm2 logs homebridge"
|
|
29
|
+
fi
|
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
This is a Homebridge plugin for Carrier Infinity / Bryant Evolution / ICP Brands Ion thermostats. It communicates directly with the Carrier/Bryant cloud API (not requiring Infinitude/Infinitive) and exposes thermostat controls, sensors, and activities to HomeKit.
|
|
8
|
+
|
|
9
|
+
## Development Commands
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# Build the plugin
|
|
13
|
+
npm run build
|
|
14
|
+
|
|
15
|
+
# Lint code (must have 0 warnings)
|
|
16
|
+
npm run lint
|
|
17
|
+
|
|
18
|
+
# Run tests
|
|
19
|
+
npm test
|
|
20
|
+
|
|
21
|
+
# Watch mode (auto-rebuild and relink to local Homebridge)
|
|
22
|
+
npm run watch
|
|
23
|
+
|
|
24
|
+
# Relink plugin to local Homebridge instance
|
|
25
|
+
npm run relink
|
|
26
|
+
|
|
27
|
+
# Generate TypeScript interfaces from XML test data
|
|
28
|
+
npm run xml2ts
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Codespace/Devcontainer Development Environment
|
|
32
|
+
|
|
33
|
+
This repository includes a complete devcontainer configuration that automatically sets up a Homebridge development environment.
|
|
34
|
+
|
|
35
|
+
### Automatic Setup
|
|
36
|
+
|
|
37
|
+
When the codespace is created, the setup script (`.devcontainer/setup-homebridge.sh`) automatically:
|
|
38
|
+
- Installs Homebridge, Homebridge Config UI X, and pm2 globally
|
|
39
|
+
- Creates `~/.homebridge/` directory structure
|
|
40
|
+
- Copies `.devcontainer/config.json.example` to `~/.homebridge/config.json` (Fahrenheit display)
|
|
41
|
+
- Copies `.devcontainer/.uix-hb-service-homebridge-startup.json.example` (debug mode enabled)
|
|
42
|
+
- Installs project dependencies
|
|
43
|
+
- Builds the plugin
|
|
44
|
+
- Symlinks the plugin to `~/.homebridge/node_modules/`
|
|
45
|
+
|
|
46
|
+
### Getting Started in Codespace
|
|
47
|
+
|
|
48
|
+
1. **Homebridge starts automatically**: On codespace startup, Homebridge and Config UI start automatically via `hb-service` managed by `pm2`
|
|
49
|
+
- pm2 keeps the service running persistently (auto-restart on crashes)
|
|
50
|
+
- Access Config UI at http://localhost:8581 (port auto-forwarded)
|
|
51
|
+
- Default credentials: admin/admin
|
|
52
|
+
- Logs: `pm2 logs homebridge` or `~/.homebridge/homebridge.log`
|
|
53
|
+
|
|
54
|
+
2. **Configure credentials**: Edit `~/.homebridge/config.json` with your Carrier Infinity username/password
|
|
55
|
+
- Config is pre-created from `.devcontainer/config.json.example` - just update credentials
|
|
56
|
+
- Can also edit via Config UI at http://localhost:8581
|
|
57
|
+
|
|
58
|
+
3. **Development workflow**:
|
|
59
|
+
- Run `npm run watch` (auto-rebuild on changes)
|
|
60
|
+
- Restart Homebridge to pick up plugin changes: `pm2 restart homebridge`
|
|
61
|
+
|
|
62
|
+
For complete development environment documentation, commands, and debugging tips, see [.devcontainer/README.md](.devcontainer/README.md).
|
|
63
|
+
|
|
64
|
+
### TypeScript Configuration
|
|
65
|
+
- Target: ES2018 (Node 10+)
|
|
66
|
+
- Output: `dist/` directory
|
|
67
|
+
- Uses experimental decorators (required for memoization and retry decorators)
|
|
68
|
+
- `noImplicitAny: false` is set in tsconfig
|
|
69
|
+
|
|
70
|
+
## Architecture Overview
|
|
71
|
+
|
|
72
|
+
### Plugin Entry Point
|
|
73
|
+
The plugin registers itself in [src/index.ts](src/index.ts) and implements the Homebridge `DynamicPlatformPlugin` interface via `CarrierInfinityHomebridgePlatform` in [src/platform.ts](src/platform.ts).
|
|
74
|
+
|
|
75
|
+
### Core Components
|
|
76
|
+
|
|
77
|
+
#### 1. Platform ([src/platform.ts](src/platform.ts))
|
|
78
|
+
- Entry point that discovers systems and zones after Homebridge launches
|
|
79
|
+
- Maintains `infinity_client` (GraphQL API client) and `systems` (indexed by serial number)
|
|
80
|
+
- Creates accessories based on config options (outdoor temp sensor, humidity sensors, etc.)
|
|
81
|
+
|
|
82
|
+
#### 2. API Layer ([src/api/](src/api/))
|
|
83
|
+
- **InfinityGraphQLClient** ([src/api/graphql_client.ts](src/api/graphql_client.ts)): OAuth 2.0 authenticated GraphQL client
|
|
84
|
+
- Handles authentication via Okta with username/password
|
|
85
|
+
- Automatic Bearer token injection via interceptor
|
|
86
|
+
- Token refresh with 24hr memoization (via Okta or assistedLogin mutation)
|
|
87
|
+
- No activation endpoint needed (simplified from old REST API)
|
|
88
|
+
|
|
89
|
+
- **GraphQL Operations** ([src/api/graphql_operations.ts](src/api/graphql_operations.ts)): GraphQL queries and mutations
|
|
90
|
+
- `GET_INFINITY_SYSTEMS`: Single query fetches profile, status, and config together (replaces 3 REST calls)
|
|
91
|
+
- `UPDATE_INFINITY_CONFIG`: System-level configuration mutations
|
|
92
|
+
- `UPDATE_INFINITY_ZONE_ACTIVITY`: Zone activity and setpoint mutations
|
|
93
|
+
- `UPDATE_INFINITY_ZONE_CONFIG`: Zone hold and schedule mutations
|
|
94
|
+
|
|
95
|
+
- **Models** ([src/api/models_graphql.ts](src/api/models_graphql.ts)): GraphQL-based models with facade pattern
|
|
96
|
+
- `UnifiedSystemModel` fetches all data (profile + status + config) via single GraphQL query
|
|
97
|
+
- `SystemProfileModel`, `SystemStatusModel`, `SystemConfigModel` are facades that maintain backward compatibility
|
|
98
|
+
- `BaseModel` provides fetch/push pattern with mutex locking to prevent race conditions
|
|
99
|
+
- Hash-based change detection (only pushes if data actually changed)
|
|
100
|
+
- 10-second memoization on fetch operations
|
|
101
|
+
- JSON parsing (native JavaScript objects)
|
|
102
|
+
|
|
103
|
+
- **Interface Files**: TypeScript interfaces for GraphQL API responses
|
|
104
|
+
- [src/api/interface_graphql_system.ts](src/api/interface_graphql_system.ts): System, profile, status, and config types
|
|
105
|
+
- [src/api/interface_graphql_mutations.ts](src/api/interface_graphql_mutations.ts): Mutation input and response types
|
|
106
|
+
|
|
107
|
+
#### 3. Accessories ([src/accessory_*.ts](src/))
|
|
108
|
+
All accessories extend `BaseAccessory` ([src/accessory_base.ts](src/accessory_base.ts)):
|
|
109
|
+
- Handles UUID generation and caching
|
|
110
|
+
- Uses `useService()` helper to find or create HAP services
|
|
111
|
+
- Each accessory type:
|
|
112
|
+
- **ThermostatAccessory**: Main thermostat control
|
|
113
|
+
- **OutdoorTemperatureAccessory**: Outdoor temp sensor (optional)
|
|
114
|
+
- **EnvSensorAccessory**: Indoor humidity sensor (optional)
|
|
115
|
+
- **ComfortActivityAccessory**: Touch-n-Go activity switches (optional)
|
|
116
|
+
|
|
117
|
+
#### 4. Characteristics ([src/characteristics_*.ts](src/))
|
|
118
|
+
The plugin uses a wrapper pattern to bind HomeKit characteristics to API data:
|
|
119
|
+
- **CharacteristicWrapper** ([src/characteristics_base.ts](src/characteristics_base.ts)): Abstract base class
|
|
120
|
+
- Each wrapper handles one characteristic (or multiple via `MultiWrapper`)
|
|
121
|
+
- Defines `get` and `set` async handlers
|
|
122
|
+
- `wrap()` method attaches handlers to HAP Service
|
|
123
|
+
- Automatic debouncing/batching of set operations
|
|
124
|
+
- Subscription-based push updates when API data changes
|
|
125
|
+
|
|
126
|
+
- Specific wrappers:
|
|
127
|
+
- `characteristics_ac.ts`: Heating/cooling characteristics
|
|
128
|
+
- `characteristics_fan.ts`: Fan mode and state
|
|
129
|
+
- `characteristics_humidity.ts`: Humidity sensing
|
|
130
|
+
- `characteristics_filter.ts`: Filter status
|
|
131
|
+
|
|
132
|
+
### Key Patterns
|
|
133
|
+
|
|
134
|
+
#### OAuth Authentication
|
|
135
|
+
The API uses OAuth 2.0 via Okta with Bearer tokens. See [src/api/oauth2.ts](src/api/oauth2.ts) for token injection and [src/settings.ts](src/settings.ts) for OAuth endpoints and client ID. Authentication is handled via the `assistedLogin` GraphQL mutation or Okta token refresh endpoint.
|
|
136
|
+
|
|
137
|
+
#### Memoization & Retry Decorators
|
|
138
|
+
- `@MemoizeExpiring(milliseconds)`: Caches method results for specified duration
|
|
139
|
+
- `@Retryable()`: Automatic retry with exponential backoff for network failures
|
|
140
|
+
- Both decorators from `typescript-memoize` and `typescript-retry-decorator` packages
|
|
141
|
+
|
|
142
|
+
#### Mutex Locking
|
|
143
|
+
API models use `async-mutex` to prevent concurrent fetch/push operations that could cause race conditions. The pattern:
|
|
144
|
+
```typescript
|
|
145
|
+
await tryAcquire(this.write_lock).runExclusive(async () => {
|
|
146
|
+
// critical section
|
|
147
|
+
});
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
#### Data Flow
|
|
151
|
+
1. **Read**: Characteristic getter → Model.fetch() → GraphQL query → JSON parse → return value
|
|
152
|
+
2. **Write**: Characteristic setter → batch changes → Model.push() → GraphQL mutation → JSON input
|
|
153
|
+
3. **Updates**: Periodic polling + hash comparison triggers characteristic updates via subscription pattern
|
|
154
|
+
|
|
155
|
+
**Note**: The GraphQL API combines profile, status, and config data in a single query, reducing network overhead by 66% compared to the old REST API (1 query vs 3 separate calls).
|
|
156
|
+
|
|
157
|
+
#### Hold Behavior
|
|
158
|
+
The plugin supports multiple thermostat hold modes (forever, until next activity, for X hours, until time X) configured via `holdBehavior` and `holdArgument` in config.
|
|
159
|
+
|
|
160
|
+
## Testing
|
|
161
|
+
|
|
162
|
+
- Test framework: Jest with ts-jest preset
|
|
163
|
+
- Test data: XML samples in `testdata/` directory
|
|
164
|
+
- Current tests: `src/helpers.spec.ts`
|
|
165
|
+
- Run: `npm test`
|
|
166
|
+
|
|
167
|
+
## Code Style (ESLint)
|
|
168
|
+
|
|
169
|
+
- Single quotes, 2-space indent
|
|
170
|
+
- Max line length: 140 characters
|
|
171
|
+
- Spellcheck plugin with custom dictionary for domain terms (carrier, bryant, oauth, etc.)
|
|
172
|
+
- See [.eslintrc](.eslintrc) for full rules
|
|
173
|
+
- Must pass with 0 warnings for builds
|
|
174
|
+
|
|
175
|
+
## Semantic Release
|
|
176
|
+
|
|
177
|
+
- Uses conventional commits (commitizen configured)
|
|
178
|
+
- Branches: `master` (next channel), `beta` (prerelease)
|
|
179
|
+
- Automated via GitHub Actions
|
|
180
|
+
|
|
181
|
+
## Important Notes
|
|
182
|
+
|
|
183
|
+
- Changes from HomeKit can take 1-2 minutes to reach the physical thermostat due to polling architecture
|
|
184
|
+
- Node version requirement: >= 18
|
|
185
|
+
- Homebridge version: >= 1.2 or ^2.0.0-beta.0
|
|
186
|
+
- The plugin uses the new GraphQL API (OAuth 2.0, JSON) which replaced the old REST API (OAuth 1.0, XML)
|
package/dist/accessory_base.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Logger, PlatformAccessory, Service, WithUUID } from 'homebridge';
|
|
2
|
-
import {
|
|
2
|
+
import { SystemModelGraphQL } from './api/models_graphql';
|
|
3
3
|
import { CarrierInfinityHomebridgePlatform } from './platform';
|
|
4
4
|
export declare abstract class BaseAccessory {
|
|
5
5
|
protected readonly platform: CarrierInfinityHomebridgePlatform;
|
|
6
6
|
protected readonly context: Record<string, string>;
|
|
7
7
|
readonly accessory: PlatformAccessory;
|
|
8
|
-
protected readonly system:
|
|
8
|
+
protected readonly system: SystemModelGraphQL;
|
|
9
9
|
protected readonly log: Logger;
|
|
10
10
|
constructor(platform: CarrierInfinityHomebridgePlatform, context: Record<string, string>);
|
|
11
11
|
protected abstract ID(context: Record<string, string>): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"accessory_base.d.ts","sourceRoot":"","sources":["../src/accessory_base.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE1E,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"accessory_base.d.ts","sourceRoot":"","sources":["../src/accessory_base.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE1E,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,iCAAiC,EAAE,MAAM,YAAY,CAAC;AAG/D,8BAAsB,aAAa;IAM/B,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAE,iCAAiC;IAC9D,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IANpD,SAAgB,SAAS,EAAE,iBAAiB,CAAC;IAC7C,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,kBAAkB,CAAoD;IACjG,SAAS,CAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAwD;gBAGjE,QAAQ,EAAE,iCAAiC,EAC3C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAkBpD,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM;IAE9D,SAAS,CAAC,UAAU,CAAC,IAAI,EAAE,QAAQ,CAAC,OAAO,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO;CAW9F"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"accessory_base.js","sourceRoot":"","sources":["../src/accessory_base.ts"],"names":[],"mappings":";;;AACA,qDAAgD;AAGhD,yCAAwD;AAExD,MAAsB,aAAa;IAKjC,YACqB,QAA2C,EAC3C,OAA+B;QAD/B,aAAQ,GAAR,QAAQ,CAAmC;QAC3C,YAAO,GAAP,OAAO,CAAwB;QALjC,WAAM,
|
|
1
|
+
{"version":3,"file":"accessory_base.js","sourceRoot":"","sources":["../src/accessory_base.ts"],"names":[],"mappings":";;;AACA,qDAAgD;AAGhD,yCAAwD;AAExD,MAAsB,aAAa;IAKjC,YACqB,QAA2C,EAC3C,OAA+B;QAD/B,aAAQ,GAAR,QAAQ,CAAmC;QAC3C,YAAO,GAAP,OAAO,CAAwB;QALjC,WAAM,GAAuB,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QAC9E,QAAG,GAAW,IAAI,6BAAY,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAMpF,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;QACnE,IAAI,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC;QACzD,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACvB,SAAS,GAAG,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,iBAAiB,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YACxE,SAAS,CAAC,OAAO,GAAG,OAAO,CAAC;YAC5B,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,2BAA2B,CAAC,sBAAW,EAAE,wBAAa,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;QACzF,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACxB,SAAS,CAAC,OAAO,GAAG,OAAO,CAAC;YAC5B,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;QAC3D,CAAC;QACD,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACzC,CAAC;IAIS,UAAU,CAAC,IAA8B,EAAE,IAAY,EAAE,QAAgB;QACjF,+BAA+B;QAC/B,IAAI,OAA4B,CAAC;QACjC,IAAI,QAAQ,EAAE,CAAC;YACb,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QAC1D,CAAC;aAAM,CAAC;YACN,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAC5C,CAAC;QACD,2BAA2B;QAC3B,OAAO,OAAO,IAAI,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;IACpE,CAAC;CACF;AAtCD,sCAsCC"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GraphQL Client for Carrier Infinity API
|
|
3
|
+
*
|
|
4
|
+
* This replaces the InfinityRestClient with a GraphQL-based implementation
|
|
5
|
+
* using OAuth 2.0 Bearer token authentication instead of OAuth 1.0 HMAC signatures.
|
|
6
|
+
*/
|
|
7
|
+
import { AxiosInstance } from 'axios';
|
|
8
|
+
import { Logger } from 'homebridge';
|
|
9
|
+
/**
|
|
10
|
+
* GraphQL Client for Carrier Infinity API
|
|
11
|
+
*
|
|
12
|
+
* Handles OAuth 2.0 authentication and GraphQL query/mutation execution.
|
|
13
|
+
* Much simpler than the old REST client - no XML parsing, no OAuth signatures,
|
|
14
|
+
* no custom certificates, no activation endpoint.
|
|
15
|
+
*/
|
|
16
|
+
export declare class InfinityGraphQLClient {
|
|
17
|
+
username: string;
|
|
18
|
+
private password;
|
|
19
|
+
readonly log: Logger;
|
|
20
|
+
private access_token;
|
|
21
|
+
private refresh_token;
|
|
22
|
+
private token_type;
|
|
23
|
+
private token_expires_in;
|
|
24
|
+
private token_acquired_at;
|
|
25
|
+
axios: AxiosInstance;
|
|
26
|
+
private axiosNoAuth;
|
|
27
|
+
constructor(username: string, password: string, log: Logger);
|
|
28
|
+
/**
|
|
29
|
+
* Refresh OAuth 2.0 token with memoization (24 hours)
|
|
30
|
+
*
|
|
31
|
+
* This is called before every API request to ensure we have a valid token.
|
|
32
|
+
* The memoization ensures we only refresh once per day unless the token expires.
|
|
33
|
+
*/
|
|
34
|
+
refreshToken(): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Force refresh OAuth 2.0 token with retry logic
|
|
37
|
+
*
|
|
38
|
+
* If we have a refresh token, use the Okta token endpoint.
|
|
39
|
+
* Otherwise, use the assistedLogin mutation to get a new token.
|
|
40
|
+
*/
|
|
41
|
+
forceRefreshToken(): Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* Refresh token via Okta OAuth2 endpoint
|
|
44
|
+
*/
|
|
45
|
+
private refreshTokenViaOkta;
|
|
46
|
+
/**
|
|
47
|
+
* Login via assistedLogin GraphQL mutation
|
|
48
|
+
*/
|
|
49
|
+
private loginViaAssistedLogin;
|
|
50
|
+
/**
|
|
51
|
+
* Execute a GraphQL query
|
|
52
|
+
*
|
|
53
|
+
* @param query - GraphQL query string
|
|
54
|
+
* @param variables - Query variables
|
|
55
|
+
* @returns GraphQL response data
|
|
56
|
+
*/
|
|
57
|
+
query<T>(query: string, variables?: Record<string, unknown>): Promise<T>;
|
|
58
|
+
/**
|
|
59
|
+
* Execute a GraphQL mutation
|
|
60
|
+
*
|
|
61
|
+
* @param mutation - GraphQL mutation string
|
|
62
|
+
* @param variables - Mutation variables
|
|
63
|
+
* @returns GraphQL response data
|
|
64
|
+
*/
|
|
65
|
+
mutate<T>(mutation: string, variables?: Record<string, unknown>): Promise<T>;
|
|
66
|
+
/**
|
|
67
|
+
* Helper to check if GraphQL response has errors
|
|
68
|
+
*/
|
|
69
|
+
private hasGraphQLErrors;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=graphql_client.d.ts.map
|