Skip to content

Commit 686832d

Browse files
authored
Merge pull request #17 from russellquinn/latest
Fix: restore cached accessories without recreating them
2 parents d852e40 + a18207c commit 686832d

File tree

1 file changed

+80
-22
lines changed

1 file changed

+80
-22
lines changed

src/index.ts

Lines changed: 80 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -48,43 +48,64 @@ interface SensorConfig {
4848
class AirGradientPlatform implements DynamicPlatformPlugin {
4949
public readonly log: Logging;
5050
public readonly api: API;
51-
public readonly accessories: PlatformAccessory[] = [];
51+
52+
// Keep a stable map of cached and newly-created accessories by UUID
53+
private readonly accessories = new Map<string, PlatformAccessory>();
54+
55+
// Persist sensor configs to act on them after didFinishLaunching
56+
private readonly sensorConfigs: SensorConfig[] = [];
5257

5358
constructor(log: Logging, config: PlatformConfig, api: API) {
5459
this.log = log;
5560
this.api = api;
5661

5762
hap = api.hap;
5863

59-
if (config.sensors) {
64+
if (Array.isArray(config?.sensors)) {
6065
for (const sensorConfig of config.sensors as SensorConfig[]) {
61-
this.log.info('Initializing sensor with serial number:', sensorConfig.serialno);
62-
this.addAccessory(sensorConfig);
66+
if (sensorConfig?.serialno) {
67+
this.sensorConfigs.push(sensorConfig);
68+
this.log.info('Queued sensor for init with serial number:', sensorConfig.serialno);
69+
}
6370
}
6471
}
6572

66-
api.on('didFinishLaunching', () => {
73+
// Only manipulate accessories after Homebridge has finished launching, so cache restore happens first.
74+
this.api.on('didFinishLaunching', () => {
6775
this.log.info('Did finish launching');
68-
});
69-
}
70-
71-
addAccessory(sensorConfig: SensorConfig) {
72-
const uuid = hap.uuid.generate(sensorConfig.serialno);
73-
const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid);
7476

75-
if (existingAccessory) {
76-
this.log.info('Restoring existing accessory from cache:', existingAccessory.displayName);
77-
new AirGradientSensor(this, existingAccessory, sensorConfig);
78-
} else {
79-
this.log.info('Adding new accessory for serial number:', sensorConfig.serialno);
80-
const accessory = new this.api.platformAccessory(`AirGradient Sensor ${sensorConfig.serialno}`, uuid);
81-
new AirGradientSensor(this, accessory, sensorConfig);
82-
this.api.registerPlatformAccessories('homebridge-airgradient', 'AirGradientPlatform', [accessory]);
83-
}
77+
for (const sensorConfig of this.sensorConfigs) {
78+
const uuid = hap.uuid.generate(sensorConfig.serialno);
79+
const cached = this.accessories.get(uuid);
80+
81+
if (cached) {
82+
this.log.info('Restoring existing accessory from cache:', cached.displayName);
83+
if (!cached.context.serial) {
84+
cached.context.serial = sensorConfig.serialno;
85+
}
86+
new AirGradientSensor(this, cached, sensorConfig);
87+
} else {
88+
this.log.info('Adding new accessory for serial number:', sensorConfig.serialno);
89+
const accessory = new this.api.platformAccessory(
90+
`AirGradient Sensor ${sensorConfig.serialno}`,
91+
uuid,
92+
);
93+
accessory.context.serial = sensorConfig.serialno;
94+
new AirGradientSensor(this, accessory, sensorConfig);
95+
this.api.registerPlatformAccessories(
96+
'homebridge-airgradient',
97+
'AirGradientPlatform',
98+
[accessory],
99+
);
100+
101+
this.accessories.set(uuid, accessory);
102+
}
103+
}
104+
});
84105
}
85106

86107
configureAccessory(accessory: PlatformAccessory) {
87-
this.accessories.push(accessory);
108+
this.accessories.set(accessory.UUID, accessory);
88109
}
89110
}
90111

@@ -135,7 +156,44 @@ class AirGradientSensor {
135156
this.serviceHumid = this.accessory.getService(hap.Service.HumiditySensor) ||
136157
this.accessory.addService(hap.Service.HumiditySensor);
137158

138-
this.updateCharacteristics();
159+
// Ensure all optional characteristics exist (getCharacteristic ensures creation for optionals)
160+
this.service.getCharacteristic(hap.Characteristic.AirQuality);
161+
this.service.getCharacteristic(hap.Characteristic.PM2_5Density);
162+
this.service.getCharacteristic(hap.Characteristic.PM10Density);
163+
164+
if (!this.service.testCharacteristic(hap.Characteristic.VOCDensity)) {
165+
this.service.addCharacteristic(hap.Characteristic.VOCDensity);
166+
}
167+
if (!this.service.testCharacteristic(hap.Characteristic.NitrogenDioxideDensity)) {
168+
this.service.addCharacteristic(hap.Characteristic.NitrogenDioxideDensity);
169+
}
170+
171+
this.serviceCO2.getCharacteristic(hap.Characteristic.CarbonDioxideDetected);
172+
this.serviceCO2.getCharacteristic(hap.Characteristic.CarbonDioxideLevel);
173+
174+
// Initialize safe placeholder values so the Home hub never sees "missing" nodes
175+
176+
this.service.updateCharacteristic(
177+
hap.Characteristic.AirQuality,
178+
(hap.Characteristic.AirQuality as any).UNKNOWN ?? hap.Characteristic.AirQuality.FAIR,
179+
);
180+
this.service.updateCharacteristic(hap.Characteristic.PM2_5Density, 0);
181+
this.service.updateCharacteristic(hap.Characteristic.PM10Density, 0);
182+
this.service.updateCharacteristic(hap.Characteristic.VOCDensity, 0);
183+
this.service.updateCharacteristic(hap.Characteristic.NitrogenDioxideDensity, 0);
184+
185+
this.serviceCO2.updateCharacteristic(
186+
hap.Characteristic.CarbonDioxideDetected,
187+
hap.Characteristic.CarbonDioxideDetected.CO2_LEVELS_NORMAL,
188+
);
189+
this.serviceCO2.updateCharacteristic(hap.Characteristic.CarbonDioxideLevel, 0);
190+
191+
this.serviceTemp.getCharacteristic(hap.Characteristic.CurrentTemperature);
192+
this.serviceTemp.updateCharacteristic(hap.Characteristic.CurrentTemperature, 0);
193+
194+
this.serviceHumid.getCharacteristic(hap.Characteristic.CurrentRelativeHumidity);
195+
this.serviceHumid.updateCharacteristic(hap.Characteristic.CurrentRelativeHumidity, 0);
196+
139197
this.updateData();
140198
}
141199

0 commit comments

Comments
 (0)