homebridge-tsvesync 1.0.0
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/.eslintignore +4 -0
- package/.eslintrc.json +31 -0
- package/README.md +170 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/platform.d.ts +25 -0
- package/dist/platform.d.ts.map +1 -0
- package/dist/platform.js +108 -0
- package/dist/platform.js.map +1 -0
- package/dist/platformAccessory.d.ts +28 -0
- package/dist/platformAccessory.d.ts.map +1 -0
- package/dist/platformAccessory.js +105 -0
- package/dist/platformAccessory.js.map +1 -0
- package/dist/settings.d.ts +9 -0
- package/dist/settings.d.ts.map +1 -0
- package/dist/settings.js +12 -0
- package/dist/settings.js.map +1 -0
- package/package.json +82 -0
- package/src/index.ts +7 -0
- package/src/platform.ts +122 -0
- package/src/platformAccessory.ts +111 -0
- package/src/settings.ts +9 -0
- package/src/tsvesync.d.ts +26 -0
- package/tsconfig.json +30 -0
package/.eslintignore
ADDED
package/.eslintrc.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"parser": "@typescript-eslint/parser",
|
|
3
|
+
"extends": [
|
|
4
|
+
"eslint:recommended",
|
|
5
|
+
"plugin:@typescript-eslint/recommended"
|
|
6
|
+
],
|
|
7
|
+
"plugins": ["@typescript-eslint"],
|
|
8
|
+
"parserOptions": {
|
|
9
|
+
"ecmaVersion": 2018,
|
|
10
|
+
"sourceType": "module"
|
|
11
|
+
},
|
|
12
|
+
"env": {
|
|
13
|
+
"node": true,
|
|
14
|
+
"es6": true
|
|
15
|
+
},
|
|
16
|
+
"rules": {
|
|
17
|
+
"@typescript-eslint/explicit-function-return-type": "off",
|
|
18
|
+
"@typescript-eslint/no-non-null-assertion": "off",
|
|
19
|
+
"@typescript-eslint/explicit-module-boundary-types": "off",
|
|
20
|
+
"@typescript-eslint/no-explicit-any": "off",
|
|
21
|
+
"no-unused-vars": "off",
|
|
22
|
+
"@typescript-eslint/no-unused-vars": ["warn", {
|
|
23
|
+
"argsIgnorePattern": "^_",
|
|
24
|
+
"varsIgnorePattern": "^_"
|
|
25
|
+
}]
|
|
26
|
+
},
|
|
27
|
+
"ignorePatterns": [
|
|
28
|
+
"dist/*",
|
|
29
|
+
"node_modules/*"
|
|
30
|
+
]
|
|
31
|
+
}
|
package/README.md
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# homebridge-tsvesync
|
|
2
|
+
|
|
3
|
+
This is a Homebridge plugin that allows you to control your VeSync/Levoit/Etekcity devices through HomeKit. It provides native HomeKit integration for a wide range of VeSync-enabled devices.
|
|
4
|
+
|
|
5
|
+
## Supported Devices
|
|
6
|
+
|
|
7
|
+
### Air Purifiers
|
|
8
|
+
All air purifiers are exposed as HomeKit air purifiers with the following features:
|
|
9
|
+
- Core200S, Core300S, Core400S, Core600S
|
|
10
|
+
* On/Off control
|
|
11
|
+
* Fan speed control
|
|
12
|
+
* Filter life monitoring
|
|
13
|
+
* Auto/Manual/Sleep mode selection
|
|
14
|
+
* Air quality monitoring (300S/400S/600S only)
|
|
15
|
+
|
|
16
|
+
- LV-PUR131S
|
|
17
|
+
* On/Off control
|
|
18
|
+
* Air quality monitoring
|
|
19
|
+
* Filter life monitoring
|
|
20
|
+
|
|
21
|
+
- Vital100S, Vital200S
|
|
22
|
+
* On/Off control
|
|
23
|
+
* Fan speed control (1-4)
|
|
24
|
+
* Mode selection (Manual, Auto, Sleep, Pet)
|
|
25
|
+
* Air quality monitoring
|
|
26
|
+
* Filter life monitoring
|
|
27
|
+
|
|
28
|
+
- EverestAir
|
|
29
|
+
* On/Off control
|
|
30
|
+
* Fan speed control (1-3)
|
|
31
|
+
* Mode selection (Manual, Auto, Sleep, Turbo)
|
|
32
|
+
* Air quality monitoring
|
|
33
|
+
* Filter life monitoring
|
|
34
|
+
|
|
35
|
+
### Humidifiers
|
|
36
|
+
All humidifiers are exposed as HomeKit humidifiers with the following features:
|
|
37
|
+
- Classic300S, Classic200S
|
|
38
|
+
* On/Off control
|
|
39
|
+
* Target humidity setting
|
|
40
|
+
* Current humidity reading
|
|
41
|
+
* Auto/Manual mode selection
|
|
42
|
+
* Mist level control (1-9)
|
|
43
|
+
* Night light control (300S only)
|
|
44
|
+
|
|
45
|
+
- Dual200S
|
|
46
|
+
* On/Off control
|
|
47
|
+
* Target humidity setting
|
|
48
|
+
* Current humidity reading
|
|
49
|
+
* Auto/Manual mode selection
|
|
50
|
+
* Mist level control (1-2)
|
|
51
|
+
|
|
52
|
+
- LV600S, OasisMist Series, Superior6000S
|
|
53
|
+
* On/Off control
|
|
54
|
+
* Target humidity setting
|
|
55
|
+
* Current humidity reading
|
|
56
|
+
* Mode selection (Auto/Manual/Sleep)
|
|
57
|
+
* Mist level control
|
|
58
|
+
* Warm mist control (where supported)
|
|
59
|
+
* Night light control (where supported)
|
|
60
|
+
|
|
61
|
+
### Smart Bulbs
|
|
62
|
+
All bulbs are exposed as HomeKit lights with their respective capabilities:
|
|
63
|
+
- ESL100
|
|
64
|
+
* On/Off control
|
|
65
|
+
* Brightness control
|
|
66
|
+
|
|
67
|
+
- ESL100CW
|
|
68
|
+
* On/Off control
|
|
69
|
+
* Brightness control
|
|
70
|
+
* Color temperature control
|
|
71
|
+
|
|
72
|
+
- ESL100MC, XYD0001
|
|
73
|
+
* On/Off control
|
|
74
|
+
* Brightness control
|
|
75
|
+
* Full RGB color control
|
|
76
|
+
* Color temperature control
|
|
77
|
+
|
|
78
|
+
### Smart Outlets
|
|
79
|
+
All outlets are exposed as HomeKit outlets with the following features:
|
|
80
|
+
- 15A Outlets (ESO15-TB, ESW15-USA)
|
|
81
|
+
* On/Off control
|
|
82
|
+
* Power/Energy monitoring
|
|
83
|
+
* Voltage monitoring
|
|
84
|
+
|
|
85
|
+
- 10A Outlets (ESW03-USA, ESW01-EU)
|
|
86
|
+
* On/Off control
|
|
87
|
+
* Power/Energy monitoring
|
|
88
|
+
* Voltage monitoring
|
|
89
|
+
|
|
90
|
+
- 7A Outlet (wifi-switch-1.3)
|
|
91
|
+
* On/Off control
|
|
92
|
+
* Basic energy usage tracking
|
|
93
|
+
|
|
94
|
+
### Tower Fans
|
|
95
|
+
- LTF-F422S Series
|
|
96
|
+
* On/Off control
|
|
97
|
+
* Fan speed control (1-12)
|
|
98
|
+
* Mode selection (Normal, Auto, Sleep, Turbo)
|
|
99
|
+
* Oscillation control
|
|
100
|
+
|
|
101
|
+
## Installation
|
|
102
|
+
|
|
103
|
+
1. Install Homebridge if you haven't already (see [homebridge.io](https://homebridge.io))
|
|
104
|
+
2. Install this plugin:
|
|
105
|
+
```bash
|
|
106
|
+
npm install -g homebridge-tsvesync
|
|
107
|
+
```
|
|
108
|
+
3. Configure the plugin in your Homebridge `config.json`
|
|
109
|
+
|
|
110
|
+
## Configuration
|
|
111
|
+
|
|
112
|
+
Add the following to your Homebridge `config.json`:
|
|
113
|
+
|
|
114
|
+
```json
|
|
115
|
+
{
|
|
116
|
+
"platforms": [
|
|
117
|
+
{
|
|
118
|
+
"platform": "TSVESyncPlatform",
|
|
119
|
+
"name": "TSVESync",
|
|
120
|
+
"username": "YOUR_VESYNC_EMAIL",
|
|
121
|
+
"password": "YOUR_VESYNC_PASSWORD"
|
|
122
|
+
}
|
|
123
|
+
]
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Configuration Options
|
|
128
|
+
|
|
129
|
+
* `platform` (required): Must be set to "TSVESyncPlatform"
|
|
130
|
+
* `name` (required): Can be anything, this is the name that will appear in your Homebridge log
|
|
131
|
+
* `username` (required): Your VeSync account email
|
|
132
|
+
* `password` (required): Your VeSync account password
|
|
133
|
+
|
|
134
|
+
## Features
|
|
135
|
+
|
|
136
|
+
* Automatic device discovery
|
|
137
|
+
* Real-time status updates
|
|
138
|
+
* Native HomeKit integration
|
|
139
|
+
* Power consumption monitoring (supported outlets)
|
|
140
|
+
* Air quality monitoring (supported purifiers)
|
|
141
|
+
* Humidity control (supported humidifiers)
|
|
142
|
+
* RGB and white color control (supported bulbs)
|
|
143
|
+
* Fan speed and mode control (purifiers and fans)
|
|
144
|
+
|
|
145
|
+
## Troubleshooting
|
|
146
|
+
|
|
147
|
+
1. Make sure your VeSync credentials are correct
|
|
148
|
+
2. Check the Homebridge logs for any error messages
|
|
149
|
+
3. Ensure your devices are properly set up in the VeSync app
|
|
150
|
+
4. Verify your devices are running the latest firmware
|
|
151
|
+
5. Check that your devices are online in the VeSync app
|
|
152
|
+
|
|
153
|
+
## Development
|
|
154
|
+
|
|
155
|
+
* Clone this repository
|
|
156
|
+
* Install dependencies: `npm install`
|
|
157
|
+
* Build the plugin: `npm run build`
|
|
158
|
+
* Link it to your local Homebridge installation: `npm link`
|
|
159
|
+
|
|
160
|
+
## Contributing
|
|
161
|
+
|
|
162
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
163
|
+
|
|
164
|
+
## License
|
|
165
|
+
|
|
166
|
+
MIT
|
|
167
|
+
|
|
168
|
+
## Credits
|
|
169
|
+
|
|
170
|
+
This plugin uses the [tsvesync](https://github.com/mickgiles/tsvesync) library for device communication.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;8BAIlB,GAAG;AAAlB,kBAEE"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const settings_1 = require("./settings");
|
|
3
|
+
const platform_1 = require("./platform");
|
|
4
|
+
module.exports = (api) => {
|
|
5
|
+
api.registerPlatform(settings_1.PLUGIN_NAME, settings_1.PLATFORM_NAME, platform_1.TSVESyncPlatform);
|
|
6
|
+
};
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,yCAAwD;AACxD,yCAA8C;AAE9C,iBAAS,CAAC,GAAQ,EAAE,EAAE;IACpB,GAAG,CAAC,gBAAgB,CAAC,sBAAW,EAAE,wBAAa,EAAE,2BAAgB,CAAC,CAAC;AACrE,CAAC,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { API, DynamicPlatformPlugin, Logger, PlatformAccessory, PlatformConfig, Service, Characteristic } from 'homebridge';
|
|
2
|
+
export declare class TSVESyncPlatform implements DynamicPlatformPlugin {
|
|
3
|
+
readonly log: Logger;
|
|
4
|
+
readonly config: PlatformConfig;
|
|
5
|
+
readonly api: API;
|
|
6
|
+
readonly Service: typeof Service;
|
|
7
|
+
readonly Characteristic: typeof Characteristic;
|
|
8
|
+
readonly accessories: PlatformAccessory[];
|
|
9
|
+
private client;
|
|
10
|
+
private deviceUpdateInterval?;
|
|
11
|
+
constructor(log: Logger, config: PlatformConfig, api: API);
|
|
12
|
+
/**
|
|
13
|
+
* This function is invoked when homebridge restores cached accessories from disk at startup.
|
|
14
|
+
*/
|
|
15
|
+
configureAccessory(accessory: PlatformAccessory): void;
|
|
16
|
+
/**
|
|
17
|
+
* Discover TSVESync devices and register them as accessories
|
|
18
|
+
*/
|
|
19
|
+
discoverDevices(): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Update all device states
|
|
22
|
+
*/
|
|
23
|
+
private updateDeviceStates;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=platform.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform.d.ts","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,qBAAqB,EAAE,MAAM,EAAE,iBAAiB,EAAE,cAAc,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAK5H,qBAAa,gBAAiB,YAAW,qBAAqB;aAW1C,GAAG,EAAE,MAAM;aACX,MAAM,EAAE,cAAc;aACtB,GAAG,EAAE,GAAG;IAZ1B,SAAgB,OAAO,EAAE,OAAO,OAAO,CAAwB;IAC/D,SAAgB,cAAc,EAAE,OAAO,cAAc,CAA+B;IAGpF,SAAgB,WAAW,EAAE,iBAAiB,EAAE,CAAM;IAEtD,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,oBAAoB,CAAC,CAAiB;gBAG5B,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,cAAc,EACtB,GAAG,EAAE,GAAG;IAoC1B;;OAEG;IACH,kBAAkB,CAAC,SAAS,EAAE,iBAAiB;IAK/C;;OAEG;IACG,eAAe;IAsCrB;;OAEG;YACW,kBAAkB;CAejC"}
|
package/dist/platform.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TSVESyncPlatform = void 0;
|
|
4
|
+
const settings_1 = require("./settings");
|
|
5
|
+
const platformAccessory_1 = require("./platformAccessory");
|
|
6
|
+
const tsvesync_1 = require("tsvesync");
|
|
7
|
+
class TSVESyncPlatform {
|
|
8
|
+
constructor(log, config, api) {
|
|
9
|
+
this.log = log;
|
|
10
|
+
this.config = config;
|
|
11
|
+
this.api = api;
|
|
12
|
+
this.Service = this.api.hap.Service;
|
|
13
|
+
this.Characteristic = this.api.hap.Characteristic;
|
|
14
|
+
// this is used to track restored cached accessories
|
|
15
|
+
this.accessories = [];
|
|
16
|
+
// Validate configuration
|
|
17
|
+
if (!config.username || !config.password) {
|
|
18
|
+
log.error('Missing required configuration. Please check your config.json');
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
// Initialize TSVESync client
|
|
22
|
+
this.client = new tsvesync_1.TSVESync({
|
|
23
|
+
username: config.username,
|
|
24
|
+
password: config.password,
|
|
25
|
+
});
|
|
26
|
+
this.log.debug('Finished initializing platform:', this.config.name);
|
|
27
|
+
// When this event is fired it means Homebridge has restored all cached accessories from disk.
|
|
28
|
+
this.api.on('didFinishLaunching', () => {
|
|
29
|
+
log.debug('Executed didFinishLaunching callback');
|
|
30
|
+
// run the method to discover / register your devices as accessories
|
|
31
|
+
this.discoverDevices();
|
|
32
|
+
// Set up periodic device updates (every 30 seconds)
|
|
33
|
+
this.deviceUpdateInterval = setInterval(() => {
|
|
34
|
+
this.updateDeviceStates();
|
|
35
|
+
}, 30000);
|
|
36
|
+
});
|
|
37
|
+
// Clean up when shutting down
|
|
38
|
+
this.api.on('shutdown', () => {
|
|
39
|
+
if (this.deviceUpdateInterval) {
|
|
40
|
+
clearInterval(this.deviceUpdateInterval);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* This function is invoked when homebridge restores cached accessories from disk at startup.
|
|
46
|
+
*/
|
|
47
|
+
configureAccessory(accessory) {
|
|
48
|
+
this.log.info('Loading accessory from cache:', accessory.displayName);
|
|
49
|
+
this.accessories.push(accessory);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Discover TSVESync devices and register them as accessories
|
|
53
|
+
*/
|
|
54
|
+
async discoverDevices() {
|
|
55
|
+
try {
|
|
56
|
+
// Login and get devices
|
|
57
|
+
await this.client.login();
|
|
58
|
+
const devices = await this.client.getDevices();
|
|
59
|
+
// Loop over the discovered devices and register each one
|
|
60
|
+
for (const device of devices) {
|
|
61
|
+
const uuid = this.api.hap.uuid.generate(device.id);
|
|
62
|
+
const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid);
|
|
63
|
+
if (existingAccessory) {
|
|
64
|
+
this.log.info('Restoring existing accessory from cache:', existingAccessory.displayName);
|
|
65
|
+
existingAccessory.context.device = device;
|
|
66
|
+
new platformAccessory_1.TSVESyncAccessory(this, existingAccessory, this.client);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
this.log.info('Adding new accessory:', device.name);
|
|
70
|
+
const accessory = new this.api.platformAccessory(device.name, uuid);
|
|
71
|
+
accessory.context.device = device;
|
|
72
|
+
new platformAccessory_1.TSVESyncAccessory(this, accessory, this.client);
|
|
73
|
+
this.api.registerPlatformAccessories(settings_1.PLUGIN_NAME, settings_1.PLATFORM_NAME, [accessory]);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// Remove accessories that are no longer present
|
|
77
|
+
this.accessories.forEach(accessory => {
|
|
78
|
+
const device = devices.find(device => this.api.hap.uuid.generate(device.id) === accessory.UUID);
|
|
79
|
+
if (!device) {
|
|
80
|
+
this.api.unregisterPlatformAccessories(settings_1.PLUGIN_NAME, settings_1.PLATFORM_NAME, [accessory]);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
this.log.error('Failed to discover devices:', error);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Update all device states
|
|
90
|
+
*/
|
|
91
|
+
async updateDeviceStates() {
|
|
92
|
+
try {
|
|
93
|
+
await this.client.login();
|
|
94
|
+
const devices = await this.client.getDevices();
|
|
95
|
+
this.accessories.forEach(accessory => {
|
|
96
|
+
const device = devices.find(d => this.api.hap.uuid.generate(d.id) === accessory.UUID);
|
|
97
|
+
if (device) {
|
|
98
|
+
accessory.context.device = device;
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
this.log.error('Failed to update device states:', error);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
exports.TSVESyncPlatform = TSVESyncPlatform;
|
|
108
|
+
//# sourceMappingURL=platform.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform.js","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { PlatformAccessory, CharacteristicValue } from 'homebridge';
|
|
2
|
+
import { TSVESyncPlatform } from './platform';
|
|
3
|
+
import { TSVESync } from 'tsvesync';
|
|
4
|
+
export declare class TSVESyncAccessory {
|
|
5
|
+
private readonly platform;
|
|
6
|
+
private readonly accessory;
|
|
7
|
+
private readonly client;
|
|
8
|
+
private service;
|
|
9
|
+
constructor(platform: TSVESyncPlatform, accessory: PlatformAccessory, client: TSVESync);
|
|
10
|
+
private setupService;
|
|
11
|
+
/**
|
|
12
|
+
* Handle "SET" requests from HomeKit for power state
|
|
13
|
+
*/
|
|
14
|
+
setOn(value: CharacteristicValue): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Handle "GET" requests from HomeKit for power state
|
|
17
|
+
*/
|
|
18
|
+
getOn(): Promise<CharacteristicValue>;
|
|
19
|
+
/**
|
|
20
|
+
* Handle "SET" requests from HomeKit for brightness
|
|
21
|
+
*/
|
|
22
|
+
setBrightness(value: CharacteristicValue): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Handle "GET" requests from HomeKit for brightness
|
|
25
|
+
*/
|
|
26
|
+
getBrightness(): Promise<CharacteristicValue>;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=platformAccessory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platformAccessory.d.ts","sourceRoot":"","sources":["../src/platformAccessory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAC7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAU,MAAM,UAAU,CAAC;AAE5C,qBAAa,iBAAiB;IAI1B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,MAAM;IALzB,OAAO,CAAC,OAAO,CAAW;gBAGP,QAAQ,EAAE,gBAAgB,EAC1B,SAAS,EAAE,iBAAiB,EAC5B,MAAM,EAAE,QAAQ;IAenC,OAAO,CAAC,YAAY;IAuBpB;;OAEG;IACG,KAAK,CAAC,KAAK,EAAE,mBAAmB;IAWtC;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,mBAAmB,CAAC;IAY3C;;OAEG;IACG,aAAa,CAAC,KAAK,EAAE,mBAAmB;IAa9C;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,mBAAmB,CAAC;CAcpD"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TSVESyncAccessory = void 0;
|
|
4
|
+
class TSVESyncAccessory {
|
|
5
|
+
constructor(platform, accessory, client) {
|
|
6
|
+
this.platform = platform;
|
|
7
|
+
this.accessory = accessory;
|
|
8
|
+
this.client = client;
|
|
9
|
+
// Get the device from context
|
|
10
|
+
const device = accessory.context.device;
|
|
11
|
+
// set accessory information
|
|
12
|
+
this.accessory.getService(this.platform.Service.AccessoryInformation)
|
|
13
|
+
.setCharacteristic(this.platform.Characteristic.Manufacturer, 'TSVESync')
|
|
14
|
+
.setCharacteristic(this.platform.Characteristic.Model, device.model || 'Unknown')
|
|
15
|
+
.setCharacteristic(this.platform.Characteristic.SerialNumber, device.id);
|
|
16
|
+
// Set up the appropriate service based on device type
|
|
17
|
+
this.setupService(device);
|
|
18
|
+
}
|
|
19
|
+
setupService(device) {
|
|
20
|
+
// Determine the appropriate service type based on device capabilities
|
|
21
|
+
if (device.supportsBrightness) {
|
|
22
|
+
// Set up as a lightbulb if it supports brightness
|
|
23
|
+
this.service = this.accessory.getService(this.platform.Service.Lightbulb)
|
|
24
|
+
|| this.accessory.addService(this.platform.Service.Lightbulb);
|
|
25
|
+
// Add brightness characteristic
|
|
26
|
+
this.service.getCharacteristic(this.platform.Characteristic.Brightness)
|
|
27
|
+
.onSet(this.setBrightness.bind(this))
|
|
28
|
+
.onGet(this.getBrightness.bind(this));
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
// Set up as a switch for basic on/off functionality
|
|
32
|
+
this.service = this.accessory.getService(this.platform.Service.Switch)
|
|
33
|
+
|| this.accessory.addService(this.platform.Service.Switch);
|
|
34
|
+
}
|
|
35
|
+
// Add on/off characteristic (common to both switch and lightbulb)
|
|
36
|
+
this.service.getCharacteristic(this.platform.Characteristic.On)
|
|
37
|
+
.onSet(this.setOn.bind(this))
|
|
38
|
+
.onGet(this.getOn.bind(this));
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Handle "SET" requests from HomeKit for power state
|
|
42
|
+
*/
|
|
43
|
+
async setOn(value) {
|
|
44
|
+
try {
|
|
45
|
+
const device = this.accessory.context.device;
|
|
46
|
+
await this.client.setPowerState(device.id, value);
|
|
47
|
+
this.platform.log.debug('Set power state ->', value);
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
this.platform.log.error('Failed to set power state:', error);
|
|
51
|
+
throw error;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Handle "GET" requests from HomeKit for power state
|
|
56
|
+
*/
|
|
57
|
+
async getOn() {
|
|
58
|
+
try {
|
|
59
|
+
const device = this.accessory.context.device;
|
|
60
|
+
const state = await this.client.getPowerState(device.id);
|
|
61
|
+
this.platform.log.debug('Get power state ->', state);
|
|
62
|
+
return state;
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
this.platform.log.error('Failed to get power state:', error);
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Handle "SET" requests from HomeKit for brightness
|
|
71
|
+
*/
|
|
72
|
+
async setBrightness(value) {
|
|
73
|
+
try {
|
|
74
|
+
const device = this.accessory.context.device;
|
|
75
|
+
if (device.supportsBrightness) {
|
|
76
|
+
await this.client.setBrightness(device.id, value);
|
|
77
|
+
this.platform.log.debug('Set brightness ->', value);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
this.platform.log.error('Failed to set brightness:', error);
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Handle "GET" requests from HomeKit for brightness
|
|
87
|
+
*/
|
|
88
|
+
async getBrightness() {
|
|
89
|
+
try {
|
|
90
|
+
const device = this.accessory.context.device;
|
|
91
|
+
if (device.supportsBrightness) {
|
|
92
|
+
const brightness = await this.client.getBrightness(device.id);
|
|
93
|
+
this.platform.log.debug('Get brightness ->', brightness);
|
|
94
|
+
return brightness;
|
|
95
|
+
}
|
|
96
|
+
return 100;
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
this.platform.log.error('Failed to get brightness:', error);
|
|
100
|
+
throw error;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
exports.TSVESyncAccessory = TSVESyncAccessory;
|
|
105
|
+
//# sourceMappingURL=platformAccessory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platformAccessory.js","sourceRoot":"","sources":["../src/platformAccessory.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This is the name of the platform that users will use to register the plugin in the Homebridge config.json
|
|
3
|
+
*/
|
|
4
|
+
export declare const PLATFORM_NAME = "TSVESyncPlatform";
|
|
5
|
+
/**
|
|
6
|
+
* This must match the name of your plugin as defined the package.json
|
|
7
|
+
*/
|
|
8
|
+
export declare const PLUGIN_NAME = "homebridge-tsvesync";
|
|
9
|
+
//# sourceMappingURL=settings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,aAAa,qBAAqB,CAAC;AAEhD;;GAEG;AACH,eAAO,MAAM,WAAW,wBAAwB,CAAC"}
|
package/dist/settings.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PLUGIN_NAME = exports.PLATFORM_NAME = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* This is the name of the platform that users will use to register the plugin in the Homebridge config.json
|
|
6
|
+
*/
|
|
7
|
+
exports.PLATFORM_NAME = 'TSVESyncPlatform';
|
|
8
|
+
/**
|
|
9
|
+
* This must match the name of your plugin as defined the package.json
|
|
10
|
+
*/
|
|
11
|
+
exports.PLUGIN_NAME = 'homebridge-tsvesync';
|
|
12
|
+
//# sourceMappingURL=settings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings.js","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":";;;AAAA;;GAEG;AACU,QAAA,aAAa,GAAG,kBAAkB,CAAC;AAEhD;;GAEG;AACU,QAAA,WAAW,GAAG,qBAAqB,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "homebridge-tsvesync",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Homebridge plugin for TSVESync devices",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"lint": "eslint src/**.ts",
|
|
8
|
+
"watch": "npm run build && npm link && nodemon",
|
|
9
|
+
"build": "rimraf ./dist && tsc",
|
|
10
|
+
"prepublishOnly": "npm run lint && npm run build"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"homebridge-plugin",
|
|
14
|
+
"tsvesync",
|
|
15
|
+
"etekcity",
|
|
16
|
+
"vesync",
|
|
17
|
+
"levoit",
|
|
18
|
+
"air purifier",
|
|
19
|
+
"humidifier",
|
|
20
|
+
"smart bulb",
|
|
21
|
+
"smart outlet",
|
|
22
|
+
"tower fan",
|
|
23
|
+
"Core200S",
|
|
24
|
+
"Core300S",
|
|
25
|
+
"Core400S",
|
|
26
|
+
"Core600S",
|
|
27
|
+
"LV-PUR131S",
|
|
28
|
+
"Vital100S",
|
|
29
|
+
"Vital200S",
|
|
30
|
+
"EverestAir",
|
|
31
|
+
"Classic300S",
|
|
32
|
+
"Classic200S",
|
|
33
|
+
"Dual200S",
|
|
34
|
+
"LV600S",
|
|
35
|
+
"OasisMist",
|
|
36
|
+
"OasisMist1000S",
|
|
37
|
+
"Superior6000S",
|
|
38
|
+
"ESL100",
|
|
39
|
+
"ESL100CW",
|
|
40
|
+
"ESL100MC",
|
|
41
|
+
"XYD0001",
|
|
42
|
+
"ESO15-TB",
|
|
43
|
+
"ESW15-USA",
|
|
44
|
+
"ESW03-USA",
|
|
45
|
+
"ESW01-EU",
|
|
46
|
+
"LTF-F422S",
|
|
47
|
+
"rgb-light",
|
|
48
|
+
"tunable-white",
|
|
49
|
+
"energy-monitoring",
|
|
50
|
+
"power-monitoring",
|
|
51
|
+
"voltage-monitoring",
|
|
52
|
+
"air-quality",
|
|
53
|
+
"humidity-control",
|
|
54
|
+
"fan-control",
|
|
55
|
+
"filter-life",
|
|
56
|
+
"night-light",
|
|
57
|
+
"warm-mist",
|
|
58
|
+
"auto-mode",
|
|
59
|
+
"sleep-mode",
|
|
60
|
+
"manual-mode",
|
|
61
|
+
"pet-mode",
|
|
62
|
+
"turbo-mode"
|
|
63
|
+
],
|
|
64
|
+
"engines": {
|
|
65
|
+
"node": ">=14.18.1",
|
|
66
|
+
"homebridge": ">=1.3.5"
|
|
67
|
+
},
|
|
68
|
+
"dependencies": {
|
|
69
|
+
"tsvesync": "^1.0.11"
|
|
70
|
+
},
|
|
71
|
+
"devDependencies": {
|
|
72
|
+
"@types/node": "^16.10.9",
|
|
73
|
+
"@typescript-eslint/eslint-plugin": "^5.0.0",
|
|
74
|
+
"@typescript-eslint/parser": "^5.0.0",
|
|
75
|
+
"eslint": "^8.0.1",
|
|
76
|
+
"homebridge": "^1.3.5",
|
|
77
|
+
"nodemon": "^2.0.13",
|
|
78
|
+
"rimraf": "^3.0.2",
|
|
79
|
+
"ts-node": "^10.3.0",
|
|
80
|
+
"typescript": "^4.4.4"
|
|
81
|
+
}
|
|
82
|
+
}
|
package/src/index.ts
ADDED
package/src/platform.ts
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { API, DynamicPlatformPlugin, Logger, PlatformAccessory, PlatformConfig, Service, Characteristic } from 'homebridge';
|
|
2
|
+
import { PLATFORM_NAME, PLUGIN_NAME } from './settings';
|
|
3
|
+
import { TSVESyncAccessory } from './platformAccessory';
|
|
4
|
+
import { TSVESync } from 'tsvesync';
|
|
5
|
+
|
|
6
|
+
export class TSVESyncPlatform implements DynamicPlatformPlugin {
|
|
7
|
+
public readonly Service: typeof Service = this.api.hap.Service;
|
|
8
|
+
public readonly Characteristic: typeof Characteristic = this.api.hap.Characteristic;
|
|
9
|
+
|
|
10
|
+
// this is used to track restored cached accessories
|
|
11
|
+
public readonly accessories: PlatformAccessory[] = [];
|
|
12
|
+
|
|
13
|
+
private client!: TSVESync;
|
|
14
|
+
private deviceUpdateInterval?: NodeJS.Timeout;
|
|
15
|
+
|
|
16
|
+
constructor(
|
|
17
|
+
public readonly log: Logger,
|
|
18
|
+
public readonly config: PlatformConfig,
|
|
19
|
+
public readonly api: API,
|
|
20
|
+
) {
|
|
21
|
+
// Validate configuration
|
|
22
|
+
if (!config.username || !config.password) {
|
|
23
|
+
log.error('Missing required configuration. Please check your config.json');
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Initialize TSVESync client
|
|
28
|
+
this.client = new TSVESync({
|
|
29
|
+
username: config.username,
|
|
30
|
+
password: config.password,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
this.log.debug('Finished initializing platform:', this.config.name);
|
|
34
|
+
|
|
35
|
+
// When this event is fired it means Homebridge has restored all cached accessories from disk.
|
|
36
|
+
this.api.on('didFinishLaunching', () => {
|
|
37
|
+
log.debug('Executed didFinishLaunching callback');
|
|
38
|
+
// run the method to discover / register your devices as accessories
|
|
39
|
+
this.discoverDevices();
|
|
40
|
+
|
|
41
|
+
// Set up periodic device updates (every 30 seconds)
|
|
42
|
+
this.deviceUpdateInterval = setInterval(() => {
|
|
43
|
+
this.updateDeviceStates();
|
|
44
|
+
}, 30000);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Clean up when shutting down
|
|
48
|
+
this.api.on('shutdown', () => {
|
|
49
|
+
if (this.deviceUpdateInterval) {
|
|
50
|
+
clearInterval(this.deviceUpdateInterval);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* This function is invoked when homebridge restores cached accessories from disk at startup.
|
|
57
|
+
*/
|
|
58
|
+
configureAccessory(accessory: PlatformAccessory) {
|
|
59
|
+
this.log.info('Loading accessory from cache:', accessory.displayName);
|
|
60
|
+
this.accessories.push(accessory);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Discover TSVESync devices and register them as accessories
|
|
65
|
+
*/
|
|
66
|
+
async discoverDevices() {
|
|
67
|
+
try {
|
|
68
|
+
// Login and get devices
|
|
69
|
+
await this.client.login();
|
|
70
|
+
const devices = await this.client.getDevices();
|
|
71
|
+
|
|
72
|
+
// Loop over the discovered devices and register each one
|
|
73
|
+
for (const device of devices) {
|
|
74
|
+
const uuid = this.api.hap.uuid.generate(device.id);
|
|
75
|
+
const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid);
|
|
76
|
+
|
|
77
|
+
if (existingAccessory) {
|
|
78
|
+
this.log.info('Restoring existing accessory from cache:', existingAccessory.displayName);
|
|
79
|
+
existingAccessory.context.device = device;
|
|
80
|
+
new TSVESyncAccessory(this, existingAccessory, this.client);
|
|
81
|
+
|
|
82
|
+
} else {
|
|
83
|
+
this.log.info('Adding new accessory:', device.name);
|
|
84
|
+
const accessory = new this.api.platformAccessory(device.name, uuid);
|
|
85
|
+
accessory.context.device = device;
|
|
86
|
+
new TSVESyncAccessory(this, accessory, this.client);
|
|
87
|
+
this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Remove accessories that are no longer present
|
|
92
|
+
this.accessories.forEach(accessory => {
|
|
93
|
+
const device = devices.find(device => this.api.hap.uuid.generate(device.id) === accessory.UUID);
|
|
94
|
+
if (!device) {
|
|
95
|
+
this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
} catch (error) {
|
|
100
|
+
this.log.error('Failed to discover devices:', error);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Update all device states
|
|
106
|
+
*/
|
|
107
|
+
private async updateDeviceStates() {
|
|
108
|
+
try {
|
|
109
|
+
await this.client.login();
|
|
110
|
+
const devices = await this.client.getDevices();
|
|
111
|
+
|
|
112
|
+
this.accessories.forEach(accessory => {
|
|
113
|
+
const device = devices.find(d => this.api.hap.uuid.generate(d.id) === accessory.UUID);
|
|
114
|
+
if (device) {
|
|
115
|
+
accessory.context.device = device;
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
} catch (error) {
|
|
119
|
+
this.log.error('Failed to update device states:', error);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge';
|
|
2
|
+
import { TSVESyncPlatform } from './platform';
|
|
3
|
+
import { TSVESync, Device } from 'tsvesync';
|
|
4
|
+
|
|
5
|
+
export class TSVESyncAccessory {
|
|
6
|
+
private service!: Service;
|
|
7
|
+
|
|
8
|
+
constructor(
|
|
9
|
+
private readonly platform: TSVESyncPlatform,
|
|
10
|
+
private readonly accessory: PlatformAccessory,
|
|
11
|
+
private readonly client: TSVESync,
|
|
12
|
+
) {
|
|
13
|
+
// Get the device from context
|
|
14
|
+
const device = accessory.context.device as Device;
|
|
15
|
+
|
|
16
|
+
// set accessory information
|
|
17
|
+
this.accessory.getService(this.platform.Service.AccessoryInformation)!
|
|
18
|
+
.setCharacteristic(this.platform.Characteristic.Manufacturer, 'TSVESync')
|
|
19
|
+
.setCharacteristic(this.platform.Characteristic.Model, device.model || 'Unknown')
|
|
20
|
+
.setCharacteristic(this.platform.Characteristic.SerialNumber, device.id);
|
|
21
|
+
|
|
22
|
+
// Set up the appropriate service based on device type
|
|
23
|
+
this.setupService(device);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
private setupService(device: Device) {
|
|
27
|
+
// Determine the appropriate service type based on device capabilities
|
|
28
|
+
if (device.supportsBrightness) {
|
|
29
|
+
// Set up as a lightbulb if it supports brightness
|
|
30
|
+
this.service = this.accessory.getService(this.platform.Service.Lightbulb)
|
|
31
|
+
|| this.accessory.addService(this.platform.Service.Lightbulb);
|
|
32
|
+
|
|
33
|
+
// Add brightness characteristic
|
|
34
|
+
this.service.getCharacteristic(this.platform.Characteristic.Brightness)
|
|
35
|
+
.onSet(this.setBrightness.bind(this))
|
|
36
|
+
.onGet(this.getBrightness.bind(this));
|
|
37
|
+
} else {
|
|
38
|
+
// Set up as a switch for basic on/off functionality
|
|
39
|
+
this.service = this.accessory.getService(this.platform.Service.Switch)
|
|
40
|
+
|| this.accessory.addService(this.platform.Service.Switch);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Add on/off characteristic (common to both switch and lightbulb)
|
|
44
|
+
this.service.getCharacteristic(this.platform.Characteristic.On)
|
|
45
|
+
.onSet(this.setOn.bind(this))
|
|
46
|
+
.onGet(this.getOn.bind(this));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Handle "SET" requests from HomeKit for power state
|
|
51
|
+
*/
|
|
52
|
+
async setOn(value: CharacteristicValue) {
|
|
53
|
+
try {
|
|
54
|
+
const device = this.accessory.context.device as Device;
|
|
55
|
+
await this.client.setPowerState(device.id, value as boolean);
|
|
56
|
+
this.platform.log.debug('Set power state ->', value);
|
|
57
|
+
} catch (error) {
|
|
58
|
+
this.platform.log.error('Failed to set power state:', error);
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Handle "GET" requests from HomeKit for power state
|
|
65
|
+
*/
|
|
66
|
+
async getOn(): Promise<CharacteristicValue> {
|
|
67
|
+
try {
|
|
68
|
+
const device = this.accessory.context.device as Device;
|
|
69
|
+
const state = await this.client.getPowerState(device.id);
|
|
70
|
+
this.platform.log.debug('Get power state ->', state);
|
|
71
|
+
return state;
|
|
72
|
+
} catch (error) {
|
|
73
|
+
this.platform.log.error('Failed to get power state:', error);
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Handle "SET" requests from HomeKit for brightness
|
|
80
|
+
*/
|
|
81
|
+
async setBrightness(value: CharacteristicValue) {
|
|
82
|
+
try {
|
|
83
|
+
const device = this.accessory.context.device as Device;
|
|
84
|
+
if (device.supportsBrightness) {
|
|
85
|
+
await this.client.setBrightness(device.id, value as number);
|
|
86
|
+
this.platform.log.debug('Set brightness ->', value);
|
|
87
|
+
}
|
|
88
|
+
} catch (error) {
|
|
89
|
+
this.platform.log.error('Failed to set brightness:', error);
|
|
90
|
+
throw error;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Handle "GET" requests from HomeKit for brightness
|
|
96
|
+
*/
|
|
97
|
+
async getBrightness(): Promise<CharacteristicValue> {
|
|
98
|
+
try {
|
|
99
|
+
const device = this.accessory.context.device as Device;
|
|
100
|
+
if (device.supportsBrightness) {
|
|
101
|
+
const brightness = await this.client.getBrightness(device.id);
|
|
102
|
+
this.platform.log.debug('Get brightness ->', brightness);
|
|
103
|
+
return brightness;
|
|
104
|
+
}
|
|
105
|
+
return 100;
|
|
106
|
+
} catch (error) {
|
|
107
|
+
this.platform.log.error('Failed to get brightness:', error);
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
package/src/settings.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This is the name of the platform that users will use to register the plugin in the Homebridge config.json
|
|
3
|
+
*/
|
|
4
|
+
export const PLATFORM_NAME = 'TSVESyncPlatform';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* This must match the name of your plugin as defined the package.json
|
|
8
|
+
*/
|
|
9
|
+
export const PLUGIN_NAME = 'homebridge-tsvesync';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
declare module 'tsvesync' {
|
|
2
|
+
export interface Device {
|
|
3
|
+
id: string;
|
|
4
|
+
name: string;
|
|
5
|
+
model?: string;
|
|
6
|
+
supportsBrightness?: boolean;
|
|
7
|
+
deviceType?: string;
|
|
8
|
+
deviceStatus?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface TSVESyncConfig {
|
|
12
|
+
username: string;
|
|
13
|
+
password: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class TSVESync {
|
|
17
|
+
constructor(config: TSVESyncConfig);
|
|
18
|
+
|
|
19
|
+
login(): Promise<void>;
|
|
20
|
+
getDevices(): Promise<Device[]>;
|
|
21
|
+
setPowerState(deviceId: string, state: boolean): Promise<void>;
|
|
22
|
+
getPowerState(deviceId: string): Promise<boolean>;
|
|
23
|
+
setBrightness(deviceId: string, brightness: number): Promise<void>;
|
|
24
|
+
getBrightness(deviceId: string): Promise<number>;
|
|
25
|
+
}
|
|
26
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2018",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"lib": [
|
|
6
|
+
"ES2015",
|
|
7
|
+
"ES2016",
|
|
8
|
+
"ES2017",
|
|
9
|
+
"ES2018"
|
|
10
|
+
],
|
|
11
|
+
"declaration": true,
|
|
12
|
+
"declarationMap": true,
|
|
13
|
+
"sourceMap": true,
|
|
14
|
+
"outDir": "./dist",
|
|
15
|
+
"rootDir": "./src",
|
|
16
|
+
"strict": true,
|
|
17
|
+
"esModuleInterop": true,
|
|
18
|
+
"noImplicitAny": false,
|
|
19
|
+
"typeRoots": [
|
|
20
|
+
"./node_modules/@types",
|
|
21
|
+
"./src"
|
|
22
|
+
]
|
|
23
|
+
},
|
|
24
|
+
"include": [
|
|
25
|
+
"src"
|
|
26
|
+
],
|
|
27
|
+
"exclude": [
|
|
28
|
+
"**/*.spec.ts"
|
|
29
|
+
]
|
|
30
|
+
}
|