homebridge-kasa-python 2.9.1 → 3.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/config.schema.json +17 -2
- package/dist/accessoryInformation.d.ts +2 -2
- package/dist/accessoryInformation.js +24 -28
- package/dist/accessoryInformation.js.map +1 -1
- package/dist/config.d.ts +5 -1
- package/dist/config.js +24 -16
- package/dist/config.js.map +1 -1
- package/dist/devices/baseDevice.d.ts +61 -0
- package/dist/devices/baseDevice.js +361 -0
- package/dist/devices/baseDevice.js.map +1 -0
- package/dist/devices/baseParent.d.ts +19 -0
- package/dist/devices/baseParent.js +150 -0
- package/dist/devices/baseParent.js.map +1 -0
- package/dist/devices/create.d.ts +3 -3
- package/dist/devices/create.js +21 -27
- package/dist/devices/create.js.map +1 -1
- package/dist/devices/descriptorHelpers.d.ts +14 -0
- package/dist/devices/descriptorHelpers.js +146 -0
- package/dist/devices/descriptorHelpers.js.map +1 -0
- package/dist/devices/deviceManager.d.ts +13 -13
- package/dist/devices/deviceManager.js +115 -137
- package/dist/devices/deviceManager.js.map +1 -1
- package/dist/devices/{kasaDevices.d.ts → deviceTypes.d.ts} +58 -42
- package/dist/devices/deviceTypes.js +20 -0
- package/dist/devices/deviceTypes.js.map +1 -0
- package/dist/devices/homekitLightBulb.d.ts +7 -28
- package/dist/devices/homekitLightBulb.js +42 -365
- package/dist/devices/homekitLightBulb.js.map +1 -1
- package/dist/devices/homekitPlug.d.ts +6 -25
- package/dist/devices/homekitPlug.js +21 -322
- package/dist/devices/homekitPlug.js.map +1 -1
- package/dist/devices/homekitPowerStrip.d.ts +6 -26
- package/dist/devices/homekitPowerStrip.js +25 -345
- package/dist/devices/homekitPowerStrip.js.map +1 -1
- package/dist/devices/homekitSwitch.d.ts +5 -26
- package/dist/devices/homekitSwitch.js +18 -302
- package/dist/devices/homekitSwitch.js.map +1 -1
- package/dist/devices/homekitSwitchWithChildren.d.ts +5 -29
- package/dist/devices/homekitSwitchWithChildren.js +24 -371
- package/dist/devices/homekitSwitchWithChildren.js.map +1 -1
- package/dist/energyCharacteristics.d.ts +8 -0
- package/dist/energyCharacteristics.js +88 -0
- package/dist/energyCharacteristics.js.map +1 -0
- package/dist/platform.d.ts +4 -2
- package/dist/platform.js +68 -63
- package/dist/platform.js.map +1 -1
- package/dist/python/kasaApi.py +68 -55
- package/dist/python/pythonChecker.js +2 -2
- package/dist/python/pythonChecker.js.map +1 -1
- package/package.json +15 -15
- package/requirements.txt +2 -2
- package/dist/devices/index.d.ts +0 -33
- package/dist/devices/index.js +0 -100
- package/dist/devices/index.js.map +0 -1
- package/dist/devices/kasaDevices.js +0 -102
- package/dist/devices/kasaDevices.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create.js","sourceRoot":"","sources":["../../src/devices/create.ts"],"names":[],"mappings":"AACA,OAAO,sBAAsB,MAAM,uBAAuB,CAAC;AAC3D,OAAO,iBAAiB,MAAM,kBAAkB,CAAC;AACjD,OAAO,uBAAuB,MAAM,wBAAwB,CAAC;AAC7D,OAAO,mBAAmB,MAAM,oBAAoB,CAAC;AACrD,OAAO,+BAA+B,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"create.js","sourceRoot":"","sources":["../../src/devices/create.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Characteristic, CharacteristicValue } from 'homebridge';
|
|
2
|
+
import type { CharacteristicDescriptor, DescriptorContext } from './deviceTypes.js';
|
|
3
|
+
import type { EnergyCharacteristics } from '../energyCharacteristics.js';
|
|
4
|
+
export declare function buildOnDescriptor(C: typeof Characteristic, setState?: (value: CharacteristicValue, context: DescriptorContext) => Promise<void>): CharacteristicDescriptor;
|
|
5
|
+
export declare function buildBrightnessDescriptor(C: typeof Characteristic, setBrightness: (value: number, context: DescriptorContext) => Promise<void>): CharacteristicDescriptor;
|
|
6
|
+
export declare function buildColorTemperatureDescriptor(C: typeof Characteristic, setColorTemp: (value: number, context: DescriptorContext) => Promise<void>): CharacteristicDescriptor;
|
|
7
|
+
export declare function buildHSVDescriptors(C: typeof Characteristic, enqueueHSV: (partial: {
|
|
8
|
+
hue?: number;
|
|
9
|
+
saturation?: number;
|
|
10
|
+
}, context: DescriptorContext) => Promise<void>): CharacteristicDescriptor[];
|
|
11
|
+
export declare function buildOutletInUseDescriptor(C: typeof Characteristic, hasEnergy: boolean): CharacteristicDescriptor;
|
|
12
|
+
export declare function buildEnergyDescriptors(energyCharacteristics: EnergyCharacteristics): CharacteristicDescriptor[];
|
|
13
|
+
export declare function buildFanActiveDescriptor(C: typeof Characteristic, setActive: (active: boolean, context: DescriptorContext) => Promise<void>): CharacteristicDescriptor;
|
|
14
|
+
export declare function buildFanRotationDescriptor(C: typeof Characteristic, setRotation: (fan_speed_level: number, context: DescriptorContext) => Promise<void>): CharacteristicDescriptor;
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
export function buildOnDescriptor(C, setState) {
|
|
2
|
+
return {
|
|
3
|
+
type: C.On,
|
|
4
|
+
name: 'On',
|
|
5
|
+
writable: true,
|
|
6
|
+
getInitial: context => (context.child ? context.child.state : context.device.state) ?? false,
|
|
7
|
+
getCurrent: context => (context.child ? context.child.state : context.device.state) ?? false,
|
|
8
|
+
applySet: setState
|
|
9
|
+
? async (value, context) => {
|
|
10
|
+
await setState(value, context);
|
|
11
|
+
const state = Boolean(value);
|
|
12
|
+
if (context.child) {
|
|
13
|
+
context.child.state = state;
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
context.device.state = state;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
: undefined,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export function buildBrightnessDescriptor(C, setBrightness) {
|
|
23
|
+
return {
|
|
24
|
+
type: C.Brightness,
|
|
25
|
+
name: 'Brightness',
|
|
26
|
+
writable: true,
|
|
27
|
+
getInitial: context => (context.child ? context.child.brightness : context.device.brightness) ?? 0,
|
|
28
|
+
getCurrent: context => (context.child ? context.child.brightness : context.device.brightness) ?? 0,
|
|
29
|
+
applySet: async (value, context) => {
|
|
30
|
+
const brightness = Number(value);
|
|
31
|
+
await setBrightness(brightness, context);
|
|
32
|
+
if (context.child) {
|
|
33
|
+
context.child.brightness = brightness;
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
context.device.brightness = brightness;
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
export function buildColorTemperatureDescriptor(C, setColorTemp) {
|
|
42
|
+
return {
|
|
43
|
+
type: C.ColorTemperature,
|
|
44
|
+
name: 'ColorTemperature',
|
|
45
|
+
writable: true,
|
|
46
|
+
getInitial: context => (context.child ? context.child.color_temp : context.device.color_temp) ?? 0,
|
|
47
|
+
getCurrent: context => (context.child ? context.child.color_temp : context.device.color_temp) ?? 0,
|
|
48
|
+
applySet: async (value, context) => {
|
|
49
|
+
const colorTemp = Number(value);
|
|
50
|
+
await setColorTemp(colorTemp, context);
|
|
51
|
+
if (context.child) {
|
|
52
|
+
context.child.color_temp = colorTemp;
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
context.device.color_temp = colorTemp;
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
export function buildHSVDescriptors(C, enqueueHSV) {
|
|
61
|
+
return [
|
|
62
|
+
{
|
|
63
|
+
type: C.Hue,
|
|
64
|
+
name: 'Hue',
|
|
65
|
+
writable: true,
|
|
66
|
+
getInitial: context => (context.child ? context.child.hsv?.hue : context.device.hsv?.hue) ?? 0,
|
|
67
|
+
getCurrent: context => (context.child ? context.child.hsv?.hue : context.device.hsv?.hue) ?? 0,
|
|
68
|
+
applySet: async (value, context) => enqueueHSV({ hue: Number(value) }, context),
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
type: C.Saturation,
|
|
72
|
+
name: 'Saturation',
|
|
73
|
+
writable: true,
|
|
74
|
+
getInitial: context => (context.child ? context.child.hsv?.saturation : context.device.hsv?.saturation) ?? 0,
|
|
75
|
+
getCurrent: context => (context.child ? context.child.hsv?.saturation : context.device.hsv?.saturation) ?? 0,
|
|
76
|
+
applySet: async (value, context) => enqueueHSV({ saturation: Number(value) }, context),
|
|
77
|
+
},
|
|
78
|
+
];
|
|
79
|
+
}
|
|
80
|
+
export function buildOutletInUseDescriptor(C, hasEnergy) {
|
|
81
|
+
const energy = (context) => ((context.child ? context.child.energy?.power : context.device.energy?.power) ?? 0) >= 1.0;
|
|
82
|
+
const state = (context) => (context.child ? context.child.state : context.device.state) ?? false;
|
|
83
|
+
return {
|
|
84
|
+
type: C.OutletInUse,
|
|
85
|
+
name: 'OutletInUse',
|
|
86
|
+
writable: false,
|
|
87
|
+
getInitial: (context) => (hasEnergy ? energy(context) : state(context)),
|
|
88
|
+
getCurrent: (context) => (hasEnergy ? energy(context) : state(context)),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
export function buildEnergyDescriptors(energyCharacteristics) {
|
|
92
|
+
const definitions = [
|
|
93
|
+
[energyCharacteristics.Volts, 'voltage', 'Volts'],
|
|
94
|
+
[energyCharacteristics.Amperes, 'current', 'Amperes'],
|
|
95
|
+
[energyCharacteristics.Watts, 'power', 'Watts'],
|
|
96
|
+
[energyCharacteristics.KiloWattHours, 'total', 'KiloWattHours'],
|
|
97
|
+
];
|
|
98
|
+
return definitions.map(([characteristicType, field, label]) => ({
|
|
99
|
+
type: characteristicType,
|
|
100
|
+
name: label,
|
|
101
|
+
writable: false,
|
|
102
|
+
getInitial: context => (context.child ? context.child.energy?.[field] : context.device.energy?.[field]) ?? 0,
|
|
103
|
+
getCurrent: context => (context.child ? context.child.energy?.[field] : context.device.energy?.[field]) ?? 0,
|
|
104
|
+
}));
|
|
105
|
+
}
|
|
106
|
+
export function buildFanActiveDescriptor(C, setActive) {
|
|
107
|
+
return {
|
|
108
|
+
type: C.Active,
|
|
109
|
+
name: 'Active',
|
|
110
|
+
writable: true,
|
|
111
|
+
getInitial: context => ((context.child ? context.child.state : context.device.state) ? C.Active.ACTIVE : C.Active.INACTIVE),
|
|
112
|
+
getCurrent: context => ((context.child ? context.child.state : context.device.state) ? C.Active.ACTIVE : C.Active.INACTIVE),
|
|
113
|
+
applySet: async (value, context) => {
|
|
114
|
+
const active = value === C.Active.ACTIVE;
|
|
115
|
+
await setActive(active, context);
|
|
116
|
+
if (context.child) {
|
|
117
|
+
context.child.state = active;
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
context.device.state = active;
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
export function buildFanRotationDescriptor(C, setRotation) {
|
|
126
|
+
return {
|
|
127
|
+
type: C.RotationSpeed,
|
|
128
|
+
name: 'RotationSpeed',
|
|
129
|
+
writable: true,
|
|
130
|
+
getInitial: context => (context.child ? context.child.fan_speed_level : context.device.fan_speed_level) ?? 0,
|
|
131
|
+
getCurrent: context => (context.child ? context.child.fan_speed_level : context.device.fan_speed_level) ?? 0,
|
|
132
|
+
applySet: async (value, context) => {
|
|
133
|
+
const fan_speed_level = Math.min(100, Math.ceil(Math.max(0, Number(value) || 0) / 25) * 25);
|
|
134
|
+
await setRotation(fan_speed_level, context);
|
|
135
|
+
if (context.child) {
|
|
136
|
+
context.child.fan_speed_level = fan_speed_level;
|
|
137
|
+
context.child.state = fan_speed_level > 0;
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
context.device.fan_speed_level = fan_speed_level;
|
|
141
|
+
context.device.state = fan_speed_level > 0;
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=descriptorHelpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"descriptorHelpers.js","sourceRoot":"","sources":["../../src/devices/descriptorHelpers.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { CharacteristicValue } from 'homebridge';
|
|
2
2
|
import KasaPythonPlatform from '../platform.js';
|
|
3
|
-
import type { HSV, SysInfo } from './
|
|
3
|
+
import type { HSV, SysInfo } from './deviceTypes.js';
|
|
4
4
|
import { EventEmitter } from 'events';
|
|
5
5
|
export declare const deviceEventEmitter: EventEmitter<[never]>;
|
|
6
|
-
type
|
|
6
|
+
type ControlValue = CharacteristicValue | HSV;
|
|
7
7
|
export default class DeviceManager {
|
|
8
8
|
private platform;
|
|
9
9
|
private log;
|
|
@@ -11,21 +11,21 @@ export default class DeviceManager {
|
|
|
11
11
|
private username;
|
|
12
12
|
private password;
|
|
13
13
|
private additionalBroadcasts;
|
|
14
|
-
private
|
|
15
|
-
private
|
|
16
|
-
private
|
|
14
|
+
private manualDeviceHosts;
|
|
15
|
+
private excludeMacs;
|
|
16
|
+
private includeMacs;
|
|
17
17
|
constructor(platform: KasaPythonPlatform);
|
|
18
|
-
private convertManualDevices;
|
|
19
|
-
private updateDeviceAlias;
|
|
20
|
-
private isKasaDevice;
|
|
21
|
-
private readConfigFile;
|
|
22
|
-
private writeConfigFile;
|
|
23
18
|
discoverDevices(): Promise<void>;
|
|
24
|
-
private
|
|
25
|
-
private shouldConvertManualDevices;
|
|
19
|
+
private persistDiscoveredDevice;
|
|
26
20
|
getSysInfo(host: string): Promise<SysInfo | undefined>;
|
|
27
|
-
controlDevice(host: string, feature: string, value:
|
|
21
|
+
controlDevice(host: string, feature: string, value: ControlValue, childNum?: number): Promise<void>;
|
|
22
|
+
private mapFeatureToAction;
|
|
28
23
|
private performDeviceAction;
|
|
24
|
+
private updateDeviceAlias;
|
|
25
|
+
private needsManualDevicesNormalization;
|
|
26
|
+
private normalizeManualDevices;
|
|
27
|
+
private readConfigFile;
|
|
28
|
+
private writeConfigFile;
|
|
29
29
|
private handleAxiosError;
|
|
30
30
|
}
|
|
31
31
|
export {};
|
|
@@ -12,9 +12,9 @@ export default class DeviceManager {
|
|
|
12
12
|
username;
|
|
13
13
|
password;
|
|
14
14
|
additionalBroadcasts;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
manualDeviceHosts;
|
|
16
|
+
excludeMacs;
|
|
17
|
+
includeMacs;
|
|
18
18
|
constructor(platform) {
|
|
19
19
|
this.platform = platform;
|
|
20
20
|
this.log = platform.log;
|
|
@@ -22,166 +22,103 @@ export default class DeviceManager {
|
|
|
22
22
|
this.password = platform.config.password;
|
|
23
23
|
this.apiUrl = `http://127.0.0.1:${platform.port}`;
|
|
24
24
|
this.additionalBroadcasts = platform.config.discoveryOptions.additionalBroadcasts;
|
|
25
|
-
this.
|
|
26
|
-
this.
|
|
27
|
-
this.
|
|
28
|
-
}
|
|
29
|
-
convertManualDevices(manualDevices) {
|
|
30
|
-
return manualDevices.map(device => {
|
|
31
|
-
if (typeof device === 'string') {
|
|
32
|
-
return { host: device, alias: 'Will Be Filled By Plug-In Automatically' };
|
|
33
|
-
}
|
|
34
|
-
else if ('breakoutChildDevices' in device) {
|
|
35
|
-
delete device.breakoutChildDevices;
|
|
36
|
-
}
|
|
37
|
-
else if ('host' in device && !('alias' in device)) {
|
|
38
|
-
device.alias = 'Will Be Filled By Plug-In Automatically';
|
|
39
|
-
}
|
|
40
|
-
return device;
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
updateDeviceAlias(device) {
|
|
44
|
-
let sysInfo;
|
|
45
|
-
if (this.isKasaDevice(device)) {
|
|
46
|
-
sysInfo = device.sys_info;
|
|
47
|
-
}
|
|
48
|
-
else {
|
|
49
|
-
sysInfo = device;
|
|
50
|
-
}
|
|
51
|
-
if (sysInfo.alias) {
|
|
52
|
-
const aliasMappings = {
|
|
53
|
-
'TP-LINK_Power Strip_': 'Power Strip',
|
|
54
|
-
'TP-LINK_Smart Plug_': 'Smart Plug',
|
|
55
|
-
'TP-LINK_Smart Bulb_': 'Smart Bulb',
|
|
56
|
-
};
|
|
57
|
-
for (const [pattern, replacement] of Object.entries(aliasMappings)) {
|
|
58
|
-
if (sysInfo.alias.includes(pattern)) {
|
|
59
|
-
sysInfo.alias = `${replacement} ${sysInfo.alias.slice(-4)}`;
|
|
60
|
-
break;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
isKasaDevice(device) {
|
|
66
|
-
return device.sys_info !== undefined;
|
|
67
|
-
}
|
|
68
|
-
async readConfigFile(configPath) {
|
|
69
|
-
try {
|
|
70
|
-
const configData = await fs.readFile(configPath, 'utf8');
|
|
71
|
-
return JSON.parse(configData);
|
|
72
|
-
}
|
|
73
|
-
catch (error) {
|
|
74
|
-
this.log.error(`Error reading config file: ${String(error)}`);
|
|
75
|
-
throw error;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
async writeConfigFile(configPath, fileConfig) {
|
|
79
|
-
try {
|
|
80
|
-
await fs.writeFile(configPath, JSON.stringify(fileConfig, null, 2), 'utf8');
|
|
81
|
-
}
|
|
82
|
-
catch (error) {
|
|
83
|
-
this.log.error(`Error writing config file: ${String(error)}`);
|
|
84
|
-
}
|
|
25
|
+
this.manualDeviceHosts = platform.config.discoveryOptions.manualDevices.map(d => d.host);
|
|
26
|
+
this.excludeMacs = platform.config.discoveryOptions.excludeMacAddresses;
|
|
27
|
+
this.includeMacs = platform.config.discoveryOptions.includeMacAddresses;
|
|
85
28
|
}
|
|
86
29
|
async discoverDevices() {
|
|
87
|
-
this.log.info('
|
|
30
|
+
this.log.info('Starting device discovery (SSE)...');
|
|
88
31
|
try {
|
|
89
|
-
const
|
|
32
|
+
const authConfig = this.username && this.password
|
|
90
33
|
? { auth: { username: this.username, password: this.password } }
|
|
91
34
|
: {};
|
|
92
35
|
const response = await axios.post(`${this.apiUrl}/discover`, {
|
|
93
36
|
additionalBroadcasts: this.additionalBroadcasts,
|
|
94
|
-
manualDevices: this.
|
|
95
|
-
excludeMacAddresses: this.
|
|
96
|
-
includeMacAddresses: this.
|
|
97
|
-
},
|
|
98
|
-
this.log.info('Discovery initiated:', response.data);
|
|
37
|
+
manualDevices: this.manualDeviceHosts,
|
|
38
|
+
excludeMacAddresses: this.excludeMacs,
|
|
39
|
+
includeMacAddresses: this.includeMacs,
|
|
40
|
+
}, authConfig);
|
|
41
|
+
this.log.info('Discovery initiated:', Object.keys(response.data).length, 'potential entries');
|
|
99
42
|
const configPath = path.join(this.platform.storagePath, 'config.json');
|
|
100
43
|
const fileConfig = await this.readConfigFile(configPath);
|
|
101
|
-
const
|
|
102
|
-
if (!
|
|
103
|
-
this.log.error('KasaPython configuration
|
|
44
|
+
const platformSection = fileConfig.platforms.find((p) => p.platform === 'KasaPython');
|
|
45
|
+
if (!platformSection) {
|
|
46
|
+
this.log.error('KasaPython configuration missing in config file.');
|
|
104
47
|
}
|
|
105
48
|
else {
|
|
106
|
-
|
|
49
|
+
platformSection.manualDevices = platformSection.manualDevices || [];
|
|
107
50
|
}
|
|
108
51
|
const eventSource = new EventSource(`${this.apiUrl}/stream`);
|
|
109
52
|
eventSource.onmessage = (event) => {
|
|
110
53
|
try {
|
|
111
54
|
const data = JSON.parse(event.data);
|
|
112
|
-
this.log.debug('
|
|
55
|
+
this.log.debug('SSE event data:', data);
|
|
113
56
|
if (data.status === 'discovery_complete') {
|
|
114
57
|
this.log.info('Device discovery complete.');
|
|
115
58
|
eventSource.close();
|
|
59
|
+
return;
|
|
116
60
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
const device = {
|
|
123
|
-
sys_info: data.sys_info,
|
|
124
|
-
feature_info: data.feature_info,
|
|
125
|
-
last_seen: new Date(),
|
|
126
|
-
offline: false,
|
|
127
|
-
};
|
|
128
|
-
this.log.info(`Received device info for ${device.sys_info.host}`);
|
|
129
|
-
this.processDevice(device, platformConfig);
|
|
130
|
-
deviceEventEmitter.emit('deviceDiscovered', device);
|
|
61
|
+
if (!data.sys_info || !data.sys_info.host) {
|
|
62
|
+
this.log.error('Invalid device payload (missing sys_info.host):', data);
|
|
63
|
+
return;
|
|
131
64
|
}
|
|
65
|
+
const device = {
|
|
66
|
+
sys_info: data.sys_info,
|
|
67
|
+
feature_info: data.feature_info,
|
|
68
|
+
last_seen: new Date(),
|
|
69
|
+
offline: false,
|
|
70
|
+
};
|
|
71
|
+
this.log.info(`Discovered device: ${device.sys_info.alias} (${device.sys_info.host})`);
|
|
72
|
+
this.updateDeviceAlias(device.sys_info);
|
|
73
|
+
this.persistDiscoveredDevice(device, platformSection);
|
|
74
|
+
deviceEventEmitter.emit('deviceDiscovered', device);
|
|
132
75
|
}
|
|
133
|
-
catch (
|
|
134
|
-
this.log.error('Error parsing SSE event
|
|
76
|
+
catch (error) {
|
|
77
|
+
this.log.error('Error parsing discovery SSE event:', error);
|
|
135
78
|
}
|
|
136
79
|
};
|
|
137
|
-
eventSource.onerror = (
|
|
138
|
-
this.log.error('EventSource error:',
|
|
80
|
+
eventSource.onerror = (error) => {
|
|
81
|
+
this.log.error('Discovery EventSource error:', error);
|
|
139
82
|
eventSource.close();
|
|
140
83
|
};
|
|
141
84
|
await new Promise(resolve => setTimeout(resolve, 10000));
|
|
142
85
|
eventSource.close();
|
|
143
|
-
if (
|
|
144
|
-
|
|
145
|
-
if (typeof
|
|
86
|
+
if (platformSection) {
|
|
87
|
+
platformSection.manualDevices = platformSection.manualDevices.filter((entry) => {
|
|
88
|
+
if (typeof entry === 'string') {
|
|
146
89
|
return true;
|
|
147
90
|
}
|
|
148
|
-
|
|
149
|
-
this.log.warn(`Removing manual device without host: ${JSON.stringify(
|
|
91
|
+
if (!entry.host) {
|
|
92
|
+
this.log.warn(`Removing manual device without host: ${JSON.stringify(entry)}`);
|
|
150
93
|
return false;
|
|
151
94
|
}
|
|
152
95
|
return true;
|
|
153
96
|
});
|
|
154
|
-
if (this.
|
|
155
|
-
|
|
97
|
+
if (this.needsManualDevicesNormalization(platformSection.manualDevices)) {
|
|
98
|
+
platformSection.manualDevices = this.normalizeManualDevices(platformSection.manualDevices);
|
|
156
99
|
}
|
|
157
100
|
await this.writeConfigFile(configPath, fileConfig);
|
|
158
|
-
this.platform.config = parseConfig(
|
|
101
|
+
this.platform.config = parseConfig(platformSection);
|
|
159
102
|
}
|
|
160
103
|
}
|
|
161
104
|
catch (error) {
|
|
162
105
|
this.handleAxiosError(error, 'discoverDevices');
|
|
163
106
|
}
|
|
164
107
|
}
|
|
165
|
-
|
|
108
|
+
persistDiscoveredDevice(device, platformConfig) {
|
|
166
109
|
try {
|
|
167
|
-
this.updateDeviceAlias(device);
|
|
168
110
|
if (platformConfig.manualDevices) {
|
|
169
|
-
const
|
|
170
|
-
if (
|
|
171
|
-
|
|
172
|
-
|
|
111
|
+
const existing = platformConfig.manualDevices.find((d) => d.host === device.sys_info.host);
|
|
112
|
+
if (existing) {
|
|
113
|
+
existing.host = device.sys_info.host;
|
|
114
|
+
existing.alias = device.sys_info.alias;
|
|
173
115
|
}
|
|
174
116
|
}
|
|
175
117
|
}
|
|
176
118
|
catch (error) {
|
|
177
|
-
this.log.error(
|
|
119
|
+
this.log.error('Error persisting discovered device:', error);
|
|
178
120
|
}
|
|
179
121
|
}
|
|
180
|
-
shouldConvertManualDevices(manualDevices) {
|
|
181
|
-
return manualDevices.length > 0 &&
|
|
182
|
-
(typeof manualDevices[0] === 'string' ||
|
|
183
|
-
manualDevices.some((device) => typeof device !== 'string'));
|
|
184
|
-
}
|
|
185
122
|
async getSysInfo(host) {
|
|
186
123
|
try {
|
|
187
124
|
const response = await axios.post(`${this.apiUrl}/getSysInfo`, { host });
|
|
@@ -198,28 +135,27 @@ export default class DeviceManager {
|
|
|
198
135
|
throw error;
|
|
199
136
|
}
|
|
200
137
|
}
|
|
201
|
-
async controlDevice(host, feature, value,
|
|
202
|
-
|
|
138
|
+
async controlDevice(host, feature, value, childNum) {
|
|
139
|
+
const action = this.mapFeatureToAction(feature, value);
|
|
140
|
+
await this.performDeviceAction(host, feature, action, value, childNum);
|
|
141
|
+
}
|
|
142
|
+
mapFeatureToAction(feature, value) {
|
|
203
143
|
switch (feature) {
|
|
204
144
|
case 'brightness':
|
|
205
145
|
case 'color_temp':
|
|
206
146
|
case 'fan_speed_level':
|
|
207
|
-
|
|
208
|
-
break;
|
|
147
|
+
return `set_${feature}`;
|
|
209
148
|
case 'hsv':
|
|
210
|
-
|
|
211
|
-
break;
|
|
149
|
+
return 'set_hsv';
|
|
212
150
|
case 'state':
|
|
213
|
-
|
|
214
|
-
break;
|
|
151
|
+
return value ? 'turn_on' : 'turn_off';
|
|
215
152
|
default:
|
|
216
153
|
throw new Error(`Unsupported feature: ${feature}`);
|
|
217
154
|
}
|
|
218
|
-
await this.performDeviceAction(host, feature, action, value, child_num);
|
|
219
155
|
}
|
|
220
156
|
async performDeviceAction(host, feature, action, value, childNumber) {
|
|
221
157
|
const url = `${this.apiUrl}/controlDevice`;
|
|
222
|
-
const
|
|
158
|
+
const payload = {
|
|
223
159
|
host,
|
|
224
160
|
feature,
|
|
225
161
|
action,
|
|
@@ -227,45 +163,87 @@ export default class DeviceManager {
|
|
|
227
163
|
...(childNumber !== undefined && { child_num: childNumber }),
|
|
228
164
|
};
|
|
229
165
|
try {
|
|
230
|
-
const response = await axios.post(url,
|
|
166
|
+
const response = await axios.post(url, payload);
|
|
231
167
|
if (response.data.status !== 'success') {
|
|
232
|
-
this.log.error(`
|
|
168
|
+
this.log.error(`Action failed (${feature}/${action}) on ${host}: ${response.data.message}`);
|
|
233
169
|
}
|
|
234
170
|
}
|
|
235
171
|
catch (error) {
|
|
236
172
|
this.handleAxiosError(error, 'controlDevice');
|
|
237
173
|
}
|
|
238
174
|
}
|
|
175
|
+
updateDeviceAlias(sysInfo) {
|
|
176
|
+
if (!sysInfo.alias) {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
const aliasPatterns = {
|
|
180
|
+
'TP-LINK_Power Strip_': 'Power Strip',
|
|
181
|
+
'TP-LINK_Smart Plug_': 'Smart Plug',
|
|
182
|
+
'TP-LINK_Smart Bulb_': 'Smart Bulb',
|
|
183
|
+
};
|
|
184
|
+
for (const [pattern, replacement] of Object.entries(aliasPatterns)) {
|
|
185
|
+
if (sysInfo.alias.includes(pattern)) {
|
|
186
|
+
sysInfo.alias = `${replacement} ${sysInfo.alias.slice(-4)}`;
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
needsManualDevicesNormalization(manualDevices) {
|
|
192
|
+
return manualDevices.length > 0 &&
|
|
193
|
+
(typeof manualDevices[0] === 'string' ||
|
|
194
|
+
manualDevices.some(entry => typeof entry !== 'string'));
|
|
195
|
+
}
|
|
196
|
+
normalizeManualDevices(manualDevices) {
|
|
197
|
+
return manualDevices.map(entry => {
|
|
198
|
+
if (typeof entry === 'string') {
|
|
199
|
+
return { host: entry, alias: 'Will Be Filled By Plug-In Automatically' };
|
|
200
|
+
}
|
|
201
|
+
else if ('host' in entry && !('alias' in entry)) {
|
|
202
|
+
entry.alias = 'Will Be Filled By Plug-In Automatically';
|
|
203
|
+
}
|
|
204
|
+
else if ('breakoutChildDevices' in entry) {
|
|
205
|
+
delete entry.breakoutChildDevices;
|
|
206
|
+
}
|
|
207
|
+
return entry;
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
async readConfigFile(configPath) {
|
|
211
|
+
const data = await fs.readFile(configPath, 'utf8');
|
|
212
|
+
return JSON.parse(data);
|
|
213
|
+
}
|
|
214
|
+
async writeConfigFile(configPath, fileConfig) {
|
|
215
|
+
try {
|
|
216
|
+
await fs.writeFile(configPath, JSON.stringify(fileConfig, null, 2), 'utf8');
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
this.log.error(`Error writing config file: ${String(error)}`);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
239
222
|
handleAxiosError(error, context) {
|
|
240
223
|
if (axios.isAxiosError(error)) {
|
|
241
224
|
if (error.response) {
|
|
242
225
|
const statusCode = error.response.status;
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
this.log.error(`Error during ${context}: ${errorMessage}`);
|
|
246
|
-
}
|
|
247
|
-
else {
|
|
248
|
-
this.log.error(`Error during ${context}: ${statusCode} - ${errorMessage}`);
|
|
249
|
-
}
|
|
226
|
+
const message = error.response.data?.error || error.response.statusText || 'Unknown error';
|
|
227
|
+
this.log.error(`[${context}] HTTP ${statusCode}: ${message}`);
|
|
250
228
|
}
|
|
251
229
|
else if (error.code === 'ECONNREFUSED') {
|
|
252
|
-
this.log.error(`
|
|
230
|
+
this.log.error(`[${context}] Connection refused (device API offline?)`);
|
|
253
231
|
}
|
|
254
232
|
else if (error.code === 'ETIMEDOUT') {
|
|
255
|
-
this.log.error(`
|
|
233
|
+
this.log.error(`[${context}] Connection timed out (network issue)`);
|
|
256
234
|
}
|
|
257
235
|
else {
|
|
258
|
-
this.log.error(`
|
|
236
|
+
this.log.error(`[${context}] Axios error: ${error.message}`);
|
|
259
237
|
}
|
|
260
238
|
}
|
|
261
239
|
else if (error instanceof Error) {
|
|
262
|
-
this.log.error(`
|
|
240
|
+
this.log.error(`[${context}] Error: ${error.message}`);
|
|
263
241
|
if (error.stack) {
|
|
264
242
|
this.log.debug(error.stack);
|
|
265
243
|
}
|
|
266
244
|
}
|
|
267
245
|
else {
|
|
268
|
-
this.log.error(`
|
|
246
|
+
this.log.error(`[${context}] Unknown error: ${JSON.stringify(error)}`);
|
|
269
247
|
}
|
|
270
248
|
}
|
|
271
249
|
}
|