@homebridge-plugins/homebridge-firstalert 0.0.1-beta.1 → 0.0.1-beta.3
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/API/README.md +310 -0
- package/API/RESIDEO_API.md +377 -0
- package/API/resideo_firstalert/__init__.py +75 -0
- package/API/resideo_firstalert/api.py +321 -0
- package/API/resideo_firstalert/application_credentials.py +122 -0
- package/API/resideo_firstalert/auth.py +336 -0
- package/API/resideo_firstalert/binary_sensor.py +245 -0
- package/API/resideo_firstalert/config_flow.py +429 -0
- package/API/resideo_firstalert/const.py +48 -0
- package/API/resideo_firstalert/coordinator.py +53 -0
- package/API/resideo_firstalert/icon.png +0 -0
- package/API/resideo_firstalert/manifest.json +13 -0
- package/API/resideo_firstalert/sensor.py +284 -0
- package/API/resideo_firstalert/strings.json +265 -0
- package/API/resideo_firstalert/translations/en.json +265 -0
- package/RESIDEO_API_REFERENCE.md +227 -0
- package/branding/icon.png +0 -0
- package/docs/variables/default.html +1 -1
- package/package.json +5 -5
- package/src/homebridge-ui/public/index.html +119 -443
- package/src/homebridge-ui/server.ts +323 -5
- package/dist/homebridge-ui/public/index.html +0 -548
- package/dist/src/api/resideoClient.d.ts +0 -32
- package/dist/src/api/resideoClient.d.ts.map +0 -1
- package/dist/src/api/resideoClient.js +0 -76
- package/dist/src/api/resideoClient.js.map +0 -1
- package/dist/src/devices/device.d.ts +0 -40
- package/dist/src/devices/device.d.ts.map +0 -1
- package/dist/src/devices/device.js +0 -207
- package/dist/src/devices/device.js.map +0 -1
- package/dist/src/devices/leaksensors.d.ts +0 -34
- package/dist/src/devices/leaksensors.d.ts.map +0 -1
- package/dist/src/devices/leaksensors.js +0 -163
- package/dist/src/devices/leaksensors.js.map +0 -1
- package/dist/src/devices/smoke.d.ts +0 -35
- package/dist/src/devices/smoke.d.ts.map +0 -1
- package/dist/src/devices/smoke.js +0 -150
- package/dist/src/devices/smoke.js.map +0 -1
- package/dist/src/devices/thermostats.d.ts +0 -42
- package/dist/src/devices/thermostats.d.ts.map +0 -1
- package/dist/src/devices/thermostats.js +0 -192
- package/dist/src/devices/thermostats.js.map +0 -1
- package/dist/src/devices/valve.d.ts +0 -21
- package/dist/src/devices/valve.d.ts.map +0 -1
- package/dist/src/devices/valve.js +0 -131
- package/dist/src/devices/valve.js.map +0 -1
- package/dist/src/homebridge-ui/server.d.ts +0 -5
- package/dist/src/homebridge-ui/server.d.ts.map +0 -1
- package/dist/src/homebridge-ui/server.js +0 -95
- package/dist/src/homebridge-ui/server.js.map +0 -1
- package/dist/src/index.d.ts +0 -4
- package/dist/src/index.d.ts.map +0 -1
- package/dist/src/index.js +0 -7
- package/dist/src/index.js.map +0 -1
- package/dist/src/platform.d.ts +0 -18
- package/dist/src/platform.d.ts.map +0 -1
- package/dist/src/platform.js +0 -108
- package/dist/src/platform.js.map +0 -1
- package/dist/src/settings.d.ts +0 -341
- package/dist/src/settings.d.ts.map +0 -1
- package/dist/src/settings.js +0 -25
- package/dist/src/settings.js.map +0 -1
- package/dist/src/utils.d.ts +0 -21
- package/dist/src/utils.d.ts.map +0 -1
- package/dist/src/utils.js +0 -58
- package/dist/src/utils.js.map +0 -1
- package/dist/test/index.test.d.ts +0 -2
- package/dist/test/index.test.d.ts.map +0 -1
- package/dist/test/index.test.js +0 -14
- package/dist/test/index.test.js.map +0 -1
- package/dist/test/platform.test.d.ts +0 -2
- package/dist/test/platform.test.d.ts.map +0 -1
- package/dist/test/platform.test.js +0 -56
- package/dist/test/platform.test.js.map +0 -1
- package/dist/test/settings.test.d.ts +0 -2
- package/dist/test/settings.test.d.ts.map +0 -1
- package/dist/test/settings.test.js +0 -48
- package/dist/test/settings.test.js.map +0 -1
- package/dist/test/utils.test.d.ts +0 -2
- package/dist/test/utils.test.d.ts.map +0 -1
- package/dist/test/utils.test.js +0 -17
- package/dist/test/utils.test.js.map +0 -1
package/API/README.md
ADDED
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
# First Alert by Resideo - Home Assistant Integration
|
|
2
|
+
|
|
3
|
+
A custom Home Assistant integration for First Alert Safe & Sound smoke/CO detectors connected via the Resideo platform.
|
|
4
|
+
|
|
5
|
+
## Supported Devices
|
|
6
|
+
|
|
7
|
+
- First Alert Safe & Sound Smart Smoke/CO Alarm (SMCO600NVACA)
|
|
8
|
+
- Other Resideo-connected First Alert devices may also work
|
|
9
|
+
|
|
10
|
+
## Features
|
|
11
|
+
|
|
12
|
+
- **Easy Setup** - Login with your Resideo email and password directly
|
|
13
|
+
- **Smoke Alarm Detection** - Binary sensor that triggers when smoke is detected
|
|
14
|
+
- **CO Alarm Detection** - Binary sensor that triggers when carbon monoxide is detected
|
|
15
|
+
- **Battery Monitoring** - Track battery status and get low battery alerts
|
|
16
|
+
- **Power Source** - See if device is on AC or battery power
|
|
17
|
+
- **Connectivity Status** - Know if your detector is online
|
|
18
|
+
- **Malfunction Detection** - Get alerts if the device has issues
|
|
19
|
+
- **Test Mode & Silence Status** - Monitor when detectors are in test mode or silenced
|
|
20
|
+
- **Early Warning** - Track early warning feature status
|
|
21
|
+
- **End of Life Alerts** - Know when your detector needs replacement
|
|
22
|
+
- **Comprehensive Fault Detection** - Monitor various fault conditions
|
|
23
|
+
- **Configurable Polling** - Adjust update interval from 5 seconds to 1 hour
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
### HACS (Recommended)
|
|
28
|
+
|
|
29
|
+
1. Open HACS in Home Assistant
|
|
30
|
+
2. Click the three dots menu → Custom repositories
|
|
31
|
+
3. Add `https://github.com/aidenmitchell/ha-resideo-firstalert` with category "Integration"
|
|
32
|
+
4. Search for "First Alert by Resideo" and install
|
|
33
|
+
5. Restart Home Assistant
|
|
34
|
+
|
|
35
|
+
### Manual Installation
|
|
36
|
+
|
|
37
|
+
1. Copy the `custom_components/resideo_firstalert` folder to your Home Assistant `config/custom_components/` directory
|
|
38
|
+
2. Restart Home Assistant
|
|
39
|
+
|
|
40
|
+
## Configuration
|
|
41
|
+
|
|
42
|
+
### Authentication
|
|
43
|
+
|
|
44
|
+
When adding the integration, you have two options:
|
|
45
|
+
|
|
46
|
+
#### Option 1: Login with Email & Password (Recommended)
|
|
47
|
+
|
|
48
|
+
1. Go to **Settings** → **Devices & Services** → **Add Integration**
|
|
49
|
+
2. Search for "First Alert by Resideo"
|
|
50
|
+
3. Select **"Login with email and password"**
|
|
51
|
+
4. Enter your Resideo account credentials (same as the First Alert app)
|
|
52
|
+
5. Your devices will be automatically discovered
|
|
53
|
+
|
|
54
|
+
#### Option 2: Manual Token Entry
|
|
55
|
+
|
|
56
|
+
If you prefer, you can manually obtain and enter a refresh token:
|
|
57
|
+
|
|
58
|
+
1. **Install a network proxy** like [Proxyman](https://proxyman.io/) (macOS/iOS) or [mitmproxy](https://mitmproxy.org/)
|
|
59
|
+
|
|
60
|
+
2. **Configure SSL interception** for `login.resideo.com`
|
|
61
|
+
|
|
62
|
+
3. **Log into the First Alert app** on your phone while capturing traffic
|
|
63
|
+
|
|
64
|
+
4. **Look for the request** to `POST https://login.resideo.com/oauth/token`
|
|
65
|
+
|
|
66
|
+
5. **Find the `refresh_token`** in the response JSON:
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"access_token": "...",
|
|
70
|
+
"refresh_token": "THIS_IS_YOUR_TOKEN",
|
|
71
|
+
"expires_in": 3600,
|
|
72
|
+
"token_type": "Bearer"
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
6. In Home Assistant, select **"Enter refresh token manually"** and paste your token
|
|
77
|
+
|
|
78
|
+
### Options
|
|
79
|
+
|
|
80
|
+
After setup, you can configure the integration via **Settings** → **Devices & Services** → **First Alert by Resideo** → **Configure**:
|
|
81
|
+
|
|
82
|
+
- **Settings** - Adjust the polling interval (5-3600 seconds, default: 60)
|
|
83
|
+
- **Update refresh token** - Enter a new token if needed without recreating the integration
|
|
84
|
+
|
|
85
|
+
## Entities Created
|
|
86
|
+
|
|
87
|
+
For each smoke detector, the following entities are created:
|
|
88
|
+
|
|
89
|
+
### Binary Sensors
|
|
90
|
+
|
|
91
|
+
| Entity | Description | Device Class | Default |
|
|
92
|
+
|--------|-------------|--------------|---------|
|
|
93
|
+
| Smoke Alarm | On when smoke is detected | `smoke` | Enabled |
|
|
94
|
+
| CO Alarm | On when CO is detected | `co` | Enabled |
|
|
95
|
+
| Malfunction | On when device has a problem | `problem` | Enabled |
|
|
96
|
+
| Connectivity | On when device is online | `connectivity` | Enabled |
|
|
97
|
+
| Battery Low | On when battery is low | `battery` | Enabled |
|
|
98
|
+
| Test Mode | On when device is in test mode | `running` | Enabled |
|
|
99
|
+
| Silenced | On when alarm is silenced | `running` | Enabled |
|
|
100
|
+
| End of Life | On when device needs replacement | `problem` | Enabled |
|
|
101
|
+
| Early Warning | On when early warning is enabled | - | Enabled |
|
|
102
|
+
| Supervision Healthy | On when supervision is healthy | - | Disabled |
|
|
103
|
+
| General Fault | On when general fault detected | `problem` | Disabled |
|
|
104
|
+
| E2 Fault | On when E2 fault detected | `problem` | Disabled |
|
|
105
|
+
| Photo Sensor Fault | On when photo sensor fault detected | `problem` | Disabled |
|
|
106
|
+
| Drift Malfunction | On when drift malfunction detected | `problem` | Disabled |
|
|
107
|
+
| CO Sensor Fault | On when CO sensor fault detected | `problem` | Disabled |
|
|
108
|
+
| Temperature Sensor Fault | On when temp sensor fault detected | `problem` | Disabled |
|
|
109
|
+
| Voice Module Fault | On when voice module fault detected | `problem` | Disabled |
|
|
110
|
+
| Radio Fault | On when radio fault detected | `problem` | Disabled |
|
|
111
|
+
|
|
112
|
+
### Sensors
|
|
113
|
+
|
|
114
|
+
| Entity | Description | Default |
|
|
115
|
+
|--------|-------------|---------|
|
|
116
|
+
| Battery Status | `good` or `low` | Enabled |
|
|
117
|
+
| Power Source | `ac` or `battery` | Enabled |
|
|
118
|
+
| Smoke Status | `idle` or `alarm` | Enabled |
|
|
119
|
+
| CO Status | `idle` or `alarm` | Enabled |
|
|
120
|
+
| Test Status | `idle` or `testing` | Enabled |
|
|
121
|
+
| Silence Status | `not_silenced` or `silenced` | Enabled |
|
|
122
|
+
| End of Life Status | `no` or `yes` | Enabled |
|
|
123
|
+
| Language | Device language setting | Enabled |
|
|
124
|
+
| Room | Room number setting | Disabled |
|
|
125
|
+
| WiFi Signal Strength | Signal strength in dBm | Disabled |
|
|
126
|
+
| WiFi Network | Connected SSID | Disabled |
|
|
127
|
+
| Last Seen | Timestamp of last communication | Disabled |
|
|
128
|
+
| Firmware Version | Device firmware | Disabled |
|
|
129
|
+
| Firmware (Exec Core) | Exec core firmware version | Disabled |
|
|
130
|
+
| Firmware (Sensor Core) | Sensor core firmware version | Disabled |
|
|
131
|
+
| Hardware Version (E2C) | E2C hardware version | Disabled |
|
|
132
|
+
| Hardware Version (Exec Core) | Exec core hardware version | Disabled |
|
|
133
|
+
| Hardware Version (Sensor Core) | Sensor core hardware version | Disabled |
|
|
134
|
+
| Voice File Version | Voice file version | Disabled |
|
|
135
|
+
| Running Hours | Total running hours | Disabled |
|
|
136
|
+
| Registration Date | When device was registered | Disabled |
|
|
137
|
+
| Last Firmware Update | Last firmware update timestamp | Disabled |
|
|
138
|
+
|
|
139
|
+
## Example Automations
|
|
140
|
+
|
|
141
|
+
### Alert on Smoke Detection
|
|
142
|
+
```yaml
|
|
143
|
+
automation:
|
|
144
|
+
- alias: "Smoke Alarm Alert"
|
|
145
|
+
trigger:
|
|
146
|
+
- platform: state
|
|
147
|
+
entity_id: binary_sensor.living_room_detector_smoke_alarm
|
|
148
|
+
to: "on"
|
|
149
|
+
action:
|
|
150
|
+
- service: notify.mobile_app
|
|
151
|
+
data:
|
|
152
|
+
title: "SMOKE DETECTED!"
|
|
153
|
+
message: "Smoke alarm triggered in Living Room"
|
|
154
|
+
data:
|
|
155
|
+
priority: high
|
|
156
|
+
ttl: 0
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Alert on CO Detection
|
|
160
|
+
```yaml
|
|
161
|
+
automation:
|
|
162
|
+
- alias: "CO Alarm Alert"
|
|
163
|
+
trigger:
|
|
164
|
+
- platform: state
|
|
165
|
+
entity_id: binary_sensor.living_room_detector_co_alarm
|
|
166
|
+
to: "on"
|
|
167
|
+
action:
|
|
168
|
+
- service: notify.mobile_app
|
|
169
|
+
data:
|
|
170
|
+
title: "CARBON MONOXIDE DETECTED!"
|
|
171
|
+
message: "CO alarm triggered - evacuate immediately!"
|
|
172
|
+
data:
|
|
173
|
+
priority: high
|
|
174
|
+
ttl: 0
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Low Battery Alert
|
|
178
|
+
```yaml
|
|
179
|
+
automation:
|
|
180
|
+
- alias: "Smoke Detector Low Battery"
|
|
181
|
+
trigger:
|
|
182
|
+
- platform: state
|
|
183
|
+
entity_id: binary_sensor.living_room_detector_battery_low
|
|
184
|
+
to: "on"
|
|
185
|
+
action:
|
|
186
|
+
- service: notify.mobile_app
|
|
187
|
+
data:
|
|
188
|
+
title: "Low Battery"
|
|
189
|
+
message: "Living Room smoke detector battery is low"
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### End of Life Alert
|
|
193
|
+
```yaml
|
|
194
|
+
automation:
|
|
195
|
+
- alias: "Smoke Detector End of Life"
|
|
196
|
+
trigger:
|
|
197
|
+
- platform: state
|
|
198
|
+
entity_id: binary_sensor.living_room_detector_end_of_life
|
|
199
|
+
to: "on"
|
|
200
|
+
action:
|
|
201
|
+
- service: notify.mobile_app
|
|
202
|
+
data:
|
|
203
|
+
title: "Detector Replacement Needed"
|
|
204
|
+
message: "Living Room smoke detector has reached end of life and should be replaced"
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## Troubleshooting
|
|
208
|
+
|
|
209
|
+
### "Invalid email or password" error
|
|
210
|
+
Double-check your credentials. These are the same as your First Alert / Resideo app login.
|
|
211
|
+
|
|
212
|
+
### "Authentication failed" error
|
|
213
|
+
Your refresh token may have expired. Use the **Configure** option to update your token, or re-authenticate with email/password.
|
|
214
|
+
|
|
215
|
+
### "Unable to connect" error
|
|
216
|
+
Check your internet connection and verify the Resideo API is accessible.
|
|
217
|
+
|
|
218
|
+
### Devices not showing
|
|
219
|
+
Make sure your devices are properly set up in the First Alert app and are online.
|
|
220
|
+
|
|
221
|
+
### Token Expiration
|
|
222
|
+
- **Access tokens** expire hourly and are automatically refreshed
|
|
223
|
+
- **Refresh tokens** expire after ~30 days. When this happens, Home Assistant will prompt you to re-authenticate
|
|
224
|
+
|
|
225
|
+
## Removing the Integration
|
|
226
|
+
|
|
227
|
+
To remove the First Alert by Resideo integration:
|
|
228
|
+
|
|
229
|
+
1. Go to **Settings** → **Devices & Services**
|
|
230
|
+
2. Find "First Alert by Resideo" and click on it
|
|
231
|
+
3. Click the three-dot menu → **Delete**
|
|
232
|
+
4. Confirm the deletion
|
|
233
|
+
|
|
234
|
+
All entities and device data will be removed. No additional cleanup is required.
|
|
235
|
+
|
|
236
|
+
## Technical Details
|
|
237
|
+
|
|
238
|
+
- **Polling Interval**: 60 seconds (configurable from 5-3600 seconds)
|
|
239
|
+
- **API Base URL**: `https://api.resideo.com`
|
|
240
|
+
- **Authentication**: OAuth 2.0 with PKCE via Auth0
|
|
241
|
+
|
|
242
|
+
## Privacy Note
|
|
243
|
+
|
|
244
|
+
This integration communicates with Resideo's cloud servers. Your device data passes through their infrastructure. The integration stores only the refresh token locally - your email and password are not stored.
|
|
245
|
+
|
|
246
|
+
## License
|
|
247
|
+
|
|
248
|
+
MIT License - See LICENSE file for details.
|
|
249
|
+
|
|
250
|
+
## Local Development
|
|
251
|
+
|
|
252
|
+
### Prerequisites
|
|
253
|
+
|
|
254
|
+
- Docker and Docker Compose
|
|
255
|
+
- A Resideo account with First Alert devices
|
|
256
|
+
|
|
257
|
+
### Quick Start
|
|
258
|
+
|
|
259
|
+
1. Clone the repository:
|
|
260
|
+
```bash
|
|
261
|
+
git clone https://github.com/aidenmitchell/ha-resideo-firstalert.git
|
|
262
|
+
cd ha-resideo-firstalert
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
2. Create the config file:
|
|
266
|
+
```bash
|
|
267
|
+
cp config/configuration.yaml.example config/configuration.yaml
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
3. Start Home Assistant:
|
|
271
|
+
```bash
|
|
272
|
+
docker compose up -d
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
4. Open http://localhost:8123 in your browser
|
|
276
|
+
|
|
277
|
+
5. Complete the Home Assistant onboarding, then add the integration:
|
|
278
|
+
- Settings → Devices & Services → Add Integration
|
|
279
|
+
- Search "First Alert by Resideo"
|
|
280
|
+
- Login with your email and password
|
|
281
|
+
|
|
282
|
+
### Development Workflow
|
|
283
|
+
|
|
284
|
+
The `custom_components` folder is mounted directly into the container, so changes to the integration code take effect after restarting Home Assistant:
|
|
285
|
+
|
|
286
|
+
```bash
|
|
287
|
+
# Restart to pick up code changes
|
|
288
|
+
docker compose restart
|
|
289
|
+
|
|
290
|
+
# View logs
|
|
291
|
+
docker compose logs -f homeassistant
|
|
292
|
+
|
|
293
|
+
# Stop
|
|
294
|
+
docker compose down
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### Debug Logging
|
|
298
|
+
|
|
299
|
+
The example configuration enables debug logging for the integration. View logs with:
|
|
300
|
+
```bash
|
|
301
|
+
docker compose logs -f homeassistant 2>&1 | grep resideo_firstalert
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
## Contributing
|
|
305
|
+
|
|
306
|
+
Contributions welcome! Please open an issue or PR on GitHub.
|
|
307
|
+
|
|
308
|
+
## Disclaimer
|
|
309
|
+
|
|
310
|
+
This is an unofficial integration and is not affiliated with, endorsed by, or supported by First Alert or Resideo. Use at your own risk.
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
# First Alert by Resideo API Documentation
|
|
2
|
+
|
|
3
|
+
This document describes the API used by the First Alert by Resideo mobile app to communicate with smoke/CO detectors.
|
|
4
|
+
|
|
5
|
+
## Authentication
|
|
6
|
+
|
|
7
|
+
The API uses **OAuth 2.0 with PKCE** via **Auth0**.
|
|
8
|
+
|
|
9
|
+
### OAuth Configuration
|
|
10
|
+
|
|
11
|
+
| Parameter | Value |
|
|
12
|
+
|-----------|-------|
|
|
13
|
+
| Auth Domain | `login.resideo.com` |
|
|
14
|
+
| Client ID | `SRmiA7CaYi1JgivDZdzzoZu4X5VBogGt` |
|
|
15
|
+
| Audience | `https://resideo-prod.auth0.com/api/v2/` |
|
|
16
|
+
| Scopes | `openid profile email offline_access` |
|
|
17
|
+
|
|
18
|
+
### Token Refresh
|
|
19
|
+
|
|
20
|
+
Access tokens expire after 1 hour. Use the refresh token to get new access tokens:
|
|
21
|
+
|
|
22
|
+
```http
|
|
23
|
+
POST https://login.resideo.com/oauth/token
|
|
24
|
+
Content-Type: application/json
|
|
25
|
+
|
|
26
|
+
{
|
|
27
|
+
"grant_type": "refresh_token",
|
|
28
|
+
"refresh_token": "<refresh_token>",
|
|
29
|
+
"client_id": "SRmiA7CaYi1JgivDZdzzoZu4X5VBogGt"
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Response:**
|
|
34
|
+
```json
|
|
35
|
+
{
|
|
36
|
+
"access_token": "eyJ...",
|
|
37
|
+
"id_token": "eyJ...",
|
|
38
|
+
"scope": "openid profile email offline_access",
|
|
39
|
+
"expires_in": 3600,
|
|
40
|
+
"token_type": "Bearer"
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## API Endpoints
|
|
47
|
+
|
|
48
|
+
Base URL: `https://api.resideo.com`
|
|
49
|
+
|
|
50
|
+
All requests require:
|
|
51
|
+
```http
|
|
52
|
+
Authorization: Bearer <access_token>
|
|
53
|
+
Content-Type: application/json
|
|
54
|
+
Accept: application/json
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Get Account Information
|
|
58
|
+
|
|
59
|
+
```http
|
|
60
|
+
GET /ris-public-api/api/v1/accounts
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Response:**
|
|
64
|
+
```json
|
|
65
|
+
{
|
|
66
|
+
"data": {
|
|
67
|
+
"id": "VXNlcjow...",
|
|
68
|
+
"firstName": "John",
|
|
69
|
+
"lastName": "Doe",
|
|
70
|
+
"contactEmail": "user@example.com",
|
|
71
|
+
"countryCode": "US",
|
|
72
|
+
"locale": "en_US",
|
|
73
|
+
"consumerUsers": [
|
|
74
|
+
{
|
|
75
|
+
"id": "Q29uc3VtZXJVc2VyOj...",
|
|
76
|
+
"role": "ADMIN",
|
|
77
|
+
"consumerAccount": {
|
|
78
|
+
"id": "Q29uc3VtZXJBY2NvdW50Oj...",
|
|
79
|
+
"locations": [
|
|
80
|
+
{
|
|
81
|
+
"id": "Q29uc3VtZXJEZXZpY2VMb2NhdGlvbjo...",
|
|
82
|
+
"name": "Home",
|
|
83
|
+
"address": {
|
|
84
|
+
"addressLine1": "123 Main St",
|
|
85
|
+
"city": "Anytown",
|
|
86
|
+
"stateProvinceRegionCode": "CA",
|
|
87
|
+
"zipPostalCode": "90210",
|
|
88
|
+
"countryCode": "US"
|
|
89
|
+
},
|
|
90
|
+
"geoCoordinate": {
|
|
91
|
+
"latitude": 34.0901,
|
|
92
|
+
"longitude": -118.4065
|
|
93
|
+
},
|
|
94
|
+
"consumerDevices": [
|
|
95
|
+
{
|
|
96
|
+
"id": "Q29uc3VtZXJEZXZpY2U6...",
|
|
97
|
+
"name": "Living Room Detector",
|
|
98
|
+
"device": {
|
|
99
|
+
"id": "THlyaWNUaGVybW9zdGF0RGV2aWNlOj...",
|
|
100
|
+
"deviceId": "XXXXXXXXXXXX",
|
|
101
|
+
"globalDeviceType": "Citadel_SC5"
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
]
|
|
105
|
+
}
|
|
106
|
+
]
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
]
|
|
110
|
+
},
|
|
111
|
+
"errors": []
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Get Device State
|
|
116
|
+
|
|
117
|
+
```http
|
|
118
|
+
GET /ris-public-api/api/v2/devices/smokeDetectors/{deviceId}/state
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
**Example:** `GET /ris-public-api/api/v2/devices/smokeDetectors/XXXXXXXXXXXX/state`
|
|
122
|
+
|
|
123
|
+
**Response:**
|
|
124
|
+
```json
|
|
125
|
+
{
|
|
126
|
+
"name": "XXXXXXXXXXXX",
|
|
127
|
+
"deviceType": "SmokeDetector",
|
|
128
|
+
"sku": "SMCO600NVACA",
|
|
129
|
+
"registrationStatus": "Registered",
|
|
130
|
+
"isOnline": true,
|
|
131
|
+
"isSupervisionHealthy": true,
|
|
132
|
+
"isOnlineComputed": true,
|
|
133
|
+
"dataSyncState": "Completed",
|
|
134
|
+
"registrationDate": "2025-12-18T03:22:17.457+00:00",
|
|
135
|
+
"lastMessageReceivedTime": "2025-12-20T17:02:30.861+00:00",
|
|
136
|
+
"deviceState": {
|
|
137
|
+
"desired": { ... },
|
|
138
|
+
"reported": {
|
|
139
|
+
"alarmState": {
|
|
140
|
+
"co": {
|
|
141
|
+
"eventSource": "self",
|
|
142
|
+
"tStampEpoch": 1766247701,
|
|
143
|
+
"deviceState": "idle"
|
|
144
|
+
},
|
|
145
|
+
"smoke": {
|
|
146
|
+
"eventSource": "self",
|
|
147
|
+
"tStampEpoch": 1766247701,
|
|
148
|
+
"deviceState": "idle"
|
|
149
|
+
},
|
|
150
|
+
"test": {
|
|
151
|
+
"eventSource": "self",
|
|
152
|
+
"tStampEpoch": 1766034736,
|
|
153
|
+
"deviceState": "idle"
|
|
154
|
+
},
|
|
155
|
+
"malfunction": {
|
|
156
|
+
"eventSource": "self",
|
|
157
|
+
"tStampEpoch": 1766247701,
|
|
158
|
+
"deviceState": "none"
|
|
159
|
+
},
|
|
160
|
+
"battery": {
|
|
161
|
+
"eventSource": "self",
|
|
162
|
+
"tStampEpoch": 1766247701,
|
|
163
|
+
"deviceState": "good"
|
|
164
|
+
},
|
|
165
|
+
"eol": {
|
|
166
|
+
"eventSource": "self",
|
|
167
|
+
"tStampEpoch": 1766247704,
|
|
168
|
+
"deviceState": "no"
|
|
169
|
+
},
|
|
170
|
+
"power": {
|
|
171
|
+
"eventSource": "self",
|
|
172
|
+
"tStampEpoch": 1766029736,
|
|
173
|
+
"deviceState": "ac"
|
|
174
|
+
},
|
|
175
|
+
"silence": {
|
|
176
|
+
"eventSource": "self",
|
|
177
|
+
"tStampEpoch": 1766247701,
|
|
178
|
+
"deviceState": "not_silenced"
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
"deviceConfig": {
|
|
182
|
+
"language": "en_US",
|
|
183
|
+
"room": 14,
|
|
184
|
+
"debugLevel": "error",
|
|
185
|
+
"earlyWarning": true
|
|
186
|
+
},
|
|
187
|
+
"deviceInfo": {
|
|
188
|
+
"hwVerE2C": "1.0.0",
|
|
189
|
+
"hwVerExecCore": "1.0.0",
|
|
190
|
+
"hwVerSensorCore": "1.0.0",
|
|
191
|
+
"fwVerE2C": "00.07.72.00",
|
|
192
|
+
"fwVerExecCore": "01.06.38",
|
|
193
|
+
"fwVerSensorCore": "11.00",
|
|
194
|
+
"voiceFileVer": "1.0.0",
|
|
195
|
+
"runningHrs": 0
|
|
196
|
+
},
|
|
197
|
+
"deviceStatus": {
|
|
198
|
+
"rssi": -30,
|
|
199
|
+
"ssid": "WiFiNetwork"
|
|
200
|
+
},
|
|
201
|
+
"deviceStatusFlags": {
|
|
202
|
+
"fault": false,
|
|
203
|
+
"e2Fault": false,
|
|
204
|
+
"photoFault": false,
|
|
205
|
+
"driftMalfunction": false,
|
|
206
|
+
"coFault": false,
|
|
207
|
+
"temperatureFault": false,
|
|
208
|
+
"voiceFault": false,
|
|
209
|
+
"radioFault": false
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
"lastFirmwareUpdateTime": "2025-12-18T03:22:45.412+00:00"
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## Alarm State Values
|
|
220
|
+
|
|
221
|
+
### `alarmState.smoke.deviceState`
|
|
222
|
+
| Value | Description |
|
|
223
|
+
|-------|-------------|
|
|
224
|
+
| `idle` | Normal - no smoke detected |
|
|
225
|
+
| `alarm` | Smoke alarm active |
|
|
226
|
+
|
|
227
|
+
### `alarmState.co.deviceState`
|
|
228
|
+
| Value | Description |
|
|
229
|
+
|-------|-------------|
|
|
230
|
+
| `idle` | Normal - no CO detected |
|
|
231
|
+
| `alarm` | CO alarm active |
|
|
232
|
+
|
|
233
|
+
### `alarmState.battery.deviceState`
|
|
234
|
+
| Value | Description |
|
|
235
|
+
|-------|-------------|
|
|
236
|
+
| `good` | Battery healthy |
|
|
237
|
+
| `low` | Battery low (assumed) |
|
|
238
|
+
|
|
239
|
+
### `alarmState.power.deviceState`
|
|
240
|
+
| Value | Description |
|
|
241
|
+
|-------|-------------|
|
|
242
|
+
| `ac` | Running on AC power |
|
|
243
|
+
| `battery` | Running on battery (assumed) |
|
|
244
|
+
|
|
245
|
+
### `alarmState.malfunction.deviceState`
|
|
246
|
+
| Value | Description |
|
|
247
|
+
|-------|-------------|
|
|
248
|
+
| `none` | No malfunction |
|
|
249
|
+
| (other) | Device malfunction |
|
|
250
|
+
|
|
251
|
+
### `alarmState.silence.deviceState`
|
|
252
|
+
| Value | Description |
|
|
253
|
+
|-------|-------------|
|
|
254
|
+
| `not_silenced` | Alarm not silenced |
|
|
255
|
+
| `silenced` | Alarm temporarily silenced (assumed) |
|
|
256
|
+
|
|
257
|
+
### `alarmState.eol.deviceState`
|
|
258
|
+
| Value | Description |
|
|
259
|
+
|-------|-------------|
|
|
260
|
+
| `no` | Not at end of life |
|
|
261
|
+
| `yes` | End of life - replace device (assumed) |
|
|
262
|
+
|
|
263
|
+
### `alarmState.test.deviceState`
|
|
264
|
+
| Value | Description |
|
|
265
|
+
|-------|-------------|
|
|
266
|
+
| `idle` | Not in test mode |
|
|
267
|
+
| `testing` | Test in progress (assumed) |
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## Other Endpoints (Discovered)
|
|
272
|
+
|
|
273
|
+
```http
|
|
274
|
+
GET /ris-public-api/api/v1/geofence
|
|
275
|
+
POST /ds-activity-feed-api/api/v1/app/events
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
## Device Types
|
|
281
|
+
|
|
282
|
+
| `globalDeviceType` | Description |
|
|
283
|
+
|-------------------|-------------|
|
|
284
|
+
| `Citadel_SC5` | First Alert Safe & Sound Smart Smoke/CO Alarm (SMCO600NVACA) |
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## Home Assistant Integration Notes
|
|
289
|
+
|
|
290
|
+
### Sensors to Expose
|
|
291
|
+
|
|
292
|
+
1. **Binary Sensors:**
|
|
293
|
+
- Smoke Alarm (`alarmState.smoke.deviceState` != "idle")
|
|
294
|
+
- CO Alarm (`alarmState.co.deviceState` != "idle")
|
|
295
|
+
- Malfunction (`alarmState.malfunction.deviceState` != "none")
|
|
296
|
+
- Online Status (`isOnline`)
|
|
297
|
+
|
|
298
|
+
2. **Sensors:**
|
|
299
|
+
- Battery Status (`alarmState.battery.deviceState`)
|
|
300
|
+
- Power Source (`alarmState.power.deviceState`)
|
|
301
|
+
- WiFi Signal Strength (`deviceStatus.rssi`)
|
|
302
|
+
- Last Message Time (`lastMessageReceivedTime`)
|
|
303
|
+
|
|
304
|
+
3. **Diagnostic Sensors:**
|
|
305
|
+
- Firmware versions
|
|
306
|
+
- End of Life status
|
|
307
|
+
- Various fault flags
|
|
308
|
+
|
|
309
|
+
### Polling Interval
|
|
310
|
+
|
|
311
|
+
Recommend polling every 30-60 seconds. The device reports timestamps in `tStampEpoch` format.
|
|
312
|
+
|
|
313
|
+
### OAuth Flow for Home Assistant
|
|
314
|
+
|
|
315
|
+
For Home Assistant, you'll need to implement the full OAuth PKCE flow:
|
|
316
|
+
1. Generate code_verifier and code_challenge
|
|
317
|
+
2. Open browser to authorization URL
|
|
318
|
+
3. Handle callback with authorization code
|
|
319
|
+
4. Exchange code for tokens
|
|
320
|
+
5. Store and refresh tokens as needed
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
## Example Python Client
|
|
325
|
+
|
|
326
|
+
```python
|
|
327
|
+
import requests
|
|
328
|
+
|
|
329
|
+
class ResideoClient:
|
|
330
|
+
def __init__(self, refresh_token: str):
|
|
331
|
+
self.client_id = "SRmiA7CaYi1JgivDZdzzoZu4X5VBogGt"
|
|
332
|
+
self.refresh_token = refresh_token
|
|
333
|
+
self.access_token = None
|
|
334
|
+
|
|
335
|
+
def _refresh_access_token(self):
|
|
336
|
+
resp = requests.post(
|
|
337
|
+
"https://login.resideo.com/oauth/token",
|
|
338
|
+
json={
|
|
339
|
+
"grant_type": "refresh_token",
|
|
340
|
+
"refresh_token": self.refresh_token,
|
|
341
|
+
"client_id": self.client_id
|
|
342
|
+
}
|
|
343
|
+
)
|
|
344
|
+
data = resp.json()
|
|
345
|
+
self.access_token = data["access_token"]
|
|
346
|
+
return self.access_token
|
|
347
|
+
|
|
348
|
+
def _headers(self):
|
|
349
|
+
if not self.access_token:
|
|
350
|
+
self._refresh_access_token()
|
|
351
|
+
return {
|
|
352
|
+
"Authorization": f"Bearer {self.access_token}",
|
|
353
|
+
"Content-Type": "application/json"
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
def get_accounts(self):
|
|
357
|
+
resp = requests.get(
|
|
358
|
+
"https://api.resideo.com/ris-public-api/api/v1/accounts",
|
|
359
|
+
headers=self._headers()
|
|
360
|
+
)
|
|
361
|
+
return resp.json()
|
|
362
|
+
|
|
363
|
+
def get_device_state(self, device_id: str):
|
|
364
|
+
resp = requests.get(
|
|
365
|
+
f"https://api.resideo.com/ris-public-api/api/v2/devices/smokeDetectors/{device_id}/state",
|
|
366
|
+
headers=self._headers()
|
|
367
|
+
)
|
|
368
|
+
return resp.json()
|
|
369
|
+
|
|
370
|
+
# Usage
|
|
371
|
+
client = ResideoClient(refresh_token="your_refresh_token")
|
|
372
|
+
accounts = client.get_accounts()
|
|
373
|
+
state = client.get_device_state("YOUR_DEVICE_ID")
|
|
374
|
+
print(f"Smoke: {state['deviceState']['reported']['alarmState']['smoke']['deviceState']}")
|
|
375
|
+
print(f"CO: {state['deviceState']['reported']['alarmState']['co']['deviceState']}")
|
|
376
|
+
print(f"Battery: {state['deviceState']['reported']['alarmState']['battery']['deviceState']}")
|
|
377
|
+
```
|