homebridge-legrand-radiant 0.1.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/LICENSE +21 -0
- package/README.md +185 -0
- package/config.schema.json +127 -0
- package/dist/cloudAccessory.d.ts +35 -0
- package/dist/cloudAccessory.js +133 -0
- package/dist/cloudPlatform.d.ts +50 -0
- package/dist/cloudPlatform.js +248 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +6 -0
- package/dist/legrandAuth.d.ts +80 -0
- package/dist/legrandAuth.js +431 -0
- package/dist/legrandCloudApi.d.ts +109 -0
- package/dist/legrandCloudApi.js +349 -0
- package/dist/settings.d.ts +8 -0
- package/dist/settings.js +11 -0
- package/package.json +47 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# Homebridge Legrand Radiant
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/homebridge-legrand-radiant)
|
|
4
|
+
[](https://github.com/yourusername/homebridge-legrand-radiant/blob/main/LICENSE)
|
|
5
|
+
|
|
6
|
+
Homebridge plugin to control **Legrand Radiant WiFi smart switches and dimmers** via the Legrand cloud API.
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- 🔌 Control Legrand Radiant WiFi switches and dimmers via HomeKit
|
|
11
|
+
- 🔄 Automatic device discovery - no manual configuration needed
|
|
12
|
+
- 🔐 Secure OAuth2 authentication with automatic token refresh
|
|
13
|
+
- 📡 Real-time status updates
|
|
14
|
+
- 💡 Support for both switches and dimmers
|
|
15
|
+
|
|
16
|
+
## Supported Devices
|
|
17
|
+
|
|
18
|
+
This plugin works with Legrand Radiant WiFi smart devices that use the **Legrand Smart Lights** app, including:
|
|
19
|
+
|
|
20
|
+
- Radiant Smart WiFi Switch
|
|
21
|
+
- Radiant Smart WiFi Dimmer
|
|
22
|
+
- Other WiFi-enabled devices using the Legrand Smart Lights app
|
|
23
|
+
|
|
24
|
+
> **Note:** This plugin is for WiFi-based switches that use the cloud API. It does **not** support RF-based switches that require the LC7001 hub.
|
|
25
|
+
|
|
26
|
+
## Requirements
|
|
27
|
+
|
|
28
|
+
- [Homebridge](https://homebridge.io/) v1.6.0 or later
|
|
29
|
+
- Node.js v18 or later
|
|
30
|
+
- A Legrand Smart Lights account with configured devices
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
### Via Homebridge UI (Recommended)
|
|
35
|
+
|
|
36
|
+
1. Open the Homebridge UI
|
|
37
|
+
2. Go to **Plugins**
|
|
38
|
+
3. Search for `homebridge-legrand-radiant`
|
|
39
|
+
4. Click **Install**
|
|
40
|
+
|
|
41
|
+
### Via npm
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npm install -g homebridge-legrand-radiant
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Configuration
|
|
48
|
+
|
|
49
|
+
Add the platform to your Homebridge `config.json`:
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"platforms": [
|
|
54
|
+
{
|
|
55
|
+
"platform": "LegrandRadiant",
|
|
56
|
+
"name": "Legrand",
|
|
57
|
+
"email": "your-email@example.com",
|
|
58
|
+
"password": "your-password"
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Configuration Options
|
|
65
|
+
|
|
66
|
+
| Option | Required | Default | Description |
|
|
67
|
+
|--------|----------|---------|-------------|
|
|
68
|
+
| `platform` | Yes | - | Must be `LegrandRadiant` |
|
|
69
|
+
| `name` | Yes | - | Display name in Homebridge logs |
|
|
70
|
+
| `email` | Yes | - | Your Legrand Smart Lights account email |
|
|
71
|
+
| `password` | Yes | - | Your Legrand Smart Lights account password |
|
|
72
|
+
| `debug` | No | `false` | Enable verbose debug logging |
|
|
73
|
+
|
|
74
|
+
### Advanced Configuration
|
|
75
|
+
|
|
76
|
+
If you need to manually specify devices (instead of auto-discovery):
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
{
|
|
80
|
+
"platforms": [
|
|
81
|
+
{
|
|
82
|
+
"platform": "LegrandRadiant",
|
|
83
|
+
"name": "Legrand",
|
|
84
|
+
"email": "your-email@example.com",
|
|
85
|
+
"password": "your-password",
|
|
86
|
+
"devices": [
|
|
87
|
+
{
|
|
88
|
+
"id": "e182f1e0-ae64-41e6-a892-43fd7c5b2bad",
|
|
89
|
+
"name": "Kitchen Light",
|
|
90
|
+
"type": "dimmer"
|
|
91
|
+
}
|
|
92
|
+
],
|
|
93
|
+
"debug": true
|
|
94
|
+
}
|
|
95
|
+
]
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
| Device Option | Required | Description |
|
|
100
|
+
|---------------|----------|-------------|
|
|
101
|
+
| `id` | Yes | Device UUID from the Legrand API |
|
|
102
|
+
| `name` | Yes | Display name in HomeKit |
|
|
103
|
+
| `type` | No | `switch` or `dimmer` (auto-detected if not specified) |
|
|
104
|
+
|
|
105
|
+
## How It Works
|
|
106
|
+
|
|
107
|
+
This plugin connects to Legrand's cloud API to control your WiFi smart switches:
|
|
108
|
+
|
|
109
|
+
1. **Authentication** - Securely logs in using OAuth2 with your Legrand account
|
|
110
|
+
2. **Discovery** - Automatically finds all your configured devices
|
|
111
|
+
3. **Control** - Sends commands through the cloud API
|
|
112
|
+
4. **Status** - Retrieves current device state
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
HomeKit → Homebridge → Legrand Cloud API → Your WiFi Switch
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Development
|
|
119
|
+
|
|
120
|
+
### Building
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
npm install
|
|
124
|
+
npm run build
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Testing
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
# Test the API directly
|
|
131
|
+
npx ts-node tools/testCloudApi.ts <deviceId> interactive <token>
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Development Mode
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
npm run watch
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Troubleshooting
|
|
141
|
+
|
|
142
|
+
### Devices not appearing
|
|
143
|
+
|
|
144
|
+
1. Make sure your devices are set up in the Legrand Smart Lights app
|
|
145
|
+
2. Verify your email and password are correct
|
|
146
|
+
3. Enable `debug: true` in the config and check Homebridge logs
|
|
147
|
+
|
|
148
|
+
### Authentication errors
|
|
149
|
+
|
|
150
|
+
1. Verify your credentials are correct
|
|
151
|
+
2. Try logging out and back in to the Legrand Smart Lights app
|
|
152
|
+
3. Check if your account has 2FA enabled (not currently supported)
|
|
153
|
+
|
|
154
|
+
### Commands not working
|
|
155
|
+
|
|
156
|
+
1. Check that the device is online in the Legrand app
|
|
157
|
+
2. Verify your internet connection
|
|
158
|
+
3. Enable debug mode to see API responses
|
|
159
|
+
|
|
160
|
+
## API Documentation
|
|
161
|
+
|
|
162
|
+
This plugin uses the Legrand Developer API:
|
|
163
|
+
|
|
164
|
+
| Endpoint | Purpose |
|
|
165
|
+
|----------|---------|
|
|
166
|
+
| `GET /servicecatalog/api/v3.0/plants` | List homes |
|
|
167
|
+
| `GET /servicecatalog/api/v3.0/plants/{id}/modules` | List devices |
|
|
168
|
+
| `POST /devicemanagement/api/v2.0/modules/{id}/commands/setState` | Control device |
|
|
169
|
+
|
|
170
|
+
## Contributing
|
|
171
|
+
|
|
172
|
+
Contributions are welcome! Please feel free to submit issues and pull requests.
|
|
173
|
+
|
|
174
|
+
## Credits
|
|
175
|
+
|
|
176
|
+
- Inspired by [homebridge-lc7001](https://github.com/sbozarth/homebridge-lc7001)
|
|
177
|
+
- Uses the Legrand cloud API (reverse engineered)
|
|
178
|
+
|
|
179
|
+
## License
|
|
180
|
+
|
|
181
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
182
|
+
|
|
183
|
+
## Disclaimer
|
|
184
|
+
|
|
185
|
+
This plugin is not affiliated with or endorsed by Legrand. Use at your own risk.
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
{
|
|
2
|
+
"pluginAlias": "LegrandRadiant",
|
|
3
|
+
"pluginType": "platform",
|
|
4
|
+
"singular": true,
|
|
5
|
+
"headerDisplay": "Control your Legrand Radiant WiFi smart switches and dimmers through HomeKit. Enter your Legrand Smart Lights app credentials below.",
|
|
6
|
+
"footerDisplay": "Devices are automatically discovered from your Legrand account. For issues, check the [GitHub repository](https://github.com/yourusername/homebridge-legrand-radiant).",
|
|
7
|
+
"schema": {
|
|
8
|
+
"type": "object",
|
|
9
|
+
"properties": {
|
|
10
|
+
"name": {
|
|
11
|
+
"title": "Platform Name",
|
|
12
|
+
"type": "string",
|
|
13
|
+
"default": "Legrand",
|
|
14
|
+
"required": true,
|
|
15
|
+
"description": "Name shown in Homebridge logs"
|
|
16
|
+
},
|
|
17
|
+
"email": {
|
|
18
|
+
"title": "Email",
|
|
19
|
+
"type": "string",
|
|
20
|
+
"format": "email",
|
|
21
|
+
"required": true,
|
|
22
|
+
"description": "Your Legrand Smart Lights account email"
|
|
23
|
+
},
|
|
24
|
+
"password": {
|
|
25
|
+
"title": "Password",
|
|
26
|
+
"type": "string",
|
|
27
|
+
"required": true,
|
|
28
|
+
"description": "Your Legrand Smart Lights account password"
|
|
29
|
+
},
|
|
30
|
+
"pollingInterval": {
|
|
31
|
+
"title": "Polling Interval (seconds)",
|
|
32
|
+
"type": "integer",
|
|
33
|
+
"default": 30,
|
|
34
|
+
"minimum": 10,
|
|
35
|
+
"maximum": 300,
|
|
36
|
+
"description": "How often to check for state changes (10-300 seconds)"
|
|
37
|
+
},
|
|
38
|
+
"debug": {
|
|
39
|
+
"title": "Debug Mode",
|
|
40
|
+
"type": "boolean",
|
|
41
|
+
"default": false,
|
|
42
|
+
"description": "Enable verbose logging for troubleshooting"
|
|
43
|
+
},
|
|
44
|
+
"accessToken": {
|
|
45
|
+
"title": "Manual Access Token",
|
|
46
|
+
"type": "string",
|
|
47
|
+
"description": "Optional: Only use if automatic login fails"
|
|
48
|
+
},
|
|
49
|
+
"devices": {
|
|
50
|
+
"title": "Manual Device List",
|
|
51
|
+
"type": "array",
|
|
52
|
+
"items": {
|
|
53
|
+
"type": "object",
|
|
54
|
+
"properties": {
|
|
55
|
+
"id": {
|
|
56
|
+
"title": "Device ID",
|
|
57
|
+
"type": "string",
|
|
58
|
+
"required": true
|
|
59
|
+
},
|
|
60
|
+
"name": {
|
|
61
|
+
"title": "Device Name",
|
|
62
|
+
"type": "string",
|
|
63
|
+
"required": true
|
|
64
|
+
},
|
|
65
|
+
"type": {
|
|
66
|
+
"title": "Device Type",
|
|
67
|
+
"type": "string",
|
|
68
|
+
"oneOf": [
|
|
69
|
+
{ "title": "Switch", "enum": ["switch"] },
|
|
70
|
+
{ "title": "Dimmer", "enum": ["dimmer"] }
|
|
71
|
+
],
|
|
72
|
+
"default": "switch"
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
"description": "Optional: Only needed if auto-discovery doesn't work"
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
"required": ["name", "email", "password"]
|
|
80
|
+
},
|
|
81
|
+
"layout": [
|
|
82
|
+
{
|
|
83
|
+
"type": "fieldset",
|
|
84
|
+
"title": "Legrand Account",
|
|
85
|
+
"expandable": false,
|
|
86
|
+
"items": [
|
|
87
|
+
"email",
|
|
88
|
+
{
|
|
89
|
+
"key": "password",
|
|
90
|
+
"type": "password"
|
|
91
|
+
}
|
|
92
|
+
]
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"type": "fieldset",
|
|
96
|
+
"title": "Settings",
|
|
97
|
+
"expandable": true,
|
|
98
|
+
"expanded": false,
|
|
99
|
+
"items": [
|
|
100
|
+
"name",
|
|
101
|
+
"pollingInterval",
|
|
102
|
+
"debug"
|
|
103
|
+
]
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"type": "fieldset",
|
|
107
|
+
"title": "Advanced",
|
|
108
|
+
"expandable": true,
|
|
109
|
+
"expanded": false,
|
|
110
|
+
"items": [
|
|
111
|
+
"accessToken",
|
|
112
|
+
{
|
|
113
|
+
"key": "devices",
|
|
114
|
+
"type": "array",
|
|
115
|
+
"title": "Manual Devices",
|
|
116
|
+
"expandable": true,
|
|
117
|
+
"expanded": false,
|
|
118
|
+
"items": [
|
|
119
|
+
"devices[].id",
|
|
120
|
+
"devices[].name",
|
|
121
|
+
"devices[].type"
|
|
122
|
+
]
|
|
123
|
+
}
|
|
124
|
+
]
|
|
125
|
+
}
|
|
126
|
+
]
|
|
127
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { PlatformAccessory } from 'homebridge';
|
|
2
|
+
import { LegrandCloudPlatform } from './cloudPlatform';
|
|
3
|
+
/**
|
|
4
|
+
* Accessory handler for a Legrand Cloud-controlled switch
|
|
5
|
+
*/
|
|
6
|
+
export declare class LegrandCloudAccessory {
|
|
7
|
+
private readonly platform;
|
|
8
|
+
private readonly accessory;
|
|
9
|
+
private readonly deviceId;
|
|
10
|
+
private readonly deviceName;
|
|
11
|
+
private service;
|
|
12
|
+
private state;
|
|
13
|
+
private readonly isDimmer;
|
|
14
|
+
constructor(platform: LegrandCloudPlatform, accessory: PlatformAccessory, deviceId: string, deviceName: string, deviceType: string);
|
|
15
|
+
/**
|
|
16
|
+
* Handle SET On
|
|
17
|
+
*/
|
|
18
|
+
private setOn;
|
|
19
|
+
/**
|
|
20
|
+
* Handle GET On
|
|
21
|
+
*/
|
|
22
|
+
private getOn;
|
|
23
|
+
/**
|
|
24
|
+
* Handle SET Brightness (for dimmers)
|
|
25
|
+
*/
|
|
26
|
+
private setBrightness;
|
|
27
|
+
/**
|
|
28
|
+
* Handle GET Brightness (for dimmers)
|
|
29
|
+
*/
|
|
30
|
+
private getBrightness;
|
|
31
|
+
/**
|
|
32
|
+
* Update state from external source (polling)
|
|
33
|
+
*/
|
|
34
|
+
updateState(isOn: boolean, brightness?: number): void;
|
|
35
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LegrandCloudAccessory = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Accessory handler for a Legrand Cloud-controlled switch
|
|
6
|
+
*/
|
|
7
|
+
class LegrandCloudAccessory {
|
|
8
|
+
platform;
|
|
9
|
+
accessory;
|
|
10
|
+
deviceId;
|
|
11
|
+
deviceName;
|
|
12
|
+
service;
|
|
13
|
+
state = {
|
|
14
|
+
on: false,
|
|
15
|
+
brightness: 100,
|
|
16
|
+
};
|
|
17
|
+
isDimmer;
|
|
18
|
+
constructor(platform, accessory, deviceId, deviceName, deviceType) {
|
|
19
|
+
this.platform = platform;
|
|
20
|
+
this.accessory = accessory;
|
|
21
|
+
this.deviceId = deviceId;
|
|
22
|
+
this.deviceName = deviceName;
|
|
23
|
+
this.isDimmer = deviceType === 'dimmer';
|
|
24
|
+
// Set accessory information
|
|
25
|
+
this.accessory.getService(this.platform.Service.AccessoryInformation)
|
|
26
|
+
.setCharacteristic(this.platform.Characteristic.Manufacturer, 'Legrand')
|
|
27
|
+
.setCharacteristic(this.platform.Characteristic.Model, `Radiant WiFi ${this.isDimmer ? 'Dimmer' : 'Switch'}`)
|
|
28
|
+
.setCharacteristic(this.platform.Characteristic.SerialNumber, deviceId.substring(0, 8))
|
|
29
|
+
.setCharacteristic(this.platform.Characteristic.FirmwareRevision, '1.0.0');
|
|
30
|
+
// Remove any conflicting services
|
|
31
|
+
const existingLightbulb = this.accessory.getService(this.platform.Service.Lightbulb);
|
|
32
|
+
const existingSwitch = this.accessory.getService(this.platform.Service.Switch);
|
|
33
|
+
if (this.isDimmer) {
|
|
34
|
+
// Remove switch service if exists (in case device type changed)
|
|
35
|
+
if (existingSwitch) {
|
|
36
|
+
this.accessory.removeService(existingSwitch);
|
|
37
|
+
}
|
|
38
|
+
// Get or create lightbulb service
|
|
39
|
+
this.service = existingLightbulb || this.accessory.addService(this.platform.Service.Lightbulb, deviceName);
|
|
40
|
+
// Set up brightness characteristic
|
|
41
|
+
this.service.getCharacteristic(this.platform.Characteristic.Brightness)
|
|
42
|
+
.onSet(this.setBrightness.bind(this))
|
|
43
|
+
.onGet(this.getBrightness.bind(this));
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
// Remove lightbulb service if exists
|
|
47
|
+
if (existingLightbulb) {
|
|
48
|
+
this.accessory.removeService(existingLightbulb);
|
|
49
|
+
}
|
|
50
|
+
// Get or create switch service
|
|
51
|
+
this.service = existingSwitch || this.accessory.addService(this.platform.Service.Switch, deviceName);
|
|
52
|
+
}
|
|
53
|
+
// Set the display name
|
|
54
|
+
this.service.setCharacteristic(this.platform.Characteristic.Name, deviceName);
|
|
55
|
+
// Register handlers for on/off
|
|
56
|
+
this.service.getCharacteristic(this.platform.Characteristic.On)
|
|
57
|
+
.onSet(this.setOn.bind(this))
|
|
58
|
+
.onGet(this.getOn.bind(this));
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Handle SET On
|
|
62
|
+
*/
|
|
63
|
+
async setOn(value) {
|
|
64
|
+
const isOn = value;
|
|
65
|
+
this.platform.log.debug(`Setting ${this.deviceName} to ${isOn ? 'ON' : 'OFF'}`);
|
|
66
|
+
try {
|
|
67
|
+
const success = isOn
|
|
68
|
+
? await this.platform.cloudApi.turnOn(this.deviceId)
|
|
69
|
+
: await this.platform.cloudApi.turnOff(this.deviceId);
|
|
70
|
+
if (success) {
|
|
71
|
+
this.state.on = isOn;
|
|
72
|
+
this.platform.log.info(`${this.deviceName} is now ${isOn ? 'ON' : 'OFF'}`);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
throw new Error('Failed to set power state');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
this.platform.log.error(`Error setting power: ${error}`);
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Handle GET On
|
|
85
|
+
*/
|
|
86
|
+
async getOn() {
|
|
87
|
+
return this.state.on;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Handle SET Brightness (for dimmers)
|
|
91
|
+
*/
|
|
92
|
+
async setBrightness(value) {
|
|
93
|
+
const brightness = value;
|
|
94
|
+
this.platform.log.debug(`Setting ${this.deviceName} brightness to ${brightness}%`);
|
|
95
|
+
try {
|
|
96
|
+
const success = await this.platform.cloudApi.setBrightness(this.deviceId, brightness);
|
|
97
|
+
if (success) {
|
|
98
|
+
this.state.brightness = brightness;
|
|
99
|
+
this.state.on = brightness > 0;
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
throw new Error('Failed to set brightness');
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
this.platform.log.error(`Error setting brightness: ${error}`);
|
|
107
|
+
throw error;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Handle GET Brightness (for dimmers)
|
|
112
|
+
*/
|
|
113
|
+
async getBrightness() {
|
|
114
|
+
return this.state.brightness;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Update state from external source (polling)
|
|
118
|
+
*/
|
|
119
|
+
updateState(isOn, brightness) {
|
|
120
|
+
// Only update if state has changed
|
|
121
|
+
if (this.state.on !== isOn) {
|
|
122
|
+
this.state.on = isOn;
|
|
123
|
+
this.service.updateCharacteristic(this.platform.Characteristic.On, isOn);
|
|
124
|
+
this.platform.log.debug(`${this.deviceName} state updated: ${isOn ? 'ON' : 'OFF'}`);
|
|
125
|
+
}
|
|
126
|
+
if (this.isDimmer && brightness !== undefined && this.state.brightness !== brightness) {
|
|
127
|
+
this.state.brightness = brightness;
|
|
128
|
+
this.service.updateCharacteristic(this.platform.Characteristic.Brightness, brightness);
|
|
129
|
+
this.platform.log.debug(`${this.deviceName} brightness updated: ${brightness}%`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
exports.LegrandCloudAccessory = LegrandCloudAccessory;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { API, DynamicPlatformPlugin, Logger, PlatformAccessory, PlatformConfig, Service, Characteristic } from 'homebridge';
|
|
2
|
+
import { LegrandCloudApi } from './legrandCloudApi';
|
|
3
|
+
/**
|
|
4
|
+
* Legrand Radiant Cloud Platform Plugin
|
|
5
|
+
* Controls Legrand Radiant WiFi smart switches via the cloud API
|
|
6
|
+
*/
|
|
7
|
+
export declare class LegrandCloudPlatform implements DynamicPlatformPlugin {
|
|
8
|
+
readonly log: Logger;
|
|
9
|
+
readonly config: PlatformConfig;
|
|
10
|
+
readonly api: API;
|
|
11
|
+
readonly Service: typeof Service;
|
|
12
|
+
readonly Characteristic: typeof Characteristic;
|
|
13
|
+
readonly accessories: PlatformAccessory[];
|
|
14
|
+
private readonly accessoryHandlers;
|
|
15
|
+
cloudApi: LegrandCloudApi;
|
|
16
|
+
private pollingInterval;
|
|
17
|
+
constructor(log: Logger, config: PlatformConfig, api: API);
|
|
18
|
+
/**
|
|
19
|
+
* Start polling for device status updates
|
|
20
|
+
*/
|
|
21
|
+
private startPolling;
|
|
22
|
+
/**
|
|
23
|
+
* Stop polling
|
|
24
|
+
*/
|
|
25
|
+
private stopPolling;
|
|
26
|
+
/**
|
|
27
|
+
* Poll all devices for their current status using real-time getState command
|
|
28
|
+
*/
|
|
29
|
+
private pollDeviceStatus;
|
|
30
|
+
/**
|
|
31
|
+
* Called when homebridge restores cached accessories from disk
|
|
32
|
+
*/
|
|
33
|
+
configureAccessory(accessory: PlatformAccessory): void;
|
|
34
|
+
/**
|
|
35
|
+
* Discover and register devices
|
|
36
|
+
*/
|
|
37
|
+
private discoverDevices;
|
|
38
|
+
/**
|
|
39
|
+
* Remove cached accessories that are no longer present
|
|
40
|
+
*/
|
|
41
|
+
private cleanupOldAccessories;
|
|
42
|
+
/**
|
|
43
|
+
* Add a device from an API module
|
|
44
|
+
*/
|
|
45
|
+
private addDeviceFromModule;
|
|
46
|
+
/**
|
|
47
|
+
* Add a device as a HomeKit accessory
|
|
48
|
+
*/
|
|
49
|
+
private addDevice;
|
|
50
|
+
}
|