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.
Files changed (76) hide show
  1. package/.devcontainer/.uix-hb-service-homebridge-startup.json.example +5 -0
  2. package/.devcontainer/README.md +211 -0
  3. package/.devcontainer/config.json.example +28 -0
  4. package/.devcontainer/devcontainer.json +55 -0
  5. package/.devcontainer/setup-homebridge.sh +51 -0
  6. package/.devcontainer/start-homebridge.sh +29 -0
  7. package/.devcontainer/stop-homebridge.sh +7 -0
  8. package/CLAUDE.md +186 -0
  9. package/dist/accessory_base.d.ts +2 -2
  10. package/dist/accessory_base.d.ts.map +1 -1
  11. package/dist/accessory_base.js.map +1 -1
  12. package/dist/api/graphql_client.d.ts +71 -0
  13. package/dist/api/graphql_client.d.ts.map +1 -0
  14. package/dist/api/graphql_client.js +261 -0
  15. package/dist/api/graphql_client.js.map +1 -0
  16. package/dist/api/graphql_operations.d.ts +56 -0
  17. package/dist/api/graphql_operations.d.ts.map +1 -0
  18. package/dist/api/graphql_operations.js +325 -0
  19. package/dist/api/graphql_operations.js.map +1 -0
  20. package/dist/api/interface_graphql_mutations.d.ts +149 -0
  21. package/dist/api/interface_graphql_mutations.d.ts.map +1 -0
  22. package/dist/api/interface_graphql_mutations.js +8 -0
  23. package/dist/api/interface_graphql_mutations.js.map +1 -0
  24. package/dist/api/interface_graphql_system.d.ts +287 -0
  25. package/dist/api/interface_graphql_system.d.ts.map +1 -0
  26. package/dist/api/interface_graphql_system.js +9 -0
  27. package/dist/api/interface_graphql_system.js.map +1 -0
  28. package/dist/api/models_graphql.d.ts +167 -0
  29. package/dist/api/models_graphql.d.ts.map +1 -0
  30. package/dist/api/models_graphql.js +590 -0
  31. package/dist/api/models_graphql.js.map +1 -0
  32. package/dist/api/oauth2.d.ts +45 -0
  33. package/dist/api/oauth2.d.ts.map +1 -0
  34. package/dist/api/oauth2.js +35 -0
  35. package/dist/api/oauth2.js.map +1 -0
  36. package/dist/characteristics_base.d.ts +2 -2
  37. package/dist/characteristics_base.d.ts.map +1 -1
  38. package/dist/characteristics_base.js.map +1 -1
  39. package/dist/platform.d.ts +4 -4
  40. package/dist/platform.d.ts.map +1 -1
  41. package/dist/platform.js +7 -12
  42. package/dist/platform.js.map +1 -1
  43. package/dist/settings.d.ts +6 -3
  44. package/dist/settings.d.ts.map +1 -1
  45. package/dist/settings.js +7 -4
  46. package/dist/settings.js.map +1 -1
  47. package/eslint.config.mjs +172 -0
  48. package/package.json +24 -26
  49. package/dist/api/interface_config.d.ts +0 -185
  50. package/dist/api/interface_config.d.ts.map +0 -1
  51. package/dist/api/interface_config.js +0 -5
  52. package/dist/api/interface_config.js.map +0 -1
  53. package/dist/api/interface_locations.d.ts +0 -45
  54. package/dist/api/interface_locations.d.ts.map +0 -1
  55. package/dist/api/interface_locations.js +0 -5
  56. package/dist/api/interface_locations.js.map +0 -1
  57. package/dist/api/interface_profile.d.ts +0 -80
  58. package/dist/api/interface_profile.d.ts.map +0 -1
  59. package/dist/api/interface_profile.js +0 -5
  60. package/dist/api/interface_profile.js.map +0 -1
  61. package/dist/api/interface_status.d.ts +0 -90
  62. package/dist/api/interface_status.d.ts.map +0 -1
  63. package/dist/api/interface_status.js +0 -5
  64. package/dist/api/interface_status.js.map +0 -1
  65. package/dist/api/models.d.ts +0 -111
  66. package/dist/api/models.d.ts.map +0 -1
  67. package/dist/api/models.js +0 -572
  68. package/dist/api/models.js.map +0 -1
  69. package/dist/api/oauth.d.ts +0 -6
  70. package/dist/api/oauth.d.ts.map +0 -1
  71. package/dist/api/oauth.js +0 -49
  72. package/dist/api/oauth.js.map +0 -1
  73. package/dist/api/rest_client.d.ts +0 -15
  74. package/dist/api/rest_client.d.ts.map +0 -1
  75. package/dist/api/rest_client.js +0 -120
  76. package/dist/api/rest_client.js.map +0 -1
@@ -0,0 +1,5 @@
1
+ {
2
+ "debugMode": true,
3
+ "insecureMode": true,
4
+ "env": {}
5
+ }
@@ -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
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ # Stop Homebridge managed by pm2
3
+
4
+ echo "Stopping Homebridge..."
5
+ pm2 delete homebridge
6
+
7
+ echo "✅ Homebridge stopped"
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)
@@ -1,11 +1,11 @@
1
1
  import { Logger, PlatformAccessory, Service, WithUUID } from 'homebridge';
2
- import { SystemModel } from './api/models';
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: SystemModel;
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,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,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,WAAW,CAAoD;IAC1F,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
+ {"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,GAAgB,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QACvE,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"}
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